use
Qt::signals
'hit()'
,
'missed()'
,
'angleChanged(int)'
,
'forceChanged(int)'
;
use
Qt::slots
'setAngle(int)'
,
'setForce(int)'
,
'shoot()'
,
'newTarget()'
;
use
Qt::slots
'setGameOver()'
,
'restartGame()'
;
@ISA
=
qw(Qt::Widget)
;
sub
new {
my
$self
=
shift
->SUPER::new(
@_
);
@$self
{
'ang'
,
'f'
,
'shooting'
,
'timerCount'
,
'shoot_ang'
,
'shoot_f'
,
'gameEnded'
,
'barrelPressed'
,
'target'
} =
(45, 0, 0, 0, 0, 0, 0, 0, Qt::Point->new(0, 0));
$self
->newTarget();
return
$self
;
}
sub
angle {
return
shift
->{
'ang'
} }
sub
force {
return
shift
->{
'f'
} }
sub
gameOver {
return
shift
->{
'gameEnded'
} }
sub
isShooting {
return
shift
->{
'shooting'
} }
sub
setAngle {
my
$self
=
shift
;
my
$degrees
=
shift
;
$degrees
= 5
if
$degrees
< 5;
$degrees
= 70
if
$degrees
> 70;
return
if
$self
->{
'ang'
} ==
$degrees
;
$self
->{
'ang'
} =
$degrees
;
$self
->repaint(
$self
->cannonRect(), 0);
emit
$self
->angleChanged(
$self
->{
'ang'
});
}
sub
setForce {
my
$self
=
shift
;
my
$newton
=
shift
;
$newton
= 0
if
$newton
< 0;
return
if
$self
->{
'f'
} ==
$newton
;
$self
->{
'f'
} =
$newton
;
emit
$self
->forceChanged(
$self
->{
'f'
});
}
sub
shoot {
my
$self
=
shift
;
return
if
$self
->{
'shooting'
};
@$self
{
'timerCount'
,
'shoot_ang'
,
'shoot_f'
,
'shooting'
} =
(0,
$self
->{
'ang'
},
$self
->{
'f'
}, 1);
$self
->startTimer(50);
}
sub
newTarget {
my
$self
=
shift
;
$self
->erase(
$self
->targetRect());
$self
->{
'target'
} = Qt::Point->new(200 +
rand
(65536) % 190,
10 +
rand
(65536) % 255);
$self
->repaint(
$self
->targetRect());
}
sub
setGameOver {
my
$self
=
shift
;
return
if
$self
->{
'gameEnded'
};
$self
->stopShooting()
if
$self
->{
'shooting'
};
$self
->{
'gameEnded'
} = 1;
$self
->repaint();
}
sub
restartGame {
my
$self
=
shift
;
$self
->stopShooting()
if
$self
->{
'shooting'
};
$self
->{
'gameEnded'
} = 0;
$self
->repaint();
}
sub
timerEvent {
my
$self
=
shift
;
$self
->erase(
$self
->shotRect());
$self
->{
'timerCount'
}++;
my
$shotR
=
$self
->shotRect();
if
(
$shotR
->intersects(
$self
->targetRect())) {
$self
->stopShooting();
emit
$self
->hit();
return
;
}
if
((
$shotR
->x() >
$self
->width() ||
$shotR
->y() >
$self
->height()) ||
$shotR
->intersects(
$self
->barrierRect())) {
$self
->stopShooting();
emit
$self
->missed();
return
;
}
$self
->repaint(
$shotR
, 0);
}
sub
paintEvent {
my
$self
=
shift
;
my
$updateR
=
shift
->rect();
my
$p
= Qt::Painter->new;
$p
->begin(
$self
);
$self
->paintCannon(
$p
)
if
$updateR
->intersects(
$self
->cannonRect());
$self
->paintBarrier(
$p
)
if
$updateR
->intersects(
$self
->barrierRect());
if
(
$self
->{
'gameEnded'
}) {
$p
->setPen(Qt::black);
$p
->setFont(Qt::Font->new(
'Courier'
, 48, Qt::Font::Bold));
$p
->drawText(
$self
->rect(), Qt::AlignCenter,
'Game Over'
);
}
else
{
$self
->paintShot(
$p
)
if
$self
->isShooting() &&
$updateR
->intersects(
$self
->shotRect());
$self
->paintTarget(
$p
)
if
$updateR
->intersects(
$self
->targetRect());
}
$p
->end();
}
sub
mousePressEvent {
my
$self
=
shift
;
my
$e
=
shift
;
return
if
$e
->button() != Qt::LeftButton;
$self
->{
'barrelPressed'
} = 1
if
$self
->barrelHit(
$e
->
pos
());
}
sub
qRound {
my
$d
=
shift
;
return
$d
> 0.0 ?
int
(
$d
+0.5) :
int
(
$d
-0.5);
}
sub
mouseMoveEvent {
my
$self
=
shift
;
my
$e
=
shift
;
return
unless
$self
->{
'barrelPressed'
};
my
$pnt
=
$e
->
pos
();
$pnt
->setX(1)
if
$pnt
->x() <= 0;
$pnt
->setY(
$self
->height() - 1)
if
$pnt
->y() >=
$self
->height();
my
$rad
= atan((
$self
->rect()->bottom() -
$pnt
->y()) /
$pnt
->x());
$self
->setAngle(qRound(
$rad
*180/3.14159265));
}
sub
mouseReleaseEvent {
my
$self
=
shift
;
my
$e
=
shift
;
$self
->{
'barrelPressed'
} = 0
if
$e
->button() == Qt::LeftButton;
}
sub
stopShooting {
my
$self
=
shift
;
$self
->{
'shooting'
} = 0;
$self
->killTimers();
}
sub
paintShot {
my
$self
=
shift
;
my
$p
=
shift
;
$p
->setBrush(Qt::black);
$p
->setPen(Qt::NoPen);
$p
->drawRect(
$self
->shotRect());
}
sub
paintTarget {
my
$self
=
shift
;
my
$p
=
shift
;
$p
->setBrush(Qt::red);
$p
->setPen(Qt::black);
$p
->drawRect(
$self
->targetRect());
}
sub
paintBarrier {
my
$self
=
shift
;
my
$p
=
shift
;
$p
->setBrush(Qt::yellow);
$p
->setPen(Qt::black);
$p
->drawRect(
$self
->barrierRect());
}
$barrel_rect
= Qt::Rect->new(33, -4, 15, 8);
sub
paintCannon {
my
$self
=
shift
;
my
$p
=
shift
;
my
$cr
=
$self
->cannonRect();
my
$pix
= Qt::Pixmap->new(
$cr
->size());
my
$tmp
= Qt::Painter->new;
$pix
->fill(
$self
,
$cr
->topLeft());
$tmp
->begin(
$pix
);
$tmp
->setBrush(Qt::blue);
$tmp
->setPen(Qt::NoPen);
$tmp
->translate(0,
$pix
->height() - 1);
$tmp
->drawPie(Qt::Rect->new(-35, -35, 70, 70), 0, 90*16);
$tmp
->rotate(-
$self
->{
'ang'
});
$tmp
->drawRect(
$barrel_rect
);
$tmp
->end();
$p
->drawPixmap(
$cr
->topLeft(),
$pix
);
}
sub
cannonRect {
my
$self
=
shift
;
my
$r
= Qt::Rect->new(0, 0, 50, 50);
$r
->moveBottomLeft(
$self
->rect()->bottomLeft());
return
$r
;
}
sub
shotRect {
my
$self
=
shift
;
my
$gravity
= 4;
my
$time
=
$self
->{
'timerCount'
}/4.0;
my
$velocity
=
$self
->{
'shoot_f'
};
my
$radians
=
$self
->{
'shoot_ang'
}*3.14159265/180;
my
$velx
=
$velocity
*cos
(
$radians
);
my
$vely
=
$velocity
*sin
(
$radians
);
my
$x0
= (
$barrel_rect
->right() + 5)
*cos
(
$radians
);
my
$y0
= (
$barrel_rect
->right() + 5)
*sin
(
$radians
);
my
$x
=
$x0
+
$velx
*$time
;
my
$y
=
$y0
+
$vely
*$time
- 0.5
*$gravity
*$time
*$time
;
my
$r
= Qt::Rect->new(0, 0, 6, 6);
$r
->moveCenter(Qt::Point->new(qRound(
$x
),
$self
->height() - 1 - qRound(
$y
)));
return
$r
;
}
sub
targetRect {
my
$self
=
shift
;
my
$target
=
$self
->{
'target'
};
my
$r
= Qt::Rect->new(0, 0, 20, 10);
$r
->moveCenter(Qt::Point->new(
$target
->x(),
$self
->height() - 1 -
$target
->y()));
return
$r
;
}
sub
barrierRect {
my
$self
=
shift
;
return
Qt::Rect->new(145,
$self
->height() - 100, 15, 100);
}
sub
barrelHit {
my
$self
=
shift
;
my
$p
=
shift
;
my
$mtx
= Qt::WMatrix->new;
$mtx
->translate(0,
$self
->height() - 1);
$mtx
->rotate(-
$self
->{
'ang'
});
$mtx
=
$mtx
->invert();
return
$barrel_rect
->contains(
$mtx
->
map
(
$p
));
}
sub
sizeHint {
return
Qt::Size->new(400, 300);
}
sub
sizePolicy {
return
Qt::SizePolicy->new(Qt::SizePolicy::Expanding,
Qt::SizePolicy::Expanding);
}