Metin2 Quest Örnekleri: Işınlayıcı NPC, Ödül Sandığı, Günlük Görev
Hazırlayan: Metin2VIP Editörleri Yayın: 2 dk okuma
İçindekiler
Aşağıdaki altı quest, bu sunucu paketindeki quest motoruna ve questlib.lua'ya göre yazıldı; kullanılan her fonksiyon quest_functions listesinde ve oyun çekirdeğinin kayıtlarında var. Her örnek tek başına bir dosyadır: dosyayı quest klasörüne koyup yolunu quest_list'e yazmak ve derlemek yeter. Dil kuralları, durumlar ve derleme quest yazma yazısında, fonksiyonların ayrıntısı quest fonksiyonları yazısında anlatılıyor; burada doğrudan kullanılabilecek kalıplar var.
Dosyaları Windows-1254 kodlamasıyla kaydet, yoksa Türkçe harfler oyunda bozuk görünür. Ücretler, ödüller ve seviye şartları örnektir, kendi sunucuna göre değiştir.
1. Işınlayıcı NPC: tablodan menü
Gidilecek yerleri bir tabloya yazmak, her yeni hedef için yalnız bir satır eklemeyi sağlar. Koordinatlar paketteki ışınlayıcı quest'inin kullandığı noktalardır ve krallıktan bağımsızdır.
quest uzak_isinlama begin
state start begin
when 9012.chat."Uzak bölgelere git" begin
local yerler = {
-- ad, seviye, Yang, x, y
{"Hwang Tapınağı", 52, 20000, 590500, 110500},
{"Hayalet Ormanı", 65, 40000, 290000, 5600},
{"Kırmızı Orman", 74, 60000, 1119500, 70200},
}
local menu = {}
for i = 1, table.getn(yerler) do
menu[i] = yerler[i][1]
end
table.insert(menu, "Vazgeç")
say_title("Işınlayıcı:")
say("Nereye gitmek istiyorsun?")
local s = select_table(menu)
if s > table.getn(yerler) then
return
end
local y = yerler[s]
if pc.get_level() < y[2] then
say(string.format("%s için en az %d. seviye olmalısın.", y[1], y[2]))
return
end
if pc.get_gold() < y[3] then
say(string.format("Bu ışınlanma %d Yang tutuyor.", y[3]))
return
end
if not pc.can_warp() then
say("Açık pencereleri kapatıp biraz sonra tekrar dene.")
return
end
pc.change_money(-y[3])
pc.warp(y[4], y[5])
end
end
end
Işınlayıcı bütün köylerde durduğu için seçenek her yerde çıkar; aynı NPC'nin mevcut ışınlama menüsü de yerinde kalır, iki quest'in seçenekleri tek menüde toplanır. select_table seçilen satırın sırasını döndürür. Yang, ışınlanmanın engellenmeyeceği anlaşıldıktan sonra alınır: pc.can_warp() ticaret, pazar ya da depo penceresi açıkken false döner ve bu durumda Yang gitmez. Koordinatların birimi ve haritaların oyuncu tarafı ışınlanma yazısında.
2. Ödül sandığı: ağırlıklı zar
Sandık, item_proto'ya ITEM_QUEST türüyle eklenmiş bir eşyadır; bu türdeki eşya kullanılınca when <vnum>.use olayı çalışır. Örnekteki 95000 varsayılan item_proto'da boş bir numaradır; eşyayı istemcinin item_proto'suna ve simge listesine de eklemek gerekir, sütunlar item_proto yazısında.
quest odul_sandigi begin
state start begin
when 95000.use begin
if pc.get_empty_inventory_count() < 1 then
syschat("Envanterinde boş yer yok.")
return
end
-- vnum, adet, ağırlık
local havuz = {
{27002, 20, 40},
{27005, 20, 40},
{71084, 2, 15},
{25040, 1, 5},
}
local toplam = 0
for i = 1, table.getn(havuz) do
toplam = toplam + havuz[i][3]
end
local zar = number(1, toplam)
local odul = havuz[1]
for i = 1, table.getn(havuz) do
zar = zar - havuz[i][3]
if zar <= 0 then
odul = havuz[i]
break
end
end
pc.remove_item(95000, 1)
pc.give_item2(odul[1], odul[2])
say_title("Ödül Sandığı")
say_item_vnum(odul[1])
say_reward(odul[2].." adet "..item_name(odul[1]))
end
end
end
Her satırın çıkma şansı, ağırlığının toplam ağırlığa oranıdır; satır eklemek ya da ağırlığı değiştirmek diğerlerinin şansını da değiştirir. Sandık, ödülden önce envanterden düşülür. item.remove() yerine pc.remove_item(95000, 1) kullanıldı, çünkü ilki bütün yığını siler. Havuzda Efsun Nesnesi ve Kırmızı İksir(O) gibi yığınlanabilen eşyalar var; tek boş yer denetimi bu yüzden yeterli. Yalnız rastgele eşya veren bir sandık için quest gerekmez, aynı iş special_item_group ile yapılır; biçimi drop dosyaları yazısında.
3. Günlük görev: günde bir ork avı
Oyuncu Şehir Bekçisi'nden görevi alır, 30 ork öldürür, ödülünü alır ve ertesi güne kadar görevi yeniden alamaz.
quest gunluk_ork begin
state start begin
function bugun()
return math.floor((get_time() + 10800) / 86400)
end
when 20354.chat."Günlük görev: ork avı" with pc.get_level() >= 25 begin
say_title("Şehir Bekçisi:")
if pc.getqf("son_gun") == gunluk_ork.bugun() then
say("Bugünkü işini yaptın, yarın yine gel.")
return
end
say("Orklar yolları kesiyor. 30 tanesini temizler misin?")
if select("Kabul ediyorum", "Şimdi değil") == 2 then
return
end
pc.setqf("sayac", 0)
set_state("avda")
end
end
state avda begin
when letter begin
send_letter("Günlük görev: ork avı")
q.set_counter("Kalan ork", 30 - pc.getqf("sayac"))
end
when button or info begin
say_title("Ork avı")
say_reward("Öldürülen: "..pc.getqf("sayac").." / 30")
end
when 601.kill or 602.kill or 603.kill or 604.kill begin
local n = pc.getqf("sayac") + 1
pc.setqf("sayac", n)
q.set_counter("Kalan ork", 30 - n)
if n >= 30 then
syschat("Görev tamam, Şehir Bekçisi'ne dön.")
set_state("dondu")
end
end
end
state dondu begin
when letter begin
send_letter("Ork avı: ödülünü al")
end
when 20354.chat."Orkları temizledim" begin
say_title("Şehir Bekçisi:")
say("Eline sağlık, yarın yine beklerim.")
pc.give_exp2(50000)
pc.change_money(30000)
pc.setqf("son_gun", gunluk_ork.bugun())
clear_letter()
set_state("start")
end
end
end
Günü saymak için bitiş zamanı yerine gün numarası tutuldu: get_time() Unix zamanıdır ve UTC'dir, 10.800 saniye eklemek günü Türkiye saatiyle gece yarısında değiştirir. Böylece akşam görevi bitiren oyuncu 24 saat beklemez, ertesi sabah yeniden alır. start içinde tanımlanan bugun fonksiyonu gunluk_ork.bugun() diye her durumdan çağrılır. Dört olay or ile bağlandığı için Ork, Ork Gözcüsü, Ork Savaşçısı ve Ork Büyücüsü'nün her biri sayaca yazılır. Kill olayı canavarın EXP'sini alan oyuncuya gelir; grup avında sayılmasını istiyorsan olaylar yazısındaki party_kill'e bak. Görev yalnız avda durumundaki oyuncuda dinlendiği için diğer oyuncuların ork öldürmesi bu quest'i hiç çalıştırmaz.
4. Seviye ödülleri: atlanan seviyeyi kaçırmadan
Ödülü levelup olayında == 30 diye vermek iki durumda ödülü kaçırır: tek seferde birkaç seviye atlayan oyuncu ve ödül sonradan eklendiğinde zaten 30'u geçmiş karakterler. Liste ve bayrakla her ikisi de kapanır:
quest seviye_odulleri begin
state start begin
function ver()
local liste = {
-- seviye, vnum, adet
{10, 27002, 50},
{20, 71084, 5},
{30, 25040, 2},
}
for i = 1, table.getn(liste) do
local bayrak = "odul_"..liste[i][1]
if pc.get_level() >= liste[i][1] and pc.getqf(bayrak) == 0 then
if pc.get_empty_inventory_count() < 1 then
syschat("Seviye ödülün için envanterde yer aç.")
return
end
pc.setqf(bayrak, 1)
pc.give_item2(liste[i][2], liste[i][3])
syschat(liste[i][1]..". seviye ödülü: "..item_name(liste[i][2]))
end
end
end
when login or levelup begin
seviye_odulleri.ver()
end
end
end
Giriş olayında select ya da wait gibi bekleyen fonksiyonlar kullanılmaz, bekleyen betik zorla kapatılır; bu yüzden bilgi syschat ile verilir. Envanter doluysa bayrak yazılmaz ve ödül bir sonraki girişte ya da seviyede yeniden denenir.
5. GM'in açtığı etkinlik
Etkinlik bir olay bayrağıyla açılıp kapanır. Bayrağın değeri etkinliğin numarasıdır; her oyuncu her numaradan bir kez hediye alır.
quest bayram_etkinligi begin
state start begin
when login with game.get_event_flag("bayram") > 0 begin
syschat("Bayram etkinliği açık, Uriel'e uğra.")
end
when 20011.chat."Bayram hediyesi" with game.get_event_flag("bayram") > 0 begin
local no = game.get_event_flag("bayram")
say_title("Uriel:")
if pc.getqf("alinan") == no then
say("Bu etkinliğin hediyesini zaten aldın.")
return
end
if pc.get_empty_inventory_count() < 1 then
say("Envanterinde yer aç, sonra gel.")
return
end
pc.setqf("alinan", no)
pc.give_item2(71084, 3)
say("İyi bayramlar.")
end
end
end
GM etkinliği /eventflag bayram 1 ile açar, /eventflag bayram 0 ile kapatır; komut GM_HIGH_WIZARD yetkisi ister. Bir sonraki bayramda /eventflag bayram 2 yazılır ve herkes yeniden hediye alabilir. Olay bayrakları db üzerinden bütün çekirdeklere dağıtılır ve yeniden başlatmada korunur. mob_exp, mob_item gibi bazı adlar oyunun oranlarını değiştirdiği için kendi bayrağına bunlardan farklı bir ad ver; o adlar etkinlik bonusları yazısında. Uriel yalnız birinci köylerde durur; etkinliği başka bir NPC'ye taşımak için vnum'u değiştirmek yeter.
6. Saatlik sunucu duyurusu
Sunucu zamanlayıcısı bir oyuncuya bağlı değildir ve çekirdek açık kaldıkça çalışır:
quest saatlik_duyuru begin
state start begin
when 20011.chat."GM: duyuruyu başlat" with pc.is_gm() begin
clear_server_timer("duyuru_saat", 1)
server_loop_timer("duyuru_saat", 3600, 1)
say("Duyuru saatte bir gönderilecek.")
end
when 20011.chat."GM: duyuruyu durdur" with pc.is_gm() begin
clear_server_timer("duyuru_saat", 1)
say("Duyuru durduruldu.")
end
when duyuru_saat.server_timer begin
notice_all("Etkinlik ve bakım duyuruları için Uriel'e uğrayın.")
end
end
end
Bu örnekte üç ayrıntı bu kaynağa özgüdür ya da kolay atlanır:
- Argüman 0 olmamalı. Bu kaynakta
clear_server_timerargümanı 0 olan zamanlayıcıyı silmez ve syserr'eLUA PREVENT: Wrong argument on ClearServerTimer!yazar. Zamanlayıcı bu yüzden 1 argümanıyla kurulur ve silinir. - Önce sil, sonra kur. Aynı ad ve argümanla ikinci kez kurulan zamanlayıcı syserr'e
already registered server timeryazdırır, listeye girmez ama çalışmaya devam eder ve artık silinemez; duyuru çift gelir. Başlatırken önce silmek bunu önler. - Olay
startdurumunda olmalı. Sunucu zamanlayıcısı oyuncu olmadan çalıştığı için quest her zamanstartdurumunda sayılır; paketteki Şeytan Kulesi quest'i de bütün zamanlayıcılarını orada tutar.notice_allduyuruyu P2P üzerinden bütün çekirdeklere yollar.
Zamanlayıcı yalnız kurulduğu çekirdekte yaşar ve çekirdek yeniden başlatılınca kaybolur. pc.is_gm() ile gizlenen seçenekler CONFIG'de test sunucusu açıkken herkese görünür, çünkü o ayarda herkes GM sayılır. GM yetkileri GM komutları rehberinde.
Örnekleri sunucuya eklemek
Her dosyayı örneğin collection/custom/ altına koy, yolunu quest_list'e yaz ve quest klasöründe derle; ardından değişikliği almak için çekirdekleri yeniden başlat ya da her çekirdekte /reload q yaz. Çalışırken oluşan hatalar syserr'e LUA_ERROR: quest <ad>.<durum> <olay> biçiminde düşer. Olayların tam listesi quest olayları yazısında, bütün fonksiyonlar quest fonksiyonları listesinde.
Sık sorulan sorular
- Günlük görev gece yarısı mı sıfırlanıyor?
- get_time() sunucunun Unix zamanını, yani UTC'yi verir. Örnekteki gün numarası 10.800 saniye (3 saat) eklenerek hesaplandığı için gün Türkiye saatiyle gece yarısı değişir. Başka bir saat diliminde bu sayıyı değiştirmek gerekir.
- server_loop_timer ile kurduğum duyuru neden clear_server_timer ile durmuyor?
- Bu kaynakta clear_server_timer argümanı 0 olan zamanlayıcıyı silmez, syserr'e 'LUA PREVENT: Wrong argument on ClearServerTimer!' yazar. Zamanlayıcıyı 0'dan farklı bir argümanla kurup aynı argümanla silmek gerekir. Aynı ad ve argümanla ikinci kez kurulan zamanlayıcı ise listeye girmeden çalışmaya devam eder ve hiç silinemez.
- Ödül sandığını special_item_group ile mi quest ile mi yapmalıyım?
- Yalnız rastgele eşya veriliyorsa special_item_group daha kısa yoldur, quest gerekmez. Seviye, krallık ya da bayrak denetimi, diyalog veya Yang ve EXP'nin hesapla verilmesi gerekiyorsa quest kullanılır.
İlgili sayfalar
- Metin2 Drop Dosyaları: mob_drop_item ve special_item_group Biçimi (Server kurma)
- Metin2 EXP, Drop ve Yang Etkinlikleri: Krallık Bonusları Nasıl Çalışır? (Wiki)
- Metin2 GM Komutları: En Çok Kullanılan Komutlar ve Yetki Seviyeleri (Server kurma)
- Metin2'de Işınlanma: Işınlayıcı NPC, Köye Dönüş ve Hatırlama Kağıtları (Wiki)
- Metin2 item_proto Rehberi: Sütunlar, Türler ve Bayraklar (Server kurma)
Kategori: Server Kurma Quest Geliştirme quest sandık ışınlayıcı lua günlük görev
Bu sayfadaki bilgiler Metin2'nin sunucu yazılımındaki hesaplamalara ve standart oyun dosyalarına dayanır. Oyun dosyalarını değiştiren sunucularda değerler farklı olabilir. İçerikleri nasıl hazırlıyoruz?