• 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] Battle Royale

zuber966

Member
Joined
Apr 11, 2020
Messages
38
Solutions
2
Reaction score
14
Location
Rzeszów, Poland
Hi!
Here is a Battle Royale mode for tfs 1.3 I have beed working on. Now I don't really have time to go on with this so I will leave it here for the community. It was my first time with LUA so there might be some garbage code D:

How does it work?

Determine area where players can fight in this event/mode. After some time, area starts burning with fire circle by circle starting from given radius to area origin point. Every player stars as a Knight with given level, max hp, mp, and skill level so every one have the same chances at the beginning. Stats of every player are stored so after death in this mode no one looses anything (skills, level, eq, vocation). Each player is teleported to the random position in the royale area, also chests are distributed randomly. Each chest contains x not repetitive items, where at least on item is weapon.
After death player's stats are returned, bonus exp time, coins are given and gold is distributed based on how many player take part in the event.

I was testing it on otservbr-global from develop branch and it was fine.

First creature events:
data/scripts/creaturescripts/battle_royale_death.lua
Lua:
-- credits: https://github.com/gurgaczj
-- contact: https://otland.net/members/zuber966.239759/

local BRPlayerDeath = CreatureEvent("BattleRoyaleDeath")

function BRPlayerDeath.onDeath(player, corpse, lasthitkiller, mostdamagekiller, lasthitunjustified, mostdamageunjustified)
    if lasthitkiller and lasthitkiller:isPlayer() then
        local targetID = player:getGuid()
        lasthitkiller:removeUnjustifiedKill(targetID)
        db.asyncQuery("UPDATE `player_deaths` SET `unjustified` = 0 and `mostdamage_unjustified` = 0 WHERE `player_id` = " .. db.escapeString(targetID) .. " AND `mostdamage_by` LIKE " .. db.escapeString(lasthitkiller:getName()))
    end
    for i = CONST_SLOT_HEAD, CONST_SLOT_AMMO do
        local item = player:getSlotItem(i)
        if item ~= nil then
            item:moveTo(corpse)
        end
    end

    brGame:playerDied(player)
    teleportToTemple(player)
    player:remove()
    return true
end

BRPlayerDeath:register()

data/scripts/creaturescripts/battle_royale_message_after_death.lua
Lua:
-- credits: https://github.com/gurgaczj
-- contact: https://otland.net/members/zuber966.239759/

local brMessage = CreatureEvent("BattleRoyaleMessageAfterDeath")

function brMessage.onLogin(player)
    local resultId = db.storeQuery("SELECT `id`, `message`, `message_type` FROM `battle_royale_reward_msg` WHERE `player_id` = " .. db.escapeString(player:getGuid()) .. " AND `unreaded` = 1")

    if resultId ~= false then
        repeat
            local message = result.getString(resultId, "message")
            local messageType = result.getNumber(resultId, "message_type")

            player:sendTextMessage(messageType, message)

            local messageId = result.getNumber(resultId, "id")

            db.asyncQuery("DELETE FROM `battle_royale_reward_msg` WHERE `id` = " .. db.escapeString(messageId))
        until not result.next(resultId)
        result.free(resultId) -- Free memory
    end

    return true
end

brMessage:register()

data/scripts/creaturescripts/battle_royale_on_health_change.lua
Lua:
-- credits: https://github.com/gurgaczj
-- contact: https://otland.net/members/zuber966.239759/

local brHealthChange = CreatureEvent("BattleRoyaleHealthChange")

function brHealthChange.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType)
    if attacker and attacker:isPlayer() then
        resetSkull(attacker)
    end

    return primaryDamage, primaryType, -secondaryDamage, secondaryType
end

brHealthChange:register()

data/scripts/creaturescripts/battle_royale_player_logout.lua
Lua:
-- credits: https://github.com/gurgaczj
-- contact: https://otland.net/members/zuber966.239759/

local brPlayerOnLogout = CreatureEvent("BattleRoyaleLogout")

function brPlayerOnLogout.onLogout(player)
    if brGame.canRegister and not brGame.isBattle then
        brGame:removePlayer(player)
    end

    if brGame.isBattle then
        if brPlayersStats[player:getName()] ~= nil then
            player:sendCancelMessage("You can not logout from battle royale.")
            return false
        end
    end
    return true
end

brPlayerOnLogout:register()

Global events

data/scripts/globalevents/royale_events.lua
Lua:
-- credits: https://github.com/gurgaczj
-- contact: https://otland.net/members/zuber966.239759/

local royaleStartSign = GlobalEvent("royaleRegisterStart")

function royaleStartSign.onTime(interval)
    brGame:startRegister()
end

royaleStartSign:time("20:00")
royaleStartSign:register()

local royaleStopSign = GlobalEvent("royaleRegisterStop")

function royaleStopSign.onTime(interval)
    brGame:closeRegister()
    broadcastMessage("Signing up for Battle Royale just stopped. \nBattle will start in minute", MESSAGE_STATUS_CONSOLE_BLUE)
end

royaleStopSign:time("20:14")
royaleStopSign:register()

local royaleBegin = GlobalEvent("royaleBegin")

function royaleBegin.onTime(interval)
    brGame:begin()
end

royaleBegin:time("20:15")
royaleBegin:register()

local royaleClean = GlobalEvent("royaleClean")

function royaleClean.onTime(interval)
    cleanBattleRoyaleMap()
end

royaleClean:time("03:00")
royaleClean:register()

Libs
data/scripts/lib/royale.lua
Lua:
-- credits: https://github.com/gurgaczj
-- contact: https://otland.net/members/zuber966.239759/

BattleRoyale = {players = {}, canRegister = false, isBattle = false, goldPool = 0}

function BattleRoyale:new (o, players, canReg, battle, pool)
   o = o or {}
   setmetatable(o, self)
   self.__index = self
   table = table or {}
   self.players = players;
   self.canRegister = canReg;
   self.isBattle = battle;
   self.goldPool = pool;
   return o
end

brGame = BattleRoyale:new(nil, {}, false, false, 1000000)
brPlayersStats = {} -- players stats, global becouse used in onDeath event, I know, should have use getter
local totalPlayerCount = 0 -- need to calculate gold reward for each player

-- everything starts here
function BattleRoyale:begin()
    totalPlayerCount = #self.players
    if totalPlayerCount <= 1 then
        broadcastMessage("Not enough players for BattleRoyale. Need at least 2. No match today :/", MESSAGE_STATUS_CONSOLE_BLUE)
        self:reset()
        return
    end

    print(totalPlayerCount .. " players registered for Battle Royale.")

    self.isBattle = true
    spawnChests()
    -- for each registered player
    for playerNum = 1, totalPlayerCount do
        local actualPlayer = self.players[playerNum]
        actualPlayer:save()
        local playerID = actualPlayer:getName()
        brPlayersStats[playerID] = getPlayerStatsInfo(actualPlayer)
        actualPlayer:removeAllConditions()
        local playerParty = actualPlayer:getParty()
        if playerParty ~= nil then
            playerParty:removeMember(actualPlayer)
        end
        actualPlayer:setSkullTime(0)
        actualPlayer:setVocation(4)
        setHPAndMana(actualPlayer)
        actualPlayer:setSkillLoss(false)
        actualPlayer:resetSkills() -- remove player skills
        actualPlayer:setSkillValues(1, 70, 0, 0)
        actualPlayer:setSkillValues(2, 70, 0, 0)
        actualPlayer:setSkillValues(3, 70, 0, 0)
        actualPlayer:setBaseMagicLevel(3)
        actualPlayer:setManaSpent(1)
        actualPlayer:setBattleRoyalePlayer(true);
        teleportPlayerToRoyale(actualPlayer)
        moveItemsToDepot(actualPlayer)
        actualPlayer:addItem(1988, 1, false, 1, CONST_SLOT_WHEREEVER)
        registerEvent(actualPlayer, "BattleRoyaleDeath")
        registerEvent(actualPlayer, "BattleRoyaleHealthChange")
        actualPlayer:sendTextMessage(MESSAGE_STATUS_WARNING, "Beware of fire. Island will start burning in 2 minutes!")
    end

    addEvent(spawnFire, FIRE_SPAWN_TIME, 0, 1494)
end

-- after player death
function BattleRoyale:playerDied(deadPlayer)
    self:returnPlayerStats(deadPlayer)
    deadPlayer:removeAllConditions()
    local actualPlayersCount = #self.players
    self:removePlayer(deadPlayer)
    deadPlayer:addItem(1988, 1, INDEX_WHEREEVER, 0)
    if actualPlayersCount ~= 1 then
        rewardPlayer(deadPlayer, totalPlayerCount, actualPlayersCount, self.goldPool)
    end
    return true
end

-- return stats for player
function BattleRoyale:returnPlayerStats(player)
    unregisterEvent(player, "BattleRoyaleDeath")
    unregisterEvent(player, "BattleRoyaleHealthChange")
    local playerID = player:getName()
    setPlayerStatsBack(player, brPlayersStats[playerID])
    print(player:getName() .. " stats returned")
    --brPlayersStats[playerID] = nil
    giveBlessing(player)
    player:setBattleRoyalePlayer(false);
    player:save()
end

-- reward winner
function BattleRoyale:rewardWinner()
    local player = self.players[1]
    if player ~= nil then
        print(player:getName() .. " won battle royale ")
        moveItemsToPosition(player)
        broadcastMessage("Player " .. player:getName() .. " won battle royale! Congratulations!", MESSAGE_STATUS_CONSOLE_BLUE)
        self:returnPlayerStats(player)
        local message = "You have earned " .. tostring(self.goldPool) .. " gold. Check your bank balance\n"
        local bankBalance = player:getBankBalance()
        bankBalance = bankBalance + self.goldPool
        player:setBankBalance(bankBalance)
        local playerId = player:getGuid()
        addPremiumPointsForPlayer(playerId, 150)
        message = message .. "Outstanding, you have earned yourself 150 tibia coins and 24 hours of 50% bonus experience.\n" .. thanks
        player:sendTextMessage(MESSAGE_INFO_DESCR, message)
        player:removeAllConditions()
        teleportToTemple(player)
        player:setSkillLoss(true)
        giveBonusExp(player, 24)
        player:addItem(1988, 1, false, 1, CONST_SLOT_WHEREEVER)
        player:save()
        db.asyncQuery("UPDATE `battle_royale_scores` SET `wins` = `wins` + 1 WHERE `player_id` = " .. db.escapeString(playerId))
        self:reset()
    end
end

-- reset br state
function BattleRoyale:reset()
    -- cleanBattleRoyaleMap()
    self.isBattle = false
    brPlayersStats = {}
    brGame = nil
    totalPlayerCount = 0
    brGame = BattleRoyale:new(nil, {}, false, false, 1000000)
end

-- adds player
function BattleRoyale:addPlayer(player)
    if self.canRegister then
        if self:alreadyRegistered(player) then
            player:sendTextMessage(MESSAGE_INFO_DESCR, "You are already signend for battle royale")
            return
        end
        table.insert(self.players, player)
        player:sendTextMessage(MESSAGE_INFO_DESCR, "You just signed up for battle royale. \n Remember that you can unregister from battle royale with !royale unregister until battle has not started.")
    else
        player:sendTextMessage(MESSAGE_INFO_DESCR, "Signing up for battle royale is not open yet.")
    end
end

-- remove player
function BattleRoyale:removePlayer(player)
    local removedPlayerName = player:getName()
    for i = 1, #self.players do
        if self.players[i]:getName() == removedPlayerName then
            table.remove(self.players, i)
            return true
        end 
    end
    return false
end

-- checks if player is already registered
function BattleRoyale:alreadyRegistered(player)
    local playerName = player:getName()
    for _, p in ipairs(self.players) do
        if p:getName() == playerName then
            return true
        end
    end
    return false
end

-- allows register
function BattleRoyale:startRegister()
    self.canRegister = true
    broadcastMessage("Signing up for Battle Royale just started! \nSign up with !royale command", MESSAGE_STATUS_CONSOLE_BLUE)
end

-- closes register
function BattleRoyale:closeRegister()
    self.canRegister = false
end

Part two in next post
Post automatically merged:

data/scripts/lib/royale_utils.lua
royale_utils.lua

Talkaction
data/scripts/talkactions/royale-register.lua
Lua:
local royaleJoin = TalkAction("!royale")

function royaleJoin.onSay(player, words, param)
    if player:getGroup():getAccess() or player:getAccountType() >= ACCOUNT_TYPE_TUTOR then
        player:sendCancelMessage("Tutors and Gamemasters can not join battle royale")
        return true
    end

    if brGame.isBattle then
        player:sendCancelMessage("There is ongoing Battle Royale game. You should try tomorrow.")
        return false
    end

    brGame:addPlayer(player)
    return false
end

royaleJoin:separator(",")
royaleJoin:register()

local royaleUnregister = TalkAction("!royale unregister")

function royaleUnregister.onSay(player, words, param)
    if not brGame.canRegister and not brGame.isBattle then
        player:sendCancelMessage("Signing up for battle royale is not open yet so you can not unregister.")
        return false
    end

    local alreadyRegistered = brGame:alreadyRegistered(player)

    if br.canRegister and not brGame.isBattle and alreadyRegistered then
        brGame:removePlayer(player)
        player:sendTextMessage(MESSAGE_INFO_DESCR, "You just unregistered from battle royale.")
        return false
    end

    if not alreadyRegistered and br.canRegister and not brGame.isBattle then
        player:sendCancelMessage("You have not registered yet.")
        return false
    end

    if brGame.isBattle and alreadyRegistered then
        player:sendCancelMessage("You cannot unregister from ongoing battle.")
        return false
    end

    return true
end

royaleUnregister:separator(" ")
royaleUnregister:register()

if brGame.canRegister and not brGame.isBattle then
        brGame:removePlayer(player)
    end

local startBR = TalkAction("!reg")
function startBR.onSay(player, words, param)
    if not player:getGroup():getAccess() or player:getAccountType() < ACCOUNT_TYPE_GOD then
        return true
    end

    brGame:startRegister()
    return false
end

startBR:separator(" ")
startBR:register()

local beginBR = TalkAction("!start")
function beginBR.onSay(player, words, param)
    if not player:getGroup():getAccess() or player:getAccountType() < ACCOUNT_TYPE_GOD then
        return true
    end

    brGame:begin()
    return false
end

beginBR:separator(" ")
beginBR:register()

local resetBR = TalkAction("!clean")
function resetBR.onSay(player, words, param)
    if not player:getGroup():getAccess() or player:getAccountType() < ACCOUNT_TYPE_GOD then
        return true
    end
    cleanBattleRoyaleMap()
    brGame:reset()
    return false
end

resetBR:separator(" ")
resetBR:register()

SQL schema
In players table add
`bonusexp` bigint(20) UNSIGNED DEFAULT '0',

And add those two tables
SQL:
--
-- Table structure `battle_royale_scores`
--

CREATE TABLE IF NOT EXISTS `battle_royale_scores` (
  `player_id` int(11) NOT NULL,
  `wins` smallint(5) DEFAULT '0',
  INDEX `player_id` (`player_id`),
  CONSTRAINT `battle_royale_scores_fk`
    FOREIGN KEY (`player_id`) REFERENCES `players` (`id`)
    ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- --------------------------------------------------------

--
-- Table structure `battle_royale_reward_msg`
--

CREATE TABLE IF NOT EXISTS `battle_royale_reward_msg` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `player_id` int(11) NOT NULL,
    `message` varchar(512) NOT NULL,
    `message_type` tinyint(2) NOT NULL,
    `unreaded` tinyint(1) NOT NULL DEFAULT '1',
    INDEX `player_id` (`player_id`),
    CONSTRAINT `battle_royale_reward_msg_fk`
        FOREIGN KEY (`player_id`) REFERENCES `players` (`id`)
        ON DELETE CASCADE,
    PRIMARY KEY(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

C++
In iologindata.cpp
Add bonusexp to save and load player query

after
player->healthMax = result->getNumber<int32_t>("healthmax");

add
player->bonusExpTime = result->getNumber<int32_t>("bonusexp");

after
query << "`bonus_rerolls` = " << player->getPreyBonusRerolls() << ',';

add
query << "`bonusexp` = " << static_cast<uint64_t>(player->getBonusExpTime()) << ",";

luascript.cpp

add somewhere in player methods
C++:
registerMethod("Player", "setMana", LuaScriptInterface::luaPlayerSetMana);
registerMethod("Player", "setBaseMagicLevel", LuaScriptInterface::luaPlayerSetBaseMagicLevel);
registerMethod("Player", "removeAllConditions", LuaScriptInterface::luaPlayerRemoveAllConditions);
registerMethod("Player", "setSkillValues", LuaScriptInterface::luaPlayerSetSkillValues);
registerMethod("Player", "resetSkills", LuaScriptInterface::luaPlayerResetSkills);
registerMethod("Player", "removeUnjustifiedKill", LuaScriptInterface::luaPlayerRemoveUnjustifiedKill);
registerMethod("Player", "setBattleRoyalePlayer", LuaScriptInterface::luaPlayerSetBattleRoyalePlayer);
registerMethod("Player", "setManaSpent", LuaScriptInterface::luaPlayerSetManaSpent);
registerMethod("Player", "setBonusExp", LuaScriptInterface::luaPlayerSetBonusExp);

add implementations
C++:
int LuaScriptInterface::luaPlayerSetMana(lua_State* L)
{
    // player:setMana(maxMana)
    Player* player = getPlayer(L, 1);
    if (player) {
        player->mana = getNumber<int32_t>(L, 2);
        player->sendStats();
        pushBoolean(L, true);
    }
    else {
        lua_pushnil(L);
    }
    return 1;
}

int LuaScriptInterface::luaPlayerSetBaseMagicLevel(lua_State* L)
{
    // player:addSkillTries(skillType, tries)
    Player* player = getUserdata<Player>(L, 1);
    uint32_t baseMagLevel = getNumber<uint32_t>(L, 2);
    if (player && baseMagLevel >= 0) {
        player->magLevel = baseMagLevel;
        player->sendSkills();
        pushBoolean(L, true);
    }
    else {
        lua_pushnil(L);
    }
    return 1;
}

int LuaScriptInterface::luaPlayerRemoveAllConditions(lua_State* L)
{
    // player:removeAllConditions()
    Player* player = getUserdata<Player>(L, 1);
    if (player) {
        player->removeAllConditions();
    }
    else {
        lua_pushnil(L);
    }
    return 1;
}

int LuaScriptInterface::luaPlayerSetSkillValues(lua_State* L)
{
    // player:setSkillValues()
    Player* player = getUserdata<Player>(L, 1);
    if (player) {
        uint16_t skill = getNumber<uint16_t>(L, 2);
        uint16_t skillLevel = getNumber<uint16_t>(L, 3);
        uint16_t skillPercent = getNumber<uint16_t>(L, 4);
        uint16_t skillTries = getNumber<uint16_t>(L, 5);

        player->setSkillValues(skill, skillLevel, skillPercent, skillTries);
        player->sendSkills();

        pushBoolean(L, true);
    }
    else {
        lua_pushnil(L);
    }
    return 1;
}

int LuaScriptInterface::luaPlayerResetSkills(lua_State* L)
{
    // player:resetSkills()
    Player* player = getUserdata<Player>(L, 1);
    if (player) {

        player->resetSkills();
        player->sendStats();

        pushBoolean(L, true);
    }
    else {
        lua_pushnil(L);
    }
    return 1;
}

int LuaScriptInterface::luaPlayerRemoveUnjustifiedKill(lua_State* L)
{
    // player:removeUnjustifiedKill()
    Player* killer = getUserdata<Player>(L, 1);
    uint32_t targetID = getNumber<uint32_t>(L, 2);
    if (killer && targetID > 0) {

        killer->removeUnjustifiedKill(targetID);
        killer->sendStats();

        pushBoolean(L, true);
    }
    else {
        lua_pushnil(L);
    }
    return 1;
}

int LuaScriptInterface::luaPlayerSetBattleRoyalePlayer(lua_State* L)
{
    // player:setBattleRoyalePlayer(true/false)
    Player* player = getUserdata<Player>(L, 1);
    if (player) {
        player->isBattleRoyalePlayer = getBoolean(L, 2);

        pushBoolean(L, true);
    }
    else {
        lua_pushnil(L);
    }
    return 1;
}

int LuaScriptInterface::luaPlayerSetManaSpent(lua_State* L)
{
    // player:setManaSpent(int val)
    Player* player = getUserdata<Player>(L, 1);
    uint64_t newManaSpent = getNumber<uint64_t>(L, 2);
    if (player && newManaSpent >= 0) {
        player->manaSpent = newManaSpent;
        player->sendSkills();

        pushBoolean(L, true);
    }
    else {
        lua_pushnil(L);
    }
    return 1;
}

int LuaScriptInterface::luaPlayerSetBonusExp(lua_State* L)
{
    // player:setBaseXpGain(value)
    Player* player = getUserdata<Player>(L, 1);
    if (player) {
        player->setBonusExpTime(getNumber<uint64_t>(L, 2));
        pushBoolean(L, true);
    }
    else {
        lua_pushnil(L);
    }
    return 1;
}

luascript.h
C++:
static int luaPlayerSetMana(lua_State* L);
static int luaPlayerSetBaseMagicLevel(lua_State* L);
static int luaPlayerRemoveAllConditions(lua_State* L);
static int luaPlayerSetSkillValues(lua_State* L);
static int luaPlayerResetSkills(lua_State* L);
static int luaPlayerRemoveUnjustifiedKill(lua_State* L);
static int luaPlayerSetBattleRoyalePlayer(lua_State* L);
static int luaPlayerSetManaSpent(lua_State* L);
static int luaPlayerSetBonusExp(lua_State* L);

Part three in next post
Post automatically merged:

player.cpp

find
if (targetPlayer->getSkull() == SKULL_NONE && getSkull() == SKULL_NONE && !targetPlayer->hasKilled(this)) {

change to
if (targetPlayer->getSkull() == SKULL_NONE && getSkull() == SKULL_NONE && !targetPlayer->hasKilled(this) && !isBattleRoyalePlayer) {

find (this can be found in two places, apply changes in both)
C++:
uint8_t monthKills = 0;

    for (const auto& kill : unjustifiedKills) {

right after for loop add
C++:
if (!kill.unavenged) {
            continue;
        }

before
addExperience(source, gainExp, true);

add
C++:
if (bonusExpTime >= time(nullptr)) {
        gainExp = gainExp + (gainExp * 0.5);
    }

at the end add
C++:
void Player::removeAllConditions()
{
    for (Condition* condition : conditions) {
        if (hasCondition(condition->getType(), condition->getSubId())) {
            removeCondition(condition->getType(), true);
        }
    }
}

void Player::setSkillValues(uint16_t skill, uint16_t skillLevel, uint16_t skillPercent, uint16_t skillTries) {
    skills[skill].level = skillLevel;
    skills[skill].percent = skillPercent;
    skills[skill].tries = skillTries;
}

void Player::resetSkills() {
    for (uint16_t skill = SKILL_FIST; skill <= SKILL_FISHING; skill++) {
        setSkillValues(skill, 10, 0, 0);
    }
}

void Player::setBonusExpTime(uint64_t bonusExpTime) {
    if (this->bonusExpTime > time(nullptr))
    {
        this->bonusExpTime += bonusExpTime;
    }
    else {
        this->bonusExpTime = time(nullptr) + bonusExpTime;
    }
}

uint64_t Player::getBonusExpTime() {

    return bonusExpTime;
}

void Player::removeUnjustifiedKill(uint32_t killID) {
    for (auto& kill : unjustifiedKills) {
        if (kill.target == killID && kill.unavenged) {
            kill.unavenged = false;
            break;
        }
    }
    sendUnjustifiedPoints();
}

player.h

add

C++:
void removeAllConditions();
void setSkillValues(uint16_t skill, uint16_t skillLevel, uint16_t skillPercent, uint16_t skillTries);
void resetSkills();
void setBonusExpTime(uint64_t bonusExpTime);
uint64_t getBonusExpTime();
void removeUnjustifiedKill(uint32_t killID);
time_t bonusExpTime = 0;
bool isBattleRoyalePlayer = false;

Disclaimer
Lua function player:removeAllConditions() defined in luascript.cpp was working fine in windows, but in ubuntu was causing server crash. Instead you might try global function removeAllConditions(player).

Things that I wanted to do, but don't have time:
  • number of chests spawned depends on number of players taking part in event,
  • speed of fire filling area depends on number of players taking part in event,
  • move player's original items after his death to owner.

If you have any questions I will try to answer and if it is too much garbage just remove thread.
Also some custom runes and items like monster summoner rune can be found on github.
Cheers :*
 
Last edited:
Back
Top