You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
693 lines
13 KiB
693 lines
13 KiB
/**
|
|
* Copyright Michel Filippi <mfilippi@sade.rhein-main.de>
|
|
* Robert Williams
|
|
* Andrew Chant <andrew.chant@utoronto.ca>
|
|
* André Luiz dos Santos <andre@netvision.com.br>
|
|
* Benjamin Meyer <ben+ksnake@meyerhome.net>
|
|
*
|
|
* This file is part of the ksnake package
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public License
|
|
* along with this library; see the file COPYING.LIB. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "rattler.h"
|
|
|
|
#include <tqtimer.h>
|
|
#include <tqlabel.h>
|
|
#include <tdeapplication.h>
|
|
#include <kstdgameaction.h>
|
|
#include <kdebug.h>
|
|
#include <tdelocale.h>
|
|
#include <tdemessagebox.h>
|
|
#include <tqbitarray.h>
|
|
#include <kstandarddirs.h>
|
|
|
|
#include "level.h"
|
|
#include "basket.h"
|
|
#include "settings.h"
|
|
|
|
TQBitArray gameState(5);
|
|
TQLabel *label = 0;
|
|
int speed[4] = { 130, 95, 55, 40 };
|
|
|
|
Rattler::Rattler( TQWidget *parent, const char *name )
|
|
: TQWidget( parent, name )
|
|
{
|
|
setFocusPolicy(TQWidget::StrongFocus);
|
|
|
|
numBalls = Settings::balls();
|
|
ballsAI = Settings::ballsAI();
|
|
numSnakes = Settings::computerSnakes();
|
|
snakesAI = Settings::snakesAI();
|
|
skill = Settings::skill();
|
|
room = Settings::startingRoom();
|
|
|
|
board = new Board(35*35);
|
|
level = new Level(board);
|
|
pix = new PixServer(board);
|
|
basket = new Basket(board, pix);
|
|
samy = new SamySnake(board, pix);
|
|
|
|
computerSnakes = new TQPtrList<CompuSnake>;
|
|
computerSnakes->setAutoDelete( true );
|
|
|
|
balls = new TQPtrList<Ball>;
|
|
balls->setAutoDelete( true );
|
|
|
|
connect( samy, TQ_SIGNAL(closeGate(int)), this, TQ_SLOT(closeGate(int)));
|
|
connect( samy, TQ_SIGNAL(score(bool, int)), this, TQ_SLOT(scoring(bool,int)));
|
|
connect( samy, TQ_SIGNAL(goingOut()), this, TQ_SLOT(speedUp()));
|
|
connect( basket, TQ_SIGNAL(openGate()), this, TQ_SLOT(openGate()));
|
|
|
|
gameState.fill(false);
|
|
gameState.setBit(Demo);
|
|
|
|
timerCount = 0;
|
|
TQTimer::singleShot( 2000, this, TQ_SLOT(demo()) ); // Why wait?
|
|
|
|
backgroundPixmaps =
|
|
TDEGlobal::dirs()->findAllResources("appdata", "backgrounds/*.png");
|
|
}
|
|
|
|
Rattler::~Rattler()
|
|
{
|
|
delete level;
|
|
delete balls;
|
|
delete computerSnakes;
|
|
delete basket;
|
|
delete samy;
|
|
delete pix;
|
|
delete board;
|
|
}
|
|
|
|
/**
|
|
* One of the settings changed, load our settings
|
|
*/
|
|
void Rattler::loadSettings(){
|
|
setBalls(Settings::balls());
|
|
setBallsAI(Settings::ballsAI());
|
|
setCompuSnakes(Settings::computerSnakes());
|
|
setSnakesAI(Settings::snakesAI());
|
|
setSkill(Settings::skill());
|
|
setRoom(Settings::startingRoom());
|
|
reloadRoomPixmap();
|
|
}
|
|
|
|
void Rattler::paintEvent( TQPaintEvent *e)
|
|
{
|
|
TQRect rect = e->rect();
|
|
|
|
if (rect.isEmpty())
|
|
return;
|
|
TQPixmap levelPix = pix->levelPix();
|
|
|
|
basket->repaint(true);
|
|
|
|
bitBlt(this, rect.x(), rect.y(),
|
|
&levelPix, rect.x(), rect.y(), rect.width(), rect.height());
|
|
}
|
|
|
|
void Rattler::timerEvent( TQTimerEvent * )
|
|
{
|
|
timerCount++;
|
|
|
|
if ( !leaving ) // advance progressBar unless Samy
|
|
emit advance(); // is going out
|
|
|
|
for (CompuSnake *c = computerSnakes->first(); c != 0; c = computerSnakes->next()){
|
|
if(c) {
|
|
c->nextMove();
|
|
c->repaint(false);
|
|
}
|
|
}
|
|
|
|
for (Ball *b = balls->first(); b != 0; b = balls->next()){
|
|
if (b) {
|
|
b->nextMove();
|
|
b->repaint();
|
|
}
|
|
}
|
|
|
|
|
|
samyState state = ok;
|
|
|
|
if(!gameState.testBit(Demo))
|
|
{
|
|
state = samy->nextMove(direction);
|
|
samy->repaint( false );
|
|
}
|
|
|
|
basket->repaint( false);
|
|
|
|
if (state == ko)
|
|
newTry();
|
|
else if (state == out)
|
|
levelUp();
|
|
|
|
TQPixmap levelPix = pix->levelPix();
|
|
bitBlt(this, 0, 0, &levelPix, 0, 0, rect().width(), rect().height());
|
|
}
|
|
|
|
void Rattler::keyPressEvent( TQKeyEvent *k )
|
|
{
|
|
if (gameState.testBit(Paused))
|
|
return;
|
|
|
|
KKey key(k);
|
|
if(actionCollection->action("Pl1Up")->shortcut().contains(key))
|
|
direction = N;
|
|
else if(actionCollection->action("Pl1Down")->shortcut().contains(key))
|
|
direction = S;
|
|
else if(actionCollection->action("Pl1Right")->shortcut().contains(key))
|
|
direction = E;
|
|
else if(actionCollection->action("Pl1Left")->shortcut().contains(key))
|
|
direction = W;
|
|
else if ((k->key() == Key_Return) ||
|
|
(k->key() == Key_Enter) || (k->key() == Key_Space)) {
|
|
if (!gameState.testBit(Demo)) {
|
|
k->ignore();
|
|
return;
|
|
}
|
|
restart();
|
|
}
|
|
else {
|
|
k->ignore();
|
|
return;
|
|
}
|
|
k->accept();
|
|
}
|
|
|
|
void Rattler::mousePressEvent( TQMouseEvent *e )
|
|
{
|
|
if (gameState.testBit(Paused))
|
|
return;
|
|
|
|
uint button = e->button();
|
|
if (button == TQt::RightButton)
|
|
{
|
|
switch (direction)
|
|
{
|
|
case N: direction=E;
|
|
break;
|
|
case E: direction=S;
|
|
break;
|
|
case S: direction=W;
|
|
break;
|
|
case W: direction=N;
|
|
break;
|
|
}
|
|
e->accept();
|
|
}
|
|
else if (button == TQt::LeftButton)
|
|
{
|
|
switch (direction)
|
|
{
|
|
case N: direction=W;
|
|
break;
|
|
case E: direction=N;
|
|
break;
|
|
case S: direction=E;
|
|
break;
|
|
case W: direction=S;
|
|
break;
|
|
}
|
|
e->accept();
|
|
}
|
|
else
|
|
e->ignore();
|
|
}
|
|
|
|
void Rattler::closeGate(int i)
|
|
{
|
|
board->set(i, brick);
|
|
pix->restore(i);
|
|
}
|
|
|
|
void Rattler::openGate()
|
|
{
|
|
board->set(NORTH_GATE, empty);
|
|
pix->erase(NORTH_GATE);
|
|
}
|
|
|
|
void Rattler::scoring(bool win, int i)
|
|
{
|
|
Fruits fruit = basket->eaten(i);
|
|
|
|
if (gameState.testBit(Demo))
|
|
win = true;
|
|
|
|
int p = 0;
|
|
|
|
switch (fruit) {
|
|
|
|
case Red:
|
|
if (win) {
|
|
if (!timerHasRunOut)
|
|
p = 1 + skill*2;
|
|
else p = 1;
|
|
}
|
|
else p = -2;
|
|
break;
|
|
|
|
case Golden:
|
|
if (win) {
|
|
if (!timerHasRunOut)
|
|
p = 2 + (skill*2) + (numSnakes*2) + (numBalls+2);
|
|
else p = 2;
|
|
}
|
|
else p = -5;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
score(p);
|
|
}
|
|
|
|
void Rattler::score(int p)
|
|
{
|
|
points += p;
|
|
points = (points < 0 ? 0 : points);
|
|
emit setPoints(points);
|
|
|
|
while (points > check*50) {
|
|
check++;
|
|
if (trys < 7 && !gameState.testBit(Demo))
|
|
emit setTrys(++trys);
|
|
}
|
|
}
|
|
|
|
void Rattler::killedComputerSnake()
|
|
{
|
|
if (!gameState.testBit(Demo))
|
|
score(20);
|
|
}
|
|
|
|
void Rattler::pause()
|
|
{
|
|
if (gameState.testBit(Init))
|
|
return;
|
|
|
|
if (gameState.testBit(Playing)) {
|
|
|
|
gameState.toggleBit(Playing);
|
|
gameState.setBit(Paused);
|
|
stop();
|
|
|
|
TDEAction* tempPauseAction = KStdGameAction::pause();
|
|
|
|
label = new TQLabel(this);
|
|
label->setFont( TQFont( "Times", 14, TQFont::Bold ) );
|
|
label->setText(i18n("Game Paused\n Press %1 to resume\n")
|
|
.arg(tempPauseAction->shortcutText()));
|
|
label->setAlignment( AlignCenter );
|
|
label->setFrameStyle( TQFrame::Panel | TQFrame::Raised );
|
|
label->setGeometry(182, 206, 198, 80);
|
|
label->show();
|
|
|
|
delete tempPauseAction;
|
|
//emit togglePaused();
|
|
}
|
|
else if (gameState.testBit(Paused)) {
|
|
gameState.toggleBit(Paused);
|
|
gameState.setBit(Playing);
|
|
start();
|
|
cleanLabel();
|
|
//emit togglePaused();
|
|
}
|
|
}
|
|
|
|
void Rattler::cleanLabel()
|
|
{
|
|
if (label) {
|
|
delete label;
|
|
label = 0;
|
|
}
|
|
}
|
|
|
|
void Rattler::restartDemo()
|
|
{
|
|
if (!gameState.testBit(Demo))
|
|
return;
|
|
|
|
int r = 50000+ (kapp->random() % 30000);
|
|
TQTimer::singleShot( r, this, TQ_SLOT(restartDemo()) );
|
|
|
|
stop();
|
|
level->create(Intro);
|
|
pix->initRoomPixmap();
|
|
init(false);
|
|
repaint();
|
|
start();
|
|
}
|
|
|
|
void Rattler::demo()
|
|
{
|
|
static bool first_time = true;
|
|
|
|
if(gameState.testBit(Init) || gameState.testBit(Playing))
|
|
return;
|
|
|
|
stop();
|
|
|
|
TQTimer::singleShot( 60000, this, TQ_SLOT(restartDemo()) );
|
|
gameState.fill(false);
|
|
gameState.setBit(Init);
|
|
gameState.setBit(Demo);
|
|
resetFlags();
|
|
|
|
if(!first_time) {
|
|
level->create(Intro);
|
|
pix->initRoomPixmap();
|
|
}
|
|
repaint(rect(), false);
|
|
init(false);
|
|
run();
|
|
first_time = false;
|
|
}
|
|
|
|
void Rattler::restart()
|
|
{
|
|
if (gameState.testBit(Init))
|
|
return;
|
|
stop();
|
|
|
|
if (gameState.testBit(Paused) || gameState.testBit(Playing)) {
|
|
|
|
switch( KMessageBox::questionYesNo( this,
|
|
i18n("A game is already started.\n"
|
|
"Start a new one?\n"), i18n("Snake Race"), i18n("Start New"), i18n("Keep Playing"))) {
|
|
case KMessageBox::No:
|
|
if (!gameState.testBit(Paused))
|
|
start();
|
|
return;
|
|
break;
|
|
case KMessageBox::Yes:
|
|
;
|
|
}
|
|
}
|
|
|
|
gameState.fill(false);
|
|
gameState.setBit(Init);
|
|
gameState.setBit(Playing);
|
|
|
|
resetFlags();
|
|
|
|
level->setLevel(room);
|
|
level->create(Banner);
|
|
pix->initRoomPixmap();
|
|
|
|
cleanLabel();
|
|
|
|
repaint();
|
|
TQTimer::singleShot( 2000, this, TQ_SLOT(showRoom()) );
|
|
}
|
|
|
|
void Rattler::newTry()
|
|
{
|
|
stop();
|
|
|
|
if(trys==0) {
|
|
gameState.fill(false);
|
|
gameState.setBit(Over);
|
|
level->create(GameOver);
|
|
pix->initRoomPixmap();
|
|
repaint();
|
|
TQTimer::singleShot( 5000, this, TQ_SLOT(demo()) );
|
|
emit setScore(points);
|
|
return;
|
|
}
|
|
--trys;
|
|
gameState.fill(false);
|
|
gameState.setBit(Init);
|
|
gameState.setBit(Playing);
|
|
|
|
level->create(Room);
|
|
pix->initRoomPixmap();
|
|
init(true);
|
|
repaint();
|
|
TQTimer::singleShot( 1000, this, TQ_SLOT(run()) );
|
|
}
|
|
|
|
void Rattler::levelUp()
|
|
{
|
|
stop();
|
|
|
|
gameState.fill(false);
|
|
gameState.setBit(Init);
|
|
gameState.setBit(Playing);
|
|
|
|
score (2*(level->getLevel())+(2*numSnakes)+(2*numBalls)+(2*skill));
|
|
|
|
level->nextLevel();
|
|
level->create(Banner);
|
|
pix->initRoomPixmap();
|
|
repaint();
|
|
|
|
TQTimer::singleShot( 2000, this, TQ_SLOT(showRoom()) );
|
|
}
|
|
|
|
/* this slot is called by the progressBar when value() == 0
|
|
or by a compuSnake wich manages to exit */
|
|
void Rattler::restartTimer()
|
|
{
|
|
timerHasRunOut = true;
|
|
timerCount = 0;
|
|
emit rewind();
|
|
|
|
if ( board->isEmpty(NORTH_GATE) )
|
|
closeGate(NORTH_GATE);
|
|
basket->newApples();
|
|
}
|
|
|
|
void Rattler::speedUp()
|
|
{
|
|
leaving = true;
|
|
stop();
|
|
start( 30 );
|
|
}
|
|
|
|
void Rattler::resetFlags()
|
|
{
|
|
trys = 2;
|
|
check = 1;
|
|
points = 0;
|
|
}
|
|
|
|
void Rattler::showRoom()
|
|
{
|
|
level->create(Room);
|
|
pix->initRoomPixmap();
|
|
init(true);
|
|
repaint();
|
|
TQTimer::singleShot( 1000, this, TQ_SLOT(run()) );
|
|
}
|
|
|
|
void Rattler::init(bool play)
|
|
{
|
|
leaving = false;
|
|
timerHasRunOut = false;
|
|
timerCount = 0;
|
|
emit rewind();
|
|
|
|
emit setTrys(trys);
|
|
emit setPoints(points);
|
|
|
|
basket->clear();
|
|
basket->newApples();
|
|
restartBalls(play);
|
|
restartComputerSnakes(play);
|
|
if(play)
|
|
samy->init();
|
|
}
|
|
|
|
void Rattler::run()
|
|
{
|
|
direction = N;
|
|
gameState.toggleBit(Init);
|
|
start();
|
|
}
|
|
|
|
void Rattler::start()
|
|
{
|
|
gameTimer = startTimer( speed [skill] );
|
|
}
|
|
|
|
void Rattler::start(int t)
|
|
{
|
|
gameTimer = startTimer(t);
|
|
}
|
|
|
|
void Rattler::stop()
|
|
{
|
|
this->killTimers();
|
|
}
|
|
|
|
void Rattler::restartComputerSnakes(bool play)
|
|
{
|
|
if( !computerSnakes->isEmpty())
|
|
computerSnakes->clear();
|
|
|
|
int i = (play == false && numSnakes == 0 ? 1 : numSnakes);
|
|
|
|
for (int x = 0; x < i; x++) {
|
|
CompuSnake *as;
|
|
switch(snakesAI) {
|
|
default: // random.
|
|
as = new CompuSnake(board, pix);
|
|
break;
|
|
case 1: // eater.
|
|
as = new EaterCompuSnake(board, pix);
|
|
break;
|
|
case 2: // killer.
|
|
as = new KillerCompuSnake(board, pix);
|
|
break;
|
|
}
|
|
connect( as, TQ_SIGNAL(closeGate(int)), this, TQ_SLOT(closeGate(int)));
|
|
connect( as, TQ_SIGNAL(restartTimer()), this, TQ_SLOT(restartTimer()));
|
|
connect( as, TQ_SIGNAL(score(bool, int)), this, TQ_SLOT(scoring(bool,int)));
|
|
connect( as, TQ_SIGNAL(killed()), this, TQ_SLOT(killedComputerSnake()));
|
|
computerSnakes->append(as);
|
|
}
|
|
}
|
|
|
|
void Rattler::restartBalls(bool play)
|
|
{
|
|
if( !balls->isEmpty())
|
|
balls->clear();
|
|
|
|
int i = (play == false && numBalls == 0 ? 1 : numBalls);
|
|
|
|
for (int x = 0; x < i; x++) {
|
|
Ball *b;
|
|
switch(ballsAI) {
|
|
default: // default.
|
|
b = new Ball(board, pix);
|
|
break;
|
|
case 1: // dumb.
|
|
b = new DumbKillerBall(board, pix);
|
|
break;
|
|
case 2: // smart.
|
|
b = new KillerBall(board, pix);
|
|
break;
|
|
}
|
|
balls->append(b);
|
|
}
|
|
}
|
|
|
|
void Rattler::setBalls(int newNumBalls)
|
|
{
|
|
numBalls = balls->count();
|
|
|
|
if (!(gameState.testBit(Playing) || gameState.testBit(Demo)) || numBalls == newNumBalls)
|
|
return;
|
|
|
|
while ( newNumBalls > numBalls) {
|
|
Ball *b = new Ball(board, pix);
|
|
balls->append(b);
|
|
numBalls++;
|
|
}
|
|
while (newNumBalls < numBalls) {
|
|
Ball *b = balls->getLast();
|
|
b->zero();
|
|
balls->removeLast();
|
|
numBalls--;
|
|
}
|
|
}
|
|
|
|
void Rattler::setBallsAI(int i)
|
|
{
|
|
ballsAI = i;
|
|
}
|
|
|
|
void Rattler::resizeEvent( TQResizeEvent * )
|
|
{
|
|
pix->initPixmaps();
|
|
pix->initBrickPixmap();
|
|
pix->initbackPixmaps();
|
|
pix->initRoomPixmap();
|
|
}
|
|
|
|
void Rattler::setCompuSnakes(int i)
|
|
{
|
|
CompuSnake *cs;
|
|
numSnakes = i;
|
|
int count = computerSnakes->count();
|
|
|
|
if (gameState.testBit(Playing) || gameState.testBit(Demo)) {
|
|
if ( i > count) {
|
|
while ( i > count) {
|
|
CompuSnake *as;
|
|
switch(snakesAI) {
|
|
default: // random.
|
|
as = new CompuSnake(board, pix);
|
|
break;
|
|
case 1: // eater.
|
|
as = new EaterCompuSnake(board, pix);
|
|
break;
|
|
case 2: // killer.
|
|
as = new KillerCompuSnake(board, pix);
|
|
break;
|
|
}
|
|
connect( as, TQ_SIGNAL(closeGate(int)), this, TQ_SLOT(closeGate(int)));
|
|
connect( as, TQ_SIGNAL(restartTimer()), this, TQ_SLOT(restartTimer()));
|
|
connect( as, TQ_SIGNAL(score(bool, int)), this, TQ_SLOT(scoring(bool,int)));
|
|
connect( as, TQ_SIGNAL(killed()), this, TQ_SLOT(killedComputerSnake()));
|
|
computerSnakes->append(as);
|
|
i--;
|
|
}
|
|
}
|
|
else if (i < count) {
|
|
while (i < count) {
|
|
cs = computerSnakes->getLast();
|
|
cs->zero();
|
|
computerSnakes->removeLast();
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Rattler::setSnakesAI(int i)
|
|
{
|
|
snakesAI = i;
|
|
}
|
|
|
|
void Rattler::setSkill(int i)
|
|
{
|
|
skill = i;
|
|
if (gameState.testBit(Playing) || gameState.testBit(Demo)) {
|
|
stop();
|
|
start();
|
|
}
|
|
}
|
|
|
|
void Rattler::setRoom(int i)
|
|
{
|
|
room = i;
|
|
}
|
|
|
|
void Rattler::reloadRoomPixmap()
|
|
{
|
|
pix->initbackPixmaps();
|
|
pix->initRoomPixmap();
|
|
demo();
|
|
}
|
|
|
|
#include "rattler.moc"
|
|
|