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.
tdegames/kwin4/kwin4/kwin4doc.cpp

1323 lines
34 KiB

/***************************************************************************
twin4doc.cpp - Boardgame for KDE
-------------------
begin : Sun Mar 26 12:50:12 CEST 2000
copyright : (C) |1995-2000 by Martin Heni
email : martin@heni-online.de
***************************************************************************/
/***************************************************************************
* *
* 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 "twin4doc.h"
// include files for TQt
#include <tqdir.h>
#include <tqtimer.h>
// include files for KDE
#include <klocale.h>
#include <kconfig.h>
#include <kstandarddirs.h>
#include <kdebug.h>
#include <krandomsequence.h>
#include <kapplication.h>
// application specific includes
#include "kspritecache.h"
#include "twin4view.h"
#include "scorewidget.h"
#include "prefs.h"
#include "statuswidget.h"
Kwin4Doc::Kwin4Doc(TQWidget *parent, const char *) : KGame(1234,TQT_TQOBJECT(parent)), pView(0), mHintProcess(0)
{
connect(this,TQT_SIGNAL(signalPropertyChanged(KGamePropertyBase *,KGame *)),
this,TQT_SLOT(slotPropertyChanged(KGamePropertyBase *,KGame *)));
dataHandler()->Debug();
//kdDebug(12010) << "Property 7 policy=" << dataHandler()->find(7)->policy() << endl;
setPolicy(KGame::PolicyDirty,true);
//kdDebug(12010) << "Property 7 policy=" << dataHandler()->find(7)->policy() << endl;
// Game design
setMaxPlayers(2);
setMinPlayers(2);
// Game initialization
mField.resize(42);
// ****************************************
// NOTE: Do not i18n the strings here. They
// are for debugging only
// ****************************************
// The field array needs not be updated as any move will change it
// Careful only in new ResetGame! Maybe unlocal it there!
// mField.setPolicy(KGamePropertyBase::PolicyLocal);
mField.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,TQString("mField"));
mFieldFilled.resize(7);
mHistory.resize(43);
mHistory.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,TQString("mHistory"));
mAmzug.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,TQString("mAmzug"));
mCurrentMove.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,TQString("mCurrentMove"));
mMaxMove.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,TQString("mMaxMove"));
mFieldFilled.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,TQString("mFieldFilled"));
mHistoryCnt.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,TQString("mHistoryCnt"));
mLastColumn.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,TQString("mLastColumn"));
mLastHint.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,TQString("mLastHint"));
mLastColour.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,TQString("mLastColour"));
mScore.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,TQString("mScore"));
// game startup parameter
mStartPlayer=Gelb;
mStartPlayer.registerData(dataHandler(),KGamePropertyBase::PolicyDirty,TQString("mStartPlayer"));
SetCurrentPlayer((FARBE)mStartPlayer.value());
if (global_debug>1) kdDebug(12010) << "amZug policy=" << mAmzug.policy() << endl;
mPlayedBy[Gelb]=KGameIO::MouseIO;
mPlayedBy[Rot]=KGameIO::MouseIO;
// last in init
ResetGame(false);
setGametqStatus(Intro);
// Listen to network
connect(this,TQT_SIGNAL(signalMessageUpdate(int,TQ_UINT32,TQ_UINT32)),
this,TQT_SLOT(slotMessageUpdate(int, TQ_UINT32,TQ_UINT32)));
connect(this,TQT_SIGNAL(signalClientJoinedGame(TQ_UINT32,KGame *)),
this,TQT_SLOT(slotClientConnected(TQ_UINT32, KGame *)));
// Debug only
connect(this,TQT_SIGNAL(signalGameOver(int, KPlayer *,KGame *)),
this,TQT_SLOT(slotGameOver(int, KPlayer *,KGame *)));
// Change global KGame policy
//dataHandler()->setPolicy(KGamePropertyBase::PolicyDirty,false);
dataHandler()->Debug();
}
/**
* Player initialization
*/
void Kwin4Doc::initPlayers()
{
// Create yellow
Kwin4Player *yellow = (Kwin4Player *)createPlayer(1, mPlayedBy[Gelb], false);
yellow->setUserId(Gelb);
yellow->setName(Prefs::name1());
addPlayer(yellow);
setPlayedBy(Gelb,mPlayedBy[Gelb]);
// Create Red
Kwin4Player *red = (Kwin4Player *)createPlayer(1, mPlayedBy[Rot], false);
red->setUserId(Rot);
red->setName(Prefs::name1());
addPlayer(red);
setPlayedBy(Rot,mPlayedBy[Rot]);
}
Kwin4Doc::~Kwin4Doc()
{
WriteConfig(kapp->config());
if (mHintProcess)
delete mHintProcess;
}
void Kwin4Doc::setView(Kwin4View *view)
{
pView=view;
}
/**
* Returns colour
*/
FARBE Kwin4Doc::QueryColour(int x,int y){
return (FARBE)mField.at(x+y*FIELD_SIZE_X);
}
/**
* Set the colour
*/
void Kwin4Doc::SetColour(int x,int y,FARBE c){
if (x<0 || x>=FIELD_SIZE_X || y<0 || y>=FIELD_SIZE_Y)
{
kdDebug(12010) << "ERROR: SetColour auf falsche Poition " << x << " " << y << endl;
return ;
}
//kdDebug(12010) << "SetColor::mField["<<x+y*FIELD_SIZE_X<<"="<<c<<endl;
mField.setAt(x+y*FIELD_SIZE_X,c);
}
/**
* Reset the whole game
*/
void Kwin4Doc::ResetGame(bool initview){
// Reset field
for (int x=0;x<FIELD_SIZE_X;x++)
{
for (int y=FIELD_SIZE_Y-1;y>=0;y--)
SetColour(x,y,Niemand);
}
mFieldFilled.fill(0);
// Reset game vars
mHistoryCnt=0;
mCurrentMove=0;
mMaxMove=0;
mLastColumn=-1;
mLastColour=Niemand;
SetScore(0);
mLastHint=-1;
// Reset the view
if (initview)
pView->initView(false);
// Who starts this game
SetCurrentPlayer((FARBE)mStartPlayer.value());
}
/**
* Set current player to setTurn true
*/
void Kwin4Doc::preparePlayerTurn()
{
if (global_debug>1)
kdDebug(12010) << "Setting the current player to turn"<<endl;
getPlayer(QueryCurrentPlayer())->setTurn(true,true);
}
/**
* End a game
*/
void Kwin4Doc::EndGame(TABLE mode)
{
setGametqStatus(End);
pView->clearError();
pView->EndGame();
Kwin4Player *yellow=getPlayer(Gelb);
Kwin4Player *red=getPlayer(Rot);
switch(mode)
{
case TWin: yellow->incWin();
red->incLost();
break;
case TLost: yellow->incLost();
red->incWin();
break;
case TRemis: yellow->incRemis();
red->incRemis();
break;
default:
// Only break if moves have been made
if (mMaxMove>0)
{
yellow->incBrk();
red->incBrk();
}
break;
}
// switch start player
}
void Kwin4Doc::moveDone(TQCanvasItem *item,int )
{
//kdDebug(12010) << "########################## SPRITE MOVE DONE ################# " << endl;
//Debug();
//for (KPlayer* p=playerList()->first(); p!= 0; p=playerList()->next() )
//{
// p->Debug();
//}
if (playerCount()>1)
playerInputFinished(getPlayer(QueryCurrentPlayer()));
pView->clearError();
KSprite *sprite=(KSprite *)item;
sprite->deleteNotify();
}
/**
* Calcualte the next players turn
*/
KPlayer * Kwin4Doc::nextPlayer(KPlayer *last,bool /*exclusive*/)
{
if (global_debug>1)
kdDebug(12010) << k_funcinfo << "nextPlayer last="<<last->id() << " admin=" << isAdmin() <<endl;
// Should be enough if the admin sets the turn
if (last->userId()==Gelb)
SetCurrentPlayer(Rot);
else
SetCurrentPlayer(Gelb);
if (global_debug>1)
kdDebug(12010) <<" Current set to "<<QueryCurrentPlayer()<<endl;
if (isAdmin())
getPlayer(QueryCurrentPlayer())->setTurn(true,true);
emit signalMoveDone(0,0);
return getPlayer(QueryCurrentPlayer());
}
/**
* Make a game move
* mode=0 normal move, =1: redo move
*/
MOVESTATUS Kwin4Doc::MakeMove(int x,int mode){
if (x<0 || x>=FIELD_SIZE_X)
{
kdDebug(12010) << "ERROR: MakeMove auf falsche Position " << x << endl;
return GNotAllowed;
}
int y=mFieldFilled.at(x);
if (y>=FIELD_SIZE_Y)
{
return GIllMove; // no space left in column
}
if (mLastHint>=0)
{
int hy;
hy=mFieldFilled.at(mLastHint);
SetColour(mLastHint,hy,Niemand);
mLastHint=-1;
}
if (mode==Tip)
{
mLastHint=x;
SetColour(x,y,Tip);
return GTip ; // no real move
}
mFieldFilled.setAt(x,mFieldFilled.at(x)+1);
SetColour(x,y,QueryCurrentPlayer());
mHistory.setAt(QueryHistoryCnt(),x);
mHistoryCnt=mHistoryCnt.value()+1;
mLastColour=QueryCurrentPlayer();
//if (QueryCurrentPlayer()==Gelb) SetCurrentPlayer(Rot);
//else SetCurrentPlayer(Gelb);
mCurrentMove=mCurrentMove.value()+1;
// only if a real move isdone the maxmove is raised
if (mode==0) mMaxMove=mCurrentMove.value();
mLastColumn=x;
pView->setArrow(x,mLastColour);
// animation onyl if no redo
pView->setPiece(x,y,mLastColour,mCurrentMove-1,mode==1?false:true);
pView->setHint(0,0,false);
return GNormal;
}
/**
* Undo a move
*/
bool Kwin4Doc::UndoMove(){
//kdDebug(12010) <<" undo: current player="<<QueryCurrentPlayer() << endl;
//kdDebug(12010) <<" undo: history="<<QueryHistoryCnt() << endl;
if (QueryHistoryCnt()<1)
return false;
if (mLastHint>=0)
{
int hy;
hy=mFieldFilled.at(mLastHint);
SetColour(mLastHint,hy,Niemand);
mLastHint=-1;
}
// kdDebug(12010) << "Undo no="<<mHistoryCnt.value() << endl;
mHistoryCnt=mHistoryCnt.value()-1;
int x=mHistory.at(QueryHistoryCnt());
mFieldFilled.setAt(x,mFieldFilled.at(x)-1);
int y=mFieldFilled.at(x);
// kdDebug(12010) << "Undo x="<<x << " y=" <<y << endl;
SetColour(x,y,Niemand);
// We have to remove the piece as well...
pView->setPiece(x,y,Niemand,mCurrentMove-1,false);
mLastColour=QueryCurrentPlayer();
if (QueryCurrentPlayer()==Gelb) SetCurrentPlayer(Rot);
else SetCurrentPlayer(Gelb);
mCurrentMove=mCurrentMove.value()-1;
// sprite no, arrow pos, arrow color, enable
int oldx=-1;
if (QueryHistoryCnt()>0) oldx=mHistory.at(QueryHistoryCnt()-1);
pView->setSprite(mCurrentMove+1,oldx,QueryHistoryCnt()>0?mLastColour.value():0,false);
pView->setHint(0,0,false);
if (QueryHistoryCnt()>0)
mLastColumn=mHistory.at(QueryHistoryCnt()-1);
else
mLastColumn=-1;
SetScore(0);
return true;
}
/**
* Redo a move
*/
bool Kwin4Doc::RedoMove(){
//kdDebug(12010) << "mMaxMove=" << mMaxMove.value() << " historycnt=" << QueryHistoryCnt() << endl;
if (QueryHistoryCnt()>=mMaxMove)
return false;
int x=mHistory.at(QueryHistoryCnt());
//kdDebug(12010) << "Redo x=" << x << endl;
MakeMove(x,1);
if (QueryCurrentPlayer()==Gelb)
SetCurrentPlayer(Rot);
else
SetCurrentPlayer(Gelb);
SetScore(0);
pView->setHint(0,0,false);
return true;
}
/**
* Set the name of col
*/
void Kwin4Doc::SetName(FARBE i, const TQString &n){
getPlayer(i)->setName(n);
}
/**
* Query the name of i
*/
TQString Kwin4Doc::QueryName(FARBE i){
return getPlayer(i)->name();
}
/**
* Returns the all time statistics for player i
*/
int Kwin4Doc::QueryStat(FARBE i,TABLE mode){
Kwin4Player *player=getPlayer(i);
switch(mode)
{
case TWin: return player->win();
break;
case TRemis: return player->remis();
break;
case TLost: return player->lost();
break;
case TBrk: return player->brk();
break;
case TSum: return (player->win()+player->remis()+player->lost());
default:
break;
}
return 0;
}
/**
* Query the colour of player i
*/
FARBE Kwin4Doc::QueryPlayerColour(int player){
if (player==0)
return (FARBE)mStartPlayer.value();
if (mStartPlayer.value()==Gelb)
return Rot;
else
return Gelb;
}
/** */
int Kwin4Doc::CheckGameOver(int x, FARBE col){
int y,i;
FARBE c;
int star=1;
FARBE winc=Niemand;
// Check vertical up
int flag=0;
for (i=0;i<4;i++)
{
y=mFieldFilled.at(x)-1-i;
if (y>=0)
{
c=QueryColour(x,y);
if (c==col) flag++;
}
}
if (flag>=4 /*&& doBlink*/)
{
// Store win fields
for (i=0;i<4;i++)
{
y=mFieldFilled.at(x)-1-i;
pView->drawStar(x,y,star++);
winc=QueryColour(x,y);
}
return 1;
}
else if (flag>=4) return 1;
int xx;
// Check horizontal to the right
y=mFieldFilled.at(x)-1;
flag=0;
for (i=-3;i<=3 && flag<4;i++)
{
xx=x+i;
if (xx>=0 && xx<FIELD_SIZE_X)
{
c=QueryColour(xx,y);
if (c==col) flag++;
else flag=0;
}
}
if (flag>=4 /*&& doBlink*/)
{
// Store win fields
y=mFieldFilled.at(x)-1;
winc=QueryColour(x,y);
int cnt=0;
for (i=0;i<4;i++)
{
xx=x+i;
if (xx>=0 && xx<FIELD_SIZE_X)
{
if (QueryColour(xx,y)!=winc) break;
pView->drawStar(xx,y,star++);
cnt++;
}
else break;
}
for (i=-1;i>-4 && cnt<4;i--)
{
xx=x+i;
if (xx>=0 && xx<FIELD_SIZE_X)
{
if (QueryColour(xx,y)!=winc) break;
pView->drawStar(xx,y,star++);
cnt++;
}
else break;
}
return 1;
}
else if (flag>=4) return 1;
// Check dy+
flag=0;
for (i=-3;i<=3 && flag<4;i++)
{
xx=x+i;
if (xx>=0 && xx<FIELD_SIZE_X)
{
y=mFieldFilled.at(x)-1-i;
if (y>=0 && y<FIELD_SIZE_Y)
{
c=QueryColour(xx,y);
if (c==col) flag++;
else flag=0;
}
}
}
if (flag>=4 /*&& doBlink*/)
{
// Store win fields
y=mFieldFilled.at(x)-1;
winc=QueryColour(x,y);
int cnt=0;
for (i=0;i<4;i++)
{
xx=x+i;
if (xx>=0 && xx<FIELD_SIZE_X)
{
y=mFieldFilled.at(x)-1-i;
if (y<0) break;
if (QueryColour(xx,y)!=winc) break;
pView->drawStar(xx,y,star++);
cnt++;
}
else break;
}
for (i=-1;i>-4 && cnt<4;i--)
{
xx=x+i;
if (xx>=0 && xx<FIELD_SIZE_X)
{
y=mFieldFilled.at(x)-1-i;
if (y>=FIELD_SIZE_Y) break;
if (QueryColour(xx,y)!=winc) break;
pView->drawStar(xx,y,star++);
cnt++;
}
else break;
}
return 1;
}
else if (flag>=4) return 1;
// Check dy-
flag=0;
for (i=-3;i<=3 && flag<4;i++)
{
xx=x+i;
if (xx>=0 && xx<FIELD_SIZE_X)
{
y=mFieldFilled.at(x)-1+i;
if (y>=0 && y<FIELD_SIZE_Y)
{
c=QueryColour(xx,y);
if (c==col) flag++;
else flag=0;
}
}
}
if (flag>=4 /*&& doBlink*/)
{
// Store win fields
y=mFieldFilled.at(x)-1;
winc=QueryColour(x,y);
int cnt=0;
for (i=0;i<4;i++)
{
xx=x+i;
if (xx>=0 && xx<FIELD_SIZE_X)
{
y=mFieldFilled.at(x)-1+i;
if (y>=FIELD_SIZE_Y) break;
if (QueryColour(xx,y)!=winc) break;
pView->drawStar(xx,y,star++);
cnt++;
}
else break;
}
//kdDebug(12010) << "Found + cnt=" << cnt <<endl;
for (i=-1;i>-4 && cnt<4;i--)
{
xx=x+i;
if (xx>=0 && xx<FIELD_SIZE_X)
{
y=mFieldFilled.at(x)-1+i;
if (y<0) break;
if (QueryColour(xx,y)!=winc) break;
pView->drawStar(xx,y,star++);
cnt++;
}
else break;
}
//kdDebug(12010) << "all cnt=" << cnt<<endl;
return 1;
}
else if (flag>=4) return 1;
if (mCurrentMove>=42) return -1;
return 0;
}
/**
* Reset the stats
*/
void Kwin4Doc::ResetStat(){
getPlayer(Gelb)->resetStats();
getPlayer(Rot)->resetStats();
}
/**
* Set computer score
*/
void Kwin4Doc::SetScore(long i){
mScore.setValue(i);
}
/**
* Height of a column
*/
int Kwin4Doc::QueryHeight(int x){
if (x<0 || x>=FIELD_SIZE_X)
{
kdError() << "ERROR: Query Height for wrong x " << x << endl;
return 0;
}
return mFieldFilled.at(x);
}
int Kwin4Doc::QueryLastHint(){
return mLastHint;
}
void Kwin4Doc::loadSettings(){
// TODO find out what to do with this...
// mLevel.setValue(Prefs::level());
SetName(Gelb, Prefs::name1());
SetName(Rot, Prefs::name2());
KGameIO::IOMode mode = KGameIO::MouseIO;
int m = Prefs::input1();
if(m == 0) mode = KGameIO::MouseIO;
if(m == 1) mode = KGameIO::ProcessIO;
if(m == 2) mode = KGameIO::KeyIO;
setPlayedBy(Gelb, mode);
m = Prefs::input2();
if(m == 0) mode = KGameIO::MouseIO;
if(m == 1) mode = KGameIO::ProcessIO;
if(m == 2) mode = KGameIO::KeyIO;
setPlayedBy(Rot, mode);
FARBE col = (FARBE)Prefs::colour1();
if (QueryPlayerColour(0)!=col)
SwitchStartPlayer();
}
/**
* read config file
*/
void Kwin4Doc::ReadConfig(KConfig *config)
{
loadSettings();
config->setGroup("YellowPlayer");
getPlayer(Gelb)->readConfig(config);
config->setGroup("RedPlayer");
getPlayer(Rot)->readConfig(config);
}
/**
* write config file
*/
void Kwin4Doc::WriteConfig(KConfig *config)
{
config->setGroup("YellowPlayer");
getPlayer(Gelb)->writeConfig(config);
config->setGroup("RedPlayer");
getPlayer(Rot)->writeConfig(config);
config->sync();
}
/**
* Returns the current player, resp amzug
*/
FARBE Kwin4Doc::QueryCurrentPlayer(){
return (FARBE)mAmzug.value();
}
void Kwin4Doc::SetCurrentPlayer(FARBE i)
{
mAmzug.setValue(i);
}
/**
* Swtich the starting player and return the new started
*/
FARBE Kwin4Doc::SwitchStartPlayer()
{
if (mStartPlayer.value()==Gelb)
mStartPlayer.setValue(Rot);
else
mStartPlayer.setValue(Gelb);
return (FARBE)mStartPlayer.value();
}
int Kwin4Doc::QueryLastcolumn()
{
return mLastColumn;
}
FARBE Kwin4Doc::QueryLastcolour()
{
return (FARBE)(mLastColour.value());
}
int Kwin4Doc::QueryCurrentMove()
{
return mCurrentMove;
}
void Kwin4Doc::SetCurrentMove(int i)
{
mCurrentMove=i;
}
int Kwin4Doc::QueryMaxMove()
{
return mMaxMove;
}
int Kwin4Doc::QueryHistoryCnt()
{
return mHistoryCnt;
}
/**
* Return the name of the computer player process
*/
TQString Kwin4Doc::QueryProcessName()
{
// First try a local dir override
TQDir dir;
TQString filename=dir.path()+TQString("/twin4/twin4proc");
TQFile flocal(filename);
if (flocal.exists())
{
if (global_debug>1) kdDebug(12010) << " Found local process " << filename << endl;
return filename;
}
TQString path=kapp->dirs()->findExe("twin4proc");
if (!path.isNull())
{
if (global_debug>1) kdDebug(12010) << " Found system process " << path << endl;
return path;
}
TQString empty;
kdError() << "Could not locate the computer player" << endl;
return empty;
}
void Kwin4Doc::slotMessageUpdate(int /*id*/,TQ_UINT32 /*sender*/,TQ_UINT32 /*recv*/)
{
// kdDebug(12010) << "MSG: id=" << id << " sender=" << sender << " receiver="<<recv<< endl;
}
/**
* Create a KPlayer
*/
KPlayer *Kwin4Doc::createPlayer(int /*rtti*/,int io,bool isvirtual)
{
KPlayer *player = new Kwin4Player;
if (!isvirtual)
createIO(player,(KGameIO::IOMode)io);
connect(player,TQT_SIGNAL(signalPropertyChanged(KGamePropertyBase *, KPlayer *)),
this,TQT_SLOT(slotPlayerPropertyChanged(KGamePropertyBase *, KPlayer *)));
((Kwin4Player *)player)->setWidget(pView->statusWidget());
return player;
}
/**
* Called when a player input is received from the KGame object
* this is e-.g. a mouse event
*/
bool Kwin4Doc::playerInput(TQDataStream &msg, KPlayer * /*player*/)
{
int move, pl;
msg >> pl >> move;
if (!Move(move,pl))
TQTimer::singleShot(0, this,TQT_SLOT(slotRepeatMove()));
return false;
}
/**
* Reactivate player in case of a move which could not pe performed
*/
void Kwin4Doc::slotRepeatMove()
{
getPlayer(QueryCurrentPlayer())->setTurn(true);
}
/**
* Performs a game move
*/
bool Kwin4Doc::Move(int x,int id)
{
if (global_debug>1)
kdDebug(12010) <<" Kwin4Doc::Move("<<x<<","<<id<<")"<<endl;
return (MakeMove(x,0) == GNormal);
}
/**
* return -1: remis, 1:won, 0: continue
*/
int Kwin4Doc::checkGameOver(KPlayer *p)
{
if (global_debug>1)
kdDebug(12010) <<"twin4doc::checkGameOver::"<<p->userId()<<endl;
return CheckGameOver(QueryLastcolumn(),QueryLastcolour());
}
KGameIO::IOMode Kwin4Doc::playedBy(int col)
{
return mPlayedBy[col];
}
void Kwin4Doc::setPlayedBy(int col, KGameIO::IOMode io)
{
if (global_debug>1)
kdDebug(12010) << " Kwin4Doc::setPlayedBy(int "<<col<<",KGameIO::IOMode "<<io<<")" << endl;
Kwin4Player *player=getPlayer((FARBE)col);
if (mPlayedBy[col]!=io && !player->isVirtual())
{
mPlayedBy[col]=io;
player->removeGameIO(0); // remove all IO's
createIO(player,io);
}
}
/* Get the io values right after a load game as the io the playedby
* is not set there
*/
void Kwin4Doc::recalcIO()
{
mPlayedBy[Gelb]=(KGameIO::IOMode)getPlayer(Gelb)->calcIOValue();
mPlayedBy[Rot]=(KGameIO::IOMode)getPlayer(Rot)->calcIOValue();
}
void Kwin4Doc::createIO(KPlayer *player,KGameIO::IOMode io)
{
if (!player)
return;
if (global_debug>1)
kdDebug(12010) << " Kwin4Doc::createIO(KPlayer *player("<<player->userId()<<"),KGameIO::IOMode "<<io<<") " << endl;
if (io&KGameIO::MouseIO)
{
KGameMouseIO *input;
if (global_debug>1) kdDebug(12010) << "Creating MOUSE IO to "<<pView<< endl;
// We want the player to work over mouse
input=new KGameMouseIO(pView);
if (global_debug>1) kdDebug(12010) << "MOUSE IO added " << endl;
// Connect mouse input to a function to process the actual input
connect(input,TQT_SIGNAL(signalMouseEvent(KGameIO *,TQDataStream &,TQMouseEvent *,bool *)),
pView,TQT_SLOT(slotMouseInput(KGameIO *,TQDataStream &,TQMouseEvent *,bool *)));
player->addGameIO(input);
}
else if (io&KGameIO::ProcessIO)
{
TQString file=QueryProcessName();
if (global_debug>1) kdDebug(12010) << "Creating PROCESS IO " << file << endl;
KGameProcessIO *input;
// We want a computer player
input=new KGameProcessIO(file);
// Connect computer player to the setTurn
connect(input,TQT_SIGNAL(signalPrepareTurn(TQDataStream &,bool,KGameIO *,bool *)),
this,TQT_SLOT(slotPrepareTurn(TQDataStream &,bool,KGameIO *,bool *)));
connect(input,TQT_SIGNAL(signalProcessQuery(TQDataStream &,KGameProcessIO *)),
this,TQT_SLOT(slotProcessQuery(TQDataStream &,KGameProcessIO *)));
player->addGameIO(input);
}
else if (io&KGameIO::KeyIO)
{
if (global_debug>1) kdDebug(12010) << "Creating KEYBOARD IO " << endl;
// We want the player to work over keyboard
KGameKeyIO *input;
input=new KGameKeyIO(pView->parentWidget());
// Connect keys input to a function to process the actual input
connect((KGameKeyIO *)input,TQT_SIGNAL(signalKeyEvent(KGameIO *,TQDataStream &,TQKeyEvent *,bool *)),
pView,TQT_SLOT(slotKeyInput(KGameIO *,TQDataStream &,TQKeyEvent *,bool *)));
player->addGameIO(input);
}
}
/**
* This slot is called when a computer move should be generated
*/
void Kwin4Doc::slotPrepareTurn(TQDataStream &stream,bool b,KGameIO *input,bool *sendit)
{
if (global_debug>1)
kdDebug(12010) << " Kwin4Doc::slotPrepareTurn b="<<b << endl;
*sendit=false;
// Our player
KPlayer *player=input->player();
if (!player->myTurn()) return ;
if (!b) return ; // only on setTurn(true)
TQ_INT32 pl;
if (global_debug>1) kdDebug(12010) << "slotPrepareComputerTurn for player id= " << player->id() << endl;
pl=player->userId();
prepareGameMessage(stream,pl);
*sendit=true;
}
/**
* Sends the current game status to the computer player
* Careful: The data needs to be the same than the computer
* player reading on the other side
**/
void Kwin4Doc::prepareGameMessage(TQDataStream &stream, TQ_INT32 pl)
{
if (global_debug>1) kdDebug(12010) << " sending col=" << pl << endl;
stream << pl ;
// This needs to be the same than the computer player reads!
stream << (TQ_INT32)QueryCurrentMove();
stream << (TQ_INT32)QueryCurrentPlayer();
stream << (TQ_INT32)QueryPlayerColour(0);
stream << (TQ_INT32)QueryPlayerColour(1);
stream << (TQ_INT32)Prefs::level();
int i,j;
for (i=0;i<FIELD_SIZE_Y;i++)
{
for (j=0;j<FIELD_SIZE_X;j++)
{
TQ_INT8 col;
col=QueryColour(j,i);
stream << col;
}
if (global_debug>1) kdDebug(12010)
<< QueryColour(0,i) << " "
<< QueryColour(1,i) << " "
<< QueryColour(2,i) << " "
<< QueryColour(3,i) << " "
<< QueryColour(4,i) << " "
<< QueryColour(5,i) << " "
<< QueryColour(6,i) << endl;
}
stream << (TQ_INT32)421256;
}
void Kwin4Doc::slotProcessQuery(TQDataStream &in,KGameProcessIO * /*me*/)
{
TQ_INT8 cid;
in >> cid;
switch(cid)
{
case 1: // value
long value;
in >> value;
if (global_debug>1) kdDebug(12010) << "#### Computer thinks value is " << value << endl;
SetScore(value);
break;
default:
kdError() << "Kwin4Doc::slotProcessQuery: Unknown id " << cid << endl;
break;
}
}
/**
* This slot is called by the signal of KGame to indicated
* that the network connection is done and a new client is
* connected
* cid is the id of the client connected. if this is equal
* gameId() WE are the client
*/
void Kwin4Doc::slotClientConnected(TQ_UINT32 cid,KGame *)
{
if (global_debug>1) kdDebug(12010) << " void Kwin4Doc::slotClientConnected id="<<cid << " we=" <<
gameId() << " we admin=" << isAdmin() << "master)" << isMaster() << endl;
if (playerList()->count()!=2)
{
kdError() << "SERIOUS ERROR: We do not have two players...Trying to disconnect!"<<endl;
disconnect();
return ;
}
// Get the two players - more are not possible
Kwin4Player *p1=(Kwin4Player *)playerList()->at(0);
Kwin4Player *p2=(Kwin4Player *)playerList()->at(1);
if (!p1->isVirtual())
{
emit signalChatChanged(p1);
if (global_debug>1) kdDebug(12010) << "CHAT to player 0 " << endl;
}
else
{
emit signalChatChanged(p2);
if (global_debug>1) kdDebug(12010) << "CHAT to player 1 " << endl;
}
// Now check whose turn it is. The Admin will rule this
if (isAdmin())
{
if (global_debug>1) kdDebug(12010) << "WE are ADMIN == COOL ! " << endl;
// p1 is local
if (!p1->isVirtual())
{
if (global_debug>1) kdDebug(12010) << "p1 id=" << p1->userId() << " is local turn="<<p1->myTurn()<< endl;
// Exclusive setting of the turn
p1->setTurn(p1->myTurn(),true);
p2->setTurn(!p1->myTurn(),true);
}
else if (!p2->isVirtual())
{
if (global_debug>1) kdDebug(12010) << "p2 id=" << p2->userId() << " is local turn="<<p2->myTurn()<< endl;
// Exclusive setting of the turn
p2->setTurn(p2->myTurn(),true);
p1->setTurn(!p2->myTurn(),true);
}
}
}
/**
* Get the KPlayer from the color by searching all players
* users id's
**/
Kwin4Player *Kwin4Doc::getPlayer(FARBE col)
{
Kwin4Player *p;
for ( p=(Kwin4Player *)playerList()->first(); p!= 0; p=(Kwin4Player *)playerList()->next() )
{
if (p->userId()==col)
return p;
}
kdError() << "SERIOUS ERROR: Cannot find player with colour " << col << ". CRASH imminent" << endl;
return 0;
}
/**
* We create a process which calulcates a computer move
* which is shown as hint
**/
void Kwin4Doc::calcHint()
{
// We allocate the hint process only if it is needed
if (!mHintProcess)
{
TQString file=QueryProcessName();
if (global_debug>1) kdDebug(12010) << "Creating HINT PROCESS IO " << endl;
// We want a computer player
mHintProcess=new KGameProcessIO(file);
connect(mHintProcess,TQT_SIGNAL(signalProcessQuery(TQDataStream &,KGameProcessIO *)),
this,TQT_SLOT(slotProcessHint(TQDataStream &,KGameProcessIO *)));
}
TQ_INT32 pl;
TQByteArray buffer;
TQDataStream stream(buffer,IO_WriteOnly);
pl=QueryCurrentPlayer();
prepareGameMessage(stream,pl);
mHintProcess->sendMessage(stream,2,0,gameId());
}
/**
* The compute rprocess sent a hint which we show in the
* game board
**/
void Kwin4Doc::slotProcessHint(TQDataStream &in,KGameProcessIO * /*me*/)
{
TQ_INT8 cid;
in >> cid;
switch(cid)
{
case 2: // Hint
{
TQ_INT32 pl;
TQ_INT32 move;
long value;
in >> pl >> move >> value;
if (global_debug>1) kdDebug(12010) << "#### Computer thinks pl=" << pl << " move =" << move << endl;
if (global_debug>1) kdDebug(12010) << "#### Computer thinks hint is " << move << " and value is " << value << endl;
int x=move;
int y=mFieldFilled.at(x);
pView->setHint(x,y,true);
}
break;
default:
kdError() << "Kwin4Doc::slotProcessHint: Unknown id " << cid << endl;
break;
}
}
/**
* Called when a player property has changed. We check whether the name
* changed and then update the score widget
* We should maybe do this for the other properties too to update
* the status widget...I am not sure here...we'll see
**/
void Kwin4Doc::slotPlayerPropertyChanged(KGamePropertyBase *prop,KPlayer *player)
{
if (!pView) return ;
if (prop->id()==KGamePropertyBase::IdName)
{
if (global_debug>1) kdDebug(12010) << "Player name id=" << player->userId() << " changed to " << player->name()<<endl;
pView->scoreWidget()->setPlayer(player->name(),player->userId());
}
}
void Kwin4Doc::slotPropertyChanged(KGamePropertyBase *prop,KGame *)
{
if (!pView)
return ;
if (prop->id()==mCurrentMove.id())
{
pView->scoreWidget()->setMove(mCurrentMove);
}
else if (prop->id()==mScore.id())
{
int sc=mScore/10000;
if (sc==0 && mScore.value()>0) sc=1;
else if (sc==0 && mScore.value()<0) sc=-1;
pView->scoreWidget()->setChance(sc);
}
else if (prop->id()==mAmzug.id())
{
if (global_debug>1) kdDebug(12010) << "Amzug changed to " << mAmzug.value()<<endl;
pView->scoreWidget()->setTurn(mAmzug);
}
else if (prop->id()==KGamePropertyBase::IdGametqStatus)
{
if (gametqStatus()==Abort)
{
if (global_debug>1) kdDebug(12010) << "PropertyChanged::status signal game abort +++" << endl;
emit signalGameOver(2,getPlayer(QueryCurrentPlayer()),0); // 2 indicates Abort
}
else if (gametqStatus()==Run)
{
if (global_debug>1) kdDebug(12010) << "PropertyChanged::status signal game run +++" << endl;
preparePlayerTurn(); // Set the current player to play
emit signalGameRun();
}
else if (gametqStatus()==Init)
{
if (global_debug>1) kdDebug(12010) << "PropertyChanged::status signal game INIT +++" << endl;
ResetGame(true);
}
else
{
if (global_debug>1) kdDebug(12010) << "PropertyChanged::other status signal +++" << endl;
}
}
}
/**
* Called by KGame if the game has ended.
* DEBUG only as we do not need any extension to
* the KGame behavior
*/
void Kwin4Doc::slotGameOver(int status, KPlayer * p, KGame * /*me*/)
{
if (global_debug>1) kdDebug(12010) << "SlotGameOver: status="<<status<<" lastplayer uid="<<p->userId()<<endl;
}
/**
* This is an overwritten function of KGame which is called
* when a game is loaded. This can either be via a networ
* connect or via a real load from file
**/
bool Kwin4Doc::loadgame(TQDataStream &stream,bool network,bool reset)
{
if (global_debug>1)
kdDebug () << "loadgame() network=" << network << " reset="<< reset << endl;
if (!network) setGametqStatus(End);
// Clear out the old game
if (global_debug>1) kdDebug(12010)<<"loadgame wants to reset the game"<<endl;
ResetGame(true);
// load the new game
bool res=KGame::loadgame(stream,network,reset);
if (global_debug>1) kdDebug(12010) << "amzug loaded to ="<<mAmzug.value() << endl;
// Replay the game be undoing and redoing
if (global_debug>1) kdDebug(12010) << "REDRAW GAME using undo/redo" << endl;
if (global_debug>1) kdDebug(12010) << "history cnt="<<mHistoryCnt.value() << endl;
if (global_debug>1) kdDebug(12010) << "amzug ="<<mAmzug.value() << endl;
int cnt=0;
while(UndoMove())
{
cnt++;
if (global_debug>1) kdDebug(12010) << "Undoing move "<<cnt<<endl;
}
if (global_debug>1) kdDebug(12010) << "amzug ="<<mAmzug.value() << endl;
while(cnt>0)
{
RedoMove();
cnt--;
if (global_debug>1) kdDebug(12010) << "Redoing move "<<cnt<<endl;
}
if (global_debug>1) kdDebug(12010) << "amzug ="<<mAmzug.value() << endl;
// Set the input devices
recalcIO();
// And set the right player to turn
preparePlayerTurn();
if (global_debug>1)
kdDebug(12010) << "loadgame done +++" << endl;
return res;
}
/**
* This is also an overwritten function of KGame. It is
* Called in the game negotiation upon connect. Here
* the games have to determine what player is remote and
* what is local
* This function is only called in the Admin.
*/
void Kwin4Doc::newPlayersJoin(KGamePlayerList * /*oldList*/,KGamePlayerList *newList,TQValueList<int> &inactivate)
{
if (global_debug>1)
kdDebug(12010) << "newPlayersJoin: START"<<endl;
Kwin4Player *yellow=getPlayer(Gelb);
Kwin4Player *red=getPlayer(Rot);
KPlayer *player;
// Take the master player with the higher priority. Prioirty is set
// be the network dialog
if (yellow->networkPriority()>red->networkPriority())
{
// Deactivate the lower one
inactivate.append(red->id());
if (global_debug>1) kdDebug(12010) << "ADMIN keeps yellow and kicks red= " << red->id()<<" userId/col="<<red->userId()<<endl;
// loop all client players and deactivate the one which have the color
// yellow
for ( player=newList->first(); player != 0; player=newList->next() )
{
if (player->userId()==yellow->userId())
{
inactivate.append(player->id());
if (global_debug>1) kdDebug(12010) << "Deactivate C1 " << player->id()<<" col="<<player->userId()<<endl;
}
}
}
else
{
// Deactivate the lower one
inactivate.append(yellow->id());
if (global_debug>1) kdDebug(12010) << "ADMIN keeps red and kicks yellow= " << yellow->id()<<" userId/col="<<yellow->userId()<<endl;
// loop all client players and deactivate the one which have the color
// red
for ( player=newList->first(); player != 0; player=newList->next() )
{
if (player->userId()==red->userId())
{
inactivate.append(player->id());
if (global_debug>1) kdDebug(12010) << "Deactivate C2 " << player->id()<<" col="<<player->userId()<<endl;
}
}
}
if (global_debug>1)
kdDebug(12010) << "newPlayersJoin: DONE"<<endl;
}
#include "twin4doc.moc"