/***************************************************************************
board.cpp - description
-------------------
begin : Thu Aug 17 2000
copyright : (C) 2000 by Waldemar Baraldi
email : baraldi@lacasilla.com.ar
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include "board.h"
#include "player.h"
#include "mosaic.h"
#include "wallpaper.h"
#define MAX_SPEED 80
Board::Board():AnimatedCanvas(){
int i,j;
for (i=0;i<BoardRows;i++)
for (j=0;j<BoardColumns;j++){
board[i][j]=NULL;
changes[i][j]=false;
}
paint_all=true;
lps=new List();
flow_row=flow_column=-1;
entry=exit=NULL;
actual_output=Void;
actual_input=Void;
state=Playing;
tick_amount=1;
tank=NULL;
background=NULL;
required=0;
still_required=0;
required_changed=false;
entry_row=-1;
entry_column=-1;
}
Board::~Board(){
int i,j;
for (i=0;i<BoardRows;i++)
for (j=0;j<BoardColumns;j++)
if (board[i][j]) delete board[i][j];
delete lps;
if (background) delete background;
if (tank) delete tank;
}
void Board::setStartDelay(int delay){
if (tank) delete tank;
tank=new Tank(delay);
tank->setPos(TankX, TankY);
}
void Board::setEntry(Entry * entry, int row, int column){
flow_row=row;
flow_column=column;
actual_input=Void;
actual_output=entry->getOutput(Void);
setPipe(entry, row, column);
entry_row=row;
entry_column=column;
entry->setLast(true);
last=entry;
last_row=row;
last_column=column;
}
void Board::setExit(Exit * exit, int row, int column){
setPipe(exit, row, column);
}
bool Board::isRemovable(int row, int column){
if (board[row][column])
return board[row][column]->isRemovable();
return true;
}
void Board::setPipe(Pipe * pipe, int row, int column){
required_changed=true;
changes[row][column]=true;
Pipe * aux=board[row][column];
if (aux==last) {
last=pipe;
pipe->setLast(true);
}
if (aux){
if (aux->isRemovable()){
delete aux;
board[row][column]=pipe;
pipe->setPos(column*pipe->width()+x, row*pipe->height()+y);
}
}else{
board[row][column]=pipe;
pipe->setPos(column*pipe->width()+x, row*pipe->height()+y);
}
}
Pipe * Board::getPipe(int row, int column){
return board[row][column];
}
BoardState Board::getState(){
return state;
}
unsigned int Board::getRequired(){
return required;
}
unsigned int Board::getStillRequired(){
if (!required_changed) return still_required;
else return (still_required=countStillRequired());
}
unsigned int Board::countStillRequired(){
int row=entry_row;
int column=entry_column;
bool end=false;
unsigned int used=0;
required_changed=false;
Pipe * aux_last=board[entry_row][entry_column];
CardinalPoint con=board[row][column]->getOutput(Void);//Entry output
changes[last_row][last_column]=true;
while (!end){
used++;
end=true;
switch (con){
case East:{
column++;
if (column<BoardColumns)
if (board[row][column] != NULL)
if (board[row][column]->hasConnection(West))
if (!board[row][column]->isRestrictedAsOutput(West)){
con=board[row][column]->getOutput(West);
end=false;
}
break;
}
case South:{
row++;
if (row<BoardRows)
if (board[row][column] != NULL)
if (board[row][column]->hasConnection(North))
if (!board[row][column]->isRestrictedAsOutput(North)){
con=board[row][column]->getOutput(North);
end=false;
}
break;
}
case West:{
column--;
if (column>=0)
if (board[row][column] != NULL)
if (board[row][column]->hasConnection(East))
if (!board[row][column]->isRestrictedAsOutput(East)){
con=board[row][column]->getOutput(East);
end=false;
}
break;
}
case North:{
row--;
if (row>=0)
if (board[row][column] != NULL)
if (board[row][column]->hasConnection(South))
if (!board[row][column]->isRestrictedAsOutput(South)){
con=board[row][column]->getOutput(South);
end=false;
}
break;
}
default:{
used--; //La salida no se cuenta como uno usado
break;
}
}
if (!end){
aux_last=board[row][column];
last_row=row;
last_column=column;
}
}//while
used--; //La entrada no se cuenta como usado
if (last != aux_last){
last->setLast(false);
last=aux_last;
last->setLast(true);
}
if (used>required) return 0;
return required-used;
}
void Board::addPointer(Pointer * p){
lps->insert(lps->getFirst(), (Pointer *)p);
}
void Board::removePointer(Pointer * p){
lps->remove(lps->indexOf((Pointer*)p));
}
void Board::movePointer(Pointer *p, int row, int column){
if (row>=0 && row<BoardRows && column>=0 && column<BoardColumns){
if (p->getRow()>=0 && p->getRow()<BoardRows && p->getColumn()>=0 && p->getColumn()<BoardColumns)
changes[p->getRow()][p->getColumn()]=true;
p->setRowColumn(row, column);
p->setMoved(true);
}
}
void Board::setPos(int x, int y){
Canvas::setPos(x,y);
int i,j;
for (i=0;i<BoardRows;i++)
for (j=0;j<BoardColumns;j++)
if (board[i][j])
board[i][j]->setPos(j*board[i][j]->width()+x, i*board[i][j]->height()+y);
paint_all=true;
}
void Board::setSpeed(int amount){
tick_amount=amount;
}
void Board::setMaxSpeed(){
tick_amount=MAX_SPEED;
}
void Board::setRequired(unsigned int value){
required=value;
required_changed=true;
}
void Board::setBackgroundType(BackgroundType type){
background_type=type;
}
void Board::tick(){
int aux;
tank->tick();
last->tick();
for (int i=0;i<BoardRows;i++)
for (int j=0;j<BoardColumns;j++)
if (board[i][j])
if (board[i][j]->getBonus()!=NormalBonus){
board[i][j]->tick();
changes[i][j]=true;
}
changes[last_row][last_column]=true;
if (!(tank->isEmpty())){//Descuento del tanque
tank->decFullLevel(tick_amount);
changes[flow_row][flow_column]=true;
}
else{//Aumento el actual
aux=(getPipe(flow_row, flow_column))->getFullLevel(actual_input);
if (aux<(getPipe(flow_row, flow_column))->full()){
(getPipe(flow_row, flow_column))->incFullLevel(actual_input, tick_amount);
changes[flow_row][flow_column]=true;
}
else{//Paso al siguiente
Pipe * next=NULL;
Pipe * last=board[flow_row][flow_column];
actual_input=Void;
switch (actual_output){
case North:{
if (flow_row>0){
next=getPipe(--flow_row, flow_column);
actual_input=South;
}
break;
}
case South:{
if (flow_row<BoardRows-1){
next=getPipe(++flow_row, flow_column);
actual_input=North;
}
break;
}
case East:{
if (flow_column<BoardColumns-1){
next=getPipe(flow_row, ++flow_column);
actual_input=West;
}
break;
}
case West:{
if (flow_column>0){
next=getPipe(flow_row, --flow_column);
actual_input=East;
}
break;
}
default:break;
}
if (actual_output==Void)
if (getStillRequired()==0)
state=OverWon;
else
state=OverLost;
else
if (actual_input==Void){
state=OverLost;
}else{
if (next)
if (next->hasConnection(actual_input) && !(next->isRestrictedAsOutput(actual_input))){
actual_output=next->getOutput(actual_input);
next->incFullLevel(actual_input,tick_amount);
/** Bonus stuff*/
if (!next->getOwner() && last->getOwner())
next->setOwner(last->getOwner());
Player * owner;
if ((owner=next->getOwner()))
switch (next->getBonus()){
case LifeBonus:{
owner->incLives();
next->setBonus(NormalBonus);
break;
}
case TimeBonus:{
//Debera incrementarse el valor del tiempo en board.
break;
}
default:{
owner->incScore(next->getBonus());
}
}
/*End of Bonus stuff*/
changes[flow_row][flow_column]=true;
}else state=OverLost;
else state=OverLost;
}
}
}
}
void Board::paint(VideoManager * vm){
int i,j;
Index * index;
Pointer * aux;
int pc, pr;
if (!background){
Image * ima=vm->getImageManager()->getImage(new Str("board_back.jpg"));
switch (background_type){
case WallpaperBackground:{
background=new WallPaper(ima);
break;
}
case MosaicBackground:{
background=new Mosaic(ima);
break;
}
}
background->setPos(x, y);
}
/** Pinta todos los pipes.*/
if (paint_all){
background->paint(vm);
for (i=0;i<BoardRows;i++)
for (j=0;j<BoardColumns;j++){
if (board[i][j]) board[i][j]->paint(vm);
changes[i][j]=false;
}
}else{/** Pinta solo los que han cambiado*/
for (i=0;i<BoardRows;i++)
for (j=0;j<BoardColumns;j++){
if (changes[i][j]){
background->repaint(vm, j*PipeWidth, i*PipeHeight, PipeWidth, PipeHeight);
if (board[i][j]) board[i][j]->paint(vm);
changes[i][j]=false;
}
}
}
/** Pinta y marca los punteros para la proxima entrada.*/
index=lps->getFirst();
for (i=0;i<lps->nObjects();i++){
aux=(Pointer*)lps->getObject(index);
pr=aux->getRow();
pc=aux->getColumn();
if (aux->moved()
||(flow_column==pc && flow_row==pr)
||(last_column==pc && last_row==pr)
||(board[pr][pc] && board[pr][pc]->getBonus()!=NormalBonus)){
aux->setPos(pc*aux->width+x, pr*aux->height+y);
aux->paint(vm);
if (aux->moved()) aux->setMoved(false); else aux->setMoved(true);
}
index=lps->getNext(index);
}
paint_all=false;
/** Pinta la mancha si el juego esta perdido.*/
if (state==OverLost){
Image * mancha=vm->getImageManager()->getImage(new Str("splash.png"));
int mx=flow_column*PipeWidth+x;
int my=flow_row*PipeHeight+y;
switch (actual_input){
case Void:{ //Esta contra un borde
switch (actual_output){
case North:my-=PipeHeight/2;break;
case South:my+=PipeHeight/2;break;
case West:mx-=PipeWidth/2;break;
case East:mx+=PipeWidth/2;break;
default:break;
}
break;
}
case North:my-=PipeHeight/2;break;
case South:my+=PipeHeight/2;break;
case West:mx-=PipeWidth/2;break;
case East:mx+=PipeWidth/2;break;
default:break;
}
vm->setClipping(x, y, width(), height());
vm->enableClipping(true);
vm->blit(mancha, mx, my);
vm->enableClipping(false);
}
if (tank->isChanged()) tank->paint(vm);
}
int Board::width(){
return BoardColumns*PipeWidth;
}
int Board::height(){
return BoardRows*PipeHeight;
}