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

Eveniment Metin2 cu timer și anunțuri: pornește, anunță și se oprește singur

2 noiembrie 2026 8 min de citit

În [lecția trecută](/blog/npc-eveniment-metin2/) am făcut un NPC care pornește „Vânătoarea de lupi”. Problema: evenimentul merge până când cineva își amintește să-l oprească. În lecția asta îi dăm un **ceas**: pornește pentru 30 de minute, anunță cu 5 minute înainte de final și se oprește singur. Și, la fel de important, vedem ce se întâmplă cu acest ceas dacă serverul repornește în mijlocul evenimentului.

Pe scurt: server_timer("nume", secunde) programează un bloc when nume.server_timer care rulează o singură dată, după numărul de secunde dat. Timerele trăiesc doar în memoria canalului pe care au fost pornite, deci se pierd la restart. De aceea salvăm și ora de final într-un event flag și o verificăm direct în efectul evenimentului.

Cuvinte pe care le vei întâlni

CuvântCe înseamnă
TimerUn „cronometru de bucătărie”: îl pornești acum, sună peste X secunde și atunci rulează un bloc din quest.
server_timerTimer al serverului, nelegat de un jucător. Merge și dacă nu e nimeni conectat.
timerTimer legat de un jucător. Se oprește dacă jucătorul iese din joc.
CoreUn proces game care rulează un canal (sau o parte din el). Fiecare are memoria lui.

Ce timere există în pachetul de referință

Toate funcțiile de mai jos apar în quest_functions:

FuncțiePentru ceCe se întâmplă la ieșire sau restart
timer("nume", secunde)Ceva legat de un jucător, de exemplu „peste 10 secunde, teleportează-l”Se oprește când jucătorul iese
loop_timer("nume", secunde)La fel, dar se repetăLa fel
server_timer("nume", secunde)Ceva pentru tot serverul, o singură datăSe pierde la restartul acelui core
server_loop_timer("nume", secunde)La fel, dar se repetăLa fel
clear_server_timer("nume", 0)Anulează un server_timer—
cleartimer("nume")Anulează un timer al jucătorului—

Pentru un eveniment global folosim server_timer. În sursa verificată, timpul e în secunde.

Codul complet

Este versiunea a doua a fișierului eveniment_lupi.quest din lecția trecută. Înlocuiește-l pe cel vechi (după backup).

lua
quest eveniment_lupi begin
    state start begin
        when 20011.chat."GM: Starea vanatorii" with pc.is_gm() begin
            say_title("Vanatoarea de lupi")
            local ramas = game.get_event_flag("ev_lupi_final") - get_time()
            say(string.format("ev_lupi = %d", game.get_event_flag("ev_lupi")))
            if ramas > 0 then
                say(string.format("Se termina in %d minute.", math.ceil(ramas / 60)))
            elseif game.get_event_flag("ev_lupi") == 1 then
                say("Timpul a expirat, dar flag-ul e inca 1. Apasa Opreste.")
            end
        end

        when 20011.chat."GM: Porneste vanatoarea (30 min)" with pc.is_gm() and game.get_event_flag("ev_lupi") == 0 begin
            say_title("Vanatoarea de lupi")
            say("Pornesti evenimentul pe 30 de minute, pe TOATE canalele?")
            local alegere = select("Da, porneste", "Nu")
            if alegere ~= 1 then
                return
            end
            if game.get_event_flag("ev_lupi") ~= 0 then
                say("Evenimentul e deja pornit.")
                return
            end
            game.set_event_flag("ev_lupi", 1)
            game.set_event_flag("ev_lupi_final", get_time() + 1800)
            notice_all("Vanatoarea de lupi a inceput! Dureaza 30 de minute.")
            server_timer("ev_lupi_5min", 1500)
        end

        when ev_lupi_5min.server_timer begin
            notice_all("Vanatoarea de lupi se termina in 5 minute!")
            server_timer("ev_lupi_stop", 300)
        end

        when ev_lupi_stop.server_timer begin
            game.set_event_flag("ev_lupi", 0)
            notice_all("Vanatoarea de lupi s-a terminat. Multumim!")
        end

        when 20011.chat."GM: Opreste vanatoarea" with pc.is_gm() and game.get_event_flag("ev_lupi") == 1 begin
            say_title("Vanatoarea de lupi")
            local alegere = select("Da, opreste", "Nu")
            if alegere == 1 then
                clear_server_timer("ev_lupi_5min", 0)
                clear_server_timer("ev_lupi_stop", 0)
                game.set_event_flag("ev_lupi", 0)
                notice_all("Vanatoarea de lupi a fost oprita.")
            end
        end

        when 102.kill with game.get_event_flag("ev_lupi") == 1 and get_time() < game.get_event_flag("ev_lupi_final") begin
            if number(1, 100) <= 5 then
                pc.give_item2(30028, 1)
            end
        end
    end
end

Explicat rând cu rând

Ce e nou față de lecția trecută:

RândCe înseamnă
game.set_event_flag("ev_lupi_final", get_time() + 1800)Salvăm ora de final: acum plus 1.800 de secunde (30 de minute). Fiind event flag, rămâne salvată și după restart.
server_timer("ev_lupi_5min", 1500)Pornim cronometrul: peste 1.500 de secunde (25 de minute) va rula blocul ev_lupi_5min.
when ev_lupi_5min.server_timer beginBlocul care rulează când „sună” cronometrul ev_lupi_5min.
notice_all("... in 5 minute!")Anunțul de avertizare, pentru toți.
server_timer("ev_lupi_stop", 300)Pornim al doilea cronometru: încă 5 minute până la final.
when ev_lupi_stop.server_timer beginLa final: punem flag-ul la 0 și anunțăm.
clear_server_timer("ev_lupi_5min", 0)La oprirea manuală, anulăm cronometrele rămase, ca să nu apară anunțuri după oprire. Al doilea argument (0) e un număr suplimentar pe care nu-l folosim aici.
and get_time() < game.get_event_flag("ev_lupi_final")Siguranța noastră: efectul merge doar până la ora de final, chiar dacă cronometrul s-a pierdut.
elseif game.get_event_flag("ev_lupi") == 1 thenÎn „Starea vânătorii”, îi spunem GM-ului dacă flag-ul a rămas 1 după ora de final.

Ce nu ai voie să faci într-un bloc server_timer

Un server_timer nu are un jucător „în față”. În sursa pachetului verificat, când rulează un astfel de bloc, nu există un personaj curent. Asta înseamnă:

  • Nu folosi funcții pc.* (pc.get_name, pc.give_item2, pc.setqf...) într-un bloc server_timer. Nu au pe cine să se aplice și pot provoca erori grave, inclusiv căderea canalului.
  • Nu folosi say() sau select(). Nu are cine să vadă fereastra.
  • Folosește doar lucruri globale: notice_all, game.set_event_flag, game.get_event_flag, alte server_timer.

Și încă o regulă: blocurile server_timer le pui în state start, ca în questurile de dungeon din pachetul de referință.

Ce se întâmplă la restart (cea mai importantă parte)

Gândește-te la evenimentul nostru ca la o mașină cu stări: oprit, pornit, „5 minute rămase”, terminat. Ce se întâmplă dacă serverul repornește în fiecare stare?

Când repornește serverulCe se pierdeCe rămâneRezultat cu codul nostru
Evenimentul e opritNimicev_lupi = 0Totul normal
În primele 25 de minuteCronometrul ev_lupi_5minev_lupi = 1, ora de finalLupii dau gheare până la ora de final, apoi se opresc singuri. Nu mai apar anunțurile de final. Flag-ul rămâne 1 până apeși „Oprește”.
În ultimele 5 minuteCronometrul ev_lupi_stopLa felLa fel
După finalNimicev_lupi = 0Totul normal

Fără ora de final (versiunea din lecția trecută), un restart în mijlocul evenimentului l-ar fi lăsat pornit pentru totdeauna, cu gheare gratuite pentru toți, până observă cineva.

Mai e un detaliu: cronometrele trăiesc pe core-ul game unde a fost apăsat butonul. Dacă pornești evenimentul de pe CH1 și îl oprești de pe CH2, clear_server_timer de pe CH2 nu găsește nimic, iar CH1 tot va anunța „s-a terminat” mai târziu. Nu strică nimic, dar arată ciudat. Regula simplă: pornești și oprești de pe același canal.

Nu porni același timer de două ori

În sursa verificată, dacă pornești un server_timer cu un nume care deja rulează, în syserr apare already registered server timer, dar al doilea cronometru tot pornește. Rezultatul: anunțuri duble. Condiția game.get_event_flag("ev_lupi") == 0 de la „Pornește” te protejează de asta în situațiile obișnuite.

Numele cronometrelor sunt comune pentru toate questurile. Dacă două questuri folosesc stop, se încurcă. Pune mereu un prefix: ev_lupi_stop, nu stop.

Testează pe serverul de test

Ca să nu aștepți 30 de minute, schimbă temporar 1800 în 180, 1500 în 120 și 300 în 60.

  1. Pornește evenimentul. Ar trebui să vezi anunțul de start.
  2. După 2 minute: anunțul „5 minute”. După încă un minut: „s-a terminat”.
  3. Pornește din nou și oprește manual după 30 de secunde: nu trebuie să mai apară niciun anunț după oprire.
  4. Pornește din nou și repornește serverul de test. După restart, la „Starea vânătorii” ar trebui să vezi ora de final; după ce trece, lupii nu mai dau gheare, iar mesajul îți spune să apeși „Oprește”.
  5. Pune la loc valorile reale (1800, 1500, 300) înainte de publicare.

Ce ar trebui să vezi: evenimentul se termină la timp, chiar și după un restart.

Greșeli des întâlnite

  • pc.* într-un bloc server_timer. Erori sau chiar căderea canalului.
  • Doar cronometru, fără ora de final salvată. După un restart evenimentul nu se mai oprește.
  • Nume de cronometru generic (stop, final). Se încurcă cu alte questuri.
  • Valorile de test uitate în fișier. Evenimentul de pe serverul live durează 3 minute.
  • /reload q cât timp evenimentul rulează. N-am verificat ce se întâmplă cu cronometrele în așteptare la reîncărcare. Fă modificările între evenimente, nu în timpul lor.

Întrebări frecvente

Pot face un eveniment care pornește singur, în fiecare sâmbătă?

Se poate, de exemplu cu un server_loop_timer care verifică ora, dar cronometrul trebuie pornit de cineva după fiecare restart. Pentru început, pornirea manuală de către GM, cu oprire automată, e mult mai ușor de controlat.

Pot afișa anunțuri la fiecare 10 minute?

Da: în blocul unui server_timer poți porni următorul server_timer, ca un lanț, exact cum am făcut cu ev_lupi_5min și ev_lupi_stop. Nu exagera cu anunțurile; jucătorii le ignoră dacă vin prea des.

Ce diferență e între notice și notice_all?

În pachetul de referință, notice afișează mesajul doar jucătorului care rulează questul, iar notice_all îl trimite tuturor, pe toate canalele.

Ce urmează

Ai acum misiuni, recompense și evenimente. Ultimul pas al capitolului e să le pui la încercare așa cum o vor face jucătorii: testezi questurile la relogare și restart.

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