aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Metin2

Metin2 arena-systeem: 1v1 duels en toernooien bouwen

Een Metin2 arena-systeem is aangepaste PvP-content waarin spelers elkaar bestrijden in veilige, eerlijke 1v1 duels en aan het eind een winnaar wordt bepaald. In deze gids loop ik door het bouwen van een duelarena vanaf nul met de Lua quest-engine: spelers koppelen, het tweetal naar een geïsoleerde map teleporteren, PvP alleen tussen die twee inschakelen, de winnaar detecteren en — als je wilt — het geheel aan een toernooischema koppelen. Het doel is niet om kant-en-klare code te plakken, maar de onderdelen te begrijpen zodat je het systeem aan je eigen server kunt aanpassen.

Hoe werkt een arena?

Een duelarena is in wezen een toestandsmachine die drie vragen beantwoordt: wie vecht tegen wie, waar ze vechten en wie heeft gewonnen. In Metin2 los je dit het schoonst op door het gevecht uit de gedeelde map te halen en het in een eigen map-instance te spelen. Zo zijn de twee tegenstanders afgeschermd van inmenging van buitenaf, kan geen andere speler de match verstoren en blijft de winst/verlies-teleport schoon.

  • Een registratie-NPC of -item — de speler gaat hiermee in interactie om aan de wachtrij/koppeling deel te nemen.
  • Een wachtrij — de lijst van spelers die op koppeling wachten; in de eenvoudigste vorm een paar globale flags.
  • Een arena-mapindex — het nummer van de lege map, gedefinieerd in het index-bestand, waar het duel wordt gespeeld.
  • Toestand-flags — wie in de wachtrij staat, wie in een match zit en wie heeft gewonnen, opgeslagen met pc.setqf en de globale set_quest_flag.

Het belangrijke onderscheid: per-spelerdata (zit deze speler in een match, wat is het id van de tegenstander) gaat in pc.setqf; serverbrede data (staat er iemand in de wachtrij) gaat in set_quest_flag. Metin2 heeft wel een ingebouwde duel- (challenge-)functie, maar dat is een confrontatie op de open map; voor een geïsoleerde, gecontroleerde en toernooiklare arena is het bouwen van je eigen quest-gebaseerde systeem veel flexibeler.

Spelers koppelen (matchmaking)

Het eerste deel van de arena is de wachtrij. Als een speler met de registratie-NPC praat, wordt hij gekoppeld aan wie al wacht; is er niemand, dan gaat hij zelf in de wachtrij. De eenvoudigste matchmaking gebruikt één globale flag die "het id van de wachtende speler" bevat:

quest arena_registratie begin
    state start begin
        when 20399.chat."Doe mee aan de arena" begin
            local mijn_id = pc.get_player_id()
            local wachtend = get_quest_flag("arena.wachtend_pid")

            if wachtend == 0 then
                -- wachtrij leeg: zet deze speler in de wacht
                set_quest_flag("arena.wachtend_pid", mijn_id)
                pc.setqf("arena_status", 1)        -- 1 = in de wachtrij
                say("Wacht terwijl er een tegenstander wordt gezocht...")
                return
            end

            if wachtend == mijn_id then
                say("Je staat al in de wachtrij.")
                return
            end

            -- koppeling gevonden: wachtrij legen en het gevecht starten
            set_quest_flag("arena.wachtend_pid", 0)
            arena_start(wachtend, mijn_id)
        end
    end
end

Hier geeft get_player_id() het unieke id van de speler terug; dit blijft hetzelfde, zelfs als de speler tussen maps wisselt, dus het is een veilige sleutel om tegenstanders te koppelen. arena_start is onze eigen hulpfunctie die de twee id's neemt en het gevecht opzet. Op een echte server zou je ook een niveau-/uitrustingsbeperking aan de wachtrij toevoegen, of een controle of de wachtende speler nog online is.

Teleporteren naar een geïsoleerde arena-map

Zodra een koppeling is gevonden, verplaatsen we beide spelers naar een verse arena-instance. d.new_jump_all maakt een nieuwe map-instance en teleporteert de aanroepende speler ernaartoe; om de tweede speler in dezelfde instance te krijgen, richten we op zijn pid en teleporteren hem naar dezelfde mapindex. Een korte aftelling vóór de match geeft beide spelers een moment om zich voor te bereiden:

function arena_start(pid1, pid2)
    -- schrijf tegenstander en status naar beide spelers
    pc.select_pc(pid1)
    pc.setqf("arena_status", 2)        -- 2 = in een match
    pc.setqf("arena_tegenstander", pid2)

    -- praktische aanpak: gedeelde instance + twee teleports:
    -- maak eerst de nieuwe instance met een speler, teleporteer
    -- daarna de ander naar dezelfde map via zijn pid.
end

In de praktijk is de robuustste aanpak: roep d.new_jump_all(ARENA_MAP_INDEX, x, y) aan met één speler om de instance te maken, selecteer dan de tweede speler met pc.select_pc(pid2) en teleporteer hem naar het tweede spawnpunt in diezelfde instance. Onthoud dat de coördinaten de lokale coördinaten van de map zijn — zet de twee spelers in tegenovergestelde hoeken zodat het gevecht eerlijk start. Een kleine "3... 2... 1... Vechten!"-aftelling via server_timer na de teleport verbetert de ervaring aanzienlijk.

PvP alleen tussen die twee inschakelen

Het cruciale punt van de arena is dat het gevecht alleen tussen de twee gekoppelde spelers plaatsvindt. De betrouwbaarste manier is de arena als een vrije-PvP-map in te stellen: in de mapinstellingen markeer je die map_index als PvP-ingeschakeld, zodat iedereen in de arena kan aanvallen — en je garandeert isolatie door slechts twee spelers per instance toe te laten. Aan de questkant zorg je dat spelers kunnen slaan zonder geblokkeerd te worden door de gilde-/gildeoorlog-flags die open maps regelen.

-- wanneer een speler de arena-map binnenkomt
when <ARENA_MAP_INDEX>.enter begin
    pc.setqf("arena_status", 2)
    -- ontsnapping blokkeren: teleport-items / ongewenste bescherming vergrendelen
    pc.remove_affect(...)             -- ongewenste beschermingseffecten verwijderen
    syschat("Je bent in de 1v1-arena. Veel succes!")
end

Als je toegang tot de source hebt, is de schoonste oplossing een controle als CHARACTER::IsAttackableMap aan te passen om de arena-map_index te openen en de vredeszone-regels binnen de arena uit te schakelen. Zonder de source aan te raken, kun je het toch beheren met een pure-quest-aanpak door de map als vrije-PvP te markeren en op isolatie te vertrouwen; voor kleine en middelgrote servers is dat ruim voldoende.

De winnaar detecteren

Een duel eindigt wanneer één kant sterft. In de Metin2 quest-engine draait de trigger when dead in de context van de speler die is gestorven; daar markeer je die speler als de "verliezer" en zijn tegenstander als de "winnaar". Dankzij het tegenstander-id dat we in pc.setqf("arena_tegenstander") hebben opgeslagen, kennen we de winnaar direct:

when dead with pc.getqf("arena_status") == 2 begin
    local verliezer = pc.get_player_id()
    local winnaar   = pc.getqf("arena_tegenstander")

    -- de verliezer opschonen en naar buiten teleporteren
    pc.setqf("arena_status", 0)
    pc.setqf("arena_tegenstander", 0)
    pc.warp(STAD_X * 100, STAD_Y * 100)

    -- de winnaar selecteren, belonen en naar buiten teleporteren
    if pc.select_pc(winnaar) then
        pc.setqf("arena_status", 0)
        pc.setqf("arena_tegenstander", 0)
        notice_all(pc.get_name() .. " heeft de arena gewonnen!")
        pc.give_item2(50300, 1)        -- voorbeeldbeloning: een trofee/item
        pc.warp(STAD_X * 100, STAD_Y * 100)
    end
end

Let op: pc.warp verwacht wereldcoördinaten vermenigvuldigd met 100 (in-game tegelcoördinaat × 100), terwijl teleporteren binnen een instance lokale coördinaten gebruikt — deze twee verwarren is de meest gemaakte beginnersfout. Voeg ook, zodat een disconnect geen ontsnappingsroute is, een "als je in een match zit, wint je tegenstander"-regel toe in when logout; anders kunnen spelers die op verliezen staan de stekker eruit trekken en het resultaat wissen.

Koppelen aan een toernooischema

Zodra het enkele duel werkt, is een toernooi gewoon dezelfde logica herhaald: winnaars gaan door naar de volgende ronde, verliezers vallen af. Je bewaart de deelnemers-id's in opeenvolgende flags (arena.ronde1_0, arena.ronde1_1, ...), schrijft de winnaar van elke match in de lijst van de volgende ronde wanneer die eindigt, en bouwt de koppelingen opnieuw op wanneer de ronde vol is.

  • Inschrijfvenster: accepteer aanmeldingen gedurende een vaste periode voordat het toernooi begint; bevries de lijst bij sluiting.
  • Oneven aantal (bye): als het aantal deelnemers oneven is, geef er één een automatische doorgang (bye); bereken dit bij het opbouwen van de lijst.
  • Opeenvolgende matches: je kunt één match tegelijk spelen en de arena bezet houden, of meerdere instances openen en rondes parallel draaien.
  • Eindbeloning: geef een speciaal kostuum, een titel of een yang-prijs alleen aan de winnaar van de laatste match.

Door het toernooi met een server_timer aan te sturen, kun je het een ritme geven als "bouw elke 5 minuten de koppelingen van de ronde". Omdat de logica identiek is aan een enkel duel, is het toernooi — zodra je een solide 1v1-kern hebt — slechts een schil eromheen.

Veelgemaakte fouten

  • Coördinaatverwarring: tegel × 100 voor pc.warp, lokale coördinaten voor teleports binnen een instance. Verwar je ze, dan valt de speler van de map.
  • Disconnect-exploit: zonder when logout-regel kan een speler die op verliezen staat uitloggen en het resultaat wissen. Behandel een logout als "de tegenstander wint".
  • Instance-lek: als beide spelers niet vertrekken wanneer de match eindigt, stapelen lege arena-instances zich op. Sluit zowel het sterf- als het uitgangspad met warp.
  • Flag-opschoning: als je de flags arena_status en arena_tegenstander niet aan het eind van een match reset, betreedt de speler zijn volgende match in een "half-afgemaakte" staat en breekt het systeem.

Veelgestelde vragen

Is het ingebouwde duelsysteem van Metin2 niet genoeg?

De ingebouwde challenge is een gevecht op de open map, in het volle zicht en open voor inmenging. Wil je een geïsoleerde arena, een eerlijke start, een winnaarsbeloning en vooral een toernooi, dan is je eigen quest-gebaseerde arena veel gecontroleerder en flexibeler. Je kunt beide ook naast elkaar aanbieden.

Kun je een arena bouwen zonder de source aan te raken?

Ja, in de meeste gevallen. Als je de arena-map als vrije-PvP markeert en slechts twee spelers per instance toelaat, krijg je een 1v1-systeem dat op pure questlogica draait. Voor zeer fijnmazige PvP-regels (bijv. bepaalde affects gedwongen verwijderen) maakt toegang tot de source het makkelijker, maar het is niet vereist.

Hoeveel matches kunnen tegelijk draaien?

Omdat elke match in zijn eigen map-instance draait, is de theoretische limiet je hardware; elke instance verbruikt geheugen en CPU. Op kleine tot middelgrote servers zijn tientallen gelijktijdige matches geen probleem. Bij zeer drukke toernooien is het veiliger het aantal instances te begrenzen en de matches in een wachtrij te zetten.

Wil je een eerlijke, geïsoleerde en misbruikbestendige arena voor je server? Van de matchmaking-wachtrij tot de geïsoleerde duelmap, van winnaardetectie tot een volledig toernooischema kan ik het hele systeem bouwen en testen. Om over je project te praten, neem contact met me op.

Bu kategorideki tüm yazılar →

Devamı için