Înapoi la blogToate tutorialele Metin2
Tutoriale Metin2 · Lecția 65 din 99

Etape, pietre Metin și boss final într-un dungeon Metin2, fără blocaje și fără crash

2 noiembrie 2026 6 min de citit

Partea cea mai vizibilă a unui dungeon e trecerea de la o etapă la alta: „ai omorât toți monștrii, se deschide ușa”, „ai spart piatra, apare bossul”. Tot aici apar și cele mai grele bug-uri: grupuri blocate pentru că serverul a numărat greșit monștrii, etaje sărite și chiar căderi ale serverului (crash) care apar abia după câteva ore. În această lecție înveți modelul pe care îl folosesc dungeon-urile stabile: **evenimentul doar notează, ceasul decide**.

Pe scurt: în when kill doar setezi un semn („piatra a căzut”). Un server_timer care se verifică singur la câteva secunde citește semnul și face trecerea: oprește spawnul, curăță monștrii cu d.kill_all(), pune bossul. Testează fiecare etapă separat.

Cuvinte pe care le vei întâlni

CuvântCe înseamnă
when killBucata de quest care rulează când un monstru moare.
server_timerUn ceas de quest legat de instanță. Rulează cod după X secunde.
d.count_monster()Câți monștri vii (inclusiv pietre) sunt în instanță.
d.regen_file / d.set_regen_filePune monștrii dintr-un fișier de spawn. Al doilea îi și reînvie periodic.
d.clear_regen()Oprește reînvierea. Nu șterge monștrii deja existenți.
d.kill_all()Omoară toți monștrii din instanță.
CrashCăderea procesului game. Jucătorii sunt deconectați.

Cum fac questurile din pachet

Am citit două dungeon-uri reale din pachetul verificat:

  • flame_dungeon.quest: după ce pune monștrii unei camere, armează ceasul killed_A_1. La fiecare 6 secunde, ceasul verifică d.count_monster() <= 0. Dacă mai sunt monștri, se re-armează singur. Dacă nu mai sunt, anunță grupul și deschide etapa următoare.
  • deviltower_zone.quest: la etajul 4 pune 7 pietre, dintre care una e „cea adevărată” (d.set_unique("real", vid)). Un ceas verifică la 10 secunde dacă piatra adevărată a murit, cu d.is_unique_dead("real").

Ambele folosesc același principiu: un ceas verifică starea, nu fiecare monstru ucis.

De ce nu faci totul direct în when kill

Când un monstru moare, codul din when kill rulează în timp ce serverul încă „termină” monstrul: împarte EXP-ul, dă dropul, abia apoi îl șterge. Dacă în acel moment ștergi brusc toți monștrii din hartă, serverul poate rămâne cu o „adresă” spre un monstru care nu mai există. Rezultatul nu e un crash imediat, ci memorie stricată, care cade mai târziu, la un kill oarecare. E unul dintre cele mai greu de găsit bug-uri.

Regula practică:

  • În when kill: setezi semne (d.setf), numeri, dai un drop mic (game.drop_item). Cel mult pui un singur NPC.
  • În server_timer: schimbi etapa, oprești spawnul, curăți harta (d.kill_all()), pui valurile noi și bossul.

d.kill_all() sau d.purge()?

Ambele golesc instanța, dar diferit. În sursa verificată, d.purge() distruge pe loc fiecare monstru, chiar în timp ce serverul parcurge lista lor. d.kill_all() îi omoară normal, iar ștergerea se face mai târziu, în siguranță.

Pentru un începător, regula e simplă: folosește d.kill_all(), niciodată d.purge() într-un eveniment de kill. Vei găsi d.purge() și în unele questuri din pachete. Asta nu înseamnă că e sigur pe sursa ta: pe un server cu pietre care cheamă monștri la spargere, a produs crash-uri repetate.

Un detaliu de verificat la tine: în sursa clasică, monștrii omorâți cu d.kill_all() care fuseseră deja loviți de jucători pot lăsa drop. În unele surse modificate, d.kill_all() nu lasă niciun drop. Testează pe serverul de test ce face la tine.

Pasul 1: Etapa „omoară toți monștrii”

lua
when verifica_camera.server_timer begin
    if d.select(get_server_timer_arg()) then
        if d.getf("level") == 1 then
            local n = d.count_monster()
            if n > 0 then
                d.setf("vazut", 1)
            end
            if d.getf("vazut") == 1 and n <= 0 then
                d.setf("level", 2)
                d.notice("Camera e curata! Sparge piatra Metin.")
                d.spawn_mob(PIATRA, 320, 394)
            end
        end
        if d.getf("level") < 4 then
            server_timer("verifica_camera", 5, get_server_timer_arg())
        end
    end
end
BucatăCe face
d.select(get_server_timer_arg())„Lucrez pe instanța pentru care a fost armat ceasul.” Dacă instanța nu mai există, nu face nimic.
d.count_monster()Câți monștri vii mai sunt.
d.setf("vazut", 1)Notează că am văzut cel puțin un monstru.
d.getf("vazut") == 1 and n <= 0Trecem mai departe doar dacă au existat monștri și acum nu mai sunt.
server_timer(..., 5, ...)Se re-armează peste 5 secunde, cât timp dungeonul nu s-a terminat.

De ce „vazut”? Imediat după d.regen_file, monștrii pot apărea abia la următorul „puls” al serverului. Dacă verifici în aceeași clipă, d.count_monster() poate da 0 și grupul sare etapa fără să fi omorât nimic. Semnul „vazut” și o primă verificare după câteva secunde rezolvă problema.

Ceasul se pornește o singură dată, la intrare, de exemplu cu server_timer("verifica_camera", 5, d.get_map_index()), exact cum face flame_dungeon.quest.

Pasul 2: Piatra Metin și bossul final

lua
when PIATRA.kill with pc.in_dungeon() and d.getf("level") == 2 begin
    d.setf("piatra_cazuta", 1)
end

Atât. Evenimentul doar notează. Apoi, în același ceas de mai sus, adaugi o ramură:

lua
if d.getf("level") == 2 and d.getf("piatra_cazuta") == 1 then
    d.setf("piatra_cazuta", 0)
    d.setf("level", 3)
    d.clear_regen()
    d.kill_all()
    d.notice("Piatra a cazut! Bossul s-a trezit.")
    d.spawn_mob(BOSS, 686, 637)
end

Observă condiția with ... d.getf("level") == 2. Fără ea, o piatră identică dintr-o altă etapă ar declanșa bossul. Fiecare etapă are un singur declanșator, valabil doar în etapa ei.

Pasul 3: Oprește ceasurile la final

Un ceas care se re-armează la nesfârșit e o resursă irosită. În exemplul nostru, ceasul se oprește singur când level ajunge la 4 (bossul a murit). Ceasurile cu nume fix le poți opri și explicit, ca în pachet: clear_server_timer("verifica_camera", d.get_map_index()). Când instanța dispare, d.select întoarce fals și ceasul nu mai face nimic.

Despre monștri, grupuri și pietre ai mai citit în grupuri de monștri și respawnul pietrelor Metin.

Ce ar trebui să vezi: după ultimul monstru, în maxim 5 secunde apare anunțul și piatra; după piatră, monștrii rămași dispar și apare bossul. Serverul rulează ore întregi fără erori în syserr.

Greșeli des întâlnite

Ce veziCe înseamnăCe faci
Grupul sare etapa imediat după intrarecount_monster a dat 0 înainte să apară monștriiAdaugă semnul „vazut” și o primă verificare după 3–5 secunde.
Grupul rămâne blocat, deși nu mai vede monștriA rămas o piatră sau un monstru ascuns, care se numărăCaută-l pe hartă; verifică fișierul de spawn.
Crash la câteva ore după ce ai adăugat dungeonulȘtergere de monștri în when kill, probabil cu d.purge()Mută curățenia în ceas și folosește d.kill_all().
Bossul apare de două oriDeclanșatorul nu are condiția de etapăAdaugă d.getf("level") == ... și resetează semnul.
Monștrii continuă să apară după bossAi uitat d.clear_regen()Oprește spawnul la fiecare trecere.

Întrebări frecvente

De ce 5 secunde și nu 1?

O verificare la o secundă, în zeci de instanțe deodată, încarcă serverul degeaba. 3–6 secunde e un compromis bun; jucătorii nu simt diferența.

Pot folosi numărul total de kill-uri ca progres?

Nu e recomandat. Monștrii din alte etape, pietrele și monștrii chemați de pietre se amestecă. Folosește o condiție clară pentru fiecare etapă.

Cât testez?

Mult. Fiecare etapă, separat, de mai multe ori, apoi tot dungeonul cap-coadă, cu grup complet și incomplet, pe serverul de test.

Ce urmează

Bossul a murit. Acum vine partea sensibilă: recompensele fără pierderi și fără dubluri. Pentru dropul bossului în sine, vezi și cum testezi dropul unui boss.

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ă.

DiscordWhatsApp