b0VIM 6.4FT{5nicknick-laptop~nick/Projects/perlman/perlmanU3210#"! Utp t w ^|  }g! q x`x^Y`adtzyma`OF8&dWI;,  Z 5  } Z < 9 8 ! W  N  3 oR;$ u^G,D }KCB1HE4 232, 16, 0, 1, 1, 0, # 5 114, 14, 0, 1, 1, 232, 16, 0, 1, 1, 0, # 5 114, 14, 0, 1, 1, 232, 16, 0, 1, 1, 0, # 5 232, 16, 0, 1, 1, 0, # 5 114, 14, 0, 1, 1, 1, # 2 14, 16, 0, 1, 0, 1, # 0my @intersections = (#### ( 498, 552, 1, 0, 1, 0 )#### corner of the screen, you can go up and left: ## directions are available, up, down, left, right. Example, the lower right## The format of this is the upper left corner coordinate, (x, y) and which#### is chasing pacman. ## it reaches an intersection, unless the ghost has a high agression level and## to determine which way to move. So a ghost will move in one direction until## When they hit an intersection they will be given which directions they can go## These intersections are for a method I am trying out with the ghosts## intersections ); { x => 298, y => 265, color => 'orange' }, { x => 222, y => 265, color => 'cyan' }, { x => 260, y => 265, color => 'pink' },my @ghostStart = ( { x => 260, y => 207, color => 'red' }, 'apple', 'pear', 'banana' );my @levelFruits = ( 'cherry', 'strawberry', 'orange', 'pretzel', my %ghostAggressions = ( 'red' => 90, 'cyan' => 70, 'pink' => 50, 'orange' => 30 );my @ghostColors = ( 'red', 'cyan', 'pink', 'orange' ); # in order of agressionmy %ghosts; # ghost datamy $eattenPellets = 0;my $baseSpeed = 0.3;my $fullscreen = 0;my $directionNext = 0;my $directionOld = $direction;my $direction = $LEFT;my $perlManAnim = 0;my $perlManAnimSpeed = 7;my $perlManSpeed = 0;my $ghostSpeed = 1;my $revdFreeLife = 0;my $freeLife = $FREE_LIFE;my $players = 0;my $lives = 0;my $highScore = 0;my $score = 0;my $redraw_all = 0;my $gameOver = 0;my $playing = 0; #0 Are we playing the game? default to '0' till game startsmy $level = 0; # keep track of the level, we default to '0' till the game beginsmy $pelletOffsetY = 0;my $pelletOffsetX = 13;my $boardOffsetY = 0;my $boardOffsetX = 5;## End of debug varsmy $drawGrid = 1; # draw path gridmy $drawIntersections = 1; # draw intersectionsmy $drawWarpGates = 1; # draw the actual gates that warp pacmanmy $drawWarps = 1; # draw warp collision rectanglesmy $collisionDebugOn = 1; # Collision data is printed to STDOUTmy $numOnly = 1; # this will draw ONLY the number labels and no grey rectanglesmy $labelRects = 1; # this numerically labels each onemy $drawPoints = 1; # this will draw the collision rectanglesmy $DEBUG = 1; # This will turn off ALL debugging, including the variables below## Debugging variables); -size => 1024 -channels => 2, -frequency => 44100,my $mixer = new SDL::Mixer(); -flags => SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_HWACCEL, -depth => 32, -height => 600, -width => 800, -title => 'PerlMan',my $app = new SDL::App(my $CHASE = 1;my $PATROL = 0;my $UP_RIGHT = 8;my $DOWN_RIGHT = 7;my $DOWN_LEFT = 6;my $UP_LEFT = 5;my $RIGHT = 4;my $LEFT = 3;my $DOWN = 2;my $UP = 1;my $FREE_LIFE = 200; #should be 10000my $LIVES = 3;## Some 'static' variable declarationsuse SDL::OpenGL;use SDL::TTFont;use SDL::Sound;use SDL::Mixer;use SDL::Event;use SDL::Cursor;use SDL::Surface;use SDL::App;use SDL;## SDL Librariesuse Switch;use strict;=cut=end comment info Version : $Id: .perlman.swp,v 1.1 2009/03/28 03:35:34 nick Exp $ PerlMan -- PacMan clone written using the Perl::SDL=begin comment info#!/usr/bin/perl -wado YtqonmlL3pT8 w h Y B A   q > ; 9 8 (  y ] @ "   i Y I -  } $app->fill ( $rect, $color ); ); -y => $y + $boardOffsetY, -x => $x + $boardOffsetX, -width => 1, -height => 1, my $rect = SDL::Rect->new ( ); -b => 0x00, -g => 0x00, -r => 0xff, my $color = SDL::Color->new ( my ( $x, $y ) = @_;sub drawPixel {} $app->fill ( $rect, $color ); ); -y => $y1 + $boardOffsetY, -x => $x1 + $boardOffsetX, -width => ( $x2 - $x1 ), -height => ( $y2 - $y1 ), my $rect = SDL::Rect->new ( ); -b => 0x00, -g => 0xff, -r => 0x00, my $color = SDL::Color->new ( my ( $x1, $y1, $x2, $y2, $num ) = @_;sub drawRectI {} } $font->print ( $app, $rect->x, $rect->y, $num ); -bg=>$color, -fg=>$fontColor ); my $font = new SDL::TTFont ( -name=>"./fonts/CourierNew-Bold.ttf", -size=>10, ); -b => 0x00, -g => 0x00, -r => 0xff, my $fontColor = new SDL::Color ( ) if ( $numOnly ); -b => 0x00, -g => 0x00, -r => 0x00, $color = new SDL::Color ( $num /= 4; if ( $labelRects ) { $app->fill ( $rect, $color ) if ( ! $numOnly ); ); -y => $y1 + $boardOffsetY, -x => $x1 + $boardOffsetX, -width => ( $x2 - $x1 ), -height => ( $y2 - $y1 ), my $rect = SDL::Rect->new ( ); -b => 0xff, -g => 0xff, -r => 0xff, my $color = SDL::Color->new ( my ( $x1, $y1, $x2, $y2, $num ) = @_;sub drawRect {################################### Temporary fucntions###############################} } $app->grab_input ( SDL_GRAB_OFF ); SDL::Cursor::show ( undef, 1 ); $fullscreen = 0; else {ad<|@t4 w v d a ` I    ~ { h E 9 8  x i d 6 %  B (  J  ron[YXWT3i=M#nFC wb8xtjge9763d?< } $app->grab_input ( SDL_GRAB_ON ); SDL::Cursor::show ( undef, 0 ); $fullscreen = 1; if ( ! $fullscreen ) { $app->fullscreen ();sub setFullScreen {#### Switch between fullscreen and windowed. Take away our mouse cursor in fullscreen##} return $hit; # returns non-zero for a hit } $hit++; } $collisionDebugOn--;EOF+-------+---------------+---------------+| Left | $leftA | $leftB || Right | $rightA | $rightB || Top | $topA | $topB || Bottom| $bottomA | $bottomB |+-------+---------------+---------------+ | Wall | PacMan | +---------------+---------------+Collision with wall $rect at:DirectionNext : $directionNextDirectionOld : $directionOldDirection : $direction print <= $rightB ); next if ( $rightA <= $leftB ); next if ( $topA >= $bottomB ); next if ( $bottomA <= $topB ); my $bottomA = $SDLBoardRects[$rect]->y + $SDLBoardRects[$rect]->height; my $topA = $SDLBoardRects[$rect]->y; my $rightA = $SDLBoardRects[$rect]->x + $SDLBoardRects[$rect]->width; my $leftA = $SDLBoardRects[$rect]->x; for my $rect ( 0 .. $#SDLBoardRects ) { my $bottomB = $object->y + $object->height; my $topB = $object->y; my $rightB = $object->x + $object->width; my $leftB = $object->x; my $hit = 0; my $object = shift; # must be a SDL->Rectsub wallCollision {##### Usefull for pacman and the ghosts## This will tell us if we are about to hit a wall.## Lets do some wall collisions.##} return @SDLRects; } push ( @SDLRects, $rect ); ); -y => $gameBoardData[$idx + 1] + $boardOffsetY - 2, -x => $gameBoardData[$idx] + $boardOffsetX - 1, -width => ( $gameBoardData[$idx + 2] - $gameBoardData[$idx] + 3 ), -height => ( $gameBoardData[$idx + 3] - $gameBoardData[$idx + 1] + 4 ), my $rect = SDL::Rect->new ( for ( my $idx = 0; $idx <= $size; $idx += 4 ) { $size--; my @SDLRects = (); my $size = scalar @gameBoardData;sub boardDataToSDLRects {## Convert our board x,y Data in to SDL rectangles so we don't need to keep redoing this} } ); -y => ( $pelletCords[$idx+1] ),# + $pelletOffsetY ), -x => ( $pelletCords[$idx] ), #+ $pelletOffsetX ), -width => 4, -height => 4, $pellets{$idx}{'rect'} = SDL::Rect->new ( ); -b => 0x97, -g => 0xb8, -r => 0xff, $pellets{$idx}{'color'} = new SDL::Color ( $pellets{$idx}{'active'} = 1; $pellets{$idx}{'points'} = 10; for ( my $idx = 0; $idx <= $pcount; $idx += 2 ) { $pcount--; my $pcount = scalar @pelletCords;sub setupPellets {#### locations, and activity## populate our pellet hash with pellet images, rectangles## We have a list of pellet coordinates. We want to ##} } &configIntroScreen unless ( defined ( $bgColor ) ); sub drawIntroScreen { } $app->sync ( ); $titleImage->blit ( 0, $app, $titleRect ); $app->fill ( 0, $bgColor ); $app->grab_input ( SDL_GRAB_OFF ); SDL::Cursor::show ( undef, 1 ); # no point in grabbing the mouse... -height=>$titleImage->height ( ), -y=>50, -x=>170 ); $titleRect = new SDL::Rect ( -width=>$titleImage->width ( ), $titleImage = new SDL::Surface ( -name=>'./images/perlman-title.gif' ); $fgColor = new SDL::Color ( -r=>0, -g=>255, -b=>255 ); #yellow $bgColor = new SDL::Color ( -r=>0, -g=>0, -b=>0 ); #black sub configIntroScreen { my ( $bgColor, $fgColor, $titleImage, $titleRect );{## ## Handling the opening intro screen## } }adxwDq7'   d a K .  t s N  z < ; v a `  m ` 4  hSRgZ/}hWspo@1JN wdc#" return 0; sub seePacMan { } return 0; return 1 if ( &wallCollision ( $gRect ) == 0 ); ); -y => $ghosts{$ghost}{'rect'}->y + $changeY, -x => $ghosts{$ghost}{'rect'}->x + $changeX, -width => $ghosts{$ghost}{'rect'}->width, -height => $ghosts{$ghost}{'rect'}->height, my $gRect = SDL::Rect->new ( } case 4 { $changeX += $ghostSpeed; } #right case 3 { $changeX -= $ghostSpeed; } #left case 2 { $changeY += $ghostSpeed; } #down case 1 { $changeY -= $ghostSpeed; } #up switch ( $dir ) { my $changeX = 0; my $changeY = 0; my ( $ghost, $dir ) = @_; sub canMove { # Can we move in the direction we want to go? }# print "$rdir\n"; $ghosts{$ghost}{'direction'} = $rdir; do { $rdir = int rand ( 4 ) + 1; } while ( $rdir != $ghosts{$ghost}{'direction'} & ! &canMove ( $_, $rdir ) );# print "Random Chase ($ghost): using direction ==> "; my $rdir = 0; my $ghost = shift; sub doRandomChase { } else { return 0; } } return 1; $ghosts{$ghost}{'pacPos'} = $UP; $ghosts{$ghost}{'direction'} = ( $ghosts{$ghost}{'scared'} ) ? $DOWN : $UP; } elsif ( &canMove ( $ghost, $UP ) ) { return 1; $ghosts{$ghost}{'pacPos'} = $DOWN; $ghosts{$ghost}{'direction'} = ( $ghosts{$ghost}{'scared'} ) ? $UP : $DOWN; #pacman is below us if ( ( $ghosts{$ghost}{'rect'}->y <= $perlManRect->y ) && ( &canMove ( $ghost, $DOWN ) ) ) { my $ghost = shift; sub tryVerticleChase { } return 0; } return 1; $ghosts{$ghost}{'pacPos'} = $LEFT; $ghosts{$ghost}{'direction'} = ( $ghosts{$ghost}{'scared'} ) ? $LEFT: $RIGHT; # PacMan is on our right } elsif ( &canMove ( $ghost, $RIGHT ) ) { return 1; $ghosts{$ghost}{'pacPos'} = $RIGHT; $ghosts{$ghost}{'direction'} = ( $ghosts{$ghost}{'scared'} ) ? $RIGHT : $LEFT; # pacman is on our left if ( ( $ghosts{$ghost}{'rect'}->x > $perlManRect->x ) && ( &canMove ( $ghost, $LEFT ) ) ) { my $ghost = shift; sub tryHorizontalChase { } } else { &doRandomChase ( $_ ) if ( &tryVerticleChase ( $_ ) == 0 ); } if ( $dx >= $dy ) { &doRandomChase ( $_ ) if ( &tryHorizontalChase ( $_ ) == 0 ); } my $dy = abs ( $ghosts{$_}{'rect'}->y - $perlManRect->y ); my $dx = abs ( $ghosts{$_}{'rect'}->x - $perlManRect->x ); ## Find PacMan and head towards him $ghosts{$_}{'targetWait'} = 200; next if ( ( $ghosts{$_}{'targetWait'} ) > $ghosts{$_}{'aggression'} ); ## Are we ready for a new target? $ghosts{$_}{'targetWait'} = 0 if ( $ghosts{$_}{'aggression'} > 50 && &seePacMan ( $_ ) ); ## If we are aggressive enough and see pacman, chase him $ghosts{$_}{'targetWait'}--; foreach ( keys %ghosts ) {sub perlManPosition { # # Determine pacman's position from a ghost and set a direction # } }EOFy+w -> $y1x+h -> $x1y -> $yx -> $xCorner Where we are going to be Where we werePosition of PerlMan on map: print <y + $perlManRect->height); my $y1 = $perlManRect->y + $perlManRect->height; $x1 .= "\t\t\t\t".($pmRect->x + $perlManRect->width); my $x1 = $perlManRect->x + $perlManRect->width; my $y = $perlManRect->y."\t\t\t\t".$pmRect->y; my $x = $perlManRect->x."\t\t\t\t".$pmRect->x; if ( $DEBUG ) { sub positionDebug { } $app->sync(); } $top += $textHeight; $gameOverFont->print ( $app, 150, $top, $m );ad`]~a3 w -  m I % $  k ( h 7  O xEv6ztGYp= q7xu? $x = ( 800 - $x ) / 2; my $x = $gameOverFont->width ( 0, $m ); foreach my $m ( @msg ) { my $top = ( 600 - ( ( scalar @msg ) * $textHeight ) ) / 2; my $textHeight = $gameOverFont->height ( $msg[0] ); "Press button to continue" ); my @msg = ( "GAME OVER", "Score: $score", "Level: $level", sub drawGameOver { } } ); -y => $ghosts{$ghostColors[$_]}{'pos'}{'old'}{'Y'} -x => $ghosts{$ghostColors[$_]}{'pos'}{'old'}{'X'}, -height => $ghostImages[$_][1]->height, -width => $ghostImages[$_][1]->width, $ghosts{$ghostColors[$_]}{'rectOld'} = new SDL::Rect ( ); -y => $ghosts{$ghostColors[$_]}{'pos'}{'Y'} -x => $ghosts{$ghostColors[$_]}{'pos'}{'X'}, -height => $ghostImages[$_][1]->height, -width => $ghostImages[$_][1]->width, $ghosts{$ghostColors[$_]}{'rect'} = new SDL::Rect ( $ghostImages[$_][13] = new SDL::Surface ( -name => './images/ghost-scared-2.gif' ); $ghostImages[$_][9] = new SDL::Surface ( -name => './images/ghost-scared-1.gif' ); } $ghostImages[$_][$idx] = new SDL::Surface ( -name => $filen ); my $filen = "./images/ghost-" . $ghostColors[$_] . "-" . $idx . ".gif"; for ( my $idx = 1; $idx <= 8; $idx++ ) { } case 3 { $ghosts{$ghostColors[$_]}{'direction'} = $DOWN; } case 2 { $ghosts{$ghostColors[$_]}{'direction'} = $UP; } case 1 { $ghosts{$ghostColors[$_]}{'direction'} = $UP; } case 0 { $ghosts{$ghostColors[$_]}{'direction'} = $LEFT; } switch ( $_ ) { $ghosts{$ghostColors[$_]}{'aggression'} = $ghostAggressions{$ghostColors[$_]}; $ghosts{$ghostColors[$_]}{'targetWait'} = 0; $ghosts{$ghostColors[$_]}{'target'}{'Y'} = 0; $ghosts{$ghostColors[$_]}{'target'}{'X'} = 0; $ghosts{$ghostColors[$_]}{'pos'}{'old'}{'Y'} = $ghostStart[$_]{y}; $ghosts{$ghostColors[$_]}{'pos'}{'old'}{'X'} = $ghostStart[$_]{x}; $ghosts{$ghostColors[$_]}{'pos'}{'Y'} = $ghostStart[$_]{y}; $ghosts{$ghostColors[$_]}{'pos'}{'X'} = $ghostStart[$_]{x}; $ghosts{$ghostColors[$_]}{'mode'} = $PATROL; $ghosts{$ghostColors[$_]}{'pacPos'} = $DOWN_LEFT; $ghosts{$ghostColors[$_]}{'scared'} = 0; $ghosts{$ghostColors[$_]}{'state'} = 0; $ghosts{$ghostColors[$_]}{'speed'} = 1; foreach ( 0 .. $#ghostColors ) { # which I have yet to learn how to do. # animation state, location, and whatever we need for the AI # We have four ghosts. We need to assign each ghost a graphic, sub setupGhosts { } return 0; } } return $pellets{$_}{'rect'}; $eattenPellets++; $pellets{$_}{'active'} = 0; $score += $pellets{$_}{'points'}; next if ( $leftA >= $rightB ); next if ( $rightA <= $leftB ); next if ( $topA >= $bottomB ); next if ( $bottomA <= $topB ); my $bottomA = $pellets{$_}{'rect'}->y + $pellets{$_}{'rect'}->height; my $topA = $pellets{$_}{'rect'}->y; my $rightA = $pellets{$_}{'rect'}->x + $pellets{$_}{'rect'}->width; my $leftA = $pellets{$_}{'rect'}->x; if ( $pellets{$_}{'active'} ) { foreach ( keys %pellets ) { my $bottomB = $object->y + $object->height; my $topB = $object->y; my $rightB = $object->x + $object->width; my $leftB = $object->x; my $object = shift; # must be a SDL->Rect sub pelletCollision { } &updateScores (); $smallTitleImage->blit ( 0, $app, $smallTitleRect ); -height=>$smallTitleImage->height(), -y=>4, -x=>550 ); my $smallTitleRect = new SDL::Rect ( -width=>$smallTitleImage->width(), my $smallTitleImage = new SDL::Surface ( -name=>'./images/perlman-title-small.png' );adCoEA>=&W g S ? >  ] X W 6 z x L n h < 8 5 4  N ; ( '  \4 ~YTS(j]YP jf?xUQNMI!n> on sub drawStats () { ## ## Display high score, initial score, pacman lives, and level fruits ## } $gameBoardImage->blit ( 0, $app, $gameBoardRect ); -y=>$boardOffsetY, -x=>$boardOffsetX ); -height=>$gameBoardImage->height(), my $gameBoardRect = new SDL::Rect ( -width=>$gameBoardImage->width(), my $gameBoardImage = new SDL::Surface ( -name=>'./images/gameboard.png' ); sub drawGameBoard () { ## ## Display our blue game board borders ## } } if ( $pellets{$_}{'active'} ); $app->fill ( $pellets{$_}{'rect'}, $pellets{$_}{'color'} ) foreach ( keys %pellets ) { sub drawPellets () { ## ## Draw Pellets ## } $perlManAnim++; $PMSD = 1 if ( $perlManState < 2 ); $PMSD = -1 if ( $perlManState > 3 ); } $perlManAnim = 0; $perlManState += $PMSD; if ( $perlManAnim > $perlManAnimSpeed ) { sub updatePerlManState () { ## Animiation state } } return 0; $directionNext = ( $dir != $directionOld ) ? $directionOld : 0; else { } return 1; $directionOld = $dir if ( $dir != $directionOld ); $perlManPosX += $changeX; $perlManPosY += $changeY; $perlManPosOldX = $perlManPosX; $perlManPosOldY = $perlManPosY; if ( &wallCollision ( $pmRect ) == 0 ) { ); -y => $perlManRect->y + $changeY, -x => $perlManRect->x + $changeX, -width => $perlManRect->width, -height => $perlManRect->height, $pmRect = SDL::Rect->new ( } case 4 { $changeX += $speed; } #right case 3 { $changeX -= $speed; } #left case 2 { $changeY += $speed; } #down case 1 { $changeY -= $speed; } #up switch ( $dir ) { $scnt = 0 if ( $scnt >= $sleep ); $scnt++; } return 0; $scnt++; if ( $scnt < $sleep ) { my $changeY = 0; my $changeX = 0; my $sleep = 1; # wait delay to move pacman (decrease to increase speed) my $speed = 2; # number of pixels to move pacman my $dir = ( $directionNext != 0 ) ? $directionNext : shift; sub updatePerlManDirection () { } } else { $ghosts{$_}{'targetWait'} = 0; } } $ghosts{$_}{'pos'}{'X'} += $changeX; $ghosts{$_}{'pos'}{'Y'} += $changeY; $ghosts{$_}{'pos'}{'old'}{'X'} = $ghosts{$_}{'pos'}{'X'}; $ghosts{$_}{'pos'}{'old'}{'Y'} = $ghosts{$_}{'pos'}{'Y'}; if ( &wallCollision ( $gRect ) == 0 ) { ); -y => $ghosts{$_}{'rect'}->y + $changeY, -x => $ghosts{$_}{'rect'}->x + $changeX, -width => $ghosts{$_}{'rect'}->width, -height => $ghosts{$_}{'rect'}->height, my $gRect = SDL::Rect->new ( } case 4 { $changeX += $ghostSpeed; } #right case 3 { $changeX -= $ghostSpeed; } #left case 2 { $changeY += $ghostSpeed; } #down case 1 { $changeY -= $ghostSpeed; } #up switch ( $ghosts{$_}{'direction'} ) { my $changeX = 0; my $changeY = 0; foreach ( keys %ghosts ) { ## Move ghost towards target &perlManPosition (); ## Determine where to move ghost# my $speed = 1; sub updateGhostDirection () { } } $rectData[$idx + 2], $rectData[$idx + 3 ], $idx ); &drawRect ( $rectData[$idx], $rectData[$idx + 1], for ( my $idx = 0; $idx <= $size; $idx += 4 ) { # Rectangles $size--; my $size = scalar @rectData; my @rectData = @$ref; my $ref = shift; sub drawBoardPoints { } } $intersections[$idx] + 1, $intersections[$idx + 1] + 1, $idx ); &drawRectI ( $intersections[$idx], $intersections[$idx + 1], for ( my $idx = 0; $idx <= $size; $idx += 6 ) {ad !zfba\ueE  { w Z  m i F B 0 [ 1  ryuZkU9UT=)%$#yj[VU2#xw*ute # Rectangles $size--; my $size = scalar @intersections; sub drawIntersections { } $app->sync(); $font->print ( $app, 600, 300, $score); -bg=>$rectColor, -fg=>$fontColor ); $font = new SDL::TTFont ( -name=>"./fonts/CourierNew-Bold.ttf", -size=>16, $app->fill ( $rect, $rectColor ); ); -width => 100, -height => 26, -y => 300, -x => 600, my $rect = new SDL::Rect ( ); -b => 0x00, -g => 0x00, -r => 0x00, my $rectColor = new SDL::Color ( ); -b => 0xff, -g => 0xff, -r => 0xff, $fontColor = new SDL::Color ( my ( $fontColor, $font ); sub updateScores { } $app->update ( @modified_areas ); # # Blink power pellets # } &updateScores(); # update the scores $mixer->play_channel ( 1, $sndMunch, 0 ); # Play the munch sound push ( @modified_areas, $rect ); $app->fill ( $rect, $color ); ); -b => 0x00, -g => 0x00, -r => 0x00, my $color = new SDL::Color ( # we ate a pellet! Yum! if ( $rect != 0 ) { my $rect = &pelletCollision ( $perlManRect ); # # Did we eat a pellet? # &updatePerlManState(); } &updatePerlManDirection ( $direction ); } else { $directionNext = 0 if ( &updatePerlManDirection( $directionNext ) ); if ( $directionNext ) { # # Check direction and update # push ( @modified_areas, $perlManRect ); else { $perlManImages[$direction][$perlManState]->blit ( 0, $app, $perlManRect ); } if ( $perlManState == 1 ) { $perlManImages[0]->blit ( 0, $app, $perlManRect ); } $perlManRect->y ( $perlManPosY ); $perlManRect->x ( $perlManPosX ); # # Draw new Pacman # push ( @modified_areas, $perlManRectOld ); $app->fill ( $perlManRectOld, $bgColor ); $perlManRectOld->y ( $perlManPosOldY ); $perlManRectOld->x ( $perlManPosOldX ); # # Erase Old Pacman # &updateGhostDirection ( ); # # Determine where to move ghosts # } push ( @modified_areas, $ghosts{$ghostColors[$_]}{'rect'} ); $ghostImages[$_][$ghostImg]->blit ( 0, $app, $ghosts{$ghostColors[$_]}{'rect'} ); $ghosts{$ghostColors[$_]}{'state'} = ( $ghosts{$ghostColors[$_]}{'state'} == 4 ) ? 0 : 4; my $ghostImg = ( $ghosts{$ghostColors[$_]}{'scared'} ) ? ( 9 + $ghosts{$ghostColors[$_]}{'state'} ) : ( $ghosts{$ghostColors[$_]}{'direction'} + $ghosts{$ghostColors[$_]}{'state'} ); $ghosts{$ghostColors[$_]}{'rect'}->y ( $ghosts{$ghostColors[$_]}{'pos'}{'Y'} ); $ghosts{$ghostColors[$_]}{'rect'}->x ( $ghosts{$ghostColors[$_]}{'pos'}{'X'} ); foreach ( 0 .. $#ghostColors ) { # # draw new ghosts ###REMOVE $app->fill ( $ghosts{ $app->fill ( $ghosts{$_}{'rectOld'}, $bgColor ); $ghosts{$_}{'rectOld'}->y ( $ghosts{$_}{'pos'}{'old'}{'Y'} ); $ghosts{$_}{'rectOld'}->x ( $ghosts{$_}{'pos'}{'old'}{'X'} ); foreach ( @ghostColors ) { # # Erase old Ghosts ## }# }# }# push ( @modified_areas, $pellets{$_}{'rect'} );# $app->fill ( $pellets{$_}{'rect'}, $pellets{$_}{'color'} ); # if ( $pellets{$_}{'active'} ) {# foreach ( keys %pellets ) {# $ptick = 0;# if ( $ptick >= 10 ) {# $ptick++; # # refresh pellets every 10th tick # my @modified_areas = (); # # redraw the pellets the ghosts go over. # We always need to modify ghosts, pacman, power pellets (blinking) and # } $redraw_all = 0; $app->sync (); &drawBoardPoints ( \@warpZoneData ) if ( $drawWarpGates && $DEBUG ); &drawGrid if ( $drawGrid && $DEBUG ); $#ad!}p\LIGF$_ k M  s p n m R Q O N K    { / | { v K F /   y x u t \ ' {z^I4!dbNM7%$h/yBu[UpolkU> \!  &drawGrid if ( @d &drawIntersections if ( $drawIntersections && $DEBUG ); &drawBoardPoints ( \@warpZones ) if ( $drawWarps && $DEBUG ); &drawBoardPoints ( \@gameBoardData ) if ( $drawPoints && $DEBUG ); &drawPellets (); &drawStats (); &drawGameBoard (); $app->fill ( 0, $bgColor ); if ( $redraw_all ) { sub drawGameScreen { } $sndMunch = new SDL::Sound ( './sounds/munch A+B.wav' ); ## Load up the music } } $perlManImages[$dir][$idx] = new SDL::Surface ( -name=>$filename ); my $filename = "./images/perlman" . $idx . "" . $d . ".gif"; } case 4 { $d = 'r'; } case 3 { $d = 'l'; } case 2 { $d = 'd'; } case 1 { $d = 'u'; } switch ( $dir ) { my $d; for ( my $idx = 2; $idx <= 4; $idx++ ) { for ( my $dir = 1; $dir <= 4; $dir++ ) { -y=>$perlManPosOldY, -x=>$perlManPosOldX ); -height=>$perlManImages[0]->height (), $perlManRectOld = new SDL::Rect ( -width=>$perlManImages[0]->width (), -y=>$perlManPosY, -x=>$perlManPosX ); -height=>$perlManImages[0]->height (), $perlManRect = new SDL::Rect ( -width=>$perlManImages[0]->width (), $perlManImages[0] = new SDL::Surface ( -name=>'./images/perlman1.gif' ); ## Load up all our perlMan images &setupGhosts (); ## Configure ghosts &setupPellets (); $perlManPosY = $perlManPosOldY = 436; $perlManPosX = $perlManPosOldX = 358; $perlManPosY = $perlManPosOldY = 436; $perlManPosX = $perlManPosOldX = 258; $direction = $LEFT; $scnt = 0; $PMSD = 1; $perlManState = 1; ## Some starting defaults -bg=>$bgColor, -fg=>$fgColor ); $gameOverFont = new SDL::TTFont ( -name=>"./fonts/crackman.ttf", -size=>20, $fgColor = new SDL::Color ( -r=>0, -g=>255, -b=>255 ); $bgColor = new SDL::Color ( -r=>0, -g=>0, -b=>0 ); sub configGameScreen { } return $nextHeartbeat; &drawGameScreen (); my $nextHeartbeat = 5; ## Will eventually speed the game up as you go on } return 500; &drawGameOver ( ); $gameOver = 1; if ( $lives <= 0 ) { ## ## Game over if player has no more lives ## &configGameScreen unless ( $bgColor ); sub updateGameState { my ( @ghostImages ); ## All ghost information is stored here. my ( $scnt, $pmRect, $sndMunch, $ptick ); my ( $perlManPosX, $perlManPosY, $perlManPosOldX, $perlManPosOldY, $PMSD); my ( @perlManImages, @perlManRects, $perlManState, $nextHeartbeat ); my ( $bgColor, $perlManRect, $perlManRectOld, $fgColor, $gameOverFont, $textHeight );{#### Handling the game state (playing the game)##}sub processEventGameOver {} } case 'g' { $lives = 0; } } } $ghosts{$_}{'scared'} = ( $ghosts{$_}{'scared'} ) ? 0 : 1; case "s" { foreach ( keys %ghosts ) { } $collisionDebugOn = 1; print "Waiting for collision...\n"; } case "c" { if ( $DEBUG ) { case "p" { &positionDebug ( ) if $DEBUG } case "right" { $directionOld = $direction; $direction = $RIGHT; } case "left" { $directionOld = $direction; $direction = $LEFT; } case "down" { $directionOld = $direction; $direction = $DOWN; } case "up" { $directionOld = $direction; $direction = $UP; } case 'f' { &setFullScreen (); } switch ( $keyName ) { my $keyName = $event->key_name (); $directionNext = 0; my $event = shift;sub processEventPlayingKeypress {} } $players = 2; $lives = $LIVES; $score = 0; $redraw_all = 1; $actions = $actions_playing; $gameOver = 0; $level = 1; $playing = 1; if ( $key_name eq '2' ) {adGt[B)(oT9 ^ E *  u 1 v 7 [ p " IE(tsp@=/.kjJIr= ~n]I*xwQ6 }nZJGF } $players = 1; $lives = $LIVES; $score = 0; $redraw_all = 1; $actions = $actions_playing; $gameOver = 0; $level = 1; $playing = 1; $key_name eq '1' ) { if ( $key_name eq 'return' || } &setFullScreen ( ); if ( $key_name eq 'f' ) { my $key_name = $event->key_name ( ); my $event = shift;sub processEventIntroScreenKeypress {} return 50; } return 500; &drawIntroScreen (); } else { } return 500; &drawPauseScreen (); } else { return &updateGameState (); if ( $playing ) { if ( $level ) {sub heartbeat {} $app->delay ( 5 ); } } $next_heartbeat = $app->ticks + $n; my $n = &heartbeat () || 50; if ( $app->ticks >= $next_heartbeat ) { } } $actions->{$type}->($event); ref ( $actions->{$type} ) eq "CODE" ) { if ( exists ( $actions->{$type} ) && $event->key_name () eq 'q' ); $event->key_name () eq 'escape' || last MAIN_LOOP if ( $type == SDL_KEYDOWN && last MAIN_LOOP if ( $type == SDL_QUIT ); my $type = $event->type ( ); while ( $event->poll ) { while ( 1 ) { MAIN_LOOP: print "Debugging is enabled.\n" if ( $DEBUG ); my $event = new SDL::Event; my $next_heartbeat = $app->ticks;sub eventLoop {&eventLoop();#### Program starts here with the main event loop##my $actions = $actions_intro_screen;}; SDL_KEYDOWN() => \&processEventGameOver,my $actions_game_over = {}; SDL_KEYDOWN() => \&processEventIntroScreenKeypress,my $actions_intro_screen = {}; SDL_KEYDOWN () => \&processEventPlayingKeypress,my $actions_playing = { my @SDLBoardRects = &boardDataToSDLRects();my %pellets; ); 564, 451, 564, 470, 564, 489, 564, 508, 564 ## bottom row 564, 318, 564, 337, 564, 356, 564, 375, 564, 394, 564, 413, 564, 432, 564, 185, 564, 204, 564, 223, 564, 242, 564, 261, 564, 280, 564, 299, 31, 564, 51, 564, 71, 564, 91, 564, 109, 564, 128, 564, 147, 564, 166, 507, 451, 507, 470, 507, 489, 507, 508, 507, ## 2nd row from bottom 507, 318, 507, 337, 507, 356, 507, 432, 185, 507, 204, 507, 223, 507, 242, 507, 299, 31, 507, 51, 507, 71, 507, 91, 507, 109, 507, 470, 450, 489, 450, 508, 450, ## 3rd row from bottom 450, 318, 450, 337, 450, 356, 450, 376, 450, 394, 450, 450, 185, 450, 204, 450, 223, 450, 242, 450, 299, 31, 450, 51, 450, 71, 450, 147, 450, 166, 391, 451, 391, 470, 391, 489, 391, 508, 391, ## 4th row from bottom 391, 318, 391, 337, 391, 356, 391, 375, 391, 394, 391, 432, 391, 185, 391, 204, 391, 223, 391, 242, 391, 299, 31, 391, 51, 391, 71, 391, 91, 391, 109, 391, 147, 391, 166, 470, 469, 470, 487, 356, 469, 356, 487, 185, 469, 185, 487, 71, 469, 71, 487, ## vertical pieces 3rd from bottom 508, 431, 508, 412, 299, 431, 299, 412, 242, 431, 242, 412, 31, 431, 31, 412, ## vertical pieces 2rd from bottom 508, 545, 508, 526, 299, 545, 299, 526, 242, 545, 242, 526, 31, 545, 31, 526, ## vertical pieces on bottom 185, 144, 356, 144, 508, 144, 185, 125, 356, 125, 508, 125, ## Verticle pieces by ghost house 508, 87, 299, 87, 508, 68, 299, 68, 508, 49, 299, 49, ## Verticle top pieces ## Long column on right 413, 391, 413, 412, 413, 431, 413, 450, 413, 469, 413, 487, 413, 507, ad Uja`$lU  _ ?   _ ?   _ ? !  c C #  t l k  b1wL<,+8|{0_^15{zD 413, 258, 413, 277, 413, 296, 413, 315, 413, 334, 413, 353, 413, 372, 413, 125, 413, 144, 413, 163, 413, 182, 413, 201, 413, 220, 413, 239, 413, 31, 413, 49, 413, 68, 413, 87, 413, 106, ## Long column on left 128, 391, 128, 412, 128, 431, 128, 450, 128, 469, 128, 487, 128, 507, 128, 258, 128, 277, 128, 296, 128, 315, 128, 334, 128, 353, 128, 372, 128, 125, 128, 144, 128, 163, 128, 182, 128, 201, 128, 220, 128, 239, 163, 451, 163, 470, 163, 489, 163, 508, 163, ## 2nd row from top 163, 318, 163, 337, 163, 356, 163, 432, 185, 163, 204, 163, 223, 163, 242, 163, 299, 51, 163, 71, 163, 91, 163, 109, 163, 451, 106, 470, 106, 489, 106, 508, 106, ## Top long row 318, 106, 337, 106, 356, 106, 376, 106, 394, 106, 432, 106, 185, 106, 204, 106, 223, 106, 242, 106, 261, 106, 280, 106, 299, 106, 51, 106, 71, 106, 91, 106, 109, 106, 128, 106, 147, 106, 166, 106, 30, 451, 30, 470, 30, 489, 30, 508, 30, ## 4th row from bottom 30, 318, 30, 337, 30, 356, 30, 375, 30, 394, 30, 432, 30, 185, 30, 204, 30, 223, 30, 242, 30, 299, 31, 30, 51, 30, 71, 30, 91, 30, 109, 30, 128, 30, 147, 30, 166, 242, 87, 242, 68, 242, 49, # top right of 2nd island 242, 30, 147, 30, 166, 30, 185, 30, 204, 30, 223, 30, # above top left 2nd island 128, 87, 128, 68, 128, 49, # top left between islands 128, 30,my @pelletCords = ( 31, 30, 31, 49, 31, 68, 31, 87, 31, 106, 31, 125, 31, 144, 31, 163, # top left going downmy @warpZoneData = ( 1,264, 2, 296, 536, 264, 537, 296 );## "transported" to the outside of the next gate## These are the actual warp zone gates. When PacMan colides with them, he will bemy @warpZones = ( 1, 264, 25, 296, 500, 264, 545, 296);#### he enters the warp zone gates#### organized the same as above; these are the rectangles that will 'hide' pacman as ); 0, 13, 9, 184 # 45 0, 377, 9, 597, # 44 0,184, 106, 262, # 43 142, 471, 164, 529, # 42 47, 529, 221, 548, # 41 9, 471, 48, 492, # 40 85, 434, 106, 492, # 39 47, 414, 106, 434, # 38 0, 585, 536, 598, # 37 315, 529, 489, 548, # 36 373, 471, 393, 529, # 35 429, 434, 451, 492, # 34 429, 414, 489, 434, # 33 142, 414, 221, 434, # 32 0, 299, 106, 377, # 31 142, 299, 163, 377, # 30 200, 241, 251, 253, # 29 200, 253, 210, 308, # 28 200, 308, 336, 320, # 27 326, 253, 336, 308, # 26 285, 241, 336, 253, # 25 258, 375, 278, 434, # 24 200, 356, 336, 377, # 23 314, 414, 393, 433, # 22 372, 298, 393, 377, # 21 257, 491, 278, 548, # 20 200, 470, 336, 492, # 19 257, 146, 278, 204, # 18 200, 127, 336, 146, # 17 314, 183, 373, 204, # 16 372, 126, 393, 262, # 15 487, 470, 528, 492, # 14 526, 375, 536, 596, # 13 429, 299, 536, 377, # 12 526, 12, 536, 185, # 11 429, 185, 536, 262, # 10 429, 127, 489, 147, # 9 429, 51, 489, 90, # 8 314, 51, 393, 90, # 7 0, 3, 534, 13, # 6 my $gridYCount = 30;my $gridXCount = 30;my $gridDistance = 30; ); 500,400 # bottom right 31, 400, # bottom left 500, 30, # upper right 31, 30, # upper leftmy @gridCorners = (## Columns and row grid for pacman and ghosts to move along ); 498, 552, 1, 0, 1, 0 14, 92, 1, 1, 0, 1, # 1 114, 92, 1, 1, 1, 1, # 1, 3 172, 92, 0, 1, 1, 1, # 3, 17 230, 92, 1, 0, 1, 1, # 17, 5adq0# q 1 - , (   r F    ` S H   z Y I 9 ) #  ~jfed`HDCBsdUFA@#k=c`\YX? # Rectangles $size--; my $size = scalar @intersections; sub drawIntersections { } } oooooooooooooooooooooooooooooooooooooooooo============================================================================================================================================================================================================================================================================================================================================================================================================ooooooooooooo333333333o3oooooooooooooooooooooooooo3qqqqqqqqqqqqqqqqqooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq for ( my $idx = 0; $id <= $#gridCorners; $id += 2 ) { sub drawGrid { } $app->sync(); $font->print ( $app, 600, 300, $score); -bg=>$rectColor, -fg=>$fontColor ); $font = new SDL::TTFont ( -name=>"./fonts/CourierNew-Bold.ttf", -size=>16, $app->fill ( $rect, $rectColor ); ); -width => 100, -height => 26, -y => 300, -x => 600, my $rect = new SDL::Rect ( ); -b => 0x00, -g => 0x00, -r => 0x00, my $rectColor = new SDL::Color ( ); -b => 0xff, -g => 0xff, -r => 0xff, $fontColor = new SDL::Color ( my ( $fontColor, $font ); sub updateScores { } $app->update ( @modified_areas ); # # Blink power pellets # } &updateScores(); # update the scores $mixer->play_channel ( 1, $sndMunch, 0 ); # Play the munch sound push ( @modified_areas, $rect ); $app->fill ( $rect, $color ); ); -b => 0x00, -g => 0x00, -r => 0x00, my $color = new SDL::Color ( # we ate a pellet! Yum! if ( $rect != 0 ) { my $rect = &pelletCollision ( $perlManRect ); # # Did we eat a pellet? # &updatePerlManState(); } &updatePerlManDirection ( $direction ); } else { $directionNext = 0 if ( &updatePerlManDirection( $directionNext ) ); if ( $directionNext ) { # # Check direction and update # push ( @modified_areas, $perlManRect ); else { $perlManImages[$direction][$perlManState]->blit ( 0, $app, $perlManRect ); } if ( $perlManState == 1 ) { $perlManImages[0]->blit ( 0, $app, $perlManRect ); } $perlManRect->y ( $perlManPosY ); $perlManRect->x ( $perlManPosX ); # # Draw new Pacman # push ( @modified_areas, $perlManRectOld ); $app->fill ( $perlManRectOld, $bgColor ); $perlManRectOld->y ( $perlManPosOldY ); $perlManRectOld->x ( $perlManPosOldX ); # # Erase Old Pacman # &updateGhostDirection ( ); # # Determine where to move ghosts # } push ( @modified_areas, $ghosts{$ghostColors[$_]}{'rect'} ); $ghostImages[$_][$ghostImg]->blit ( 0, $app, $ghosts{$ghostColors[$_]}{'rect'} ); $ghosts{$ghostColors[$_]}{'state'} = ( $ghosts{$ghostColors[$_]}{'state'} == 4 ) ? 0 : 4; my $ghostImg = ( $ghosts{$ghostColors[$_]}{'scared'} ) ? ( 9 + $ghosts{$ghostColors[$_]}{'state'} ) : ( $ghosts{$ghostColors[$_]}{'direction'} + $ghosts{$ghostColors[$_]}{'state'} ); $ghosts{$ghostColors[$_]}{'rect'}->y ( $ghosts{$ghostColors[$_]}{'pos'}{'Y'} ); $ghosts{$ghostColors[$_]}{'rect'}->x ( $ghosts{$ghostColors[$_]}{'pos'}{'X'} ); foreach ( 0 .. $#ghostColors ) { # # draw new ghosts # } push ( @modified_areas, $ghosts{$_}{'rectOld'} );adF^iL-~^? | \ <  | \ <  ~ ^ >  d G +   S R $xwg=-b=j 6F 413, 258, 413, 277, 413, 296, 413, 315, 413, 334, 413, 353, 413, 372, 413, 125, 413, 144, 413, 163, 413, 182, 413, 201, 413, 220, 413, 239, 413, 31, 413, 49, 413, 68, 413, 87, 413, 106, ## Long column on left 128, 391, 128, 412, 128, 431, 128, 450, 128, 469, 128, 487, 128, 507, 128, 258, 128, 277, 128, 296, 128, 315, 128, 334, 128, 353, 128, 372, 128, 125, 128, 144, 128, 163, 128, 182, 128, 201, 128, 220, 128, 239, 163, 451, 163, 470, 163, 489, 163, 508, 163, ## 2nd row from top 163, 318, 163, 337, 163, 356, 163, 432, 185, 163, 204, 163, 223, 163, 242, 163, 299, 51, 163, 71, 163, 91, 163, 109, 163, 451, 106, 470, 106, 489, 106, 508, 106, ## Top long row 318, 106, 337, 106, 356, 106, 376, 106, 394, 106, 432, 106, 185, 106, 204, 106, 223, 106, 242, 106, 261, 106, 280, 106, 299, 106, 51, 106, 71, 106, 91, 106, 109, 106, 128, 106, 147, 106, 166, 106, 30, 451, 30, 470, 30, 489, 30, 508, 30, ## 4th row from bottom 30, 318, 30, 337, 30, 356, 30, 375, 30, 394, 30, 432, 30, 185, 30, 204, 30, 223, 30, 242, 30, 299, 31, 30, 51, 30, 71, 30, 91, 30, 109, 30, 128, 30, 147, 30, 166, 242, 87, 242, 68, 242, 49, # top right of 2nd island 242, 30, 147, 30, 166, 30, 185, 30, 204, 30, 223, 30, # above top left 2nd island 128, 87, 128, 68, 128, 49, # top left between islands 128, 30,my @pelletCords = ( 31, 30, 31, 49, 31, 68, 31, 87, 31, 106, 31, 125, 31, 144, 31, 163, # top left going downmy @warpZoneData = ( 1,264, 2, 296, 536, 264, 537, 296 );## "transported" to the outside of the next gate## These are the actual warp zone gates. When PacMan colides with them, he will bemy @warpZones = ( 1, 264, 25, 296, 500, 264, 545, 296);#### he enters the warp zone gates#### organized the same as above; these are the rectangles that will 'hide' pacman as ); 0, 13, 9, 184 # 45 0, 377, 9, 597, # 44 0,184, 106, 262, # 43 142, 471, 164, 529, # 42 47, 529, 221, 548, # 41 9, 471, 48, 492, # 40 85, 434, 106, 492, # 39 47, 414, 106, 434, # 38 0, 585, 536, 598, # 37 315, 529, 489, 548, # 36 373, 471, 393, 529, # 35 429, 434, 451, 492, # 34 429, 414, 489, 434, # 33 142, 414, 221, 434, # 32 0, 299, 106, 377, # 31 142, 299, 163, 377, # 30 200, 241, 251, 253, # 29 200, 253, 210, 308, # 28 200, 308, 336, 320, # 27 326, 253, 336, 308, # 26 285, 241, 336, 253, # 25 258, 375, 278, 434, # 24 200, 356, 336, 377, # 23 314, 414, 393, 433, # 22 372, 298, 393, 377, # 21 257, 491, 278, 548, # 20 200, 470, 336, 492, # 19 257, 146, 278, 204, # 18 200, 127, 336, 146, # 17 314, 183, 373, 204, # 16 372, 126, 393, 262, # 15 487, 470, 528, 492, # 14 526, 375, 536, 596, # 13 $# 429, 299, 536, 377, # 12 526, 12, 536, 185, # 11 429, 185, 536, 262, # 10 429, 127, 489, 147, # 9 429, 51, 489, 90, # 8 314, 51, 393, 90, # 7 0, 3, 534, 13, # 6 258, 13, 278, 90, # 5 163, 183, 221, 204, # 4 142, 127, 163, 262, # 3 142, 50, 221, 90, # 2 47, 126, 106, 147, # 1my @gameBoardData = ( 46, 50, 106, 90, # 0## rectangle, top left, lower right (x1, y1, x2, y2 ); 1 rectangle per linead   f c _ \ [ B     # Rectangles $size--; my $size = scalar @intersections; sub drawIntersections { } } oooooooooooooooooooooooooooooooooooooooooo============================================================================================================================================================================================================================================================================================================================================================================================================ooooooooooooo333333333o3oooooooooooooooooooooooooo3qqqqqqqqqqqqqqqqqooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq