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();