/*************************************************************************** twin4doc.cpp - Boardgame for TDE ------------------- 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 #include // include files for TDE #include #include #include #include #include #include // 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); setGameStatus(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["<=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"<setTurn(true,true); } /** * End a game */ void Kwin4Doc::EndGame(TABLE mode) { setGameStatus(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="<id() << " admin=" << isAdmin() <userId()==Gelb) SetCurrentPlayer(Rot); else SetCurrentPlayer(Gelb); if (global_debug>1) kdDebug(12010) <<" Current set to "<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="<setGroup("YellowPlayer"); getPlayer(Gelb)->readConfig(config); config->setGroup("RedPlayer"); getPlayer(Rot)->readConfig(config); } /** * write config file */ void Kwin4Doc::WriteConfig(TDEConfig *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="<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("<1) kdDebug(12010) <<"twin4doc::checkGameOver::"<userId()<1) kdDebug(12010) << " Kwin4Doc::setPlayedBy(int "<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("<userId()<<"),KGameIO::IOMode "<1) kdDebug(12010) << "Creating MOUSE IO to "<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="<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;i1) 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="<count()!=2) { kdError() << "SERIOUS ERROR: We do not have two players...Trying to disconnect!"<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="<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="<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()<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()<scoreWidget()->setTurn(mAmzug); } else if (prop->id()==KGamePropertyBase::IdGameStatus) { if (gameStatus()==Abort) { if (global_debug>1) kdDebug(12010) << "PropertyChanged::status signal game abort +++" << endl; emit signalGameOver(2,getPlayer(QueryCurrentPlayer()),0); // 2 indicates Abort } else if (gameStatus()==Run) { if (global_debug>1) kdDebug(12010) << "PropertyChanged::status signal game run +++" << endl; preparePlayerTurn(); // Set the current player to play emit signalGameRun(); } else if (gameStatus()==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="<1) kdDebug(12010) << "amzug loaded to ="<1) kdDebug(12010) << "REDRAW GAME using undo/redo" << endl; if (global_debug>1) kdDebug(12010) << "history cnt="<1) kdDebug(12010) << "amzug ="<1) kdDebug(12010) << "Undoing move "<1) kdDebug(12010) << "amzug ="<0) { RedoMove(); cnt--; if (global_debug>1) kdDebug(12010) << "Redoing move "<1) kdDebug(12010) << "amzug ="<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 &inactivate) { if (global_debug>1) kdDebug(12010) << "newPlayersJoin: START"<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="<userId()<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="<userId()<id()); if (global_debug>1) kdDebug(12010) << "ADMIN keeps red and kicks yellow= " << yellow->id()<<" userId/col="<userId()<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="<userId()<1) kdDebug(12010) << "newPlayersJoin: DONE"<