Ușa dungeonului e primul loc unde jucătorii încearcă să „păcălească” regulile: intră cu un personaj prea mic, intră fără bilet sau cer liderului să deschidă intrarea în timp ce el pleacă pe altă hartă. Toate aceste verificări trebuie făcute **pe server**, în quest, nu în client. În lecția aceasta construim un dialog de intrare care verifică grupul, liderul, nivelul fiecărui membru și biletul, și abia apoi îi trimite înăuntru.
d.new_jump_party.Cuvinte pe care le vei întâlni
| Cuvânt | Ce înseamnă |
|---|---|
| Party | Grupul de jucători. Are un lider. |
| PID | Numărul unic al unui personaj (player id). |
d.new_jump_party | Funcția care creează o instanță nouă și trimite grupul în ea. |
| Bilet de intrare | Un item consumat la intrare (în multe pachete „Pasul” sau „Biletul” dungeonului). |
| Core | Un proces game care rulează un set de hărți. Un canal poate avea mai multe core-uri. |
Ce face, de fapt, d.new_jump_party
Am verificat funcția în sursa serverului (questlua_dungeon.cpp). Ea face trei lucruri:
- Creează o instanță nouă a hărții pe care i-o dai.
- Dacă jucătorul nu are grup, se oprește și scrie în syserr
cannot go to dungeon alone. Nimeni nu e mutat. - Mută în instanță doar membrii grupului aflați pe aceeași hartă cu cel care a pornit questul. Cine e pe altă hartă sau pe alt canal rămâne pe loc.
Al treilea punct e important. La fel se comportă și party.get_member_pids(): întoarce doar membrii de pe aceeași hartă. Deci dacă verifici nivelul „tuturor”, verifici de fapt doar membrii de lângă tine. Pentru dungeon e exact ce vrei.
Pasul 1: Codul dialogului de intrare
Exemplul de mai jos e simplificat după flame_dungeon.quest din pachet. Valorile scrise cu majuscule sunt „etichete” definite la începutul questului cu define; înlocuiește-le cu numerele tale.
define NPC_INTRARE 20094
define BILET 30329
define MAPA_DUNGEON 351
define NIVEL_MINIM 50
quest pestera_intrare begin
state start begin
when NPC_INTRARE.chat."Intra in Pestera Lupilor" begin
if not party.is_party() then
say("Ai nevoie de un grup ca sa intri.")
return
end
if not party.is_leader() then
say("Doar liderul grupului poate deschide intrarea.")
return
end
local pids = {party.get_member_pids()}
local prea_mici = {}
for i, pid in next, pids, nil do
q.begin_other_pc_block(pid)
if pc.get_level() < NIVEL_MINIM then
table.insert(prea_mici, pc.get_name())
end
q.end_other_pc_block()
end
if table.getn(prea_mici) > 0 then
say("Acesti membri nu au nivelul necesar:")
for i, nume in next, prea_mici, nil do
say(nume)
end
return
end
local s = select("Intra", "Anulare")
if s == 2 then
return
end
if pc.count_item(BILET) < 1 then
say("Nu ai biletul de intrare.")
return
end
pc.remove_item(BILET, 1)
d.new_jump_party(MAPA_DUNGEON, 7762, 6739)
end
end
endAtenție: 20094, 30329 și 351 sunt doar exemple. Caută VNUM-urile și indexul hărții în pachetul tău, cum ai învățat în lecția despre VNUM.
Pasul 2: Explicat bucată cu bucată
| Bucată | Ce face, pe românește |
|---|---|
party.is_party() | „Are jucătorul grup?” Dacă nu, oprim totul cu return. |
party.is_leader() | „E el liderul?” Doar liderul poate porni intrarea. |
{party.get_member_pids()} | Face o listă cu PID-urile membrilor de pe aceeași hartă. |
q.begin_other_pc_block(pid) | „De acum, pc.* se referă la membrul acesta.” Așa citești nivelul fiecăruia. |
q.end_other_pc_block() | „Revino la jucătorul care vorbește cu NPC-ul.” Nu-l uita, altfel restul codului lucrează pe alt personaj. |
select("Intra", "Anulare") | Cere confirmarea. 1 = Intra, 2 = Anulare. |
pc.count_item(BILET) < 1 | Verifică biletul după confirmare, chiar înainte de a-l lua. |
pc.remove_item(BILET, 1) | Ia biletul. |
d.new_jump_party(351, 7762, 6739) | Creează instanța hărții 351 și mută grupul la coordonatele date. |
De ce verificăm biletul după „Intra”, nu înainte
Între momentul în care apare fereastra cu butoane și momentul în care jucătorul apasă „Intra” pot trece multe secunde. În acest timp, jucătorul poate arunca biletul, îl poate pune în depozit sau îl poate da altcuiva. Dacă ai verificat biletul doar la început, l-ai lăsa să intre gratis. Regula simplă: verifică chiar înainte de acțiune, în același bloc de cod, fără alte ferestre între verificare și acțiune.
Coordonatele: atenție la două sisteme diferite
În pachetul verificat, d.new_jump_party și d.jump_all primesc coordonate globale (poziția hărții în lume plus poziția în hartă). În schimb, d.spawn_mob primește coordonate locale, din interiorul hărții. În flame_dungeon.quest, intrarea folosește {7762, 6739}, iar bossul e pus la {686, 637}. Dacă amesteci cele două, grupul ajunge în afara hărții sau monstrul nu apare. Detalii în lecția despre coordonate și hărți.
Bilet pentru lider sau pentru toți?
Exemplul nostru ia biletul doar de la lider. Dungeonul de foc din pachet cere bilet de la fiecare membru și îl ia pe rând, în același tip de buclă cu q.begin_other_pc_block. Ambele variante sunt corecte; alege una și scrie-o clar în descrierea dungeonului, ca jucătorii să știe.
Serverele cu mai multe core-uri
Instanța se creează pe core-ul pe care rulează questul. Dacă harta dungeonului nu e încărcată pe acel core (în fișierul CONFIG, la hărțile permise), crearea eșuează. Pe unele surse modificate există funcții separate pentru intrarea pe alt core. Nu le folosi decât dacă le găsești în quest_functions la tine. Despre CONFIG ai citit în lecția despre porturi și canale.
Ce ar trebui să vezi: liderul alege „Intra”, biletul dispare din inventar, iar toți membrii de lângă el sunt teleportați în dungeon.
Cum testezi intrarea
Pe serverul de test, încearcă pe rând:
| Ce încerci | Ce trebuie să se întâmple |
|---|---|
| Solo, fără grup | Mesaj „Ai nevoie de un grup”, nimeni nu intră. |
| Membru, nu lider | Mesaj „Doar liderul…”. |
| Un membru sub nivel | Apare numele lui, nimeni nu intră, biletul rămâne. |
| Arunci biletul după ce apare fereastra | Mesaj „Nu ai biletul”, nimeni nu intră. |
| Un membru pe altă hartă | Ceilalți intră, el rămâne afară. |
| Totul corect | Biletul dispare o singură dată, grupul intră. |
Greșeli des întâlnite
- Lipsește
q.end_other_pc_block(). Restul questului lucrează pe alt personaj, iar biletul se ia de la cine nu trebuie. - Biletul se verifică înainte de
select. Jucătorul îl poate arunca în timp ce fereastra e deschisă. - Biletul se ia înainte de toate verificările. Dacă intrarea eșuează, jucătorul pierde biletul degeaba.
- Verificarea e doar în client (de exemplu un buton gri). Clientul se poate modifica; serverul decide.
- Coordonate locale date funcției de intrare. Grupul ajunge într-un loc greșit.
Întrebări frecvente
Pot lăsa un jucător să intre singur?
Da, dar cu altă funcție. În pachet există și d.new_jump și d.new_jump_all, care nu cer grup. Verifică exact cum le folosesc questurile din pachetul tău înainte să le copiezi.
De ce nu intră un membru al grupului?
Cel mai des, era pe altă hartă sau pe alt canal în momentul intrării. Doar membrii de pe aceeași hartă cu liderul sunt mutați.
Unde văd de ce n-a mers?
În syserr-ul core-ului pe care rulează questul. Mesaje ca cannot go to dungeon alone sau cannot create dungeon îți spun exact problema.
Ce urmează
Grupul e înăuntru. Acum construim etapele, pietrele Metin și bossul final. Pentru recapitulare, vezi primul quest Lua.
Toate lecțiile sunt în lista tutorialelor Metin2.
Server Metin2 gata de joc
Găzduire Metin2 în România, cu instalare de serverfiles, bază de date MySQL și protecție Anti-DDoS inclusă.