Show hiscore on gameover screen. Also fix: last score change wasn't shown
[generic-block-game.git] / gbg.bas
blob5dc821cfe05ab8ccfc0fb8bb0c89d04f150b46a2
1 /'
2 Generic Block Game
3 Copyright 2008, 2018, 2019 Pajo <xpio at tut dot by>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
19 Open Cons For Output As #3 ' debug
21 #include "fbgfx.bi"
22 Using fb
24 Const GFXPATH = "gfx"
25 Const VARPATH = "variations"
26 Const SPEED = 2
27 Const MAX_SPEED = 10
28 Const SPEEDUP_TIME = 30
29 Const SPEED_INDICATOR_SIZE = 8
30 Const SCORE_INDICATOR_SIZE = 16
31 Const DRAWPLAYFIELD_DELAY = 0.1
33 Type block
34 'Declare Constructor
35 Declare Sub init()
36 Declare Sub imprint(remove As Byte = True, place As Byte = True, noshadow As Byte = True)
37 Declare Sub drop()
38 Declare Function move(as Byte,as Byte=0,As UByte=0) As Byte
39 As UByte typ
40 As Short x,y
41 As Byte shadow
42 As Byte dropping = False 'Used when dropping with mouse
43 private:
44 As Short oldx,oldy
45 Declare Function isblocked(x as Byte, y As Byte=0, typ As UByte=0, mode As Byte=False) As Byte
46 As UByte oldtyp
47 End Type
50 Type settings_type
51 As Byte show_shadow = True, show_score = False, show_speed = False
52 As Byte play_mouse = True, first_button_turning = False
53 As UByte tileset = 0
54 End Type
57 Type game_type
58 As String title
59 As Short w, h
60 As UByte speed = 1
61 As Double tim, speed_timer, menu_timer
62 As Short score = 0, hiscore = 0
63 End Type
66 Dim Shared settings As settings_type
67 Dim Shared game As game_type
69 Dim Shared nextvariation As String
70 ReDim Shared help () As String
71 Dim Shared As UByte playabletiles, numberoftiles
72 Dim Shared As Short columns=1, rows=1
73 Dim Shared As Byte wrap, gravity, randominputs, randomgoals, inputrotate
74 ReDim Shared tilesfiles(0) As String
75 ReDim Shared randomtable() As UByte
76 Dim Shared keypress As String
77 Dim Shared tilewidth As Short
78 Dim Shared tilesimg As Any Ptr
79 Dim Shared scoreimg As Any Ptr
80 Dim Shared As block blok, shadow
81 Dim Shared As Byte lazydrawplayfield = False
82 Dim Shared As UByte maxgoalsw = 0, maxgoalsh = 0
85 Declare Function loadgame(filename As String) As Byte
86 Declare Sub loadTiles(increment As Byte=1)
87 Declare Sub initplayfield()
88 Declare Sub drop_shadow()
91 Sub resizewindow(w As UShort = game.w, h As Short = -1, t As String = game.title)
92 Dim As Integer oldw, oldh : ScreenInfo oldw, oldh
93 Dim As Integer oldx, oldy : ScreenControl GET_WINDOW_POS, oldx, oldy
94 If h = -1 Then
95 h = game.h
96 If settings.show_score Then h += SCORE_INDICATOR_SIZE
97 If settings.show_speed Then h += SPEED_INDICATOR_SIZE
98 End If
99 If oldw = w And oldh = h Then
101 Else
102 ScreenRes w, h, 16, 2
103 If oldx > 0 And oldy > 0 Then ScreenControl SET_WINDOW_POS, oldx, oldy
104 End If
105 WindowTitle(t)
106 End Sub
109 Function asctonumber(ch As UByte) As UByte
110 If ch=46 Then : Return 0
111 ElseIf ch=33 Then : Return 254 'start=!=254
112 ElseIf ch=Asc("@") Then : Return 253 'exit=@=253
113 ElseIf ch>=48 And ch<=57 Then : Return ch-48
114 ElseIf ch>=65 And ch<=90 Then : Return ch-65+10
115 ElseIf ch>=97 And ch<=122 Then : Return ch-97+36
116 Else : Return 255
117 End If
118 End Function
120 Function getImgWidth(filename As String) As Short
121 Open filename For Input As #2
122 If Err>0 Then Return 0
123 Dim w As Integer
124 Get #2,19,w
125 Close #2
126 Return w
127 End Function
129 ReDim Shared inputsindex(0) As Short
130 ReDim Shared inputs(0,2) As UByte
131 'inputs and inputsindex are linked arrays. inputsindex shows individual inputs' ends
132 ReDim Shared goals(0,2) As UByte
135 Sub end_shape(shapes() As UByte, index As Byte = False)
136 shapes(UBound(shapes), 0) = 255
137 shapes(UBound(shapes), 1) = 255
138 shapes(UBound(shapes), 2) = 255
139 If index Then
140 inputsindex(UBound(inputsindex)) = UBound(shapes)
141 ReDim Preserve inputsindex(UBound(inputsindex) + 1)
142 End If
143 ReDim Preserve shapes(UBound(shapes) + 1, 2)
144 End Sub
147 Sub rotate_shape()
148 Dim As Short strt, stp, a, offset
149 If UBound(inputsindex) = 1 Then strt = 0 Else strt = inputsindex(UBound(inputsindex) - 1 - 1) + 1 'skip "255" point
150 stp = inputsindex(UBound(inputsindex) - 1) - 1 'leave out "255" point
151 offset = stp - strt
152 ReDim Preserve inputs(UBound(inputs) + offset + 1, 2)
153 For a = strt To stp
154 'mirror one axis and swap x and y to rotate
155 inputs(a + offset + 2, 1) = inputs(a, 0)
156 inputs(strt + stp - a + offset + 2, 0) = inputs(a, 1)
157 inputs(a + offset + 2, 2) = inputs(a, 2)
158 Next
159 End Sub
162 Sub loadshapes(shapes() As UByte, index As Byte = False)
163 ReDim shapes(0, 2)
164 If index Then ReDim inputsindex(0)
165 Dim l as String
166 Dim As UByte a, b
167 Do Until l = "}" Or Eof(1)
168 Line Input #1, l
169 If l = "" Then
170 b = 0
171 end_shape(shapes(), index)
172 If inputrotate And index Then
173 For a = 1 To 3
174 rotate_shape()
175 end_shape(shapes(), index)
176 Next
177 End If
178 ElseIf l <> "}" Then
179 For a = 0 To Len(l) - 1
180 shapes(UBound(shapes), 0) = a
181 shapes(UBound(shapes), 1) = b
182 If l[a]=46 Then : shapes(UBound(shapes), 2) = 0
183 Else : shapes(UBound(shapes), 2) = asctonumber(l[a])
184 End If
185 ReDim Preserve shapes(UBound(shapes) + 1, 2)
186 Next
187 b += 1
188 If Not index Then
189 If Len(l) > maxgoalsw Then maxgoalsw = Len(l)
190 If b > maxgoalsh Then maxgoalsh = b
191 End If
192 End If
193 Loop
195 end_shape(shapes(), index)
196 If inputrotate And index Then
197 For a = 1 To 3
198 rotate_shape()
199 end_shape(shapes(), index)
200 Next
201 End If
202 If index Then ReDim Preserve inputsindex(UBound(inputsindex) - 1)
203 End Sub
206 Declare Sub gameOver()
208 Dim Shared playfield(0 to columns-1,0 to rows-1) as UByte
209 Dim Shared originalplayfield(0 to columns-1,0 to rows-1) as UByte
211 ReDim Shared nextshape() As UByte
213 Function block.isblocked(x as Byte, y As Byte=0, typ As UByte=0, mode As Byte=False) As Byte
214 Dim As Short strt, stp, a
215 Dim As Byte failed
217 If typ=1 Then strt=0 Else strt=inputsindex(typ-1-1)+1 'preskace se ona 255 tacka
218 stp=inputsindex(typ-1)-1 'izostavlja se ona 255 tacka
219 'mode=True = ispituje se origin
220 For a = strt To stp
221 failed=inputs(a,2)<>0 And _
222 playfield(x+inputs(a,0),y+inputs(a,1))<>254 And _
223 playfield(x+inputs(a,0),y+inputs(a,1))<>253
224 If Not mode Then failed=failed And playfield(x+inputs(a,0),y+inputs(a,1))<>0
225 failed=failed Or y+inputs(a,1)>=rows _
226 Or y+inputs(a,1)<0 _
227 Or x+inputs(a,0)>=columns _
228 Or x+inputs(a,0)<0
229 If failed Then Exit For
230 Next
231 Return failed
232 End Function
235 Sub block.init()
236 this.dropping = False
237 If randominputs Then 'permutovati brojeve od 1 do playabletiles
238 Dim a As UByte
239 ReDim randomtable(playabletiles) As UByte
240 For a = 1 To playabletiles
241 randomtable(a)=fix(rnd*playabletiles)+1
242 Next
243 EndIf
245 Type xy
246 As UByte x,y
247 End Type
248 ReDim startpositions(0) As xy
249 Dim posnumber As UShort=0
250 Dim As UShort x,y
251 Dim starttyp As UByte
252 this.typ=Fix(RND*(UBound(inputsindex)+1))+1
253 starttyp=this.typ
255 For y=0 to rows-1 : For x=0 to columns-1
256 If originalplayfield(x,y)=254 Then
257 If Not this.isblocked(x,y,this.typ,True) Then 'dodaj na listu
258 ReDim Preserve startpositions(posnumber) As xy
259 startpositions(posnumber)=Type(x,y)
260 posnumber+=1
261 EndIf
262 EndIf
263 Next x: Next y
264 If posnumber=0 Then
265 this.typ=nextshape(this.typ)
266 If this.typ=starttyp Then
267 gameOver()
268 Return
269 EndIf
270 EndIf
271 Loop Until posnumber>0
272 posnumber=fix(rnd*posnumber)
273 this.x=startpositions(posnumber).x
274 this.y=startpositions(posnumber).y
275 this.oldx=this.x
276 this.oldy=this.y
277 this.oldtyp=this.typ
278 If Not this.shadow Then drop_shadow()
279 End Sub
281 'Constructor block
282 'constructor bi se pokretao kod samog deklarisanja, pre playfielda
283 'End Constructor
285 Function getshapewidthheight(typ As UByte,geth As Byte = False) As Short
286 Dim As Short strt, stp, a, maxwidth=0, maxheight=0
288 If typ=1 Then strt=0 Else strt=inputsindex(typ-1-1)+1 'preskace se ona 255 tacka
289 stp=inputsindex(typ-1)-1 'izostavlja se ona 255 tacka
290 For a = strt To stp
291 If maxwidth<inputs(a,0) Then maxwidth=inputs(a,0)
292 If maxheight<inputs(a,1) Then maxheight=inputs(a,1)
293 Next
294 If geth Then Return maxheight
295 Return maxwidth
296 End Function
299 Function block.move(x as Byte, y As Byte=0, typ As UByte=0) As Byte 'successful?True/False
300 Dim As Byte dw,dh
301 If typ=0 Then
302 typ=this.typ
303 Else
304 '!centriranje
305 dw=(getshapewidthheight(this.typ)-getshapewidthheight(typ)) / 2
306 If gravity Then
307 dh=0
308 Else
309 dh=(getshapewidthheight(this.typ,True)-getshapewidthheight(typ,True)) / 2
310 EndIf
311 EndIf
312 this.oldx=this.x
313 this.oldy=this.y
314 this.oldtyp=this.typ
315 this.imprint(True, False) 'just erase oldx i oldy
317 this.x=x + this.x + dw
318 this.y=y + this.y + dh
319 If wrap Then
320 this.x=(this.x + columns) Mod columns
321 If Not gravity Then this.y=(this.y + rows) Mod rows
322 EndIf
324 failed=failed Or y+inputs(a,1)>=rows _
325 Or y+inputs(a,1)<0 _
326 Or x+inputs(a,0)>=columns _
327 Or x+inputs(a,0)<0
330 Dim As Byte failed
331 failed=this.isblocked(this.x,this.y,typ)
332 If failed Then
333 this.x=this.oldx
334 this.y=this.oldy
335 this.typ=this.oldtyp
336 Else
337 this.typ=typ
338 EndIf
339 If not this.shadow Then drop_shadow()
340 this.imprint()
341 Return Not failed
343 End Function
346 Sub markshape(shapestart As Short, shapeend As Short, x As Short, y As Short)
347 Dim As Short mx, my, q
348 For q = shapestart To shapeend - 1
349 mx = x - goals(q, 0)
350 my = y - goals(q, 1)
351 If mx >= 0 And mx < columns And my >= 0 And my < rows And _
352 goals(q, 2) <> 0 And playfield(mx, my) <> 0 And playfield(mx, my) < 100 Then
353 playfield(mx, my) += 100
354 End if
355 Next
356 End Sub
359 Function matchshape(x As Short,y As Short) As Byte
360 Dim As Byte fullshape,foundshape=False
361 Dim As Short mx,my,shapestart=0,p=0,q
362 Dim a As UByte
363 Dim goalscolors(100) As UByte 'za randomgoals '!max 100 randomgolova
364 fullshape=True
365 For p=0 To UBound(goals)
366 If goals(p,0)=255 And goals(p,1)=255 And goals(p,2)=255 Then '255,255,255=end
367 If fullshape=True Then
368 markshape(shapestart,p,x,y)
369 foundshape=True
370 End If
371 shapestart=p+1
372 fullshape=True
373 For a=0 To UBound(goalscolors) : goalscolors(a)=0 : Next a
374 ElseIf goals(p,2)<>254 Then
375 'do nothing with fullshape variable if 254(joker)
376 '254="!"=matches also empty space (useful when tiles surrounding matched shape should be destroyed)
377 mx=x-goals(p,0)
378 my=y-goals(p,1)
379 If mx<0 Or mx>columns-1 Or my<0 Or my>rows-1 Then
380 fullshape=False
381 ElseIf goals(p,2)=255 Then '255 matches everything except empty space
382 fullshape = fullshape And _
383 (playfield(mx,my)<>0) And _
384 (playfield(mx,my)<>254) And _
385 (playfield(mx,my)<>253)
386 ElseIf goals(p,2)<>0 Then
387 If randomgoals Then
388 If goalscolors(goals(p,2))=0 Then
389 If (playfield(mx,my)<>0) And _
390 (playfield(mx,my)<>254) And _
391 (playfield(mx,my)<>253)Then
392 goalscolors(goals(p,2))=playfield(mx,my)
393 Else
394 fullshape=False
395 EndIf
396 EndIf
397 fullshape = fullshape And (playfield(mx,my)=goalscolors(goals(p,2)) _
398 Or playfield(mx,my)=goalscolors(goals(p,2))+100)
399 Else
400 fullshape = fullshape And (playfield(mx,my)=goals(p,2) _
401 Or playfield(mx,my)=goals(p,2)+100)
402 EndIf
403 End If
404 End If
405 Next
406 Return foundshape
407 End Function
410 Sub score(inc As Short)
411 game.score += inc
412 If game.score > game.hiscore Then game.hiscore = game.score
413 End Sub
416 Sub checksituation()
417 Dim As Byte x, y, gap, removed=False, scoremulti=0, existsnextvariation=False, gotonextvariation=True
418 For y=0 To rows-1 + maxgoalsh : For x=0 To columns-1 + maxgoalsw
419 If matchshape(x,y) Then
420 scoremulti += 1
421 'delay speedup
422 game.speed_timer += (Timer() - game.speed_timer) * 0.5
423 End If
424 Next x: Next y
425 If gravity Then 'remove >100 (marked shapes)
426 For x=0 To columns-1
427 gap=0
428 For y=rows-1 To 0 Step -1
429 If playfield(x,y)>=100 And playfield(x,y)<>254 And playfield(x,y)<>253 Then
430 gap+=1
431 Else
432 If playfield(x,y)=254 Then
433 If originalplayfield(x,y)=254 Or originalplayfield(x,y)=253 Then
434 playfield(x,y+gap)=originalplayfield(x,y)
435 Else
436 playfield(x,y+gap)=0
437 EndIf
438 Else playfield(x,y+gap)=playfield(x,y)
439 End If
440 End if
441 If y<gap Then
442 If originalplayfield(x,y)=254 Or originalplayfield(x,y)=253 Then
443 playfield(x,y)=originalplayfield(x,y)
444 Else
445 playfield(x,y)=0
446 EndIf
447 EndIf
448 Next y
449 removed=removed or gap>0
450 Next x
451 If removed Then checksituation()
452 Else 'no gravity
453 For y=0 To rows-1 : For x=0 To columns-1
454 If playfield(x,y)>100 And playfield(x,y)<>254 And playfield(x,y)<>253 Then
455 If originalplayfield(x,y)=254 Or originalplayfield(x,y)=254 Then
456 playfield(x,y)=originalplayfield(x,y)
457 Else
458 playfield(x,y)=0
459 EndIf
460 EndIf
461 Next x: Next y
462 EndIf
463 score(scoremulti * 10)
464 'check exit
465 For y=0 To rows-1 : For x=0 To columns-1
466 If originalplayfield(x,y)=253 Then
467 existsnextvariation=True
468 If playfield(x,y)=253 Then gotonextvariation=False
469 EndIf
470 Next x: Next y
471 If existsnextvariation And gotonextvariation Then
472 loadgame(VARPATH+"/"+nextvariation)
473 loadTiles(0)
474 game.speed = 1
475 initplayfield()
476 EndIf
477 End Sub
480 Sub putshape(x As Short, y As Short, typ As UByte, nodel As Byte, noshadow As Byte)
481 Dim As Short strt, stp, a
482 Dim As Short o
483 'skip the 255-point that marks the end of the previous shape
484 If typ = 1 Then strt = 0 Else strt=inputsindex(typ - 1 - 1) + 1
485 stp = inputsindex(typ - 1) - 1 'leave out 255-point
486 For a = strt To stp : If inputs(a, 2) <> 0 Then
487 ' determine output
488 If nodel Then
489 If Not noshadow Then
490 'don't overwrite blocks with shadow
491 o = playfield(x + inputs(a, 0), y + inputs(a, 1))
492 If o = 0 Or o = 254 Then o = 253
493 ElseIf randominputs Then
494 o = randomtable(inputs(a, 2))
495 Else
496 o = inputs(a, 2)
497 EndIf
498 Else 'delete
499 o = originalplayfield(x + inputs(a, 0), y + inputs(a, 1))
500 'preserve special tiles, but don't restore blocks (if any) from the beginning
501 If Not (o = 254 Or o = 253) Then o = 0
502 EndIf
503 ' put
504 playfield(x + inputs(a, 0), y + inputs(a, 1)) = o
505 End If : Next
506 End Sub
509 Sub block.imprint(remove As Byte = True, place As Byte = True, noshadow As Byte = True)
510 If (Not (settings.show_shadow And gravity) Or noshadow) And this.shadow Then Return
511 If remove Then putshape(this.oldx, this.oldy, this.oldtyp, False, noshadow) 'delete old shape
512 If place Then putshape(this.x, this.y, this.typ, True, noshadow) 'put new shape
513 End Sub
516 Sub block.drop()
517 If Not this.move(0,1) Then
518 checksituation()
519 this.init()
520 this.imprint()
521 EndIf
522 If Not this.shadow Then game.tim = Timer()
523 End Sub
526 Sub initplayfield
527 ReDim playfield(0 To columns-1,0 To rows-1) As UByte
528 Dim As UByte x,y
529 For y=0 To rows-1 : For x=0 To columns-1
530 playfield(x,y)=originalplayfield(x,y)
531 Next x : Next y
532 End Sub
535 Declare Sub drawplayfield()
537 Sub loadTiles(increment As Byte)
538 settings.tileset += increment
539 If settings.tileset > UBound(tilesfiles) Then settings.tileset = 0
540 If settings.tileset < 0 Then settings.tileset = UBound(tilesfiles)
542 Dim tilesfile As String
543 tilesfile=GFXPATH & "/" & tilesfiles(settings.tileset)
544 tilewidth = getImgWidth(tilesfile)
545 game.w = tilewidth*columns : game.h = tilewidth*rows
546 tilesimg = ImageCreate(tilewidth, tilewidth*(3+numberoftiles))
547 BLoad tilesfile, tilesimg
548 scoreimg = ImageCreate(130,20)
549 BLoad GFXPATH+"/brojke.bmp", scoreimg
550 resizewindow()
551 ScreenSet 0,1
552 drawplayfield()
553 End Sub
556 Sub drawscore(hi As Byte = False)
557 Dim As UByte a,digit
558 Dim As Byte numberstarted=False
559 Dim As UShort printscore = game.score
560 Dim As UShort offsety = 0
561 If hi Then
562 printscore = game.hiscore
563 offsety = 20
564 End If
566 Dim xpx As UShort = tilewidth*columns
567 Line(0,offsety)-(xpx,offsety + 16),RGB(0,0,0),BF
568 For a=0 To 9 'max 10 digits - traze se otpozadi
569 If 10^a > printscore Then Exit For
570 digit=Fix((printscore Mod 10^(a+1)) / 10^a)
571 If digit=1 Then
572 xpx-=5
573 Put (xpx,offsety + 1),scoreimg,(13,0)-(17,14),PSet
574 ElseIf digit=0 Then
575 xpx-=13
576 Put (xpx,offsety + 1),scoreimg,(0,0)-(12,14),PSet
577 Else
578 xpx-=13
579 Put (xpx,offsety + 1),scoreimg,(-8+digit*13,0)-(-8+12+digit*13,14),PSet
580 EndIf
581 Next
582 If printscore > 0 And printscore = game.hiscore Then
583 xpx-=13
584 Put (xpx,offsety + 1),scoreimg,(122,0)-(130,14),PSet
585 End If
586 End Sub
589 Sub drawspeed()
590 Dim y As Short = 0
591 If settings.show_score Then y += SCORE_INDICATOR_SIZE
592 Line (0, y) - (game.w, y + 7), RGB(22, 22, 22), BF ' background
593 Line (0, y) - (game.w * game.speed / 10, y + 3), RGB(222, 222, 222), BF
594 Line (0, y + 4) - (game.w * (Timer() - game.speed_timer) / SPEEDUP_TIME, y + 7), RGB(166, 166, 233), BF
595 End Sub
598 Sub drawplayfield()
599 ' Prevent too much redraws
600 Static lastdrawplayfield As Double = 0
601 If Timer() - lastdrawplayfield < DRAWPLAYFIELD_DELAY Then
602 lazydrawplayfield = True
603 Return
604 End If
605 lastdrawplayfield = Timer()
606 lazydrawplayfield = False
608 shadow.imprint(False, True, False)
610 Dim As Short offsetx=0,offsety=0,offsetpx=0,offsetpy=0
611 If settings.show_score Then offsetpy += SCORE_INDICATOR_SIZE
612 If settings.show_speed Then offsetpy += SPEED_INDICATOR_SIZE
613 Dim As UByte x,y,t
614 for y=0 to rows-1 : for x=0 to columns-1
615 If playfield(x,y)=0 Then : t=0
616 ElseIf playfield(x,y)=254 Then : t=1
617 ElseIf playfield(x,y)=253 Then : t=2
618 Else : t=playfield(x,y)+2
619 End If
620 Put (offsetpx+(offsetx+x)*tilewidth, offsetpy+(offsety+y)*tilewidth), _
621 tilesimg,(0,t*tilewidth) - Step(tilewidth-1,tilewidth-1),PSet
622 Next x: Next y
624 shadow.imprint(True, False, False)
626 If settings.show_score Then drawscore()
628 ScreenSync
629 ScreenCopy
630 End Sub
633 Sub loadtilesets()
634 Dim l As String, n As UByte=0
635 Do Until l="}" Or Eof(1)
636 Line Input #1,l
637 ReDim Preserve tilesfiles(n)
638 tilesfiles(n)=l
639 n+=1
640 Loop
641 ReDim Preserve tilesfiles(n-2)
642 End Sub
645 Sub loadcycle()
646 Dim l As String, n As UByte
647 If inputrotate Then
648 ReDim nextshape(UBound(inputsindex) + 1) As UByte
649 For n = 0 To UBound(inputsindex) Step 4
650 nextshape(n + 1) = n + 2
651 nextshape(n + 2) = n + 3
652 nextshape(n + 3) = n + 4
653 nextshape(n + 4) = n + 1
654 Next
655 Else
656 Line Input #1,l
657 ReDim nextshape(Len(l)) As UByte
658 For n = 0 To Len(l) 'nextshape(0) is not used
659 nextshape(n+1)=asctonumber(l[n])
660 Next
661 EndIf
662 End Sub
665 Sub loadplayfield()
666 columns=0 : rows=0
667 Dim l As String, n As UByte
668 Dim position As Integer = Seek(1)
669 Dim As Short a,b=0
670 Do Until l="}" Or Eof(1)
671 rows+=1
672 Line Input #1,l
673 If Len(l)>columns Then columns=Len(l)
674 Loop
675 ReDim originalplayfield(0 to columns-1,0 to rows-1) as UByte
676 rows-=1
677 Seek #1,position
678 l=""
679 Do Until l="}" Or Eof(1)
680 b+=1
681 Line Input #1,l
682 For a=0 To Len(l)-1
684 If l[a]=46 Then : originalplayfield(a,b-1)=0
685 Else : originalplayfield(a,b-1)=asctonumber(l[a])
686 End If
688 n=asctonumber(l[a])
689 originalplayfield(a,b-1)=n
690 If n>numberoftiles And n<100 Then numberoftiles=n
691 Next
692 Loop
693 If playabletiles>numberoftiles Then numberoftiles=playabletiles
694 End Sub
697 Sub loadsettings()
698 wrap=False
699 gravity=False
700 inputrotate=False
701 randominputs=False
702 randomgoals=False
703 Dim l As String
704 Do Until l="}" Or Eof(1)
705 Line Input #1,l
706 wrap=wrap Or l="wrap"
707 gravity=gravity Or l="gravity"
708 inputrotate=inputrotate Or l="inputrotate"
709 randominputs=randominputs Or l="randominputs"
710 randomgoals=randomgoals Or l="randomgoals"
711 If Left(l,6)="tiles:" Then playabletiles=CInt(Mid(l,7))
712 Loop
713 End Sub
716 Sub drop_shadow()
717 If Not (settings.show_shadow And gravity) Then Return
718 shadow.x = blok.x
719 shadow.y = blok.y
720 shadow.typ = blok.typ
721 shadow.shadow = True
722 Do : Loop While shadow.move(0,1)
723 End Sub
726 Sub gameRestart()
727 game.score = 0
728 game.speed = 1
729 initplayfield()
730 blok.init()
731 blok.imprint()
732 drawplayfield()
733 End Sub
736 Sub loadhelp()
737 ReDim help(0)
738 Dim l As String
739 Do Until Eof(1)
740 Line Input #1,l
741 If l="}" Then Exit Do
742 ReDim Preserve help(UBound(help) + 1)
743 help(UBound(help)) = l
744 Loop
745 End Sub
748 Function loadgame(filename As String) As Byte
749 If filename = "" Then Return False
750 Dim l As String, loadedcycle As Byte
751 Open filename For Input As #1
752 If Err>0 Then Print "Error opening the file":End
754 Line Input #1,l
755 Select Case l
756 Case "Title {"
757 Line Input #1,l
758 game.title = l
759 Case "Settings {"
760 loadsettings()
761 Case "Next {"
762 Line Input #1,nextvariation
763 Case "Tilesets {"
764 loadtilesets()
765 Case "Playfield {"
766 loadplayfield()
767 Case "Input {"
768 loadshapes(inputs(),True)
769 Case "Cycle {"
770 loadcycle()
771 loadedcycle = True
772 Case "Goals {"
773 loadshapes(goals())
774 Case "Help {"
775 loadhelp()
776 End Select
777 Loop Until Eof(1)
778 Close #1
779 If Not loadedcycle Then loadcycle()
780 game.hiscore = 0
781 gameRestart()
782 loadTiles(0)
783 Return True
784 End Function
787 Sub togglescore()
788 settings.show_score = Not settings.show_score
789 resizewindow()
790 ScreenSet 0,1
791 drawplayfield()
792 End Sub
795 Sub quit()
796 ImageDestroy(tilesimg)
797 ImageDestroy(scoreimg)
798 Close #3 'Debug
800 End Sub
802 #include "menus.bi"
805 Sub drop_but_dont_fix(y As Integer)
806 If Int(y / tilewidth - blok.y) <= 0 Then Return
807 If blok.move(0,1) Then
808 game.tim = Timer()
809 drop_but_dont_fix(y)
810 End If
811 blok.imprint()
812 End Sub
815 Function windowmouse(mode As String) As String
816 Dim e As EVENT
817 Static pressed As Integer = 0
818 If (ScreenEvent(@e)) Then
819 Select Case e.type
820 Case EVENT_MOUSE_BUTTON_PRESS
821 If mode = "gameover" Then Return " " 'Space=end gameover
822 If settings.play_mouse Then
823 If e.button=1 And gravity Then
824 If settings.first_button_turning And Int(e.y / tilewidth - blok.y) <= 0 Then
825 Return " " 'Space = turn
826 Else
827 drop_but_dont_fix(e.y)
828 blok.dropping = True
829 End If
830 drawplayfield()
831 End If
832 If e.button=2 Then Return " " 'Space = turn
833 End If
834 If Not settings.play_mouse Or e.button>2 Then Return Chr(27) 'Esc = open menu
835 Case EVENT_MOUSE_BUTTON_RELEASE
836 If settings.play_mouse And e.button=1 Then
837 If Not gravity Or blok.dropping Then Return Chr(13) 'Enter = drop
838 End If
839 Case EVENT_WINDOW_CLOSE
840 quit()
841 Case EVENT_MOUSE_MOVE
842 If e.x > 0 And e.x < game.w And settings.play_mouse And mode <> "gameover"Then
843 While Int(e.x / tilewidth - blok.x) <> 0 And blok.move(Sgn(Int(e.x / tilewidth - blok.x)))
844 WEnd
845 If gravity Then
846 If blok.dropping Then drop_but_dont_fix(e.y)
847 Else
848 While Int(e.y / tilewidth - blok.y) <> 0 And blok.move(0, Sgn(Int(e.y / tilewidth - blok.y)))
849 WEnd
850 End If
851 drawplayfield()
852 End If
853 End Select
854 End If
855 End Function
858 Sub gameOver()
859 Dim As Short starty = 0, y = 0, endy
860 Dim As Byte scoredrawn = False, hiscoredrawn = False
861 Dim tim2 As Double
862 If settings.show_score Then starty += SCORE_INDICATOR_SIZE
863 y = starty
864 endy = game.h + starty - 6
865 If settings.show_speed Then endy += SPEED_INDICATOR_SIZE
866 game.tim = Timer()
867 tim2 = Timer()
868 keypress = ""
870 If Timer() - tim2 > 0.5 Then
871 keypress = InKey()
872 If keypress = "" Then keypress = windowmouse("gameover")
873 Else
874 While InKey() <> "" : Wend 'prevents exit from gameover screen for 0.5 seconds
875 EndIf
877 If y < endy Then
878 If Timer() - game.tim > 0.03 Then
879 game.tim = Timer()
880 Line (0, y) - (game.w, y + 6), RGB(0, 0, 0), BF
881 Put(game.w / 2 - 47 / 2, y + 1), scoreimg,(52, 15) - (99, 19), PSet
882 y += 1
883 ScreenSync
884 ScreenCopy
885 EndIf
886 EndIf
887 If y > SCORE_INDICATOR_SIZE + 4 And Not scoredrawn Then
888 drawscore()
889 scoredrawn = True
890 EndIf
891 If y > SCORE_INDICATOR_SIZE + 24 And Not hiscoredrawn Then
892 drawscore(True)
893 hiscoredrawn = True
894 EndIf
895 Loop Until keypress<>""
897 gameRestart()
898 End Sub
901 Randomize
902 If Not loadgame(Command) Then
903 If Not loadgame(openloader(VARPATH)) Then End
904 End If
905 game.tim = Timer()
906 game.speed_timer = game.tim
908 If lazydrawplayfield Then drawplayfield()
909 If settings.show_speed Then drawspeed()
911 keypress = InKey
912 If keypress = "" Then keypress = windowmouse("play")
913 Select Case keypress
914 Case Chr(255) + "K" 'left
915 blok.move(-1)
916 drawplayfield()
917 Case Chr(255) + "M" 'right
918 blok.move(1)
919 drawplayfield()
920 Case " "
921 blok.move(0,0,nextshape(blok.typ)) 'cycle
922 drawplayfield()
923 Case Chr(255) + "H" 'move up or rotate
924 If gravity Then
925 blok.move(0,0,nextshape(blok.typ)) 'cycle
926 Else
927 blok.move(0,-1)
928 EndIf
929 drawplayfield()
930 Case Chr(255) + "P" 'down
931 If gravity Then
932 score(1)
933 blok.drop()
934 Else
935 blok.move(0,1)
936 EndIf
937 drawplayfield()
938 Case "t", "T"
939 loadTiles(1)
940 Case Chr(13) 'Enter = fix or drop
941 If gravity Then
942 Do : score(1) : Loop While blok.move(0,1)
943 EndIf
944 checksituation()
945 blok.init()
946 blok.imprint()
947 drawplayfield()
948 Case "s", "S" 'score
949 togglescore()
950 Case "o", "O" 'settings
951 game.menu_timer = Timer()
952 opensettings()
953 'make up for lost time
954 game.tim += Timer() - game.menu_timer
955 game.speed_timer += Timer() - game.menu_timer
956 resizewindow() : drawplayfield()
957 Case "l", "L" 'load
958 game.menu_timer = Timer()
959 If Not loadgame(openloader(VARPATH)) Then
960 resizewindow() 'restore window size and title
961 'make up for lost time
962 game.tim += Timer() - game.menu_timer
963 game.speed_timer += Timer() - game.menu_timer
964 drawplayfield()
965 End If
966 Case Chr(255)+";" 'F1
967 game.menu_timer = Timer()
968 openhelp()
969 'make up for lost time
970 game.tim += Timer() - game.menu_timer
971 game.speed_timer += Timer() - game.menu_timer
972 Case Chr(255)+"<" 'F2
973 gameRestart()
974 Case "+" 'speed+
975 game.speed += 1
976 Case Chr(27) 'Esc
977 game.menu_timer = Timer()
978 openmenu()
979 'make up for lost time
980 game.tim += Timer() - game.menu_timer
981 game.speed_timer += Timer() - game.menu_timer
982 End Select
983 'drop
984 If gravity And Timer() - game.tim > SPEED / game.speed then
985 blok.drop()
986 drawplayfield()
987 End If
988 'speedup based on time
989 If gravity And game.speed < MAX_SPEED And Timer() > game.speed_timer + SPEEDUP_TIME Then
990 game.speed_timer = Timer()
991 game.speed += 1
992 End If
993 Sleep 5
994 Loop