• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

TFS 1.X+ 8.60 Guilds emblem, need reconnect

Dark Magican

Batory 7.72 - map developer.
Joined
Jun 30, 2009
Messages
60
Reaction score
20
Location
Legnica
Hello everyone!
I have a problem, I added the war system to TFS 1.5 8.60 downgrade by Nekiro, everything works - only for the 'emblem' shields to appear, I have to relog the characters. I use OTCv8

Could anyone help fix this?


warsystem.lua
function onSay(cid, words, param)

local player = Player(cid)
local guild = player:getGuild()
if(guild == nil) then
player:sendCancelMessage("You need to be in a guild in order to execute this talkaction.")
return false
end

local guild = getPlayerGuildId(cid)
if not guild or (player:getGuildLevel() < 3) then
player:sendCancelMessage("You cannot execute this talkaction.")
return false
end

local t = string.split(param, ",")
if(not t[2]) then
player:sendChannelMessage("", "Not enough param(s).", TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
return false
end

local enemy = getGuildId(t[2])
if(not enemy) then
player:sendChannelMessage("", "Guild \"" .. t[2] .. "\" does not exists.", TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
return false
end


if(enemy == guild) then
player:sendChannelMessage("", "You cannot perform war action on your own guild.", TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
return false
end

local enemyName, tmp = "", db.storeQuery("SELECT name FROM guilds WHERE id = " .. enemy)
if tmp ~= false then
enemyName = result.getDataString(tmp, "name")
result.free(tmp)
end

if(isInArray({"accept", "reject", "cancel"}, t[1])) then
local query = "guild1 = " .. enemy .. " AND guild2 = " .. guild
if(t[1] == "cancel") then
query = "guild1 = " .. guild .. " AND guild2 = " .. enemy
end

tmp = db.storeQuery("SELECT id, started, ended, payment FROM guild_wars WHERE " .. query .. " AND status = 0")
if(tmp == false) then
player:sendChannelMessage("", "Currently there's no pending invitation for a war with " .. enemyName .. ".", TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
return false
end

if(t[1] == "accept") then
local _tmp = db.storeQuery("SELECT balance FROM guilds WHERE id = " .. guild)
local state = result.getDataInt(_tmp, "balance") < result.getDataInt(tmp, "payment")

result.free(_tmp)
if(state) then
player:sendChannelMessage("", "Your guild balance is too low to accept this invitation.", TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
return false
end

db.query("UPDATE guilds SET balance = balance - " .. result.getDataInt(tmp, "payment") .. " WHERE id = " .. guild)
end

query = "UPDATE guild_wars SET "
local msg = "accepted " .. enemyName .. " invitation to war."
if(t[1] == "reject") then
query = query .. "ended = " .. os.time() .. ", status = 2"
msg = "rejected " .. enemyName .. " invitation to war."
elseif(t[1] == "cancel") then
query = query .. "ended = " .. os.time() .. ", status = 3"
msg = "canceled invitation to a war with " .. enemyName .. "."
else
query = query .. "started = " .. os.time() .. ", ended = " .. (result.getDataInt(tmp, "ended") > 0 and (os.time() + ((result.getDataInt(tmp, "started") - result.getDataInt(tmp, "ended")) / 86400)) or 0) .. ", status = 1"
end


query = query .. " WHERE id = " .. result.getDataInt(tmp, "id")
result.free(tmp)
db.query(query)
broadcastMessage(getPlayerGuildName(cid) .. " has " .. msg, MESSAGE_EVENT_ADVANCE)
return false
end

if(t[1] == "invite") then
local str = ""
tmp = db.storeQuery("SELECT guild1, status FROM guild_wars WHERE guild1 IN (" .. guild .. "," .. enemy .. ") AND guild2 IN (" .. enemy .. "," .. guild .. ") AND status IN (0, 1)")
if(tmp ~= false) then

if(result.getDataInt(tmp, "status") == 0) then
if(result.getDataInt(tmp, "guild1") == guild) then
str = "You have already invited " .. enemyName .. " to war."
else
str = enemyName .. " have already invited you to war."
end
else
str = "You are already on a war with " .. enemyName .. "."
end

result.free(tmp)
end

if(str ~= "") then
player:sendChannelMessage("", str, TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
return false
end

local frags = tonumber(t[3])
if(frags ~= nil) then
frags = math.max(10, math.min(1000, frags))
else
frags = 100
end

local payment = tonumber(t[4])
if(payment ~= nil) then
payment = math.floor(payment)+1000
tmp = db.storeQuery("SELECT balance FROM guilds WHERE id = " .. guild)

local state = result.getDataInt(tmp, "balance") < payment
result.free(tmp)
if(state) then
player:sendChannelMessage("", "Your guild balance is too low for such payment.", TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
return false
end

db.query("UPDATE guilds SET balance = balance - " .. payment .. " WHERE id = " .. guild)
else
payment = 0
end

local begining, ending = os.time(), tonumber(t[5])
if(ending ~= nil and ending ~= 0) then
ending = begining + (ending * 86400)
else
ending = 0
end

db.query("INSERT INTO guild_wars (guild1, guild2, started, ended, frags, payment,name1,name2) VALUES (" .. guild .. ", " .. enemy .. ", " .. begining .. ", " .. ending .. ", " .. frags .. ", " .. payment .. ", '" .. getPlayerGuildName(cid) .."', '"..enemyName.."');")
db.query("INSERT INTO znote_guild_wars (limit) VALUES ('"..frags.."');")
broadcastMessage(getPlayerGuildName(cid) .. " has invited " .. enemyName .. " to war till " .. frags .. " frags.", MESSAGE_EVENT_ADVANCE)
return false
end

if(not isInArray({"end", "finish"}, t[1])) then
return false
end

local status = (t[1] == "end" and 1 or 4)
tmp = db.storeQuery("SELECT id FROM guild_wars WHERE guild1 = " .. guild .. " AND guild2 = " .. enemy .. " AND status = " .. status)
if(tmp ~= false) then
local query = "UPDATE guild_wars SET ended = " .. os.time() .. ", status = 5 WHERE id = " .. result.getDataInt(tmp, "id")
result.free(tmp)

db.query(query)
broadcastMessage(getPlayerGuildName(cid) .. " has " .. (status == 4 and "mend fences" or "ended up a war") .. " with " .. enemyName .. ".", MESSAGE_EVENT_ADVANCE)
return false
end

if(status == 4) then
player:sendChannelMessage("", "Currently there's no pending war truce from " .. enemyName .. ".", TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
return false
end

tmp = db.storeQuery("SELECT id, ended FROM guild_wars WHERE guild1 = " .. enemy .. " AND guild2 = " .. guild .. " AND status = 1")
if(tmp ~= false) then
if(result.getDataInt(tmp, "ended") > 0) then
result.free(tmp)
player:sendChannelMessage("", "You cannot request ending for war with " .. enemyName .. ".", TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
return false
end

local query = "UPDATE guild_wars SET status = 4, ended = " .. os.time() .. " WHERE id = " .. result.getDataInt(tmp, "id")
result.free(tmp)

db.query(query)
broadcastMessage(getPlayerGuildName(cid) .. " has signed an armstice declaration on a war with " .. enemyName .. ".", MESSAGE_EVENT_ADVANCE)
return false
end

player:sendChannelMessage("", "Currently there's no active war with " .. enemyName .. ".", TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
return false
end
playerdeath.lua

local deathListEnabled = true
local maxDeathRecords = 5

local function sendWarStatus(guildId, enemyGuildId, warId, playerName, killerName)
local guild, enemyGuild = Guild(guildId), Guild(enemyGuildId)
if not guild or not enemyGuild then
return
end

local resultId = db.storeQuery("SELECT guild_wars.id, (SELECT limit FROM znote_guild_wars WHERE znote_guild_wars.id = guild_wars.id) AS limit, (SELECT COUNT(1) FROM guildwar_kills WHERE guildwar_kills.warid = guild_wars.id AND guildwar_kills.killerguild = guild_wars.guild1) guild1_kills, (SELECT COUNT(1) FROM guildwar_kills WHERE guildwar_kills.warid = guild_wars.id AND guildwar_kills.killerguild = guild_wars.guild2) guild2_kills FROM guild_wars WHERE (guild1 = " .. guildId .. " OR guild2 = " .. guildId .. ") AND status = 1 AND id = " .. warId)
if resultId then

local guild1_kills = result.getNumber(resultId, "guild1_kills")
local guild2_kills = result.getNumber(resultId, "guild2_kills")
local limit = result.getNumber(resultId, "limit")
result.free(resultId)

local members = guild:getMembersOnline()
for i = 1, #members do
members:sendChannelMessage("", string.format("%s was killed by %s. The new score is %d:%d frags (limit: %d)", playerName, killerName, guild1_kills, guild2_kills, limit), TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
end

local enemyMembers = enemyGuild:getMembersOnline()
for i = 1, #enemyMembers do
enemyMembers:sendChannelMessage("", string.format("%s was killed by %s. The new score is %d:%d frags (limit: %d)", playerName, killerName, guild1_kills, guild2_kills, limit), TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
end

if guild1_kills >= limit or guild2_kills >= limit then
db.query("UPDATE guild_wars SET status = 4, ended = " .. os.time() .. " WHERE status = 1 AND id = " .. warId)
Game.broadcastMessage(string.format("%s has just won the war against %s.", guild:getName(), enemyGuild:getName()), MESSAGE_EVENT_ADVANCE)
end
end
end

function onDeath(player, corpse, killer, mostDamageKiller, lastHitUnjustified, mostDamageUnjustified)
local playerId = player:getId()
if nextUseStaminaTime[playerId] then
nextUseStaminaTime[playerId] = nil
end

player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are dead.")
if not deathListEnabled then
return
end

local byPlayer = 0
local killerName
if killer 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 byPlayerMostDamage = 0
local mostDamageKillerName
if mostDamageKiller 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 .. ", " .. (lastHitUnjustified 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 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.getNumber(resultId, "id")
result.free(resultId)
end

if warId ~= false then
local playerName = player:getName()
db.asyncQuery("INSERT INTO guildwar_kills (killer, target, killerguild, targetguild, time, warid) VALUES (" .. db.escapeString(killerName) .. ", " .. db.escapeString(playerName) .. ", " .. killerGuild .. ", " .. targetGuild .. ", " .. os.time() .. ", " .. warId .. ")")
addEvent(sendWarStatus, 1000, killerGuild, targetGuild, warId, playerName, killerName)
end
end
end
end
end


fbad568dd0c5f5c598f689b5baf68561.gif
 

You could use this as example
so if war has been accepted then send guild members that are online the emblem
 

You could use this as example
so if war has been accepted then send guild members that are online the emblem
Thank you, I didn't come across this topic. Tomorrow I will add these functions and test them ;) However, I am surprised that TFS 1.5 does not have this feature

although, I'm sure it won't change anything, because in my case the emblem is given - unfortunately only after reloading the character.
 
However, I am surprised that TFS 1.5 does not have this feature
Because official Tibia does not allow to start/stop war with server online (you manage wars on website and they start/stop with global save every 24 hours), so they did not need 'updateEmblem' function and did not add packet in client that would do this.

This commit contains code to resend (remove and add again) creature to all clients on screen ( creature:getEmblem(), creature:setEmblem(emblem) · infernumx/forgottenserver@209fbd5 (https://github.com/infernumx/forgottenserver/commit/209fbd51191ac2def5c9cdc979527278b24edcf9#diff-eb01d79b279ebf659ed73216c0b3c490685221c9358e058ce55a1c8ab4e99c35R1259-R1275) ):
Code:
void ProtocolGame::sendCreatureEmblem(const Creature* creature)
{
    if (!canSee(creature)) {
        return;
    }
    // Remove creature from client and re-add to update
    Position pos = creature->getPosition();
    int32_t stackpos = creature->getTile()->getClientIndexOfCreature(player, creature);
    sendRemoveTileThing(pos, stackpos);
    NetworkMessage msg;
    msg.addByte(0x6A);
    msg.addPosition(pos);
    msg.addByte(stackpos);
    AddCreature(msg, creature, false, creature->getID());
    writeToOutputBuffer(msg);
}
It should update emblem of player in-game, but it's also possible that player will disappear for millisecond which will un-check him on Battle List, if there is other player already attacking him.

EDIT:
TFS and commit from post above does not contain 'guild war management' code (probably it's not in any open source OTS, I've only seen it on 500+ OTSes), so wars of given player are loaded from database when he is loaded from database (login). Re-sending emblem code won't fix anything, because TFS will still think this player is not at war.
 
Last edited:
Ponieważ oficjalna Tibia nie pozwala na rozpoczynanie/zatrzymywanie wojny, gdy serwer jest online (wojnami zarządza się na stronie internetowej, a rozpoczynają się/zatrzymują globalnie co 24 godziny), nie była potrzebna funkcja „updateEmblem” i nie dodali pakietu w kliencie, który by to umożliwiał.

To zatwierdzenie zawiera kod umożliwiający ponowne wysłanie (usunięcie i ponowne dodanie) stworzenia do wszystkich klientów na ekranie ( creature:getEmblem(), creature:setEmblem(emblem) · infernumx/forgottenserver@209fbd5 (https://github.com/infernumx/forgottenserver/commit/209fbd51191ac2def5c9cdc979527278b24edcf9#diff-eb01d79b279ebf659ed73216c0b3c490685221c9358e058ce55a1c8ab4e99c35R1259-R1275) ):
[KOD]void ProtocolGame::sendCreatureEmblem(const Creature* creature)
{
jeśli (!canSee(stworzenie)) {
powrót;
}
// Usuń stworzenie z klienta i dodaj je ponownie do aktualizacji
Pozycja pos = creature->getPosition();
int32_t stackpos = creature->getTile()->getClientIndexOfCreature(player, creature);
wyślijRemoveTileThing(pos, stackpos);
Wiadomość sieciowa;
wiadomość.addByte(0x6A);
msg.addPosition(pos);
msg.addByte(stos);
DodajStworzenie(msg, stworzenie, fałsz, stworzenie->getID());
writeToOutputBuffer(wiadomość);
}
[/KOD]
Powinno to zaktualizować emblemat gracza w grze, ale istnieje również możliwość, że gracz zniknie na milisekundę, co spowoduje usunięcie jego zaznaczenia z listy bitewnej, jeśli inny gracz już go atakuje.

REDAGOWAĆ:
TFS i commit z postu powyżej nie zawierają kodu 'guild war management' (prawdopodobnie nie ma go w żadnym OTS open source, widziałem go tylko na ponad 500 OTS), więc wojny danego gracza są ładowane z bazy danych, gdy jest on ładowany z bazy danych (logowanie). Ponowne wysłanie kodu emblematu niczego nie naprawi, ponieważ TFS nadal będzie uważać, że ten gracz nie jest w stanie wojny.
Dziękuję Ci bardzo, tak dokładnie :) przeglądałem swoje source, jak i wielu innych dostępnych silników i żaden nie posiadał wysyłania/update do klienta w momencie isInWar.



// Thank you all, the topic has been resolved - you can close it :)
 
Back
Top