/*************************************************************************** kgrcanvas.cpp - description ------------------- begin : Wed Jan 23 2002 copyright : (C) 2002 by Marco Krüger and Ian Wadham email : See menu "Help, About KGoldrunner" ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifdef KGR_PORTABLE // If compiling for portability, redefine KDE's i18n. #define i18n tr #endif #include "kgrconsts.h" #include "kgrdialog.h" #include "kgrcanvas.h" #include "kgrgame.h" // Graphics files for moving figures and background. #include "hero.xpm" #include "enemy1.xpm" #include "enemy2.xpm" #include "kgraphics.h" class KGoldrunner; KGrCanvas::KGrCanvas (QWidget * parent, const char *name) : QCanvasView (0, parent, name) { setBackgroundMode (NoBackground); m = new QCursor (); // For handling the mouse. scaleStep = STEP; // Initial scale is 1:1. baseScale = scaleStep; baseFontSize = fontInfo().pointSize(); border = 4; // Allow 2 tile-widths on each side for border. cw = 4*STEP; // Playfield cell width (= four steps). bw = border*cw/2; // Total border width (= two cells). lw = cw/8; // Line width (for edge of border). mw = bw - lw; // Border main-part width. initView(); // Set up the graphics, etc. } KGrCanvas::~KGrCanvas() { } void KGrCanvas::changeLandscape (const QString & name) { for (int i = 0; strcmp (colourScheme [i], "") != 0; i++) { if (colourScheme [i] == name) { // Change XPM colours and re-draw the tile-pictures used by QCanvas. changeColours (& colourScheme [i]); makeTiles(); // Set all cells to same tile-numbers as before, but new colours. int tileNo [FIELDWIDTH] [FIELDHEIGHT]; int offset = border / 2; for (int x = 0; x < FIELDWIDTH; x++) { for (int y = 0; y < FIELDHEIGHT; y++) { tileNo[x][y] = field->tile (x + offset, y + offset); } } field->setTiles (bgPix, (FIELDWIDTH+border), (FIELDHEIGHT+border), bgw, bgh); // Sets all tile-numbers to 0. for (int x = 0; x < FIELDWIDTH; x++) { for (int y = 0; y < FIELDHEIGHT; y++) { field->setTile (x + offset, y + offset, tileNo[x][y]); } } borderB->setBrush (QBrush (borderColor)); borderL->setBrush (QBrush (borderColor)); borderR->setBrush (QBrush (borderColor)); QString t = title->text(); makeTitle (); setTitle (t); // Repaint the playing area. updateCanvas(); return; } } } bool KGrCanvas::changeSize (int d) { #ifdef QT3 if ((d < 0) && (scaleStep <= STEP)) { // Note: Smaller scales lose detail (e.g. the joints in brickwork). KGrMessage::information (this, i18n("Change Size"), i18n("Sorry, you cannot make the play area any smaller.")); return FALSE; } if ((d >= 0) && (scaleStep >= 2 * STEP)) { // Note: Larger scales go off the edge of the monitor. KGrMessage::information (this, i18n("Change Size"), i18n("Sorry, you cannot make the play area any larger.")); return FALSE; } QWMatrix wm = worldMatrix(); double wmScale = 1.0; // Set the scale back to 1:1 and calculate the new scale factor. wm.reset(); scaleStep = (d < 0) ? (scaleStep - 1) : (scaleStep + 1); // If scale > 1:1, scale up to the new factor (e.g. 1.25:1, 1.5:1, etc.) if (scaleStep > STEP) { wmScale = (wmScale * scaleStep) / STEP; wm.scale (wmScale, wmScale); } setWorldMatrix (wm); // Force the title size and position to be re-calculated. QString t = title->text(); makeTitle (); setTitle (t); // Fit the QCanvasView and its frame to the canvas. int frame = frameWidth()*2; setFixedSize ((FIELDWIDTH + 4) * 4 * scaleStep + frame, (FIELDHEIGHT + 4) * 4 * scaleStep + frame); return TRUE; #else KGrMessage::information (this, i18n( "Change Size" ), i18n( "Sorry, you cannot change the size of the playing area. " "That function requires Qt Library version 3 or later." )); return FALSE; #endif } void KGrCanvas::updateCanvas() { field->update(); } void KGrCanvas::paintCell (int x, int y, char type, int offset) { int tileNumber = 0; switch (type) { case FREE: tileNumber = freebg; break; // Free space. case NUGGET: tileNumber = nuggetbg; break; // Nugget. case POLE: tileNumber = polebg; break; // Pole or bar. case LADDER: tileNumber = ladderbg; break; // Ladder. case HLADDER: tileNumber = hladderbg; break;// Hidden ladder (for editing). case HERO: tileNumber = edherobg; break; // Static hero (for editing). case ENEMY: tileNumber = edenemybg; break;// Static enemy (for editing). case BETON: tileNumber = betonbg; break; // Concrete. case BRICK: tileNumber = brickbg; break; // Solid brick. case FBRICK: tileNumber = fbrickbg; break; // False brick (for editing). default: tileNumber = freebg; break; } tileNumber = tileNumber + offset; // Offsets 1-9 are for digging sequence. // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. x++; y++; field->setTile (x, y, tileNumber); // Paint cell with required pixmap. } void KGrCanvas::setBaseScale () { // Synchronise the desktop font size with the initial canvas scale. baseScale = scaleStep; QString t = title->text(); makeTitle (); setTitle (t); } void KGrCanvas::setTitle (QString newTitle) { title->setText (newTitle); } void KGrCanvas::makeTitle () { // This uses a calculated QLabel and QFont size because a QCanvasText // object does not always display scaled-up fonts cleanly (in Qt 3.1.1). if (title != 0) title->close (TRUE); // Close and delete previous title. title = new QLabel ("", this); title->setFixedWidth (((FIELDWIDTH * cw + 2 * bw) * scaleStep) / STEP); title->setFixedHeight ((mw * scaleStep) / STEP); title->move (0, 0); title->setPaletteBackgroundColor (borderColor); title->setPaletteForegroundColor (textColor); title->setFont (QFont (fontInfo().family(), (baseFontSize * scaleStep) / baseScale, QFont::Bold)); title->setAlignment (Qt::AlignCenter); title->raise(); title->show(); } void KGrCanvas::contentsMousePressEvent (QMouseEvent * m) { emit mouseClick (m->button ()); } void KGrCanvas::contentsMouseReleaseEvent (QMouseEvent * m) { emit mouseLetGo (m->button ()); } QPoint KGrCanvas::getMousePos () { int i, j; int fw = frameWidth(); int cell = 4 * scaleStep; QPoint p = mapFromGlobal (m->pos()); // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. i = ((p.x() - fw) / cell) - 1; j = ((p.y() - fw) / cell) - 1; return (QPoint (i, j)); } void KGrCanvas::setMousePos (int i, int j) { int fw = frameWidth(); int cell = 4 * scaleStep; // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. i++; j++; //m->setPos (mapToGlobal (QPoint (i * 4 * STEP + 8, j * 4 * STEP + 8))); //m->setPos (mapToGlobal (QPoint (i * 5 * STEP + 10, j * 5 * STEP + 10))); m->setPos (mapToGlobal ( QPoint (i * cell + fw + cell / 2, j * cell + fw + cell / 2))); } void KGrCanvas::makeHeroSprite (int i, int j, int startFrame) { heroSprite = new QCanvasSprite (heroArray, field); // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. i++; j++; heroSprite->move (i * 4 * STEP, j * 4 * STEP, startFrame); heroSprite->setZ (1); heroSprite->setVisible (TRUE); } void KGrCanvas::setHeroVisible (bool newState) { heroSprite->setVisible (newState); // Show or hide the hero. } void KGrCanvas::makeEnemySprite (int i, int j, int startFrame) { QCanvasSprite * enemySprite = new QCanvasSprite (enemyArray, field); enemySprites->append (enemySprite); // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. i++; j++; enemySprite->move (i * 4 * STEP, j * 4 * STEP, startFrame); enemySprite->setZ (2); enemySprite->show(); } void KGrCanvas::moveHero (int x, int y, int frame) { // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. heroSprite->move (x + 4 * STEP, y + 4 * STEP, frame); updateCanvas(); } void KGrCanvas::moveEnemy (int id, int x, int y, int frame, int nuggets) { if (nuggets != 0) { // If enemy is carrying gold, frame = frame + goldEnemy; // show him with gold outline. } // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. enemySprites->at(id)->move (x + 4 * STEP, y + 4 * STEP, frame); updateCanvas(); } void KGrCanvas::deleteEnemySprites() { enemySprites->clear(); } QPixmap KGrCanvas::getPixmap (char type) { QPixmap pic (bgw, bgh, bgd); QPainter p (& pic); int tileNumber; // Get a pixmap from the tile-array for use on an edit-button. switch (type) { case FREE: tileNumber = freebg; break; // Free space. case NUGGET: tileNumber = nuggetbg; break; // Nugget. case POLE: tileNumber = polebg; break; // Pole or bar. case LADDER: tileNumber = ladderbg; break; // Ladder. case HLADDER: tileNumber = hladderbg; break;// Hidden ladder (for editing). case HERO: tileNumber = edherobg; break; // Static hero (for editing). case ENEMY: tileNumber = edenemybg; break;// Static enemy (for editing). case BETON: tileNumber = betonbg; break; // Concrete. case BRICK: tileNumber = brickbg; break; // Solid brick. case FBRICK: tileNumber = fbrickbg; break; // False brick (for editing). default: tileNumber = freebg; break; } // Copy a tile of width bgw and height bgh from the tile-array. p.drawPixmap (0, 0, bgPix, tileNumber * bgw, 0, bgw, bgh); p.end(); return (pic); } void KGrCanvas::initView() { changeColours (& colourScheme [0]); // Set "KGoldrunner" landscape. // Set up the pixmaps for the editable objects. freebg = 0; // Free space. nuggetbg = 1; // Nugget. polebg = 2; // Pole or bar. ladderbg = 3; // Ladder. hladderbg = 4; // Hidden ladder (for editing). edherobg = 5; // Static hero (for editing). edenemybg = 6; // Static enemy (for editing). betonbg = 7; // Concrete. // The bricks have 10 pixmaps (showing various stages of digging). brickbg = 8; // Solid brick - 1st pixmap. fbrickbg = 15; // False brick - 8th pixmap (for editing). QPixmap pixmap; QImage image; pixmap = QPixmap (hgbrick_xpm); bgw = pixmap.width(); // Save dimensions for "getPixmap". bgh = pixmap.height(); bgd = pixmap.depth(); // Assemble the background and editing pixmaps into a strip (18 pixmaps). bgPix = QPixmap ((brickbg + 10) * bgw, bgh, bgd); makeTiles(); // Fill the strip with 18 tiles. // Define the canvas as an array of tiles. Default tile is 0 (free space). int frame = frameWidth()*2; field = new QCanvas ((FIELDWIDTH+border) * bgw, (FIELDHEIGHT+border) * bgh); field->setTiles (bgPix, (FIELDWIDTH+border), (FIELDHEIGHT+border), bgw, bgh); // Embed the canvas in the view and make it occupy the whole of the view. setCanvas (field); setVScrollBarMode (QScrollView::AlwaysOff); setHScrollBarMode (QScrollView::AlwaysOff); setFixedSize (field->width() + frame, field->height() + frame); ////////////////////////////////////////////////////////////////////////// // The pixmaps for hero and enemies are arranged in strips of 20: walk // // right (4), walk left (4), climb right along bar (4), climb left (4), // // climb up ladder (2) and fall (2) --- total 20. // ////////////////////////////////////////////////////////////////////////// // Convert the pixmap strip for hero animation into a QCanvasPixmapArray. pixmap = QPixmap (hero_xpm); image = pixmap.convertToImage (); #ifdef QT3 QPixmap pm; QValueList pmList; for (int i = 0; i < 20; i++) { pm.convertFromImage (image.copy (i * 16, 0, 16, 16)); pmList.append (pm); } heroArray = new QCanvasPixmapArray (pmList); // Hot spots all (0,0). #else QPixmap * pm; QPoint * pt; QList pmList; QList ptList; pt = new QPoint (0, 0); // "Hot spot" not used in KGoldrunner. for (int i = 0; i < 20; i++) { pm = new QPixmap (); pm->convertFromImage (image.copy (i * 16, 0, 16, 16)); pmList.append (pm); ptList.append (pt); } heroArray = new QCanvasPixmapArray (pmList, ptList); #endif // Convert pixmap strips for enemy animations into a QCanvasPixmapArray. // First convert the pixmaps for enemies with no gold ... pixmap = QPixmap (enemy1_xpm); image = pixmap.convertToImage (); pmList.clear(); #ifdef QT3 for (int i = 0; i < 20; i++) { pm.convertFromImage (image.copy (i * 16, 0, 16, 16)); pmList.append (pm); } #else ptList.clear(); for (int i = 0; i < 20; i++) { pm = new QPixmap (); pm->convertFromImage (image.copy (i * 16, 0, 16, 16)); pmList.append (pm); ptList.append (pt); } #endif // ... then convert the gold-carrying enemies. pixmap = QPixmap (enemy2_xpm); image = pixmap.convertToImage (); #ifdef QT3 for (int i = 0; i < 20; i++) { pm.convertFromImage (image.copy (i * 16, 0, 16, 16)); pmList.append (pm); } enemyArray = new QCanvasPixmapArray (pmList); // Hot spots all (0,0). #else for (int i = 0; i < 20; i++) { pm = new QPixmap (); pm->convertFromImage (image.copy (i * 16, 0, 16, 16)); pmList.append (pm); ptList.append (pt); } enemyArray = new QCanvasPixmapArray (pmList, ptList); #endif goldEnemy = 20; // Offset of gold-carrying frames. // Draw the border around the playing area (z = 0). makeBorder(); // Create a title item, in off-white colour, on top of the border. title = 0; makeTitle(); // Create an empty list of enemy sprites. #ifdef QT3 enemySprites = new QPtrList (); #else enemySprites = new QList (); #endif enemySprites->setAutoDelete(TRUE); } void KGrCanvas::makeTiles () { QPainter p (& bgPix); // First draw the single pixmaps (8 tiles) ... p.drawPixmap (freebg * bgw, 0, QPixmap (hgbrick_xpm)); // Free space. p.drawPixmap (nuggetbg * bgw, 0, QPixmap (nugget_xpm)); // Nugget. p.drawPixmap (polebg * bgw, 0, QPixmap (pole_xpm)); // Pole or bar. p.drawPixmap (ladderbg * bgw, 0, QPixmap (ladder_xpm)); // Ladder. p.drawPixmap (hladderbg * bgw, 0, QPixmap (hladder_xpm)); // Hidden laddr. p.drawPixmap (edherobg * bgw, 0, QPixmap (edithero_xpm)); // Static hero. p.drawPixmap (edenemybg * bgw, 0, QPixmap (editenemy_xpm)); // Static enemy. p.drawPixmap (betonbg * bgw, 0, QPixmap (beton_xpm)); // Concrete. // ... then add the 10 brick pixmaps. p.drawPixmap (brickbg * bgw, 0, QPixmap (bricks_xpm)); // Bricks. p.end(); } void KGrCanvas::makeBorder () { // Draw main part of border, in the order: top, bottom, left, right. // Allow some overlap to prevent slits appearing when using "changeSize". colour = borderColor; // The first rectangle is actually a QLabel drawn by "makeTitle()". // borderT = drawRectangle (11, 0, 0, FIELDWIDTH*cw + 2*bw, mw); borderB = drawRectangle (11, 0, FIELDHEIGHT*cw + bw + lw, FIELDWIDTH*cw + 2*bw, mw); borderL = drawRectangle (12, 0, bw - lw - 1, mw, FIELDHEIGHT*cw + 2*lw + 2); borderR = drawRectangle (12, FIELDWIDTH*cw + bw + lw, bw - lw - 1, mw, FIELDHEIGHT*cw + 2*lw + 2); // Draw inside edges of border, in the same way. colour = QColor (black); drawRectangle (10, bw-lw, bw-lw-1, FIELDWIDTH*cw + 2*lw, lw+1); drawRectangle (10, bw-lw, FIELDHEIGHT*cw + bw, FIELDWIDTH*cw + 2*lw, lw+1); drawRectangle (10, bw - lw, bw, lw, FIELDHEIGHT*cw); drawRectangle (10, FIELDWIDTH*cw + bw, bw, lw, FIELDHEIGHT*cw); } QCanvasRectangle * KGrCanvas::drawRectangle (int z, int x, int y, int w, int h) { QCanvasRectangle * r = new QCanvasRectangle (x, y, w, h, field); r->setBrush (QBrush (colour)); r->setPen (QPen (NoPen)); r->setZ (z); r->show(); return (r); } void KGrCanvas::changeColours (const char * colours []) { recolourObject (hgbrick_xpm, colours); recolourObject (nugget_xpm, colours); recolourObject (pole_xpm, colours); recolourObject (ladder_xpm, colours); recolourObject (hladder_xpm, colours); recolourObject (edithero_xpm, colours); recolourObject (edithero_xpm, colours); recolourObject (editenemy_xpm, colours); recolourObject (beton_xpm, colours); recolourObject (bricks_xpm, colours); borderColor = QColor (colours [1]); textColor = QColor (colours [2]); KGrThumbNail::backgroundColor = QColor (QString(colours [3]).right(7)); KGrThumbNail::brickColor = QColor (QString(colours [6]).right(7)); KGrThumbNail::ladderColor = QColor (QString(colours [9]).right(7)); KGrThumbNail::poleColor = QColor (QString(colours [11]).right(7)); } void KGrCanvas::recolourObject (const char * object [], const char * colours []) { int i; for (i = 0; i < 9; i++) { object [i+1] = colours [i+3]; } } #include "kgrcanvas.moc"