diff --git a/src/displayintro.cpp b/src/displayintro.cpp index 12d1c13..5a161ab 100644 --- a/src/displayintro.cpp +++ b/src/displayintro.cpp @@ -1,620 +1,621 @@ /* 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 "displayintro.h" // Local includes #include "introsprite.h" #include "pixmapsprite.h" #include "buttonsprite.h" #include "prefs.h" #include "kfontutils.h" #include "kfourinline_debug.h" // Standard includes #include // Qt includes #include #include #include #include #include #include #include #include #include #include +#include // KDE includes #include // Constructor for the intro display DisplayIntro::DisplayIntro(QGraphicsScene* scene, ThemeManager* theme, QGraphicsView* parent) : Themeable(QStringLiteral("introdisplay"),theme), QObject(parent) { // Choose a background color scene->setBackgroundBrush(QColor(0,0,128)); // Store the theme manager and other attributes mLastMoveEvent = nullptr; mTheme = theme; mScene = scene; mView = parent; // Storage of all sprites mSprites.clear(); // Create all sprites used for intro for (int i=0; i<42; i++) { IntroSprite* sprite = new IntroSprite(QStringLiteral("intro_piece"), mTheme, i, mScene); mSprites.append(sprite); if ((i/1)%2==0) sprite->setFrame(0); else sprite->setFrame(1); sprite->setZValue(i); sprite->hide(); } // Create board PixmapSprite* pixmap = new PixmapSprite(QStringLiteral("introboard"), mTheme, 0, mScene); mSprites.append(pixmap); pixmap->show(); // Create quicklaunch. We align text horizontally using QTextDocument mQuickLaunch = new PixmapSprite(QStringLiteral("quicklaunch"), mTheme, 0, mScene); mSprites.append(mQuickLaunch); mQuickLaunch->show(); mTextQuicklaunch = new QGraphicsTextItem(mQuickLaunch); scene->addItem(mTextQuicklaunch); mTextQuicklaunch->setPlainText(i18nc("Name of quicklaunch field", "Quick Launch")); QTextDocument* text_document = mTextQuicklaunch->document(); text_document->setDefaultTextOption(QTextOption(Qt::AlignHCenter)); mTextQuicklaunch->setDocument(text_document); mTextQuicklaunch->show(); mTextStartplayer = new QGraphicsTextItem(mQuickLaunch); scene->addItem(mTextStartplayer); mTextStartplayer->setPlainText(i18nc("Ask player who should start game", "Who starts?")); mTextStartplayer->show(); mTextColor = new QGraphicsTextItem(mQuickLaunch); scene->addItem(mTextColor); mTextColor->setPlainText(i18nc("Ask player which color he wants to play", "Your color?")); mTextColor->show(); // Static decoration KConfigGroup config = thememanager()->config(id()); QStringList deco = config.readEntry("decoration", QStringList()); for (int i = 0; i < deco.size(); i++) { PixmapSprite* sprite = new PixmapSprite(deco.at(i), mTheme, i, mScene); mSprites.append(sprite); sprite->show(); } // Color buttons mStartButton[0] = new ButtonSprite(false, QStringLiteral("button0_start"), mTheme, 0, mScene); mSprites.append(mStartButton[0]); mStartButton[0]->show(); connect(mStartButton[0]->notify(),&SpriteNotify::signalNotify, this,&DisplayIntro::buttonPressed); if (Prefs::startcolouryellow()) mStartButton[0]->setStatus(true); mStartButton[1] = new ButtonSprite(false, QStringLiteral("button1_start"), mTheme, 1, mScene); mSprites.append(mStartButton[1]); mStartButton[1]->show(); connect(mStartButton[1]->notify(),&SpriteNotify::signalNotify, this,&DisplayIntro::buttonPressed); if (Prefs::startcolourred()) mStartButton[1]->setStatus(true); mPlayerButton[0] = new ButtonSprite(false, QStringLiteral("button0_color"), mTheme, 2, mScene); mSprites.append(mPlayerButton[0]); mPlayerButton[0]->show(); connect(mPlayerButton[0]->notify(),&SpriteNotify::signalNotify, this,&DisplayIntro::buttonPressed); if (Prefs::input0mouse()|| Prefs::input0key()) mPlayerButton[0]->setStatus(true); mPlayerButton[1] = new ButtonSprite(false, QStringLiteral("button1_color"), mTheme, 3, mScene); mSprites.append(mPlayerButton[1]); mPlayerButton[1]->show(); connect(mPlayerButton[1]->notify(),&SpriteNotify::signalNotify, this,&DisplayIntro::buttonPressed); if ((Prefs::input1mouse()|| Prefs::input1key()) && !mPlayerButton[0]->status()) mPlayerButton[0]->setStatus(true); // Start game buttons ButtonSprite* button = new ButtonSprite(true, QStringLiteral("button_aieasy"), mTheme, 10, mScene); mSprites.append(button); button->setText(i18nc("quick start button - player versus AI level easy", "Easy Game")); button->show(); connect(button->notify(),&SpriteNotify::signalNotify, this,&DisplayIntro::buttonPressed); button = new ButtonSprite(true, QStringLiteral("button_ainormal"), mTheme, 11, mScene); mSprites.append(button); button->setText(i18nc("quick start button - player versus AI level normal", "Normal Game")); button->show(); connect(button->notify(),&SpriteNotify::signalNotify, this,&DisplayIntro::buttonPressed); button = new ButtonSprite(true, QStringLiteral("button_aihard"), mTheme, 12, mScene); mSprites.append(button); button->setText(i18nc("quick start button - player versus AI level hard", "Hard Game")); button->show(); connect(button->notify(),&SpriteNotify::signalNotify, this,&DisplayIntro::buttonPressed); button = new ButtonSprite(true, QStringLiteral("button_player"), mTheme, 13, mScene); mSprites.append(button); button->setText(i18nc("quick start button - player versus player", "Two Player Game")); button->show(); connect(button->notify(),&SpriteNotify::signalNotify, this,&DisplayIntro::buttonPressed); // Animation timer mTimer = new QTimer(this); connect(mTimer, &QTimer::timeout, this, &DisplayIntro::advance); // Redraw if (theme) theme->updateTheme(this); qCDebug(KFOURINLINE_LOG) << "CONSTRUCTOR DONE"; } // One of the graphical sprite buttons was pressed. Item and its id are delivered void DisplayIntro::buttonPressed(QGraphicsItem* item, int id) { int status = id >> 8; int no = id & 0xff; //qCDebug(KFOURINLINE_LOG) << "Button" << no << "pressed status="<(item); Q_ASSERT(button); if (button == mStartButton[0]) { mStartButton[1]->setStatus(!button->status()); Prefs::setStartcolouryellow(button->status()); Prefs::setStartcolourred(!button->status()); } if (button == mStartButton[1]) { mStartButton[0]->setStatus(!button->status()); Prefs::setStartcolouryellow(!button->status()); Prefs::setStartcolourred(button->status()); } if (button == mPlayerButton[0]) { mPlayerButton[1]->setStatus(!button->status()); Prefs::setStartcolouryellow(button->status()); Prefs::setStartcolourred(!button->status()); } if (button == mPlayerButton[1]) { mPlayerButton[0]->setStatus(!button->status()); Prefs::setStartcolouryellow(!button->status()); Prefs::setStartcolourred(button->status()); } // Start game from button if (no >=10 && no <= 13 && status == 1) { // Emit quick start with status (start player color, player color, AI level, two player? // Color COLOUR startPlayer; if (mStartButton[0]->status()) startPlayer = Yellow; else startPlayer = Red; // Inputs KGameIO::IOMode input0 = KGameIO::ProcessIO; KGameIO::IOMode input1 = KGameIO::ProcessIO; if (mPlayerButton[0]->status()) input0 = KGameIO::MouseIO; if (mPlayerButton[1]->status()) input1 = KGameIO::MouseIO; // Level int level = -1; if (no == 10) level = 2; else if (no == 11) level = 4; else if (no == 12) level = 6; if (no == 13) {input0=KGameIO::MouseIO;input1=KGameIO::MouseIO;} // Emit signal emit signalQuickStart(startPlayer, input0, input1, level); } } // Desctruct the intro and all sprites DisplayIntro::~DisplayIntro() { delete mTimer; qDeleteAll(mSprites); } // Master theme change function. Redraw the display void DisplayIntro::changeTheme() { qCDebug(KFOURINLINE_LOG) << "THEME CHANGE " << thememanager()->themefileChanged(); // Measure time for resize - QTime time; + QElapsedTimer time; time.restart(); // Retrieve theme data KConfigGroup config = thememanager()->config(id()); // Retrieve background pixmap QString bgsvgid = config.readEntry("background-svgid"); QPixmap pixmap = thememanager()->getPixmap(bgsvgid, mScene->sceneRect().size().toSize()); mScene->setBackgroundBrush(pixmap); mView->update(); // Process quicklaunch sprite (could be own class...) double width = mQuickLaunch->boundingRect().width(); double height = mQuickLaunch->boundingRect().height(); // Retrieve theme data config = thememanager()->config(mQuickLaunch->id()); QPointF posQuickstart = config.readEntry("posQuickstart", QPointF(1.0,1.0)); QPointF posStartplayer = config.readEntry("posStartplayer", QPointF(1.0,1.0)); QPointF posColor = config.readEntry("posColor", QPointF(1.0,1.0)); // HEADER // Calculate proper font size double fontHeight = config.readEntry("fontHeightHeader", 1.0); fontHeight *= height; double textHeight = config.readEntry("textHeightHeader", 1.0); textHeight *= height; double textWidth = config.readEntry("textWidthHeader", 1.0); textWidth *= width; // Retrieve font color QColor fontColor; fontColor = config.readEntry("fontColorHeader", QColor(Qt::white)); // Create and set current font QFont font; fontHeight = KFontUtils::adaptFontSize(mTextQuicklaunch, textWidth, textHeight, fontHeight, 8.0); font.setPointSizeF(fontHeight); // Set font and color for all text items mTextQuicklaunch->setFont(font); mTextQuicklaunch->setDefaultTextColor(fontColor); mTextQuicklaunch->setTextWidth(textWidth); // Set position of sub sprites, we centered horizontally at creation time, // now we center it vertically QRectF bounding = mTextQuicklaunch->boundingRect(); mTextQuicklaunch->setPos(posQuickstart.x() * width, posQuickstart.y() * height + (textHeight - bounding.height()) / 2); // TEXT // Calculate proper font size fontHeight = config.readEntry("fontHeight", 1.0); fontHeight *= height; textHeight = config.readEntry("textHeight", 1.0); textHeight *= height; textWidth = config.readEntry("textWidth", 1.0); textWidth *= width; // Retrieve font color fontColor = config.readEntry("fontColor", QColor(Qt::white)); // Create and set current font // Calculate proper font point size to not wrap translations. double newFontHeight0 = KFontUtils::adaptFontSize(mTextStartplayer, textWidth, textHeight, fontHeight, 8.0); double newFontHeight1 = KFontUtils::adaptFontSize(mTextColor, textWidth, textHeight, fontHeight, 8.0); font.setPointSizeF(qMin(newFontHeight0, newFontHeight1)); // Set font and color for all text items mTextStartplayer->setFont(font); mTextStartplayer->setDefaultTextColor(fontColor); mTextStartplayer->setTextWidth(textWidth); mTextColor->setFont(font); mTextColor->setDefaultTextColor(fontColor); mTextColor->setTextWidth(textWidth); // Set position of sub sprites, we centered horizontally at creation time, // now we center it vertically bounding = mTextStartplayer->boundingRect(); mTextStartplayer->setPos(posStartplayer.x() * width, posStartplayer.y() * height + (textHeight - bounding.height()) / 2); bounding = mTextColor->boundingRect(); mTextColor->setPos(posColor.x() * width, posColor.y() * height + (textHeight - bounding.height()) / 2); int elapsed = time.elapsed(); qCDebug(KFOURINLINE_LOG) << "THEME CHANGE took " << elapsed << " ms"; // Renew animation on theme change? if (thememanager()->themefileChanged()) { start(0); } else { delaySprites(elapsed); } } // Start the animation void DisplayIntro::start(int delay) { qCDebug(KFOURINLINE_LOG) << "START TIMER"; // Do the timer mTimer->stop(); mTimer->setSingleShot(true); mTimer->start(delay); } // Delay all sprite animation by the given time void DisplayIntro::delaySprites(int duration) { // Setup sprites for (int i = 0; i < mSprites.size(); ++i) { // Use only intro sprites if (mSprites.at(i)->type() != QGraphicsItem::UserType+1) continue; IntroSprite* sprite = (IntroSprite*)mSprites.at(i); sprite->delayAnimation(duration); } } // Animation main routine to advance the animation. Called // by a timer void DisplayIntro::advance() { - QTime time; + QElapsedTimer time; time.restart(); int duration = createAnimation(true); qCDebug(KFOURINLINE_LOG) << "ADVANCE: Restarting timer in " << (duration+5000) <<" creation time " << time.elapsed(); mTimer->start(duration + 5000); // [ms] } // Animation main routine to advance the animation. Called // by a timer int DisplayIntro::createAnimation(bool restartTime) { // Columns for the moves in the intro. Results in a drawn game // Note: Once could load the last played game here or so. static int moves[] = { 3,4,4,3,3,4,4, 1,3,5,3,3,6,6, 6,6,6,0,6,4,5, 5,2,2,5,5,1,4, 5,0,0,1,1,1,2, 2,2,2,1,0,0,0 }; // How many moves to perform (size of moves array) static int maxMoves = 42; // Config data KConfigGroup config = thememanager()->config(id()); QPointF piece0_pos = config.readEntry("piece0-pos", QPointF(1.0,1.0)); QPointF piece1_pos = config.readEntry("piece1-pos", QPointF(1.0,1.0)); double piece_spread = config.readEntry("piece-spread", 0.1); QPointF board_pos = config.readEntry("board-pos", QPointF(1.0,1.0)); QPointF board_spread = config.readEntry("board-spread", QPointF(1.0,1.0)); double move_velocity = config.readEntry("move-velocity", 0.1); // Local variables double dura, delay, rad; QPointF start, end; int maxDuration = 0; int addWaitTime = 0; // ============================================================================ // Setup sprites for (int i = 0; i < mSprites.size(); ++i) { // Use only intro sprites if (mSprites.at(i)->type() != QGraphicsItem::UserType+1) continue; IntroSprite* sprite = (IntroSprite*)mSprites.at(i); int no = sprite->number(); sprite->setZValue(no); sprite->hide(); sprite->clearAnimation(restartTime); } // ============================================================================ // First part of intro animation. Move sprites into window // Loop all sprites for (int i = 0; i < mSprites.size(); ++i) { // Move only intro sprites if (mSprites.at(i)->type() != QGraphicsItem::UserType+1) continue; IntroSprite* sprite = (IntroSprite*)mSprites.at(i); int no = sprite->number(); { if (no%2==0) { start = QPointF(1.05, 0.5); end = QPointF(piece0_pos.x() + piece_spread*no, piece0_pos.y()); dura = 3000.0; delay = 80.0*no; rad = 0.1; } else { start = QPointF(-0.05, 0.5); end = QPointF(piece1_pos.x() + piece_spread*(no-1), piece1_pos.y()); dura = 3000.0; delay = 80.0*(no-1); rad = 0.1; } sprite->addPosition(start); sprite->addShow(); sprite->addPause(int(delay)); sprite->addLinear(start, (start+end)*0.5, int(dura/4.0)); sprite->addCircle((start+end)*0.5, rad, int(dura/2.0)); sprite->addLinear((start+end)*0.5, end, int(dura/4.0)); if (sprite->animationDuration() > maxDuration) maxDuration = sprite->animationDuration(); } }// end list loop // ============================================================================ // ============================================================================ // Second part of intro animation. Move sprites inwards int moveCnt = 0; delay = 0.0; // Reset height int height[7]; addWaitTime = maxDuration; for (int i=0; i<=6; i++) height[i] = 6; // Loop all sprites for (int i = mSprites.size()-1; i >= 0 ; --i) { // Move only intro sprites if (mSprites.at(i)->type() != QGraphicsItem::UserType+1) continue; IntroSprite* sprite = (IntroSprite*)mSprites.at(i); // No more moves left if (moveCnt >= maxMoves) continue; int ix = moves[moveCnt]; moveCnt++; int iy = height[ix]; height[ix]--; double x = board_pos.x() + ix*board_spread.x(); double y = board_pos.y() + iy*board_spread.y(); sprite->addWait(addWaitTime); sprite->addPause(3000); sprite->addPause(int(delay)); AnimationCommand* anim = sprite->addRelativeManhatten(QPointF(x, y), move_velocity); delay += sprite->duration(anim); if (sprite->animationDuration() > maxDuration) maxDuration = sprite->animationDuration(); }// end for // ============================================================================ // ============================================================================ // Third part of intro animation. Move sprites outwards addWaitTime = maxDuration; // Loop all sprites for (int i = 0; i < mSprites.size(); ++i) { // Move only intro sprites if (mSprites.at(i)->type() != QGraphicsItem::UserType+1) continue; IntroSprite* sprite = (IntroSprite*)mSprites.at(i); int no = sprite->number(); double xc = board_pos.x() + 3.0*board_spread.x(); double yc = board_pos.y() + 3.5*board_spread.y(); double x = xc + 1.50*cos(no/42.0*2.0*M_PI); double y = yc + 1.50*sin(no/42.0*2.0*M_PI); sprite->addWait(addWaitTime); sprite->addPause(8000); sprite->addRelativeLinear(QPointF(x,y), 800); if (sprite->animationDuration() > maxDuration) maxDuration = sprite->animationDuration(); }// end for // ============================================================================ return maxDuration; } // Find the sprite on the given position (buttons) QGraphicsItem* DisplayIntro::findSprite(QPoint pos) { QGraphicsItem* found = nullptr; for (int i = 0; i < mSprites.size(); ++i) { QGraphicsItem* item = mSprites[i]; if (!item) continue; if (!item->isVisible()) continue; // Map position to local QPointF p = item->mapFromScene (QPointF(pos) ); // Take highest zValue item if (item->contains(p) && (!found || (found->zValue() < item->zValue()))) found = item; } return found; } // Handle view events and forward them to the sprites void DisplayIntro::viewEvent(QEvent* event) { // Only process some mouse events QEvent::Type type = event->type(); if (type != QEvent::MouseButtonPress && type != QEvent::MouseButtonRelease && type != QEvent::MouseMove) return; // Cast items and find right sprite QMouseEvent* e = dynamic_cast(event); QGraphicsItem* item = findSprite(e->pos()); // Which event type? Forward them to Button sprites only! switch (event->type()) { case QEvent::MouseButtonPress: if (item!=nullptr && item->type() == QGraphicsItem::UserType+100) (dynamic_cast(item))->mousePressEvent(e); break; case QEvent::MouseButtonRelease: if (item!=nullptr && item->type() == QGraphicsItem::UserType+100) (dynamic_cast(item))->mouseReleaseEvent(e); break; case QEvent::MouseMove: if (item != mLastMoveEvent) { if (mLastMoveEvent!=nullptr && mLastMoveEvent->type() == QGraphicsItem::UserType+100) { (dynamic_cast(mLastMoveEvent))->hoverLeaveEvent(e); } if (item!=nullptr && item->type() == QGraphicsItem::UserType+100) { (dynamic_cast(item))->hoverEnterEvent(e); } mLastMoveEvent = item; } break; default: break; } return; } diff --git a/src/introsprite.h b/src/introsprite.h index ba84ca4..804b348 100644 --- a/src/introsprite.h +++ b/src/introsprite.h @@ -1,197 +1,197 @@ #ifndef INTRO_SPRITE_H #define INTRO_SPRITE_H /* 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. */ // Qt includes #include #include #include -#include +#include // Local includes #include "thememanager.h" #include "pixmapsprite.h" class AnimationCommand; /** The sprite to display the introduction pieces and their animation. */ class IntroSprite : public PixmapSprite { public: /** Constructor for the sprite. * @param id The theme id * @param theme The theme manager * @param no The sprite number [0..41] * @param scene The scene */ IntroSprite(const QString &id, ThemeManager* theme, int no, QGraphicsScene* scene); /** Destructor */ ~IntroSprite() override; /** Clear the animation script. * @param restartTime Reset the timer too (default) */ void clearAnimation(bool restartTime = true); /** Create a set position command. The sprite is directly set to the given * position. * @param start The position [rel coord.] * @return The command. */ AnimationCommand* addPosition(QPointF start); /** Create a relative pause command. The sprite does nothing for the given * amount of [ms]. * @param duration The pause duration [ms] * @return The command. */ AnimationCommand* addPause(int duration); /** Create an absolute wait command. The sprite does nothing until the given * animation time. If the time is in the past nothing is done. Used to * synchronize several sprites. * @param duration The time until when to pause [ms] * @return The command. */ AnimationCommand* addWait(int duration); /** Create a command to show the sprite. * @return The command. */ AnimationCommand* addShow(); /** Create an command to hide the sprite. * @return The command. */ AnimationCommand* addHide(); /** Create a linear movement from start to end with the given duration. * @param start The start point [rel. coord] * @param end The end point [rel. coord] * @param duration The duration of the move [ms] * @return The command. */ AnimationCommand* addLinear(QPointF start, QPointF end, int duration); /** Create a linear movement from the current position to end with the given duration. * @param end The end point [rel. coord] * @param duration The duration of the move [ms] * @return The command. */ AnimationCommand* addRelativeLinear(QPointF end, int duration); /** Create a command to move in a full circle around the given center position. * The circle will have the given radius and take the given amount of time to * perform. * @param start The center of the circle [rel. coord] * @param radius The circle radius [rel. coord] * @param duration The duration of the move [ms] * @return The command. */ AnimationCommand* addCircle(QPointF start, double radius, int duration); /** Create a linear manhatte movement (edge following) from the current position to end * with the given duration. * @param end The end point [rel. coord] * @param velocity The velocity of the move times 1000 [rel] * @return The command. */ AnimationCommand* addRelativeManhatten(QPointF end, double velocity); /** Retrieve the time the whole animation script takes. * @return The animation duration [ms] */ int animationDuration(); /** Standard QGI advance function. * @param phase The advance phase */ void advance(int phase) override; /** Retrieve the type of QGI. This item is UserType+1 * @return The type of item. */ int type() const override {return QGraphicsItem::UserType+1;} /** Retrieve the sprite numbers (which introsprite e.g. 0-42) * @return The sprite numbers. */ int number() {return mNo;} /** Main theme notification method. Is called for any theme changes. It must be * implemented so that the item redraws correctly when the theme changed. */ void changeTheme() override; /** Retrieve the duration of an animation. * @return The duration [ms] */ int duration(AnimationCommand* anim); /** Delay the animation of this sprite by the given amount in [ms]. * Used to compensate for long delays in SVG resizes etc. * @param duration The delay [ms] */ void delayAnimation(int duration); protected: /** Execute a given animation command for the given time relative to the * start of the command. * @param anim The command * @param elapsed The time since the begin of the command [ms] */ void executeCmd(AnimationCommand* anim, int elapsed); /** Retrieve the start position of the last added animation command. * @return The position [rel. coord]. */ QPointF previousStart(); /** Retrieve the end position of the last added animation command. * @return The position [rel. coord]. */ QPointF previousEnd(); private: /// The current running time for the animation. - QTime mTime; + QElapsedTimer mTime; // List of animation commands QList mAnimList; // Time since start of an animation command [ms] int mDeltaT; // Global animation delay [ms] int mDelayT; // Start animation bool mStartAnimation; // Debug timer - QTime mDebugTime; + QElapsedTimer mDebugTime; }; #endif diff --git a/src/kgamedebugdialog.cpp b/src/kgamedebugdialog.cpp index 6352f42..d6ea8ab 100644 --- a/src/kgamedebugdialog.cpp +++ b/src/kgamedebugdialog.cpp @@ -1,577 +1,575 @@ /* This file is part of the KDE games library Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) Copyright (C) 2001 Martin Heni (kde at heni-online.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 "kgamedebugdialog.h" #include "kfourinline_debug.h" #define USE_UNSTABLE_LIBKDEGAMESPRIVATE_API #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class KGameDebugDialogPrivate { public: KGameDebugDialogPrivate() { mGame = nullptr; mGamePage = nullptr; mGameProperties = nullptr; mGameAddress = nullptr; mGameId = nullptr; mGameCookie = nullptr; mGameMaster = nullptr; mGameAdmin = nullptr; mGameOffering = nullptr; mGameStatus = nullptr; mGameRunning = nullptr; mGameMaxPlayers = nullptr; mGameMinPlayers = nullptr; mGamePlayerCount = nullptr; mPlayerPage = nullptr; mPlayerList = nullptr; mPlayerProperties = nullptr; mPlayerAddress = nullptr; mPlayerId = nullptr; mPlayerName = nullptr; mPlayerGroup = nullptr; mPlayerUserId = nullptr; mPlayerMyTurn = nullptr; mPlayerAsyncInput= nullptr; mPlayerKGameAddress = nullptr; mPlayerVirtual = nullptr; mPlayerActive = nullptr; mPlayerRtti = nullptr; mPlayerNetworkPriority = nullptr; mMessagePage = nullptr; mMessageList = nullptr; mHideIdList = nullptr; } const KGame* mGame; QFrame* mGamePage; QTreeWidget* mGameProperties; QTreeWidgetItem* mGameAddress; QTreeWidgetItem* mGameId; QTreeWidgetItem* mGameCookie; QTreeWidgetItem* mGameMaster; QTreeWidgetItem* mGameAdmin; QTreeWidgetItem* mGameOffering; QTreeWidgetItem* mGameStatus; QTreeWidgetItem* mGameRunning; QTreeWidgetItem* mGameMaxPlayers; QTreeWidgetItem* mGameMinPlayers; QTreeWidgetItem* mGamePlayerCount; QFrame* mPlayerPage; QListWidget* mPlayerList; QTreeWidget* mPlayerProperties; QTreeWidgetItem* mPlayerAddress; QTreeWidgetItem* mPlayerId; QTreeWidgetItem* mPlayerName; QTreeWidgetItem* mPlayerGroup; QTreeWidgetItem* mPlayerUserId; QTreeWidgetItem* mPlayerMyTurn; QTreeWidgetItem* mPlayerAsyncInput; QTreeWidgetItem* mPlayerKGameAddress; QTreeWidgetItem* mPlayerVirtual; QTreeWidgetItem* mPlayerActive; QTreeWidgetItem* mPlayerRtti; QTreeWidgetItem* mPlayerNetworkPriority; QFrame* mMessagePage; QTreeWidget* mMessageList; QListWidget* mHideIdList; }; KGameDebugDialog::KGameDebugDialog(KGame* g, QWidget* parent, bool modal) : KPageDialog(parent), d( new KGameDebugDialogPrivate ) { setWindowTitle(i18n("KGame Debug Dialog")); //QT5 setButtons(Close); //QT5 setDefaultButton(Close); setModal(modal); //QT5 showButtonSeparator(true); setFaceType(KPageDialog::Tabbed); initGamePage(); initPlayerPage(); initMessagePage(); setKGame(g); } KGameDebugDialog::~KGameDebugDialog() { delete d; } void KGameDebugDialog::initGamePage() { d->mGamePage = new QFrame(); addPage(d->mGamePage,i18n("Debug &KGame")); QVBoxLayout* topLayout = new QVBoxLayout(d->mGamePage); //QT5 topLayout->setMargin( marginHint() ); //QT5 topLayout->setSpacing( spacingHint() ); QHBoxLayout* layout = new QHBoxLayout; topLayout->addLayout(layout); QTreeWidget* v = new QTreeWidget(d->mGamePage); QTreeWidgetItem* vheader = new QTreeWidgetItem(); vheader->setText(0, tr("Data")); vheader->setText(1, tr("Value")); v->setHeaderItem(vheader); layout->addWidget(v); d->mGameProperties = new QTreeWidget(d->mGamePage); QTreeWidgetItem* mGamePropertiesHeader = new QTreeWidgetItem(); mGamePropertiesHeader->setText(0, tr("Property")); mGamePropertiesHeader->setText(1, tr("Value")); mGamePropertiesHeader->setText(2, tr("Policy")); d->mGameProperties->setHeaderItem(mGamePropertiesHeader); layout->addWidget(d->mGameProperties); QPushButton* b = new QPushButton(i18n("Update"), d->mGamePage); connect(b, &QPushButton::pressed, this, &KGameDebugDialog::slotUpdateGameData); topLayout->addWidget(b); // game data d->mGameAddress = new QTreeWidgetItem(v, QStringList(i18n("KGame Pointer"))); d->mGameId = new QTreeWidgetItem(v, QStringList(i18n("Game ID"))); d->mGameCookie = new QTreeWidgetItem(v, QStringList(i18n("Game Cookie"))); d->mGameMaster = new QTreeWidgetItem(v, QStringList(i18n("Is Master"))); d->mGameAdmin = new QTreeWidgetItem(v, QStringList(i18n("Is Admin"))); d->mGameOffering = new QTreeWidgetItem(v, QStringList(i18n("Is Offering Connections"))); d->mGameStatus = new QTreeWidgetItem(v, QStringList(i18n("Game Status"))); d->mGameRunning = new QTreeWidgetItem(v, QStringList(i18n("Game is Running"))); d->mGameMaxPlayers = new QTreeWidgetItem(v, QStringList(i18n("Maximal Players"))); d->mGameMinPlayers = new QTreeWidgetItem(v, QStringList(i18n("Minimal Players"))); d->mGamePlayerCount = new QTreeWidgetItem(v, QStringList(i18n("Players"))); } void KGameDebugDialog::initPlayerPage() { d->mPlayerPage = new QFrame(); addPage(d->mPlayerPage,i18n("Debug &Players")); QVBoxLayout* topLayout = new QVBoxLayout(d->mPlayerPage); //QT5 topLayout->setMargin( marginHint() ); //QT5 topLayout->setSpacing( spacingHint() ); QHBoxLayout* layout = new QHBoxLayout; topLayout->addLayout(layout); //TODO: connect to the KGame signals for joined/removed players!!! QVBoxLayout* listLayout = new QVBoxLayout; layout->addLayout(listLayout); QLabel* listLabel = new QLabel(i18n("Available Players"), d->mPlayerPage); listLayout->addWidget(listLabel); d->mPlayerList = new QListWidget(d->mPlayerPage); connect(d->mPlayerList, SIGNAL(executed(QListWidgetItem*)), this, SLOT(slotUpdatePlayerData(QListWidgetItem*))); listLayout->addWidget(d->mPlayerList); d->mPlayerList->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); QTreeWidget* v = new QTreeWidget(d->mPlayerPage); layout->addWidget(v); QTreeWidgetItem* vheader = new QTreeWidgetItem(); vheader->setText(0, tr("Data")); vheader->setText(1, tr("Value")); v->setHeaderItem(vheader); d->mPlayerProperties = new QTreeWidget(d->mPlayerPage); QTreeWidgetItem* mPlayerPropertiesHeader = new QTreeWidgetItem(); mPlayerPropertiesHeader->setText(0, tr("Property")); mPlayerPropertiesHeader->setText(1, tr("Value")); mPlayerPropertiesHeader->setText(2, tr("Policy")); d->mPlayerProperties->setHeaderItem(mPlayerPropertiesHeader); layout->addWidget(d->mPlayerProperties); QPushButton* b = new QPushButton(i18n("Update"), d->mPlayerPage); connect(b, &QPushButton::pressed, this, &KGameDebugDialog::slotUpdatePlayerList); topLayout->addWidget(b); d->mPlayerAddress = new QTreeWidgetItem(v, QStringList(i18n("Player Pointer"))); d->mPlayerId = new QTreeWidgetItem(v, QStringList(i18n("Player ID"))); d->mPlayerName = new QTreeWidgetItem(v, QStringList(i18n("Player Name"))); d->mPlayerGroup = new QTreeWidgetItem(v, QStringList(i18n("Player Group"))); d->mPlayerUserId = new QTreeWidgetItem(v, QStringList(i18n("Player User ID"))); d->mPlayerMyTurn = new QTreeWidgetItem(v, QStringList(i18n("My Turn"))); d->mPlayerAsyncInput = new QTreeWidgetItem(v, QStringList(i18n("Async Input"))); d->mPlayerKGameAddress = new QTreeWidgetItem(v, QStringList(i18n("KGame Address"))); d->mPlayerVirtual = new QTreeWidgetItem(v, QStringList(i18n("Player is Virtual"))); d->mPlayerActive = new QTreeWidgetItem(v, QStringList(i18n("Player is Active"))); d->mPlayerRtti = new QTreeWidgetItem(v, QStringList(i18n("RTTI"))); d->mPlayerNetworkPriority = new QTreeWidgetItem(v, QStringList(i18n("Network Priority"))); } void KGameDebugDialog::initMessagePage() { d->mMessagePage = new QFrame(); addPage(d->mMessagePage,i18n("Debug &Messages")); QGridLayout* layout = new QGridLayout(d->mMessagePage); //QT5 layout->setMargin(marginHint()); //QT5 layout->setSpacing(spacingHint()); d->mMessageList = new QTreeWidget(d->mMessagePage); layout->addWidget(d->mMessageList, 0, 0, 10, 4); QTreeWidgetItem* mMessageListHeader = new QTreeWidgetItem(); mMessageListHeader->setText(0, tr("Time")); mMessageListHeader->setText(1, tr("ID")); mMessageListHeader->setText(2, tr("Receiver")); mMessageListHeader->setText(2, tr("Sender")); mMessageListHeader->setText(2, tr("ID - Text")); d->mMessageList->setHeaderItem(mMessageListHeader); QPushButton* hide = new QPushButton(i18n("&>>"), d->mMessagePage); connect(hide, &QPushButton::pressed, this, &KGameDebugDialog::slotHideId); layout->addWidget(hide, 4, 4); QPushButton* show = new QPushButton(i18n("&<<"), d->mMessagePage); connect(show, &QPushButton::pressed, this, &KGameDebugDialog::slotShowId); layout->addWidget(show, 6, 4); QLabel* l = new QLabel(i18n("Do not show IDs:"), d->mMessagePage); layout->addWidget(l, 0, 5, 1, 2); d->mHideIdList = new QListWidget(d->mMessagePage); layout->addWidget(d->mHideIdList, 1, 5, 8, 2); QPushButton* clear = new QPushButton(d->mMessagePage); KGuiItem::assign(clear, KStandardGuiItem::clear()); connect(clear, &QPushButton::pressed, this, &KGameDebugDialog::slotClearMessages); layout->addWidget(clear, 10, 0, 1, 7); //TODO: "show all but..." and "show nothing but..." } void KGameDebugDialog::clearPlayerData() { d->mPlayerAddress->setText(1, QLatin1String( "" )); d->mPlayerId->setText(1, QLatin1String( "" )); d->mPlayerName->setText(1, QLatin1String( "" )); d->mPlayerGroup->setText(1, QLatin1String( "" )); d->mPlayerUserId->setText(1, QLatin1String( "" )); d->mPlayerMyTurn->setText(1, QLatin1String( "" )); d->mPlayerAsyncInput->setText(1, QLatin1String( "" )); d->mPlayerKGameAddress->setText(1, QLatin1String( "" )); d->mPlayerVirtual->setText(1, QLatin1String( "" )); d->mPlayerActive->setText(1, QLatin1String( "" )); d->mPlayerRtti->setText(1, QLatin1String( "" )); d->mPlayerNetworkPriority->setText(1, QLatin1String( "" )); d->mPlayerProperties->clear(); } void KGameDebugDialog::clearGameData() { d->mGameAddress->setText(1, QLatin1String( "" )); d->mGameId->setText(1, QLatin1String( "" )); d->mGameCookie->setText(1, QLatin1String( "" )); d->mGameMaster->setText(1, QLatin1String( "" )); d->mGameAdmin->setText(1, QLatin1String( "" )); d->mGameOffering->setText(1, QLatin1String( "" )); d->mGameStatus->setText(1, QLatin1String( "" )); d->mGameRunning->setText(1, QLatin1String( "" )); d->mGameMaxPlayers->setText(1, QLatin1String( "" )); d->mGameMinPlayers->setText(1, QLatin1String( "" )); d->mGameProperties->clear(); } void KGameDebugDialog::slotUpdatePlayerData() { if (!d->mGame || d->mPlayerList->currentRow() == -1) { return; } slotUpdatePlayerData(d->mPlayerList->item(d->mPlayerList->currentRow())); } void KGameDebugDialog::slotUpdatePlayerList() { QListWidgetItem* i = d->mPlayerList->item(0); for (; d->mPlayerList->count() > 0; i = d->mPlayerList->item(0)) { removePlayer(i); } for ( QList::const_iterator it = d->mGame->playerList()->begin(); it!=d->mGame->playerList()->end(); ++it ) { addPlayer(*it); } } void KGameDebugDialog::slotUpdateGameData() { if (!d->mGame) { d->mGameAddress->setText(1, i18n("NULL pointer")); return; } clearGameData(); - QString buf; - buf.sprintf("%p", (void*)d->mGame); + QString buf = QString::asprintf("%p", (void*)d->mGame); d->mGameAddress->setText(1, buf); d->mGameId->setText(1, QString::number(d->mGame->gameId())); d->mGameCookie->setText(1, QString::number(d->mGame->cookie())); d->mGameMaster->setText(1, d->mGame->isMaster() ? i18n("True") : i18n("False")); d->mGameAdmin->setText(1, d->mGame->isAdmin() ? i18n("True") : i18n("False")); d->mGameOffering->setText(1, d->mGame->isOfferingConnections() ? i18n("True") : i18n("False")); d->mGameStatus->setText(1, QString::number(d->mGame->gameStatus())); d->mGameRunning->setText(1, d->mGame->isRunning() ? i18n("True") : i18n("False")); d->mGameMaxPlayers->setText(1, QString::number(d->mGame->maxPlayers())); d->mGameMinPlayers->setText(1, QString::number(d->mGame->minPlayers())); d->mGamePlayerCount->setText(1, QString::number(d->mGame->playerCount())); //TODO ios KGamePropertyHandler* handler = d->mGame->dataHandler(); QHashIterator it(handler->dict()); while (it.hasNext()) { it.next(); QString policy; switch (it.value()->policy()) { case KGamePropertyBase::PolicyClean: policy = i18n("Clean"); break; case KGamePropertyBase::PolicyDirty: policy = i18n("Dirty"); break; case KGamePropertyBase::PolicyLocal: policy = i18n("Local"); break; case KGamePropertyBase::PolicyUndefined: default: policy = i18n("Undefined"); break; } QStringList items; items << handler->propertyName(it.value()->id()) << handler->propertyValue(it.value()) << policy; new QTreeWidgetItem(d->mGameProperties,items); // qCDebug(KFOURINLINE_LOG) << ": checking for all game properties: found property name" << name; } } void KGameDebugDialog::slotUpdatePlayerData(QListWidgetItem* item) { if (!item || !d->mGame) { return; } KPlayer* p = d->mGame->findPlayer(item->text().toInt()); if (!p) { qCCritical(KFOURINLINE_LOG) << ": cannot find player"; return; } clearPlayerData(); - QString buf; - buf.sprintf("%p", (void*)p); + QString buf = QString::asprintf("%p", (void*)p); d->mPlayerAddress->setText(1, buf); d->mPlayerId->setText(1, QString::number(p->id())); d->mPlayerName->setText(1, p->name()); d->mPlayerGroup->setText(1, p->group()); d->mPlayerUserId->setText(1, QString::number(p->userId())); d->mPlayerMyTurn->setText(1, p->myTurn() ? i18n("True") : i18n("False")); d->mPlayerAsyncInput->setText(1, p->asyncInput() ? i18n("True") : i18n("False")); - buf.sprintf("%p", (void*)p->game()); + buf = QString::asprintf("%p", (void*)p->game()); d->mPlayerKGameAddress->setText(1, buf); d->mPlayerVirtual->setText(1, p->isVirtual() ? i18n("True") : i18n("False")); d->mPlayerActive->setText(1, p->isActive() ? i18n("True") : i18n("False")); d->mPlayerRtti->setText(1, QString::number(p->rtti())); d->mPlayerNetworkPriority->setText(1, QString::number(p->networkPriority())); //TODO ios // Properties KGamePropertyHandler * handler = p->dataHandler(); QHashIterator it((handler->dict())); while (it.hasNext()) { it.next(); QString policy; switch (it.value()->policy()) { case KGamePropertyBase::PolicyClean: policy = i18n("Clean"); break; case KGamePropertyBase::PolicyDirty: policy = i18n("Dirty"); break; case KGamePropertyBase::PolicyLocal: policy = i18n("Local"); break; case KGamePropertyBase::PolicyUndefined: default: policy = i18n("Undefined"); break; } QStringList items; items << handler->propertyName(it.value()->id()) << handler->propertyValue(it.value()) << policy; new QTreeWidgetItem(d->mPlayerProperties,items); } } void KGameDebugDialog::clearPages() { clearPlayerData(); clearGameData(); d->mPlayerList->clear(); slotClearMessages(); } void KGameDebugDialog::setKGame(const KGame* g) { slotUnsetKGame(); d->mGame = g; if (g) { //TODO: connect to the KGame signals for joined/removed players!!! connect(d->mGame, &QObject::destroyed, this, &KGameDebugDialog::slotUnsetKGame); // connect(); for ( QList::const_iterator it = d->mGame->playerList()->begin(); it!=d->mGame->playerList()->end(); ++it ) { addPlayer(*it); } slotUpdateGameData(); connect(d->mGame, &KGame::signalMessageUpdate, this, &KGameDebugDialog::slotMessageUpdate); } } void KGameDebugDialog::slotUnsetKGame() { if (d->mGame) { disconnect(d->mGame, nullptr, this, nullptr); } d->mGame = nullptr; clearPages(); } void KGameDebugDialog::addPlayer(KPlayer* p) { if (!p) { qCCritical(KFOURINLINE_LOG) << "trying to add NULL player"; return; } (void) new QListWidgetItem(QString::number(p->id()), d->mPlayerList); //TODO connect to signals, like deleted/removed, ... } void KGameDebugDialog::removePlayer(QListWidgetItem* i) { if (!i || !d->mGame) { return; } KPlayer* p = d->mGame->findPlayer(i->text().toInt()); if (!p) { return; } disconnect(p, nullptr, this, nullptr); if (i->isSelected()) { clearPlayerData(); } delete i; } void KGameDebugDialog::slotMessageUpdate(int msgid, quint32 receiver, quint32 sender) { if (!showId(msgid)) { return; } QString msgidText = KGameMessage::messageId2Text(msgid); if (msgidText.isNull()) { if (msgid > KGameMessage::IdUser) { emit signalRequestIdName(msgid-KGameMessage::IdUser, true, msgidText); } else { emit signalRequestIdName(msgid, false, msgidText); } if (msgidText.isNull()) { msgidText = i18n("Unknown"); } } QStringList items; items << QTime::currentTime().toString() << QString::number(msgid) << QString::number(receiver) << QString::number(sender) << msgidText; new QTreeWidgetItem( d->mMessageList, items); } void KGameDebugDialog::slotClearMessages() { d->mMessageList->clear(); } void KGameDebugDialog::slotShowId() { /* QListBoxItem* i = d->mHideIdList->firstItem(); for (; i; i = i->next()) { if (i->selected()) { d->mHideIdList->removeItem(i->); } }*/ if (!d->mHideIdList->currentItem()) { return; } d->mHideIdList->takeItem(d->mHideIdList->currentRow()); } void KGameDebugDialog::slotHideId() { if (!d->mMessageList->currentItem()) { return; } int msgid = d->mMessageList->currentItem()->text(1).toInt(); if (!showId(msgid)) { return; } (void)new QListWidgetItem(QString::number(msgid), d->mHideIdList); } bool KGameDebugDialog::showId(int msgid) { for (int j = 0; j < d->mHideIdList->count(); ++j) { QListWidgetItem* i = d->mHideIdList->item(j); if (i->text().toInt() == msgid) { return false; } } return true; } diff --git a/src/kwin4proc.cpp b/src/kwin4proc.cpp index be80b33..da0d619 100644 --- a/src/kwin4proc.cpp +++ b/src/kwin4proc.cpp @@ -1,700 +1,700 @@ /* This file is part of the KDE games kwin4 program Copyright (c) 2000 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 "kwin4proc.h" // Standard includes #include #include #include #include // Qt includes #include #include #include #include -#include +#include #include // KDE includes #include #define USE_UNSTABLE_LIBKDEGAMESPRIVATE_API #include // Algorithm defines #define MIN_TIME 1000 // min time in milli sec for move //#define MIN_TIME 10 // min time in milli sec for move // Board and game geometry #define MAX_PIECES_COL 6 // Max 6 pieces per column #define WIN4 4 // 4 in a row won #define MAX_MOVE 42 // Maximum so many moves possible #define FIELD_OFFSET 10 // Offset // AI rating and routines #define LOWERT -999999999L // Init variables with this value #define VICTORY_VALUE 9999999L // Win or loss value #define MAX_EXTRA_RECURSION 15 // Maximum so many extra recursions #define PERCENT_FOR_INC_ITERATION 40 // If less than this amount of estimated moves are #define EVAL_RANDOM 2500 // Randomize position evaluation by this // calculated increase recursion // AI Learning #define LEARN_DELTA -15000L // Learn if position drops by more than this #define MAX_LEARNED_POSITIONS 100000 // Learn not more than this positions #define MIN_LEARN_LEVEL 3 // Use learning only >= this level #define BRAIN_VERSION 1 // Version number // Constructor: Setup AI KComputer::KComputer() { const char *s1="7777776666666123456654321123456654321"; const char *s2="0000000000000000000123456000000123456"; unsigned int i; // Init variables for (i=0;i> version; if (version != (qint32)BRAIN_VERSION) { fprintf(stderr," KComputer::Loading brain version error %ld\n",(long)version); fflush(stderr); file.close(); return; } qint32 noOfItems; in >> noOfItems; fprintf(stderr," KComputer::Loading %ld brain data\n",(long)noOfItems); fflush(stderr); bool erase = false; if (noOfItems >MAX_LEARNED_POSITIONS) erase = true; for (int cnt=0; cnt> board >> value; // Forget 10% positions in case of overload if (erase && cnt%10 != 0) { mBrain.insert(board, value); } } fflush(stderr); qint32 check; in >> check; file.close(); if (check != (qint32)0x18547237) { fprintf(stderr," KComputer::Loading brain CRC error %ld cnt=%d\n",(long)check,noOfItems); fflush(stderr); mBrain.clear(); return; } fprintf(stderr," KComputer::Loading brain (%d items) succeeded\n",(int)noOfItems); fflush(stderr); } // Save brain position cache void KComputer::saveBrain() { QFile file(mBrainDir+QStringLiteral("kwin4.brain")); if (!file.open(QIODevice::WriteOnly )) { fprintf(stderr," KComputer::saving brain failed.\n"); fflush(stderr); return; } QDataStream out(&file); out << (qint32)BRAIN_VERSION; // Version out << (qint32)mBrain.size(); QHashIterator it(mBrain); while (it.hasNext()) { it.next(); AIBoard board = it.key(); AIValue value = it.value(); out << board << value; } out << (qint32)0x18547237; // Checksum file.close(); fprintf(stderr," KComputer:: Brain saved.\n"); fflush(stderr); } // Received a command from the main program void KComputer::slotCommand(QDataStream &in, int msgid, int /*receiver*/, int /*sender*/) { //fprintf(stderr,"KComputer::slotCommand(Msgid=%d)\n",msgid); //fflush(stderr); QByteArray buffer; QDataStream out(&buffer,QIODevice::WriteOnly); switch(msgid) { case 2: // hint { qint8 cid = 2; quint32 recv = 0; out << cid; MoveResult result = think(in,out,true); out << ( qint32 )result.value; int id = KGameMessage::IdProcessQuery; proc.sendSystemMessage(out, id, recv); } break; case 3: // AI board value changed, maybe learn { AIBoard aiBoard; qint32 value, level, delta; in >> aiBoard >> value >> delta >> level; // Only learn big changes if (mIsLearning && delta < LEARN_DELTA && level >= MIN_LEARN_LEVEL && level < MIN_LEARN_LEVEL+AIValue::NO_OF_LEVELS) { fprintf(stderr,"KComputer:: LEARNING Received AI board for value %ld delta %ld level %ld\n", (long)value, (long)delta, (long)level); aiBoard.print(); // Learn board? AIValue aiValue; // Do we already store board? if (mBrain.contains(aiBoard)) { aiValue = mBrain.value(aiBoard); } // Do we store mirror board? else if (mBrain.contains(aiBoard.mirror())) { aiBoard = aiBoard.mirror(); aiValue = mBrain.value(aiBoard); } // Set not set or decreased values in value structure if (!aiValue.isSet((int)level-MIN_LEARN_LEVEL) || aiValue.value((int)level-MIN_LEARN_LEVEL) > (long)value) { aiValue.setValue((int)level-MIN_LEARN_LEVEL, (long)value); mBrain.insert(aiBoard, aiValue); saveBrain(); fprintf(stderr, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n"); fprintf(stderr, "$ LEARNING $\n"); fprintf(stderr, "$ Setting board level %d to value = %ld\n",(int)level, (long)value); fprintf(stderr, "$ mBrain size=%d\n", mBrain.size()); fprintf(stderr, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n"); fflush(stderr); } else // TODO: Remove else { fprintf(stderr, "NOT LEARNING $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n"); fprintf(stderr, " REASON: %d %ld %ld\n", aiValue.isSet((int)level-MIN_LEARN_LEVEL), aiValue.value((int)level-MIN_LEARN_LEVEL),(long)value); fflush(stderr); } }// end delta } break; default: fprintf(stderr,"KComputer:: unknown command (msgid=%d)\n",msgid); fflush(stderr); } } // Think up a move (plus reading data from stream) KComputer::MoveResult KComputer::think(QDataStream& in, QDataStream& out, bool /*hint*/) { qint32 pl; qint32 move; qint32 tmp; COLOUR secondPlayer; // Which color's move is to come COLOUR currentPlayer; // Which player started the game COLOUR startPlayer; // Read command data stream into local variables in >> pl ; in >> tmp; mCurMoveNo=tmp; in >> tmp; currentPlayer=(COLOUR)(tmp); in >> tmp; startPlayer=(COLOUR)(tmp); in >> tmp; secondPlayer=(COLOUR)(tmp); in >> tmp; mLevel=tmp; in >> tmp; mIsLearning=tmp; in >> mBrainDir; // Check proper learning level. If not proper switch learning off. if (mLevel=MIN_LEARN_LEVEL+AIValue::NO_OF_LEVELS) { mIsLearning = false; fprintf(stderr,"KComputer:: Switching off learning for level %d\n",mLevel); } fprintf(stderr,"KComputer::think: pl=%d, mCurMoveNo=%d currentPlayer=%d begin=%d secondPlayer=%d level=%d learn=%d\n", pl,mCurMoveNo,currentPlayer,startPlayer,secondPlayer,mLevel,mIsLearning); //fprintf(stderr,"KComputer::learning=%d path=%s\n",mIsLearning,mBrainDir.toLatin1().data()); // Brain loaded? if (!mBrainLoaded && mIsLearning) { mBrainLoaded = true; loadBrain(); } // Setup board (init) // The game board prepared for the AI FARBE fieldMatrix[SIZE_Y_ALL+1][SIZE_X+1]; // Amount of pieces on the game board char numberMatrix[SIZE_Y_ALL+1]; for (int y=0;y<=SIZE_Y_ALL;++y) { numberMatrix[y]=0; } for (int y=0;y<=SIZE_Y;++y) { for (int x=0;x<=SIZE_X;++x) { fieldMatrix[y][x] = (FARBE)(y+FIELD_OFFSET); fieldMatrix[6+x][y] = (FARBE)(y+FIELD_OFFSET); fieldMatrix[13+x+y][x] = (FARBE)(y+FIELD_OFFSET); fieldMatrix[30+x-y][x] = (FARBE)(y+FIELD_OFFSET); } // next x }// next y // Field comes as 42 qint8's representing moves int i,j; for (i=0;i<=SIZE_Y;++i) { for (j=0;j<=SIZE_X;++j) { qint8 col; in >> col; DoMove(j, (COLOUR)col, fieldMatrix, numberMatrix); } } // Check final checksum in >> tmp; if ((long)tmp != 421256L) { fprintf(stderr,"CHECKSUM=%ld [should be 421256]\n",(long)tmp); fflush(stderr); } // Estimated number of positions to evaluate (MAX) int estimated = 0; for (int i=1; i<= mLevel+1; ++i) { estimated += int(pow(7.,i)); } // Measure time of move and positions evaluated - QTime timer; + QElapsedTimer timer; timer.start(); mPosEvaluations = 0; // Get move int gameOver = 0; int extraRecurstion = 0; MoveResult result; // Loop movement if not many positions are evaluated (end game) // to look ahead a bit more do { // Actually calculate move result = MinMax(currentPlayer, fieldMatrix, numberMatrix, mLevel + extraRecurstion, mCurMoveNo, true); result.value = -result.value; // Do not recalculate for (nearly finished) games // if (result.value >= VICTORY_VALUE*0.95 || result.value <= -VICTORY_VALUE *0.95) gameOver = 1; extraRecurstion++; }while(PERCENT_FOR_INC_ITERATION/10*mPosEvaluations <= estimated && !gameOver && extraRecurstion < MAX_EXTRA_RECURSION); // Measure elapsed time int elapsed = timer.elapsed(); if (elapsed < 1) elapsed = 1; mCalcPosPerMS = (float)mPosEvaluations/(float)elapsed; // Debug fprintf(stderr,"AI MOVE to %d value=%ld level %d took time=%d ms and evals=%d estimated=%d pos/ms=%f brain=%ld\n", result.move,result.value,mLevel, elapsed, mPosEvaluations,estimated,mCalcPosPerMS,mBrainUsed); // Sleep a minimum amount to slow down moves if (elapsed < MIN_TIME) { // is usleep(1000*(MIN_TIME-elapsed)); QMutex mutex; QWaitCondition cond; mutex.lock(); cond.wait(&mutex, MIN_TIME-elapsed); elapsed = timer.elapsed(); fprintf(stderr,"AI after sleeping time elapsed=%d\n",elapsed); } fflush(stderr); // Send out move move = result.move; out << pl << move; return result; } // Min-Max AI algorithm KComputer::MoveResult KComputer::MinMax(COLOUR color, FARBE field[][SIZE_X+1], char numbers[], int reklev, int moveNo, bool first) { // Modify move value static long gauss[]={10,50,300,500,300,50,10}; // Local board FARBE locField[SIZE_Y_ALL+1][SIZE_X+1]; char locNumbers[SIZE_Y_ALL+1]; // Result of move MoveResult result; result.move = -1; // No move found result.value = LOWERT; for (int x=0; x<=SIZE_X; ++x) { long wert; if (numbers[6+x]>=MAX_PIECES_COL) continue; // Perform test move memcpy(locNumbers, numbers, sizeof(locNumbers)); memcpy(locField, field, sizeof(locField)); DoMove(x, color, locField, locNumbers); // Count evaluations mPosEvaluations++; // Check for game over COLOUR winner = isGameOver(field, numbers); if (winner != Nobody) { // Choose tree with fastest victory or slowest loss if (winner==color) wert = VICTORY_VALUE+reklev*5000; else wert = -VICTORY_VALUE-reklev*5000; } // Drawn / Remis else if (moveNo >= MAX_MOVE) { wert = 0; } // End of recursion reached else if (reklev <= 0) { wert = PositionEvaluation(color, field); //wert += gauss[x]; // TODO: Gauss should be here // No need for filling the test board here } // MinMax calculation for next recursion level else { // Check learning AIBoard testBoard(color, locField); // Found cached board or not bool haveToCalculate = true; // Check for learned board if (mIsLearning && mBrain.contains(testBoard)) { AIValue testValue = mBrain.value(testBoard); if (testValue.isSet(mLevel-MIN_LEARN_LEVEL)) { wert = testValue.value(mLevel-MIN_LEARN_LEVEL); mBrainUsed++; haveToCalculate = false; fprintf(stderr, "$$$$$$ Board[%d] found in cache value = %ld used=%ld\n",mLevel, wert, mBrainUsed); fflush(stderr); } } // Check for learned mirror board else if (mIsLearning && mBrain.contains(testBoard.mirror())) { testBoard = testBoard.mirror(); AIValue testValue = mBrain.value(testBoard); if (testValue.isSet(mLevel-MIN_LEARN_LEVEL)) { wert = testValue.value(mLevel-MIN_LEARN_LEVEL); mBrainUsed++; haveToCalculate = false; fprintf(stderr, "$$$$$$ MIRROR 2 Board[%d] found in cache value = %ld used=%ld\n",mLevel, wert, mBrainUsed); fflush(stderr); } } // Real calculation necessary if (haveToCalculate) { // Swap color for next move COLOUR nextColor; if (color == Red) nextColor = Yellow; else nextColor = Red; MoveResult minmaxResult = MinMax(nextColor, locField, locNumbers, reklev-1, moveNo+1, false); wert = minmaxResult.value + gauss[x]; // TODO: Remove gauss here } }// end else MinMax // New maximum? if (wert >= result.value) { result.value = wert; result.move = x; if (first) mMaxAIBoard.fromField(color, false, locField); //if (result.value >= VICTORY_VALUE) break; } }//next x // MinMax result.value = -result.value; return result; }// end MinMax // Position evaluation long KComputer::PositionEvaluation(COLOUR curColor, FARBE field[][SIZE_X+1]) { /* Abstand: 0 1 2 3 4 5 */ static long myWERT[]={2200,600, 300, 75, 20, 0}; //static long myWERT[]={0,0,0,0,0,0}; /* How many ofCOLOUR: 0 1 2 3 4 */ static long steinWERT[4][5]= { { 0, 500L, 40000L,200000L,VICTORY_VALUE}, // Leerfelder=0 { 0, 500L, 8000L, 40000L,VICTORY_VALUE}, // =1 { 0, 00L, 4000L, 25000L,VICTORY_VALUE}, // =2 { 0, 00L, 2000L, 12500L,VICTORY_VALUE}, // =3 }; // Initial values long yellow_value = random(EVAL_RANDOM); long red_value = random(EVAL_RANDOM); for (int y=0; y<=SIZE_Y_ALL; ++y) { if (mRowLengths[y]3) cnt=3; if (color==Red) red_value += (value+steinWERT[cnt][cntcol]); else if (color==Yellow) yellow_value += (value+steinWERT[cnt][cntcol]); }/*next i*/ }/*next y*/ // Return value if (curColor==Red) return red_value-yellow_value; else return yellow_value-red_value; } // Check for game over COLOUR KComputer::isGameOver(FARBE field[][SIZE_X+1],char numbers[]) { for (int y=0; y<=SIZE_Y_ALL; ++y) { if (numbers[y] < WIN4) continue; if (mRowLengths[y] < WIN4) continue; int cnt = 0; COLOUR thiscolor = Nobody; for (int x=0; x=WIN4) &&( (thiscolor==Yellow)||(thiscolor==Red) ) ) return thiscolor; }// next x }// next y return Nobody ; } // Execute move on given board void KComputer::DoMove(int move, COLOUR color, FARBE field[][SIZE_X+1], char numbers[]) { if (color==Tip || color==Nobody) return ; // no real move int x = move; int y = numbers[6+move]; field[y][x] = color; field[6+x][y] = color; field[13+x+y][x] = color; field[30+x-y][x] = color; numbers[y]++; numbers[6+x]++; numbers[13+x+y]++; numbers[30+x-y]++; for (int i=y+1; i<=SIZE_Y; ++i) { field[i][x]--; field[6+x][i]--; field[13+x+i][x]--; field[30+x-i][x]--; } } // Retrieve random number 0..max long KComputer::random(long max) { //return 0; return proc.random()->getLong(max); } // Main startup int main() { // This is the computer player...it should do the calculation // It doesn't do much here fprintf(stderr,"AI process starts up\n"); fflush(stderr); KComputer comp; // And start the event loop comp.proc.exec(); fprintf(stderr,"AI process exists.\n"); fflush(stderr); return 1; } diff --git a/src/kwin4view.cpp b/src/kwin4view.cpp index 254ace5..a8cf15d 100644 --- a/src/kwin4view.cpp +++ b/src/kwin4view.cpp @@ -1,546 +1,546 @@ /* 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 "kwin4view.h" // Standard includes #include // Qt includes #include #include -#include +#include // KDE includes #include "kfourinline_debug.h" #define USE_UNSTABLE_LIBKDEGAMESPRIVATE_API #include // Local includes #include "kwin4global.h" #include "displayintro.h" #include "displaygame.h" #include "spritenotify.h" #include "score.h" #include "reflectiongraphicsscene.h" // How many time measurements for average #define MEASUREMENT_LIST_SIZE 50 // How many warnings until reflections are switched off #define WARNING_MAX_COUNT 5 // How many milliseconds rounding error #define MEASUREMENT_ROUNDING_ERROR 5 // Constructor for the view KWin4View::KWin4View(int updateTime, const QSize &size, ReflectionGraphicsScene* scene, ThemeManager* theme, QWidget* parent) : Themeable(QStringLiteral("theview"), theme), QGraphicsView(scene, parent) { // Store attributes mScene = scene; mTheme = theme; mDefaultUpdateTime = updateTime; mSlowDownFactor = 1.0; mSlowCnt = 0; mReflectPhase = 0; // We do not need scrolling so switch it off setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setFrameStyle(QFrame::NoFrame); setCacheMode(QGraphicsView::CacheBackground); //setAlignment(Qt::AlignHCenter); //setViewportUpdateMode(QGraphicsView::FullViewportUpdate); setViewportUpdateMode(QGraphicsView::SmartViewportUpdate); setOptimizationFlags(QGraphicsView::DontClipPainter | QGraphicsView::DontSavePainterState | QGraphicsView::DontAdjustForAntialiasing ); viewport()->setMouseTracking(true); setMouseTracking(true); // Choose a background color scene->setBackgroundBrush(QColor(0,0,128)); mTimer = new QTimer(this); connect(mTimer, &QTimer::timeout, this, &KWin4View::updateAndAdvance); mTimer->start(mDefaultUpdateTime); // Game status mIsRunning = false; // Queue mThemeQueue.clear(); mThemeOffset.clear(); // Set size and position of the view and the canvas: // they are resized once a level is loaded resize(size); scene->setSceneRect(0, 0, this->width(), this->height()); adjustSize(); // Interact with user setInteractive(true); // Scale theme mTheme->rescale(this->width(), QPoint(0,0)); // Start with the intro display mGameDisplay = nullptr; mIntroDisplay = nullptr; // Reflections mReflectionSprite = new QGraphicsPixmapItem(); scene->addItem(mReflectionSprite); mReflectionSprite->setZValue(1000.0); mReflectionSprite->hide(); // Debug mFrameSprite = new QGraphicsTextItem(); scene->addItem(mFrameSprite); mFrameSprite->setPos(QPointF(0.0, 0.0)); mFrameSprite->setZValue(1000.0); if (global_debug > 0) mFrameSprite->show(); else mFrameSprite->hide(); // Skip the intro? if (!global_skip_intro) { mIntroDisplay = new DisplayIntro(scene, mTheme, this); connect(mIntroDisplay, &DisplayIntro::signalQuickStart, this, &KWin4View::signalQuickStart); mIntroDisplay->start(); } } // Destruct the view object KWin4View::~KWin4View() { delete mIntroDisplay; delete mGameDisplay; if (global_debug>0) qCDebug(KFOURINLINE_LOG) << "TRACKING" << hasMouseTracking() << "and" << viewport()->hasMouseTracking(); delete mFrameSprite; delete mReflectionSprite; } // Main themeable function. Called for any theme change. void KWin4View::changeTheme() { if (global_debug > 0) qCDebug(KFOURINLINE_LOG) << "CHANGE THEME IN VIEW ... resetting slow counter"; mDrawTimes.clear(); mSlowDownFactor = 1.0; mSlowCnt = 0; mTimer->setInterval(int(mDefaultUpdateTime*mSlowDownFactor)); } // Advance and update canvas/scene void KWin4View::updateAndAdvance() { // Time measurement (maybe remove static at some point) static bool first = true; - static QTime time; + static QElapsedTimer time; int elapsed = time.elapsed(); if (first) {elapsed = 0;first=false;} time.restart(); // Time display mDrawTimes.append(elapsed); if (mDrawTimes.size() > MEASUREMENT_LIST_SIZE) mDrawTimes.removeFirst(); double avg = 0.0; for (int i=0; i 0) { mFrameSprite->setPlainText(QStringLiteral("CurrentUpdate: %1 ms AverageUpdate%2 ms DefaultUpdate: %3*%4 ms"). arg(elapsed).arg(int(avg)).arg(mDefaultUpdateTime).arg(mSlowDownFactor)); } // Dynamic update of the graphics advance and update speed if (mDrawTimes.size() == MEASUREMENT_LIST_SIZE && avg > mDefaultUpdateTime*mSlowDownFactor+MEASUREMENT_ROUNDING_ERROR) { mSlowCnt++; qCDebug(KFOURINLINE_LOG) << "Warning " << mSlowCnt << " avg=" << avg; mDrawTimes.clear(); if (mSlowCnt > WARNING_MAX_COUNT) { mSlowDownFactor = double(MEASUREMENT_ROUNDING_ERROR+avg)/double(mDefaultUpdateTime); mSlowCnt = 0; mTimer->setInterval(int(mDefaultUpdateTime*mSlowDownFactor)); qCDebug(KFOURINLINE_LOG) << "SLOW COMPUTER WARNING: Decreasing graphics update speed " << mDefaultUpdateTime*mSlowDownFactor<<"ms. Maybe switch off reflections."; } } // Scene advance scene()->advance(); // QGV takes care of updating dirty rects, no need to call update or the whole scene is dirtied and repainted // scene()->update(); // ==================================================================================== // Reflections need to be done in the view otherwise the update's go wrong if (mReflectionRect.width() >0 && mReflectionRect.height() > 0) { // Draw reflection in steps to save processing power if (mReflectPhase == 0) { mReflectImage = QImage(mReflectionRect.width(), mReflectionRect.height(), QImage::Format_ARGB32); mReflectImage.fill(Qt::transparent); QPainter imagePainter(&mReflectImage); // imagePainter.fillRect(image.rect(),QBrush(Qt::red)); //Turn on all optimizations imagePainter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, false); imagePainter.setClipping(true); imagePainter.setWorldTransform(QTransform(1.0,0.0,0.0,-1.0,0.0,mReflectImage.height())); QRect source = QRect(mReflectionRect.x(),mReflectionRect.y()-mReflectImage.height(), mReflectImage.width(), mReflectImage.height()); bool vis = mReflectionSprite->isVisible(); mReflectionSprite->hide(); dynamic_cast(scene())->setBackground(false); scene()->render(&imagePainter, mReflectImage.rect(), source, Qt::IgnoreAspectRatio); dynamic_cast(scene())->setBackground(true); if (vis) mReflectionSprite->show(); mReflectPhase = 1; } // Draw reflection in steps to save processing power else if (mReflectPhase == 1) { // Semi transparent QPainter imagePainter(&mReflectImage); imagePainter.setTransform(QTransform()); imagePainter.setCompositionMode(QPainter::CompositionMode_DestinationIn); imagePainter.drawImage(0,0,mGradientImage); mReflectPhase = 2; } // Draw reflection in steps to save processing power else if (mReflectPhase == 2) { // Set to sprite QPixmap pm = QPixmap::fromImage(mReflectImage); mReflectionSprite->setPixmap(pm); mReflectionSprite->update(); mReflectPhase = 0; } } // ==================================================================================== } // Define the reflection void KWin4View::setReflection(int x, int y, int width, int height) { mReflectionRect = QRect(x,y,width,height); QPoint p1, p2; p2.setY(height); mGradient = QLinearGradient(p1, p2); mGradient.setColorAt(0, QColor(0, 0, 0, 100)); mGradient.setColorAt(1, Qt::transparent); qCDebug(KFOURINLINE_LOG) << "Set reflection "<< x << " " << y << " " << width << " " << height ; mGradientImage = QImage(width, height, QImage::Format_ARGB32); mGradientImage.fill(Qt::transparent); QPainter p( &mGradientImage ); p.fillRect(0,0,width, height, mGradient); p.end(); mReflectionSprite->setPos(x,y); if (width >0 && height > 0) { mReflectionSprite->show(); } else { mReflectionSprite->hide(); } } // QGV drawItems function (for debug time measurements) void KWin4View::drawItems(QPainter* painter, int numItems, QGraphicsItem* items[], const QStyleOptionGraphicsItem options[]) { QGraphicsView::drawItems(painter, numItems, items, options); } // Stop intro display and init game display void KWin4View::initGame(Score* scoreData) { qCDebug(KFOURINLINE_LOG) << "KWin4View::initGame"; // For better performance disable mouse tracking now viewport()->setMouseTracking(false); setMouseTracking(false); delete mIntroDisplay; mIntroDisplay = nullptr; if (!mGameDisplay) { mGameDisplay = new DisplayGame(mScene, mTheme, this); } mGameDisplay->start(); // Connect score and score sprite scoreData->setDisplay(mGameDisplay->score()); mIsRunning = true; } // End the game void KWin4View::endGame() { mIsRunning = false; mGameDisplay->displayEnd(); } // Slot called by the framework when the view is resized. void KWin4View::resizeEvent (QResizeEvent* e) { if (global_debug > 2) qCDebug(KFOURINLINE_LOG) <<"RESIZE EVENT" << e->size() << "oldSize="<< e->oldSize(); // Test to prevent double resizing // if (QWidget::testAttribute(Qt::WA_PendingResizeEvent)) // { // return; // } double diffW = double(e->oldSize().width()-e->size().width()); double diffH = double(e->oldSize().height()-e->size().height()); double delta = fabs(diffW) + fabs(diffH); // Adapt the canvas size to the window size if (scene()) { scene()->setSceneRect(0, 0, e->size().width(), e->size().height()); } QSizeF size = QSizeF(e->size()); // Rescale on minimum fitting aspect ratio either width or height limiting double width = 0.0; double aspect = size.width() / size.height(); QPoint offset; // Scale width: // Ideal size would be: 'width'*'height' // Offset in width is (e->size().width()-width)/2, offset in height is zero if (aspect > mTheme->aspectRatio()) { width = e->size().height()*mTheme->aspectRatio(); offset = QPoint(int((e->size().width()-width)/2.0), 0); } // Scale height: // 'height' = width/mTheme->aspectRatio() // Ideal size would be: 'width'*'height': // Offset in height is (e->size().height()-width/mTheme->aspectRatio())/2, offset in width is zero else { width = e->size().width(); // Scale height offset = QPoint(0, int((e->size().height()-width/mTheme->aspectRatio())/2.0)); } // Pixel rescale double oldScale = mTheme->getScale(); //resetTransform(); QTransform transform; if (width > oldScale) { transform.scale(double(width/oldScale), double(width/oldScale)); } setTransform(transform); mThemeQueue.prepend(int(width)); mThemeOffset.prepend(offset); if (global_debug > 2) qCDebug(KFOURINLINE_LOG) << "Quequed resize, aspect=" << aspect << "theme aspect="<< mTheme->aspectRatio(); long queueDelay = 0; if (delta < 15) queueDelay = 750; else if (delta < 35) queueDelay = 500; QTimer::singleShot(queueDelay, this, &KWin4View::rescaleTheme ); } // Rescale the theme (update theme SVG graphics) from the theme list void KWin4View::rescaleTheme() { if (mThemeQueue.size() == 0) { if (global_debug > 2) qCDebug(KFOURINLINE_LOG) << "***************** Swallowing rescale event ***********************"; return; } - QTime t; + QElapsedTimer t; t.start(); resetTransform(); int width = mThemeQueue.first(); QPoint offset = mThemeOffset.first(); if (global_debug > 2) qCDebug(KFOURINLINE_LOG) << "Theme queue size=" << mThemeQueue.size() << "Rescale width to" << width << " offset " << offset; mThemeQueue.clear(); mThemeOffset.clear(); mTheme->rescale(width, offset); if (global_debug > 2) qCDebug(KFOURINLINE_LOG) << "Time elapsed: "<< t.elapsed() << "ms"; } // This slot is called when a mouse key is pressed. As the mouse is used as // input for all players. It is called to generate a player move out of a mouse input, i.e. // it converts a QMouseEvent into a move for the game. void KWin4View::mouseInput(KGameIO* input, QDataStream& stream, QMouseEvent* mouse, bool* eatevent) { // Only react to mouse pressed not released if (mouse->type() != QEvent::MouseButtonPress ) return; if (mouse->button() != Qt::LeftButton) return ; if (!mIsRunning) return; // Our player KPlayer* player=input->player(); if (!player->myTurn()) { // qCDebug(KFOURINLINE_LOG) <<" Kwin4View::TODO wrongPlayer"; // *eatevent=wrongPlayer(player,KGameIO::MouseIO); return; } // Calculate movement position from mouse position int x = -1; if (mGameDisplay) x = mGameDisplay->mapMouseToMove(mouse->pos()); if (x<0) return; // Create a game move (pl id and move coordinate) qint32 move = x; qint32 pl = player->userId(); stream << pl << move; *eatevent=true; } // This slot is called when a key event is received. It then produces a // valid move for the game. // This is analogous to the mouse event only it is called when a key is // pressed. void KWin4View::keyInput(KGameIO* input, QDataStream& stream, QKeyEvent* key, bool* eatevent) { // Ignore non running if (!mIsRunning) return; // Ignore non key press if (key->type() != QEvent::KeyPress) return ; // Check key code int code=key->key(); if (code< Qt::Key_1 || code> Qt::Key_7) return ; // Our player KPlayer *player=input->player(); if (!player->myTurn()) { //qCDebug(KFOURINLINE_LOG) <<" Kwin4View::TODO wrongPlayer"; // *eatevent=wrongPlayer(player,KGameIO::KeyIO); return; } // Create a valid game move (player id and movement position) qint32 move = code-Qt::Key_1; qint32 pl = player->userId(); stream << pl << move; *eatevent=true; } // Displays a move on the game board. void KWin4View::displayMove(int x, int y, int color, int xarrow, int colorarrow, int no, bool animation) { mGameDisplay->displayArrow(xarrow, colorarrow); // animation only if no redo SpriteNotify* notify = mGameDisplay->displayPiece(x, y, color, no, animation); if (notify && animation) { QObject::disconnect(notify,&SpriteNotify::signalNotify, this,&KWin4View::moveDone); connect(notify, &SpriteNotify::signalNotify, this, &KWin4View::moveDone); } mGameDisplay->displayHint(0,0,false); } // Display a star of the given sprite number void KWin4View::displayStar(int x, int y, int no) { mGameDisplay->displayStar(x, y, no); } // Display a hint on the board void KWin4View::displayHint(int x, int y) { mGameDisplay->displayHint(x, y, true); } // Slot called when a sprite animation move is done. void KWin4View::moveDone(QGraphicsItem* /*item*/, int mode) { emit signalMoveDone(mode); } bool KWin4View::viewportEvent ( QEvent * event ) { if (mIntroDisplay) mIntroDisplay->viewEvent(event); return QGraphicsView::viewportEvent(event); } diff --git a/src/pixmapsprite.h b/src/pixmapsprite.h index a6c6e73..619347b 100644 --- a/src/pixmapsprite.h +++ b/src/pixmapsprite.h @@ -1,184 +1,184 @@ #ifndef PIXMAP_SPRITE_H #define PIXMAP_SPRITE_H /* 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. */ // Qt includes #include #include -#include +#include // Local includes #include "thememanager.h" /** This sprite is used to display a pixmap on the canvas. * It can be animated and it is themeable. */ class PixmapSprite : public QGraphicsPixmapItem, public virtual Themeable { public: /** Constructor for the sprite. * @param no A user defined ID number * @param scene The graphics scene */ PixmapSprite(int no, QGraphicsScene* scene); /** Constructor for the sprite. * @param id The theme file ID string * @param theme The theme manager * @param no A user defined ID number * @param scene The graphics scene */ PixmapSprite(const QString &id, ThemeManager* theme, int no, QGraphicsScene* scene); /** Possible animation states of the sprite */ enum AnimationState {Idle, Animated}; /** Standard QGI advance function. * @param phase The advance phase */ void advance(int phase) override; /** Retrieve the type of QGI. This item is UserType+3 * @return The type of item. */ int type() const override {return QGraphicsItem::UserType+3;} /** Retrieve the user defined sprite number (i.e. which PixmapSprite) * @return The sprite numbers. */ int number() {return mNo;} /** Main theme manager function. Called when any theme change like * a new theme or a theme size change occurs. This object needs to * resize and redraw then. */ void changeTheme() override; /** Choose a pixmap frame of this sprite. If the setting is forced a * redraw is even performed when the frame number stays the same. This * is important for theme changes. * @param no The new frame number (0..) * @param force Force redraw */ void setFrame(int no, bool force=false); /** Initialize and start a frame animation between the start and end frame. * The delay between the frames is given in [ms]. After the last frame * is displayed the animation starts with the first frame. * @param start The start frame number * @param end The end frame number * @param delay Delay between a frame change [ms] */ void setAnimation(int start, int end, int delay); /** Start or stop an animation. * @param status True to start and false to stop the animation */ void setAnimation(bool status); /** Set/Move the sprite to the given position. The position is * given in relative coordinates [0..1, 0..1] to its parent object. * Thus the position is scaled with the scale from the theme manager. * @param pos The position of the sprite [0..1, 0..1] */ void setPosition(QPointF pos); /** Store the logical coordinates (e.g 0-6, 0-5) for theme changes. Need to * manually be called after all setPosition or set Linear calls. * @param pos The logical position */ void setLogicalPos(QPoint pos); /** Retrieve the logical coordinates. * @return The logical position. */ QPoint logicalPos(); /** Reads a theme configuration item of type double and of the given * name. * @deprecated Thsi is debug only * @param item The theme configuration item name * @return The read double value. */ double getDoubleValue(const QString &item); /** Set whether the sprite should respect a theme offset * (default: true) or not (false), that is, it is handled * by its parent item. * @param status True: Handle theme offset, False: do not */ void setOffsetStatus(bool status); protected: /** The user defined sprite number. */ int mNo; /** The state of the animation. */ AnimationState mAnimationState; /** The position of the sprite [rel 0..1, rel 0..1] */ QPointF mStart; /** The first frame in the frame animation. */ int mStartFrame; /** The last frame in the frame animation. */ int mEndFrame; /** The delay between two frames in the frame animation. */ int mDelay; /** The current running time for the animation. */ - QTime mTime; + QElapsedTimer mTime; /** The current running frame number for the animation. */ int mCurrentFrame; /** Storage of the frame pixmaps. */ QList mFrames; /** Storage of the frame pixmap offset points. */ QList mHotspots; /** The logical sprite pos for theme changs */ QPoint mLPos; /** Offset status. */ bool mOffsetStatus; }; #endif diff --git a/src/reflectiongraphicsscene.cpp b/src/reflectiongraphicsscene.cpp index 4775165..c16d763 100644 --- a/src/reflectiongraphicsscene.cpp +++ b/src/reflectiongraphicsscene.cpp @@ -1,110 +1,110 @@ /* 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 "reflectiongraphicsscene.h" #include "kwin4global.h" #include "kfourinline_debug.h" #include #include #include #include #include -#include +#include // How many time measurements for average #define MEASUREMENT_LIST_SIZE 50 // How many warnings until reflections are switched off #define WARNING_MAX_COUNT 10 // Construct a new scene ReflectionGraphicsScene::ReflectionGraphicsScene(int updateTime, QObject * parent) : QGraphicsScene(parent) { Q_UNUSED(updateTime) // Initialize mBackground = true; } // Destruct scene ReflectionGraphicsScene::~ReflectionGraphicsScene() { } // QGV basic function to draw all items of a scene void ReflectionGraphicsScene::drawItems(QPainter *painter, int numItems, QGraphicsItem *items[], const QStyleOptionGraphicsItem options[], QWidget *widget) { - QTime time; + QElapsedTimer time; time.start(); // No reflections call parent function QGraphicsScene::drawItems(painter, numItems, items, options, widget); /* // ========================================================================== // Update time measurement and display int elapsed = time.elapsed(); mDrawTimes.append(elapsed); if (mDrawTimes.size() > MEASUREMENT_LIST_SIZE) mDrawTimes.removeFirst(); double avg = 0.0; for (int i=0; i 0) mFrameSprite->setPlainText(QString("Draw: %1 ms Average %2 ms Update: %3 ms").arg(elapsed).arg(int(avg)).arg(mDisplayUpdateTime)); // Disable reflections on slow computers if (mDrawTimes.size() >= MEASUREMENT_LIST_SIZE ) { if (avg > 2*mUpdateTime ) { mUpdateWarning++; mDrawTimes.clear(); qCDebug(KFOURINLINE_LOG) << mUpdateWarning << ". slow computer reflection theme warning"; } else { mUpdateWarning = 0; } } if (mUpdateWarning >= WARNING_MAX_COUNT ) { update(); qCDebug(KFOURINLINE_LOG) << "DISABLING REFLECTIONS DUE TO POOR COMPUTER PERFORMANCE"; } // ========================================================================== */ } void ReflectionGraphicsScene::drawBackground ( QPainter * painter, const QRectF & rect ) { if (mBackground) QGraphicsScene::drawBackground(painter, rect); }