• 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.X] Infinite PVP Arena

ralke

(҂ ͠❛ ෴ ͡❛)ᕤ
Joined
Dec 17, 2011
Messages
1,470
Solutions
27
Reaction score
844
Location
Santiago - Chile
GitHub
ralke23
Twitch
ralke23
Hi! Here's a system made by @Itutorial all credits to him. I wanted to share this in case someone want to use it. This system is a request made in the following thread: Lua - Don't waste potions and runes on area (frompos topos) (https://otland.net/threads/dont-waste-potions-and-runes-on-area-frompos-topos.281692/#post-2700178)

How it works?:
  • PVP arena has 2 entrance tiles. Each one, for a different town
  • The entrance, and the exits must be determined on tileEnterPos, tileExitPos, enterPos, exitPos
  • This tiles are onStepIn events that makes the player get inside the arena and set creature:setPvPArenaPlayer condition.
  • As soon the player enters the arena it's going to have infinite potions, ammunition and runes.
  • Also function creatureeventPvPArena.onPrepareDeath(creature, killer) will prevent player's death if is inside the arena.
  • After being "killed" the player will spawn on exitPos
  • You must use PVP tiles and no-logout tiles inside the arena

Remere's Set-up (images)
redside.png
blueside.png
e_redside.png
e_blueside.png

First we need to go to our actions/scripts/liquids/potions.lua and replace this

Lua:
    item:remove(1)
    return true
end

For
Lua:
    if not player:isPvPArenaPlayer() then
        item:remove(1)
    end
    return true
end

Add this anywhere in global.lua
Lua:
PVP_ARENA_PLAYERS = {}

On data/events/scripts/player.lua add this below function Player:onGainSkillTries(skill, tries)
Lua:
    if self:isPvPArenaPlayer() then
     return 0
    end

Go to creaturescripts/scripts/login.lua add:
Lua:
player:registerEvent("PvP_Arena_PrepareDeath")

Now in data/scripts create a new file (example: pvpArena.lua) and add this
Lua:
local config = {
-- TownID    Pos enter arena              pos exit arena
    [1] = {
        tileEnterPos = Position(185, 663, 9),
        tileExitPos = Position(187, 759, 9),
        enterPos = Position(189, 761, 9),
        exitPos = Position(182, 664, 9)
    },
    [2] = {
        tileEnterPos = Position(262, 661, 9),
        tileExitPos = Position(244, 727, 9),
        enterPos = Position(246, 729, 9),
        exitPos = Position(264, 662, 9)
    }
}

-- Globalevent used to teleport players out of arena when server shuts down --
local globaleventPvPArena = GlobalEvent("PvP_Arena_Global")
function globaleventPvPArena.onShutdown()
    for _, player in pairs(Game.getPlayers()) do
        if player and player:isPvPArenaPlayer() then
            local town = config[player:getTown():getId()]
            if town then
                player:teleportTo(town.exitPos)
            end
        end
    end
    return true
end
globaleventPvPArena:register()

-- Prepare death. Handles players dying in arena. --
local creatureeventPvPArena = CreatureEvent("PvP_Arena_PrepareDeath")
function creatureeventPvPArena.onPrepareDeath(creature, killer)
    if not creature:isPlayer() then return true end
    if not creature:isPvPArenaPlayer() then return true end
    local town = config[creature:getTown():getId()]
    if not town then return true end
 
    creature:teleportTo(town.exitPos)
    creature:addHealth(creature:getMaxHealth())
    creature:addMana(creature:getMaxMana())
    print("PVP")
    creature:setPvPArenaPlayer(false)
    return false
end
creatureeventPvPArena:register()

-- Moveevent used to teleport players in and out of the arena --
local movementPvPArena = MoveEvent()
movementPvPArena:type("stepin")

function movementPvPArena.onStepIn(player, item, position, fromPosition)
    if not player then return true end
    local town = config[player:getTown():getId()]
    if not town then
        player:sendCancelMessage("You do not have a town set. Report to GM.")
        player:teleportTo(fromPosition, true)
        return true
    end
 
    if not player:isPvPArenaPlayer() then
        if position ~= town.tileEnterPos then
            player:sendCancelMessage("You do not have the correct town to enter here.")
            player:teleportTo(fromPosition, true)
            return true
        end
   
        player:teleportTo(town.enterPos)
        town.enterPos:sendMagicEffect(CONST_ME_TELEPORT)
        player:setPvPArenaPlayer(true)
    else
        if position ~= town.tileExitPos then
            player:sendCancelMessage("You do not have the correct town to exit here.")
            player:teleportTo(fromPosition, true)
            return true
        end
   
        player:teleportTo(town.exitPos)
        town.exitPos:sendMagicEffect(CONST_ME_TELEPORT)
        player:setPvPArenaPlayer(false)
    end
    return true
end

movementPvPArena:aid(55235)
movementPvPArena:register()

-- New /c command --
local TalkSetMana = TalkAction("/c")
function TalkSetMana.onSay(player, words, param)
    if not player:getGroup():getAccess() then
        return false
    end
 
    if param == "" then
        player:sendCancelMessage("Usage: /c name")
        return false
    end
 
    local target = Player(param)
 
    if not target then
        player:sendCancelMessage("Player is not online or doesn't exist.")
        return false
    end
 
    if target:isPvPArenaPlayer() then
        target:setPvPArenaPlayer(false)
    end

    target:teleportTo(player:getPosition())
    target:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
    return false
end
TalkSetMana:separator(" ")
TalkSetMana:register()

Now the c++ part

Go to player.h and find: void updateRegeneration(); add under:
C++:
bool inPvPArenaPlayer() {
            return isPvPArenaPlayer;
        }

        void setPvPArenaPlayer(bool value) {
            isPvPArenaPlayer = value;
        }

Now find: bool inventoryAbilities add under:
C++:
bool isPvPArenaPlayer = false;

Go to luascript.h and find: static int luaPlayerSetStorageValue(lua_State* L); add under:
C++:
static int luaPlayerIsPvPArenaPlayer(lua_State* L);
static int luaPlayerSetPvPArenaPlayer(lua_State* L);

Go to luascript.cpp and find: registerMethod("Player", "setStorageValue", LuaScriptInterface::luaPlayerSetStorageValue); add under
C++:
registerMethod("Player", "isPvPArenaPlayer", LuaScriptInterface::luaPlayerIsPvPArenaPlayer);
registerMethod("Player", "setPvPArenaPlayer", LuaScriptInterface::luaPlayerSetPvPArenaPlayer);

Now find: int LuaScriptInterface::luaPlayerSetStorageValue(lua_State* L) under whole method add:
C++:
int LuaScriptInterface::luaPlayerIsPvPArenaPlayer(lua_State* L)
{
    // player:isPvPArenaPlayer()
    Player* player = getUserdata<Player>(L, 1);
    if (!player) {
        lua_pushboolean(L, false);
        return 1;
    }

    bool value = player->inPvPArenaPlayer();
    lua_pushboolean(L, value);
    return 1;
}

int LuaScriptInterface::luaPlayerSetPvPArenaPlayer(lua_State* L)
{
    // player:setPvPArenaPlayer(bool)
    Player* player = getUserdata<Player>(L, 1);
    if (!player) {
        lua_pushboolean(L, false);
        return 1;
    }

    bool value = getBoolean(L, 2, false);

    player->setPvPArenaPlayer(value);
    lua_pushboolean(L, true);
    return 1;
}

Now go to weapons.cpp and look for this:
C++:
switch (action) {
        case WEAPONACTION_REMOVECOUNT:
            if (g_config.getBoolean(ConfigManager::REMOVE_WEAPON_AMMO)) {
                Weapon::decrementItemCount(item);
            }
            break;

        case WEAPONACTION_REMOVECHARGE: {
            uint16_t charges = item->getCharges();
            if (charges != 0 && g_config.getBoolean(ConfigManager::REMOVE_WEAPON_CHARGES)) {
                g_game.transformItem(item, item->getID(), charges - 1);
            }
            break;
        }

Change it for this:
C++:
switch (action) {
        case WEAPONACTION_REMOVECOUNT:
            if (g_config.getBoolean(ConfigManager::REMOVE_WEAPON_AMMO)) {
                if (!player->inPvPArenaPlayer()) {
                    Weapon::decrementItemCount(item);
                }
            }
            break;

        case WEAPONACTION_REMOVECHARGE: {
            uint16_t charges = item->getCharges();
            if (charges != 0 && g_config.getBoolean(ConfigManager::REMOVE_WEAPON_CHARGES)) {
                if (!player->inPvPArenaPlayer()) {
                    g_game.transformItem(item, item->getID(), charges - 1);
                }
            }
            break;
        }

In case you have spears that are dropped on the ground, then change this:
C++:
case WEAPONACTION_MOVE:
            g_game.internalMoveItem(item->getParent(), destTile, INDEX_WHEREEVER, item, 1, nullptr, FLAG_NOLIMIT);
            break;

To this
C++:
case WEAPONACTION_MOVE:
            if (!player->inPvPArenaPlayer()) {
                g_game.internalMoveItem(item->getParent(), destTile, INDEX_WHEREEVER, item, 1, nullptr, FLAG_NOLIMIT);
            }
            break;

Regards!
 
Last edited:
what is the difference between enterPos and tileEnterPos
Also what is the differenct from entering from the blue/red side? is there teams in the arena?

very interesting system by the way
 
what is the difference between enterPos and tileEnterPos
Also what is the differenct from entering from the blue/red side? is there teams in the arena?

very interesting system by the way
tileEnterPos will determine the tile used to enter to arena. For [1] on table, the players from town 1 are only allowed to enter. For [2] the players from town 2. About enterPos, is the position where the player is teleported when steps on the tile tileEnterPos. Same logic applies for exit; remember to set the actions ids to the tileEnterPos and tileExitPos on remere's map editor (aid: 55235 in images).

the difference between the sides is only from which town you're entering to arena. This also leaves the option of adding functions directly to the creatureevent, when player is onPrepareDeath, because it checks town, and if creature:isPvPArenaPlayer():

Lua:
function creatureeventPvPArena.onPrepareDeath(creature, killer)
    if not creature:isPlayer() then return true end
    if not creature:isPvPArenaPlayer() then return true end
    local town = config[creature:getTown():getId()]
    if not town then return true end

Forgot to say, but no-logout tiles and pvp tiles on remere's are requiered to work (it's shown in the images but just in case) ;)
 
Is it possible to add a column for example arena_kills and record each kill that the player does inside the arena?
 
Is it possible to add a column for example arena_kills and record each kill that the player does inside the arena?

Hmmm i'm not sure how to do it but surely would be a nice implementation
You can open a new thread on requests for that aswell ^^
 
Back
Top