- Mesaj
- 181
- Çözümler
- 3
- Beğeni
- 147
- Puan
- 764
- Ticaret Puanı
- 0
Selamlar herkese,
Dünya Patronu etkinliğini eklerken boss koordinatlarını ve boss spawn'ı cpp dosyasından yapmaya çalışıyorum ancak bossun spawn olmasında sürekli hata alıyorum belki kullandığım fonksiyonlar hatalıdır bilemiyorum.
game/syserr
Dünya Patronu etkinliğini eklerken boss koordinatlarını ve boss spawn'ı cpp dosyasından yapmaya çalışıyorum ancak bossun spawn olmasında sürekli hata alıyorum belki kullandığım fonksiyonlar hatalıdır bilemiyorum.
world_boss_manager.cpp:
#include "stdafx.h" // Genellikle en başa eklenir
#include "world_boss_manager.h"
#include "char_manager.h" // Karakter işlemleri için gerekebilir
#include "sectree_manager.h" // Varlıkların konumu vb. için
#include "desc_manager.h" // Oyunculara paket göndermek için
#include "config.h" // test_server gibi konfigürasyonlar için
#include "log.h" // Loglama için
#include "locale_service.h" // Yerelleştirilmiş mesajlar için
#include "utils.h" // random_element, number gibi yardımcı fonksiyonlar için
#include "char.h" // CHARACTER, CHpulse vs. için (zaten vardı)
#include "event.h" // Zamanlayıcı olayları için
#include "db.h" // DB sorguları için (db_clientdesc için)
#include "desc.h" // LPDESC için (zaten packet.h içinde olabilir)
#include "constants.h" // PASSES_PER_SEC gibi sabitler için
#include "guild.h" // CGuild için EKLENDİ
#include "party.h" // Oyuncunun partide olup olmadığını kontrol etmek için (gerekirse) - EKLENDİ
#include "affect.h" // Gerekirse buff işlemleri için - EKLENDİ
#include "desc_client.h" // LPDESC->Packet() için - EKLENDİ
// Gerekirse diğer başlık dosyaları eklenecek
CWorldBossManager::CWorldBossManager() :
m_dwMinSpawnIntervalSeconds(60), // TEST: 1 dakika
m_dwMaxSpawnIntervalSeconds(120), // TEST: 2 dakika
m_dwBossActiveDurationSeconds(300), // TEST: 5 dakika
m_dwBossNoDamageDespawnSeconds(180), // TEST: 3 dakika
m_dwEventCooldownSeconds(120), // TEST: 2 dakika
m_dwMinDamageForQuest(250000),
m_dwQuestBuffVnum(0), // Config'den yüklenecek
m_dwQuestBuffDurationSeconds(7200), // Varsayılan 2 saat
m_currentState(STATE_INACTIVE),
m_tNextStateChangeTime(0),
m_pkCurrentBoss(NULL),
m_dwCurrentBossVnum(0),
m_tBossSpawnTime(0),
m_tLastDamageTime(0),
m_wLastRankingPlayerCount(0),
m_dwLastKilledBossVnumForReward(0),
m_tLastBossKillTime(0)
{
}
CWorldBossManager::~CWorldBossManager()
{
Destroy();
}
EVENTINFO(worldboss_event_info)
{
// worldboss_event_info() {} // Gerekirse constructor
};
EVENTFUNC(worldboss_timer_event)
{
return 0;
}
bool CWorldBossManager::Initialize() // void'dan bool'a çevrildi
{
// Sadece Channel 1'de etkinleştir
if (g_bChannel != 1)
{
sys_log(0, "WORLD_BOSS: Manager disabled for channel %d. Event will only run on channel 1.", g_bChannel);
m_currentState = STATE_INACTIVE; // Etkinliği bu kanalda başlatma
return true; // Başarılı bir şekilde "pasif" olarak başlatıldı.
}
// === GEÇİCİ DEBUG KONTROLÜNÜ ŞİMDİLİK KALDIRABİLİRİZ ===
// if (this == nullptr)
// {
// fprintf(stderr, "CRITICAL ERROR: CWorldBossManager::Initialize() called on a NULL instance!\n");
// return false;
// }
// ==============================
if (m_currentState != STATE_INACTIVE && m_currentState != STATE_COOLDOWN)
{
if (m_currentState == STATE_COOLDOWN && m_tNextStateChangeTime > time(0))
{
sys_log(0, "WORLD_BOSS: Manager already in COOLDOWN on Channel 1. Next spawn in %d seconds.", (int)(m_tNextStateChangeTime - time(0)));
return true; // Zaten düzgün bir durumda, hata değil.
}
else if (m_currentState != STATE_INACTIVE)
{
sys_log(0, "WORLD_BOSS: Manager already initialized and running on Channel 1 (State: %d).", m_currentState);
return true; // Zaten düzgün bir durumda, hata değil.
}
}
sys_log(0, "WORLD_BOSS: Initializing Manager for Channel 1...");
LoadConfig();
if (m_vecBossInfos.empty())
{
sys_err("WORLD_BOSS: No boss configurations loaded for Channel 1. Event will not start.");
m_currentState = STATE_INACTIVE;
return false; // Başarısız, false döndür
}
m_dwQuestBuffVnum = 70053;
if (m_currentState != STATE_COOLDOWN || m_tNextStateChangeTime <= time(0))
{
m_currentState = STATE_WAITING_FOR_SPAWN;
ScheduleNextSpawn();
sys_log(0, "WORLD_BOSS: Manager Initialized on Channel 1. State: WAITING_FOR_SPAWN. Next spawn in %d seconds.", (int)(m_tNextStateChangeTime - time(0)));
}
else
{
m_currentState = STATE_WAITING_FOR_SPAWN;
sys_log(0, "WORLD_BOSS: Manager was in COOLDOWN on Channel 1. Continuing to wait for next spawn in %d seconds.", (int)(m_tNextStateChangeTime - time(0)));
}
return true; // Başarılı
}
void CWorldBossManager::LoadConfig()
{
sys_log(0, "WORLD_BOSS: Loading configuration...");
m_vecBossInfos.clear();
m_mapRankRewards.clear();
// --- Ana Boss Tanımlaması (Test: Mavi Bayrak 1. Köy Başlangıç Noktası) ---
FWorldBossInfo main_boss;
main_boss.vnum = 101; // TEST İÇİN YABANİ KÖPEK VNUM'u
main_boss.name = "Başlangıç Test Köpeği"; // Test adı
main_boss.max_hp = 1000; // Test canı
// Mavi Bayrak (Shinsoo) Başlangıç Noktası (start_position.cpp'den alındı)
// Harita İndeksi: 1
// Dünya Koordinatları: (469300, 964200)
main_boss.spawn_points.push_back({1, 469300, 964200});
m_vecBossInfos.push_back(main_boss);
sys_log(0, "WORLD_BOSS: Loaded %u boss configuration(s). Boss: %s (VNUM %u) at Map Index %d (%ld, %ld)",
m_vecBossInfos.size(), main_boss.name.c_str(), main_boss.vnum,
main_boss.spawn_points[0].map_index, main_boss.spawn_points[0].x, main_boss.spawn_points[0].y);
// --- Test Ödül Tanımlaması (İsteğe bağlı, ihtiyaca göre düzenle) ---
FWorldBossRankReward reward_rank1_test;
reward_rank1_test.item_vnum = 71224;
reward_rank1_test.item_count = 1;
reward_rank1_test.ep_amount = 0;
m_mapRankRewards[1] = reward_rank1_test;
sys_log(0, "WORLD_BOSS: Loaded %u rank reward configuration(s).", m_mapRankRewards.size());
}
void CWorldBossManager::ScheduleNextSpawn()
{
if (m_vecBossInfos.empty())
{
sys_err("WORLD_BOSS: Cannot schedule next spawn, no boss info available.");
m_currentState = STATE_INACTIVE;
return;
}
DWORD random_interval = number(m_dwMinSpawnIntervalSeconds, m_dwMaxSpawnIntervalSeconds);
m_tNextStateChangeTime = time(0) + random_interval;
sys_log(0, "WORLD_BOSS: Next boss spawn scheduled in %u seconds (timestamp: %u).", random_interval, (unsigned int)m_tNextStateChangeTime);
}
void CWorldBossManager::Destroy()
{
if (m_pkCurrentBoss)
{
M2_DESTROY_CHARACTER(m_pkCurrentBoss);
m_pkCurrentBoss = NULL;
}
m_vecBossInfos.clear();
m_mapRankRewards.clear();
m_mapPlayerTotalDamage.clear();
m_mapPlayerNames.clear();
m_mapPlayerGuildNames.clear();
m_mapPlayerEmpires.clear();
m_vecCachedRanking.clear();
m_setPlayersRequestedRewardThisCycle.clear();
m_mapPlayerFightDuration.clear();
m_mapPlayerFirstHitTime.clear();
m_currentState = STATE_INACTIVE;
sys_log(0, "WORLD_BOSS: Manager Destroyed");
}
void CWorldBossManager::Update(DWORD dwPulse)
{
time_t current_time = time(0);
switch (m_currentState)
{
case STATE_INACTIVE:
break;
case STATE_WAITING_FOR_SPAWN:
if (current_time >= m_tNextStateChangeTime)
{
if (m_vecBossInfos.empty())
{
sys_err("WORLD_BOSS: No boss info to spawn. Resetting.");
ResetEventState(); // Ya da ScheduleNextSpawn() ile yeniden dene?
break;
}
const FWorldBossInfo& selected_boss_info = m_vecBossInfos[number(0, m_vecBossInfos.size() - 1)];
if (selected_boss_info.spawn_points.empty())
{
sys_err("WORLD_BOSS: Boss %s (VNUM %u) has no spawn points defined. Skipping.", selected_boss_info.name.c_str(), selected_boss_info.vnum);
ScheduleNextSpawn(); // Bir sonrakini planla
break;
}
const FWorldBossSpawnPoint& spawn_point = selected_boss_info.spawn_points[number(0, selected_boss_info.spawn_points.size() - 1)];
SpawnBossInternal(selected_boss_info.vnum, spawn_point.map_index, spawn_point.x, spawn_point.y);
}
break;
case STATE_BOSS_ACTIVE:
if (m_pkCurrentBoss)
{
if (current_time - m_tLastDamageTime >= m_dwBossNoDamageDespawnSeconds)
{
sys_log(0, "WORLD_BOSS: Boss %s (VID: %u) despawning due to no damage for %u seconds.", m_pkCurrentBoss->GetName(), (DWORD)(m_pkCurrentBoss->GetVID()), m_dwBossNoDamageDespawnSeconds);
HandleBossDespawn(true); // true -> kaçtı
}
else if (current_time - m_tBossSpawnTime >= m_dwBossActiveDurationSeconds)
{
sys_log(0, "WORLD_BOSS: Boss %s (VID: %u) despawning due to active duration timeout (%u seconds).", m_pkCurrentBoss->GetName(), (DWORD)(m_pkCurrentBoss->GetVID()), m_dwBossActiveDurationSeconds);
HandleBossDespawn(true); // true -> kaçtı
}
}
else
{
sys_err("WORLD_BOSS: State is BOSS_ACTIVE but m_pkCurrentBoss is NULL. Resetting.");
ResetEventState();
}
break;
case STATE_COOLDOWN:
if (current_time >= m_tNextStateChangeTime)
{
m_currentState = STATE_WAITING_FOR_SPAWN;
ScheduleNextSpawn();
sys_log(0, "WORLD_BOSS: Cooldown finished. State: WAITING_FOR_SPAWN. Next spawn in %d seconds.", (int)(m_tNextStateChangeTime - time(0)));
}
break;
}
}
void CWorldBossManager::ResetEventState()
{
if (m_pkCurrentBoss)
{
M2_DESTROY_CHARACTER(m_pkCurrentBoss);
m_pkCurrentBoss = NULL;
}
m_mapPlayerTotalDamage.clear();
m_mapPlayerNames.clear();
m_mapPlayerGuildNames.clear();
m_mapPlayerEmpires.clear();
m_vecCachedRanking.clear();
m_wLastRankingPlayerCount = 0;
m_dwCurrentBossVnum = 0;
m_setPlayersRequestedRewardThisCycle.clear();
m_mapPlayerFightDuration.clear();
m_mapPlayerFirstHitTime.clear();
m_currentState = STATE_WAITING_FOR_SPAWN;
ScheduleNextSpawn();
sys_log(0, "WORLD_BOSS: Event state has been reset. Next spawn in %d seconds.", (int)(m_tNextStateChangeTime - time(0)));
}
void CWorldBossManager::SpawnBossInternal(DWORD dwBossVnum, long lMapIndex, long x, long y)
{
if (m_pkCurrentBoss)
{
sys_err("WORLD_BOSS: Attempted to spawn a new boss while one is already active (VID: %u). Despawning old one first.", (DWORD)(m_pkCurrentBoss->GetVID()));
HandleBossDespawn(false); // false -> kaçmadı, sistem tarafından kaldırıldı
}
LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(lMapIndex);
if (!pkSectreeMap)
{
sys_err("WORLD_BOSS: Cannot spawn boss, map index %ld not found.", lMapIndex);
ScheduleNextSpawn(); // Hata oluştu, bir sonrakini planla
return;
}
m_pkCurrentBoss = CHARACTER_MANAGER::instance().SpawnMob(dwBossVnum, lMapIndex, x, y, 0, false, -1, true); // true for world boss flag
if (!m_pkCurrentBoss)
{
sys_err("WORLD_BOSS: Failed to spawn boss with VNUM %u at %ld (%ld, %ld).", dwBossVnum, lMapIndex, x, y);
ScheduleNextSpawn(); // Hata oluştu, bir sonrakini planla
return;
}
char szMapName[64] = "Bilinmeyen Harita";
const TMapRegion* pMapRegion = SECTREE_MANAGER::instance().GetMapRegion(lMapIndex);
if (pMapRegion)
{
strlcpy(szMapName, pMapRegion->strMapName.c_str(), sizeof(szMapName));
}
sys_log(0, "WORLD_BOSS: Spawned boss %s (VNUM %u, VID %u) at %s (%ld, %ld).", m_pkCurrentBoss->GetName(), dwBossVnum, (DWORD)(m_pkCurrentBoss->GetVID()), szMapName, x, y);
m_dwCurrentBossVnum = dwBossVnum;
m_currentBossSpawnPoint = { (int)lMapIndex, x, y };
m_tBossSpawnTime = time(0);
m_tLastDamageTime = time(0); // Hasar alınana kadar bu zaman kullanılacak
m_currentState = STATE_BOSS_ACTIVE;
m_mapPlayerTotalDamage.clear();
m_mapPlayerNames.clear();
m_mapPlayerGuildNames.clear();
m_mapPlayerEmpires.clear();
m_vecCachedRanking.clear();
m_wLastRankingPlayerCount = 0;
m_setPlayersRequestedRewardThisCycle.clear();
m_mapPlayerFightDuration.clear();
m_mapPlayerFirstHitTime.clear();
char buf[512];
snprintf(buf, sizeof(buf), LC_TEXT("Dünya Patronu %s, %s (%ld, %ld) bölgesinde belirdi!"), m_pkCurrentBoss->GetName(), szMapName, x/100, y/100);
Announce(buf, true);
}
void CWorldBossManager::SpawnBoss(DWORD dwBossVnum, long lMapIndex, long x, long y, long z) // z parametresi şimdilik kullanılmıyor
{
const FWorldBossInfo* bossInfo = GetBossInfo(dwBossVnum);
if (!bossInfo)
{
sys_err("WORLD_BOSS: SpawnBoss called with unknown VNUM %u", dwBossVnum);
return;
}
SpawnBossInternal(dwBossVnum, lMapIndex, x, y);
}
const FWorldBossInfo* CWorldBossManager::GetBossInfo(DWORD vnum) const
{
for (const auto& boss : m_vecBossInfos)
{
if (boss.vnum == vnum)
return &boss;
}
return nullptr;
}
void CWorldBossManager::HandleRequestUIInfo(LPCHARACTER ch)
{
if (!ch) return;
SendUIToPlayer(ch);
TPacketGCWorldBossRankingUpdate rankingPacket;
rankingPacket.header = HEADER_GC_WORLD_BOSS_RANKING_UPDATE;
rankingPacket.ranking_count = static_cast<BYTE>(m_vecCachedRanking.size());
if (rankingPacket.ranking_count > MAX_RANKING_ENTRIES_WB)
{
rankingPacket.ranking_count = MAX_RANKING_ENTRIES_WB;
}
for (WORD i = 0; i < rankingPacket.ranking_count; ++i)
{
rankingPacket.ranking_list[i] = m_vecCachedRanking[i];
}
rankingPacket.my_rank_in_list = 0;
rankingPacket.my_damage_for_rank = 0;
DWORD chID = ch->GetPlayerID();
auto itDamage = m_mapPlayerTotalDamage.find(chID);
if (itDamage != m_mapPlayerTotalDamage.end())
{
rankingPacket.my_damage_for_rank = itDamage->second;
}
for (WORD i = 0; i < m_vecCachedRanking.size(); ++i)
{
if (m_vecCachedRanking[i].player_id == chID)
{
rankingPacket.my_rank_in_list = m_vecCachedRanking[i].rank;
break;
}
}
if (rankingPacket.my_rank_in_list == 0 && rankingPacket.my_damage_for_rank > 0)
{
WORD actualRank = 0;
for(size_t i = 0; i < m_vecCachedRanking.size(); ++i) {
if(m_vecCachedRanking[i].player_id == chID) {
actualRank = m_vecCachedRanking[i].rank;
break;
}
}
rankingPacket.my_rank_in_list = actualRank;
}
ch->GetDesc()->Packet(&rankingPacket, sizeof(TPacketGCWorldBossRankingUpdate) - ( (MAX_RANKING_ENTRIES_WB - rankingPacket.ranking_count) * sizeof(TRankingEntryWB) ));
}
void CWorldBossManager::HandleClaimReward(LPCHARACTER ch)
{
if (!ch) return;
DWORD dwPlayerID = ch->GetPlayerID();
if (m_dwLastKilledBossVnumForReward == 0)
{
SendRewardResultPacket(ch, REWARD_RESULT_NO_BOSS_KILLED);
return;
}
if (time(0) > m_tLastBossKillTime + (60 * 60))
{
SendRewardResultPacket(ch, REWARD_RESULT_NONE_AVAILABLE_OR_EXPIRED);
return;
}
if (m_setPlayersRequestedRewardThisCycle.count(dwPlayerID))
{
SendRewardResultPacket(ch, REWARD_RESULT_ALREADY_REQUESTED_THIS_CYCLE);
return;
}
WORD playerRank = 0;
bool bFoundInRanking = false;
for (const auto& entry : m_vecCachedRanking)
{
if (entry.player_id == dwPlayerID)
{
playerRank = entry.rank;
bFoundInRanking = true;
break;
}
}
if (!bFoundInRanking || playerRank == 0)
{
SendRewardResultPacket(ch, REWARD_RESULT_NOT_ELIGIBLE);
return;
}
auto itReward = m_mapRankRewards.find(playerRank);
if (itReward == m_mapRankRewards.end())
{
SendRewardResultPacket(ch, REWARD_RESULT_NOT_ELIGIBLE);
return;
}
TPacketGDWorldBossClaimRewardRequest packetDB;
packetDB.header = HEADER_GD_WORLD_BOSS_CLAIM_REWARD_REQUEST;
packetDB.player_id = dwPlayerID;
packetDB.boss_vnum_killed = m_dwLastKilledBossVnumForReward;
packetDB.rank = playerRank;
LPDESC dbDesc = db_clientdesc;
if (!dbDesc)
{
sys_err("WORLD_BOSS: No DB descriptor found to send claim reward request.");
SendRewardResultPacket(ch, REWARD_RESULT_FAILED);
return;
}
dbDesc->Packet(&packetDB, sizeof(TPacketGDWorldBossClaimRewardRequest));
m_setPlayersRequestedRewardThisCycle.insert(dwPlayerID);
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ödül isteğiniz işleniyor..."));
}
void CWorldBossManager::KillBoss(LPCHARACTER pkKiller)
{
if (!m_pkCurrentBoss || m_pkCurrentBoss != pkKiller)
{
if (pkKiller && m_pkCurrentBoss && m_pkCurrentBoss->GetVID() != pkKiller->GetVID()) {
sys_err("WORLD_BOSS: KillBoss called for a different boss (Caller VID: %u, Current Boss VID: %u). Ignoring.", (DWORD)pkKiller->GetVID(), (DWORD)m_pkCurrentBoss->GetVID());
return;
}
if (!m_pkCurrentBoss) {
sys_log(0, "WORLD_BOSS: KillBoss called but no current boss is active.");
return;
}
}
sys_log(0, "WORLD_BOSS: Boss %s (VNUM %u, VID %u) has been killed.", m_pkCurrentBoss->GetName(), m_pkCurrentBoss->GetRaceNum(), (DWORD)m_pkCurrentBoss->GetVID());
LPCHARACTER pkLastAttacker = m_pkCurrentBoss->GetVictim();
if (pkLastAttacker && pkLastAttacker->IsPC())
{
Announce(LC_TEXT("Dünya Patronu %s, %s tarafından yenildi!"), true, true, m_pkCurrentBoss->GetName(), pkLastAttacker->GetName());
}
else
{
Announce(LC_TEXT("Dünya Patronu %s yenildi!"), true, true, m_pkCurrentBoss->GetName());
}
UpdateRankingCache();
m_dwLastKilledBossVnumForReward = m_pkCurrentBoss->GetRaceNum();
m_tLastBossKillTime = time(0);
m_setPlayersRequestedRewardThisCycle.clear();
m_pkCurrentBoss = NULL;
m_dwCurrentBossVnum = 0;
m_currentState = STATE_COOLDOWN;
DWORD cooldown_duration = m_dwEventCooldownSeconds;
m_tNextStateChangeTime = time(0) + cooldown_duration;
sys_log(0, "WORLD_BOSS: Event entering COOLDOWN state for %u seconds.", cooldown_duration);
sys_log(0, "WORLD_BOSS: Next event cycle will start in %u seconds (at %u).", cooldown_duration + number(m_dwMinSpawnIntervalSeconds, m_dwMaxSpawnIntervalSeconds), (unsigned int)(m_tNextStateChangeTime + number(m_dwMinSpawnIntervalSeconds, m_dwMaxSpawnIntervalSeconds)));
Announce(LC_TEXT("Sıralamaya giren oyuncular ödüllerini Dünya Patronu (F8) arayüzünden alabilirler."), false);
}
void CWorldBossManager::HandleBossDespawn(bool bEscaped)
{
if (!m_pkCurrentBoss)
{
sys_err("WORLD_BOSS: HandleBossDespawn called but no boss is active.");
ResetEventState();
return;
}
sys_log(0, "WORLD_BOSS: Boss %s (VNUM %u, VID %u) is despawning (Escaped: %s).", m_pkCurrentBoss->GetName(), m_pkCurrentBoss->GetRaceNum(), (DWORD)m_pkCurrentBoss->GetVID(), bEscaped ? "Yes" : "No");
if (bEscaped)
{
Announce(LC_TEXT("Dünya Patronu %s kaçtı! Bir dahaki sefere daha hızlı olmalısınız!"), true, true, m_pkCurrentBoss->GetName());
m_dwLastKilledBossVnumForReward = 0;
m_tLastBossKillTime = 0;
}
else
{
// Manuel despawn (örn: GM komutu, server kapanışı vb.)
}
UpdateRankingCache();
M2_DESTROY_CHARACTER(m_pkCurrentBoss);
m_pkCurrentBoss = NULL;
m_dwCurrentBossVnum = 0;
m_currentState = STATE_COOLDOWN;
DWORD cooldown_duration = m_dwEventCooldownSeconds;
m_tNextStateChangeTime = time(0) + cooldown_duration;
sys_log(0, "WORLD_BOSS: Event entering COOLDOWN state for %u seconds.", cooldown_duration);
sys_log(0, "WORLD_BOSS: Next event cycle will start in %u seconds (at %u).", cooldown_duration + number(m_dwMinSpawnIntervalSeconds, m_dwMaxSpawnIntervalSeconds), (unsigned int)(m_tNextStateChangeTime + number(m_dwMinSpawnIntervalSeconds, m_dwMaxSpawnIntervalSeconds)));
}
void CWorldBossManager::OnBossDamaged(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int iDamage)
{
if (!pkAttacker || !pkVictim || !m_pkCurrentBoss || pkVictim->GetVID() != m_pkCurrentBoss->GetVID() || iDamage <= 0)
return;
if (!pkAttacker->IsPC())
return;
DWORD dwPlayerID = pkAttacker->GetPlayerID();
bool is_first_hit_for_player = (m_mapPlayerTotalDamage.find(dwPlayerID) == m_mapPlayerTotalDamage.end());
m_mapPlayerTotalDamage[dwPlayerID] += iDamage;
m_tLastDamageTime = time(0);
if (is_first_hit_for_player)
{
m_mapPlayerNames[dwPlayerID] = pkAttacker->GetName();
m_mapPlayerEmpires[dwPlayerID] = pkAttacker->GetEmpire();
m_mapPlayerFirstHitTime[dwPlayerID] = time(0);
CGuild* pGuild = pkAttacker->GetGuild();
if (pGuild)
{
m_mapPlayerGuildNames[dwPlayerID] = pGuild->GetName();
}
else
{
m_mapPlayerGuildNames[dwPlayerID] = "";
}
if (m_mapPlayerTotalDamage.size() == 1)
{
Announce(LC_TEXT("%s, Dünya Patronu %s'e ilk darbeyi vurdu! Savaş başladı!"), false, true, pkAttacker->GetName(), m_pkCurrentBoss->GetName());
}
}
SendUIToPlayer(pkAttacker);
UpdateRankingCache();
}
void CWorldBossManager::UpdateRankingCache()
{
m_vecCachedRanking.clear();
std::vector<std::pair<DWORD, long long>> sorted_damage(m_mapPlayerTotalDamage.begin(), m_mapPlayerTotalDamage.end());
std::sort(sorted_damage.begin(), sorted_damage.end(), [](const auto& a, const auto& b) {
return a.second > b.second;
});
WORD rank_counter = 1;
for (const auto& pair_entry : sorted_damage)
{
TRankingEntryWB entry;
entry.rank = rank_counter++;
entry.player_id = pair_entry.first;
entry.total_damage = pair_entry.second;
auto itName = m_mapPlayerNames.find(entry.player_id);
if (itName != m_mapPlayerNames.end())
strlcpy(entry.player_name, itName->second.c_str(), sizeof(entry.player_name));
else
strlcpy(entry.player_name, "Bilinmiyor", sizeof(entry.player_name));
auto itGuild = m_mapPlayerGuildNames.find(entry.player_id);
if (itGuild != m_mapPlayerGuildNames.end() && !itGuild->second.empty())
strlcpy(entry.guild_name, itGuild->second.c_str(), sizeof(entry.guild_name));
else
strlcpy(entry.guild_name, "", sizeof(entry.guild_name));
auto itEmpire = m_mapPlayerEmpires.find(entry.player_id);
if (itEmpire != m_mapPlayerEmpires.end())
entry.empire = itEmpire->second;
else
entry.empire = 0;
m_vecCachedRanking.push_back(entry);
}
m_wLastRankingPlayerCount = m_vecCachedRanking.size();
}
void CWorldBossManager::SendUIToPlayer(LPCHARACTER ch)
{
if (!ch || !ch->GetDesc()) return;
TPacketGCWorldBossUIUpdate packet;
packet.header = HEADER_GC_WORLD_BOSS_UI_UPDATE;
if (m_pkCurrentBoss && m_currentState == STATE_BOSS_ACTIVE)
{
packet.boss_vnum = m_pkCurrentBoss->GetRaceNum();
packet.boss_state = 1;
packet.boss_next_spawn_cooldown_seconds = 0;
packet.fight_or_escape_remaining_time_seconds = (m_tBossSpawnTime + m_dwBossActiveDurationSeconds) - time(0);
if (packet.fight_or_escape_remaining_time_seconds < 0) packet.fight_or_escape_remaining_time_seconds = 0;
auto itDamage = m_mapPlayerTotalDamage.find(ch->GetPlayerID());
if (itDamage != m_mapPlayerTotalDamage.end())
{
packet.my_total_damage_on_current_boss = itDamage->second;
}
else
{
packet.my_total_damage_on_current_boss = 0;
}
auto itFightTime = m_mapPlayerFirstHitTime.find(ch->GetPlayerID());
if (itFightTime != m_mapPlayerFirstHitTime.end()) {
packet.my_fight_duration_seconds_on_current_boss = time(0) - itFightTime->second;
} else {
packet.my_fight_duration_seconds_on_current_boss = 0;
}
}
else if (m_currentState == STATE_WAITING_FOR_SPAWN || m_currentState == STATE_COOLDOWN)
{
packet.boss_vnum = 0;
packet.boss_state = (m_currentState == STATE_WAITING_FOR_SPAWN) ? 2 : 3;
packet.fight_or_escape_remaining_time_seconds = (m_tNextStateChangeTime > time(0)) ? (m_tNextStateChangeTime - time(0)) : 0;
packet.my_total_damage_on_current_boss = 0;
packet.my_fight_duration_seconds_on_current_boss = 0;
}
else
{
packet.boss_vnum = 0;
packet.boss_state = 0;
packet.fight_or_escape_remaining_time_seconds = 0;
packet.my_total_damage_on_current_boss = 0;
packet.my_fight_duration_seconds_on_current_boss = 0;
}
packet.quest_min_damage_requirement = m_dwMinDamageForQuest;
packet.can_claim_reward_for_last_killed_boss = 0;
if (m_dwLastKilledBossVnumForReward != 0 && time(0) <= m_tLastBossKillTime + (60*60) )
{
for (const auto& entry : m_vecCachedRanking)
{
if (entry.player_id == ch->GetPlayerID())
{
if (m_mapRankRewards.count(entry.rank))
{
packet.can_claim_reward_for_last_killed_boss = 1;
}
break;
}
}
}
ch->GetDesc()->Packet(&packet, sizeof(packet));
}
void CWorldBossManager::Announce(const char* message, bool bIsBigNotice /*= false*/, bool bIsLocale /*= false*/, ...)
{
char chatbuf[CHAT_MAX_LEN + 1];
va_list args;
va_start(args, bIsLocale);
vsnprintf(chatbuf, sizeof(chatbuf), message, args);
va_end(args);
if (bIsLocale)
{
// Bu durumda chatbuf zaten formatlanmış stringi içermeli.
}
if (bIsBigNotice)
{
SendNoticeMap(chatbuf, 0, true);
}
else
{
SendNoticeMap(chatbuf, 0, false);
}
sys_log(0, "WORLD_BOSS_ANNOUNCE: %s", chatbuf);
}
void CWorldBossManager::SendRewardResultPacket(LPCHARACTER ch, BYTE bResultCode, DWORD dwItemVnum /*= 0*/, BYTE bItemCount /*= 0*/)
{
if (!ch || !ch->GetDesc()) return;
TPacketGCWorldBossRewardResult packet;
packet.header = HEADER_GC_WORLD_BOSS_REWARD_RESULT;
packet.result_code = bResultCode;
packet.reward_item_vnum = dwItemVnum;
packet.reward_item_count = bItemCount;
switch (bResultCode)
{
case REWARD_RESULT_SUCCESS:
strlcpy(packet.message, LC_TEXT("Ödül başarıyla alındı!"), sizeof(packet.message));
break;
case REWARD_RESULT_ALREADY_CLAIMED:
strlcpy(packet.message, LC_TEXT("Bu boss için ödül zaten alındı."), sizeof(packet.message));
break;
case REWARD_RESULT_FAILED:
strlcpy(packet.message, LC_TEXT("Ödül alınırken bir hata oluştu."), sizeof(packet.message));
break;
case REWARD_RESULT_NONE_AVAILABLE_OR_EXPIRED:
strlcpy(packet.message, LC_TEXT("Alınabilecek bir ödül yok veya ödül süresi dolmuş."), sizeof(packet.message));
break;
case REWARD_RESULT_NOT_ELIGIBLE:
strlcpy(packet.message, LC_TEXT("Sıralamanız bu ödül için yeterli değil."), sizeof(packet.message));
break;
case REWARD_RESULT_ALREADY_REQUESTED_THIS_CYCLE:
strlcpy(packet.message, LC_TEXT("Ödül isteğiniz zaten işleniyor, lütfen bekleyin."), sizeof(packet.message));
break;
case REWARD_RESULT_CANNOT_CLAIM_YET:
strlcpy(packet.message, LC_TEXT("Henüz ödül alamazsınız (örn: boss ölmedi)."), sizeof(packet.message));
break;
case REWARD_RESULT_NO_BOSS_KILLED:
strlcpy(packet.message, LC_TEXT("Ödül alınabilecek aktif bir boss kesimi bulunmuyor."), sizeof(packet.message));
break;
default:
strlcpy(packet.message, LC_TEXT("Bilinmeyen bir durum oluştu."), sizeof(packet.message));
break;
}
ch->GetDesc()->Packet(&packet, sizeof(packet));
}
void CWorldBossManager::HandlePacket_DB_ClaimRewardResult(LPCHARACTER ch, TPacketDGWorldBossClaimRewardResult* pPacketInfo)
{
if (!ch || !pPacketInfo) return;
DWORD dwPlayerID = ch->GetPlayerID();
if (pPacketInfo->player_id != dwPlayerID)
{
sys_err("WORLD_BOSS: DB Claim Result for PID %u but current char is %u (%s)", pPacketInfo->player_id, dwPlayerID, ch->GetName());
return;
}
m_setPlayersRequestedRewardThisCycle.erase(dwPlayerID);
sys_log(0, "WORLD_BOSS: DB Claim Reward Result for PID %u. Result: %u", dwPlayerID, pPacketInfo->result_code);
if (pPacketInfo->result_code == REWARD_RESULT_SUCCESS)
{
WORD playerRank = 0;
for (const auto& entry : m_vecCachedRanking)
{
if (entry.player_id == dwPlayerID)
{
playerRank = entry.rank;
break;
}
}
if (playerRank > 0)
{
auto itReward = m_mapRankRewards.find(playerRank);
if (itReward != m_mapRankRewards.end())
{
const FWorldBossRankReward& reward = itReward->second;
if (reward.item_vnum > 0 && reward.item_count > 0)
{
ch->AutoGiveItem(reward.item_vnum, reward.item_count);
sys_log(0, "WORLD_BOSS: Gave item %u (count %u) to player %s (PID %u) for rank %u.", reward.item_vnum, reward.item_count, ch->GetName(), dwPlayerID, playerRank);
}
if (reward.ep_amount > 0)
{
sys_log(0, "WORLD_BOSS: (TODO) Gave %u EP to player %s (PID %u) for rank %u.", reward.ep_amount, ch->GetName(), dwPlayerID, playerRank);
}
SendRewardResultPacket(ch, REWARD_RESULT_SUCCESS, reward.item_vnum, reward.item_count);
if (m_mapPlayerTotalDamage[dwPlayerID] >= m_dwMinDamageForQuest && m_dwQuestBuffVnum > 0)
{
ch->AddAffect(AFFECT_WORLD_BOSS_BUFF, POINT_NONE, 0, AFF_WORLD_BOSS_BUFF, m_dwQuestBuffDurationSeconds, 0, true);
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Dünya Patronu görevini tamamladın ve özel bir güç kazandın!"));
}
return;
}
}
sys_err("WORLD_BOSS: DB returned SUCCESS for PID %u but no valid rank/reward found in game server.", dwPlayerID);
SendRewardResultPacket(ch, REWARD_RESULT_FAILED);
}
else
{
SendRewardResultPacket(ch, pPacketInfo->result_code);
}
}
DWORD CWorldBossManager::GetCurrentBossVID() const
{
if (m_pkCurrentBoss && m_currentState == STATE_BOSS_ACTIVE)
{
return m_pkCurrentBoss->GetVID();
}
return 0;
}
game/syserr
game/syserr:
SYSERR: May 23 23:00:00 :: pid_init:
Start of pid: 3533
SYSERR: May 23 23:01:02 :: SpawnBossInternal: WORLD_BOSS: Failed to spawn boss with VNUM 101 at 4 (3110, 7256).
SYSERR: May 23 23:02:15 :: SpawnBossInternal: WORLD_BOSS: Failed to spawn boss with VNUM 101 at 4 (3110, 7256).
SYSERR: May 23 23:03:22 :: SpawnBossInternal: WORLD_BOSS: Failed to spawn boss with VNUM 101 at 4 (3110, 7256).
SYSERR: May 23 23:05:08 :: SpawnBossInternal: WORLD_BOSS: Failed to spawn boss with VNUM 101 at 4 (3110, 7256).
SYSERR: May 23 23:06:17 :: SpawnBossInternal: WORLD_BOSS: Failed to spawn boss with VNUM 101 at 4 (3110, 7256).
SYSERR: May 23 23:07:18 :: SpawnBossInternal: WORLD_BOSS: Failed to spawn boss with VNUM 101 at 4 (3110, 7256).
SYSERR: May 23 23:08:54 :: SpawnBossInternal: WORLD_BOSS: Failed to spawn boss with VNUM 101 at 4 (3110, 7256).
SYSERR: May 23 23:10:42 :: SpawnBossInternal: WORLD_BOSS: Failed to spawn boss with VNUM 101 at 4 (3110, 7256).
SYSERR: May 23 23:11:09 :: hupsig: SIGHUP, SIGINT, SIGTERM signal has been received. shutting down.
SYSERR: May 23 23:11:11 :: pid_deinit:
End of pid
SYSERR: May 23 23:12:07 :: pid_init:
Start of pid: 3618