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

Feature [Nostalrius 7.7] Guild War

Stellow

C++/C#/PHP/LUA
Joined
Oct 23, 2008
Messages
1,072
Reaction score
183
Location
Germany
GitHub
eubrunomiguel
Hello *,

this set of scripts provides a War System for Nostalrius 7.7.

Features:
  • broadcast messages on the guild channel (killing, deaths, war status)
  • green skull for enemies
  • configure limit of frags
  • no unjustified kill upon enemy killing
  • cancel, reject war invitation
  • create armistice with running war

Commands:
- !war invite, guildname
Invite guildname to war.

- !war invite, guildname, fraglimit
Invite guildname to war for fraglimit frags.

- !war cancel, guildname
Cancel invitation to war against guildname.

- !war reject, guildname
Reject invitation to war from guildname.

- !war end, guildname
Request war armistice with guildname.

data/talkactions/Talkactions.xml (add line)
XML:
<talkaction words="!war" separator=" " script="custom/war.lua"/>

data/talkactions/custom/war.lua (create file)
Lua:
--
-- Created by IntelliJ IDEA.
-- User: eubrunomiguel
-- Date: 2020-05-10
-- Time: 12:49
-- To change this template use File | Settings | File Templates.
--

function onSay(player, words, param)
    if not player:getGuild() or player:getGuildLevel() < 3 then
        player:sendCancelMessage("You cannot execute this talkaction.")
        return true
    end

    local t = param:split(",")

    if not t[2] then
        player:sendCancelMessage("Not enough param(s).")
        return true
    end

    local enemyGuildName = string.trim(t[2])

    local enemyGuild = Guild(getGuildId(enemyGuildName))

    if not enemyGuild then
        player:sendCancelMessage("Guild \"" .. guildName .. "\" does not exists.")
        return true
    end

    if enemyGuild:getId() == player:getGuild():getId() then
        player:sendCancelMessage("You cannot perform war action on your own guild.")
        return true
    end
   
    local warCommand = string.trim(t[1])

    if isInArray({"accept", "reject", "cancel"}, warCommand) then
        local pendingWarId = 0

        if warCommand == "cancel" then
            pendingWarId = guildwars:getPendingInvitation(player:getGuild():getId(), enemyGuild:getId())
        else
            pendingWarId = guildwars:getPendingInvitation(enemyGuild:getId(), player:getGuild():getId())
        end

        if pendingWarId == 0 then
            player:sendCancelMessage("Currently there's no pending invitation for a war with " .. enemyGuild:getName() .. ".")
            return true
        end

        if warCommand == "reject" then
            guildwars:rejectWar(pendingWarId, player:getGuild(), enemyGuild)
        elseif warCommand == "cancel" then
            guildwars:cancelWar(pendingWarId, player:getGuild(), enemyGuild)
        else
            guildwars:startWar(pendingWarId, player:getGuild(), enemyGuild)
        end
   
        return true
    end

    if warCommand == "invite" then
        guildwars:invite(player:getGuild(), enemyGuild, tonumber(t[3] and string.trim(t[3]) or 100))
        return true
    end

    if isInArray({"end", "finish"}, warCommand) then
        if not guildwars:acceptRequestToEndWar(player:getGuild(), enemyGuild) then
            if not guildwars:requestToEndWar(enemyGuild, player:getGuild()) then
                player:sendCancelMessage("Currently there's no active war with " .. enemyGuild:getName() .. ".")
            end
        end

        return true
    end

    return true
end

data/creaturescripts/playerdeath.lua (replace)
Lua:
local deathListEnabled = true
local maxDeathRecords = 50

function onDeath(player, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified)
    local playerId = player:getId()

    player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are dead.")
   
    -- restart blessings values
    player:setStorageValue(101,0)
    player:setStorageValue(102,0)
    player:setStorageValue(103,0)
    player:setStorageValue(104,0)
    player:setStorageValue(105,0)
   
    if not deathListEnabled then
        return
    end

    local byPlayer = false
    local killerName
    if killer ~= nil then
        if killer:isPlayer() then
            byPlayer = true
        else
            local master = killer:getMaster()
            if master and master ~= killer and master:isPlayer() then
                killer = master
                byPlayer = true
            end
        end
        killerName = killer:getName()
    else
        killerName = "field item"
    end

    local byPlayerMostDamage = 0
    local mostDamageKillerName
    if mostDamageKiller ~= nil then
        if mostDamageKiller:isPlayer() then
            byPlayerMostDamage = 1
        else
            local master = mostDamageKiller:getMaster()
            if master and master ~= mostDamageKiller and master:isPlayer() then
                mostDamageKiller = master
                byPlayerMostDamage = 1
            end
        end
        mostDamageName = mostDamageKiller:getName()
    else
        mostDamageName = "field item"
    end

    local playerGuid = player:getGuid()
    db.query("INSERT INTO `player_deaths` (`player_id`, `time`, `level`, `killed_by`, `is_player`, `mostdamage_by`, `mostdamage_is_player`, `unjustified`, `mostdamage_unjustified`) VALUES (" .. playerGuid .. ", " .. os.time() .. ", " .. player:getLevel() .. ", " .. db.escapeString(killerName) .. ", " .. (byPlayer and 1 or 0) .. ", " .. db.escapeString(mostDamageName) .. ", " .. byPlayerMostDamage .. ", " .. (unjustified and 1 or 0) .. ", " .. (mostDamageUnjustified and 1 or 0) .. ")")
    local resultId = db.storeQuery("SELECT `player_id` FROM `player_deaths` WHERE `player_id` = " .. playerGuid)

    local deathRecords = 0
    local tmpResultId = resultId
    while tmpResultId ~= false do
        tmpResultId = result.next(resultId)
        deathRecords = deathRecords + 1
    end

    if resultId ~= false then
        result.free(resultId)
    end

    local limit = deathRecords - maxDeathRecords
    if limit > 0 then
        db.asyncQuery("DELETE FROM `player_deaths` WHERE `player_id` = " .. playerGuid .. " ORDER BY `time` LIMIT " .. limit)
    end

    if not byPlayer then
        return
    end

    local warId = guildwars:isInWar(killer, player)

    if warId ~= 0 then
        guildwars:processKill(warId, killer, player)
    end

end

data/lib/core/core.lua (add line)
Code:
dofile('data/lib/core/guildwars.lua')

data/lib/core/guildwars.lua (create file)
Lua:
--
-- Created by IntelliJ IDEA.
-- User: eubrunomiguel
-- Date: 2020-05-23
-- Time: 08:37
-- To change this template use File | Settings | File Templates.
--

guildwars = {}

guildwars.__index = guildwars

function guildwars:isInWar(player1, player2)
    if not player1:getGuild() or not player2:getGuild() then
        return 0
    end

    if player1:getGuild():getId() == 0 or player2:getGuild():getId() == 0 then
        return 0
    end

    if player1:getGuild():getId() == player2:getGuild():getId() then
        return 0
    end

    return isInWar(player1:getId(), player2:getId())
end

function guildwars:processKill(warId, killer, player)
    local fragLimit = self:getFragLimit(warId)

    local killerFrags = self:getKills(warId, killer:getGuild():getId()) + 1
    local deadFrags = self:getKills(warId, player:getGuild():getId())

    local killerMsg = "Opponent " .. player:getName() .. " of the " .. player:getGuild():getName() .. " was killed by " .. killer:getName() .. ". The new score is " .. killerFrags .. ":" .. deadFrags .. " frags (limit " .. fragLimit .. ")."
    killer:guildBroadcast(killerMsg)

    local deadMsg = "Guild member " .. player:getName() .. " was killed by " .. killer:getName() .. " of the " .. killer:getGuild():getName() .. ". The new score is " .. deadFrags .. ":" .. killerFrags .. " frags (limit " .. fragLimit .. ")."
    player:guildBroadcast(deadMsg)

    self:insertKill(warId, killer, player)

    if killerFrags >= fragLimit then
        self:endWar(warId, killer, player, killerFrags)
    end
end

function guildwars:getFragLimit(warId)
    local resultId = db.storeQuery("SELECT `frag_limit` FROM `guild_wars` WHERE `id` = " .. warId)
    if resultId ~= false then
        local frag_limit = result.getDataInt(resultId, "frag_limit")
        result.free(resultId)
        return frag_limit
    end
    return 0
end

function guildwars:getKills(warId, guildId)
    local resultId = db.storeQuery("SELECT COUNT(*) as frags FROM `guildwar_kills` WHERE `warid` = " .. warId .. " and `killerguild` = " .. guildId)
    if resultId ~= false then
        local frags = result.getDataInt(resultId, "frags")
        result.free(resultId)
        return frags
    end
    return 0
end

function guildwars:insertKill(warId, killer, target)
    db.asyncQuery("INSERT INTO `guildwar_kills` (`killer`, `target`, `killerguild`, `targetguild`, `warid`) VALUES (" .. db.escapeString(killer:getName()) .. ", " .. db.escapeString(target:getName()) .. ", " .. killer:getGuild():getId() .. ", " .. target:getGuild():getId() .. ", " .. warId .. ")")
end

function guildwars:endWar(warId, killer, player, frags)
    local winGuildInternalMessage = "Congratulations! You have won the war against " .. player:getGuild():getName() .. " with " .. frags .. " frags."
    killer:guildBroadcast(winGuildInternalMessage)

    local loseGuildInternalMessage = "You have lost the war against " .. killer:getGuild():getName() .. ". They have reached the limit of " .. frags .. " frags."
    player:guildBroadcast(loseGuildInternalMessage)

    broadcastMessage(killer:getGuild():getName() .. " have won the war against " .. player:getGuild():getName() .. " with " .. frags .. " frags.", MESSAGE_EVENT_ADVANCE)

    self:updateState(warId, 5)

    self:setWarEmblem(killer:getGuild(), player:getGuild(), 0)
end

function guildwars:setWarEmblem(guild1, guild2, emblem)
    local guild1Members = guild1:getMembersOnline()
    for i = #guild1Members, 1, -1 do
        guild1Members[i]:setSkull(emblem)
    end

    local guild2Members = guild2:getMembersOnline()
    for i = #guild2Members, 1, -1 do
        guild2Members[i]:setSkull(emblem)
    end
end

function guildwars:updateState(warId, status)
    db.asyncQuery("UPDATE `guild_wars` SET `status` = " .. status .. " WHERE `id` = " .. warId)
end

function guildwars:getPendingInvitation(guild1, guild2)
    local resultId = db.storeQuery("SELECT `id` FROM `guild_wars` WHERE `guild1` = " .. guild1 .. " AND `guild2` = " .. guild2 .. " AND `status` = 0")

    if resultId then
        local id = result.getDataInt(resultId, "id")
        result.free(resultId)

        return id
    end

    return 0
end

function guildwars:startWar(warId, guild1, guild2)
    self:updateState(warId, 1)

    self:setWarEmblem(guild1, guild2, 2)

    broadcastMessage(guild1:getName() .. " has accepted " .. guild2:getName() .. " invitation to war.", MESSAGE_EVENT_ADVANCE)
end

function guildwars:rejectWar(warId, guild1, guild2)
    self:updateState(warId, 2)
    broadcastMessage(guild1:getName() .. " has rejected " .. guild2:getName() .. " invitation to war.", MESSAGE_EVENT_ADVANCE)
end

function guildwars:cancelWar(warId, guild1, guild2)
    self:updateState(warId, 3)
    broadcastMessage(guild1:getName() .. " has canceled invitation to a war with " .. guild2:getName() .. ".", MESSAGE_EVENT_ADVANCE)
end

function guildwars:invite(guild1, guild2, frags)
    local str = ""
    local tmpQuery = db.storeQuery("SELECT `guild1`, `status` FROM `guild_wars` WHERE `guild1` IN (" .. guild1:getId() .. "," .. guild2:getId() .. ") AND `guild2` IN (" .. guild2:getId() .. "," .. guild1:getId() .. ") AND `status` IN (0, 1)")
    if tmpQuery then
        if result.getDataInt(tmpQuery, "status") == 0 then
            if result.getDataInt(tmpQuery, "guild1") == guild1:getId() then
                str = "You have already invited " .. guild2:getName() .. " to war."
            else
                str = guild2:getName() .. " have already invited you to war."
            end
        else
            str = "You are already on a war with " .. guild2:getName() .. "."
        end

        result.free(tmpQuery)
    end

    if str ~= "" then
        player:sendCancelMessage(str)
        return true
    end

    frags = math.max(10, math.min(1000, frags))

    db.asyncQuery("INSERT INTO `guild_wars` (`guild1`, `guild2`, `frag_limit`) VALUES (" .. guild1:getId() .. ", " .. guild2:getId() .. ", " .. frags .. ");")
    broadcastMessage(guild1:getName() .. " has invited " .. guild2:getName() .. " to war for " .. frags .. " frags.", MESSAGE_EVENT_ADVANCE)
end

function guildwars:acceptRequestToEndWar(guild1, guild2)
    local resultId = db.storeQuery("SELECT `id`, `status` FROM `guild_wars` WHERE `guild1` = " .. guild1:getId() .. " AND `guild2` = " .. guild2:getId() .. " AND `status` IN (1, 4)")
    if resultId then
        local warId = result.getDataInt(resultId, "id")
        local status = result.getDataInt(resultId, "status")
        result.free(resultId)

        self:updateState(warId, 5)

        broadcastMessage(guild1:getName() .. " has " .. (status == 4 and "mend fences" or "ended up a war") .. " with " .. guild2:getName() .. ".", MESSAGE_EVENT_ADVANCE)

        self:setWarEmblem(guild1, guild2, 0)

        return true
    end

    return false
end

function guildwars:requestToEndWar(guild1, guild2)
    local resultId = db.storeQuery("SELECT `id` FROM `guild_wars` WHERE `guild1` = " .. guild1:getId() .. " AND `guild2` = " .. guild2:getId() .. " AND `status` = 1")
    if resultId then
        local warId = result.getDataInt(resultId, "id")
        result.free(resultId)

        self:updateState(warId, 4)

        broadcastMessage(guild1:getName() .. " has signed an armstice declaration on a war with " .. guild2:getName() .. ".", MESSAGE_EVENT_ADVANCE)
        return true
    end
    return false
end

data/global.lua (add)
Lua:
function isInArray(array, value, isCaseSensitive)
    local compareLowerCase = false
    if value ~= nil and type(value) == "string" and not isCaseSensitive then
        value = string.lower(value)
        compareLowerCase = true
    end
    if array == nil or value == nil then
        return (array == value), nil
    end
    local t = type(array)
    if t ~= "table" then
        if compareLowerCase and t == "string" then
            return (string.lower(array) == string.lower(value)), nil
        else
            return (array == value), nil
        end
    end
    for k,v in pairs(array) do
        local newV
        if compareLowerCase and type(v) == "string" then
            newV = string.lower(v)
        else
            newV = v
        end
        if newV == value then
            return true, k
        end
    end
    return false
end

luascript.h (add)
C++:
static int luaPlayerGuildBroadcast(lua_State* L);

Replace
C++:
pushBoolean(L, player->isInWar(targetPlayer));
With
C++:
lua_pushnumber(L, player->isInWar(targetPlayer));

luascript.cpp (add)
C++:
registerMethod("Player", "guildBroadcast", LuaScriptInterface::luaPlayerGuildBroadcast);
C++:
int LuaScriptInterface::luaPlayerGuildBroadcast(lua_State* L)
{
        // player:guildBroadcast(text)
        Player* player = getUserdata<Player>(L, 1);
        if (!player || !player->getGuild()) {
          lua_pushnil(L);
          return 1;
        }

        const std::string& text = getString(L, 2);
        g_chat->talkToChannel(*player, TALKTYPE_CHANNEL_R2, text, CHANNEL_GUILD, "");

        pushBoolean(L, true);
        return 1;
}

player.h
Replace
C++:
bool isInWar(const Player* player) const;
With
C++:
uint32_t isInWar(const Player* player) const;

Remove
C++:
bool isInWarList(uint32_t guild_id) const;

Remove
C++:
const GuildWarList& getGuildWarList() const {       
             return guildWarList;       
         }

Remove
C++:
RemoveGuildWarList guildWarList;

player.cpp
Replace
C++:
if (!Combat::isInPvpZone(this, targetPlayer) && !isInWar(targetPlayer)) {
With
C++:
if (!Combat::isInPvpZone(this, targetPlayer) && isInWar(targetPlayer) == 0) {

Replace
C++:
if (targetPlayer->getSkull() == SKULL_NONE && !isInWar(targetPlayer)) {
With
C++:
if (targetPlayer->getSkull() == SKULL_NONE && isInWar(targetPlayer) == 0) {

Replace
C++:
if (isInWar(player)) {
With
C++:
if (isInWar(player) != 0) {

Remove
C++:
bool Player::isInWarList(uint32_t guildId) const
{
...
}

Replace
C++:
bool Player::isInWar(const Player* targetPlayer) const
{
   ...
}
With
C++:
uint32_t Player::isInWar(const Player* targetPlayer) const
{
    if (!targetPlayer || !guild) {
            return false;
    }

    const Guild* targetPlayerGuild = targetPlayer->getGuild();
    if (!targetPlayerGuild) {
        return false;
    }

    const auto targetGuild = guild->getId();
    const auto killerGuild = targetPlayerGuild->getId();

    Database* db = Database::getInstance();

    std::ostringstream query;
    query << "SELECT `id` FROM `guild_wars` WHERE `status` = 1 AND ((`guild1` = " << killerGuild << " AND `guild2` = " << targetGuild << ") OR (`guild1` = " << targetGuild << " AND `guild2` = " << killerGuild << "))";
    DBResult_ptr result = db->storeQuery(query.str());

    if (!result) {
      return 0;
    }

    return result->getNumber<uint32_t>("id");
}

Remove
C++:
if (!player->getGuildWarList().empty() && guild == player->getGuild()) {
    return SKULL_GREEN;
}

iologindata.cpp
Remove
C++:
IOGuild::getWarList(guildId, player->guildWarList);

ioguild.h
Remove
C++:
typedef std::vector<uint32_t> GuildWarList;

Remove
C++:
static void getWarList(uint32_t guildId, GuildWarList& guildWarList);

ioguild.cpp
Remove
C++:
void IOGuild::getWarList(uint32_t guildId, GuildWarList& guildWarList){
...
}

chat.cpp
Replace
C++:
if (users.find(fromPlayer.getID()) == users.end()) {
With
C++:
if (type != TALKTYPE_CHANNEL_R2 && users.find(fromPlayer.getID()) == users.end()) {

Replace
C++:
if (channelId == CHANNEL_GUILD) {
With
C++:
if (channelId == CHANNEL_GUILD && type != TALKTYPE_CHANNEL_R2) {

MYSQL
SQL:
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";

CREATE TABLE `guild_wars` (
  `id` int(11) NOT NULL,
  `guild1` int(11) NOT NULL DEFAULT '0',
  `guild2` int(11) NOT NULL DEFAULT '0',
  `status` tinyint(2) NOT NULL DEFAULT '0',
  `frag_limit` int(11) NOT NULL DEFAULT '0',
  `declaration_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


ALTER TABLE `guild_wars`
  ADD PRIMARY KEY (`id`),
  ADD KEY `guild1` (`guild1`),
  ADD KEY `guild2` (`guild2`);


ALTER TABLE `guild_wars`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;


ALTER TABLE `guild_wars`
  ADD CONSTRAINT `guild1_ibfk_1` FOREIGN KEY (`guild1`) REFERENCES `guilds` (`id`) ON DELETE CASCADE,
  ADD CONSTRAINT `guild2_ibfk_1` FOREIGN KEY (`guild2`) REFERENCES `guilds` (`id`) ON DELETE CASCADE;
COMMIT;
 
Last edited:

Evil Puncker

prolonged absenteeism
TFS Developer
Joined
May 30, 2009
Messages
7,263
Solutions
142
Reaction score
2,891
is that isInArray really necessary? doesn't 'table.contains' do the same?
 

WibbenZ

Global Moderator
Staff member
Global Moderator
Joined
Oct 16, 2008
Messages
6,303
Solutions
225
Reaction score
1,289
Location
Sweden
is that isInArray really necessary? doesn't 'table.contains' do the same?

It's the exact same function, isInArray is the "compat" function, but I still have a hard time using table.contains since it's longer / more annoying to write hehe
I guess OP agrees with that.
 

Blasphemy

Member
Joined
Jan 5, 2012
Messages
230
Reaction score
19
Difficult to help with this detailed information.
I didn't founded this line:
1590974377643.png
Later I realized that it was misspelled and its only "GuildWarList guildWarList;"
but I did not tryed again... I'll check it again :)
 

wizinx

Member
Joined
Jul 6, 2010
Messages
53
Reaction score
6
error.png
Post automatically merged:

killing a player now gets this error.

errores.png

playerdeath

Lua:
    local warId = guildwars:isInWar(killer, player)

    if warId ~= 0 then
        guildwars:processKill(warId, killer, player)
    end
 
Last edited:
Top