File indexing completed on 2023-09-24 08:17:45
0001 /* 0002 This file is part of the KDE games lskat program 0003 SPDX-FileCopyrightText: 2006 Martin Heni <kde@heni-online.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "cardsprite.h" 0009 #include "lskat_debug.h" 0010 0011 // General includes 0012 #include <cmath> 0013 0014 // Qt includes 0015 #include <QGraphicsScene> 0016 #include <QTransform> 0017 0018 // KF includes 0019 #include <KConfigGroup> 0020 0021 // Delay for card turning animation [ms] 0022 #define ANIM_CNT_TURNING 20.0 0023 // Time in [ms] for a shuffle move 0024 #define SHUFFLEMOVE_TIME 100.0 0025 0026 // Theme manager stuff 0027 #define THEME_ID "card" 0028 0029 // Constructor for the view 0030 CardSprite::CardSprite(const Suite suite, const CardType cardtype, ThemeManager *theme, 0031 int advancePeriod, QGraphicsScene *scene) 0032 : Themable(QStringLiteral(THEME_ID), theme), QGraphicsPixmapItem(nullptr) 0033 0034 { 0035 scene->addItem(this); 0036 mAnimationState = Idle; 0037 mCurrentFrame = -1; // Frame will be set to backside 0038 mAdvancePeriod = advancePeriod; 0039 mSuite = suite; 0040 mCardType = cardtype; 0041 mFrames.clear(); 0042 0043 // Redraw 0044 if (theme) theme->updateTheme(this); 0045 } 0046 0047 // Main themable function. Called for any theme change. The sprites needs to 0048 // resize and redraw here. 0049 void CardSprite::changeTheme() 0050 { 0051 // Get scaling change 0052 double oldscale = this->getScale(); 0053 double scale = thememanager()->getScale(); 0054 Themable::setScale(scale); 0055 0056 // Retrieve theme data from configuration 0057 KConfigGroup config = thememanager()->config(id()); 0058 double width = config.readEntry("width", 1.0); 0059 width *= scale; 0060 mWidth = width; // Store for later use 0061 0062 // Z-value by program only 0063 // Nothing to do 0064 0065 // Animation 0066 int startFrame = config.readEntry("start-frame", 0); 0067 int endFrame = config.readEntry("end-frame", 0); 0068 0069 // Pos py program only 0070 setPos(x() * scale / oldscale, y() * scale / oldscale); 0071 0072 // Frames (loaded on demand) 0073 mFrames.clear(); 0074 mHotspots.clear(); 0075 QPixmap nullPixmap; 0076 for (int i = startFrame; i <= endFrame; i++) 0077 { 0078 mFrames.append(nullPixmap); 0079 mHotspots.append(QPointF(0.0, 0.0)); 0080 } 0081 0082 // Start with backside to save calculation time 0083 if (mCurrentFrame < 0) mCurrentFrame = endFrame; 0084 0085 // Set pixmap to sprite 0086 setFrame(mCurrentFrame, true); 0087 update(); 0088 } 0089 0090 // Stop all movement and animation 0091 void CardSprite::stop() 0092 { 0093 mAnimationState = Idle; 0094 mTime = 0.0; 0095 setBackside(); 0096 } 0097 0098 // Set the current animation mode of this sprite 0099 void CardSprite::setTurning(bool front) 0100 { 0101 mFrontFlag = front; 0102 mAnimationState = Turning; 0103 mTime = 0.0; 0104 } 0105 0106 // Set target position and calculate moving speed. 0107 void CardSprite::calcTargetAndSpeed(QPointF pos, double time) 0108 { 0109 // Convert time from [ms] to advance cycles 0110 time = time / mAdvancePeriod; 0111 0112 double scale = getScale(); 0113 mMoveTarget = pos; 0114 // Calculate move speed so that the duration for the move 0115 // is fixed 0116 double dx = mMoveTarget.x() - x() / scale; 0117 double dy = mMoveTarget.y() - y() / scale; 0118 double angle = atan2(dy, dx); 0119 mMoveSpeedX = cos(angle) * sqrt(dx * dx + dy * dy) / time; 0120 mMoveSpeedY = sin(angle) * sqrt(dx * dx + dy * dy) / time; 0121 } 0122 0123 // Move the sprite to the given relative position 0124 void CardSprite::setPosition(QPointF pos) 0125 { 0126 mStart = pos; 0127 setPos(mStart.x() * getScale(), mStart.y() * getScale()); 0128 } 0129 0130 // Move the sprite slowly to the target area. Stop movement if it arrived there. 0131 void CardSprite::setMove(QPointF pos, double time) 0132 { 0133 mAnimationState = Moving; 0134 mTime = 0.0; 0135 calcTargetAndSpeed(pos, time); 0136 } 0137 0138 // Move the sprite slowly to the target area. Stop 0139 // movement and remove sprite if it arrived there. 0140 void CardSprite::setRemove(QPointF pos, double time) 0141 { 0142 mAnimationState = Removing; 0143 calcTargetAndSpeed(pos, time); 0144 } 0145 0146 // Delay before moving, then move the sprite fast to the 0147 // target area. Stop movement and depending on the last 0148 // argument turn backside/frontside sprite if it arrived there. 0149 void CardSprite::setShuffleMove(QPointF pos, double delay, bool front) 0150 { 0151 setBackside(); 0152 calcTargetAndSpeed(pos, SHUFFLEMOVE_TIME); 0153 mAnimationState = ShuffleMove; 0154 mTime = delay; 0155 mFrontFlag = front; 0156 } 0157 0158 // Display the card front pixmap image 0159 void CardSprite::setFrontside() 0160 { 0161 // Choose card front frame 0162 setFrame(0); 0163 } 0164 0165 // Display the card back pixmap image 0166 void CardSprite::setBackside() 0167 { 0168 // Choose card back frame (last one in the animation sequence) 0169 setFrame(mFrames.size() - 1); 0170 } 0171 0172 int CardSprite::count() 0173 { 0174 return mFrames.count(); 0175 } 0176 0177 // Set a new bitmap into the sprite. If the number is the same as the 0178 // current one, nothing is done. 0179 void CardSprite::setFrame(int no, bool force) 0180 { 0181 if (!force && no == mCurrentFrame) return; 0182 if (no < 0 || no >= mFrames.count()) return; 0183 0184 // Calculate Pixmap (only done if necessary) 0185 calcFrame(no); 0186 0187 // Set frame pixmap 0188 QPixmap pixmap = mFrames.at(no); 0189 setPixmap(pixmap); 0190 0191 // Translation 0192 QPoint offset = thememanager()->getOffset(); 0193 resetTransform(); 0194 setTransform(QTransform::fromTranslate(mHotspots[no].x() + offset.x(), mHotspots[no].y() + offset.y()), true); 0195 0196 mCurrentFrame = no; 0197 update(); 0198 } 0199 0200 // Calculate a pixmap for a frame. Only calculates it if it 0201 // is not previously stored to avoid double calculations. 0202 void CardSprite::calcFrame(int no) 0203 { 0204 QPixmap pixmap = mFrames.at(no); 0205 // Check whether frame is already loaded 0206 if (pixmap.isNull()) 0207 { 0208 double dx = 0.0; 0209 double dy = 0.0; 0210 // Frontside 0211 if (no == 0) 0212 { 0213 pixmap = thememanager()->getCard(mSuite, mCardType, mWidth); 0214 } 0215 // Backside 0216 else if (no >= mFrames.count() - 1) 0217 { 0218 pixmap = thememanager()->getCardback(mWidth); 0219 } 0220 // Animation 0221 else 0222 { 0223 QPixmap front = thememanager()->getCard(mSuite, mCardType, mWidth); 0224 QPixmap back = thememanager()->getCardback(mWidth); 0225 pixmap = createCard(front, back, no, mFrames.count()); 0226 dx = (front.width() - pixmap.width()) / 2.0; 0227 dy = (front.height() - pixmap.height()) / 2.0; 0228 } 0229 mFrames[no] = pixmap; 0230 mHotspots[no] = QPointF(dx, dy); 0231 } 0232 } 0233 0234 // Perform a move by a delta given by the sprites velocity. 0235 // Returns true if the target position is reached 0236 bool CardSprite::deltaMove() 0237 { 0238 // Calculate difference vector 0239 double scale = getScale(); 0240 double dx = mMoveTarget.x() - x() / scale; 0241 double dy = mMoveTarget.y() - y() / scale; 0242 0243 // Check arrival at target 0244 if (dx * dx + dy * dy < mMoveSpeedX * mMoveSpeedX + mMoveSpeedY * mMoveSpeedY) 0245 { 0246 setPosition(mMoveTarget); 0247 return true; 0248 } 0249 // Move towards target by given velocity 0250 else 0251 { 0252 setPosition(QPointF(x() / scale + mMoveSpeedX, y() / scale + mMoveSpeedY)); 0253 return false; 0254 } 0255 } 0256 0257 // CanvasItem advance method 0258 void CardSprite::advance(int phase) 0259 { 0260 // Ignore phase 0 (collisions) 0261 if (phase == 0) 0262 { 0263 QGraphicsItem::advance(phase); 0264 return; 0265 } 0266 0267 // Turn a card 0268 if (mAnimationState == Turning) 0269 { 0270 mTime += mAdvancePeriod; 0271 // Turn delay counter 0272 if (mTime >= ANIM_CNT_TURNING) 0273 { 0274 mTime = 0.0; 0275 // Check whether animation is over 0276 if ((mFrontFlag && frame() == 0) || 0277 (!mFrontFlag && frame() == mFrames.size() - 1)) 0278 { 0279 mAnimationState = Idle; 0280 } 0281 else 0282 { 0283 if (mFrontFlag) setFrame(frame() - 1); 0284 else setFrame(frame() + 1); 0285 } 0286 } 0287 }// end if Turning 0288 0289 // Move a card 0290 else if (mAnimationState == Moving) 0291 { 0292 // Perform a move by a delta given by the sprites velocity. 0293 if (deltaMove()) 0294 { 0295 mAnimationState = Idle; 0296 } 0297 }// end if Moving 0298 0299 // Move a card 0300 else if (mAnimationState == Removing) 0301 { 0302 // Perform a move by a delta given by the sprites velocity. 0303 if (deltaMove()) 0304 { 0305 // Turn to backside 0306 setTurning(false); 0307 } 0308 }// end if Removing 0309 0310 // Shuffle move a card 0311 else if (mAnimationState == ShuffleMove) 0312 { 0313 // First delay move until counter is run down 0314 if (mTime > 0.0) 0315 { 0316 mTime -= mAdvancePeriod; 0317 } 0318 // Then move to target position 0319 else 0320 { 0321 // Perform a move by a delta given by the sprites velocity. 0322 if (deltaMove()) 0323 { 0324 if (mFrontFlag) mAnimationState = Turning; 0325 else mAnimationState = Idle; 0326 mTime = 0.0; 0327 } 0328 } 0329 }// end if ShuffleMove 0330 0331 QGraphicsItem::advance(phase); 0332 } 0333 0334 // Create turn animation, i.e. card combined out of backside and frontside 0335 QPixmap CardSprite::createCard(const QPixmap &front, const QPixmap &back, int curNo, int count) 0336 { 0337 int halfCount = count / 2; 0338 // Turn the frontside of the card 0..90 degree 0339 if (curNo < halfCount) 0340 { 0341 QTransform m; 0342 // Get an angle eps..90 deg for the values i is running 0343 double angle = (double)curNo / (double)halfCount * 90.0; 0344 // Conversion to rad 0345 angle = angle / 180.0 * M_PI; 0346 // Scale pixmap to simulate rotating card 0347 m.scale(cos(angle), 1.0); 0348 QPixmap pm = front.transformed(m, Qt::SmoothTransformation); 0349 return pm; 0350 } 0351 0352 // Turn the backside of the card 90..eps degree 0353 else 0354 { 0355 QTransform m; 0356 // Get an angle 0..90 deg for the values i is running 0357 double angle = 90.0 - ((double)(curNo - halfCount + 1) / (double)halfCount * 90.0); 0358 // Conversion to rad 0359 angle = angle / 180.0 * M_PI; 0360 // Scale pixmap to simulate rotating card 0361 m.scale(cos(angle), 1.0); 0362 QPixmap pm = back.transformed(m, Qt::SmoothTransformation); 0363 return pm; 0364 } 0365 }