Kill Streak Sistemi – Mantık
Oyuncu art arda kill aldıkça:
Lakap kazanır (isim önünde görünür)
Kritik vuruş 'ı alır
Tüm sunucuya anons geçebilir
Log kaydı oluşur (kill.txt)
GM'ler lakap verebilir sıfırlayabilir kill streak'i sıfırlayabilir
Kill Sayısı - Ödüller:
[TABLE=full][TR]<th>Kill</th><th>Lakap</th><th>Kritik</th><th>Anons</th>[/TR][TR][TD]5[/TD][TD]Savaşçı[/TD][TD]%3[/TD][TD]
Abuse Engelleme:
Aşağıdaki durumlarda kill sayılmaz
-
Aynı IP’den giriş yapan 2 karakter
Aynı lonca içindelerse
Arkadaş listesinde birbirleri varsa
Aynı Karakterden sadece 2 kez kill alabilirsin 3.kez kill aldıktan sonra kill sayılmaz
Ölüm Sonrası:
Kill streak tamamen sıfırlanır
Kritik silinir
Lakap temizlenir
Bilgilendirme mesajı gider
Loglama:
Her kill sonrası şu satır, şu dosyaya yazılır:/usr/game/locale/turkey/kill.txt
Duyurular:
Şu kill sayılarında tüm oyunculara duyuru gönderilir:10 kill: [BILDIRI] ITJA 10 kişiyi üst üste yok etti! [Katliamcı]
20 kill: [BILDIRI]ITJA 20 kill'e ulaştı! [İnfazcı]
30 kill: [BILDIRI] ITJA artık bir efsane! 30 kill! [Efsane Katil]
GM Komutları:
[TABLE=full][TR]<th>Komut</th><th>Açıklama</th>[/TR][TR][TD]/lakapver ITJAKatliamcı[/TD][TD]Oyuncuya manuel lakap verir[/TD][/TR][TR][TD]/lakapsil ITJA[/TD][TD]Oyuncunun lakabını siler[/TD][/TR][TR][TD]/killreset ITJA[/TD][TD]Kill streak + buff + lakap sıfırlar[/TD][/TR][/TABLE]
UI Tarafı – Top Kill Sıralama Penceresi
F10 tuşuyla sıralama açılır. Python tarafında uiTopKill.py penceresi açılır Server /topkill komutuyla sıralama gönderir
Oyuncu 30 Kill Yapmışsa Eğer
Test Senaryosu:
[TABLE=full][TR]<th>Durum</th><th>Sonuç</th>[/TR][TR][TD]30 kill'den az[/TD][TD]Komut reddedilir[/TD][/TR][TR][TD]Geçersiz Lakap[/TD][TD]Komut reddedilir[/TD][/TR][TR][TD]Daha önce kullanmışsa[/TD][TD]“Sadece 1 kez” hatası[/TD][/TR][TR][TD]Tüm şartlar uygunsa[/TD][TD]lakap atanır, tekrar kullanılamaz[/TD][/TR][TR][TD]/unvansec SavaşKralı[/TD][TD]Başarılı işlem → [SavaşKralı] ITJA görünür[/TD][/TR][/TABLE]
char.h → class CHARACTER içine (public
void IncreaseKillStreak(CHARACTER* pkVictim);
void ResetKillStreak();
const char* GetKillTitle() const;
bool IsFriendByVID(DWORD dwVID);
std::string GetDisplayedName() const;
int m_iKillStreak;
DWORD m_dwLastKilledVID;
std::string m_strKillTitle;
char.h → class CHARACTER içine (public):
bool m_bUsedCustomTitle;
bool HasUsedCustomTitle() const { return m_bUsedCustomTitle; }
void SetUsedCustomTitle(bool val) { m_bUsedCustomTitle = val; }
char.cpp → Karakter constructor'una ekle (başlangıç değeri):
CHARACTER::CHARACTER()
{
...
m_bUsedCustomTitle = false;
}
char.cpp → Dosyanın en sonuna (en altına) EKLE:
void CHARACTER::IncreaseKillStreak(CHARACTER* pkVictim)
{
if (pkVictim)
m_dwLastKilledVID = pkVictim->GetVID();
++m_iKillStreak;
if (m_iKillStreak == 5)
{
m_strKillTitle = "Savaşçı";
ChatPacket(CHAT_TYPE_INFO, "5 kill! [Savaşçı] unvanı kazandın!");
AddAffect(AFFECT_CRITICAL, POINT_CRITICAL_PCT, 3, AFF_NONE, INFINITE_AFFECT_DURATION, 0, true);
}
else if (m_iKillStreak == 10)
{
m_strKillTitle = "Katliamcı";
ChatPacket(CHAT_TYPE_INFO, "10 kill! [Katliamcı] unvanı kazandın!");
AddAffect(AFFECT_CRITICAL, POINT_CRITICAL_PCT, 6, AFF_NONE, INFINITE_AFFECT_DURATION, 0, true);
}
else if (m_iKillStreak == 15)
{
m_strKillTitle = "Yıkım Gücü";
ChatPacket(CHAT_TYPE_INFO, "15 kill! [Yıkım Gücü] unvanı kazandın!");
AddAffect(AFFECT_CRITICAL, POINT_CRITICAL_PCT, 9, AFF_NONE, INFINITE_AFFECT_DURATION, 0, true);
}
else if (m_iKillStreak == 20)
{
m_strKillTitle = "İnfazcı";
ChatPacket(CHAT_TYPE_INFO, "20 kill! [İnfazcı] unvanı kazandın!");
AddAffect(AFFECT_CRITICAL, POINT_CRITICAL_PCT, 12, AFF_NONE, INFINITE_AFFECT_DURATION, 0, true);
}
else if (m_iKillStreak == 30)
{
m_strKillTitle = "Efsane Katil";
ChatPacket(CHAT_TYPE_INFO, "30 kill! [Efsane Katil] unvanı kazandın!");
AddAffect(AFFECT_CRITICAL, POINT_CRITICAL_PCT, 15, AFF_NONE, INFINITE_AFFECT_DURATION, 0, true);
}
}
void CHARACTER::ResetKillStreak()
{
if (m_iKillStreak >= 5)
ChatPacket(CHAT_TYPE_INFO, "Kill serin sona erdi. (%d kill)", m_iKillStreak);
m_iKillStreak = 0;
m_dwLastKilledVID = 0;
m_strKillTitle.clear();
RemoveAffect(AFFECT_CRITICAL);
}
const char* CHARACTER::GetKillTitle() const
{
return m_strKillTitle.c_str();
}
bool CHARACTER::IsFriendByVID(DWORD dwVID)
{
if (!GetDesc()) return false;
CMessengerManager* pMessenger = CMessengerManager::instance().Get(GetName());
return (pMessenger && pMessenger->IsFriendByVID(dwVID));
}
std::string CHARACTER::GetDisplayedName() const
{
if (!m_strKillTitle.empty())
return m_strKillTitle + " " + m_stName;
return m_stName;
}
battle.cpp → void battle_end_ex(LPCHARACTER ch) içine:
LPCHARACTER victim = ch->GetVictim();
if (ch && victim && ch->IsPC() && victim->IsPC())
{
if (ch->GetDesc() && victim->GetDesc() &&
strcmp(ch->GetDesc()->GetHostName(), victim->GetDesc()->GetHostName()) == 0)
return;
if (ch->GetGuild() && victim->GetGuild() &&
ch->GetGuild() == victim->GetGuild())
return;
if (ch->IsFriendByVID(victim->GetVID()))
return;
ch->IncreaseKillStreak(victim);
victim->ResetKillStreak();
char.cpp → Fonksiyon tanımı (sonuna ekle):
void CHARACTER::SetKillTitle(const std::string& title){
m_strKillTitle = title;
ChatPacket(CHAT_TYPE_INFO, "Yeni unvanınız: [%s]", title.c_str());
}
cmd_general.cpp → En uygun yere
ACMD(do_lakapver)
{
char arg1[256], arg2[256];
one_argument(two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)), arg2, sizeof(arg2));
if (!*arg1 || !*arg2)
{
ch->ChatPacket(CHAT_TYPE_INFO, "Kullanım: /lakapver [oyuncu_adı] [lakap]");
return;
}
LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg1);
if (!tch)
{
ch->ChatPacket(CHAT_TYPE_INFO, "Oyuncu bulunamadı: %s", arg1);
return;
}
tch->SetKillTitle(arg2);
ch->ChatPacket(CHAT_TYPE_INFO, "%s adlı oyuncuya [%s] lakabı verildi.", tch->GetName(), arg2);
}
cmd.cpp
{ "lakapver", do_lakapver, GM_HIGH_WIZARD },
char.cpp
void CHARACTER::SetKillTitle(const std::string& title)
{
m_strKillTitle = title;
if (title.empty())
ChatPacket(CHAT_TYPE_INFO, "Unvanın sıfırlandı.");
else
ChatPacket(CHAT_TYPE_INFO, "Yeni unvan: [%s]", title.c_str());
}
cmd_general.cpp
ACMD(do_lakapsil)
{
char arg1[256];
one_argument(argument, arg1, sizeof(arg1));
if (!*arg1)
{
ch->ChatPacket(CHAT_TYPE_INFO, "Kullanım: /lakapsil [oyuncu_adı]");
return;
}
LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg1);
if (!tch)
{
ch->ChatPacket(CHAT_TYPE_INFO, "Oyuncu bulunamadı: %s", arg1);
return;
}
tch->SetKillTitle("");
ch->ChatPacket(CHAT_TYPE_INFO, "%s adlı oyuncunun lakabı sıfırlandı.", tch->GetName());
}
Komut listesine ekle cmd.cpp
{ "lakapsil", do_lakapsil, GM_HIGH_WIZARD },
char.cpp → IncreaseKillStreak() Fonksiyonuna ŞUNU EKLE:
if (m_iKillStreak == 10)
{
m_strKillTitle = "Katliamcı";
ChatPacket(CHAT_TYPE_INFO, "10 kill! [Katliamcı] unvanı kazandın!");
AddAffect(AFFECT_CRITICAL, POINT_CRITICAL_PCT, 6, AFF_NONE, INFINITE_AFFECT_DURATION, 0, true);
BroadcastNotice(fmt::format("{} 10 kişiyi üst üste yok etti! [Katliamcı]", GetName()));
}
else if (m_iKillStreak == 20)
{
m_strKillTitle = "İnfazcı";
ChatPacket(CHAT_TYPE_INFO, "20 kill! [İnfazcı] unvanı kazandın!");
AddAffect(AFFECT_CRITICAL, POINT_CRITICAL_PCT, 12, AFF_NONE, INFINITE_AFFECT_DURATION, 0, true);
BroadcastNotice(fmt::format("{} 20 kill'e ulaştı! [İnfazcı]", GetName()));
}
else if (m_iKillStreak == 30)
{
m_strKillTitle = "Efsane Katil";
ChatPacket(CHAT_TYPE_INFO, "30 kill! [Efsane Katil] unvanı kazandın!");
AddAffect(AFFECT_CRITICAL, POINT_CRITICAL_PCT, 15, AFF_NONE, INFINITE_AFFECT_DURATION, 0, true);
BroadcastNotice(fmt::format("{} artık bir efsane! 30 kill yaptı! [Efsane Katil]", GetName()));
}
char.cpp → IncreaseKillStreak() fonksiyonu içine ŞUNU EKLE:
FILE* f = fopen("/usr/game/locale/turkey/kill.txt", "a+");
if (f)
{
fprintf(f, "[KillStreak] [%s] %s → %s | Seri: %d\n",
m_strKillTitle.c_str(),
GetName(),
pkVictim ? pkVictim->GetName() : "???",
m_iKillStreak);
fclose(f);
char.cpp → IncreaseKillStreak() içine ŞU SATIRI EKLE:
sys_log(0, "[KillStreak] %s(%s) → %s | Seri: %d",
GetName(), m_strKillTitle.c_str(),
pkVictim ? pkVictim->GetName() : "???",
m_iKillStreak);
cmd_general.cpp
ACMD(do_killreset)
{
char arg1[256];
one_argument(argument, arg1, sizeof(arg1));
if (!*arg1)
{
ch->ChatPacket(CHAT_TYPE_INFO, "Kullanım: /killreset [oyuncu_adi]");
return;
}
LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg1);
if (!tch)
{
ch->ChatPacket(CHAT_TYPE_INFO, "Oyuncu bulunamadı.");
return;
}
tch->ResetKillStreak();
tch->SetKillTitle(""); // Eğer varsa lakabı temizle
ch->ChatPacket(CHAT_TYPE_INFO, "%s adlı oyuncunun kill serisi ve unvanı sıfırlandı.", tch->GetName());
}
cmd.cpp
{ "killreset", do_killreset, GM_HIGH_WIZARD },
1. cmd_general.cpp
ACMD(do_topkill)
{
std::vector<CHARACTER*> players;
CHARACTER_MANAGER::instance().for_each_pc([&](CHARACTER* ch) {
if (ch->GetKillStreak() >= 1)
players.push_back(ch);
});
std::sort(players.begin(), players.end(), [*](CHARACTER* a, CHARACTER* b) {
return a->GetKillStreak() > b->GetKillStreak();
});
std::string result = "SetTopKillList";
int count = 0;
for (CHARACTER* ch : players)
{
if (++count > 10)
break;
char line[128];
snprintf(line, sizeof(line), "|%s|%d|%s",
ch->GetName(),
ch->GetKillStreak(),
ch->GetKillTitle());
result += line;
}
// Python tarafına sıralamayı gönderiyoruz:
ch->ChatPacket(CHAT_TYPE_COMMAND, result.c_str());
}
cmd.cpp
{ "topkill", do_topkill, GM_PLAYER },
cmd_general.cpp
ACMD(do_unvansec)
{
char arg1[256];
one_argument(argument, arg1, sizeof(arg1));
if (!*arg1)
{
ch->ChatPacket(CHAT_TYPE_INFO, "Kullanım: /unvansec [Unvan]");
ch->ChatPacket(CHAT_TYPE_INFO, "Mevcut Unvanlar: Efsane, SavaşKralı, İntikamcı");
return;
}
if (ch->HasUsedCustomTitle())
{
ch->ChatPacket(CHAT_TYPE_INFO, "Bu komutu daha önce kullandın. Sadece 1 kez kullanılabilir.");
return;
}
if (ch->GetKillStreak() < 30)
{
ch->ChatPacket(CHAT_TYPE_INFO, "Bu komutu kullanmak için en az 30 kill yapmalısın!");
return;
}
std::string title(arg1);
std::vector<std::string> allowed = { "Efsane", "SavaşKralı", "İntikamcı" };
if (std::find(allowed.begin(), allowed.end(), title) == allowed.end())
{
ch->ChatPacket(CHAT_TYPE_INFO, "Bu unvan seçilemez. Geçerli unvanlar: Efsane, SavaşKralı, İntikamcı");
return;
}
ch->SetKillTitle(title);
ch->SetUsedCustomTitle(true); //
ch->ChatPacket(CHAT_TYPE_INFO, "[%s] unvanı başarıyla seçildi! Bu hak tek kullanımlıktır.", arg1);
}
cmd.cpp
{ "unvansec", do_unvansec, GM_PLAYER },
cmd.cpp
ACMD Tarafına:
ACMD(do_unvansec);
ACMD(do_killreset);
ACMD(do_topkill);
ACMD(do_lakapsil);
ACMD(do_lakapver);
Pyhton Ui Sıralama
interfaceModule.py Pencere Tanımı
Kod:
import uiTopKill # en üste ekle [B][COLOR=rgb(184, 49, 47)]class Interface içinde bu fonksiyonu EKLE:[/COLOR][/B] [CODE=python]def __MakeTopKillWindow(self): self.wndTopKill = uiTopKill.TopKillWindow() self.wndTopKill.Hide()
class Interface içinde bu fonksiyonu EKLE
Kod:
def __MakeTopKillWindow(self): self.wndTopKill = uiTopKill.TopKillWindow() self.wndTopKill.Hide()
def MakeInterface(self): fonksiyonunun içine ŞU SATIRI EKLE:
Kod:
self.__MakeTopKillWindow()
def CloseAllWindows(self): içine ekle (pencereyi kapatmak için)
Kod:
if self.wndTopKill: self.wndTopKill.Hide()
game.py içine şunu ekle
Kod:
elif line.startswith("SetTopKillList"): import constInfo parts = line.split("|")[1:] constInfo.KILL_LIST = [] for entry in parts: vals = entry.split("\n") for val in vals: if val: name, kill, title = val.split("|") constInfo.KILL_LIST.append((name, int(kill), title))
[TABLE=full][TR][TD][/TD][TD][/TD][/TR][/TABLE]
Kod:
self.__MakeTopKillWindow()
def CloseAllWindows(self): içine ekle (pencereyi kapatmak için)
Kod:
if self.wndTopKill: self.wndTopKill.Hide()
uiTopKill.py
pack/root/ içine at
topkillrankingwindow.py
pack/uiscript/ sizde orda değilse root/uiscript e atın
Kill Ünvan Sistemi (KillStreak) Nedir?
Metin2 özel sunucularında oyuncuların PvP (Player vs Player) etkinliklerini teşvik etmek ve oyun deneyimini zenginleştirmek amacıyla geliştirilen Kill Ünvan Sistemi, yani KillStreak, oyuncuların art arda öldürme serilerine göre özel ünvanlar almasını sağlar. Bu sistem, sadece savaş becerilerini değil aynı zamanda stratejik düşünmeyi ve takım çalışmasını da teşvik eder.
Kill Ünvan Sisteminin Temel Amacı
Bu sistem sayesinde oyuncular daha aktif PvP yapmaya teşvik edilir. Art arda öldürme serisi yakaladıklarında özel ünvanlar kazanır ve bu sayede hem kendi başarılarını gösterir hem de diğer oyuncular tarafından tanınır. Bu durum, sunucu içi rekabeti artırır ve oyuncuların daha fazla zaman harcamasını sağlar.
Kill Ünvan Sistemine Ait Ünvanlar
Genellikle KillStreak sistemlerinde belli öldürme sayılarına göre ünvanlar belirlenir. Örneğin:
- 5 art arda öldürme: Katil
- 10 art arda öldürme: Savaş Tanrısı
- 15 art arda öldürme: Yıkım Efendisi
- 20+ art arda öldürme: Efsane Katil
C++ ile Kill Ünvan Sistemi Geliştirme
Metin2 özel sunucularında Kill Ünvan sistemi genellikle C++ tabanlı sunucu kaynak kodları üzerinde geliştirilir. Bu sistem için öncelikle oyuncunun öldürme sayısını takip edecek bir sayaç oluşturulmalıdır. Bu sayaç, oyuncu başka bir oyuncuyu öldürdüğünde artar, kendisi öldüğünde sıfırlanır.
Ayrıca bu sayaç, oyuncunun karakter bilgileriyle senkronize edilmeli ve veritabanında tutulmalıdır. Böylece sunucu yeniden başlatıldığında bile veriler korunur.
Python GUI Kullanarak Kill Ünvan Arayüzü
Oyuncuların kill streak sayılarını ve ünvanlarını görebilmeleri için Python GUI veya UIScript tabanlı bir arayüz geliştirilebilir. Bu arayüz sayesinde oyuncu, anlık olarak kaç art arda öldürmesi olduğunu ve hangi ünvana sahip olduğunu takip edebilir. Py Root dosyaları üzerinden bu arayüz entegrasyonu sağlanabilir.
Sunucu Taraflı Kodlama ve DB Entegrasyonu
KillStreak sistemi, Game Server ve Auth Server arasında koordineli çalışmalıdır. Oyuncunun öldürme eylemi Game Server tarafında tespit edilir, Auth Server aracılığıyla veritabanına yazılır ve oyuncunun sonraki girişinde bu bilgiye göre ünvanı güncellenir.
DB-Core ve Game-Core yapıları dikkatlice yapılandırılmak zorundadır. Bu sayede performans kaybı yaşanmadan sistem sorunsuz çalışabilir.
Sistem Performansı ve Stabilite
KillStreak sistemi, doğru yapılandırılmadığında sunucu üzerinde ciddi yük oluşturabilir. Özellikle yüksek oyuncu yoğunluğunda her öldürme anında veritabanına yazma işlemi yapılması sistemi yavaşlatabilir. Bu nedenle işlemler bellek (memory) üzerinden yönetilerek, belirli aralıklarla DB’ye yazılmalıdır.
Özel Sunucularda Kill Ünvan Sistemi Avantajları
Özel sunucularda bu tür sistemler, oyuncu sadakati ve rekabet ortamı açısından büyük önem taşır. KillStreak gibi sistemler, oyuncuların PvP yapmaya istekli olmasını sağlayarak sunucunun popülerliğini artırır. Aynı zamanda turnuvalar, özel ödüller gibi etkinliklerle de birleştirilebilir.
Sonuç
Kill Ünvan Sistemi, Metin2 özel sunucularında oyuncuların daha aktif PvP yapmasını sağlayan etkili bir sistemdir. C++ ve Python dilleri ile geliştirilen bu sistem, doğru entegre edildiğinde sunucunuzun rekabetçi ortamını ciddi anlamda güçlendirir. Bu sistem, sadece teknik açıdan değil, aynı zamanda oyun içi sosyal yapı açısından da değerlidir.
What is the Kill Title System (KillStreak)?
The Kill Title System, also known as KillStreak, is developed for Metin2 private servers to encourage players to engage more actively in PvP (Player vs Player) activities and enrich the overall gaming experience. This system allows players to earn special titles based on their consecutive kill streaks. It not only promotes combat skills but also strategic thinking and teamwork.
The Main Goal of the Kill Title System
This system motivates players to participate more in PvP. As they achieve consecutive kills, they earn special titles that allow them to showcase their achievements and gain recognition from other players. This increases competition within the server and encourages players to spend more time in-game.
Titles Available in the Kill Streak System
In most KillStreak systems, specific titles are awarded based on kill counts. For example:
- 5 consecutive kills: Killer
- 10 consecutive kills: War God
- 15 consecutive kills: Destroyer
- 20+ consecutive kills: Legendary Killer
Developing the Kill Title System with C++
The KillStreak system for Metin2 private servers is typically developed using C++ based server source codes. A counter must first be created to track a player's kill count. This counter increases when a player kills another player and resets if the player dies.
Furthermore, this counter should be synchronized with the character data and stored in the database so that data remains intact even after a server restart.
Using Python GUI for the Kill Title Interface
To allow players to view their current kill streak and title, a Python GUI or UIScript-based interface can be developed. This interface enables real-time tracking of consecutive kills and titles. Integration can be done through Py Root files.
Server-Side Coding and DB Integration
The KillStreak system must operate coherently between the Game Server and Auth Server. When a player kills another, the action is detected on the Game Server side, recorded via the Auth Server into the database, and the player's title is updated accordingly upon their next login.
DB-Core and Game-Core structures must be carefully configured to ensure smooth performance without loss of efficiency.
System Performance and Stability
If misconfigured, the KillStreak system can significantly burden the server. Particularly during high player traffic, writing to the database at every kill event may slow down the server. Therefore, operations should be managed in memory and periodically written to the database.
Advantages of the Kill Title System in Private Servers
Such systems are crucial for increasing player loyalty and competitiveness in private servers. Systems like KillStreak encourage players to engage more in PvP, thus boosting the server’s popularity. These can also be combined with events like tournaments and special rewards.
Conclusion
The Kill Title System is an effective feature in Metin2 private servers that encourages active PvP gameplay. When properly integrated using C++ and Python, this system significantly enhances the competitive environment of your server. It adds value not only technically but also socially within the game.
