#!perl -w # Cannon v1.1 # by Kevin Reid # # A game. # # I recommend running this with a resolution of 800x600. # Click to fire a shot. # The mouse position controls the velocity and angle of the shot. # # To stop, press a key. # # Version History # 1.1 # * Added friendly targets. # * Now prevents switching out of MacPerl. # 1.0 # * Initial release use Mac::QuickDraw; use Mac::Windows; use Mac::Events; use Mac::Menus; use Mac::LowMem; ### \/ \/ Change these two parameters to affect how fast the game runs. $system_time = 1; # Other applications will be given time # every $system_time frames. $vel_mult = 1; # Increasing this value will increase the speed of all # objects; however, making it too high will result # in objects passing through each other. $color_vel_max = 5000; # Maximum color-change speed for the shots. $linewid = 2; # Width of the shot lines. $grav = 0.08; # Gravity. $loss = 0.89; # Velocity lost with each bounce. $num_targets = 10; # Number of targets created. $maxshots = 7; # Maximum number of shots you can have on the # screen at once. $cansiz = 45; # Size of your cannon. $canpos = 120; $shotlife = 500; # Shots last this long before disappearing. $sid = 0; $OScore = $Score = 0; $ERect = OffsetRect new Rect(0, 0, StringWidth('-100'), 16), StringWidth('Score: '), 0; $cwhite = new RGBColor((65535) x 3); $cblack = new RGBColor((0) x 3); @cteam = ( new RGBColor(65535, 0, 0), new RGBColor((65535) x 3), ); sub ltwh ($$$$) { my ($left, $top, $width, $height) = @_; new Rect ($left, $top, $left+$width, $top+$height); } $origrgn = CopyRgn(GetGrayRgn()); RectRgn(GetGrayRgn, GetMainDevice()->gdRect); BEGIN { $oheight = LMGetMBarHeight; LMSetMBarHeight(0); DisableItem GetMenu 129; DisableItem GetMenu 130; } END { LMSetMBarHeight($oheight); EnableItem GetMenu 129; EnableItem GetMenu 130; if ($win) { SetPort $win->window; EraseRect($bbx); } if ($origrgn) { CopyRgn($origrgn, GetGrayRgn); DisposeRgn $origrgn; } $win->dispose if $win; } $bbx = InsetRect(GetWMgrPort->portRect, 0, 0); $bw = $bbx->right - $bbx->left; $bh = $bbx->bottom - $bbx->top; $canrect = new Rect(-$cansiz + $canpos, $bh - $cansiz, $cansiz + $canpos, $bh + $cansiz); $win = new MacColorWindow ( $bbx, 'Bouncer', 1, dBoxProc, 1, ); $win->sethook('drawgrowicon', sub {}); $win->sethook(key => sub {EndGame()}); $win->sethook(cursor => sub {SetCursor(crossCursor)}); SetPort $win->window; PenSize($linewid, $linewid); RGBBackColor(new RGBColor(0,0,0)); $spacing = (($bh - 150) / $num_targets); for (1..$num_targets) { $targets{$_} = { rect => ltwh(rand($bw - 100), $_ * $spacing, 10 + rand 90, 10 + rand ($spacing - 10)), xv => (rand 3 * $vel_mult) + 1, yv => 0, team => int rand 1.5, }; $targcount[$targets{$_}{team}]++; $otargcount[$targets{$_}{team}]++; } sub getcolor { my @colors = (); for (qw(R G B)) { push @colors, { val => rand 65535, vel => rand $color_vel_max, }; } return \@colors; } sub fire { my ($xv, $yv) = @_; $shots{$sid++} = { begin => { xp => $canpos, yp => $bh, xv => $xv, yv => -$yv, 'time' => $shotlife, exist => 0, col => getcolor(), bounces => 0, }, end => { xp => $canpos, yp => $bh, xv => $xv, yv => -$yv, 'time' => $shotlife, start => 30/($xv+$yv), col => [{val => 0, vel => 0}, {val => 0, vel => 0}, {val => 0, vel => 0}], bounces => 0, }, }; # $fired++; $Score -= 20 / $num_targets; } sub Won {EndGame('YOU WIN')} sub Lost {EndGame('YOU LOSE')} sub EndGame { my ($Msg) = @_; $win->sethook(key => sub {}); HideCursor(); TextMode(srcXor); TextFont(0); TextSize(100); ShowText($Msg) if $Msg; ShowText('Score:'); # $score = eval {($destroyed / $num_targets * 100) - (($fired - $destroyed) / $fired) * 20} || 0; ShowText(sprintf '%.0f', $Score); ShowCursor(); exit; } sub ShowText { my ($Msg) = @_; my ($ascent, $descend) = GetFontInfo(); my $height = $ascent + $descend; my $TextY = $bh/2 + $height/2 - ($height - $ascent) - .5; my $TextX = $bw/2 - StringWidth($Msg)/2; MoveTo($TextX, $TextY); DrawString($Msg); ScreenStripes(); sleep 2; ScreenStripes(); MoveTo($TextX, $TextY); DrawString($Msg); EraseRect(new Rect(0, 0, $bw, $bh)); } sub ScreenStripes { my $max = $bh/2 - 130; my $inc = 18; for (my $i = 0; $i < $max; $i += $inc) { InvertRoundRect(new Rect($i, $i, $bw-$i, $bh-$i), 100, 100); WaitNextEvent; } } sub ExplodePoint { my ($x, $y, $speed, $size) = @_; for (1..(20 / $vel_mult)) { for (my $i = 0; $i < $size; $i+= 2) { InvertOval(new Rect($x-$i, $y-$i, $x+$i, $y+$i)); for (my $i = 0; $i < (1000/$speed); $i++) {} } } } sub Run_Shots { SHOT: foreach $k (keys %shots) { my $head = $shots{$k}{begin}; if ($head) { $head->{exist}++; my $hpt = new Point($head->{xp}, $head->{yp}); foreach (keys %targets) { my $t = $targets{$_}; if (PtInRect($hpt, $t->{rect})) { my $bigrect = InsetRect($t->{rect}, -20, -20); for (1..50) { InvertRoundRect($bigrect, 10, 10); } ExplodePoint($head->{xp}, $head->{yp}, 50, 30); EraseRoundRect($bigrect, 10, 10); delete $targets{$_}; # $destroyed++; $Score += (120 / $otargcount[$t->{team}]) * ($t->{team} ? -.5 : 1); $targcount[$t->{team}]--; if ($targcount[0] == 0) {Won()} delete $shots{$k}; last SHOT; } } if ($head->{exist} > $cansiz+10 and PtInRect($hpt, $canrect)) { ExplodePoint($head->{xp}, $head->{yp}, 1, 30); $Score -= 20; Lost(); } } PT: foreach $pk (keys %{$shots{$k}}) { $s = $shots{$k}{$pk}; if ($s->{start} and $s->{start} > 0) { $s->{start}--; next PT; } $s->{'time'}--; if ($s->{'time'} <= 0) { delete $shots{$k}{$pk}; if (keys %{$shots{$k}} < 1) { delete $shots{$k}; } next SHOT; } RGBForeColor(new RGBColor(map {$_->{val}} @{$s->{col}})); MoveTo($s->{xp}, $s->{yp}); $s->{xp} += $s->{xv}; $s->{yp} += $s->{yv}; if ($s->{xp} > $bw) { $s->{xv} *= -$loss; $s->{xp} = $bw; } if ($s->{xp} < 0) { $s->{xv} *= -$loss; $s->{xp} = 0; } if ($s->{yp} > $bh) { $s->{yv} *= -$loss; $s->{yp} = $bh; } if ($s->{yp} < 0) { $s->{yv} *= -$loss; $s->{yp} = 0; } $s->{yv} += $grav * $vel_mult**2; LineTo($s->{xp}, $s->{yp}); foreach $c (@{$s->{col}}) { $c->{val} += $c->{vel}; if ($c->{val} > 65535 or $c->{val} < 0) { $c->{vel} *= -1; redo; } } } } } sub Run_Targets { foreach my $k (keys %targets) { my $t = $targets{$k}; #$twid = $t->{rect}->right - $t->{rect}->left; $trect = $t->{rect}; RGBForeColor($cteam[$t->{team}]); PaintRoundRect($trect, 10, 10); #EraseRoundRect(OffsetRect($trect, $t->{xv} > 0 ? -$twid : $twid, 0), 10, 10); EraseRoundRect(Rect->new($t->{xv} > 0 ? ( $trect->left - (9 + $t->{xv}), $trect->top, $trect->left, $trect->bottom, ) : ( $trect->right, $trect->top, $trect->right + (9 + -$t->{xv}), $trect->bottom, )), 10, 10); $t->{rect} = OffsetRect($trect, $t->{xv}, $t->{yv}); if ($t->{rect}->right > $bw or $t->{rect}->left < 0) { $t->{xv} *= -1; } } } for (1..10) {WaitNextEvent} $tick = 0; $done = 0; while (!$done) { ++$tick; SetPort $win->window; if ($Mouse xor (my $b = Button())) { $Mouse = $b; if ($b and not keys %shots >= $maxshots) { my $pt = GetMouse(); my ($mx, $my) = ($pt->h, $pt->v); fire(($mx-$canpos) / $bw * 10 * $vel_mult, ($bh-$my) / $bh * 16 * $vel_mult); } } Run_Shots(); Run_Targets(); { my $pt = GetMouse(); my ($mx, $my) = ($pt->h, $pt->v); $OldBarX = $BarX || 0; $OldBarY = $BarY || 0; $BarX = ($mx-$canpos) / 8; $BarY = $bh - ($bh-$my) / 5; if ($OldBarX != $BarX or $OldBarY != $BarY) { RGBForeColor($cblack); MoveTo($canpos + 0, $bh); LineTo($canpos + $OldBarX, $OldBarY); RGBForeColor($cteam[1]); MoveTo($canpos + 0, $bh); LineTo($canpos + $BarX, $BarY); } } RGBForeColor($cteam[1]); PaintOval($canrect); if ($OScore != $Score) { $OScore = $Score; EraseRect($ERect); MoveTo(1, 12); RGBForeColor($cwhite); DrawString sprintf 'Score: %.0f', $Score; } FlushEvents(mouseDown + mouseUp, keyDown); WaitNextEvent unless $tick % $system_time; } __END__