Metin2 quest timer, görev sisteminin en güçlü ama en çok yanlış kullanılan parçalarından biridir: bir olayı anında değil, belirli bir süre sonra tetiklemenize olanak tanır. Geri sayım yapan bir görev, belirli aralıkla tekrar eden bir uyarı, boss yeniden doğma süresi veya "bu ödülü 24 saatte bir alabilirsin" şeklinde bir bekleme süresi (cooldown) kurmak istiyorsanız, hepsinin temelinde zamanlayıcılar yatar. Bu yazıda Metin2 quest motorundaki iki ana zamanlayıcı mekanizmasını — oyuncuya bağlı q.start_timer ve global server_timer — gerçek, çalışan örneklerle adım adım inceleyeceğiz.
Quest timer nedir, nasıl çalışır?
Quest motoru olay tabanlıdır: when ... begin ... end blokları belirli olaylara (sohbet, öldürme, giriş) tepki verir. Zamanlayıcı ise bu mantığa "gecikme" ekler. Bir timer kurduğunuzda motor onu kaydeder ve süresi dolduğunda özel bir olay üretir; siz de bu olayı yine bir when bloğuyla yakalarsınız. İki tür timer vardır:
- Oyuncuya bağlı timer (
q.start_timer): zamanlayıcı belirli bir karaktere aittir. Süre dolduğunda blok o oyuncunun bağlamında çalışır; yanipc.*fonksiyonlarına doğrudan erişebilirsiniz. - Global server timer (
server_timer): zamanlayıcı bir oyuncuya değil, sunucuya aittir. Boss doğma, harita olayları gibi kimseye bağlı olmayan işler için idealdir.
Bu ayrımı baştan doğru kurmak önemlidir, çünkü iki tür timer farklı bağlamlarda çalışır ve birini diğerinin yerine kullanmak en sık karşılaşılan hatalardandır. Aşağıdaki bölümlerde her ikisini de tek tek ele alacağız.
q.start_timer ile oyuncuya bağlı zamanlayıcı
En sık ihtiyaç duyulan senaryo budur: oyuncu bir NPC ile konuşur, X saniye sonra bir şey olur. q.start_timer("isim", saniye) ile başlatırsınız, sonucu when isim.timer bloğunda yakalarsınız.
quest hediye_kutusu begin
state start begin
when 20095.chat."Kutuyu ac" begin
say_title("Tuccar:")
say("Kutu 10 saniye sonra acilacak, bekle...")
q.start_timer("kutu_ac", 10)
end
when kutu_ac.timer begin
say("Kutu acildi! Iste odulun.")
pc.give_item2(27003, 1) -- ornek item vnum
pc.give_exp2(5000)
end
end
end
Burada dikkat edilecek nokta: kutu_ac.timer bloğu zaten doğru oyuncunun bağlamında çalışır, çünkü timer'ı o oyuncu için başlattık. Aynı isimle bir timer zaten varken yeniden q.start_timer çağırırsanız eskisi yenisiyle değişir, bu da istemeden çift tetiklemeyi önler.
server_timer ile global gecikmeli olaylar
Bir bossun ölümünden bir saat sonra yeniden doğmasını istediğinizi düşünün. Bu işin belirli bir oyuncuyla ilgisi yoktur, dolayısıyla server_timer kullanılır. İmza şudur: server_timer("isim", saniye, arg). Üçüncü parametre olarak bir VID veya sayı geçirebilir, tetiklendiğinde get_server_timer_arg() ile geri okuyabilirsiniz.
quest boss_dogus begin
state start begin
when 6091.kill begin
-- Boss oldu, 1 saat sonra yeniden dogsun
local map_index = pc.get_map_index()
server_timer("boss_respawn", 60 * 60, map_index)
notice_all("Boss oldu! 1 saat sonra geri donecek.")
end
when boss_respawn.server_timer begin
local map_index = get_server_timer_arg()
regen_in_map(map_index, "data/boss_regen.txt")
notice_all("Boss yeniden dogdu!")
end
end
end
server_timer bloğunun içinde varsayılan bir oyuncu bağlamı yoktur. Belirli bir oyuncuya iş yaptırmanız gerekirse, argümanla taşıdığınız PID üzerinden q.begin_other_pc_block(pid) ... q.end_other_pc_block() kalıbını kullanmalısınız.
Bekleme süresi (cooldown) deseni
Timer'lar "şu an dolacak" işler içindir; ama "bu ödülü 24 saatte bir al" gibi kalıcı bir cooldown, sunucu yeniden başlasa bile saklanmalıdır. Bunun doğru yolu timer değil, bir zaman damgasını oyuncu flag'ine yazmaktır. get_global_time() sunucunun anlık zamanını saniye cinsinden verir; bunu pc.setqf ile saklar ve sonra karşılaştırırsınız.
quest gunluk_odul begin
state start begin
when 20095.chat."Gunluk odul" begin
local now = get_global_time()
local last = pc.getqf("son_odul")
local bekleme = 24 * 60 * 60 -- 24 saat
if now - last < bekleme then
local kalan = bekleme - (now - last)
say("Bir sonraki odul icin " .. math.floor(kalan / 3600) .. " saat bekle.")
return
end
pc.setqf("son_odul", now)
pc.give_item2(50300, 1)
say("Gunluk odulun hazir!")
end
end
end
Buradaki fark kritiktir: q.start_timer sunucu yeniden başlatıldığında kaybolur, ama flag'e yazılan zaman damgası veritabanında kalır. Kalıcı bekleme süreleri için her zaman bu deseni tercih edin. Aynı mantık günlük giriş ödülleri, saatlik avlanma sınırı veya boss çağırma taşı gibi kullanım sıklığı sınırlı tüm sistemler için geçerlidir; süreyi timer ile değil, zaman damgasıyla yönetmek hem güvenli hem de restart'a dayanıklıdır.
Tekrarlı timer ve iptal etme
Belirli aralıkla tekrar eden bir görev (örneğin her 30 saniyede bir uyarı) için timer'ı kendi bloğunun içinde yeniden kurarsınız. Süreyi durdurmak istediğinizde ise bir quest flag'iyle döngüyü kırarsınız — çünkü bittiğinde yeniden başlatmazsanız döngü kendiliğinden durur.
quest sayac begin
state start begin
when login begin
pc.setqf("tur", 0)
q.start_timer("tik", 30)
end
when tik.timer begin
local tur = pc.getqf("tur") + 1
pc.setqf("tur", tur)
chat("Tur: " .. tur)
if tur < 5 then
q.start_timer("tik", 30) -- yeniden kur = devam
else
chat("Sayac bitti.") -- yeniden kurma = dur
end
end
end
end
Bir oyuncuya bağlı timer'ı erken iptal etmeniz gerekirse q.clear_timer() çalışan timer'ları temizler. Global timer için ise q.clear_server_timer("isim", arg) kullanılır.
Yaygın hatalar ve ipuçları
- Timer ismi ile when bloğu eşleşmiyor:
q.start_timer("kutu_ac", 10)başlattıysanız blok mutlakawhen kutu_ac.timerolmalı. İsimler birebir aynı olmalıdır. - Kalıcı cooldown için timer kullanmak: sunucu restart'ında timer silinir. Uzun ve kalıcı süreler için
get_global_time()+ flag desenini kullanın. - server_timer içinde pc.* çağırmak: global bağlamda oyuncu yoktur; PID'yi argümanla taşıyıp
q.begin_other_pc_blockbloğuna girin. - Çok kısa süreler: 1 saniyenin altında işlerden kaçının; quest motoru saniye çözünürlüğünde çalışır ve çok sık tetiklenen timer'lar performansı düşürür.
- syserr kontrolü: timer beklediğiniz gibi tetiklenmiyorsa önce
syserr.txtdosyasına bakın; Lua hataları çoğu zaman orada belirir.
Sık Sorulan Sorular
q.start_timer ile server_timer arasındaki fark nedir?
q.start_timer belirli bir oyuncuya aittir ve blok o oyuncunun bağlamında çalışır; karakterden çıkış yapılırsa düşer. server_timer ise sunucuya aittir, kimseye bağlı değildir ve boss doğma gibi global olaylar için kullanılır.
Sunucu yeniden başlarsa timer'lar ne olur?
Bellekte tutulan timer'lar kaybolur. Bu yüzden saatler veya günler süren bekleme sürelerini timer ile değil, get_global_time() değerini bir oyuncu flag'ine yazarak yönetmelisiniz; flag veritabanında kalıcıdır.
Aynı anda birden fazla timer çalıştırabilir miyim?
Evet, sadece her birine farklı bir isim verin. Aynı isimle yeni bir timer başlatmak eskisini iptal edip yenisini kurar, bu da bazen kasıtlı olarak "sıfırla" mantığı için kullanılır.
Quest sisteminde takıldınız mı? Zamanlayıcı tabanlı görevler, dungeon mantığı veya özel event sistemleri için Metin2 quest geliştirmesi konusunda yardıma ihtiyacınız varsa benimle iletişime geçin — projenizi birlikte çözelim.