[Date Prev][Date Next][Thread Prev][Thread Next] [Search] [Date Index] [Thread Index]

[MacPerl] Cannon



#!perl -w

# Cannon v1.0
# by Kevin Reid
#
# 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.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 = 20;
  # Other applications will be given time
  # every $system_time frames.
$vel_mult = 2;
  # 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.04;
  # 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 = 70;
  # Size of your cannon.
$shotlife = 500;
  # Shots last this long before disappearing.

$sid = 0;
$OScore = $Score = 0;
$cwhite = 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, $bh - $cansiz, $cansiz, $bh + $cansiz);

$win = new MacColorWindow (
  $bbx,
  'Bouncer',
  1,
  plainDBox,
  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,
  };
}

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 => 0,
      yp => $bh,
      xv => $xv,
      yv => -$yv,
      'time' => $shotlife,
      exist => 0,
      col => getcolor(),
      bounces => 0,
    },
    end => {
      xp => 0,
      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 / $num_targets;
          if (not keys %targets) {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 {
  RGBForeColor($cwhite);
  foreach my $k (keys %targets) {
    my $t = $targets{$k};

    #$twid = $t->{rect}->right - $t->{rect}->left;
    $trect = $t->{rect};
    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 / $bw * 5 * $vel_mult, ($bh-$my) / $bh * 8 * $vel_mult);
    }
  }

  Run_Shots();
  Run_Targets();
  RGBForeColor($cwhite);
  PaintOval($canrect);
  if ($OScore != $Score) {
    $OScore = $Score;
    EraseRect(new Rect(0, 0, 50, 15));
    MoveTo(1, 12);
    DrawString sprintf '%.0f', $Score;
  }
 
  FlushEvents(mouseDown + mouseUp, keyDown);
  WaitNextEvent unless $tick % $system_time;
}

__END__

===== Want to unsubscribe from this list?
===== Send mail with body "unsubscribe" to macperl-request@macperl.org