/
script.lua
executable file
·640 lines (434 loc) · 13 KB
/
script.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
-- LUA Memory Game
--
-- by DSI
--
version = "1.1"
num_images = 50
num_cards = 18 -- multiple of 2
num_rows = 3 -- can divide num_cards
num_columns = num_cards / num_rows
card_gap = 7
selected_row = 1
selected_column = 1
myfont = Font.createProportional()
myfont:setPixelSizes(0,11)
y_fix = 9
matches = 0
turns = 0
first_card_turned = 0
second_card_turned = 0
quit_early = false
best = 100
black = Color.new(0,0,0)
white = Color.new(255,255,255)
red = Color.new(255,0,0)
green = Color.new(0,255,0)
blue = Color.new(0,0,255)
score_file = "score.dat"
scroll_snd = "sounds/scroll.wav"
turn_snd = "sounds/turn.wav"
match_snd = "sounds/match.wav"
background = "misc/background.png"
cardback = "misc/cardback.png"
card_image = {}
card_width = "?"
card_height = "?"
card = {}
-- END of global variables
function loadFiles()
showLoading()
loadSounds()
loadImages()
loadHighScore()
-- Generate a random number seed based on the time
-- Note: sometimes the first number is not random, so call it twice
math.randomseed(os.time())
math.randomseed(os.time())
end -- loadFiles
function showLoading()
loadfont = Font.createProportional()
loadfont:setPixelSizes(0,18)
load_str = "Loading ..."
x = (480 - 10*string.len(load_str)) / 2 + 1
y = (272 - 18) / 2 + 1
screen:fontPrint(loadfont,x,y,load_str,green)
screen.waitVblankStart()
screen:flip()
end -- showLoading
function loadImages()
for i = 1, num_images do
card_image[i] = Image.load("cards/card" .. i .. ".png")
end
background = Image.load(background)
cardback = Image.load(cardback)
card_width = cardback:width()
card_height = cardback:height()
end -- loadImages
function loadSounds()
scroll_snd = Sound.load(scroll_snd)
turn_snd = Sound.load(turn_snd)
match_snd = Sound.load(match_snd)
end -- loadSounds
function getSelectedCardNum()
return (selected_row-1)*num_columns + selected_column
end -- getSelectedCardNum
function readInput()
pad = Controls.read()
-- Turn over card
if pad:cross() or pad:circle() then
current_card = getSelectedCardNum()
-- If card not already matched and it's not already turned over, turn it over
if not card[current_card].matched and not card[current_card].turned_over then
card[current_card].turned_over = true
if first_card_turned == 0 then
first_card_turned = current_card
turn_snd:play()
System.sleep(200)
else
second_card_turned = current_card
turns = turns + 1
-- Don't play sound yet, determine if it is matched
-- in drawEverything() first
end
end
waitForPadUp()
end
-- Quit game
if pad:start() then
waitForPadUp()
quit_early = true
return "quit"
end
-- Screenshot
if pad:select() then
screen:save("screenshot.png")
waitForPadUp()
end
if pad:up() then
scroll_snd:play()
setSelectedAbove()
waitForPadUp()
end
if pad:down() then
scroll_snd:play()
setSelectedBelow()
waitForPadUp()
end
if pad:left() then
scroll_snd:play()
setSelectedLeft()
waitForPadUp()
end
if pad:right() then
scroll_snd:play()
setSelectedRight()
waitForPadUp()
end
end -- readInput
-- Go to the next card above which is still unmatched
function setSelectedAbove()
original_row = selected_row
original_column = selected_column
while true do
-- If already at the topmost row
if selected_row == 1 then
selected_row = num_rows
else
selected_row = selected_row - 1
end
-- Keep moving if this card is matched / off the board
if not card[getSelectedCardNum()].matched then
break
end
end
if selected_row == original_row and selected_column == original_column then
setSelectedRight()
end
end -- setSelectedAbove
-- Go to the next card below which is still unmatched
function setSelectedBelow()
original_row = selected_row
original_column = selected_column
while true do
-- If already at the bottom row
if selected_row == num_rows then
selected_row = 1
else
selected_row = selected_row + 1
end
-- Keep moving if this card is matched / off the board
if not card[getSelectedCardNum()].matched then
break
end
end
if selected_row == original_row and selected_column == original_column then
setSelectedLeft()
end
end -- setSelectedBelow
-- Go to the next card on the left which is still unmatched
function setSelectedLeft()
while true do
-- If already at first column
if selected_column == 1 then
selected_column = num_columns
-- If already at first row
if selected_row == 1 then
selected_row = num_rows
else
selected_row = selected_row - 1
end
else
selected_column = selected_column - 1
end
-- Keep moving if this card is matched / off the board
if not card[getSelectedCardNum()].matched then
break
end
end
end -- setSelectedLeft
-- Go to the next card on the right which is still unmatched
function setSelectedRight()
while true do
-- If at rightmost
if selected_column == num_columns then
selected_column = 1
-- If at last row
if selected_row == num_rows then
selected_row = 1
else
selected_row = selected_row + 1
end
else
selected_column = selected_column + 1
end
-- Keep moving if this card is matched / off the board
if not card[getSelectedCardNum()].matched then
break
end
end
end -- setSelectedRight
--
-- waitForPadUp: Pauses execution till all buttons are released;
-- prevents scrolling too fast when pressing d-pad
--
-- This function is credited to Dark Killer at www.ps2dev.org
--
function waitForPadUp()
pad = Controls.read()
while pad:up() or pad:down() or pad:left() or pad:right() or pad:cross()
or pad:circle() do
pad = Controls.read()
end
end -- waitForPadUp
function drawEverything()
current_card = 0
match_x = 0
match_y = 0
-- Draw background
screen:blit(0, 0, background, 0, 0, background:width(), background:height(), false)
-- Draw cards
for row = 1,num_rows do
x = card_gap
y = (row-1)*card_height + row*card_gap
for column = 1, num_columns do
current_card = current_card + 1
if card[current_card].turned_over and (current_card == first_card_turned
or current_card == second_card_turned) and second_card_turned > 0 then
-- Second card has been turned over, check for match
if card[first_card_turned].id == card[second_card_turned].id then
card[current_card].matched = true
-- Show cards on a match; they will be taken off the board the next time
-- this function is called though
id = card[current_card].id
screen:blit(x, y, card_image[id])
if current_card == second_card_turned then
matches = matches + 1
end
end
end
if card[current_card].turned_over and not card[current_card].matched then
-- Show card if we turned it over
id = card[current_card].id
screen:blit(x, y, card_image[id])
elseif not card[current_card].matched then
-- Show the back (don't show the cards if they're a match)
screen:blit(x, y, cardback)
end
if row == selected_row and column == selected_column then
-- Card is currently selected with d-pad
screen:fillRect(x-4, y-4, card_width+8, 4, black)
screen:fillRect(x-4, y, 4, card_height, black)
screen:fillRect(x+card_width, y, 4, card_height, black)
screen:fillRect(x-4, y+card_height, card_width+8, 4, black)
end
x = x + card_width + card_gap
end -- column
end -- row
-- Status messages
screen:fontPrint(myfont,300, 100+y_fix, " v" .. version .. " By DSI", black)
screen:fontPrint(myfont,310, 140+y_fix, "MATCHED: " .. matches .. "/" .. num_cards/2, black)
screen:fontPrint(myfont,310, 160+y_fix, "TURNS: " .. turns, black)
screen:fontPrint(myfont,310, 180+y_fix, "BEST: " .. best, black)
screen:fontPrint(myfont,300, 210+y_fix, "CONTROLS:", black)
screen:fontPrint(myfont,300, 230+y_fix, "- D-pad to move", black)
screen:fontPrint(myfont,300, 240+y_fix, "- CROSS or CIRCLE to select", black)
screen:fontPrint(myfont,300, 250+y_fix, "- START to quit", black)
if first_card_turned == 0 then
status_string = "Pick first card"
screen:fontPrint(myfont,10, 250+y_fix, status_string, black)
screen.waitVblankStart()
screen:flip()
elseif second_card_turned == 0 then
status_string = "Pick second card"
screen:fontPrint(myfont,10, 250+y_fix, status_string, black)
screen.waitVblankStart()
screen:flip()
elseif second_card_turned > 0 then
if not card[second_card_turned].matched then
status_string = "Sorry, no match"
turn_snd:play()
System.sleep(500)
screen:fontPrint(myfont,10, 250+y_fix, status_string, black)
screen.waitVblankStart()
screen:flip()
screen.waitVblankStart(90)
card[first_card_turned].turned_over = false
card[second_card_turned].turned_over = false
else
status_string = "Match found!"
match_snd:play()
screen:fontPrint(myfont,10, 250+y_fix, status_string, black)
screen.waitVblankStart()
screen:flip()
screen.waitVblankStart(120)
if not gameEnd() then
if selected_column == num_columns then
setSelectedLeft()
else
setSelectedRight()
end
end
end
first_card_turned = 0
second_card_turned = 0
end
end -- drawEverything
function initDeck()
for i = 1,(num_cards/2) do
card[i] = { turned_over = false, matched = false, id = i }
-- The next half of cards have the same ID as those in the first half
-- (matched cards)
card[i + num_cards/2] = { turned_over = false, matched = false, id = i }
end
matches = 0
turns = 0
first_card_turned = 0
second_card_turned = 0
quit_early = false
end -- initDeck
function shuffleDeck()
-- Shuffle all of the card *images*
-- (There may be more images than those that are to be played)
for i = 1,num_images do
-- Swap current card with a random card in the set of images
rand = math.random(1,num_images)
temp = card_image[i]
card_image[i] = card_image[rand]
card_image[rand] = temp
end
-- Shuffle the IDs of the cards in the deck to be played
for i = 1,num_cards do
-- Swap current card with a random card in the deck
rand = math.random(1,num_cards)
if card[i].id ~= card[rand].id then
temp = card[i].id
card[i].id = card[rand].id
card[rand].id = temp
end
end
end -- shuffleDeck
function gameEnd()
if matches == num_cards/2 then
return true
else
return false
end
end -- gameEnd
function gameOverScreen()
screen:blit(0, 0, background, 0, 0, background:width(), background:height(), false)
screen:fontPrint(myfont,300, 100+y_fix, " v" .. version .. " By DSI", black)
-- Successfully finished game
if not quit_early then
screen:fontPrint(myfont,310, 140+y_fix, "GAME OVER", black)
screen:fontPrint(myfont,310, 160+y_fix, "TURNS: " .. turns, black)
if turns < best then
best = turns
screen:fontPrint(myfont,310, 180+y_fix, "NEW HIGH SCORE!", black)
saveHighScore()
else
screen:fontPrint(myfont,310, 180+y_fix, "BEST: " .. best, black)
end
else
-- We quit before finishing game
screen:fontPrint(myfont,310, 160+y_fix, "GAME OVER", black)
current_card = 0
for row = 1,num_rows do
x = card_gap
y = (row-1)*card_height + row*card_gap
for column = 1, num_columns do
current_card = current_card + 1
if not card[current_card].matched then
screen:blit(x, y, card_image[card[current_card].id])
end
x = x + card_width + card_gap
end
end
end
screen:fontPrint(myfont,300, 210+y_fix, "Paypal donations: ", black)
screen:fontPrint(myfont,300, 220+y_fix, "dislam@rocketmail.com", black)
screen:fontPrint(myfont,10, 250+y_fix, "Press TRIANGLE to restart; HOME to quit", black)
screen.waitVblankStart()
screen:flip()
-- Triangle to restart
while not Controls.read():triangle() do
-- Screenshot
if Controls.read():select() then
screen:save("screenshot_end.png")
waitForPadUp()
end
end
end -- gameOverScreen
function loadHighScore()
-- open file for reading
file = io.open(score_file, "r")
-- file not found
if file == nil then
saveHighScore()
-- if file found then grab the high score
else
best = file:read("*n")
file:close()
end
end -- loadHighScore
function saveHighScore()
file = io.open(score_file, "w")
if file then
file:write(best)
file:close()
end
end -- saveHighScore
---------------------
-- Main program
---------------------
loadFiles()
while true do
initDeck()
shuffleDeck()
while true do
drawEverything()
if gameEnd() or readInput() == "quit" then
break
end
end
gameOverScreen()
end