diff --git a/data/creaturescripts/scripts/playerdeath.lua b/data/creaturescripts/scripts/playerdeath.lua
index 99f4afd..73fdc56 100644
--- a/data/creaturescripts/scripts/playerdeath.lua
+++ b/data/creaturescripts/scripts/playerdeath.lua
@@ -2,96 +2,85 @@ local deathListEnabled = true
local maxDeathRecords = 50
function onDeath(player, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified)
- local playerId = player:getId()
+ local playerId = player:getId()
if nextUseStaminaTime[playerId] then
nextUseStaminaTime[playerId] = nil
end
- 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
+ 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 = 0
- local killerName
- if killer ~= nil then
- if killer:isPlayer() then
- byPlayer = 1
- else
- local master = killer:getMaster()
- if master and master ~= killer and master:isPlayer() then
- killer = master
- byPlayer = 1
- end
- end
- killerName = killer:getName()
- else
- killerName = "field item"
- 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 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 .. ", " .. 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 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
+ 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
+ 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
+ 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 byPlayer == 1 then
- local targetGuild = player:getGuild()
- targetGuild = targetGuild and targetGuild:getId() or 0
- if targetGuild ~= 0 then
- local killerGuild = killer:getGuild()
- killerGuild = killerGuild and killerGuild:getId() or 0
- if killerGuild ~= 0 and targetGuild ~= killerGuild and isInWar(playerId, killer:getId()) then
- local warId = false
- resultId = db.storeQuery("SELECT `id` FROM `guild_wars` WHERE `status` = 1 AND ((`guild1` = " .. killerGuild .. " AND `guild2` = " .. targetGuild .. ") OR (`guild1` = " .. targetGuild .. " AND `guild2` = " .. killerGuild .. "))")
- if resultId ~= false then
- warId = result.getDataInt(resultId, "id")
- result.free(resultId)
- end
+ if not byPlayer then
+ return
+ end
- if warId ~= false then
- db.asyncQuery("INSERT INTO `guildwar_kills` (`killer`, `target`, `killerguild`, `targetguild`, `time`, `warid`) VALUES (" .. db.escapeString(killerName) .. ", " .. db.escapeString(player:getName()) .. ", " .. killerGuild .. ", " .. targetGuild .. ", " .. os.time() .. ", " .. warId .. ")")
- end
- end
- end
- end
-end
+ local warId = guildwars:isInWar(killer, player)
+ if warId ~= 0 then
+ guildwars:processKill(warId, killer, player)
+ end
+
+end
\ No newline at end of file
diff --git a/data/global.lua b/data/global.lua
index 6c2ede8..dfb8606 100644
--- a/data/global.lua
+++ b/data/global.lua
@@ -48,4 +48,35 @@ end
if not nextUseStaminaTime then
nextUseStaminaTime = {}
+end
+
+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
\ No newline at end of file
diff --git a/data/lib/core/core.lua b/data/lib/core/core.lua
index 8bc8ac0..457e61e 100644
--- a/data/lib/core/core.lua
+++ b/data/lib/core/core.lua
@@ -9,4 +9,5 @@ dofile('data/lib/core/player.lua')
dofile('data/lib/core/position.lua')
dofile('data/lib/core/teleport.lua')
dofile('data/lib/core/tile.lua')
-dofile('data/lib/core/vocation.lua')
\ No newline at end of file
+dofile('data/lib/core/vocation.lua')
+dofile('data/lib/core/guildwars.lua')
\ No newline at end of file
diff --git a/data/lib/core/guildwars.lua b/data/lib/core/guildwars.lua
new file mode 100644
index 0000000..0deb969
--- /dev/null
+++ b/data/lib/core/guildwars.lua
@@ -0,0 +1,199 @@
+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 .. ")."
+ sendGuildChannelMessage(killer:getGuild():getId(), TALKTYPE_CHANNEL_O, 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 .. ")."
+ sendGuildChannelMessage(player:getGuild():getId(), TALKTYPE_CHANNEL_O, 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:getBounty(warId)
+ local resultId = db.storeQuery("SELECT `bounty` FROM `guild_wars` WHERE `id` = " .. warId)
+ if resultId ~= false then
+ local bounty = result.getDataInt(resultId, "bounty")
+ result.free(resultId)
+ return bounty
+ 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`, `time`) VALUES (" .. db.escapeString(killer:getName()) .. ", " .. db.escapeString(target:getName()) .. ", " .. killer:getGuild():getId() .. ", " .. target:getGuild():getId() .. ", " .. warId .. ", " .. os.time() .. ")")
+end
+
+function guildwars:endWar(warId, killer, player, frags)
+ local winGuildInternalMessage = "Congratulations! You have won the war against " .. player:getGuild():getName() .. " with " .. frags .. " frags."
+ sendGuildChannelMessage(killer:getGuild():getId(), TALKTYPE_CHANNEL_O, winGuildInternalMessage)
+
+ local loseGuildInternalMessage = "You have lost the war against " .. killer:getGuild():getName() .. ". They have reached the limit of " .. frags .. " frags."
+ sendGuildChannelMessage(player:getGuild():getId(), TALKTYPE_CHANNEL_O, 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())
+
+ local bounty = self:getBounty(warId)
+ if bounty > 0 then
+ killer:getGuild():increaseBankBalance(bounty * 2)
+ end
+end
+
+function guildwars:setWarEmblem(guild1, guild2)
+ guild1:setGuildWarEmblem(guild2)
+end
+
+function guildwars:updateState(warId, status)
+ db.query("UPDATE `guild_wars` SET `status` = " .. status .. " WHERE `id` = " .. warId)
+end
+
+function guildwars:getPendingInvitation(guild1, guild2)
+ local resultId = db.storeQuery("SELECT `id`, `bounty` FROM `guild_wars` WHERE `guild1` = " .. guild1 .. " AND `guild2` = " .. guild2 .. " AND `status` = 0")
+
+ if resultId then
+ local id = result.getDataInt(resultId, "id")
+ local bounty = result.getDataInt(resultId, "bounty")
+ result.free(resultId)
+
+ return id, bounty
+ end
+
+ return 0
+end
+
+function guildwars:startWar(player, warId, guild1, guild2, bounty)
+ if bounty > 0 then
+ local guildBalance = guild1:getBankBalance()
+ if guildBalance < bounty then
+ player:sendCancelMessage("Your guild does not have that much money in the bank account balance to accept this war with the bounty of " .. bounty .. " gold.")
+ return true
+ end
+
+ if not guild1:decreaseBankBalance(bounty) then
+ player:sendCancelMessage("Your guild does not have that much money in the bank account balance to accept this war with the bounty of " .. bounty .. " gold.")
+ return true
+ end
+ end
+
+ self:updateState(warId, 1)
+
+ self:setWarEmblem(guild1, guild2)
+
+ broadcastMessage(guild1:getName() .. " has accepted " .. guild2:getName() .. " invitation to war.", MESSAGE_EVENT_ADVANCE)
+end
+
+function guildwars:rejectWar(warId, guild1, guild2, bounty)
+ self:updateState(warId, 2)
+ broadcastMessage(guild1:getName() .. " has rejected " .. guild2:getName() .. " invitation to war.", MESSAGE_EVENT_ADVANCE)
+
+ if bounty > 0 then
+ guild2:increaseBankBalance(bounty)
+ end
+end
+
+function guildwars:cancelWar(warId, guild1, guild2, bounty)
+ self:updateState(warId, 3)
+ broadcastMessage(guild1:getName() .. " has canceled invitation to a war with " .. guild2:getName() .. ".", MESSAGE_EVENT_ADVANCE)
+
+ if bounty > 0 then
+ guild1:increaseBankBalance(bounty)
+ end
+end
+
+function guildwars:invite(player, guild1, guild2, frags, bounty)
+ 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(500, frags))
+ bounty = math.max(0, math.min(10000000, bounty))
+
+ if bounty > 0 then
+ local guildBalance = guild1:getBankBalance()
+ if guildBalance < bounty then
+ player:sendCancelMessage("Your guild does not have that much money in the bank account balance to set this bounty.")
+ return true
+ end
+
+ if not guild1:decreaseBankBalance(bounty) then
+ player:sendCancelMessage("Your guild does not have that much money in the bank account balance to set this bounty.")
+ return true
+ end
+ end
+
+ db.asyncQuery("INSERT INTO `guild_wars` (`guild1`, `guild2`, `frag_limit`, `bounty`) VALUES (" .. guild1:getId() .. ", " .. guild2:getId() .. ", " .. frags .. ", " .. bounty .. ");")
+
+ local message = guild1:getName() .. " has invited " .. guild2:getName() .. " to war for " .. frags .. " frags."
+ if bounty > 0 then
+ message = message .. " The bounty reward is set to " .. bounty .. " gold."
+ end
+ broadcastMessage(message, MESSAGE_EVENT_ADVANCE)
+end
\ No newline at end of file
diff --git a/data/talkactions/scripts/war.lua b/data/talkactions/scripts/war.lua
new file mode 100644
index 0000000..146dfdd
--- /dev/null
+++ b/data/talkactions/scripts/war.lua
@@ -0,0 +1,60 @@
+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 \"" .. enemyGuildName .. "\" does not exists or nobody is online from the guild.")
+ 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, bounty = 0
+
+ if warCommand == "cancel" then
+ pendingWarId, bounty = guildwars:getPendingInvitation(player:getGuild():getId(), enemyGuild:getId())
+ else
+ pendingWarId, bounty = 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, bounty)
+ elseif warCommand == "cancel" then
+ guildwars:cancelWar(pendingWarId, player:getGuild(), enemyGuild, bounty)
+ else
+ guildwars:startWar(player, pendingWarId, player:getGuild(), enemyGuild, bounty)
+ end
+
+ return true
+ end
+
+ if warCommand == "invite" then
+ guildwars:invite(player, player:getGuild(), enemyGuild, tonumber(t[3] and string.trim(t[3]) or 100), tonumber(t[4] and string.trim(t[4]) or 0))
+ return true
+ end
+
+ return true
+end
\ No newline at end of file
diff --git a/data/talkactions/talkactions.xml b/data/talkactions/talkactions.xml
index 8407033..4c3abf8 100644
--- a/data/talkactions/talkactions.xml
+++ b/data/talkactions/talkactions.xml
@@ -57,6 +57,7 @@
+
diff --git a/sabrehaven.sql b/sabrehaven.sql
index c1c1ce6..b6f4428 100644
--- a/sabrehaven.sql
+++ b/sabrehaven.sql
@@ -172,11 +172,10 @@ CREATE TABLE `guild_wars` (
`id` int(11) NOT NULL,
`guild1` int(11) NOT NULL DEFAULT '0',
`guild2` int(11) NOT NULL DEFAULT '0',
- `name1` varchar(255) NOT NULL,
- `name2` varchar(255) NOT NULL,
`status` tinyint(2) NOT NULL DEFAULT '0',
- `started` bigint(15) NOT NULL DEFAULT '0',
- `ended` bigint(15) NOT NULL DEFAULT '0'
+ `frag_limit` int(11) NOT NULL DEFAULT '0',
+ `declaration_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `bounty` bigint(20) UNSIGNED NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
@@ -1586,6 +1585,14 @@ ALTER TABLE `account_viplist`
ALTER TABLE `guilds`
ADD CONSTRAINT `guilds_ibfk_1` FOREIGN KEY (`ownerid`) REFERENCES `players` (`id`) ON DELETE CASCADE;
+--
+-- Constraints for table `guild_wars`
+--
+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;
+
--
-- Constraints for table `guildwar_kills`
--
diff --git a/src/behaviourdatabase.cpp b/src/behaviourdatabase.cpp
index 95b286b..d5a788e 100644
--- a/src/behaviourdatabase.cpp
+++ b/src/behaviourdatabase.cpp
@@ -1087,11 +1087,12 @@ void BehaviourDatabase::checkAction(const NpcBehaviourAction* action, Player* pl
break;
}
- if (IOLoginData::getGuildBalance(playerGuild->getId()) < static_cast(money)) {
+
+ if (IOGuild::getGuildBalance(playerGuild->getId()) < static_cast(money)) {
break;
}
- if (IOLoginData::decreaseGuildBankBalance(playerGuild->getId(), money)) {
+ if (IOGuild::decreaseGuildBankBalance(playerGuild->getId(), money)) {
player->setBankBalance(player->getBankBalance() + money);
}
@@ -1117,7 +1118,7 @@ void BehaviourDatabase::checkAction(const NpcBehaviourAction* action, Player* pl
break;
}
- if (IOLoginData::increaseGuildBankBalance(playerGuild->getId(), money)) {
+ if (IOGuild::increaseGuildBankBalance(playerGuild->getId(), money)) {
player->setBankBalance(player->getBankBalance() - money);
}
@@ -1326,7 +1327,7 @@ int32_t BehaviourDatabase::evaluate(NpcBehaviourNode* node, Player* player, cons
return false;
}
- return IOLoginData::getGuildBalance(playerGuild->getId());
+ return IOGuild::getGuildBalance(playerGuild->getId());
}
case BEHAVIOUR_TYPE_CLIENTVERSION:
return g_game.getClientVersion();
diff --git a/src/chat.cpp b/src/chat.cpp
index 59973c8..a046aa9 100644
--- a/src/chat.cpp
+++ b/src/chat.cpp
@@ -105,6 +105,17 @@ bool ChatChannel::removeUser(const Player& player)
return true;
}
+bool ChatChannel::hasUser(const Player& player) {
+ return users.find(player.getID()) != users.end();
+}
+
+void ChatChannel::sendToAll(const std::string& message, SpeakClasses type) const
+{
+ for (const auto& it : users) {
+ it.second->sendChannelMessage("", message, type, id);
+ }
+}
+
bool ChatChannel::talk(const Player& fromPlayer, SpeakClasses type, const std::string& text)
{
if (users.find(fromPlayer.getID()) == users.end()) {
diff --git a/src/chat.h b/src/chat.h
index e181142..5a07cfc 100644
--- a/src/chat.h
+++ b/src/chat.h
@@ -41,8 +41,10 @@ class ChatChannel
bool addUser(Player& player);
bool removeUser(const Player& player);
+ bool hasUser(const Player& player);
bool talk(const Player& fromPlayer, SpeakClasses type, const std::string& text);
+ void sendToAll(const std::string& message, SpeakClasses type) const;
const std::string& getName() const {
return name;
diff --git a/src/ioguild.cpp b/src/ioguild.cpp
index b4ef9b5..67b91af 100644
--- a/src/ioguild.cpp
+++ b/src/ioguild.cpp
@@ -39,7 +39,7 @@ uint32_t IOGuild::getGuildIdByName(const std::string& name)
void IOGuild::getWarList(uint32_t guildId, GuildWarList& guildWarList)
{
std::ostringstream query;
- query << "SELECT `guild1`, `guild2` FROM `guild_wars` WHERE (`guild1` = " << guildId << " OR `guild2` = " << guildId << ") AND `ended` = 0 AND `status` = 1";
+ query << "SELECT `guild1`, `guild2` FROM `guild_wars` WHERE (`guild1` = " << guildId << " OR `guild2` = " << guildId << ") AND `status` IN (1, 4)";
DBResult_ptr result = Database::getInstance()->storeQuery(query.str());
if (!result) {
@@ -55,3 +55,29 @@ void IOGuild::getWarList(uint32_t guildId, GuildWarList& guildWarList)
}
} while (result->next());
}
+
+uint64_t IOGuild::getGuildBalance(uint32_t id)
+{
+ std::ostringstream query;
+ query << "SELECT `balance` FROM `guilds` WHERE `id` = " << id;
+ DBResult_ptr result = Database::getInstance()->storeQuery(query.str());
+ if (!result) {
+ return 0;
+ }
+
+ return result->getNumber("balance");
+}
+
+bool IOGuild::increaseGuildBankBalance(uint32_t guid, uint64_t bankBalance)
+{
+ std::ostringstream query;
+ query << "UPDATE `guilds` SET `balance` = `balance` + " << bankBalance << " WHERE `id` = " << guid;
+ return Database::getInstance()->executeQuery(query.str());
+}
+
+bool IOGuild::decreaseGuildBankBalance(uint32_t guid, uint64_t bankBalance)
+{
+ std::ostringstream query;
+ query << "UPDATE `guilds` SET `balance` = `balance` - " << bankBalance << " WHERE `id` = " << guid;
+ return Database::getInstance()->executeQuery(query.str());
+}
\ No newline at end of file
diff --git a/src/ioguild.h b/src/ioguild.h
index e5f058c..6414ae5 100644
--- a/src/ioguild.h
+++ b/src/ioguild.h
@@ -27,6 +27,10 @@ class IOGuild
public:
static uint32_t getGuildIdByName(const std::string& name);
static void getWarList(uint32_t guildId, GuildWarList& guildWarList);
+
+ static uint64_t getGuildBalance(uint32_t id);
+ static bool increaseGuildBankBalance(uint32_t guid, uint64_t bankBalance);
+ static bool decreaseGuildBankBalance(uint32_t guid, uint64_t bankBalance);
};
#endif
diff --git a/src/iologindata.cpp b/src/iologindata.cpp
index 023f33a..ebe3a04 100644
--- a/src/iologindata.cpp
+++ b/src/iologindata.cpp
@@ -829,18 +829,6 @@ uint32_t IOLoginData::getGuidByName(const std::string& name)
return result->getNumber("id");
}
-uint64_t IOLoginData::getGuildBalance(uint32_t id)
-{
- std::ostringstream query;
- query << "SELECT `balance` FROM `guilds` WHERE `id` = " << id;
- DBResult_ptr result = Database::getInstance()->storeQuery(query.str());
- if (!result) {
- return 0;
- }
-
- return result->getNumber("balance");
-}
-
// Return 0 means player not found, 1 player is without vocation, 2 player with vocation
uint16_t IOLoginData::canTransferMoneyToByName(const std::string& name)
{
@@ -930,20 +918,6 @@ void IOLoginData::increaseBankBalance(uint32_t guid, uint64_t bankBalance)
Database::getInstance()->executeQuery(query.str());
}
-bool IOLoginData::increaseGuildBankBalance(uint32_t guid, uint64_t bankBalance)
-{
- std::ostringstream query;
- query << "UPDATE `guilds` SET `balance` = `balance` + " << bankBalance << " WHERE `id` = " << guid;
- return Database::getInstance()->executeQuery(query.str());
-}
-
-bool IOLoginData::decreaseGuildBankBalance(uint32_t guid, uint64_t bankBalance)
-{
- std::ostringstream query;
- query << "UPDATE `guilds` SET `balance` = `balance` - " << bankBalance << " WHERE `id` = " << guid;
- return Database::getInstance()->executeQuery(query.str());
-}
-
void IOLoginData::increaseBankBalance(std::string name, uint64_t bankBalance)
{
Database* db = Database::getInstance();
diff --git a/src/iologindata.h b/src/iologindata.h
index cb09ed9..6bfbecd 100644
--- a/src/iologindata.h
+++ b/src/iologindata.h
@@ -45,14 +45,11 @@ class IOLoginData
static bool loadPlayer(Player* player, DBResult_ptr result);
static bool savePlayer(Player* player);
static uint32_t getGuidByName(const std::string& name);
- static uint64_t getGuildBalance(uint32_t id);
static uint16_t canTransferMoneyToByName(const std::string& name);
static bool getGuidByNameEx(uint32_t& guid, bool& specialVip, std::string& name);
static std::string getNameByGuid(uint32_t guid);
static bool formatPlayerName(std::string& name);
static void increaseBankBalance(uint32_t guid, uint64_t bankBalance);
- static bool increaseGuildBankBalance(uint32_t guid, uint64_t bankBalance);
- static bool decreaseGuildBankBalance(uint32_t guid, uint64_t bankBalance);
static void increaseBankBalance(const std::string name, uint64_t bankBalance);
static bool hasBiddedOnHouse(uint32_t guid);
diff --git a/src/luascript.cpp b/src/luascript.cpp
index b207b86..222ecb3 100644
--- a/src/luascript.cpp
+++ b/src/luascript.cpp
@@ -1012,6 +1012,12 @@ void LuaScriptInterface::registerFunctions()
//getWaypointPosition(name)
lua_register(luaState, "getWaypointPositionByName", LuaScriptInterface::luaGetWaypointPositionByName);
+ //sendChannelMessage(channelId, type, message)
+ lua_register(luaState, "sendChannelMessage", LuaScriptInterface::luaSendChannelMessage);
+
+ //sendGuildChannelMessage(guildId, type, message)
+ lua_register(luaState, "sendGuildChannelMessage", LuaScriptInterface::luaSendGuildChannelMessage);
+
#ifndef LUAJIT_VERSION
//bit operations for Lua, based on bitlib project release 24
//bit.bnot, bit.band, bit.bor, bit.bxor, bit.lshift, bit.rshift
@@ -2154,10 +2160,15 @@ void LuaScriptInterface::registerFunctions()
registerMethod("Guild", "getId", LuaScriptInterface::luaGuildGetId);
registerMethod("Guild", "getName", LuaScriptInterface::luaGuildGetName);
registerMethod("Guild", "getMembersOnline", LuaScriptInterface::luaGuildGetMembersOnline);
+ registerMethod("Guild", "setGuildWarEmblem", LuaScriptInterface::luaGuildSetGuildWarEmblem);
registerMethod("Guild", "addRank", LuaScriptInterface::luaGuildAddRank);
registerMethod("Guild", "getRankById", LuaScriptInterface::luaGuildGetRankById);
registerMethod("Guild", "getRankByLevel", LuaScriptInterface::luaGuildGetRankByLevel);
+
+ registerMethod("Guild", "getBankBalance", LuaScriptInterface::luaGuildGetBankBalance);
+ registerMethod("Guild", "increaseBankBalance", LuaScriptInterface::luaGuildIncreaseBankBalance);
+ registerMethod("Guild", "decreaseBankBalance", LuaScriptInterface::luaGuildDecreaseBankBalance);
// Group
registerClass("Group", "", LuaScriptInterface::luaGroupCreate);
@@ -3572,7 +3583,7 @@ int LuaScriptInterface::luaIsInWar(lua_State* L)
return 1;
}
- pushBoolean(L, player->isInWar(targetPlayer));
+ lua_pushnumber(L, player->getWarId(targetPlayer));
return 1;
}
@@ -3590,6 +3601,40 @@ int LuaScriptInterface::luaGetWaypointPositionByName(lua_State* L)
return 1;
}
+int LuaScriptInterface::luaSendChannelMessage(lua_State* L)
+{
+ //sendChannelMessage(channelId, type, message)
+ uint32_t channelId = getNumber(L, 1);
+ ChatChannel* channel = g_chat->getChannelById(channelId);
+ if (!channel) {
+ pushBoolean(L, false);
+ return 1;
+ }
+
+ SpeakClasses type = getNumber(L, 2);
+ std::string message = getString(L, 3);
+ channel->sendToAll(message, type);
+ pushBoolean(L, true);
+ return 1;
+}
+
+int LuaScriptInterface::luaSendGuildChannelMessage(lua_State* L)
+{
+ //sendGuildChannelMessage(guildId, type, message)
+ uint32_t guildId = getNumber(L, 1);
+ ChatChannel* channel = g_chat->getGuildChannelById(guildId);
+ if (!channel) {
+ pushBoolean(L, false);
+ return 1;
+ }
+
+ SpeakClasses type = getNumber(L, 2);
+ std::string message = getString(L, 3);
+ channel->sendToAll(message, type);
+ pushBoolean(L, true);
+ return 1;
+}
+
std::string LuaScriptInterface::escapeString(const std::string& string)
{
std::string s = string;
@@ -9225,6 +9270,44 @@ int LuaScriptInterface::luaGuildGetName(lua_State* L)
return 1;
}
+int LuaScriptInterface::luaGuildSetGuildWarEmblem(lua_State* L)
+{
+ // guild:setGuildWarEmblem(guild2)
+ const Guild* guild = getUserdata(L, 1);
+ if (!guild) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ const Guild* guild2 = getUserdata(L, 2);
+ if (!guild2) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ auto& members = guild->getMembersOnline();
+ for (Player* player : members) {
+ GuildWarList guildWarList;
+ IOGuild::getWarList(player->getGuild()->getId(), guildWarList);
+ player->guildWarList = guildWarList;
+ }
+
+ auto& members2 = guild2->getMembersOnline();
+ for (Player* player : members2) {
+ GuildWarList guildWarList;
+ IOGuild::getWarList(player->getGuild()->getId(), guildWarList);
+ player->guildWarList = guildWarList;
+ g_game.updateCreatureSkull(player);
+ }
+
+ for (Player* player : members) {
+ g_game.updateCreatureSkull(player);
+ }
+
+ pushBoolean(L, true);
+ return 1;
+}
+
int LuaScriptInterface::luaGuildGetMembersOnline(lua_State* L)
{
// guild:getMembersOnline()
@@ -9306,6 +9389,49 @@ int LuaScriptInterface::luaGuildGetRankByLevel(lua_State* L)
return 1;
}
+int LuaScriptInterface::luaGuildGetBankBalance(lua_State* L)
+{
+ // guild:getBankBalance()
+ Guild* guild = getUserdata(L, 1);
+ if (guild) {
+ lua_pushnumber(L, IOGuild::getGuildBalance(guild->getId()));
+ }
+ else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+int LuaScriptInterface::luaGuildIncreaseBankBalance(lua_State* L)
+{
+ // guild:increaseBankBalance(amount)
+ Guild* guild = getUserdata(L, 1);
+ if (guild) {
+ uint32_t amount = getNumber(L, 2);
+ bool isSuccess = IOGuild::increaseGuildBankBalance(guild->getId(), amount);
+ pushBoolean(L, isSuccess);
+ }
+ else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+int LuaScriptInterface::luaGuildDecreaseBankBalance(lua_State* L)
+{
+ // guild:decreaseBankBalance(amount)
+ Guild* guild = getUserdata(L, 1);
+ if (guild) {
+ uint32_t amount = getNumber(L, 2);
+ bool isSuccess = IOGuild::decreaseGuildBankBalance(guild->getId(), amount);
+ pushBoolean(L, isSuccess);
+ }
+ else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
// Group
int LuaScriptInterface::luaGroupCreate(lua_State* L)
{
diff --git a/src/luascript.h b/src/luascript.h
index 7f9508e..7fad48b 100644
--- a/src/luascript.h
+++ b/src/luascript.h
@@ -485,6 +485,9 @@ class LuaScriptInterface
static int luaGetWaypointPositionByName(lua_State* L);
+ static int luaSendChannelMessage(lua_State* L);
+ static int luaSendGuildChannelMessage(lua_State* L);
+
#ifndef LUAJIT_VERSION
static int luaBitNot(lua_State* L);
static int luaBitAnd(lua_State* L);
@@ -977,11 +980,16 @@ class LuaScriptInterface
static int luaGuildGetId(lua_State* L);
static int luaGuildGetName(lua_State* L);
static int luaGuildGetMembersOnline(lua_State* L);
+ static int luaGuildSetGuildWarEmblem(lua_State* L);
static int luaGuildAddRank(lua_State* L);
static int luaGuildGetRankById(lua_State* L);
static int luaGuildGetRankByLevel(lua_State* L);
+ static int luaGuildGetBankBalance(lua_State* L);
+ static int luaGuildIncreaseBankBalance(lua_State* L);
+ static int luaGuildDecreaseBankBalance(lua_State* L);
+
// Group
static int luaGroupCreate(lua_State* L);
diff --git a/src/player.cpp b/src/player.cpp
index 597da0e..6e6afcd 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -3447,10 +3447,6 @@ Skulls_t Player::getSkullClient(const Creature* creature) const
return SKULL_GREEN;
}
- if (!player->getGuildWarList().empty() && guild == player->getGuild()) {
- return SKULL_GREEN;
- }
-
if (player->hasAttacked(this)) {
return SKULL_YELLOW;
}
@@ -3614,6 +3610,33 @@ bool Player::hasLearnedInstantSpell(const std::string& spellName) const
return false;
}
+uint32_t Player::getWarId(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` IN (1, 4) 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("id");
+}
+
bool Player::isInWar(const Player* player) const
{
if (!player || !guild) {
diff --git a/src/player.h b/src/player.h
index eb55b22..10a40bb 100644
--- a/src/player.h
+++ b/src/player.h
@@ -201,6 +201,7 @@ class Player final : public Creature, public Cylinder
guildNick = nick;
}
+ uint32_t getWarId(const Player* player) const;
bool isInWar(const Player* player) const;
bool isInWarList(uint32_t guild_id) const;
@@ -638,6 +639,11 @@ class Player final : public Creature, public Cylinder
}
}
+ void sendChannelMessage(const std::string& author, const std::string& text, SpeakClasses type, uint16_t channel) {
+ if (client) {
+ client->sendChannelMessage(author, text, type, channel);
+ }
+ }
void sendCreatureAppear(const Creature* creature, const Position& pos, bool isLogin) {
if (client) {
client->sendAddCreature(creature, pos, creature->getTile()->getStackposOfCreature(this, creature), isLogin);
diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp
index 90b7388..d0a4a23 100644
--- a/src/protocolgame.cpp
+++ b/src/protocolgame.cpp
@@ -1176,6 +1176,18 @@ void ProtocolGame::sendChannel(uint16_t channelId, const std::string& channelNam
writeToOutputBuffer(msg);
}
+void ProtocolGame::sendChannelMessage(const std::string& author, const std::string& text, SpeakClasses type, uint16_t channel)
+{
+ NetworkMessage msg;
+ msg.addByte(0xAA);
+ msg.add(0x00);
+ msg.addString(author);
+ msg.add(0x00);
+ msg.addByte(type);
+ msg.add(channel);
+ msg.addString(text);
+ writeToOutputBuffer(msg);
+}
void ProtocolGame::sendIcons(uint16_t icons)
{
diff --git a/src/protocolgame.h b/src/protocolgame.h
index c581a5e..1d5f637 100644
--- a/src/protocolgame.h
+++ b/src/protocolgame.h
@@ -140,6 +140,7 @@ class ProtocolGame final : public Protocol
void parseCloseChannel(NetworkMessage& msg);
//Send functions
+ void sendChannelMessage(const std::string& author, const std::string& text, SpeakClasses type, uint16_t channel);
void sendClosePrivate(uint16_t channelId);
void sendCreatePrivateChannel(uint16_t channelId, const std::string& channelName);
void sendChannelsDialog();