[Guide][Wiki-Beitrag] How-To Total RP 3 Extended ❔

Extended am Sonntag #15


Ein Minispiel: Pairs

In Zereth Mortis gibt es gar keine neuen Puzzlespiele (hust), also muss wohl mit Extended nachgeholfen werden.
Diesmal geht es um das Kartenspiel Pairs. (auch unter dem Markennamen Memory bekannt)

  • Das Spiel besteht aus 32 Kartenpaaren mit unterschiedlichen Bildmotiven auf der einen Seite. Die Rückseite sieht bei allen Karten gleich aus.
  • Vor dem Spiel werden alle Kartenpaare gemischt und mit der Rückseite nach oben in einem Rechteck angeordnet.
  • Der Spieler darf pro Zug zwei Karten umdrehen, d.h. das Bild wird sichtbar.
  • Wenn beide Karten das selbe Motiv zeigen, dürfen die Karten aufgedeckt bleiben. (Im Originalspiel werden diese Karten aus der Anordnung entfernt und der Spieler erhält sie als Punkte.)
  • Wenn die Karten nicht das selbe Motiv zeigen, müssen sie wieder umgedreht werden und der nächste Spielzug beginnt.
  • Sobald alle Karten aufgedeckt wurden, ist das Spiel zuende.

Import-Code

Vor der Erklärung findet ihr hier das fertige Item zum Schnell-Importieren:

Pairs !LJvBZTnUr4)lA6hStVtMsQo1XZKpyl7ulxh5CbYj3nxdHbbxjItGaQaG2rrJ1V9UaK6DkL761V1rAiXlp4bl2DXIfmUv8G4(TW)rDWNTJjrDIoTDNorrD6pA6DM4(DqeKE99pt0mt6vAUV8GFjMC19XK76H1CXKlV3Zd5MlkREZ1X9)BNgHTpi(YycPRFmAvxKGUsbFSVkzG)PxciqPyGLePXel3iMGKWmJSLDqKAoto)roo(EQu4RZF8TZF0Pvf5jG5OrG7tmZrE8)W8hB8Cd)ZLyBC8XZVTIGNcd0dSPo53aUR5tmJD(TZVvme7T5qHX68sjIdbgHtsgOW(FS83MiwxGUfKwOKelW1Q0dYYgq2GMfykPY3H9xxBw)str16)TR3)k(wcyZPC1uxdL2m9ZQsvQPao0GwFE((JAtDvuDq2srudgy4q0oDudRUqLsfPulih6TWlm3K39Z(QV51D(74RHm0oC8kA8ML)uKEwDKQsln4)U8mo0QnW0s3ZWoSqxnASSr(cSd1g0ViuR1p4P7zrQlB(JVYxodeJYCZFmvxVpKyPbBblVDlHEJ2xxsR3tALSwvQztuSNjWXDEJYAp10xZ73SugcoNFPc85DA)sJVNPApZJuOg)Q1IOC0sf(BRMDrf0JF1wcvcJp2ksH1KIneJGjPQsPYhF(xrL(MAw(cDvLz4pIA6FPASlu(2BbkDZ8p2oIvZTOfJ56)rW)sHfFQIjT9n0E3WRPAEroOCuV)Wgrz3k4Ff3(i(Br)IFKpCXsQh8ZXKzzToN)YhyOt1StYA9YSjy153(xMficln7KjVuoYl7(pJ7Fwm59x5h87XJs6FFGr67Ij)dwomqRLoXKBawkyIjV7JXjXKp3R6CLBA5XDfyDc103PvUBkgbyZT9n)tfy7(gPvT2zbP(g7Rn5m5D46g76s)cG0)cKl94GAzXsZFcxVbRo76bl8h9uRDoNP2dFcB8M9IpmWoM5kxlCyNvtVl2Zx2DDEjl909HueQNOcvPCk0kAkZmoxRv(DoudmrBHg1styxCGJzRCoBWsesHBknfY1QScLdmurEUwY8S7JEwd4kCCJ2AXypWHH9SqM(VlepbM9GdN5Xu7eGzYyQ09cczJMZqzZLrtKOsH2Xcm3EPDctYsfkelgVsOgj9659G1W4cMKMykSzcNE4W9HtpQaOS8ee3EGyhp1amEgDOCkoRuEM3jDpGFMzmcTX)MBMUniEMaEccBZtqvnDmyWnUgM9BSdanTqncqpdUKzTcEv1CMTgvvnddLFoBIASHnA0bGpYl5weVtaMOwuMukyk(2luV76VbpdsUHn051hJ0YuqHkL6GMlSCAcYf6rtJAFG5xPtKakeixOkwZ9ESafoOmByBB)xUkskqVuV4LGT43WvJW96Of6ifWh3oPgimjpdYNsFteTWvAIDatwhstoA3nOOPsnGlJRT5GtWPP46MMaYTf1WG(kqBLrN80KudduAJTLhFRAGMWsM6kmoj0Mofqf6Z1bcNgQe3eLH7tnmrAP)TQEk5A9yVkcxCCHsXW9TjAfmuyZqFR64pfEsxyaJfJvlZlW5QM9GEGGAKqbO7Dj9JeFlxxdSHadZXB)RNHO83EFA8aPuwklNPCchm0GNLudUmqMJBEcHzqfXeHX7zvTBcJTgcYxLwNQ2tdwlNU9fU)21Umuvu5x2j5q1jT3mxqhdD6X0VSGXfYvYNM7mXYltjWQRYOpKW7lh)NE4BMERdYNG4gkftgO3vK)XwBkYv489NJ(znnyiED(r(PsTUOHSUmLB7VwoQVSQ)nBEfqXoyeLDhiCHOVJvyLAVSuDy25Ip7GOM798F1LauBQY(FDYYhiJIAZD(7NU8(C4))XSNtbhtiTH0kVUl9JxFb5((us3p27ddQN0W3OzdEl)cmK7FyroQytpqIltCTQjYfyrdWZC2XEZpOqpNICufHroN)izIaqxSVvGImGbtvGk8nCQ(woORAHbpo6o8ahzqOktA(deSsR3GcupK(FhjxsrODV3NgVpF7WTec5tJn8txG51hUobw5H((NFcBct0VBiV)R8Zv7O3GiVoaHe(SsxgUtqyqDd885R9dYT4AfKRqn4RpjQZjTXWHrrN35SZBFkc2pyqIli7poiZGNWGZxC)oTcCUtxD90C6cAA358orNh1P8wlxJx7X9PQVGgj9t4DdAEAtuWVd7SurIRzVfQNxaF)NdRQREi8YBIWLnPxm50V6l8rSqWS61gLe5IJ)pp

Das Item verwendet den Lua-Effekt. Um es zu benutzen, müsst ihr mit ALT+Rechtsklick Lua-Skripte für dieses Item erlauben.

Struktur

Das Item ist als Dokument-Item aufgebaut. Der Arbeitsablauf onUse im Item sorgt für das Mischen der Karten, während das innere Dokument sich um die Darstellung des Spielbretts sowie die einzelnen Spielzüge kümmert.
Das Spieldokument wird hierbei dynamisch erzeugt und nach jedem Zug aktualisiert. Dafür sorgt der Arbeitsablauf onCardClick im Dokument.

Einstellen des Spielbretts (onUse)

Grundeinstellungen

Im Lua-Effekt des Item-Arbeitsablaufs lege ich zunächst einige Variablen fest. Das erkennt man am vorangestellten args.object.vars.

  • width und height stellen die Breite und Höhe des Spielbretts ein.
  • backside ist das Icon, das die Rückseite der Karten darstellt.
  • icons ist die Liste der Motive.

Modellierung der Karten

Alle Karten werden in der Liste cards gespeichert. Jede einzelne davon hat zwei Eigenschaften:

  • index gibt an, um welche Karte es sich handelt. Ich verwende also keine Namen wie Kaninchenkarte, sondern simple Nummern von 1 bis 32, denn um 8x8 Karten zu belegen, brauche ich 32 Paare. Dieser Index wird sich nicht mehr ändern.
  • shown ist ein Wahrheitswert, der angibt, ob die Karte schon aufgedeckt wurde. Am Anfang ist dieser Wert bei allen Karten false, aber im Verlauf des Spiel werden diese nach und nach auf true gesetzt.
local n = args.object.vars.width * args.object.vars.height
local cards = {}
for i = 1, n/2 do
    table.insert(cards, {index = i, shown = false})
    table.insert(cards, {index = i, shown = false})
end

In diesem Code-Teil erkennt man, wie die Karten nach Paaren geordnet in die Liste eingefügt werden. table.insert steht zwei mal dort, denn für jedes Motiv gibt es zwei Karten.

Mischen der Karten

Wären die Karten bereits geordnet, wäre das Spiel ziemlich leicht, also sorgt der nächste Teil fürs Mischen:

local temp, flipTo
for i = 1, n-1 do
    flipTo = math.random(i, n)
    temp = cards[flipTo]
    cards[flipTo] = cards[i]
    cards[i] = temp
end

Alle Karten müssen vollständig gemischt werden, also benutze ich eine Abwandlung der Vorgehensweise aus dem Beitrag zum Kartenmischen.

  • Um festzustellen welche Karte an die erste Stelle soll, würfle von 1 bis 64. Vertausche dann die Karte an Position 1 mit der an der erwürfelten Position.
  • Für die zweite Karte, würfle von 2 bis 64, denn die Karte an Position 1 wurde bereits ausgewürfelt. Danach wird wie im ersten Schritt getauscht.
  • Fahre fort, bis alle Karten gemischt sind.

Vorbereiten des ersten Spielzugs

Schließlich speichere ich meine nun gemischte Kartenanordnung als Objektvariable (args.object.vars.cards = cards). Außerdem lege ich zwei weitere Variablen firstCard und secondCard fest. Darin halte ich fest, welche Karten der Spieler umdreht. Beide Variablen sind zum Anfang 0, d.h. der Spieler hat noch keine Karte angerührt.
Ganz am Ende des Lua-Codeblocks zeichne ich das Spielbrett board. Die Erläuterung dazu kommt weiter unten.

Auswerten der Spielzüge im Dokument (onCardClick)

Jedes Mal, wenn der Spieler auf eine Karte klickt, muss das Spiel angemessen reagieren. Das hängt einerseits davon ab, welche Karte angeklickt wurde, und andererseits davon, wieviele Karten bereits aufgedeckt sind.
Ganz am Anfang des Lua-Skripts importiere ich die Arbeitsablauf-Variable cardIndex:

local cardIndex = tonumber(getVar(args, "w", "cardIndex"))
local v = args.object.vars

cardIndex steht für die Position der angeklickten Karte, nicht für das Motiv.
In der zweiten Zeile lege ich eine Abkürzung fest, das erspart Tipparbeit.

Spielzuglogik

Danach folgt ein kleines Monster. Dieses Monster sind die in Code gegossenen Spielregeln:

if v.firstCard == 0 then -- 1
    v.firstCard = cardIndex
elseif v.secondCard == 0 then -- 2
    v.secondCard = cardIndex
    if v.cards[v.firstCard].index == v.cards[v.secondCard].index then -- 2A
        v.cards[v.firstCard].shown = true
        v.cards[v.secondCard].shown = true
        v.firstCard = 0
        v.secondCard = 0
        effect("sound_id_self", args, "SFX", 9637, false)
    else -- 2B
        effect("sound_id_self", args, "SFX", 9638, false)
    end
else -- 3
    v.firstCard = cardIndex
    v.secondCard = 0
end

Okay, einmal kurz durchatmen, wir haben es mit einer Wenn-Dann-Sequenz zu tun.

  • 1 beschreibt die Situation, wenn noch keine Karte umgedreht wurde. In diesem Fall setzen wir die Variable firstCard. D.h. die erste Karte ist die soeben umgedrehte Karte
  • Bei 2 wurde die zweite Karte umgedreht. Jetzt muss das Spiel überprüfen, ob ein Paar gefunden wurde.
  • 2A behandelt den Fall, dass ein Paar gefunden wurde. Wir setzen die Marke shown für beide Karten auf true. Dann setzen wir unsere beiden Variablen firstCard und secondCard wieder auf 0 zurück. Somit beginnt ein neuer Zug.
    Schließlich lasse ich noch einen Sound abspielen, in dem eine freundliche Elfe darauf hinweist, dass ein Paar gefunden wurde.
  • 2B behandelt den Fall, dass kein Paar gefunden wurde. Auch dieser Fall wird von der überaus freundlichen Bluthelfe kommentiert.
    Beachte, dass hier die Variablen firstCard und secondCard nicht zurückgesetzt werden, sonst würde der Spieler das falsche Paar nicht sehen.
  • Der letzte Fall 3 behandelt die Auswirkungen eines falschen Zuges. Sobald eine dritte Karte gezogen wird, werden die anderen beiden verdeckt.

Darstellung des Spielbretts

Die zweite Hälfte des Lua-Effekts befasst sich mit dem sichtbaren Teil des Spiels.

local board = ""
local c = 0
for i = 1, v.width * v.height do
    if v.cards[i].shown or i == v.firstCard or i == v.secondCard then -- 1
        board = board .. "{icon:" .. v.icons[v.cards[i].index] .. ":32}"
    else -- 2
        board = board .. "{link*onCardClick(cardIndex=" .. i .. ")*{icon:" .. v.backside .. ":32}}"
    end
    c = c + 1 -- 3
    if c == v.width then
        board = board .. "\n"
        c = 0
    end
end
args.object.vars.board = board

Für jede Karte wird ein passendes Icon angezeigt.

  • An der Stelle 1 überprüfe ich, ob die Karte gerade sichtbar ist. Eine Karte ist sichtbar, wenn sie bereits als Paar gefunden wurde (shown) ODER wenn sie die erste (firstCard) oder zweite (secondCard) eines Spielzugs ist.
    Eine sichtbare Karte kann man nicht anklicken (d.h. umdrehen), also wird die Karte als normales Icon dargestellt.
  • Wenn die Karte nicht sichtbar ist (Stelle 2), muss die Rückseite angezeigt werden (v.backside). Außerdem muss die Karte anklickbar sein. Das übernimmt der Teil {link*onCardClick(cardIndex=" .. i .. ")*. Das ist ein parametrisierter Dokument-Link, ein neues Feature mit Extended 1.5.0.
  • Damit das Spielbrett schön rechteckig aussieht, füge ich in regelmäßigen Abständen (3) einen Zeilenumbruch ein: board = board .. "\n". Die Sequenz \n erzeugt einen solchen Zeilenumbruch.

Platz für Verbesserung

Nachdem du das Spiel ausprobiert hast, kannst du dir eine Kopie des Items erstellen und dich an folgendem probieren:

  • Verändere die Motive, verwende z.B. nur Bilder, die zu einer bestimmten Thematik passen.
  • Vergrößere oder verkleinere das Spielbrett. Wenn du das Spielbrett vergrößerst, brauchst du auch mehr Motive.
  • Verwende andere Soundeffekte, z.B. ein Kommentar eines zufälligen Volks.
  • Füge eine besonderen Effekt ein, wenn der Spieler alle Karten aufgedeckt hat. Dazu wirst du ein paar Codezeilen brauchen, die das Spielende feststellen.

Viel Spaß beim Experimentieren und
:wave: bis zum nächsten Sonntag…

3 Likes