Bir metin2 kemer sistemi, oyuncunun ekipman penceresine yeni bir kuşam yuvası (belt slot) ve o yuvaya kemer takıldığında açılan ayrı bir küçük envanteri (belt inventory) ekleyen özelliktir. Kemerin içine iksir, can şişesi gibi tüketilebilir eşyalar koyup oyun boyunca hızlı erişebilirsiniz; kemer yükseltildikçe açılan hücre sayısı artar. Bu rehberde kemeri üç katmanda — item_proto, server source ve client (Python UI) — çalışır hâle nasıl getirdiğimi anlatıyorum.
Kemer sisteminin mimarisi
Kemer tek bir betikle değil, üç katmanın uyumuyla çalışır. Hepsinin aynı sabitleri (slot aralığı) paylaşması şart, yoksa eşyalar "yok olur" veya client çöker:
- item_proto: Kemer eşyasının türü, takıldığı yuva ve hangi kademede kaç hücre açtığı burada tanımlıdır.
- Server source: Belt inventory için ayrı bir slot aralığı, eşya yerleştirme doğrulaması ve veritabanına kaydetme mantığı.
- Client (root/Python): Ekipman penceresinde belt yuvası ve kemer takılınca açılan grid arayüzü.
Çoğu açık kaynak source'ta sistem bir derleme bayrağı arkasındadır; örneğin ENABLE_BELT_INVENTORY_SYSTEM. Server ve client'ı bu tanım açıkken derlemezseniz paket yapıları uyuşmaz ve oyuncu girişte düşer.
item_proto'ya kemer eşyası eklemek
Kemer, kendine ait bir eşya türüdür ve özel bir kuşam yuvasına takılır. item_proto kaydında üç alan kritik: tür (ITEM_BELT), takılma bayrağı (WEARABLE_BELT) ve hangi yuvaya oturacağı (WEAR_BELT). Kademe (kaç hücre açacağı) genelde değer alanlarında (value0) tutulur:
-- proto'yu MySQL'den düzenliyorsanız mantık şöyle:
-- type = ITEM_BELT (kemer türü)
-- subtype = 0
-- wearflags = WEARABLE_BELT (belt yuvasına takılabilir)
-- value0 = belt grade (1,2,3,4 → açılan hücre kademesi)
UPDATE item_proto
SET type = 'ITEM_BELT',
wearflags = 'WEARABLE_BELT',
value0 = 1 -- başlangıç kademesi
WHERE vnum = 18000;
Türkçe/çoklu source'larda alan adları item_names.txt ve item_proto.txt üzerinden de yönetilebilir. Önemli olan: kemerin türü silah/zırh değil ayrı bir kemer türüdür, böylece WEAR_BELT yuvasına oturur ve normal envanteri işgal etmez. Eşyayı ekledikten sonra proto'yu item_proto.txt'ten yeniden derleyip (veya canlı tabloyu güncelleyip) game core'u yeniden başlatın.
Server source: belt inventory slot aralığı
Kemer envanteri, normal envanter ve ekipman yuvalarından ayrı bir adres aralığında yaşar. Source'ta bu aralık sabitlerle tanımlanır (dosya ve adlar source'a göre değişebilir, ama mantık aynıdır):
// service.h / item_length benzeri bir başlıkta
#define BELT_INVENTORY_SLOT_START (INVENTORY_AND_EQUIP_SLOT_END)
#define BELT_INVENTORY_SLOT_COUNT 16
#define BELT_INVENTORY_SLOT_END (BELT_INVENTORY_SLOT_START + BELT_INVENTORY_SLOT_COUNT)
Bu aralığı tanıyan bir yardımcı fonksiyon, eşya yerleştirme doğrulamasında kullanılır. Game core, bir eşyanın hangi hücreye konabileceğine karar verirken slotun belt aralığında olup olmadığını kontrol eder:
bool CHARACTER::IsBeltInventorySlot(WORD wCell) const
{
return (wCell >= BELT_INVENTORY_SLOT_START &&
wCell < BELT_INVENTORY_SLOT_END);
}
// Eşya taşıma doğrulamasında (char_item.cpp benzeri):
if (IsBeltInventorySlot(wDestCell))
{
// 1) Oyuncuda takılı bir kemer var mı?
LPITEM belt = GetWear(WEAR_BELT);
if (!belt)
return false; // kemer yoksa hücre kullanılamaz
// 2) Hedef hücre, kemerin kademesince açılmış mı?
if (!IsValidBeltCell(belt, wDestCell))
return false; // kilitli hücreye konamaz
}
İki kontrol kritiktir: kemer takılı değilken belt hücrelerine eşya konamamalı ve yalnızca kemerin value0 kademesince açılmış hücreler kullanılabilir olmalı. Bu mantığı atlarsanız oyuncular kilitli hücreleri sömürerek bedava ekstra envanter elde eder. Kemer çıkarıldığında game core, içindeki eşyaları normal envantere taşımalı (yer yoksa işlemi reddetmeli); aksi hâlde eşyalar erişilemez kalır.
Veritabanı ve kalıcılık
Belt inventory'deki eşyalar da diğer eşyalar gibi player.item tablosunda window ve pos alanlarıyla saklanır; sadece pos değeri belt aralığına düşer. Burada Metin2'nin klasik tuzağına dikkat: oyuncu online iken envanter DB cache katmanında bellekte tutulur. Belt eşyalarını doğrudan MySQL'e yazmaya çalışırsanız oyuncu çıkışta kendi bellek kopyasını üzerine yazar ve eşyalar kaybolur. Eşya hareketleri her zaman game core API'si üzerinden yapılmalı, doğrudan SQL ile değil. Kayıt şeması farklılık göstermez; tek değişiklik, belt slotlarının yeni pos aralığına yazılmasıdır.
Pratikte bu, oyuncu oyundan çıkarken DB cache'in belt hücrelerini de normal hücrelerle birlikte tek seferde kalıcılaştırması anlamına gelir. Yeni bir aralık eklediğiniz için, source'taki kayıt/yükleme döngüsünün belt slotlarını da tarayıp yazdığından emin olun; bu kısmı atlayan sunucularda kemer eşyaları yalnızca o oturum boyunca görünür, sunucu yeniden başlatılınca silinir. Test ederken birkaç eşyayı kemere koyup çıkış-giriş yapın ve game core'u yeniden başlatın — eşyalar hâlâ yerindeyse kalıcılık doğru kurulmuş demektir.
Client: belt yuvası ve grid arayüzü
Client tarafında iki ekleme var. Birincisi ekipman penceresine kemer yuvası; ikincisi kemer takılınca açılan belt inventory grid'i. Python UI dosyalarında (uiInventory.py ve ilgili pencere betiği) slot aralığı, server'la birebir aynı tanımlanmalıdır:
# playerSettingModule / player.py içinde
BELT_INVENTORY_SLOT_START = 0
BELT_INVENTORY_SLOT_COUNT = 16
BELT_INVENTORY_SLOT_END = BELT_INVENTORY_SLOT_START + BELT_INVENTORY_SLOT_COUNT
# Kemer takılınca grid'i aç/kapat
def OnEquipBelt(self, isEquipped):
if isEquipped:
self.beltInventoryGrid.Show()
else:
self.beltInventoryGrid.Hide()
Grid hücreleri için ekonomik bir kural: kemerin kademesi (server'dan gelen değer) kaç hücre açtıysa o kadarını "aktif", kalanını "kilitli" göster. Kilitli hücrelere sürükle-bırak engellenmeli. Arayüzün .tga görsellerini ve hücre koordinatlarını da eklemeyi unutmayın; çoğu source bunları bir belt_inventory_window.py dosyasında tutar. Son olarak grid'in açılış/kapanış animasyonu sadece görseldir — eşya doğrulaması daima server'da yapılır, client'a asla güvenmeyin.
Kemeri yükseltmek: hücre açma
Kemerin çekiciliği yükseltilebilir olmasıdır. Genel yaklaşım, kemerin value0 kademesini bir yükseltme NPC'si ya da quest ile artırmaktır. Quest tarafında basit bir kademe artırma şöyle görünür:
quest belt_upgrade begin
state start begin
when 18000.use begin
local belt = item.get_value(0) -- mevcut kademe
if belt >= 4 then
say("Kemerin zaten en üst kademede.")
return
end
-- yükseltme malzemesi/yang kontrolü burada
item.set_value(0, belt + 1)
say("Kemerin "..(belt+1).." kademesine yükseldi!")
end
end
end
Kademe arttığında server, takılı kemerin yeni value0 değerine göre daha fazla belt hücresini "açılmış" sayar ve client'a günceller. Kademe sınırını (örnekte 4) hem quest'te hem source'taki IsValidBeltCell mantığında tutarlı tutun; ikisi farklı düşünürse ya boşa kilitli hücre kalır ya da sömürülebilir açık oluşur.
Sık Sorulan Sorular
Kemer takınca grid açılıyor ama eşya koyunca çıkışta kayboluyor, neden?
Klasik DB cache tuzağı. Belt eşyalarını doğrudan MySQL'e yazıyorsanız oyuncu çıkışta bellek kopyasını üzerine yazar. Eşya taşımayı daima game core API'si üzerinden yapın ve pos değerinin belt aralığına doğru yazıldığından emin olun.
Oyuncular kemer takmadan belt hücrelerini kullanabiliyor.
Server tarafında GetWear(WEAR_BELT) kontrolü eksik ya da zayıf. Belt aralığına yapılan her yerleştirmede önce takılı kemer var mı, sonra hedef hücre kademece açılmış mı diye doğrulayın. Client'taki kilit yalnızca kozmetiktir.
Sistemi açtım ama oyuncular girişte düşüyor.
Neredeyse her zaman server ile client'ın ENABLE_BELT_INVENTORY_SYSTEM bayrağının/slot sabitlerinin uyuşmamasıdır. İkisini de aynı tanımlarla yeniden derleyin; paket boyutları eşleşmezse oturum kopar.
Kemer sistemini sunucunuza sorunsuz kurmak mı istiyorsunuz? item_proto'dan source ve client entegrasyonuna kadar belt inventory'yi kademeli hücre açma dahil baştan sona kurarım. Projenizi benimle iletişime geçerek konuşalım.