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
undheight
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 wieKaninchenkarte
, 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 Kartenfalse
, aber im Verlauf des Spiel werden diese nach und nach auftrue
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 auftrue
. Dann setzen wir unsere beiden VariablenfirstCard
undsecondCard
wieder auf0
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 VariablenfirstCard
undsecondCard
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
bis zum nächsten Sonntag…