1 2=head1 DESCRIPTION 3 4app.pir - a tetris application object 5 6B<Note:> The Tetris::App class is implemented as a singleton. 7 8=head1 SYNOPSIS 9 10 app = new "Tetris::App" 11 12 app."run"() 13 app."shutdown"() 14 end 15 16 ... 17 18 # create a new random C<next block> on board 3 19 app = get_hll_global [ "Tetris::App" ], "app" 20 app."nextBlock"( 3 ) 21 22=head1 CLASS INFORMATION 23 24This is the main tetris class. Neither has 25it parent classes nor is it subclassed. 26 27=cut 28 29 30.include "timer.pasm" 31.namespace ["Tetris::App"] 32 33.sub __onload :load 34 $P0 = get_class "Tetris::App" 35 unless null $P0 goto END 36 37 load_bytecode "SDL/App.pir" 38 load_bytecode "SDL/Color.pir" 39 load_bytecode "SDL/Event.pir" 40 load_bytecode "SDL/Rect.pir" 41 42 load_bytecode "examples/sdl/tetris/eventhandler.pir" 43 load_bytecode "examples/sdl/tetris/board.pir" 44 45 newclass $P0, "Tetris::App" 46 47 addattribute $P0, "SDL" 48 addattribute $P0, "EventHandler" 49 addattribute $P0, "DebugFlags" 50 addattribute $P0, "Timer" 51 addattribute $P0, "TimerDisableCount" 52 addattribute $P0, "InTimer" 53 addattribute $P0, "Players" 54 addattribute $P0, "Palette" 55 addattribute $P0, "Boards" 56 57 # set the BUILD method name 58 $P1 = new 'String' 59 $P1 = 'BUILD' 60 setprop $P0, 'BUILD', $P1 61END: 62.end 63 64=head1 CONSTRUCTOR 65 66=over 4 67 68=item BUILD - called automatically by "new" 69 70Initializes the application. 71 72It performs the SDL initialization and 73creates some internal data structures afterwards. 74 75This method throws an exception if an error occurs. 76 77=cut 78 79.sub BUILD :method 80 # create the app object 81 set_hll_global [ "Tetris::App" ], "app", self 82 83 $P0 = new 'Hash' 84 setattribute self, 'DebugFlags', $P0 85 86 # prepare SDL's constructor arguments 87 $P0 = new 'Hash' 88 $P0["height"] = 480 89 $P0["width"] = 640 90 $P0["bpp"] = 32 91 $P0["flags"] = 1 92 93 # create the SDL object 94 $P0 = new ['SDL'; 'App'], $P0 95 96 # store the SDL object 97 setattribute self, 'SDL', $P0 98 99 # generate some data structures 100 self."genPalette"() 101 102 # create the app timer 103 self."initTimer"() 104 105 # init the SDL event handler 106 $P0 = new "Tetris::EventHandler", self 107 setattribute self, 'EventHandler', $P0 108 109 # create the debug flags hash 110 $P0 = new 'Hash' 111 setattribute self, 'DebugFlags', $P0 112 113 # start a new single player game 114 self."newGame"( 1 ) 115.end 116 117=back 118 119=head1 METHODS 120 121The Tetris::App class provides the following methods: 122 123=over 4 124 125=item sdl = app."SDL"() 126 127=cut 128 129.sub SDL :method 130 getattribute $P0, self, 'SDL' 131 132 .return ($P0) 133.end 134 135=item app."shutdown"() 136 137Shuts SDL down and performs some internal cleanup. 138 139B<Note:> The application object is invalid afterwards, you are not allowed to 140call any other of its methods after this one. 141 142This method returns nothing. 143 144=cut 145 146.sub shutdown :method 147 .local pmc sdl 148 149 # XXX free data structures 150 # ... 151 152 # shutdown the SDL system 153 $P0 = self."SDL"() 154 if_null $P0, END 155 $P0."quit"() 156END: 157.end 158 159=item success = app."run"() 160 161The application's main loop. 162 163Returns if the user requested a shutdown. 164An exception is thrown if an error occurs. 165 166=cut 167 168.sub _app_timer 169 get_hll_global $P0, [ "Tetris::App" ], "app" 170 $P0."timer"() 171.end 172 173.sub run :method 174 .local pmc eh 175 .local pmc loop 176 177 getattribute eh, self, 'EventHandler' 178 179 loop = new ['SDL'; 'Event'] 180 181 self."enableTimer"() 182 loop."process_events"( 0.1, eh, self ) 183 self."disableTimer"() 184 print "done\n" 185.end 186 187=item app."initTimer"() 188 189=cut 190 191.sub initTimer :method 192 $P0 = new "Array" 193 $P1 = get_hll_global [ "Tetris::App" ], "_app_timer" 194 $P0 = 8 195 $P0[0] = .PARROT_TIMER_NSEC 196 $P0[1] = 0.1 197 $P0[2] = .PARROT_TIMER_HANDLER 198 $P0[3] = $P1 199 $P0[4] = .PARROT_TIMER_REPEAT 200 $P0[5] = -1 201 $P0[6] = .PARROT_TIMER_RUNNING 202 $P0[7] = 1 203 204 $P1 = new 'Timer', $P0 205 setattribute self, 'Timer', $P1 206 207 $P0 = new 'Integer' 208 $P0 = 1 209 setattribute self, 'TimerDisableCount', $P0 210 211 $P0 = new 'Integer' 212 $P0 = 0 213 setattribute self, 'InTimer', $P0 214.end 215 216=item app."setTimerStatus"( status ) 217 218=cut 219 220.sub setTimerStatus :method 221 .param int status 222 223 getattribute $P0, self, 'Timer' 224 set $P0[.PARROT_TIMER_RUNNING], status 225.end 226 227=item app."enableTimer"() 228 229=cut 230 231.sub enableTimer :method 232 getattribute $P0, self, 'TimerDisableCount' 233 dec $P0 234 if $P0 != 0 goto END 235 self."setTimerStatus"( 1 ) 236END: 237.end 238 239=item app."disableTimer"() 240 241=cut 242 243.sub disableTimer :method 244 getattribute $P0, self, 'TimerDisableCount' 245 inc $P0 246 self."setTimerStatus"( 0 ) 247.end 248 249=item color = app."color"( number ) 250 251Returns the specified color entry from the palette. 252 253=cut 254 255.sub color :method 256 .param int number 257 .local pmc palette 258 .local pmc color 259 260 palette = self."palette"() 261 color = palette[number] 262 263 .return (color) 264.end 265 266=item palette = app."palette"() 267 268Returns the color palette. 269 270=cut 271 272.sub palette :method 273 .local pmc palette 274 275 getattribute palette, self, 'Palette' 276 if_null palette, CREATE 277 branch RET 278CREATE: 279 (palette) = self."genPalette"() 280 281 branch RET 282 283NULL: 284 print "warning: no color palette found!\n" 285 286RET: 287 .return (palette) 288.end 289 290=item palette = app."genPalette"() B<(internal)> 291 292Creates the color palette. 293 294This method returns the created palette. 295 296=cut 297 298.sub genPalette :method 299 .local pmc palette 300 .local pmc hash 301 .local pmc color 302 .local int i 303 .local int r 304 .local int g 305 .local int b 306 .local int l 307 308 palette = new 'ResizablePMCArray' 309 hash = new 'Hash' 310 311 set i, 0 312GENLOOP: 313 band l, i, 8 314 band r, i, 4 315 band g, i, 2 316 band b, i, 1 317 shr r, 2 318 shr g, 1 319 mul r, 127 320 mul g, 127 321 mul b, 127 322 unless l, NOT_BRIGHT 323 add r, 64 324 add g, 64 325 add b, 64 326NOT_BRIGHT: 327 328 hash["r"] = r 329 hash["g"] = g 330 hash["b"] = b 331 color = new ['SDL'; 'Color'], hash 332 333 push palette, color 334 inc i 335 if i < 16 goto GENLOOP 336 337 setattribute self, 'Palette', palette 338 339 .return (palette) 340.end 341 342=item board = self."board"( boardID ) 343 344Lookup a board using its ID. 345 346=over 4 347 348=item parameter C<boardID> 349 350The ID of the board to return. 351 352=back 353 354Returns the board object, or NULL if 355no board with the specified ID exists. 356 357=cut 358 359.sub board :method 360 .param int boardID 361 .local pmc board 362 363 getattribute board, self, 'Boards' 364 365 $I0 = board 366 if boardID < $I0 goto OK 367ERR: 368 null board 369 print "board " 370 print boardID 371 print " not found!\n" 372 branch END 373 374OK: 375 board = board[boardID] 376 defined $I0, board 377 unless $I0 goto ERR 378 379END: 380 .return (board) 381.end 382 383=item block = app."currentBlock"( boardID ) 384 385Returns the currently falling block of a board. 386 387=over 4 388 389=item parameter C<boardID> 390 391The ID of the board whose C<current block> should be returned. 392 393=back 394 395Returns the block object, or NULL if the board was not found. 396 397=cut 398 399.sub currentBlock :method 400 .param int boardID 401 .local pmc temp 402 403 temp = self."board"( boardID ) 404 if_null temp, BLOCKISNULL 405 temp = temp."currentBlock"() 406 407BLOCKISNULL: 408 .return (temp) 409.end 410 411=item success = app."rotate"( boardID, dir ) 412 413Rotates the current block of a board. 414 415=over 4 416 417=item parameter C<boardID> 418 419The ID of the board whose block should be rotated. 420 421=item parameter C<dir> 422 423+1 = rotate clockwise 424 425-1 = rotate counterclockwise 426 427=back 428 429Returns 1 if the rotation was possible, 0 otherwise. 430 431=cut 432 433.sub rotate :method 434 .param int boardID 435 .param int dir 436 .local pmc block 437 .local int ret 438 439 self."disableTimer"() 440 441 ret = 0 442 443 # lookup the block 444 block = self."currentBlock"( boardID ) 445 if_null block, END 446 447 # rotate the block 448 ret = block."rotate"( dir ) 449 if ret == 0 goto END 450 451 # redraw the screen 452 self."drawScreen"( 0 ) 453 454END: 455 self."enableTimer"() 456 457 .return (ret) 458.end 459 460=item success = app."move"( boardID, xval, yval ) 461 462Moves the currently falling block of a board. 463It does not lock the block onto the board in any case, use 464C<_Board::lockBlock()> if you want this. 465 466=over 4 467 468=item parameter C<boardID> 469 470The ID of the board whose block should be moved. 471 472=item parameter C<xval> 473 474Number of units the block should be moved horizontally. 475 476Positiv numbers will move the block rightwards, negative 477numbers leftwards. 478 479=item parameter C<xval> 480 481Number of units the block should be moved vertically. 482 483Positiv numbers will move the block downwards, negative 484numbers upwards (untested; not recommended). 485 486=back 487 488Returns 1 if the movement was possible, 0 otherwise. 489 490=cut 491 492.sub move :method 493 .param int boardID 494 .param int xval 495 .param int yval 496 .local int success 497 .local pmc block 498 499 # disable the timer 500 self."disableTimer"() 501 502 block = self."currentBlock"( boardID ) 503 if_null block, END 504 success = block."move"( xval, yval ) 505 506 unless success goto END 507 self."drawScreen"( 0 ) 508 509END: 510 # reenable the timer 511 self."enableTimer"() 512 513 .return (success) 514.end 515 516=item block = self."nextBlock"( boardID, id ) 517 518Activates the C<next block> on the specified board 519and creates a new C<next block>. 520The old current block is just overwritten, and not locked 521onto the board. Use C<_Board::lockBlock()> for this. 522 523=over 4 524 525=item parameter C<boardID> 526 527The ID of the board where the next block should be activated. 528 529=item parameter C<id> B<(optional)> 530 531The id of the block to create. The id is an index in the 532blockdata array. Please refer to <_Tetris::App::blockdata()> 533for more information. 534 535=back 536 537This method returns the new falling block. 538 539=cut 540 541.sub nextBlock :method 542 .param int boardID 543 .param int id :optional 544 .param int got_id :opt_flag 545 .local pmc temp 546 547 print "nextBlock: (" 548 print boardID 549 print ") " 550 print id 551 print "\n" 552 553 if got_id goto SKIP_SET_ID 554 # no INT arg => use a random next block 555 id = -1 556SKIP_SET_ID: 557 558 temp = self."board"( boardID ) 559 if_null temp, APP_NEXTBLOCK_END 560 temp = temp."nextBlock"(id ) 561 562APP_NEXTBLOCK_END: 563 .return (temp) 564.end 565 566=item app."fall"( boardID ) 567 568Lets the current block of the specified board 569fall down fast. 570 571=over 4 572 573=item parameter C<boardID> 574 575The ID of the board where the block should fall down fast. 576 577=back 578 579This method returns 1 if the board was found, 0 otherwise. 580 581=cut 582 583.sub fall :method 584 .param int boardID 585 .local pmc board 586 .local int ret 587 588 ret = 0 589 board = self."board"( boardID ) 590 if_null board, APP_FALL_END 591 board."fall"() 592 ret = 1 593 594APP_FALL_END: 595 .return (ret) 596.end 597 598=item falling = app."falling"( boardID ) 599 600Checks if the current block of the specified board 601is falling down fast. 602 603=over 4 604 605=item parameter C<boardID> 606 607The ID of the board to check. 608 609=back 610 611This method returns 1 if the block is falling down fast, 0 otherwise. 612 613=cut 614 615.sub falling :method 616 .param int boardID 617 .local pmc board 618 .local int ret 619 620 ret = 0 621 board = self."board"( boardID ) 622 if_null board, APP_FALLING_END 623 ret = board."falling"() 624 625APP_FALLING_END: 626 .return (ret) 627.end 628 629=item interval = app."fallInterval"( boardID ) 630 631Checks the board's falling speed. 632 633=over 4 634 635=item parameter C<boardID> 636 637The ID of the board to check. 638 639=back 640 641This method returns how many seconds it take for a block 642to fall down one unit. 643 644=cut 645 646.sub fallInterval :method 647 .param int boardID 648 .local pmc board 649 .local num ret 650 651 ret = 0 652 board = self."board"( boardID ) 653 if_null board, APP_INTERVAL_END 654 ret = board."fallInterval"() 655 656APP_INTERVAL_END: 657 .return (ret) 658.end 659 660=item nextfall = app."nextFallTime"( boardID ) 661 662Checks when the block on the specified board falls down 663the next unit. 664 665=over 4 666 667=item parameter C<boardID> 668 669The ID of the board to check. 670 671=back 672 673Returns the time when the block falls down the next time. 674 675=cut 676 677.sub nextFallTime :method 678 .param int boardID 679 .local pmc board 680 .local num ret 681 682 board = self."board"( boardID ) 683 if_null board, APP_NEXTFALL_END 684 ret = board."nextFallTime"() 685 686APP_NEXTFALL_END: 687 .return (ret) 688.end 689 690=item app."setNextFallTime"( boardID, val ) 691 692Sets the time when the block on the specified board 693falls down the next unit. 694 695=over 4 696 697=item parameter C<boardID> 698 699The ID of the board to modify. 700 701=item parameter C<val> 702 703The time when the block falls down the next time. 704 705=back 706 707This method returns nothing. 708 709=cut 710 711.sub setNextFallTime :method 712 .param int boardID 713 .param num val 714 .local pmc board 715 716 board = self."board"( boardID ) 717 if_null board, APP_SETFALL_END 718 board."setNextFallTime"( val ) 719 720APP_SETFALL_END: 721 .return () 722.end 723 724=item redrawn = app."timer"() 725 726Has to be called at frequent intervals. 727 728Returns 1 if the screen has been redrawn, 0 otherwise. 729 730=cut 731 732.sub timer :method 733 .local pmc board 734 .local int redraw 735 .local int ret 736 .local pmc boards 737 .local pmc board 738 .local int i 739 .local pmc inTimer 740 741 redraw = 0 742 743 # check the timer disable count 744 # do nothing if the timer is disabled 745 getattribute $P0, self, 'TimerDisableCount' 746 if $P0 > 0 goto END 747 748 getattribute inTimer, self, 'InTimer' 749 750 i = inTimer 751 if i goto END 752 753 # XXX: fetch exceptions and reset this flag 754 # we are in the timer handler function 755 inc inTimer 756 757 # get the boards array 758 (boards, i) = self."boards"( self ) 759 760LOOP: 761 dec i 762 if i < 0 goto REDRAW 763 board = boards[i] 764 765 # call the board's timer 766 ret = board."timer"() 767 768 unless ret goto LOOP 769 redraw = 1 770 branch LOOP 771 772REDRAW: 773 unless redraw goto NOREDRAW 774 self."drawScreen"( 0 ) 775NOREDRAW: 776 777 dec inTimer 778END: 779 780 .return (redraw) 781.end 782 783=item app."drawScreen"( full ) 784 785Redraws the screen. 786 787=over 4 788 789=item parameter C<full> 790 7910 = update only modified parts (uses a draw cache) 792 7931 = draw everything 794 795=back 796 797This method returns nothing. 798 799=cut 800 801.sub drawScreen :method 802 .param int full 803 .local pmc screen 804 .local pmc temp 805 806 self."disableTimer"() 807 808 screen = self."SDL"() 809 screen = screen."surface"() 810 811 # draw everything? 812 $I0 = self."flag"( "show blocksize" ) 813 if $I0 goto FULL 814 $I0 = self."flag"( "draw full" ) 815 if $I0 goto FULL 816 branch NOT_FULL 817FULL: 818 full = 1 819NOT_FULL: 820 821 # 822 # draw the main background 823 # 824 825 # to see the draw cache optimisation 826 $I0 = self."flag"( "show optimisation" ) 827 if $I0 goto FORCE 828 unless full goto NO_MAINBACKGROUND 829FORCE: 830 .local pmc rect 831 .local pmc screen 832 .local pmc color 833 834 rect = new 'Hash' 835 rect["width"] = 640 836 rect["height"] = 480 837 rect["x"] = 0 838 rect["y"] = 0 839 temp = new ['SDL'; 'Rect'], rect 840 color = self."color"( 3 ) 841 842 screen."fill_rect"( temp, color ) 843NO_MAINBACKGROUND: 844 # 845 # draw the boards 846 # 847 self."drawBoards"( screen, full ) 848 849 # 850 # update the screen 851 # 852 # XXX: optimize screen updates 853 rect = new 'Hash' 854 rect["width"] = 640 855 rect["height"] = 480 856 rect["x"] = 0 857 rect["y"] = 0 858 temp = new ['SDL'; 'Rect'], rect 859 screen."update_rect"( temp ) 860 861 self."enableTimer"() 862.end 863 864=item app."drawBoards"( screen, full ) 865 866=cut 867 868.sub drawBoards :method 869 .param pmc screen 870 .param int full 871 .local pmc boards 872 .local pmc board 873 .local int i 874 875 (boards, i) = self."boards"() 876 877LOOP: 878 dec i 879 if i < 0 goto END 880 board = boards[i] 881 board."draw"( screen, full ) 882 branch LOOP 883 884END: 885 .return () 886.end 887 888=item (boards, count) = app."boards"() 889 890Returns the number of boards registered as well as 891the boards array. 892 893=cut 894 895.sub boards :method 896 .local pmc boards 897 .local int count 898 899 getattribute boards, self, 'Boards' 900 count = 0 901 if_null boards, END 902 count = boards 903 904END: 905 .return (boards, count) 906.end 907 908=item app."registerBoard"( board, id ) B<(internal)> 909 910Called by the board constructor to add the board object 911to the application's board array. Returns the index where 912the board has been added, which is used as the board ID. 913 914=cut 915 916.sub registerBoard :method 917 .param pmc board 918 .local pmc boards 919 .local int id 920 921 (boards, id) = self."boards"() 922 set boards[id], board 923 924 .return (id) 925.end 926 927=item value = app."flag"( name, value ) 928 929Get or sets an integer debug flag. 930 931=over 4 932 933=item parameter C<name> 934 935The flag's name. 936 937=item parameter C<value> B<(optional)> 938 939Set the flag to the specified (integer) value. 940 941=back 942 943Returns the flag's (new) value. 944 945=cut 946 947.sub flag :method 948 .param string name 949 .param int value :optional 950 .param int got_value :opt_flag 951 952 .local pmc flag 953 .local int ret 954 955 # get the flags hash 956 getattribute flag, self, 'DebugFlags' 957 958 # check the number of INT args 959 unless got_value goto FLAG_GET 960 # set a new value 961 set flag[name], value 962 963FLAG_GET: 964 set ret, flag[name] 965 966 .return (ret) 967.end 968 969=item app."newGame"( boards ) 970 971Starts a new game with the specified number of boards. 972 973=over 4 974 975=item parameter C<boards> 976 977The number of boards to create. 978 979=back 980 981This method returns nothing. 982 983=cut 984 985.sub newGame :method 986 .param int players :optional 987 .param int got_players :opt_flag 988 .local pmc temp 989 .local int xpos 990 991 self."disableTimer"() 992 993 set xpos, 10 994 995 # check the number of INT args 996 if got_players goto SET 997 998 getattribute temp, self, 'Players' 999 players = 1 1000 if_null temp, SET 1001 players = temp 1002 branch END_SET 1003 1004SET: 1005 # save the number of players 1006 new temp, 'Integer' 1007 set temp, players 1008 1009 setattribute self, 'Players', temp 1010END_SET: 1011 1012 print "starting a " 1013 print players 1014 print " player game...\n" 1015 1016 # create the boards array 1017 new temp, 'ResizablePMCArray' 1018 1019 setattribute self, 'Boards', temp 1020 1021NEWGAME_NEW_BOARD: 1022 if players <= 0 goto NEWGAME_END 1023 print "new board...\n" 1024 temp = new "Tetris::Board", self 1025 print "new board done.\n" 1026 temp."setPosition"( xpos, 10 ) 1027 add xpos, 320 1028 dec players 1029 branch NEWGAME_NEW_BOARD 1030 1031NEWGAME_END: 1032 self."drawScreen"( 1 ) 1033 1034 self."enableTimer"() 1035 1036 .return () 1037.end 1038 1039=back 1040 1041=head1 AUTHOR 1042 1043Jens Rieks E<lt>parrot at jensbeimsurfen dot deE<gt> is the author 1044and maintainer. 1045Please send patches and suggestions to the Perl 6 Internals mailing list. 1046 1047=head1 COPYRIGHT 1048 1049Copyright (C) 2004-2008, Parrot Foundation. 1050 1051=cut 1052 1053# Local Variables: 1054# mode: pir 1055# fill-column: 100 1056# End: 1057# vim: expandtab shiftwidth=4 ft=pir: 1058