HU/Bevezetés a scriptelésbe

From Multi Theft Auto: Wiki
Jump to: navigation, search

A resourceok egy nagy része az MTAnak. A resource lényegében egy mappa vagy egy zip ami tartalmazza a fájlokat, plusz egy meta fájlt, ami leírja a szervernek, hogy a resource hogyan legyen betöltve, és milyen fájlokat tartalmaz. A resource részben hasonlít egy programhoz, ami az operációs rendszerünkön fut - lelehet állítani, ellehet indítani, és egyszerre több resource is futhat.

Minden ami a scripteléssel foglalkozik, a resourcekben történik, amit a resource tesz határozza meg, hogy az egy játéktípus, egy map vagy bármi más. Az MTA tartalmaz scripteket, amiket opcionálisan fel is használhatsz, mint pl egy map korlátozás, ami által csak a játszható térben lehetünk vagy deathpickupok, amivel létrehozhatunk fegyver felvételi pontokat.

[[{{{image}}}|link=]] Tipp: Az első lépés ahhoz, hogy elkezdj scriptelni, az az hogy használj egy LUA szerkesztőt. Ez megkönnyíti a scriptelést. Ajánlom a Sublime Text, a Notepad++ vagy a LuaEdit programot. Van egy nem hivatalos MTA Script Szerkesztő (fejlesztési státuszban, de nem tudok arról, hogy még fejlesztik) amit kipróbálhatsz.

Egy működő script készítése

Lépésről-lépésre megfogunk tanulni egy olyan alap script elkészítését, ami lehetővé teszi, hogy a karakterünk a városban sétáljon.

Hol találjuk meg a scripteket?

Először is tekintsük meg a script fájlok szerkezetét. Menj az MTA szerver mappájába és kövesd a következő útvonalat:

server/mods/deathmatch/resources/

Itt találni fogsz egy csomó .zip fájlt, amik az általános tömörített scriptek, amit az MTA hordoz magával. Minden fájl egy "resource", az összes kilesz csomagolva és ellesz indítva a szerver által, amikor az elindul. Ahhoz, hogy létrehozzuk a saját resourceunkat csak szimplán létre kell hoznunk egy mappát az általad preferált névvel. Mi most az "enszerverem" mappa nevet fogjuk használni az oktatáshoz. Hozzuk létre ezt a mappát, majd menjünk bele.

Most ebben a könyvtárban kell lenned:

server/mods/deathmatch/resources/enszerverem

A resourceok azonosítása

Ahhoz, hogy a szerver tudja, hogy mi van a resourceban, szükségünk lesz egy meta.xml fájlra a tartalmazott fájlokkal. Ennek a resource főkönyvtárában kell elhelyezkednie, ami az "enszerverem" az esetünkben. Szóval hozz létre egy szöveges dokumentumot, majd nevezd át "meta.xml"-re, és nyisd meg notepaddal.

Írd be a következő kódot a meta.xml fájlba.

<meta>
      <info author="A neved" type="gamemode" name="A szerverem" description="Az első MTA szerverem"/>
      <script src="script.lua"/>
</meta>

Az <info/> címkében láthatsz egy "type" mezőt ami eldönti, hogy ez a resource egy játéktípus, és nem egy map, vagy egy script, ami később lesz elmagyarázva. A játéktípus kell nekünk, hogy egy önálló szervert tudjunk létrehozni.

A <script/> címke dönti el azokat a script fájlokat, amit a resource tartalmaz. Ezeket fogjunk elkészíteni következőnek.

Egy egyszerű script létrehozása

Ne feledd, hogy a <script/> címkében a .lua nincs egy másik könyvtárban. Ez miatt ugyan ott fogjuk létrehozni, mint ahol a meta.xml-t. Hozd létre, majd nyisd meg azt a fájlt. Most már betudod másolni a következő kódot a script.lua fájlodba.

local spawnX, spawnY, spawnZ = 1959,55, -1714,46, 10
function csatlakozasKezelo()
	spawnPlayer(source, spawnX, spawnY, spawnZ)
	fadeCamera(source, true)
	setCameraTarget(source, source)
	outputChatBox ("Üdvözöllek a szerveremen.", source)
end
addEventHandler ("onPlayerJoin", getRootElement (), csatlakozasKezelo)

Amikor csatlakozunk, a script lespawnol minket arra a koordinátára ( x, y, z ), amint az elején adtunk meg neki. Ne feledjük, hogy a fadeCamera funkció szükséges, vagy különben fekete lesz a képernyő. Szintúgy, a DP2 kiadása után be kell állítanunk a kamera célját (különben minden játékos a kék eget fogja látni.)

A source változó jelenti, hogy ki hívta meg az eseményt. Mivel egy játékos csatlakozott, amikor a kódunk meglett hívva, ezért ezt a változót használjuk, ahhoz hogy megnézzük, hogy ki csatlakozott. Ez miatt nem fog mindenkit, vagy egy véletlenszerű játékost lespawnolni.

Ha egy közelebbi pillantást vetünk az addEventHandler-re, akkor láthatunk 3 dolgot: "onPlayerJoin", ami azt jelzi, hogy mikor legyen meghívva. getRootElement(), ami azt jelzi, hogy ki/mi által lehet meghívva. (A getRootElement() mindent/mindenkit jelent.) És joinHandler, ami azt jelzi, hogy melyik funckió legyen lefuttatva, az esemény meghívása után. Egyéb részletek hamarosan le lesznek írva egy másik példában, most indítsuk el a szerverünket, és próbáljuk ki!

A script futtatása

Ahhoz, hogy a szervert elindítsuk, egyszerűen futtassuk az "MTA Server" fájlt a server/ mappában. Egy lista fog kijönni először, a szerver adataival; jegyezd meg a port számát, ami a kapcsolódáshoz lesz szükséges. Ezután a szerver betölti a resourceokat a server/mods/deathmatch/resources/ mappából, és ezután "kész a csatlakozásokra".

Mielőtt kapcsolódnál a szerverre, el kell indítani a játékmódunkat. Írd be a "start enszerverem", parancsot, és nyomj entert. A szerver elindítja a játékmódot, amit létrehoztál, ettől a ponttól kezdve a hibákat és a figyelmeztetéseket is írni fogja. Most már elindíthatod az MTA klienst, majd a "Qyors Csatlakozás" gombbal csatlakozhatsz is az előbb látott IP-vel és porttal. Ha minden jól megy, pár másodperc múlva a karaktered megjelenik, és Los Santos utcáin járkál.

Következőnek egy parancsot fogunk létrehozni, amivel képesek leszünk egy kocsit létrehozni a helyzetünkön. Lehet, hogy kiszeretnéd ezt hagyni, és a haladó scriptelést szeretnéd megismerni a Map Manager használatával, ami ezt az oktatást folytatja. Egy másik ág a Bevezetés a GUI scriptelésbe, ami megmutatja hogy hogyan hozunk létre Grafikus Felhasználói Felületet MTAn belül.

Egy egyszerű parancs létrehozása

Menjünk vissza a script.lua fájl tartalmához. Ahogy említettük feljebb, egy parancsot akarunk szolgáltatni, amivel egy kocsit lehet létrehozni a jelenlegi helyzeteden. Először, létre kell hoznunk egy funkciót, amit megakarunk hívni és egy parancskezelőt, ami létrehozza a parancsot, amit a játékos betud írni a konzolba.

-- A funckió létrehozása, ezekkel az argumentumokkal: thePlayer, command, vehicleModel
function jarmuLetrehozasaJatekosnak(thePlayer, command, vehicleModel)
   -- A jármű létrehozása és még pár dolog
end

-- A parancskezelő létrehozása
addCommandHandler("jarmuletrehozas", jarmuLetrehozasaJatekosnak)

Megjegyzés: A funckió nevek megnyomhatóak a példákban, és a wikin elhelyezkedő funkció leírására dobnak.

A parancskezelőkről

Az első argumentuma az addCommandHandler funckiónak a parancs neve, amit a játékos betud majd írni, a második argumentuma amit ez megfog hívni, az esetünkben a jarmuLetrehozasaJatekosnak.

Ha már van némi tudásod a scriptelésben, akkor tudni fogod, hogy így hívjuk meg a funckiókat:

funkcioNeve(argumentum1, argumentum2, argumentum3, ..)
funkcioNeve(thePlayer, parancsNeve, argumentum3, ..)

Ha egy közelebbről megnézzük az alsó példát felettünk láthatjuk, hogy az argumentum1 a thePlayer és az argumentum2 a commandName. thePlayer simán az az egy, aki beírta a parancsot, szóval, akárminek nevezed el, ez a változó fogja tartalmazni a játékost, aki aktiválta a parancsot. A commandName simán a parancs, amit beírtak. Szóval ha beírták a "/üdvözlet", parancsot akkor ez az argumentum tartalmazza az "üdvözlet" szöveget. Az argumentum3 az plusz dolog, amit a játékos beírt. Erről egy kicsit később fogsz tanulni az oktatásban. Soha ne felejtsd el, hogy az első kettő argumentum standard, de akárminek elnevezheted.

Mi már meghívtuk az addCommandHandler funckiót ilyen módon, és mióta a jarmuLetrehozasaJatekosnak is egy ilyen funckió, ezért ilyen módon meghívható ő is. De e helyett mi egy parancskezelőt használunk, ami hasonló módon hívja meg.

Például: Valaki beírja a "jarmuletrehozas 468" parancsot a játék konzoljába, hogy lehívjon egy Sanchezt, a parancskezelő meghívja a "jarmuLetrehozasaJatekosnak" funkciót, az olyan mintha ezt a sort raktuk volna a scriptbe:

jarmuLetrehozasaJatekosnak(thePlayer,"jarmuletrehozas","468") -- A thePlayer a játékos, aki beírta a parancsot

Ahogy láthatjuk, ez pár paramétert szolgáltat: A játékost, aki beírta a parancsot, a parancsot, amit beírt és minden más szöveget, amit utána beírt, ebben a példában a "468" ami a Sanchez IDje a játékban. Az első kettő paraméter mindig ugyan az a parancskezelőknél, amit elolvashatsz az addEventHandler oldalnál. Ez miatt, mindig minimum kettő paramétert kell meghatározni, hogy akármi mást tudsz utána használni (például, ahhoz, hogy egy szöveget beolvassunk a parancs beírása után, a mi példánkban a jármű model IDje).

Megjegyzés: Minden esetben a funckió létrehozása UTÁN kell definiálni a parancskezelőt, különben nem fogja megtalálni. A meghívások sorrendje számít.

A funckió megírása

Ahhoz, hogy feltöltsük azt a funkciót, amit írtunk, először gondolkoznunk kell, hogy mit is akarunk csinálni:

  • Lekérni a játékos pozícióját, hogy megtudjuk, hogy hova is rakjuk le a kocsit (azt akarjuk, hogy pontosan a játékosnál jelenjen meg).
  • Számítsuk ki a pozíciót, ahova létrehozzuk a járművet (nem akarjuk, hogy a játékosban jelenjen meg).
  • Hozzuk létre a járművet.
  • Nézzük meg, hogy létre lett hova, vagy írjunk ki egy hibát.

Ahhoz, hogy elérjük a célunkat, szükséges lesz pár funkcióra. Ahhoz, hogy megtaláljuk a funkciókat, amiket használnunk kell, látogassunk el a Szerver Scriptelési Funckiókhoz. Először is, le kell kérnünk a játékos pozícióját. Mivel a játékosok elementek, először az Element funkcióhoz lépünk, ahol megtaláljuk a getElementPosition funkciót. A funkció nevére kattintással a listában, eljutsz a funkció leírásához. Itt láthatjuk a szintaxist, mit ad vissza, és általában egy példát. A szintaxis mutatja meg, hogy milyen argumentumokat lehet, vagy kell beírnunk.

A getElementPosition funkcióhoz szintaxis:

float, float, float getElementPosition ( element azElement )

A három float a visszaadási érték típusa. Ebben az esetben a funkció három lebegőpontos szám. (x, y és z) A zárójelekben láthatod, hogy milyen argumentumokat kell beírnod. Ebben az esetben csak az elementet, akinek leakarod kérni a pozícióját, ami a játékos a példánkban.

function jarmuLetrehozasaJatekosnak(thePlayer, command, vehicleModel)
	-- Kérjük le a koordinátát és rakjuk be az x, y, z változókban
	-- (A local azt jelenti, hgy a változó csak a jelenlegi területben létezik, ebben az esetben a funckió)
	local x, y, z = getElementPosition(thePlayer)
end

Következőnek to biztosítani akarjuk, hogy a jármű nem lesz létrehozva pontosan a játékosban szóval hozzáadunk pár egységet az x változóhoz, ami azt fogja eredményezni, hogy a játékostól keletre lesz létrehozva.

function jarmuLetrehozasaJatekosnak(thePlayer, command, vehicleModel)
	local x, y, z = getElementPosition(thePlayer) -- A játékos pozíciójának lekérése
	x = x + 5 -- 5 egység hozzáadása az "x" változóhoz
end

Most kell egy másik funckió, ami lehívja a járművet. Mégegyszer megkeressük a Szerver Funkciók Listájában, most - miután járművekről beszélünk - a Jármű funkciók résznél, ahol kifogjuk választani a createVehicle funkciót. Ennek a funkciónak a szintaxisában, csak egy visszaadási értékünk van (ami többször jellemző), egy kocsi element ami egy kocsira mutat, amit éppen létrehoztunk. Ugyanúgy látunk argumentumokat amik [ ] között vannak, ez azt jelenti, hogy nem kötelező.

Minden argumentumunk megvan a createVehicle-hoz a funkicónban: A pozíciók ami kiszámoltunk x, y, z változó és a model ID amit a parancson keresztül szolgáltattunk ("jarmuletrehozas 468") és eltudjuk érni a funkción belül, mint vehicleModel változó.

function jarmuLetrehozasaJatekosnak(thePlayer, command, vehicleModel)
	local x, y, z = getElementPosition(thePlayer) -- A játékos pozíciójának lekérése
	x = x + 5 -- 5 egység hozzáadása az "x" változóhoz
	-- Hozzuk létre a kocsit, és mentsük el a visszaadott értéket, mint a ''letrehozottJarmu'' változó.
	local letrehozottJarmu = createVehicle(tonumber(vehicleModel),x,y,z)
end

Persze ez a kód fejleszthető lenne nagyon sok módon, de legalább akarunk benne, egy ellenőrzést, hogy a kocsi sikeresen létre lett-e hozva, vagy sem. Ahogy olvashatjuk a createVehicle oldal alatt Returns-nél, a funckió visszaad egy false értéket, ha nem sikerült létrehozni a járművet. Ezért, ellenőrizzuk az értéket a letrehozottJarmu változónak.

Most már megvan a teljes script:

function jarmuLetrehozasaJatekosnak(thePlayer, command, vehicleModel)
	local x, y, z = getElementPosition(thePlayer) -- A játékos pozíciójának lekérése
	x = x + 5 -- 5 egység hozzáadása az "x" változóhoz
	-- Hozzuk létre a kocsit, és mentsük el a visszaadott értéket, mint a ''letrehozottJarmu'' változó.
	local letrehozottJarmu = createVehicle(tonumber(vehicleModel),x,y,z)
	-- Nézzük meg, hogy a visszaadási érték az ''false''
	if (letrehozottJarmu == false) then
		-- Ha igen, akkor írjunk ki egy üzenet, de csak ennek a játékosnak.
		outputChatBox("Nem sikerült létrehozni a járművet.",thePlayer)
	end
end
addCommandHandler("jarmuletrehozas", jarmuLetrehozasaJatekosnak)

Ahogy láthatod, megismertettük veled az outputChatBox funkciót is. Mostmár saját magadnak is képesnek kell lenned, hogy felfedezd a funkciók dokumentációs oldalát. Több haladó scriptért látogass el a Map Manager oldalra.

Amit tudnod kell

Már elolvastál pár dolgot a resourceokról, parancskezelőkről, és funkciók megtalálásáról a dokumentumokban az első paragrafusban, de még nagyon sok minden van, amit meg kell tanulnod. Ez a rész adni fog egy rövid bemutatót ezekről a dolgokról, miközben linkelgetek oldalakra, ha lehetséges.

Kliens oldali és Szerver oldali scriptek

Talán már találkoztál ezekkel, vagy hasonlókkal (Szerver/Kliens) valahol a wikin, legtöbbet a funkciókkal kapcsolatban. Az MTA nem csak a szerver oldalon futó scripteket támogatja, parancsokat szolgáltat ( mint amit feljebb írtunk ), vagy más funkciókat, hanem azokat a scripteket is, amik azon az MTA kliensen futnak, amit a játékosok a szerverekhez való csatlakozásra használnak. Ennek az az oka, hogy néhány funkciónak, amit az MTA szolgáltat, kliens oldalon kell lennie ( mint például a GUI - Grafikus Felhasználói Felület ), a többinek szerver oldalon kell lennie, mivel ott jobban működnek, vagy nem működnek kliens oldalon.

A legtöbb script, amit készíteni fogsz (játéktípusok, mappok) valószínűleg szerver oldalon lesznek létrehozva, mint amit írtunk az első részben. Ha belefutsz valamibe, amit nem lehet megoldani szerveroldalon, valószínűleg kliens oldalon kell megoldanod. Egy kliens oldali scripthez, létre kell hozni egy sima script fájlt, ( például nevezzük el client.lua-nak ) és jelezzük a meta.xml-ben így:

<script src="client.lua" type="client" />

A type tulajdonság alapból 'server', szóval csak akkor kell meghatározni, ha kliens oldali a script. Amikor ezt csinálot, a kliens oldali script le lesz töltve a játékos számítógépére amikor csatlakozik a szerverre. Többet olvass a Kliens Oldali Scriptek oldalon.

Összetettebb scriptek

Az előző rész tömören megmutatta nekünk, hogyan adjunk hozzá kliens oldali scripteket a resourcehoz, de van rá több másik mód is. Ahogy említettem az oldal tetején, a resource körülbelül minden lehet. Az a céljük az alapján van meghatározva, hogy mit csinálnak. Legyen egy elméleti resourceunk, a fájlokra nézve tartalmaz egy meta.xmlt.

Első példa

/admin_commands
	/meta.xml
	/commands.lua
	/client.lua
<meta>
	<info author="Someguy" description="Admin Parancsok" />
	<script src="commands.lua" />
	<script src="client.lua" type="client" />
</meta>
  • A commands.lua fájl szolgáltat pár admin parancsot, mint pl. játékos kitiltása, vagy némítása, vagy egyéb más, amivel a szervert moderálhatod.
  • A client.lua fájl szolgáltatja a GUI-t amivel egyszerűbben tudod elvégezni az akciókat.

Ez a példa talán mindig fut (esetleg még auto-indítva is van, amikor a szerver elindul) hiszen ez egy hasznos script az egész játéktapasztalat közben, és nem fogja zavarni a játékmenetet, hacsak egy admin nem dönt úgy, és tesz néhány akciót.

Második példa - Egy játékmód

/counterstrike
	/meta.xml
	/counterstrike.lua
	/buymenu.lua
<meta>
	<info author="Someguy" description="Counter Strike [REMAKE]" type="gamemode" />
	<script src="counterstrike.lua" />
	<script src="buymenu.lua" type="client" />
</meta>
  • A counterstrike.lua tartalmazza az ehhez hasonló funkciókat:
    • Engedélyezze a játékosokat a csapatváltáshoz, és hozza létre őket.
    • Fegyvereket, célpontokat és leírásokat szolgáltat.
    • Meghatározza a játékszabályokat, pl. mikor ér véget egy kör, mi történik ha egy játékos meghal
    • .. és talán még néhány dolog
  • A buymenu.lua egy kliens oldali script, ez hozza létra a fegyvervásárlás menüt.

Ezt a példát hívhatjuk egy játéktípusnak, hiszen nem csak a játékmenetbe avatkozik bele, de meghatározza a szabályait is. A type tulajdonság jelzi, hogy ez a példa a Map manager-rel működik, egy másik resource amit a QA Team írt, hogy kezelje a játéktípusait, és a map betöltést. Magasan ajánlott, hogy olyan technikákra alapozd a játékmódot, amit szolgáltat ez a resource.

Ez azt is jelenti, hogy a játékmód nem fog futni egy map nélkül. A játékmódoknak annyira kell generikusnak lennie, amennyire csak lehet. Egy példa a mapra a következő példában található.

Harmadik példa - Egy map

/cs-airport
	/meta.xml
	/airport.map
	/airport.lua
<meta>
	<info author="Someguy" description="Counter Strike Reptér Map" type="map" gamemodes="counterstrike" />
	<map src="airport.map" />
	<script src="airport.lua" />
</meta>
  • Az airport.map az XML fájlban szolgáltat információkat a mapról a játékmódnak, ami ezeket tartalmazhatja:
    • A játékosok hogyan legyenek létrehozva, milyen fegyverrel, és melyik csapatban
    • Mik a célpontok
    • Időjárás, Idő, Időlimit
    • Kocsikat szolgáltat
  • Az airport.lua talán tartalmaz különleges funkciókat amik ezek lehetnek:
    • Nyitható ajtók/robbanjon fel valami, amikor valami történik
    • Hozzon létre, vagy mozgasson egyedi objekteket, vagy manipulálja azokat az objekteket, amik a .map fájl által lettek létrehozva
    • .. Minden különleges, amit eltudsz képzelni

Ahogy láthatod, a type tulajdonság megváltozott 'map'-ra, jelezve a Map manager-nek, hogy ez a resource egy map, míg a gamemodes tulajdonság megmondja, hogy melyik játéktípushoz érvényes a pálya, ebben az esetben a felette lévő példában lévő játékmód. Ami talán meglepő, hogy egy script van a map resourceben. Persze ez nem kötelező egy mapban, de elég nagy szélességben megnyitja a lehetőségeket a map készítőknek, hogy létrehozhassák a saját világukat azokkal a szabályokkal, amivel létrehozták a játékmódot.

Az airport.map fájl hasonlóan nézhet ki:

<map mode="deathmatch" version="1.0">
	<terrorists>
		<spawnpoint posX="2332.23" posY="-12232.33" posZ="4.42223" skins="23-40" />
	</terrorists>
	<counterterrorists>
		<spawnpoint posX="2334.23443" posY="-12300.233" posZ="10.2344" skins="40-50" />
	</counterterrorists>

	<bomb posX="23342.23" posY="" posZ="" />
	
	<vehicle posX="" posY="" posZ="" model="602" />	
	<vehicle posX="" posY="" posZ="" model="603" />	
</map>

Ha egy játékmód egy mappal indul, a map automatikusan ellesz indítva a mapmanager által, és az információkat, amiket tárol, beolvashatóvá válnak a játéktípus resource által. Amikor egy map váltás van, az éppen futó map resource leáll, és a következő map resource elindul. Több mélyreható magyarázatért és példákért, hogy hogyan vannak felhasználva a map resourceok a fő scriptben, kérlek látogass el a Játékmód készítése oldalra.

Események

Az Események a módja, ahogy az MTA közli, hogy valami történt. Például, ha egy játékos meghal, az onPlayerWasted esemény lesz meghívva. Ahhoz, hogy valamit tudj tenni, amikor egy játékos meghal, fel kell készítened magad, hogy olyasmi, mint egy parancskezelő létrehozása, ahogy az első fejezetben leírtam.

Ez a példa kifog írni egy üzenet, a játékos nevével, aki meghalt.

function jatekosMeghalt(osszesTolteny, gyilkos, gyilkosFegyver, testresz)
	outputChatBox(getPlayerName(source).." meghalt!")
end
addEventHandler("onPlayerWasted",getRootElement(),jatekosMeghalt)

Ahelyett, hogy megmutatnám, milyen argumentumok szükségesek, az Események dokumentum oldala megmutatja, hogy milyen paramétereket kell megadni a függvénynek, éppen ugyan úgy, ahogy a parancskezelőknél is csak itt különbözik az eseményeknél. Egy másik fontos pont, a source változó, ami létezik a függvényeknél. Ennek nem kell szerepelni a paraméterlistában a funkciónál, de mégis létezik. Minden eseménynél más-más értéket vesz, játékos eseménynél (ahogy a példában) a játékos element. Egy másik példáért nézd meg az első részben létrehozott alap spawn scriptet, hogy hogyan is használjuk a source változót.

Innen hova tovább

Most már egy kicsit ismered az alap helyzetet az MTA scripteléssel és egy a kicsit dokumentációkkal kapcsolatban. A Főoldal több linkel szolgáltat még több információért, oktatásért, és referenciákért amivel mélyebb betekintést nyerhetsz azokban a témákban amire vágysz.

[[{{{image}}}|link=]] Megjegyzés: Innentől ajánlom a hibakeresés oktatást. A jó hibakeresési tudás feltétlen szükséges, amikor scripteket készítesz. Továbbá javaslom, hogy használd, az előre meghatározott változók listáját, hogy segítse, megkönnyítse, és gyorsabbá tegye a scriptelést.

Lásd még:

Fordította

  • NeXuS