diff --git a/src/chatdlg.cpp b/src/chatdlg.cpp index 18bf48d..0b7ec1d 100644 --- a/src/chatdlg.cpp +++ b/src/chatdlg.cpp @@ -1,98 +1,97 @@ /* This file is part of the KDE games kwin4 program Copyright (c) 2006 Martin Heni This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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. */ // Header includes #include "chatdlg.h" // include files for QT #include #include #include #include #include // include files for KDE -#include #include #define USE_UNSTABLE_LIBKDEGAMESPRIVATE_API #include // application specific includes #include "kfourinline_debug.h" #include "kchatdialog.h" #include "kwin4player.h" // Constructor for the chat widget. This widget // is derived from the libkdegames chat widget ChatDlg::ChatDlg(KGame *game,QWidget *parent) : QDialog(parent),mChat(), mChatDlg() { setWindowTitle(i18n("Chat Dlg")); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); okButton->setDefault(true); setModal(false); setMinimumSize(QSize(200,200)); QFrame* frame = new QFrame(this); QGridLayout* mGridLayout = new QGridLayout(frame); QGroupBox* b = new QGroupBox(i18n("Chat"), frame); QVBoxLayout* gboxLay = new QVBoxLayout(b); mChat = new KGameChat(game, 10000, b); gboxLay->addWidget(mChat); mGridLayout->addWidget(b,0,0); QPushButton *mButton = new QPushButton(i18n("Configure..."),frame); mGridLayout->addWidget(mButton,1,1); mainLayout->addWidget(frame); mainLayout->addWidget(buttonBox); adjustSize(); mChatDlg = new KChatDialog(mChat,frame,true); connect(mButton, &QPushButton::clicked, mChatDlg, &KChatDialog::show); } // Set the player in who does the chat. This should be the local player. void ChatDlg::setPlayer(KWin4Player* p) { if (!mChat) { qCCritical(KFOURINLINE_LOG) << "ChatDlg::setPlayer::Chat not defined can't set player"; return ; } if (!p) { qCCritical(KFOURINLINE_LOG) << "ChatDlg::setPlayer::Player not defined can't set player"; return ; } mChat->setFromPlayer(p); } diff --git a/src/introsprite.cpp b/src/introsprite.cpp index 4ba190b..f36dd43 100644 --- a/src/introsprite.cpp +++ b/src/introsprite.cpp @@ -1,439 +1,438 @@ /* This file is part of the KDE games kwin4 program Copyright (c) 2006 Martin Heni This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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. */ // Header includes #include "introsprite.h" #include "kfourinline_debug.h" // General includes #include // KDE includes -#include #define sign(x) ( (x)>0?1:((x)<0?-1:0) ) /** * Store an animation script command. The command represents certain possible * animation sub mentods. The possible methods are listed in the enum Cmd */ class AnimationCommand { public: /** * The supported commands. */ enum Cmd {SHOW, HIDE, PAUSE, POSITION, LINEAR_DURATION, CIRCLE_DURATION, MANHATTEN}; /** * Construct an animation command */ AnimationCommand() { cmd = PAUSE; duration = 0; radius = 0.0; velocity = 0.0; } /** The command type. */ Cmd cmd; /** How long does the command take [ms]. */ int duration; /** The start position [0..1, 0..1]. */ QPointF start; /** The end position [0..1, 0..1]. */ QPointF end; /** The radius [0..1] (if necessary). */ double radius; /** The velocity [0...1000] (if necessary). */ double velocity; }; // Constructor for the sprite IntroSprite::IntroSprite(const QString &id, ThemeManager* theme, int no, QGraphicsScene* scene) : Themeable(id, theme), PixmapSprite(no, scene) { hide(); mDeltaT = 0; mDelayT = 0; mNo = no; mTime.restart(); mDebugTime.restart(); mStartAnimation = true; if (theme) theme->updateTheme(this); } // Destructor IntroSprite::~IntroSprite() { // Clear animation list (deletes objects) clearAnimation(); } // Change the theme void IntroSprite::changeTheme() { PixmapSprite::changeTheme(); } // Clear the animation script void IntroSprite::clearAnimation(bool restartTime) { qDeleteAll(mAnimList); mAnimList.clear(); // Reset time? if (restartTime) { mTime.restart(); mDeltaT = 0; mDelayT = 0; } mStartAnimation = true; } // Add a show sprite command AnimationCommand* IntroSprite::addShow() { AnimationCommand* cmd = new AnimationCommand(); cmd->cmd = AnimationCommand::SHOW; cmd->duration = 0; cmd->start=previousStart(); cmd->end=previousEnd(); mAnimList.append(cmd); return cmd; } // Add a hide sprite command AnimationCommand* IntroSprite::addHide() { AnimationCommand* cmd = new AnimationCommand(); cmd->cmd = AnimationCommand::HIDE; cmd->duration = 0; cmd->start=previousStart(); cmd->end=previousEnd(); mAnimList.append(cmd); return cmd; } // Add a relative pause AnimationCommand* IntroSprite::addPause(int duration) { AnimationCommand* cmd = new AnimationCommand(); cmd->cmd = AnimationCommand::PAUSE; cmd->duration = duration; cmd->start=previousStart(); cmd->end=previousEnd(); mAnimList.append(cmd); return cmd; } // Add an absolute pause...wait until the given time AnimationCommand* IntroSprite::addWait(int duration) { int currentDuration = animationDuration(); if (currentDuration < duration) { AnimationCommand* cmd = new AnimationCommand(); cmd->cmd = AnimationCommand::PAUSE; cmd->duration = duration-currentDuration; cmd->start=previousStart(); cmd->end=previousEnd(); mAnimList.append(cmd); return cmd; } return nullptr; } // Add a set position command AnimationCommand* IntroSprite::addPosition(QPointF start) { AnimationCommand* cmd = new AnimationCommand(); cmd->cmd = AnimationCommand::POSITION; cmd->duration = 0; cmd->start=start; cmd->end=start; mAnimList.append(cmd); return cmd; } // Add a linear movement AnimationCommand* IntroSprite::addLinear(QPointF start, QPointF end, int duration) { AnimationCommand* cmd = new AnimationCommand(); cmd->cmd = AnimationCommand::LINEAR_DURATION; cmd->duration = duration; cmd->start=start; cmd->end=end; mAnimList.append(cmd); return cmd; } // Add a relative linear movement, that is, starting from the previous position AnimationCommand* IntroSprite::addRelativeLinear(QPointF end, int duration) { AnimationCommand* cmd = new AnimationCommand(); cmd->cmd = AnimationCommand::LINEAR_DURATION; cmd->duration = duration; cmd->start=previousEnd(); cmd->end=end; mAnimList.append(cmd); return cmd; } // Add a circle movement AnimationCommand* IntroSprite::addCircle(QPointF start, double radius, int duration) { AnimationCommand* cmd = new AnimationCommand(); cmd->cmd = AnimationCommand::CIRCLE_DURATION; cmd->duration = duration; cmd->start=start; cmd->end=start; cmd->radius=radius; mAnimList.append(cmd); return cmd; } // Add a relative manhatten move AnimationCommand* IntroSprite::addRelativeManhatten(QPointF end, double velocity) { AnimationCommand* cmd = new AnimationCommand(); cmd->cmd = AnimationCommand::MANHATTEN; cmd->start = previousEnd(); cmd->end=end; cmd->velocity=velocity; qreal dtx = fabs(cmd->end.x()-cmd->start.x())/cmd->velocity*1000.0; qreal dty = fabs(cmd->end.y()-cmd->start.y())/cmd->velocity*1000.0; cmd->duration = int(dtx+dty+1.0); mAnimList.append(cmd); return cmd; } // Retrieve the last commands start position or the current position if none exists QPointF IntroSprite::previousStart() { AnimationCommand* previous = mAnimList.last(); if (previous != nullptr) return previous->start; else return QPointF(x()/getScale(), y()/getScale()); } // Retrieve the last commands end position or the current position if none exists QPointF IntroSprite::previousEnd() { AnimationCommand* previous = mAnimList.last(); if (previous != nullptr) return previous->end; return QPointF(x()/getScale(), y()/getScale()); } // Retrieve the duration of the given animation command int IntroSprite::duration(AnimationCommand* anim) { return anim->duration; } // Retrieve the overall duration of the animation int IntroSprite::animationDuration() { int dura = 0; for (int i=0; iduration; } return dura; } // Globally delay the animation void IntroSprite::delayAnimation(int duration) { mDelayT += duration; } // CanvasItem advance method void IntroSprite::advance(int phase) { // Ignore phase 0 (collisions) if (phase == 0) { QGraphicsItem::advance(phase); return ; } // No animation pending if (mAnimList.isEmpty()) { QGraphicsItem::advance(phase); return ; } // First time animation start if (mStartAnimation) { mTime.restart(); mStartAnimation = false; } int elapsed = mTime.elapsed()+mDeltaT-mDelayT; if (elapsed<0) elapsed = 0; // Get the current/first command AnimationCommand* anim = mAnimList.first(); // Execute commands who were passed in the time since the // last call while (anim != nullptr && anim->duration <= elapsed) { executeCmd(anim, anim->duration); mAnimList.removeFirst(); elapsed -= anim->duration; if (!mAnimList.isEmpty()) anim = mAnimList.first(); else anim = nullptr; } // Handle current command if (anim != nullptr) { executeCmd(anim, elapsed); } // Restart timer and store remaining time mTime.restart(); mDeltaT = elapsed; mDelayT = 0; // Parent advance QGraphicsItem::advance(phase); } // Execute the given animation command. The given time is the time elapsed // since start of the animation sequence. void IntroSprite::executeCmd(AnimationCommand* anim, int elapsed) { if (anim == nullptr) return; // Scale double scale = this->getScale(); // Pause command if (anim->cmd == AnimationCommand::PAUSE) { // Do nothing } // Show sprite command if (anim->cmd == AnimationCommand::SHOW) { show(); } // Hide sprite command if (anim->cmd == AnimationCommand::HIDE) { hide(); } // Set position command if (anim->cmd == AnimationCommand::POSITION) { qreal x = anim->end.x(); qreal y = anim->end.y(); setPos(x*scale, y*scale); } // Linear move command if (anim->cmd == AnimationCommand::LINEAR_DURATION) { double qt = double(elapsed)/double(anim->duration); qreal x = anim->start.x() + qt*(anim->end.x()-anim->start.x()); qreal y = anim->start.y() + qt*(anim->end.y()-anim->start.y()); setPos(x*scale, y*scale); } // Circle move command if (anim->cmd == AnimationCommand::CIRCLE_DURATION) { double qt = double(elapsed)/double(anim->duration); double sign = 1.0; if (anim->start.x() > 0.5) sign = -1.0; // Direction of turn qreal cx = anim->start.x(); qreal cy = anim->start.y(); qreal x = cx + anim->radius*sin(sign*qt*2.0*M_PI); qreal y = cy-anim->radius + anim->radius*cos(sign*qt*2.0*M_PI); setPos(x*scale, y*scale); } // Manhatten move command if (anim->cmd == AnimationCommand::MANHATTEN) { if ( (fabs(anim->end.x()-x()/scale) <= 0.00001 && fabs(anim->end.y()-y()/scale) <= 0.00001 ) || (elapsed >= anim->duration) ) { setPos(anim->end.x()*scale, anim->end.y()*scale); } // x-edge else if (fabs(anim->end.x()-x()/scale) > 0.00001) { qreal x = anim->start.x() + sign(anim->end.x()-anim->start.x())*anim->velocity*double(elapsed)/1000.0; // Finished? if ( (anim->end.x() > anim->start.x() && x >= anim->end.x()) || (anim->end.x() < anim->start.x() && x <= anim->end.x()) ) { x = anim->end.x(); } setPos(x*scale, anim->start.y()*scale); } // y-edge else { qreal dtx = fabs(anim->end.x()-anim->start.x())/anim->velocity*1000.0; qreal y = anim->start.y() + sign(anim->end.y()-anim->start.y())*anim->velocity*double(elapsed-dtx)/1000.0; // Finished? if ( (anim->end.y() > anim->start.y() && y >= anim->end.y()) || (anim->end.y() < anim->start.y() && y <= anim->end.y()) ) { y = anim->end.y(); } setPos(anim->end.x()*scale, y*scale); } } } diff --git a/src/kchatdialog.cpp b/src/kchatdialog.cpp index f510743..7981291 100644 --- a/src/kchatdialog.cpp +++ b/src/kchatdialog.cpp @@ -1,285 +1,283 @@ /* This file is part of the KDE games library Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 "kchatdialog.h" #include "kfourinline_debug.h" #define USE_UNSTABLE_LIBKDEGAMESPRIVATE_API #include -#include #include #include #include #include #include #include -#include #include #include class KChatDialogPrivate { public: KChatDialogPrivate() { mTextPage = nullptr; mNamePreview = nullptr; mTextPreview = nullptr; mSystemNamePreview = nullptr; mSystemTextPreview = nullptr; mChat = nullptr; } QFrame* mTextPage; QLabel* mNamePreview; QLabel* mTextPreview; QLabel* mSystemNamePreview; QLabel* mSystemTextPreview; QLineEdit* mMaxMessages; KChatBase* mChat; }; KChatDialog::KChatDialog(KChatBase* chat, QWidget* parent, bool modal) : QDialog(parent), d( new KChatDialogPrivate ) { setModal(modal); init(); plugChatWidget(chat); } KChatDialog::KChatDialog(QWidget* parent, bool modal) : QDialog(parent), d( new KChatDialogPrivate ) { setModal(modal); init(); } KChatDialog::~KChatDialog() { delete d; } void KChatDialog::init() { d->mTextPage = new QFrame( this ); QGridLayout* layout = new QGridLayout(d->mTextPage); setWindowTitle(i18n("Configure Chat")); buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel|QDialogButtonBox::Apply); QWidget *mainWidget = new QWidget(this); setLayout(layout); layout->addWidget(mainWidget); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &KChatDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &KChatDialog::reject); // General fonts QPushButton* nameFont = new QPushButton(i18n("Name Font..."), d->mTextPage); connect(nameFont, &QPushButton::pressed, this, &KChatDialog::slotGetNameFont); layout->addWidget(nameFont, 0, 0); QPushButton* textFont = new QPushButton(i18n("Text Font..."), d->mTextPage); connect(textFont, &QPushButton::pressed, this, &KChatDialog::slotGetTextFont); layout->addWidget(textFont, 0, 1); QFrame* messagePreview = new QFrame(d->mTextPage); messagePreview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); QHBoxLayout* messageLayout = new QHBoxLayout(messagePreview); layout->addWidget(messagePreview, 1, 0, 1, 2); d->mNamePreview = new QLabel(i18n("Player: "), messagePreview); messageLayout->addWidget(d->mNamePreview, 0); d->mTextPreview = new QLabel(i18n("This is a player message"), messagePreview); messageLayout->addWidget(d->mTextPreview, 1); layout->addItem(new QSpacerItem(0, 10), 2, 0); // System Message fonts QLabel* systemMessages = new QLabel(i18n("System Messages - Messages directly sent from the game"), d->mTextPage); layout->addWidget(systemMessages, 3, 0, 1, 2); QPushButton* systemNameFont = new QPushButton(i18n("Name Font..."), d->mTextPage); connect(systemNameFont, &QPushButton::pressed, this, &KChatDialog::slotGetSystemNameFont); layout->addWidget(systemNameFont, 4, 0); QPushButton* systemTextFont = new QPushButton(i18n("Text Font..."), d->mTextPage); connect(systemTextFont, &QPushButton::pressed, this, &KChatDialog::slotGetSystemTextFont); layout->addWidget(systemTextFont, 4, 1); QFrame* systemMessagePreview = new QFrame(d->mTextPage); systemMessagePreview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); QHBoxLayout* systemMessageLayout = new QHBoxLayout(systemMessagePreview); layout->addWidget(systemMessagePreview, 5, 0, 1, 2); d->mSystemNamePreview = new QLabel(i18n("--- Game: "), systemMessagePreview); systemMessageLayout->addWidget(d->mSystemNamePreview, 0); d->mSystemTextPreview = new QLabel(i18n("This is a system message"), systemMessagePreview); systemMessageLayout->addWidget(d->mSystemTextPreview, 1); // message count QLabel* maxMessages = new QLabel(i18n("Maximum number of messages (-1 = unlimited):"), d->mTextPage); layout->addWidget(maxMessages, 6, 0); d->mMaxMessages = new QLineEdit(d->mTextPage); d->mMaxMessages->setText(QString::number(-1)); layout->addWidget(d->mMaxMessages, 6, 1); layout->addWidget(buttonBox, 7, 0, 1, 2); connect(buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &KChatDialog::slotApply); connect(okButton, &QPushButton::clicked, this, &KChatDialog::slotOk); } void KChatDialog::slotGetNameFont() { QFont font = nameFont(); bool ok; font = QFontDialog::getFont(&ok, font, this); if (ok) setNameFont(font); } void KChatDialog::slotGetTextFont() { QFont font = textFont(); bool ok; font = QFontDialog::getFont(&ok, font, this); if (ok) setTextFont(font); } void KChatDialog::slotGetSystemNameFont() { QFont font = systemNameFont(); bool ok; font = QFontDialog::getFont(&ok, font, this); if (ok) setSystemNameFont(font); } void KChatDialog::slotGetSystemTextFont() { QFont font = systemTextFont(); bool ok; font = QFontDialog::getFont(&ok, font, this); if (ok) setSystemTextFont(font); } QFont KChatDialog::nameFont() const { return d->mNamePreview->font(); } QFont KChatDialog::textFont() const { return d->mTextPreview->font(); } QFont KChatDialog::systemNameFont() const { return d->mSystemNamePreview->font(); } QFont KChatDialog::systemTextFont() const { return d->mSystemTextPreview->font(); } void KChatDialog::plugChatWidget(KChatBase* widget, bool applyFonts) { d->mChat = widget; if (applyFonts && d->mChat) { setNameFont(d->mChat->nameFont()); setTextFont(d->mChat->messageFont()); setSystemNameFont(d->mChat->systemNameFont()); setSystemTextFont(d->mChat->systemMessageFont()); setMaxMessages(d->mChat->maxItems()); } } void KChatDialog::configureChatWidget(KChatBase* widget) { if (!widget) { return; } widget->setNameFont(nameFont()); widget->setMessageFont(textFont()); widget->setSystemNameFont(systemNameFont()); widget->setSystemMessageFont(systemTextFont()); widget->setMaxItems(maxMessages()); widget->saveConfig(); qCDebug(KFOURINLINE_LOG) << "Saved configuration"; } void KChatDialog::slotOk() { slotApply(); QDialog::accept(); } void KChatDialog::slotApply() { configureChatWidget(d->mChat); } void KChatDialog::setNameFont(const QFont &f) { d->mNamePreview->setFont(f); } void KChatDialog::setTextFont(const QFont &f) { d->mTextPreview->setFont(f); } void KChatDialog::setSystemNameFont(const QFont &f) { d->mSystemNamePreview->setFont(f); } void KChatDialog::setSystemTextFont(const QFont &f) { d->mSystemTextPreview->setFont(f); } void KChatDialog::setMaxMessages(int max) { d->mMaxMessages->setText(QString::number(max)); } int KChatDialog::maxMessages() const { bool ok; int max = d->mMaxMessages->text().toInt(&ok); if (!ok) { return -1; // unlimited is default } return max; } diff --git a/src/kfontutils.cpp b/src/kfontutils.cpp index ce3b6c7..2284569 100644 --- a/src/kfontutils.cpp +++ b/src/kfontutils.cpp @@ -1,87 +1,87 @@ /********************************************************************************* * * * Copyright (C) 2005, 2009 by Albert Astals Cid * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) version 3, or any * * later version accepted by the membership of KDE e.V. (or its * * successor approved by the membership of KDE e.V.), which shall * * act as a proxy defined in Section 6 of version 3 of the license. * * * * 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library. If not, see . * * * *********************************************************************************/ #include "kfontutils.h" #include "kfourinline_debug.h" #include #include -#include +#include qreal KFontUtils::adaptFontSize(QGraphicsTextItem* string, qreal width, qreal height, qreal maxFontSize, qreal minFontSize, qreal precision) { qreal size = maxFontSize; QRectF boundingRect; // Guard for avoiding possible infinite loop qreal lowerBound = 0; qreal higherBound = std::numeric_limits::max(); int count = 0; // Loop until found a point size that makes the text fit in the rectangle but // don't have more than precision pixels of unused space in any axys. while (count < 20) { QFont f = string->font(); f.setPointSizeF(size); string->setFont(f); string->setTextWidth(width); boundingRect = string->boundingRect(); // Error painting the text, return -1 if (boundingRect.width() == 0 || boundingRect.height() == 0) { return -1; } // Text doesn't fit in rectangle or has too much unused space, adjust size else if (boundingRect.height() > height || boundingRect.height() < height - precision) { if (boundingRect.width() > width || boundingRect.height() > height) higherBound = qMin(higherBound, size); else lowerBound = qMax(lowerBound, size); if (lowerBound > 0 && higherBound < std::numeric_limits::max()) { size = (lowerBound + higherBound) / 2; } else { size = (height / boundingRect.height()) * size; } count ++; } // Text fits correctly else { break; } } // Assure to return a value between minimum and maximum values. if (size < minFontSize) return minFontSize; else if (size > maxFontSize) return maxFontSize; else return size; } qreal KFontUtils::adaptFontSize(QGraphicsTextItem* text, const QSizeF &availableSize, qreal maxFontSize, qreal minFontSize, qreal precision) { return adaptFontSize(text, availableSize.width(), availableSize.height(), maxFontSize, minFontSize, precision); } diff --git a/src/kwin4.cpp b/src/kwin4.cpp index c6a30d5..ad7f4bb 100644 --- a/src/kwin4.cpp +++ b/src/kwin4.cpp @@ -1,905 +1,903 @@ /* This file is part of the KDE games kwin4 program Copyright (c) 1995-2007 Martin Heni This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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. */ // Header includes #include "kwin4.h" // Qt includes -#include #include #include #include #include #include #include #include #include // KDE includes #include #include #include -#include #include #include #include #include #include // KGame includes #define USE_UNSTABLE_LIBKDEGAMESPRIVATE_API #include #include "kgamedialog.h" #include "kgamedialogconfig.h" #include "kgamedebugdialog.h" // application specific includes #include "chatdlg.h" #include "kwin4doc.h" #include "kwin4view.h" #include "reflectiongraphicsscene.h" #include "prefs.h" #include "ui_settings.h" #include "ui_statistics.h" #include "kfourinline_debug.h" // Abbreviations #define ACTION(x) (actionCollection()->action(x)) #define ID_STATUS_MSG 1003 #define ID_STATUS_MOVER 1002 #define UPDATE_TIME 25 /* [ms] */ // Configuration file #include "config-src.h" // Construct the main application window KWin4App::KWin4App(QWidget *parent) : KXmlGuiWindow(parent), mTheme(), mView(), mDoc(), mScene(), mColorGroup(), mMyChatDlg(), mStatusMsg(), mStatusMover() { // default names for players (void)I18N_NOOP2("default name of first player", "Player 1"); (void)I18N_NOOP2("default name of second player", "Player 2"); // Read theme files QStringList themeList; const QString dir = QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("grafix"), QStandardPaths::LocateDirectory); const QStringList fileNames = QDir(dir).entryList(QStringList() << QStringLiteral("*.desktop")); for (const QString& file : fileNames) themeList.append(dir + QLatin1Char('/') + file); if (themeList.isEmpty()) { KMessageBox::error(this, i18n("Installation error: No theme list found.")); QTimer::singleShot(0, this,&QWidget::close); return; } // Read theme files for (int i = 0; i < themeList.size(); i++) { KConfig themeInfo( themeList.at(i), KConfig::SimpleConfig); KConfigGroup themeGroup(&themeInfo, "Theme"); QString name = themeGroup.readEntry("Name", QString()); QString file = themeGroup.readEntry("File", QString()); bool isDefault = themeGroup.readEntry("Default", false); if (mThemeDefault.isNull()) mThemeDefault = name; if (isDefault) mThemeDefault = name; mThemeFiles[name] = file; qCDebug(KFOURINLINE_LOG) << "Found theme("<findProcessName(); qCDebug(KFOURINLINE_LOG) << "Init AI" << aiEngine; if (aiEngine.isEmpty()) { KMessageBox::error(this, i18n("Installation error: No AI engine found. Continue without AI.")); } // Read properties (before GUI and thememanager but after new document) qCDebug(KFOURINLINE_LOG) << "read prop"; readProperties(); // Scene mScene = new ReflectionGraphicsScene(UPDATE_TIME, this); // Theme QString themeFile = themefileFromIdx(mThemeIndexNo); qCDebug(KFOURINLINE_LOG) << "Loading theme" << themeFile << " #"<checkTheme() != 0) { KMessageBox::error(this, i18n("Installation error: Theme file error.")); QTimer::singleShot(0, this,&QWidget::close); return; } // View mView = new KWin4View(UPDATE_TIME, QSize(800,600),mScene,mTheme,this); mDoc->setView(mView); connect(mView, &KWin4View::signalQuickStart, this, &KWin4App::quickStart); // Players (after view) qCDebug(KFOURINLINE_LOG) << "Init pl"; mDoc->initPlayers(); // Init GUI initGUI(); initStatusBar(); // Adjust GUI setCentralWidget(mView); setupGUI(); // Connect signals connectDocument(); // Read global config for document (after initPlayers) mDoc->readConfig(KSharedConfig::openConfig().data()); // Check menus checkMenus(); // Skip intro? if (global_skip_intro) { menuNewGame(); } // Start game automatically in demo mode else if (global_demo_mode) { QTimer::singleShot(11500, this,&KWin4App::menuNewGame); } } // Destruct application KWin4App::~KWin4App() { qCDebug(KFOURINLINE_LOG) << "~KWin4App()"; delete mDoc; delete mView; delete mScene; delete mTheme; delete mMyChatDlg; qCDebug(KFOURINLINE_LOG) << "~KWin4App()"; } // Called by Qt when the window is closed void KWin4App::closeEvent(QCloseEvent *event) { endGame(); saveProperties(); KXmlGuiWindow::closeEvent(event); } // Retrieve a theme file name from the menu index number QString KWin4App::themefileFromIdx(int idx) { QStringList list(mThemeFiles.keys()); list.sort(); QString themeFile = mThemeFiles[list.at(idx)]; return themeFile; } // Retrieve a theme idx from a theme name int KWin4App::themeIdxFromName(const QString &name) { QStringList list(mThemeFiles.keys()); list.sort(); for (int i=0; i < list.size(); ++i) { if (list[i] == name) return i; } qCCritical(KFOURINLINE_LOG) << "Theme index lookup failed for " << name; return 0; } // This method is called from various places // and signals to check, uncheck and enable // or disable all menu items. // The menu parameter can limit this operation // to one or more of the main menus (File,View,...) void KWin4App::checkMenus(CheckFlags menu) { bool localgame=(!mDoc->isNetwork()); bool isRunning = (mDoc->gameStatus()==KGame::Run); // Check file menu if (!menu || (menu&CheckFileMenu)) { changeAction("move_hint", !(!isRunning && localgame)); changeAction("game_new", !isRunning); changeAction("game_save", isRunning); changeAction("game_end", isRunning); } // Edit menu if (!menu || (menu&CheckEditMenu)) { if (!isRunning || !localgame) { disableAction("move_undo"); } else if (mDoc->getHistoryCnt()==0) { disableAction("move_undo"); } else if (mDoc->getCurrentMove()<1 ) { disableAction("move_undo"); } else { enableAction("move_undo"); } // Show redo if (!isRunning || !localgame) { disableAction("move_redo"); } else if (mDoc->getHistoryCnt()==mDoc->getMaxMove()) { disableAction("move_redo"); } else { enableAction("move_redo"); } } // Disable some menus in demo mode if (global_demo_mode) { disableAction(KStandardAction::name(KStandardAction::Preferences)); disableAction("move_undo"); disableAction("move_redo"); disableAction("game_new"); disableAction("game_end"); disableAction("game_save"); disableAction("game_open"); disableAction("network_conf"); disableAction("network_chat"); disableAction("statistics"); disableAction("move_hint"); } } // Create the actions for the menu. This works together with the xml guirc file void KWin4App::initGUI() { QAction* action; // Game KStandardGameAction::gameNew(this, SLOT(menuNewGame()), actionCollection()); KStandardGameAction::load(this, SLOT(menuOpenGame()), actionCollection()); KStandardGameAction::save(this, SLOT(menuSaveGame()), actionCollection()); action = KStandardGameAction::end(this, SLOT(endGame()), actionCollection()); action->setWhatsThis(i18n("Ends a currently played game. No winner will be declared.")); KStandardGameAction::hint(this, SLOT(askForHint()), actionCollection()); KStandardGameAction::quit(this, SLOT(close()), actionCollection()); action = actionCollection()->addAction( QStringLiteral( "network_conf" )); action->setText(i18n("&Network Configuration...")); connect(action, &QAction::triggered, this, &KWin4App::configureNetwork); action = actionCollection()->addAction( QStringLiteral( "network_chat" )); action->setText(i18n("Network Chat...")); connect(action, &QAction::triggered, this, &KWin4App::configureChat); action = actionCollection()->addAction( QStringLiteral( "statistics" )); action->setIcon(QIcon::fromTheme( QStringLiteral( "view-statistics" ))); action->setText(i18n("&Show Statistics")); connect(action, &QAction::triggered, this, &KWin4App::showStatistics); action->setToolTip(i18n("Show statistics.")); // Move KStandardGameAction::undo(this, SLOT(undoMove()), actionCollection()); KStandardGameAction::redo(this, SLOT(redoMove()), actionCollection()); actionCollection()->addAction(KStandardAction::Preferences, this, SLOT(configureSettings())); // Add all theme files to the menu QStringList themes(mThemeFiles.keys()); themes.sort(); KSelectAction *themeAction = new KSelectAction(i18n("Theme" ), this); actionCollection()->addAction( QStringLiteral( "theme" ) , themeAction ); themeAction->setIcon(QIcon::fromTheme( QStringLiteral( "games-config-theme" ))); themeAction->setItems(themes); connect(themeAction, static_cast(&KSelectAction::triggered), this, &KWin4App::changeTheme); qCDebug(KFOURINLINE_LOG) << "Setting current theme item to" << mThemeIndexNo; themeAction->setCurrentItem(mThemeIndexNo); // Debug if (global_debug>0) { action = actionCollection()->addAction( QStringLiteral( "file_debug" )); action->setText(i18n("Debug KGame")); connect(action, &QAction::triggered, this, &KWin4App::debugKGame); } } // Change the theme of the game void KWin4App::changeTheme(int idx) { mThemeIndexNo = idx; QString themeFile = themefileFromIdx(idx); qCDebug(KFOURINLINE_LOG) << "Select theme" << themeFile; mTheme->updateTheme(themeFile); updateStatusNames(); } // Create the status bar with the message part, the player part. void KWin4App::initStatusBar() { mStatusMsg = new QLabel(); mStatusMover = new QLabel(); statusBar()->addWidget(mStatusMsg); statusBar()->addPermanentWidget(mStatusMover); displayStatusMessage(i18n("Welcome to Four Wins")); } // Set up the document, i.e. the KGame object // and connect all signals emitted by it void KWin4App::connectDocument() { // KGame signals connect(mDoc, &KWin4Doc::signalGameOver, this, &KWin4App::slotGameOver); connect(mDoc, &KWin4Doc::signalNextPlayer, this, &KWin4App::moveDone); connect(mDoc, &KWin4Doc::signalClientLeftGame, this, &KWin4App::networkBroken); connect(mDoc, &KWin4Doc::signalGameRun, this, &KWin4App::gameRun); } // Enable or disable an action void KWin4App::changeAction(const char* action, bool enable) { if (!action) { return; } QAction* act=actionCollection()->action(QLatin1String(action)); if (act) { act->setEnabled(enable); } } // Save instance-specific properties. The function is void KWin4App::saveProperties(KConfigGroup& grp) { qCDebug(KFOURINLINE_LOG) << "SAVE PROPERTIES for GROUP" << grp.name(); // Save current game? QString name = QStringLiteral("current_game")+grp.name(); QString filename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + name; bool isRunning = (mDoc->gameStatus()==KGame::Run); if (isRunning) { qCDebug(KFOURINLINE_LOG) << "Saving" << filename; mDoc->save(filename); grp.writeEntry("CurrentGame", filename); } else { QFile file(filename); qCDebug(KFOURINLINE_LOG) << "Deleting" << file.fileName(); file.remove(); grp.deleteEntry("CurrentGame"); } } // Read instance-specific properties. void KWin4App::readProperties(const KConfigGroup& grp) { qCDebug(KFOURINLINE_LOG) << "READ PROPERTIES for GROUP" << grp.name(); QString filename = grp.readEntry("CurrentGame", QString()); qCDebug(KFOURINLINE_LOG) << "Filename is" << filename; if(!filename.isNull() && QFile::exists(filename)) { qCDebug(KFOURINLINE_LOG) << "Loading" << filename; // TODO: CRASHES mDoc->load(filename); qCDebug(KFOURINLINE_LOG) << "Loading" << filename << "done"; } } // Store the current game void KWin4App::saveProperties() { KConfig *config = KSharedConfig::openConfig().data(); // Program data KConfigGroup cfg = config->group("ProgramData"); cfg.writeEntry("ThemeIndexNo", mThemeIndexNo); mDoc->writeConfig(config); config->sync(); qCDebug(KFOURINLINE_LOG) << "SAVED PROPERTIES"; } // Load current game back void KWin4App::readProperties() { KConfig *config = KSharedConfig::openConfig().data(); // Program data KConfigGroup cfg = config->group("ProgramData"); mThemeIndexNo = cfg.readEntry("ThemeIndexNo", themeIdxFromName(mThemeDefault)); if (mThemeIndexNo >= mThemeFiles.size()) mThemeIndexNo = 0; qCDebug(KFOURINLINE_LOG) << "Index = " << mThemeIndexNo << " def index=" << themeIdxFromName(mThemeDefault); qCDebug(KFOURINLINE_LOG) << "LOADED PROPERTIES"; } // Load a game menu void KWin4App::menuOpenGame() { QString dir(QStringLiteral(":")); QString filter(QStringLiteral("*")); QString file(QStringLiteral("/tmp/kwin.save")); if (global_debug < 1) file = QFileDialog::getOpenFileName(this, QString(), dir, filter); mDoc->load(file,true); checkMenus(); } // Save game menu void KWin4App::menuSaveGame() { QString dir(QStringLiteral(":")); QString filter(QStringLiteral("*")); QString file(QStringLiteral("/tmp/kwin.save")); if (global_debug < 1) file = QFileDialog::getSaveFileName(this, QString(), dir, filter); mDoc->save(file); } // Received quick start command from view void KWin4App::quickStart(COLOUR startPlayer, KGameIO::IOMode input0, KGameIO::IOMode input1, int level) { if (startPlayer == Yellow) { Prefs::setStartcolourred(false); Prefs::setStartcolouryellow(true); } else if (startPlayer == Red) { Prefs::setStartcolourred(true); Prefs::setStartcolouryellow(false); } if (level >= 0) { Prefs::setLevel(level); } if (input0 == KGameIO::MouseIO) { Prefs::setInput0mouse(true); Prefs::setInput0key(false); Prefs::setInput0ai(false); } if (input0 == KGameIO::ProcessIO) { Prefs::setInput0mouse(false); Prefs::setInput0key(false); Prefs::setInput0ai(true); } if (input1 == KGameIO::MouseIO) { Prefs::setInput1mouse(true); Prefs::setInput1key(false); Prefs::setInput1ai(false); } if (input1 == KGameIO::ProcessIO) { Prefs::setInput1mouse(false); Prefs::setInput1key(false); Prefs::setInput1ai(true); } // Reload settings mDoc->loadSettings(); // Start game (direct call will crash as intro object will be deleted) QTimer::singleShot(0, this,&KWin4App::menuNewGame); } // Start a new game menu void KWin4App::menuNewGame() { qCDebug(KFOURINLINE_LOG) << "MENU NEW GAME"; // End the intro if it is running mDoc->setGameStatus(KWin4Doc::End); // Init the board and Clear the old game out mDoc->setGameStatus(KWin4Doc::Init); // Run it mDoc->setGameStatus(KWin4Doc::Run); // Display game status displayStatusMessage(i18n("Game running...")); } // Slot: Noticed that a new game started...update menus void KWin4App::gameRun() { updateStatusNames(); checkMenus(All); } // Abort a running game void KWin4App::endGame() { mDoc->setGameStatus(KWin4Doc::Abort); } // Menu to ask for a game hint void KWin4App::askForHint() { if (mDoc) mDoc->calculateHint(); } // Show statistics dialog void KWin4App::showStatistics() { QPointer dlg = new QDialog(this); Ui::Statistics ui; ui.setupUi(dlg); ui.p1_name->setText(mDoc->getName(Yellow)); ui.p1_won->display(mDoc->getStatistic(Yellow, TWin)); ui.p1_drawn->display(mDoc->getStatistic(Yellow, TRemis)); ui.p1_lost->display(mDoc->getStatistic(Yellow, TLost)); ui.p1_aborted->display(mDoc->getStatistic(Yellow, TBrk)); ui.p1_sum->display(mDoc->getStatistic(Yellow, TSum)); ui.p2_name->setText(mDoc->getName(Red)); ui.p2_won->display(mDoc->getStatistic(Red, TWin)); ui.p2_drawn->display(mDoc->getStatistic(Red, TRemis)); ui.p2_lost->display(mDoc->getStatistic(Red, TLost)); ui.p2_aborted->display(mDoc->getStatistic(Red, TBrk)); ui.p2_sum->display(mDoc->getStatistic(Red, TSum)); if(dlg->exec() == QDialog::Rejected) { mDoc->resetStatistic(); } delete dlg; } // Undo menu call void KWin4App::undoMove() { mDoc->undoMove(); // Undo twice if computer is moving to keep player as input if (mDoc->playedBy(mDoc->getCurrentPlayer())==KGameIO::ProcessIO) { mDoc->undoMove(); } // Refresh menus updateStatusNames(); checkMenus(CheckEditMenu); } // Redo menu call void KWin4App::redoMove() { mDoc->redoMove(); // Redo twice if computer is moving to keep player as input if (mDoc->playedBy(mDoc->getCurrentPlayer())==KGameIO::ProcessIO) { mDoc->redoMove(); } updateStatusNames(); checkMenus(CheckEditMenu); } // Set the given text into the statusbar change status message permanently void KWin4App::displayStatusMessage(const QString &text) { mStatusMsg->setText(text); } // Set the string in the statusbar window for // the player currently moving change status mover permanently void KWin4App::displayStatusbarMover(const QString& text) { mStatusMover->setText(text); } // Ends the current game. // Called by the gameover signal void KWin4App::EndGame(TABLE mode) { mDoc->endGame(mode); mDoc->switchStartPlayer(); updateStatusNames(); checkMenus(); // Automatically restart game in demo mode if (global_demo_mode) { QTimer::singleShot(10000, this,&KWin4App::menuNewGame); } } // Set the names in the mover field void KWin4App::updateStatusNames() { QString msg; if (!(mDoc->gameStatus()==KGame::Run)) msg=i18n("No game "); else if (mDoc->getCurrentPlayer()==Yellow) msg=i18n(" %1 - %2 ", mDoc->getName(Yellow), mTheme->colorNamePlayer(0)); else if (mDoc->getCurrentPlayer()) msg=i18n(" %1 - %2 ", mDoc->getName(Red), mTheme->colorNamePlayer(1)); else msg=i18n("Nobody "); displayStatusbarMover(msg); } // Notification that the network connection is lost. void KWin4App::networkBroken(int /*id*/, int oldstatus ,KGame * /*game */) { qCDebug(KFOURINLINE_LOG) << "KWin4App::networkBroken("<playedBy(Yellow)==0) mDoc->setPlayedBy(Yellow,KGameIO::MouseIO); if (mDoc->playedBy(Red)==0) mDoc->setPlayedBy(Red,KGameIO::MouseIO); qCDebug(KFOURINLINE_LOG) << "CurrrentPlayer=" << mDoc->getCurrentPlayer(); qCDebug(KFOURINLINE_LOG) << " " << mDoc->getPlayer(mDoc->getCurrentPlayer()); // Activate input device mDoc->getPlayer(mDoc->getCurrentPlayer())->setTurn(true,true); // Issue message KMessageBox::information(this,i18n("The network game ended!\n")); // Restore status mDoc->setGameStatus(oldstatus); } // A move is done. Update status display. void KWin4App::moveDone(int /*playerNumber*/) { checkMenus(CheckEditMenu); updateStatusNames(); displayStatusMessage(i18n("Game running...")); } // The game is over or aborted. Set status and display it. void KWin4App::slotGameOver(int status, KPlayer* p, KGame* /*me*/) { qCDebug(KFOURINLINE_LOG) << "KWin4App::slotGameOver"; if (status==-1) // remis { EndGame(TRemis); displayStatusMessage(i18n("The game is drawn. Please restart next round.")); } else if (status==1) // One of the players won { if (p->userId()==Yellow) EndGame(TWin); else EndGame(TLost); QString msg=i18n("%1 won the game. Please restart next round.", mDoc->getName(((COLOUR)p->userId()))); displayStatusMessage(msg); } else if (status==2) // Abort { EndGame(TBrk); QString m=i18n(" Game ended. Please restart next round."); displayStatusMessage(m); } else { qCCritical(KFOURINLINE_LOG) << "Gameover with status" << status << ". This is unexpected and a serious problem"; } checkMenus(CheckEditMenu); } // Show the network configuration dialog void KWin4App::configureNetwork() { if (mDoc->gameStatus()==KWin4Doc::Intro) { mDoc->setGameStatus(KWin4Doc::Pause); } QString host = Prefs::host(); int port=Prefs::port(); // just for testing - should be non-modal KGameDialog dlg(mDoc, nullptr, i18n("Network Configuration"), this); dlg.networkConfig()->setDefaultNetworkInfo(host, port); dlg.networkConfig()->setDiscoveryInfo(QStringLiteral("_kfourinline._tcp"),Prefs::gamename()); QWidget *box=dlg.configPage(); QLayout *l=box->layout(); mColorGroup=new QGroupBox(box); QVBoxLayout *grouplay=new QVBoxLayout(mColorGroup); connect(dlg.networkConfig(), &KGameDialogNetworkConfig::signalServerTypeChanged, this, &KWin4App::serverTypeChanged); QRadioButton *b1 = new QRadioButton(i18n("Black should be played by remote player"), mColorGroup); QRadioButton *b2 = new QRadioButton(i18n("Red should be played by remote player"), mColorGroup); grouplay->addWidget(b1); grouplay->addWidget(b2); l->addWidget(mColorGroup); b1->setChecked(true); remoteChanged(0); connect(b1, &QAbstractButton::toggled, this, [this](bool toggled) { if (toggled) remoteChanged(0); }); connect(b2, &QAbstractButton::toggled, this, [this](bool toggled) { if (toggled) remoteChanged(1); }); dlg.adjustSize(); dlg.exec();// note: we don't have to check for the result - maybe a bug } // Can't get rid of this function in KGame's current state. // Can't pass a int signal to a bool slot, so this must be here void KWin4App::serverTypeChanged(int t) { mColorGroup->setDisabled(t); } // The remote player in the network dialog has changed. Adapt priorities. void KWin4App::remoteChanged(int button) { if (button==0) { mDoc->getPlayer(Yellow)->setNetworkPriority(0); mDoc->getPlayer(Red)->setNetworkPriority(10); } else { mDoc->getPlayer(Yellow)->setNetworkPriority(10); mDoc->getPlayer(Red)->setNetworkPriority(0); } } // Show the chat dialog. void KWin4App::configureChat() { if (!mMyChatDlg) { mMyChatDlg=new ChatDlg(mDoc,this); KWin4Player *p=mDoc->getPlayer(Yellow); if (!p->isVirtual()) mMyChatDlg->setPlayer(mDoc->getPlayer(Yellow)); else mMyChatDlg->setPlayer(mDoc->getPlayer(Red)); connect(mDoc, &KWin4Doc::signalChatChanged, mMyChatDlg, &ChatDlg::setPlayer); } if (mMyChatDlg->isHidden()) mMyChatDlg->show(); else mMyChatDlg->hide(); } // Show the KGame debug window. void KWin4App::debugKGame() { KGameDebugDialog* debugWindow = new KGameDebugDialog(mDoc, this); debugWindow->show(); } // Show Configure dialog. void KWin4App::configureSettings() { static Ui::Settings ui; // Dialog is internally static anyway if(KConfigDialog::showDialog(QStringLiteral("settings"))) { // The dialog need to refresh the buttons as they are not connectable via a signal-slot // in KConfigDialog ui.kcfg_startcolourred->setChecked(Prefs::startcolourred()); ui.kcfg_startcolourred->setText(mTheme->colorNamePlayer(0)); ui.kcfg_startcolouryellow->setChecked(Prefs::startcolouryellow()); ui.kcfg_startcolouryellow->setText(mTheme->colorNamePlayer(1)); ui.kcfg_level->setValue(Prefs::level()); ui.Input0->setTitle(i18n("%1 Plays With", mTheme->colorNamePlayer(0))); ui.Input1->setTitle(i18n("%1 Plays With", mTheme->colorNamePlayer(1))); ui.kcfg_input0mouse->setChecked(Prefs::input0mouse()); ui.kcfg_input0key->setChecked(Prefs::input0key()); ui.kcfg_input0ai->setChecked(Prefs::input0ai()); ui.kcfg_input1mouse->setChecked(Prefs::input1mouse()); ui.kcfg_input1key->setChecked(Prefs::input1key()); ui.kcfg_input1ai->setChecked(Prefs::input1ai()); return; } KConfigDialog* dialog = new KConfigDialog(this, QStringLiteral("settings"), Prefs::self()); dialog->setFaceType(KPageDialog::Plain); dialog->setStandardButtons(QDialogButtonBox::Ok|QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Help); dialog->button(QDialogButtonBox::Ok)->setDefault(true); dialog->setModal(true); //QT5 dialog->setHelp(QString(),"kfourinline"); QWidget* frame = new QWidget(dialog); ui.setupUi(frame); ui.kcfg_startcolourred->setText(mTheme->colorNamePlayer(0)); ui.kcfg_startcolouryellow->setText(mTheme->colorNamePlayer(1)); ui.Input0->setTitle(i18n("%1 Plays With", mTheme->colorNamePlayer(0))); ui.Input1->setTitle(i18n("%1 Plays With", mTheme->colorNamePlayer(1))); dialog->addPage(frame, i18n("General"), QStringLiteral("games-config-options")); connect(dialog, &KConfigDialog::settingsChanged, mDoc, &KWin4Doc::loadSettings); dialog->show(); } diff --git a/src/kwin4doc.cpp b/src/kwin4doc.cpp index 3cb170f..f556e01 100644 --- a/src/kwin4doc.cpp +++ b/src/kwin4doc.cpp @@ -1,1423 +1,1421 @@ /* This file is part of the KDE games kwin4 program Copyright (c) 2006 Martin Heni This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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. */ /** Note: The AI engine of kwin4 does on purpose not implement a perfect * Connect-4(tm) engine but tries to play more human as playing against * a perfect engine is only frustrating. Connect four is proven to * always win for the first player with perfect play. * see e.g. the Valena AI Engine http://www.ce.unipr.it/~gbe/velena.html */ // Header includes #include "kwin4doc.h" // include files for Qt #include -#include #include #include #include // include files for KDE #include -#include // application specific includes #include "kfourinline_debug.h" #include "kwin4view.h" #include "scoresprite.h" #include "prefs.h" #include "score.h" #include "ui_statuswidget.h" #include "config-src.h" #define FIELD_SIZE_X 7 #define FIELD_SIZE_Y 6 // Constructor KWin4Doc::KWin4Doc(QWidget *parent) : KGame(1234,parent), pView(), mHintProcess() { mStatus = new Score(parent); connect(this, &KWin4Doc::signalPropertyChanged, this, &KWin4Doc::gamePropertyChanged); dataHandler()->Debug(); //qCDebug(KFOURINLINE_LOG) << "Property 7 policy=" << dataHandler()->find(7)->policy(); setPolicy(KGame::PolicyDirty,true); //qCDebug(KFOURINLINE_LOG) << "Property 7 policy=" << dataHandler()->find(7)->policy(); // 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,QStringLiteral("mField")); mFieldFilled.resize(7); mHistory.resize(43); mHistory.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mHistory")); mAmzug.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mAmzug")); mCurrentMove.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mCurrentMove")); mMaxMove.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mMaxMove")); mFieldFilled.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mFieldFilled")); mHistoryCnt.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mHistoryCnt")); mLastColumn.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mLastColumn")); mLastHint.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mLastHint")); mLastColour.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mLastColour")); mScore.registerData(dataHandler(),KGamePropertyBase::PolicyLocal,QStringLiteral("mScore")); // game startup parameter mStartPlayer=Yellow; mStartPlayer.registerData(dataHandler(),KGamePropertyBase::PolicyDirty,QStringLiteral("mStartPlayer")); setCurrentPlayer((COLOUR)mStartPlayer.value()); if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "amZug policy=" << mAmzug.policy(); mPlayedBy[Yellow] = KGameIO::MouseIO; mPlayedBy[Red] = KGameIO::MouseIO; // AI support mAIValues.resize(42); // last in init resetGame(false); setGameStatus(Intro); // Listen to network connect(this, &KWin4Doc::signalMessageUpdate, this, &KWin4Doc::networkMessageUpdate); connect(this, &KWin4Doc::signalClientJoinedGame, this, &KWin4Doc::clientConnected); // Change global KGame policy //dataHandler()->setPolicy(KGamePropertyBase::PolicyDirty,false); dataHandler()->Debug(); } // Destructor KWin4Doc::~KWin4Doc() { qCDebug(KFOURINLINE_LOG) << "~KWin4Doc()"; delete mHintProcess; delete mStatus; qCDebug(KFOURINLINE_LOG) << "~KWin4Doc() done"; } // Player initialization void KWin4Doc::initPlayers() { // Create yellow KWin4Player* yellow = (KWin4Player*)createPlayer(1, mPlayedBy[Yellow], false); yellow->setUserId(Yellow); yellow->setName(Prefs::name1()); addPlayer(yellow); setPlayedBy(Yellow,mPlayedBy[Yellow]); // Create Red KWin4Player* red = (KWin4Player*)createPlayer(1, mPlayedBy[Red], false); red->setUserId(Red); red->setName(Prefs::name1()); addPlayer(red); setPlayedBy(Red,mPlayedBy[Red]); } // Set the view to the doc void KWin4Doc::setView(KWin4View *view) { pView=view; connect(pView, &KWin4View::signalMoveDone, this, &KWin4Doc::moveDone); } // Returns colour on the game board COLOUR KWin4Doc::getColour(int x,int y) { return (COLOUR)mField.at(x+y*FIELD_SIZE_X); } // Set the colour on the game board void KWin4Doc::setColour(int x,int y,COLOUR c) { if (x<0 || x>=FIELD_SIZE_X || y<0 || y>=FIELD_SIZE_Y) { qCCritical(KFOURINLINE_LOG) << "ERROR: setColour on wrong position" << x << " " << y; return ; } mField.setAt(x+y*FIELD_SIZE_X,c); } // Reset the whole game (and the view) void KWin4Doc::resetGame(bool initview) { // Reset field for (int x=0;x=0;--y) { setColour(x,y,Nobody); } } mFieldFilled.fill(0); // Reset game vars mHistoryCnt=0; mCurrentMove=0; mMaxMove=0; mLastColumn=-1; mLastColour=Nobody; setScore(0); mLastHint=-1; // Reset the view if (initview) { pView->initGame(mStatus); } // Who starts this game setCurrentPlayer((COLOUR)mStartPlayer.value()); } // Set current player to setTurn true void KWin4Doc::activateCurrentPlayer() { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Setting the current player to turn"; getPlayer(getCurrentPlayer())->setTurn(true,true); } // End a game. Update statistic and forward end game to view. void KWin4Doc::endGame(TABLE mode) { setGameStatus(End); // TODO pView->clearError(); pView->endGame(); // Increase game statistics KWin4Player *yellow=getPlayer(Yellow); KWin4Player *red=getPlayer(Red); 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; } } // Indication that a move has been visually done void KWin4Doc::moveDone(int /*mode*/ ) { if (playerCount()>1) { playerInputFinished(getPlayer(getCurrentPlayer())); } // TODO pView->clearError(); } // Calculate the next players to turn. Here the players just swap. KPlayer* KWin4Doc::nextPlayer(KPlayer* last, bool /*exclusive*/) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "nextPlayer last="<id() << "admin=" << isAdmin(); // Should be enough if the admin sets the turn if (last->userId()==Yellow) setCurrentPlayer(Red); else setCurrentPlayer(Yellow); if (global_debug>1) qCDebug(KFOURINLINE_LOG) <<" Current set to "<setTurn(true,true); emit signalNextPlayer(int(getCurrentPlayer())); return getPlayer(getCurrentPlayer()); } // Performs a game move on the given x position. Just calls makeMove() // and transforms the return value so that true indicates a valid move. bool KWin4Doc::doMove(int x,int id) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) <<" KWin4Doc::Move pos="<=FIELD_SIZE_X) { qCDebug(KFOURINLINE_LOG) << "ERROR: makeMove auf falsche Position" << x; 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,Nobody); 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,getCurrentPlayer()); mHistory.setAt(getHistoryCnt(),x); mHistoryCnt=mHistoryCnt.value()+1; mLastColour=getCurrentPlayer(); //if (getCurrentPlayer()==Yellow) setCurrentPlayer(Red); //else setCurrentPlayer(Yellow); mCurrentMove=mCurrentMove.value()+1; // only if a real move isdone the maxmove is raised if (mode==0) mMaxMove=mCurrentMove.value(); mLastColumn=x; // Show graphics pView->displayMove(x, y, mLastColour, x, mLastColour, mCurrentMove-1 , mode==1?false:true); return GNormal; } // Undo a move. bool KWin4Doc::undoMove() { if (getHistoryCnt()<1) return false; if (mLastHint>=0) { int hy; hy=mFieldFilled.at(mLastHint); setColour(mLastHint,hy,Nobody); mLastHint=-1; } // qCDebug(KFOURINLINE_LOG) << "Undo no="<userId(); return checkGameOver(mLastColumn ,(COLOUR)(mLastColour.value())); } // Check whether the current game has a game over situation // return -1: remis, 1:won, 0: continue int KWin4Doc::checkGameOver(int x, COLOUR col) { int y,i; COLOUR c; int star=1; COLOUR winc=Nobody; // Check vertical up int flag=0; for (i=0;i<4;++i) { y=mFieldFilled.at(x)-1-i; if (y>=0) { c=getColour(x,y); if (c==col) ++flag; } } if (flag>=4 ) { // Store win fields for (i=0;i<4;++i) { y=mFieldFilled.at(x)-1-i; pView->displayStar(x,y,star++); winc=getColour(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=4 ) { // Store win fields y=mFieldFilled.at(x)-1; winc=getColour(x,y); int cnt=0; for (i=0;i<4;++i) { xx=x+i; if (xx>=0 && xxdisplayStar(xx,y,star++); ++cnt; } else break; } for (i=-1;i>-4 && cnt<4;--i) { xx=x+i; if (xx>=0 && xxdisplayStar(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=0 && y=4 ) { // Store win fields y=mFieldFilled.at(x)-1; winc=getColour(x,y); int cnt=0; for (i=0;i<4;++i) { xx=x+i; if (xx>=0 && xxdisplayStar(xx,y,star++); ++cnt; } else break; } for (i=-1;i>-4 && cnt<4;--i) { xx=x+i; if (xx>=0 && xx=FIELD_SIZE_Y) break; if (getColour(xx,y)!=winc) break; pView->displayStar(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=0 && y=4 ) { // Store win fields y=mFieldFilled.at(x)-1; winc=getColour(x,y); int cnt=0; for (i=0;i<4;++i) { xx=x+i; if (xx>=0 && xx=FIELD_SIZE_Y) break; if (getColour(xx,y)!=winc) break; pView->displayStar(xx,y,star++); ++cnt; } else break; } for (i=-1;i>-4 && cnt<4;--i) { xx=x+i; if (xx>=0 && xxdisplayStar(xx,y,star++); ++cnt; } else break; } return 1; } else if (flag>=4) return 1; if (mCurrentMove>=42) return -1; return 0; } // Reset all the player stats void KWin4Doc::resetStatistic() { getPlayer(Yellow)->resetStats(); getPlayer(Red)->resetStats(); } // Set computer AI score value void KWin4Doc::setScore(long value) { mScore.setValue(value); } // Load settings from Prefs void KWin4Doc::loadSettings() { qCDebug(KFOURINLINE_LOG) << "++++ KWin4Doc::loadSettings() "; qCDebug(KFOURINLINE_LOG) << "Level:" << Prefs::level(); qCDebug(KFOURINLINE_LOG) << "Name:" << Prefs::name1(); qCDebug(KFOURINLINE_LOG) << "Name2:" << Prefs::name2(); qCDebug(KFOURINLINE_LOG) << "input0mouse:" << Prefs::input0mouse(); qCDebug(KFOURINLINE_LOG) << "input0key:" << Prefs::input0key(); qCDebug(KFOURINLINE_LOG) << "input0ai:" << Prefs::input0ai(); qCDebug(KFOURINLINE_LOG) << "input1mouse:" << Prefs::input1mouse(); qCDebug(KFOURINLINE_LOG) << "input1key:" << Prefs::input1key(); qCDebug(KFOURINLINE_LOG) << "input1ai:" << Prefs::input1ai(); qCDebug(KFOURINLINE_LOG) << "start red:" << Prefs::startcolourred(); qCDebug(KFOURINLINE_LOG) << "start yellow" << Prefs::startcolouryellow(); qCDebug(KFOURINLINE_LOG) << "Learning " << Prefs::learning(); qCDebug(KFOURINLINE_LOG) << "Lock " << Prefs::ailock(); // Store level for score sprite display mStatus->setLevel(Prefs::level(), 0); mStatus->setLevel(Prefs::level(), 1); setName(Yellow, Prefs::name1()); setName(Red, Prefs::name2()); KGameIO::IOMode mode = KGameIO::MouseIO; if(Prefs::input0mouse()) mode = KGameIO::MouseIO; else if(Prefs::input0key()) mode = KGameIO::KeyIO; else if(Prefs::input0ai()) mode = KGameIO::ProcessIO; else qCCritical(KFOURINLINE_LOG) << "Unknown input device for player 0"; if (global_demo_mode) mode = KGameIO::ProcessIO; setPlayedBy(Yellow, mode); qCDebug(KFOURINLINE_LOG) << "Played by Yellow="<group("YellowPlayer"); getPlayer(Yellow)->readConfig(ygrp); KConfigGroup rgrp = config->group("RedPlayer"); getPlayer(Red)->readConfig(rgrp); } // Write config file void KWin4Doc::writeConfig(KConfig *config) { KConfigGroup ygrp = config->group("YellowPlayer"); getPlayer(Yellow)->writeConfig(ygrp); KConfigGroup rgrp = config->group("RedPlayer"); getPlayer(Red)->writeConfig(rgrp); config->sync(); } // Returns the current player, resp amzug. COLOUR KWin4Doc::getCurrentPlayer() { return (COLOUR)mAmzug.value(); } // Set the current player void KWin4Doc::setCurrentPlayer(COLOUR no) { mAmzug.setValue(no); } // Switch the starting player and return the new started COLOUR KWin4Doc::switchStartPlayer() { if (mStartPlayer.value()==Yellow) { mStartPlayer.setValue(Red); Prefs::setStartcolouryellow(false); Prefs::setStartcolourred(true); qCDebug(KFOURINLINE_LOG) << "Setting startplayer to RED"; } else { mStartPlayer.setValue(Yellow); Prefs::setStartcolouryellow(true); Prefs::setStartcolourred(false); qCDebug(KFOURINLINE_LOG) << "Setting startplayer to YELLOW"; } Prefs::self()->save(); return (COLOUR)mStartPlayer.value(); } // Retrieve the current move number. int KWin4Doc::getCurrentMove() { return mCurrentMove; } // Retrieve the maximum move number before undo int KWin4Doc::getMaxMove() { return mMaxMove; } // Retrieve the amount of history moves stored int KWin4Doc::getHistoryCnt() { return mHistoryCnt; } // Return the filename of the computer player AI process. QString KWin4Doc::findProcessName() { // Try whether we run from a development source dir #ifndef NDEBUG #ifdef SRC_DIR QString srcname = QStringLiteral(SRC_DIR)+QStringLiteral("/src/kfourinlineproc"); QFile fsrc(srcname); if (fsrc.exists()) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Found SRC_DIR process" << srcname; return srcname; } #endif #endif // First try a local dir override QDir dir; // TODO: This local filename is not found!! QString filename=dir.path()+QStringLiteral("/kwin4/kfourinlineproc"); qCDebug(KFOURINLINE_LOG) << "PROC FILENAME="<1) qCDebug(KFOURINLINE_LOG) << "Found local process" << filename; return filename; } QString path= QStandardPaths::findExecutable(QStringLiteral("kfourinlineproc")); if (!path.isNull()) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Found system process" << path; return path; } QString empty; qCCritical(KFOURINLINE_LOG) << "Could not locate the computer player"; return empty; } // Debug: Listen to messages void KWin4Doc::networkMessageUpdate(int /*id*/,quint32 /*sender*/,quint32 /*recv*/) { // qCDebug(KFOURINLINE_LOG) << "MSG: id=" << id << "sender=" << sender << "receiver="<setStatus(mStatus); return player; } // Called when a player input is received from the KGame object // this is e.g. a mouse event, the AI or the network bool KWin4Doc::playerInput(QDataStream& msg, KPlayer* /*player*/) { qint32 move, pl; msg >> pl >> move; qCDebug(KFOURINLINE_LOG) << "KWin4Doc::playerInput: ================ pl="<setTurn(true); } // Query the IO mode of player og the given color. KGameIO::IOMode KWin4Doc::playedBy(int col) { return mPlayedBy[col]; } // Sets the input device mode for the given player color. void KWin4Doc::setPlayedBy(int col, KGameIO::IOMode io) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << " KWin4Doc::setPlayedBy(int "<status()->setPlayedBy((int)io,player->userId()); if (mPlayedBy[col]!=io && !player->isVirtual()) { bool myTurn = player->myTurn(); player->setTurn(false); // turn of move mPlayedBy[col]=io; player->removeGameIO(); // remove all IO's createIO(player,io); player->setTurn(myTurn); // turn on move } } // Get the io values right after a load game as the io the playedby // is not set there. void KWin4Doc::recalcIO() { mPlayedBy[Yellow]=(KGameIO::IOMode)getPlayer(Yellow)->calcIOValue(); mPlayedBy[Red]=(KGameIO::IOMode)getPlayer(Red)->calcIOValue(); } // Create player input devicea (KGame) void KWin4Doc::createIO(KPlayer* player, KGameIO::IOMode io) { if (!player) return; if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "KWin4Doc::createIO(KPlayer *player("<userId()<<"),KGameIO::IOMode "<1) qCDebug(KFOURINLINE_LOG) << "Creating MOUSE IO to "<viewport(), true); if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "MOUSE IO added"; // Connect mouse input to a function to process the actual input connect(input, &KGameMouseIO::signalMouseEvent, pView, &KWin4View::mouseInput); player->addGameIO(input); } else if (io&KGameIO::ProcessIO) { QString file=findProcessName(); if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Creating PROCESS IO" << file; KGameProcessIO *input; // We want a computer player input=new KGameProcessIO(file); // Connect computer player to the setTurn connect(input, &KGameProcessIO::signalPrepareTurn, this, &KWin4Doc::prepareAITurn); connect(input, &KGameProcessIO::signalProcessQuery, this, &KWin4Doc::processAICommand); connect(input, &KGameProcessIO::signalReceivedStderr, this, &KWin4Doc::receivedStderr); player->addGameIO(input); } else if (io&KGameIO::KeyIO) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Creating KEYBOARD IO"; // 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,&KGameKeyIO::signalKeyEvent, pView,&KWin4View::keyInput); player->addGameIO(input); } } void KWin4Doc::receivedStderr(const QString &s) { if (global_debug>0) qCDebug(KFOURINLINE_LOG) << "##### AI:" << s; } // This slot is called when a computer move should be generated void KWin4Doc::prepareAITurn(QDataStream& stream, bool b, KGameIO* input, bool* sendit) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "KWin4Doc::prepareAITurn b="<player(); if (!player->myTurn()) return ; if (!b) return ; // only create move on setTurn(true) qint32 pl; if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "slotPrepareComputerTurn for player id=" << player->id(); pl=player->userId(); // Pack the game into the message prepareGameMessage(stream,pl); // Do send *sendit=true; } // Sends the current game status to the computer player // Careful: The data needs to be exactly the same as the computer // player reading on the other side void KWin4Doc::prepareGameMessage(QDataStream& stream, qint32 pl) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << " sending col=" << pl; stream << pl ; // This needs to be the same than the computer player reads! stream << (qint32)getCurrentMove(); stream << (qint32)getCurrentPlayer(); stream << (qint32)getPlayerColour(0); stream << (qint32)getPlayerColour(1); stream << (qint32)Prefs::level(); bool learning = Prefs::learning(); // Allow learning only for one AI if ( mPlayedBy[Yellow] == KGameIO::ProcessIO && mPlayedBy[Red] == KGameIO::ProcessIO && pl == Yellow) learning = false; stream << (qint32)learning; // Where to save the learn cache QString learnPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + QStringLiteral("kwin4"); stream << learnPath; int i,j; for (i=0;i1) qCDebug(KFOURINLINE_LOG) << getColour(0,i) << " " << getColour(1,i) << " " << getColour(2,i) << " " << getColour(3,i) << " " << getColour(4,i) << " " << getColour(5,i) << " " << getColour(6,i); } stream << (qint32)421256; } // The AI send a command, e.g. the game value to us void KWin4Doc::processAICommand(QDataStream& in, KGameProcessIO* io) { qint8 cid; // Receive command in >> cid; switch(cid) { case 1: // game value { AIBoard aiBoard; qint32 value, moveNo, level; in >> value >> moveNo >> level >> aiBoard; if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "#### Computer thinks move" << moveNo << "value is" << value; // Store AI data mAIValues[moveNo] = value; setScore(value); // Check AI mistakes if (moveNo>=2) { long delta = mAIValues[moveNo]-mAIValues[moveNo-2]; // Send game to process QByteArray buffer; QDataStream outstream(&buffer, QIODevice::WriteOnly); // Send last board to learn with current value outstream << aiBoard << value << (qint32)delta << level; io->sendMessage(outstream, 3, 0, gameId()); } } break; default: qCCritical(KFOURINLINE_LOG) << "KWin4Doc::processAICommand: Unknown id" << cid; 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::clientConnected(quint32 cid, KGame* /* me */) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "void KWin4Doc::clientConnected id="<count()!=2) { qCCritical(KFOURINLINE_LOG) << "SERIOUS ERROR: We do not have two players...Trying to disconnect!"; 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) qCDebug(KFOURINLINE_LOG) << "CHAT to player 0"; } else { emit signalChatChanged(p2); if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "CHAT to player 1"; } // Now check whose turn it is. The Admin will rule this if (isAdmin()) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "WE are ADMIN == COOL ! "; // p1 is local if (!p1->isVirtual()) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "p1 id=" << p1->userId() << "is local turn="<myTurn(); // Exclusive setting of the turn p1->setTurn(p1->myTurn(),true); p2->setTurn(!p1->myTurn(),true); } else if (!p2->isVirtual()) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "p2 id=" << p2->userId() << "is local turn="<myTurn(); // 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(COLOUR col) { for (KGamePlayerList::const_iterator it = playerList()->constBegin(); it!= playerList()->constEnd(); ++it ) { if ((*it)->userId()==col) return (KWin4Player *)(*it); } qCCritical(KFOURINLINE_LOG) << "SERIOUS ERROR: Cannot find player with colour" << col << ". CRASH imminent"; return nullptr; } // We create a process which calculates a computer move which is shown as hint to the player. void KWin4Doc::calculateHint() { // We allocate the hint process only if it is needed if (!mHintProcess) { QString file = findProcessName(); if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Creating HINT PROCESS"; // We want a computer player mHintProcess=new KGameProcessIO(file); connect(mHintProcess, &KGameProcessIO::signalProcessQuery, this, &KWin4Doc::processAIHintCommand); } // Send game to process qint32 pl; QByteArray buffer; QDataStream stream(&buffer, QIODevice::WriteOnly); pl=getCurrentPlayer(); prepareGameMessage(stream, pl); mHintProcess->sendMessage(stream, 2, 0, gameId()); } // The compute process sent a hint which we show in the game board. void KWin4Doc::processAIHintCommand(QDataStream& in,KGameProcessIO* /*io*/) { qint8 cid; // Read command in >> cid; switch(cid) { case 2: // Hint { qint32 pl; qint32 move; qint32 value; // Read parameters of command in >> pl >> move >> value; if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "#### Computer thinks pl=" << pl << "move =" << move; if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "#### Computer thinks hint is" << move << "and value is" << value; // Display hint int x = move; int y = mFieldFilled.at(x); pView->displayHint(x,y); } break; default: qCCritical(KFOURINLINE_LOG) << "KWin4Doc::processAIHintCommand: Unknown id" << cid; 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::playerPropertyChanged(KGamePropertyBase* prop,KPlayer* player) { if (!pView) return ; // Check for name changes if (prop->id()==KGamePropertyBase::IdName) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Player name id=" << player->userId() << "changed to" << player->name(); mStatus->setPlayerName(player->name(),player->userId()); } } // Called by KGame when a game property has changed. void KWin4Doc::gamePropertyChanged(KGamePropertyBase* prop, KGame* /* me */) { if (!pView) return; // Move number if (prop->id()==mCurrentMove.id()) { // TODO pView->scoreWidget()->setMove(mCurrentMove); } // Computer AI value 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; // TODO pView->scoreWidget()->setChance(sc); } // Whose turn is it else if (prop->id()==mAmzug.id()) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Amzug changed to" << mAmzug.value(); mStatus->setTurn(mAmzug); } // The game status else if (prop->id()==KGamePropertyBase::IdGameStatus) { if (gameStatus()==Abort) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "PropertyChanged::status signal game abort +++"; emit signalGameOver(2,getPlayer(getCurrentPlayer()),this); // 2 indicates Abort } else if (gameStatus()==Run) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "PropertyChanged::status signal game run +++"; if (playerList()->count()==2) { activateCurrentPlayer(); // Set the current player to play emit signalGameRun(); } if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "PropertyChanged::status signal game run done +++"; } else if (gameStatus()==Init) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "PropertyChanged::status signal game INIT +++"; resetGame(true); } else if (gameStatus()==End) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "PropertyChanged::status signal game END +++"; } else { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "PropertyChanged::other status signal +++"; } } } // This is an overwritten function of KGame which is called // when a game is loaded. This can either be via a network // connect or via a real load from file bool KWin4Doc::loadgame(QDataStream &stream,bool network,bool reset) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "loadgame() network=" << network << "reset="<< reset; if (!network) setGameStatus(End); // Clear out the old game if (global_debug>1) qCDebug(KFOURINLINE_LOG)<<"loadgame wants to reset the game"; resetGame(true); // load the new game bool res=KGame::loadgame(stream,network,reset); if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "amzug loaded to ="<1) qCDebug(KFOURINLINE_LOG) << "REDRAW GAME using undo/redo"; if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "history cnt="<1) qCDebug(KFOURINLINE_LOG) << "amzug ="<1) qCDebug(KFOURINLINE_LOG) << "Undoing move "<1) qCDebug(KFOURINLINE_LOG) << "amzug ="<0) { redoMove(); --cnt; if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Redoing move "<1) qCDebug(KFOURINLINE_LOG) << "amzug ="<1) qCDebug(KFOURINLINE_LOG) << "loadgame done +++"; 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,QList &inactivate) { if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "newPlayersJoin: START"; KWin4Player *yellow=getPlayer(Yellow); KWin4Player *red=getPlayer(Red); // Take the master player with the higher priority. Priority is set // by the network dialog if (yellow->networkPriority()>red->networkPriority()) { // Deactivate the lower one inactivate.append(red->id()); if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "ADMIN keeps yellow and kicks red=" << red->id()<<" userId/col="<userId(); // loop all client players and deactivate the one which have the color // yellow for ( KGamePlayerList::const_iterator it = newList->constBegin(); it != newList->constEnd(); ++it ) { KPlayer *player = *it; if (player->userId()==yellow->userId()) { inactivate.append(player->id()); if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Deactivate C1" << player->id()<<" col="<userId(); } } } else { // Deactivate the lower one inactivate.append(yellow->id()); if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "ADMIN keeps red and kicks yellow=" << yellow->id()<<" userId/col="<userId(); // loop all client players and deactivate the one which have the color // red for ( KGamePlayerList::const_iterator it = newList->constBegin(); it != newList->constEnd(); ++it ) { KPlayer *player = *it; if (player->userId()==red->userId()) { inactivate.append(player->id()); if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "Deactivate C2" << player->id()<<" col="<userId(); } } } if (global_debug>1) qCDebug(KFOURINLINE_LOG) << "newPlayersJoin: DONE"; }