Matching joker shapes partially outside playfield boundary.
[generic-block-game.git] / gbg.bas
blob142c33b9de1c8c36682a7bc17495558497e27882
1 /'
2 Generic Block Game
3 Copyright 2008, 2018 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 = 20
29 Const SPEED_INDICATOR_SIZE = 8
30 Const SCORE_INDICATOR_SIZE = 16
31 Const DRAWPLAYFIELD_DELAY = 0.02
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 End Type
65 Dim Shared settings As settings_type
66 Dim Shared game As game_type
68 Dim Shared nextvariation As String
69 ReDim Shared help () As String
70 Dim Shared As UByte playabletiles, numberoftiles
71 Dim Shared As Short columns=1, rows=1
72 Dim Shared As Byte wrap, gravity, randominputs, randomgoals, inputrotate
73 Dim Shared score As Short=0
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 Double lazydrawplayfield = -1, lastdrawplayfield
82 lastdrawplayfield = Timer()
83 Dim Shared As UByte maxgoalsw = 0, maxgoalsh = 0
86 Declare Function loadgame(filename As String) As Byte
87 Declare Sub loadTiles(increment As Byte=1)
88 Declare Sub initplayfield()
89 Declare Sub drop_shadow()
92 Sub resizewindow(w As UShort = game.w, h As Short = -1, t As String = game.title)
93 Dim As Integer oldw, oldh : ScreenInfo oldw, oldh
94 Dim As Integer oldx, oldy : ScreenControl GET_WINDOW_POS, oldx, oldy
95 If h = -1 Then
96 h = game.h
97 If settings.show_score Then h += SCORE_INDICATOR_SIZE
98 If settings.show_speed Then h += SPEED_INDICATOR_SIZE
99 End If
100 If oldw = w And oldh = h Then
102 Else
103 ScreenRes w, h, 16, 2
104 If oldx > 0 And oldy > 0 Then ScreenControl SET_WINDOW_POS, oldx, oldy
105 End If
106 WindowTitle(t)
107 End Sub
110 Function asctonumber(ch As UByte) As UByte
111 If ch=46 Then : Return 0
112 ElseIf ch=33 Then : Return 254 'start=!=254
113 ElseIf ch=Asc("@") Then : Return 253 'exit=@=253
114 ElseIf ch>=48 And ch<=57 Then : Return ch-48
115 ElseIf ch>=65 And ch<=90 Then : Return ch-65+10
116 ElseIf ch>=97 And ch<=122 Then : Return ch-97+36
117 Else : Return 255
118 End If
119 End Function
121 Function getImgWidth(filename As String) As Short
122 Open filename For Input As #2
123 If Err>0 Then Return 0
124 Dim w As Integer
125 Get #2,19,w
126 Close #2
127 Return w
128 End Function
130 ReDim Shared inputsindex(0) As Short
131 ReDim Shared inputs(0,2) As UByte
132 'inputs and inputsindex are linked arrays. inputsindex shows individual inputs' ends
133 ReDim Shared goals(0,2) As UByte
136 Sub end_shape(shapes() As UByte, index As Byte = False)
137 shapes(UBound(shapes), 0) = 255
138 shapes(UBound(shapes), 1) = 255
139 shapes(UBound(shapes), 2) = 255
140 If index Then
141 inputsindex(UBound(inputsindex)) = UBound(shapes)
142 ReDim Preserve inputsindex(UBound(inputsindex) + 1)
143 End If
144 ReDim Preserve shapes(UBound(shapes) + 1, 2)
145 End Sub
148 Sub rotate_shape()
149 Dim As Short strt, stp, a, offset
150 If UBound(inputsindex) = 1 Then strt = 0 Else strt = inputsindex(UBound(inputsindex) - 1 - 1) + 1 'skip "255" point
151 stp = inputsindex(UBound(inputsindex) - 1) - 1 'leave out "255" point
152 offset = stp - strt
153 ReDim Preserve inputs(UBound(inputs) + offset + 1, 2)
154 For a = strt To stp
155 'mirror one axis and swap x and y to rotate
156 inputs(a + offset + 2, 1) = inputs(a, 0)
157 inputs(strt + stp - a + offset + 2, 0) = inputs(a, 1)
158 inputs(a + offset + 2, 2) = inputs(a, 2)
159 Next
160 End Sub
163 Sub loadshapes(shapes() As UByte, index As Byte = False)
164 ReDim shapes(0, 2)
165 If index Then ReDim inputsindex(0)
166 Dim l as String
167 Dim As UByte a, b
168 Do Until l = "}" Or Eof(1)
169 Line Input #1, l
170 If l = "" Then
171 b = 0
172 end_shape(shapes(), index)
173 If inputrotate And index Then
174 For a = 1 To 3
175 rotate_shape()
176 end_shape(shapes(), index)
177 Next
178 End If
179 ElseIf l <> "}" Then
180 For a = 0 To Len(l) - 1
181 shapes(UBound(shapes), 0) = a
182 shapes(UBound(shapes), 1) = b
183 If l[a]=46 Then : shapes(UBound(shapes), 2) = 0
184 Else : shapes(UBound(shapes), 2) = asctonumber(l[a])
185 End If
186 ReDim Preserve shapes(UBound(shapes) + 1, 2)
187 Next
188 b += 1
189 If Not index Then
190 If Len(l) > maxgoalsw Then maxgoalsw = Len(l)
191 If b > maxgoalsh Then maxgoalsh = b
192 End If
193 End If
194 Loop
196 end_shape(shapes(), index)
197 If inputrotate And index Then
198 For a = 1 To 3
199 rotate_shape()
200 end_shape(shapes(), index)
201 Next
202 End If
203 If index Then ReDim Preserve inputsindex(UBound(inputsindex) - 1)
204 End Sub
207 Declare Sub gameOver()
209 Dim Shared playfield(0 to columns-1,0 to rows-1) as UByte
210 Dim Shared originalplayfield(0 to columns-1,0 to rows-1) as UByte
212 ReDim Shared nextshape() As UByte
214 Function block.isblocked(x as Byte, y As Byte=0, typ As UByte=0, mode As Byte=False) As Byte
215 Dim As Short strt, stp, a
216 Dim As Byte failed
218 If typ=1 Then strt=0 Else strt=inputsindex(typ-1-1)+1 'preskace se ona 255 tacka
219 stp=inputsindex(typ-1)-1 'izostavlja se ona 255 tacka
220 'mode=True = ispituje se origin
221 For a = strt To stp
222 failed=inputs(a,2)<>0 And _
223 playfield(x+inputs(a,0),y+inputs(a,1))<>254 And _
224 playfield(x+inputs(a,0),y+inputs(a,1))<>253
225 If Not mode Then failed=failed And playfield(x+inputs(a,0),y+inputs(a,1))<>0
226 failed=failed Or y+inputs(a,1)>=rows _
227 Or y+inputs(a,1)<0 _
228 Or x+inputs(a,0)>=columns _
229 Or x+inputs(a,0)<0
230 If failed Then Exit For
231 Next
232 Return failed
233 End Function
236 Sub block.init()
237 this.dropping = False
238 If randominputs Then 'permutovati brojeve od 1 do playabletiles
239 Dim a As UByte
240 ReDim randomtable(playabletiles) As UByte
241 For a = 1 To playabletiles
242 randomtable(a)=fix(rnd*playabletiles)+1
243 Next
244 EndIf
246 Type xy
247 As UByte x,y
248 End Type
249 ReDim startpositions(0) As xy
250 Dim posnumber As UShort=0
251 Dim As UShort x,y
252 Dim starttyp As UByte
253 this.typ=Fix(RND*(UBound(inputsindex)+1))+1
254 starttyp=this.typ
256 For y=0 to rows-1 : For x=0 to columns-1
257 If originalplayfield(x,y)=254 Then
258 If Not this.isblocked(x,y,this.typ,True) Then 'dodaj na listu
259 ReDim Preserve startpositions(posnumber) As xy
260 startpositions(posnumber)=Type(x,y)
261 posnumber+=1
262 EndIf
263 EndIf
264 Next x: Next y
265 If posnumber=0 Then
266 this.typ=nextshape(this.typ)
267 If this.typ=starttyp Then
268 gameOver()
269 Return
270 EndIf
271 EndIf
272 Loop Until posnumber>0
273 posnumber=fix(rnd*posnumber)
274 this.x=startpositions(posnumber).x
275 this.y=startpositions(posnumber).y
276 this.oldx=this.x
277 this.oldy=this.y
278 this.oldtyp=this.typ
279 If Not this.shadow Then drop_shadow()
280 End Sub
282 'Constructor block
283 'constructor bi se pokretao kod samog deklarisanja, pre playfielda
284 'End Constructor
286 Function getshapewidthheight(typ As UByte,geth As Byte = False) As Short
287 Dim As Short strt, stp, a, maxwidth=0, maxheight=0
289 If typ=1 Then strt=0 Else strt=inputsindex(typ-1-1)+1 'preskace se ona 255 tacka
290 stp=inputsindex(typ-1)-1 'izostavlja se ona 255 tacka
291 For a = strt To stp
292 If maxwidth<inputs(a,0) Then maxwidth=inputs(a,0)
293 If maxheight<inputs(a,1) Then maxheight=inputs(a,1)
294 Next
295 If geth Then Return maxheight
296 Return maxwidth
297 End Function
300 Function block.move(x as Byte, y As Byte=0, typ As UByte=0) As Byte 'successful?True/False
301 Dim As Byte dw,dh
302 If typ=0 Then
303 typ=this.typ
304 Else
305 '!centriranje
306 dw=(getshapewidthheight(this.typ)-getshapewidthheight(typ)) / 2
307 If gravity Then
308 dh=0
309 Else
310 dh=(getshapewidthheight(this.typ,True)-getshapewidthheight(typ,True)) / 2
311 EndIf
312 EndIf
313 this.oldx=this.x
314 this.oldy=this.y
315 this.oldtyp=this.typ
316 this.imprint(True, False) 'just erase oldx i oldy
318 this.x=x + this.x + dw
319 this.y=y + this.y + dh
320 If wrap Then
321 this.x=(this.x + columns) Mod columns
322 If Not gravity Then this.y=(this.y + rows) Mod rows
323 EndIf
325 failed=failed Or y+inputs(a,1)>=rows _
326 Or y+inputs(a,1)<0 _
327 Or x+inputs(a,0)>=columns _
328 Or x+inputs(a,0)<0
331 Dim As Byte failed
332 failed=this.isblocked(this.x,this.y,typ)
333 If failed Then
334 this.x=this.oldx
335 this.y=this.oldy
336 this.typ=this.oldtyp
337 Else
338 this.typ=typ
339 EndIf
340 If not this.shadow Then drop_shadow()
341 this.imprint()
342 Return Not failed
344 End Function
347 Sub markshape(shapestart As Short,shapeend As Short,x As Short,y As Short)
348 Dim As Short mx,my,q
349 For q=shapestart To shapeend-1
350 mx=x-goals(q,0)
351 my=y-goals(q,1)
352 If 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 print #3, "Marking shape"
369 markshape(shapestart,p,x,y)
370 foundshape=True
371 End If
372 shapestart=p+1
373 fullshape=True
374 For a=0 To UBound(goalscolors) : goalscolors(a)=0 : Next a
375 ElseIf goals(p,2)<>254 Then
376 'do nothing with fullshape variable if 254(joker)
377 '254="!"=matches also empty space (useful when tiles surrounding matched shape should be destroyed)
378 mx=x-goals(p,0)
379 my=y-goals(p,1)
380 If mx<0 Or mx>columns-1 Or my<0 Or my>rows-1 Then
381 fullshape=False
382 ElseIf goals(p,2)=255 Then '255 matches everything except empty space
383 fullshape = fullshape And _
384 (playfield(mx,my)<>0) And _
385 (playfield(mx,my)<>254) And _
386 (playfield(mx,my)<>253)
387 ElseIf goals(p,2)<>0 Then
388 If randomgoals Then
389 If goalscolors(goals(p,2))=0 Then
390 If (playfield(mx,my)<>0) And _
391 (playfield(mx,my)<>254) And _
392 (playfield(mx,my)<>253)Then
393 goalscolors(goals(p,2))=playfield(mx,my)
394 Else
395 fullshape=False
396 EndIf
397 EndIf
398 fullshape = fullshape And (playfield(mx,my)=goalscolors(goals(p,2)) _
399 Or playfield(mx,my)=goalscolors(goals(p,2))+100)
400 Else
401 fullshape = fullshape And (playfield(mx,my)=goals(p,2) _
402 Or playfield(mx,my)=goals(p,2)+100)
403 EndIf
404 End If
405 End If
406 Next
407 Return foundshape
408 End Function
411 Sub checksituation()
412 Dim As Byte x, y, gap, removed=False, scoremulti=0, existsnextvariation=False, gotonextvariation=True
413 For y=0 To rows-1 + maxgoalsh : For x=0 To columns-1 + maxgoalsw
414 If matchshape(x,y) Then
415 scoremulti += 1
416 game.speed_timer = Timer() 'delay speedup
417 End If
418 Next x: Next y
419 If gravity Then 'remove >100 (marked shapes)
420 For x=0 To columns-1
421 gap=0
422 For y=rows-1 To 0 Step -1
423 If playfield(x,y)>=100 And playfield(x,y)<>254 And playfield(x,y)<>253 Then
424 gap+=1
425 Else
426 If playfield(x,y)=254 Then
427 If originalplayfield(x,y)=254 Or originalplayfield(x,y)=253 Then
428 playfield(x,y+gap)=originalplayfield(x,y)
429 Else
430 playfield(x,y+gap)=0
431 EndIf
432 Else playfield(x,y+gap)=playfield(x,y)
433 End If
434 End if
435 If y<gap Then
436 If originalplayfield(x,y)=254 Or originalplayfield(x,y)=253 Then
437 playfield(x,y)=originalplayfield(x,y)
438 Else
439 playfield(x,y)=0
440 EndIf
441 EndIf
442 Next y
443 removed=removed or gap>0
444 Next x
445 If removed Then checksituation()
446 Else 'no gravity
447 For y=0 To rows-1 : For x=0 To columns-1
448 If playfield(x,y)>100 And playfield(x,y)<>254 And playfield(x,y)<>253 Then
449 If originalplayfield(x,y)=254 Or originalplayfield(x,y)=254 Then
450 playfield(x,y)=originalplayfield(x,y)
451 Else
452 playfield(x,y)=0
453 EndIf
454 EndIf
455 Next x: Next y
456 EndIf
457 score+=scoremulti*10
458 'check exit
459 For y=0 To rows-1 : For x=0 To columns-1
460 If originalplayfield(x,y)=253 Then
461 existsnextvariation=True
462 If playfield(x,y)=253 Then gotonextvariation=False
463 EndIf
464 Next x: Next y
465 If existsnextvariation And gotonextvariation Then
466 loadgame(VARPATH+"/"+nextvariation)
467 loadTiles(0)
468 game.speed = 1
469 initplayfield()
470 EndIf
471 End Sub
474 Sub putshape(x As Short, y As Short, typ As UByte, nodel As Byte, noshadow As Byte)
475 Dim As Short strt, stp, a
476 Dim As Short o
477 'skip the 255-point that marks the end of the previous shape
478 If typ = 1 Then strt = 0 Else strt=inputsindex(typ - 1 - 1) + 1
479 stp = inputsindex(typ - 1) - 1 'leave out 255-point
480 For a = strt To stp : If inputs(a, 2) <> 0 Then
481 ' determine output
482 If nodel Then
483 If Not noshadow Then
484 'don't overwrite blocks with shadow
485 o = playfield(x + inputs(a, 0), y + inputs(a, 1))
486 If o = 0 Or o = 254 Then o = 253
487 ElseIf randominputs Then
488 o = randomtable(inputs(a, 2))
489 Else
490 o = inputs(a, 2)
491 EndIf
492 Else 'delete
493 o = originalplayfield(x + inputs(a, 0), y + inputs(a, 1))
494 'preserve special tiles, but don't restore blocks (if any) from the beginning
495 If Not (o = 254 Or o = 253) Then o = 0
496 EndIf
497 ' put
498 playfield(x + inputs(a, 0), y + inputs(a, 1)) = o
499 End If : Next
500 End Sub
503 Sub block.imprint(remove As Byte = True, place As Byte = True, noshadow As Byte = True)
504 If (Not (settings.show_shadow And gravity) Or noshadow) And this.shadow Then Return
505 If remove Then putshape(this.oldx, this.oldy, this.oldtyp, False, noshadow) 'delete old shape
506 If place Then putshape(this.x, this.y, this.typ, True, noshadow) 'put new shape
507 End Sub
510 Sub block.drop()
511 If Not this.move(0,1) Then
512 checksituation()
513 this.init()
514 this.imprint()
515 EndIf
516 If Not this.shadow Then game.tim = Timer()
517 End Sub
520 Sub initplayfield
521 ReDim playfield(0 To columns-1,0 To rows-1) As UByte
522 Dim As UByte x,y
523 For y=0 To rows-1 : For x=0 To columns-1
524 playfield(x,y)=originalplayfield(x,y)
525 Next x : Next y
526 End Sub
529 Declare Sub drawplayfield()
531 Sub loadTiles(increment As Byte)
532 settings.tileset += increment
533 If settings.tileset > UBound(tilesfiles) Then settings.tileset = 0
534 If settings.tileset < 0 Then settings.tileset = UBound(tilesfiles)
536 Dim tilesfile As String
537 tilesfile=GFXPATH & "/" & tilesfiles(settings.tileset)
538 tilewidth = getImgWidth(tilesfile)
539 game.w = tilewidth*columns : game.h = tilewidth*rows
540 tilesimg = ImageCreate(tilewidth, tilewidth*(3+numberoftiles))
541 BLoad tilesfile, tilesimg
542 scoreimg = ImageCreate(122,20)
543 BLoad GFXPATH+"/brojke.bmp", scoreimg
544 resizewindow()
545 ScreenSet 0,1
546 drawplayfield()
547 End Sub
550 Sub drawscore
551 Dim As UByte a,digit
552 Dim As Byte numberstarted=False
554 Dim xpx As UShort = tilewidth*columns
555 Line(0,0)-(xpx,16),RGB(0,0,0),BF
556 For a=0 To 9 'max 10 digits - traze se otpozadi
557 If 10^a > score Then Exit For
558 digit=Fix((score Mod 10^(a+1)) / 10^a)
559 If digit=1 Then
560 xpx-=5
561 Put (xpx,1),scoreimg,(13,0)-(17,14),PSet
562 ElseIf digit=0 Then
563 xpx-=13
564 Put (xpx,1),scoreimg,(0,0)-(12,14),PSet
565 Else
566 xpx-=13
567 Put (xpx,1),scoreimg,(-8+digit*13,0)-(-8+12+digit*13,14),PSet
568 EndIf
569 Next
570 End Sub
573 Sub drawspeed()
574 Dim y As Short = 0
575 If settings.show_score Then y += SCORE_INDICATOR_SIZE
576 Line (0, y) - (game.w, y + 7), RGB(22, 22, 22), BF ' background
577 Line (0, y) - (game.w * game.speed / 10, y + 3), RGB(222, 222, 222), BF
578 Line (0, y + 4) - (game.w * (Timer() - game.speed_timer) / SPEEDUP_TIME, y + 7), RGB(166, 166, 233), BF
579 End Sub
582 Sub drawplayfield
583 ' Prevent too much redraws
584 If Timer() - lastdrawplayfield < DRAWPLAYFIELD_DELAY Then
585 lazydrawplayfield = Timer()
586 Return
587 End If
589 shadow.imprint(False, True, False)
591 Dim As Short offsetx=0,offsety=0,offsetpx=0,offsetpy=0
592 If settings.show_score Then offsetpy += SCORE_INDICATOR_SIZE
593 If settings.show_speed Then offsetpy += SPEED_INDICATOR_SIZE
594 Dim As UByte x,y,t
595 for y=0 to rows-1 : for x=0 to columns-1
596 If playfield(x,y)=0 Then : t=0
597 ElseIf playfield(x,y)=254 Then : t=1
598 ElseIf playfield(x,y)=253 Then : t=2
599 Else : t=playfield(x,y)+2
600 End If
601 Put (offsetpx+(offsetx+x)*tilewidth, offsetpy+(offsety+y)*tilewidth), _
602 tilesimg,(0,t*tilewidth) - Step(tilewidth-1,tilewidth-1),PSet
603 Next x: Next y
605 shadow.imprint(True, False, False)
607 If settings.show_score Then drawscore()
608 If settings.show_speed Then drawspeed()
610 ScreenSync
611 ScreenCopy
613 ' Prevent too much redraws
614 lastdrawplayfield = Timer()
615 lazydrawplayfield = -1
617 End Sub
620 Sub loadtilesets()
621 Dim l As String, n As UByte=0
622 Do Until l="}" Or Eof(1)
623 Line Input #1,l
624 ReDim Preserve tilesfiles(n)
625 tilesfiles(n)=l
626 n+=1
627 Loop
628 ReDim Preserve tilesfiles(n-2)
629 End Sub
632 Sub loadcycle()
633 Dim l As String, n As UByte
634 If inputrotate Then
635 ReDim nextshape(UBound(inputsindex) + 1) As UByte
636 For n = 0 To UBound(inputsindex) Step 4
637 nextshape(n + 1) = n + 2
638 nextshape(n + 2) = n + 3
639 nextshape(n + 3) = n + 4
640 nextshape(n + 4) = n + 1
641 Next
642 Else
643 Line Input #1,l
644 ReDim nextshape(Len(l)) As UByte
645 For n = 0 To Len(l) 'nextshape(0) is not used
646 nextshape(n+1)=asctonumber(l[n])
647 Next
648 EndIf
649 End Sub
652 Sub loadplayfield()
653 columns=0 : rows=0
654 Dim l As String, n As UByte
655 Dim position As Integer = Seek(1)
656 Dim As Short a,b=0
657 Do Until l="}" Or Eof(1)
658 rows+=1
659 Line Input #1,l
660 If Len(l)>columns Then columns=Len(l)
661 Loop
662 ReDim originalplayfield(0 to columns-1,0 to rows-1) as UByte
663 rows-=1
664 Seek #1,position
665 l=""
666 Do Until l="}" Or Eof(1)
667 b+=1
668 Line Input #1,l
669 For a=0 To Len(l)-1
671 If l[a]=46 Then : originalplayfield(a,b-1)=0
672 Else : originalplayfield(a,b-1)=asctonumber(l[a])
673 End If
675 n=asctonumber(l[a])
676 originalplayfield(a,b-1)=n
677 If n>numberoftiles And n<100 Then numberoftiles=n
678 Next
679 Loop
680 If playabletiles>numberoftiles Then numberoftiles=playabletiles
681 End Sub
684 Sub loadsettings()
685 wrap=False
686 gravity=False
687 inputrotate=False
688 randominputs=False
689 randomgoals=False
690 Dim l As String
691 Do Until l="}" Or Eof(1)
692 Line Input #1,l
693 wrap=wrap Or l="wrap"
694 gravity=gravity Or l="gravity"
695 inputrotate=inputrotate Or l="inputrotate"
696 randominputs=randominputs Or l="randominputs"
697 randomgoals=randomgoals Or l="randomgoals"
698 If Left(l,6)="tiles:" Then playabletiles=CInt(Mid(l,7))
699 Loop
700 End Sub
703 Sub drop_shadow()
704 If Not (settings.show_shadow And gravity) Then Return
705 shadow.x = blok.x
706 shadow.y = blok.y
707 shadow.typ = blok.typ
708 shadow.shadow = True
709 Do : Loop While shadow.move(0,1)
710 End Sub
713 Sub gameRestart()
714 score = 0
715 game.speed = 1
716 initplayfield()
717 blok.init()
718 blok.imprint()
719 drawplayfield()
720 End Sub
723 Sub loadhelp()
724 ReDim help(0)
725 Dim l As String
726 Do Until Eof(1)
727 Line Input #1,l
728 If l="}" Then Exit Do
729 ReDim Preserve help(UBound(help) + 1)
730 help(UBound(help)) = l
731 Loop
732 End Sub
735 Function loadgame(filename As String) As Byte
736 If filename = "" Then Return False
737 Dim l As String, loadedcycle As Byte
738 Open filename For Input As #1
739 If Err>0 Then Print "Error opening the file":End
741 Line Input #1,l
742 Select Case l
743 Case "Title {"
744 Line Input #1,l
745 game.title = l
746 Case "Settings {"
747 loadsettings()
748 Case "Next {"
749 Line Input #1,nextvariation
750 Case "Tilesets {"
751 loadtilesets()
752 Case "Playfield {"
753 loadplayfield()
754 Case "Input {"
755 loadshapes(inputs(),True)
756 Case "Cycle {"
757 loadcycle()
758 loadedcycle = True
759 Case "Goals {"
760 loadshapes(goals())
761 Case "Help {"
762 loadhelp()
763 End Select
764 Loop Until Eof(1)
765 Close #1
766 If Not loadedcycle Then loadcycle()
767 gameRestart()
768 loadTiles(0)
769 Return True
770 End Function
773 Sub togglescore()
774 settings.show_score = Not settings.show_score
775 resizewindow()
776 ScreenSet 0,1
777 drawplayfield()
778 End Sub
781 Sub quit()
782 ImageDestroy(tilesimg)
783 ImageDestroy(scoreimg)
784 Close #3
786 End Sub
788 #include "menus.bi"
791 Sub drop_but_dont_fix(y As Integer)
792 If Int(y / tilewidth - blok.y) <= 0 Then Return
793 If blok.move(0,1) Then
794 game.tim = Timer()
795 drop_but_dont_fix(y)
796 End If
797 blok.imprint()
798 End Sub
801 Function windowmouse(mode As String) As String
802 Dim e As EVENT
803 Static pressed As Integer = 0
804 If (ScreenEvent(@e)) Then
805 Select Case e.type
806 Case EVENT_MOUSE_BUTTON_PRESS
807 If mode = "gameover" Then Return " " 'Space=end gameover
808 If settings.play_mouse Then
809 If e.button=1 Then
810 If settings.first_button_turning And Int(e.y / tilewidth - blok.y) <= 0 Then
811 Return " " 'Space = turn
812 Else
813 drop_but_dont_fix(e.y)
814 blok.dropping = True
815 End If
816 drawplayfield()
817 End If
818 If e.button=2 Then Return " " 'Space = turn
819 End If
820 If Not settings.play_mouse Or e.button>2 Then Return Chr(27) 'Esc = open menu
821 Case EVENT_MOUSE_BUTTON_RELEASE
822 If settings.play_mouse Then
823 If e.button=1 And blok.dropping Then Return Chr(13) 'Enter = drop
824 End If
825 Case EVENT_WINDOW_CLOSE
826 quit()
827 Case EVENT_MOUSE_MOVE
828 If e.x > 0 And e.x < game.w And settings.play_mouse And mode <> "gameover" Then
829 While Int(e.x / tilewidth - blok.x) <> 0 And blok.move(Sgn(Int(e.x / tilewidth - blok.x)))
830 WEnd
831 If blok.dropping Then drop_but_dont_fix(e.y)
832 drawplayfield()
833 End If
834 End Select
835 End If
836 End Function
839 Sub gameOver()
840 Dim As Short starty = 0, y = 0, endy
841 Dim As Byte scoredrawn = False
842 Dim tim2 As Double
843 scoredrawn = settings.show_score
844 If settings.show_score Then starty += SCORE_INDICATOR_SIZE
845 y = starty
846 endy = game.h + starty - 6
847 If settings.show_speed Then endy += SPEED_INDICATOR_SIZE
848 game.tim = Timer()
849 tim2 = Timer()
850 keypress = ""
852 If Timer() - tim2 > 0.5 Then
853 keypress = InKey()
854 If keypress = "" Then keypress = windowmouse("gameover")
855 Else
856 While InKey() <> "" : Wend 'prevents exit from gameover screen for 0.5 seconds
857 EndIf
859 If y < endy Then
860 If Timer() - game.tim > 0.03 Then
861 game.tim = Timer()
862 Line (0, y) - (game.w, y + 6), RGB(0, 0, 0), BF
863 Put(game.w / 2 - 47 / 2, y + 1), scoreimg,(52, 15) - (99, 19), PSet
864 y += 1
865 ScreenSync
866 ScreenCopy
867 EndIf
868 EndIf
869 If y > SCORE_INDICATOR_SIZE + 4 And Not scoredrawn Then
870 drawscore()
871 scoredrawn = True
872 EndIf
873 Loop Until keypress<>""
875 gameRestart()
876 End Sub
879 Randomize
880 If Not loadgame(Command) Then
881 If Not loadgame(openloader(VARPATH)) Then End
882 End If
883 game.tim = Timer()
884 game.speed_timer = game.tim
886 If lazydrawplayfield > 0 And Timer() - lazydrawplayfield > DRAWPLAYFIELD_DELAY Then
887 lazydrawplayfield = -1
888 drawplayfield()
889 End If
890 keypress = InKey
891 If keypress = "" Then keypress = windowmouse("play")
892 Select Case keypress
893 Case Chr(255) + "K" 'left
894 blok.move(-1)
895 drawplayfield()
896 Case Chr(255) + "M" 'right
897 blok.move(1)
898 drawplayfield()
899 Case " "
900 blok.move(0,0,nextshape(blok.typ)) 'cycle
901 drawplayfield()
902 Case Chr(255) + "H" 'move up or rotate
903 If gravity Then
904 blok.move(0,0,nextshape(blok.typ)) 'cycle
905 Else
906 blok.move(0,-1)
907 EndIf
908 drawplayfield()
909 Case Chr(255) + "P" 'down
910 If gravity Then
911 score+=1
912 blok.drop()
913 Else
914 blok.move(0,1)
915 EndIf
916 drawplayfield()
917 Case "t", "T"
918 loadTiles(1)
919 Case Chr(13) 'Enter = fix or drop
920 If gravity Then
921 Do : score+=1 : Loop While blok.move(0,1)
922 EndIf
923 checksituation()
924 blok.init()
925 blok.imprint()
926 drawplayfield()
927 Case "s", "S" 'score
928 togglescore()
929 Case "o", "O" 'settings
930 game.menu_timer = Timer()
931 opensettings()
932 'make up for lost time
933 game.tim += Timer() - game.menu_timer
934 game.speed_timer += Timer() - game.menu_timer
935 resizewindow() : drawplayfield()
936 Case "l", "L" 'load
937 game.menu_timer = Timer()
938 If Not loadgame(openloader(VARPATH)) Then
939 resizewindow() 'restore window size and title
940 'make up for lost time
941 game.tim += Timer() - game.menu_timer
942 game.speed_timer += Timer() - game.menu_timer
943 drawplayfield()
944 End If
945 Case Chr(255)+";" 'F1
946 game.menu_timer = Timer()
947 openhelp()
948 'make up for lost time
949 game.tim += Timer() - game.menu_timer
950 game.speed_timer += Timer() - game.menu_timer
951 Case Chr(255)+"<" 'F2
952 gameRestart()
953 Case "+" 'speed+
954 game.speed += 1
955 Case Chr(27) 'Esc
956 game.menu_timer = Timer()
957 openmenu()
958 'make up for lost time
959 game.tim += Timer() - game.menu_timer
960 game.speed_timer += Timer() - game.menu_timer
961 End Select
962 'drop
963 If gravity And Timer() - game.tim > SPEED / game.speed then
964 blok.drop()
965 drawplayfield()
966 End If
967 'speedup based on time
968 If gravity And game.speed < MAX_SPEED And Timer() > game.speed_timer + SPEEDUP_TIME Then
969 game.speed_timer = Timer()
970 game.speed += 1
971 End If
972 Loop