File indexing completed on 2022-09-27 13:19:25

0001 /*
0002     SPDX-FileCopyrightText: 1998-2001 Andreas Zehender <az@azweb.de>
0003     SPDX-FileCopyrightText: 2006-2007 Dirk Rathlev <dirkrathlev@gmx.de>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "mainview.h"
0009 #include "ai.h"
0010 #include "options.h"
0011 
0012 #include <KActionCollection>
0013 #include <KConfigGroup>
0014 #include <KLocalizedString>
0015 #include <KSharedConfig>
0016 #include <KToggleAction>
0017 
0018 #include <QAbstractEventDispatcher>
0019 #include <QBrush>
0020 #include <QFontDatabase>
0021 #include <QGraphicsSimpleTextItem>
0022 #include <QKeyEvent>
0023 #include <QKeySequence>
0024 #include <QResizeEvent>
0025 #include <QSvgRenderer>
0026 #include <QStandardPaths>
0027 
0028 #include <math.h>
0029 
0030 KToggleAction *MyMainView::pauseAction = nullptr;
0031 
0032 static struct
0033  {
0034      int id;
0035      const char *path;
0036  }
0037  kspd_animations [] =
0038  {
0039      { ID_EXPLOSION, "explos%1"},
0040      { ID_MINE1, "mine_red%1"},
0041      { ID_MINE2, "mine_blue%1"},
0042      { ID_MINEEXPLO, "mineex%1"},
0043      { 0, nullptr}
0044  };
0045 
0046 MyMainView::MyMainView(QWidget *parent)
0047     :QWidget(parent),
0048     field(this),//0,0,DEF_WIDTH,DEF_HEIGHT),
0049     view(&field,this)
0050 {
0051    int i,p;
0052    setMinimumSize(600,400);
0053    random.seed(QRandomGenerator::global()->generate());
0054    QPixmap backgr(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral(MV_BACKGROUND)));
0055 
0056    field.setBackgroundBrush(QBrush(backgr));
0057    view.setCacheMode(QGraphicsView::CacheBackground);
0058 
0059    setFocusPolicy(Qt::StrongFocus);
0060    // FIXME: is this needed anymore?
0061    //view.setResizePolicy(Q3ScrollView::AutoOne);
0062    view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0063    view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0064    view.setFrameStyle(QFrame::NoFrame);
0065 
0066    for(p=0;p<2;++p)
0067    {
0068       for(i=0;i<PlayerKeyNum;++i)
0069          playerKeyPressed[p][i]=false;
0070       bulletShot[p]=false;
0071       minePut[p]=false;
0072    }
0073 
0074    svgrender = new QSvgRenderer(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral(MV_SVG_FILE)));
0075 
0076    sun=new SunSprite(svgrender,QStringLiteral( MV_SUN ));
0077    field.addItem(sun);
0078    sun->setPos(QPointF(width()/2-1-(sun->width()/2),
0079                        height()/2-1-(sun->height()/2)));
0080 
0081    powerupelements[0] = QStringLiteral( MV_POWERMINE );
0082    powerupelements[1] = QStringLiteral( MV_POWERBULLET );
0083    powerupelements[2] = QStringLiteral( MV_POWERSHIELD );
0084    powerupelements[3] = QStringLiteral( MV_POWERENERGY );
0085 
0086    ship[0]=new ShipSprite(svgrender, QStringLiteral( MV_SHIP1 ), 0);
0087    ship[1]=new ShipSprite(svgrender, QStringLiteral( MV_SHIP2 ), 1);
0088    field.addItem(ship[0]);
0089    field.addItem(ship[1]);
0090 
0091    readSprites();
0092 
0093    for(i=0;i<2;++i)
0094    {
0095       // ship[i]->setBoundsAction(QwRealMobileSprite::Wrap);
0096       ship[i]->hide();
0097       bullets[i]=new QList<BulletSprite*>;
0098       mines[i]=new QList<MineSprite*>;
0099 
0100    }
0101 
0102    waitForStart=false;
0103    textSprite = nullptr;
0104    readConfig();
0105 }
0106 
0107 MyMainView::~MyMainView()
0108 {
0109    int i;
0110    QAbstractEventDispatcher::instance()->unregisterTimers(this);
0111 
0112    for(i=0;i<2;i++)
0113    {
0114       delete ai[i];
0115       qDeleteAll(*mines[i]);
0116       delete mines[i];
0117       qDeleteAll(*bullets[i]);
0118       delete bullets[i];
0119    }
0120 
0121    qDeleteAll(powerups);
0122    powerups.clear();
0123 
0124    qDeleteAll(explosions);
0125    explosions.clear();
0126 
0127    writeConfig();
0128    delete svgrender;
0129 }
0130 
0131 void MyMainView::setActionCollection(KActionCollection *a)
0132 {
0133    actionCollection = a;
0134 }
0135 
0136 /* Assumes that there are no gaps between animation frames. ie 1,2,3 will only have frames 1&2
0137    recognized. It also assumes that there is at least one frame. */
0138 // FIXME: Add Check for existence of first frame
0139 // TODO: Add support for missing frames (ie 1,2,5)
0140 bool MyMainView::readSprites()
0141 {
0142    int i = 0;
0143    while ( kspd_animations[i].id )
0144    {
0145       QList<QString> anim;
0146       short frame = 0;
0147       QString element =QLatin1String( kspd_animations[i].path );
0148       QString elem = element.arg(frame, 2, 10, QLatin1Char('0'));
0149       do
0150       {
0151      anim.append(elem);
0152      frame++;
0153      elem = element.arg(frame, 2, 10, QLatin1Char('0'));
0154       } while ( svgrender->elementExists(elem) );
0155       animation.insert( kspd_animations[i].id, anim );
0156       i++;
0157    }
0158 
0159    // FIXME: Perform test!
0160    return true;
0161 }
0162 
0163 void MyMainView::readConfig()
0164 {
0165    KConfigGroup game(KSharedConfig::openConfig(), "Game");
0166    int i;
0167 
0168    customConfig.gamespeed=game.readEntry("gamespeed",
0169                                                   predefinedConfig[0].gamespeed);
0170 
0171    customConfig.gravity=
0172       game.readEntry("gravity",predefinedConfig[0].gravity);
0173    customConfig.acc=
0174       game.readEntry("acceleration",predefinedConfig[0].acc);
0175    customConfig.bulletDamage=
0176       game.readEntry("bulletDamage",predefinedConfig[0].bulletDamage);
0177    customConfig.bulletLifeTime=
0178       game.readEntry("bulletLifeTime",predefinedConfig[0].bulletLifeTime);
0179    customConfig.bulletReloadTime=
0180       game.readEntry("bulletReloadTime",predefinedConfig[0].bulletReloadTime);
0181    customConfig.mineDamage=
0182       game.readEntry("mineDamage",predefinedConfig[0].mineDamage);
0183    customConfig.shipDamage=
0184       game.readEntry("shipDamage",predefinedConfig[0].shipDamage);
0185    customConfig.maxBullets=
0186       game.readEntry("maxBullets",predefinedConfig[0].maxBullets);
0187    customConfig.maxMines=
0188       game.readEntry("maxMines",predefinedConfig[0].maxMines);
0189    customConfig.mineReloadTime=
0190       game.readEntry("mineReloadTime",predefinedConfig[0].mineReloadTime);
0191    customConfig.rotationSpeed=
0192       game.readEntry("rotationSpeed",predefinedConfig[0].rotationSpeed);
0193    customConfig.shotSpeed=
0194       game.readEntry("shotSpeed",predefinedConfig[0].shotSpeed);
0195    customConfig.energyNeed=
0196       game.readEntry("accEnergyNeed",predefinedConfig[0].energyNeed);
0197    customConfig.rotationEnergyNeed=
0198       game.readEntry("rotationEnergyNeed",predefinedConfig[0].rotationEnergyNeed);
0199    customConfig.sunEnergy=
0200       game.readEntry("sunEnergy",predefinedConfig[0].sunEnergy);
0201    customConfig.mineActivateTime=
0202       game.readEntry("mineActivateTime",predefinedConfig[0].mineActivateTime);
0203    customConfig.mineFuel=
0204       game.readEntry("mineFuel",predefinedConfig[0].mineFuel);
0205    customConfig.shotEnergyNeed=
0206       game.readEntry("shotEnergyNeed",predefinedConfig[0].shotEnergyNeed);
0207    customConfig.mineEnergyNeed=
0208       game.readEntry("mineEnergyNeed",predefinedConfig[0].mineEnergyNeed);
0209    customConfig.startPosX=
0210       game.readEntry("startPosX",predefinedConfig[0].startPosX);
0211    customConfig.startPosY=
0212       game.readEntry("startPosY",predefinedConfig[0].startPosY);
0213    customConfig.startVelX=
0214       game.readEntry("startVelX",predefinedConfig[0].startVelX);
0215    customConfig.startVelY=
0216       game.readEntry("startVelY",predefinedConfig[0].startVelY);
0217    customConfig.powerupLifeTime=
0218       game.readEntry("powerupLifeTime",predefinedConfig[0].powerupLifeTime);
0219    customConfig.powerupRefreshTime=
0220       game.readEntry("powerupRefreshTime",predefinedConfig[0].powerupRefreshTime);
0221    customConfig.powerupShieldAmount=
0222       game.readEntry("powerupShieldAmount",
0223                               predefinedConfig[0].powerupShieldAmount);
0224    customConfig.powerupEnergyAmount=
0225       game.readEntry("powerupEnergyAmount",
0226                               predefinedConfig[0].powerupEnergyAmount);
0227 
0228    if(Options::lastConfig() < predefinedConfigNum)
0229       config=modifyConfig(predefinedConfig[Options::lastConfig()]);
0230    else
0231       config=modifyConfig(customConfig);
0232 
0233    for(i=0;i<2;++i)
0234       ai[i]=new Ai(i,ship,bullets,mines,&config);
0235 }
0236 
0237 void MyMainView::writeConfig()
0238 {
0239    KConfigGroup game(KSharedConfig::openConfig(), "Game");
0240 
0241    game.writeEntry("gravity",customConfig.gravity);
0242    game.writeEntry("acceleration",customConfig.acc);
0243    game.writeEntry("bulletDamage",customConfig.bulletDamage);
0244    game.writeEntry("bulletLifeTime",customConfig.bulletLifeTime);
0245    game.writeEntry("bulletReloadTime",customConfig.bulletReloadTime);
0246    game.writeEntry("mineDamage",customConfig.mineDamage);
0247    game.writeEntry("shipDamage",customConfig.shipDamage);
0248    game.writeEntry("maxBullets",customConfig.maxBullets);
0249    game.writeEntry("maxMines",customConfig.maxMines);
0250    game.writeEntry("rotationSpeed",customConfig.rotationSpeed);
0251    game.writeEntry("shotSpeed",customConfig.shotSpeed);
0252    game.writeEntry("accEnergyNeed",customConfig.energyNeed);
0253    game.writeEntry("rotationEnergyNeed",customConfig.rotationEnergyNeed);
0254    game.writeEntry("sunEnergy",customConfig.sunEnergy);
0255    game.writeEntry("mineActivateTime",customConfig.mineActivateTime);
0256    game.writeEntry("mineReloadTime",customConfig.mineReloadTime);
0257    game.writeEntry("mineFuel",customConfig.mineFuel);
0258    game.writeEntry("shotEnergyNeed",customConfig.shotEnergyNeed);
0259    game.writeEntry("mineEnergyNeed",customConfig.mineEnergyNeed);
0260 
0261    game.writeEntry("startPosX",customConfig.startPosX);
0262    game.writeEntry("startPosY",customConfig.startPosY);
0263    game.writeEntry("startVelX",customConfig.startVelX);
0264    game.writeEntry("startVelY",customConfig.startVelY);
0265 
0266    game.writeEntry("powerupLifeTime",customConfig.powerupLifeTime);
0267    game.writeEntry("powerupRefreshTime",customConfig.powerupRefreshTime);
0268    game.writeEntry("powerupShieldAmount",customConfig.powerupShieldAmount);
0269    game.writeEntry("powerupEnergyAmount",customConfig.powerupEnergyAmount);
0270 }
0271 
0272 SConfig MyMainView::modifyConfig(const SConfig &conf)
0273 {
0274    SConfig newConfig=conf;
0275    newConfig.gamespeed*=Options::refreshTime()/33.0;
0276    newConfig.acc*=newConfig.gamespeed;
0277    newConfig.rotationSpeed*=newConfig.gamespeed*M_PI/ROTNUM*4;
0278    newConfig.energyNeed*=newConfig.gamespeed;
0279    newConfig.rotationEnergyNeed*=newConfig.gamespeed;
0280    newConfig.mineActivateTime*=newConfig.gamespeed;
0281 
0282    return newConfig;
0283 }
0284 
0285 void MyMainView::keyPressEvent(QKeyEvent *ev)
0286 {
0287     // if-statement kept for historical reasons, maybe not needed anymore
0288     if ( ((gameEnd>0.0) || (gameEnd<=-2.0)) && (!waitForStart) )
0289     {
0290       if(actionCollection->action(QStringLiteral( "P1KeyLeft" ))->shortcuts().contains(ev->key()))
0291             playerKeyPressed[0][PlayerKeyLeft]=true;
0292       else if(actionCollection->action(QStringLiteral( "P2KeyLeft" ))->shortcuts().contains(ev->key()))
0293             playerKeyPressed[1][PlayerKeyLeft]=true;
0294 
0295       else if(actionCollection->action(QStringLiteral( "P1KeyRight" ))->shortcuts().contains(ev->key()))
0296             playerKeyPressed[0][PlayerKeyRight]=true;
0297       else if(actionCollection->action(QStringLiteral( "P2KeyRight" ))->shortcuts().contains(ev->key()))
0298             playerKeyPressed[1][PlayerKeyRight]=true;
0299 
0300       else if(actionCollection->action(QStringLiteral( "P1KeyAcc" ))->shortcuts().contains(ev->key()))
0301             playerKeyPressed[0][PlayerKeyAcc]=true;
0302       else if(actionCollection->action(QStringLiteral( "P2KeyAcc" ))->shortcuts().contains(ev->key()))
0303             playerKeyPressed[1][PlayerKeyAcc]=true;
0304 
0305       else if(actionCollection->action(QStringLiteral( "P1Shot" ))->shortcuts().contains(ev->key()))
0306             playerKeyPressed[0][PlayerKeyShot]=true;
0307       else if(actionCollection->action(QStringLiteral( "P2Shot" ))->shortcuts().contains(ev->key()))
0308             playerKeyPressed[1][PlayerKeyShot]=true;
0309 
0310       else if(actionCollection->action(QStringLiteral( "P1Mine" ))->shortcuts().contains(ev->key()))
0311             playerKeyPressed[0][PlayerKeyMine]=true;
0312       else if(actionCollection->action(QStringLiteral( "P2Mine" ))->shortcuts().contains(ev->key()))
0313             playerKeyPressed[1][PlayerKeyMine]=true;
0314       else
0315         ev->ignore();
0316     }
0317 }
0318 
0319 void MyMainView::keyReleaseEvent(QKeyEvent *ev)
0320 {
0321    if(actionCollection->action(QStringLiteral( "P1KeyLeft" ))->shortcuts().contains(ev->key()))
0322       playerKeyPressed[0][PlayerKeyLeft]=false;
0323    else if(actionCollection->action(QStringLiteral( "P2KeyLeft" ))->shortcuts().contains(ev->key()))
0324       playerKeyPressed[1][PlayerKeyLeft]=false;
0325 
0326    else if(actionCollection->action(QStringLiteral( "P1KeyRight" ))->shortcuts().contains(ev->key()))
0327       playerKeyPressed[0][PlayerKeyRight]=false;
0328    else if(actionCollection->action(QStringLiteral( "P2KeyRight" ))->shortcuts().contains(ev->key()))
0329       playerKeyPressed[1][PlayerKeyRight]=false;
0330 
0331    else if(actionCollection->action(QStringLiteral( "P1KeyAcc" ))->shortcuts().contains(ev->key()))
0332       playerKeyPressed[0][PlayerKeyAcc]=false;
0333    else if(actionCollection->action(QStringLiteral( "P2KeyAcc" ))->shortcuts().contains(ev->key()))
0334       playerKeyPressed[1][PlayerKeyAcc]=false;
0335 
0336    else if(actionCollection->action(QStringLiteral( "P1Shot" ))->shortcuts().contains(ev->key()))
0337       playerKeyPressed[0][PlayerKeyShot]=false;
0338    else if(actionCollection->action(QStringLiteral( "P2Shot" ))->shortcuts().contains(ev->key()))
0339       playerKeyPressed[1][PlayerKeyShot]=false;
0340 
0341    else if(actionCollection->action(QStringLiteral( "P1Mine" ))->shortcuts().contains(ev->key()))
0342       playerKeyPressed[0][PlayerKeyMine]=false;
0343    else if(actionCollection->action(QStringLiteral( "P2Mine" ))->shortcuts().contains(ev->key()))
0344       playerKeyPressed[1][PlayerKeyMine]=false;
0345    else
0346       ev->ignore();
0347 }
0348 
0349 void MyMainView::pause()
0350 {
0351    if( !waitForStart )
0352    {
0353       pauseAction->setChecked( true );
0354 
0355       waitForStart=true;
0356       QAbstractEventDispatcher::instance()->unregisterTimers(this);
0357       Q_EMIT setStatusText(i18n(" paused "), IDS_PAUSE);
0358    }
0359 }
0360 
0361 void MyMainView::resume()
0362 {
0363    waitForStart=false;
0364    timerID=startTimer(Options::refreshTime());
0365    Q_EMIT setStatusText(QLatin1String( "" ),IDS_PAUSE);
0366    Q_EMIT setStatusText(QLatin1String( "" ),IDS_MAIN);
0367 }
0368 
0369 void MyMainView::start( )
0370 {
0371    if( ( gameEnd <= 0.0 ) && ( gameEnd > -2.0 ) )
0372    {
0373       newRound( );
0374    }
0375    else if( waitForStart )
0376    {
0377       waitForStart = false;
0378       timerID=startTimer(Options::refreshTime());
0379       Q_EMIT setStatusText(QLatin1String( "" ),IDS_PAUSE);
0380       Q_EMIT setStatusText(QLatin1String( "" ),IDS_MAIN);
0381       pauseAction->setEnabled( true );
0382       pauseAction->setChecked( false );
0383    }
0384 }
0385 
0386 void MyMainView::stop()
0387 {
0388    pauseAction->setEnabled( false );
0389    pauseAction->setChecked( false );
0390 
0391    QAbstractEventDispatcher::instance()->unregisterTimers(this);
0392    waitForStart = true;
0393 }
0394 
0395 void MyMainView::togglePause( )
0396 {
0397    if( waitForStart )
0398       resume( );
0399    else
0400       pause( );
0401 }
0402 
0403 void MyMainView::resizeEvent(QResizeEvent *event)
0404 {
0405    double mx,my;
0406    int i,current;
0407    int listsize; // used for caching QtList::size()
0408 
0409    mx=(event->size().width()-event->oldSize().width())/2.0;
0410    my=(event->size().height()-event->oldSize().height())/2.0;
0411    QWidget::resizeEvent(event);
0412    view.resize(width(),height());
0413    field.setSceneRect(0, 0, width(),height());
0414 
0415    // printf("%d %d\n",field.width(),field.height());
0416 
0417    sun->setPos(QPointF(width()/2-1-(sun->width()/2),
0418                        height()/2-1-(sun->height()/2)));
0419 
0420    for(i=0;i<2;++i)
0421    {
0422       // ship[i]->adoptSpritefieldBounds();
0423       ship[i]->moveBy(mx,my);
0424 
0425       listsize = mines[i]->size();
0426       for (current=0; current<listsize; ++current)
0427       {
0428          // mine->adoptSpritefieldBounds();
0429          mines[i]->value(current)->moveBy(mx,my);
0430       }
0431 
0432       listsize = bullets[i]->size();
0433       for (current=0; current<listsize; ++current)
0434       {
0435          // bullet->adoptSpritefieldBounds();
0436          bullets[i]->value(current)->moveBy(mx,my);
0437       }
0438    }
0439    if(textSprite)
0440       textSprite->moveBy((int)mx,(int)my);
0441 
0442    listsize = powerups.size();
0443    for (current=0; current<listsize; ++current)
0444    {
0445       powerups[current]->moveBy(mx,my);
0446    }
0447 }
0448 
0449 void MyMainView::newRound()
0450 {
0451    double mx,my;
0452    int i;
0453 
0454    timeToNextPowerup=random.bounded(config.powerupRefreshTime);
0455    qDeleteAll(powerups);
0456    powerups.clear();
0457 
0458    QAbstractEventDispatcher::instance()->unregisterTimers(this);
0459    mx=width()/2.0;
0460    my=height()/2.0;
0461    ship[0]->setPos(QPointF(mx+config.startPosX-(ship[0]->width()/2),
0462                            my+config.startPosY-(ship[0]->height()/2)));
0463    ship[0]->setRotation(0.0);
0464 
0465    ship[1]->setPos(QPointF(mx-config.startPosX-(ship[1]->width()/2),
0466                            my-config.startPosY-(ship[1]->height()/2)));
0467    ship[1]->setRotation(M_PI);
0468 
0469    ship[0]->setVelocity(config.startVelX,config.startVelY);
0470    ship[1]->setVelocity(-config.startVelX,-config.startVelY);
0471    for(i=0;i<2;++i)
0472    {
0473       ship[i]->show();
0474       ship[i]->setEnergy(MAX_ENERGY);
0475       ship[i]->setHitPoints(Options::startHitPoints(i));
0476       ship[i]->stop(false);
0477       ship[i]->setExplosion(-1);
0478       Q_EMIT energy(i,(int)ship[i]->getEnergy());
0479       Q_EMIT hitPoints(i,ship[i]->getHitPoints());
0480       bulletShot[i]=false;
0481       qDeleteAll(*bullets[i]);
0482       bullets[i]->clear();
0483 
0484       qDeleteAll(*mines[i]);
0485       mines[i]->clear();
0486 
0487       ship[i]->mine(0.0);
0488       ship[i]->bullet(0.0);
0489       ship[i]->setBulletPowerups(0);
0490       ship[i]->setMinePowerups(0);
0491 
0492       ai[i]->newRound();
0493    }
0494    qDeleteAll(explosions);
0495    explosions.clear();
0496    gameEnd=-10.0;
0497    for(i=0;i<PlayerKeyNum;++i)
0498    {
0499       playerKeyPressed[0][i]=false;
0500       playerKeyPressed[1][i]=false;
0501    }
0502    if(textSprite)
0503    {
0504       textSprite->hide();
0505       delete textSprite;
0506       textSprite = nullptr;
0507    }
0508    //field.update();
0509 
0510    QString str = i18n("Press %1 to start",
0511                   actionCollection->action(QStringLiteral("game_start"))->shortcut().toString(QKeySequence::NativeText));
0512    Q_EMIT setStatusText(str,IDS_MAIN);
0513    Q_EMIT setStatusText( QLatin1String( "" ), IDS_PAUSE );
0514    stop( );
0515 }
0516 
0517 void MyMainView::newGame()
0518 {
0519    int i;
0520    for(i=0;i<2;i++)
0521    {
0522       ship[i]->setWins(0);
0523       Q_EMIT wins(i,0);
0524    }
0525    newRound();
0526 }
0527 
0528 void MyMainView::timerEvent(QTimerEvent *event)
0529 {
0530    unsigned w;
0531    int i;
0532    bool stopped = false;
0533 
0534    if(event->timerId()==timerID)
0535    {
0536       QAbstractEventDispatcher::instance()->unregisterTimers(this);
0537       if(gameEnd>0.0)
0538       {
0539          gameEnd-=1.0;
0540          if(gameEnd<=0.0)
0541          {
0542             stopped = true;
0543             if(textSprite)
0544             {
0545                textSprite->hide();
0546                delete textSprite;
0547                textSprite = nullptr;
0548             }
0549             textSprite = new QGraphicsSimpleTextItem(nullptr);
0550             field.addItem(textSprite);
0551         // FIXME
0552            // textSprite->setTextFlags(Qt::AlignCenter);
0553         textSprite->setBrush(QBrush(QColor(255,160,0)));
0554             textSprite->setFont(QFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont).family(),14));
0555             textSprite->show( );
0556             if(ship[0]->getHitPoints()==0)
0557             {
0558                if(ship[1]->getHitPoints()==0)
0559                   textSprite->setText(i18n("draw round"));
0560                else
0561                {
0562                   textSprite->setText(i18n("blue player won the round"));
0563                   w=ship[1]->getWins()+1;
0564                   ship[1]->setWins(w);
0565                   Q_EMIT wins(1,w);
0566                }
0567             }
0568             else
0569             {
0570                textSprite->setText(i18n("red player won the round"));
0571                w=ship[0]->getWins()+1;
0572                ship[0]->setWins(w);
0573                Q_EMIT wins(0,w);
0574             }
0575             // must do this after setting text, because length is unknown until now
0576             textSprite->setPos(QPointF((width()-textSprite->boundingRect().width()) / 2,height()/2-90));
0577 
0578             QString str = i18n("Press %1 for new round",
0579                           actionCollection->action(QStringLiteral("game_start"))->shortcut().toString(QKeySequence::NativeText));
0580             Q_EMIT setStatusText(str,IDS_MAIN);
0581             stop( );
0582          }
0583       }
0584 
0585       if( !stopped )
0586       {
0587          for(i=0;i<2;++i)
0588             if(Options::playerIsAi(i)&&(ship[i]->getHitPoints()>0))
0589                ai[i]->think();
0590 
0591          moveMines();
0592          moveBullets();
0593          moveExplosions();
0594          moveShips();
0595          calculatePowerups();
0596          collisions();
0597          timerID=startTimer(Options::refreshTime());
0598       }
0599      //field.update();
0600    }
0601 }
0602 
0603 void MyMainView::moveShips()
0604 {
0605    int i,olde;
0606    double nx,ny,en,nr;
0607    BulletSprite *bullet;
0608    MineSprite *mine;
0609 
0610 
0611    for(i=0;i<2;++i)
0612    {
0613       bool playerIsAi = Options::playerIsAi(i);
0614       olde=(int)ship[i]->getEnergy();
0615       if(ship[i]->getHitPoints()==0)
0616       {
0617          ship[i]->forward(config.gamespeed);
0618          ship[i]->calculateGravityAndEnergy(config.gravity,config.sunEnergy,
0619                                             config.gamespeed);
0620       }
0621       else
0622       {
0623          ship[i]->calculateGravityAndEnergy(config.gravity,config.sunEnergy,
0624                                          config.gamespeed);
0625 
0626 
0627          if ((!playerIsAi && playerKeyPressed[i][PlayerKeyRight]) ||
0628              (playerIsAi && ai[i]->rotateRight()))
0629             ship[i]->rotateRight(config.rotationEnergyNeed,
0630                                  config.rotationSpeed);
0631 
0632          if ((!playerIsAi && playerKeyPressed[i][PlayerKeyLeft]) ||
0633              (playerIsAi && ai[i]->rotateLeft()))
0634             ship[i]->rotateLeft(config.rotationEnergyNeed,
0635                                 config.rotationSpeed);
0636 
0637          en=ship[i]->getEnergy();
0638          nr=ship[i]->getRotation();
0639 
0640          nx=cos(nr);
0641          ny=sin(nr);
0642          if (((!playerIsAi && playerKeyPressed[i][PlayerKeyAcc]) ||
0643               (playerIsAi && ai[i]->accelerate()))
0644          && (en>config.energyNeed) )
0645          {
0646             en-=config.energyNeed;
0647             ship[i]->setVelocity(ship[i]->xVelocity()+nx*config.acc,
0648                                  ship[i]->yVelocity()-ny*config.acc);
0649 
0650         // limit speed to avoid "tunneling" through other objects
0651         // FIXME: find a more elegant way
0652         if (ship[i]->xVelocity()*ship[i]->xVelocity()+
0653         ship[i]->yVelocity()*ship[i]->yVelocity() > MAX_VELOCITY*MAX_VELOCITY)
0654         {
0655             double alpha;
0656             alpha = fabs(atan(ship[i]->yVelocity()/ship[i]->xVelocity()));
0657             ship[i]->setVelocity(MAX_VELOCITY*cos(alpha)*fabs(ship[i]->xVelocity())/ship[i]->xVelocity(),
0658                                  MAX_VELOCITY*sin(alpha)*fabs(ship[i]->yVelocity())/ship[i]->yVelocity());
0659         }
0660          }
0661          if(en>MAX_ENERGY)
0662             en=MAX_ENERGY;
0663 
0664          ship[i]->forward(config.gamespeed);
0665 
0666              //Bullets and Mines
0667          if ((!playerIsAi && playerKeyPressed[i][PlayerKeyShot]) ||
0668              (playerIsAi && ai[i]->shootBullet()))
0669          {
0670             if((en>config.shotEnergyNeed) && (!ship[i]->reloadsBullet()))
0671             {
0672                if(bullets[i]->count() <
0673                   (static_cast<int>(config.maxBullets) + ship[i]->getBulletPowerups()))
0674                {
0675                   ship[i]->bullet(config.bulletReloadTime);
0676                   en-=config.shotEnergyNeed;
0677           if(i)
0678              bullet=new BulletSprite(svgrender, QStringLiteral( MV_BULLET2 ), i,
0679                          config.bulletLifeTime);
0680           else
0681              bullet=new BulletSprite(svgrender, QStringLiteral( MV_BULLET1 ), i,
0682                                           config.bulletLifeTime);
0683           field.addItem(bullet);
0684           QPointF p;
0685           p = ship[i]->mapToScene(ship[i]->center());
0686           bullet->setPos(QPointF(p.x()+nx*SHOTDIST,p.y()-ny*SHOTDIST));
0687                   bullet->setVelocity(ship[i]->xVelocity()+nx*config.shotSpeed,
0688                                       ship[i]->yVelocity()-ny*config.shotSpeed);
0689                   // bullet->setBoundsAction(QwRealMobileSprite::Wrap);
0690                   bullet->show();
0691                   bullets[i]->append(bullet);
0692                }
0693             }
0694          }
0695          if ((!Options::playerIsAi(i) && playerKeyPressed[i][PlayerKeyMine]) ||
0696              (Options::playerIsAi(i) && ai[i]->layMine()))
0697          {
0698             if((en>config.mineEnergyNeed) && (!ship[i]->reloadsMine()))
0699             {
0700                if(mines[i]->count() <
0701                   (static_cast<int>(config.maxMines) + ship[i]->getMinePowerups()))
0702                {
0703                   ship[i]->mine(config.mineReloadTime);
0704                   en-=config.mineEnergyNeed;
0705           if (i==0)
0706                      mine=new MineSprite(svgrender,animation[ID_MINE1],animation[ID_MINEEXPLO],i,
0707                                       config.mineActivateTime,config.mineFuel);
0708           else
0709              mine=new MineSprite(svgrender,animation[ID_MINE2],animation[ID_MINEEXPLO],i,
0710                                       config.mineActivateTime,config.mineFuel);
0711           field.addItem(mine);
0712           mine->setPos(ship[i]->mapToScene(ship[i]->center()));
0713                   // move mine to center
0714                   mine->moveBy(-mine->center().x(),-mine->center().y());
0715                   mine->setVelocity(0,0);
0716                   //mine->setBoundsAction(QwRealMobileSprite::Wrap);
0717                   mine->show();
0718                   mines[i]->append(mine);
0719                }
0720             }
0721          }
0722          ship[i]->setEnergy(en);
0723          if(olde!=(int)en)
0724             Q_EMIT energy(i,(int)en);
0725       }
0726    }
0727 }
0728 
0729 void MyMainView::moveMines()
0730 {
0731    int i;
0732    MineSprite* mine;
0733    int p;
0734    int listsize; // used for caching QtList::size()
0735 
0736    for(p=0;p<2;p++)
0737    {
0738       i=0;
0739       listsize = mines[p]->size();
0740       while (i<listsize)
0741       {
0742          mine = mines[p]->value(i);
0743          mine->calculateGravity(config.gravity,config.gamespeed);
0744          mine->forward(config.gamespeed);
0745          if(mine->over())
0746          {
0747             mine->hide();
0748             mines[p]->removeAt(i);
0749             delete mine;
0750             listsize--;
0751          }
0752          else
0753             i++;
0754       }
0755    }
0756 }
0757 
0758 void MyMainView::moveBullets()
0759 {
0760    int i,j;
0761    BulletSprite *sp;
0762    int listsize; // used for caching QtList::size()
0763 
0764    for(i=0;i<2;i++)
0765    {
0766       j=0;
0767       listsize = bullets[i]->size();
0768       while (j<listsize)
0769       {
0770          sp = bullets[i]->value(j);
0771          sp->calculateGravity(config.gravity,config.gamespeed);
0772          sp->forward(config.gamespeed);
0773          if(sp->timeOut())
0774          {
0775             sp->hide();
0776             bullets[i]->removeAll(sp);
0777             listsize--;
0778          }
0779          else
0780             j++;
0781       }
0782    }
0783 }
0784 
0785 void MyMainView::moveExplosions()
0786 {
0787    int i=0;
0788    ExplosionSprite *ex;
0789    int listsize; // used for caching QtList::size()
0790    listsize = explosions.size();
0791    while (i<listsize)
0792    {
0793       ex = explosions[i];
0794       ex->forward(config.gamespeed);
0795       if(ex->isOver())
0796       {
0797          explosions.removeAt(i);
0798          delete ex;
0799          listsize--;
0800       }
0801       else
0802          i++;
0803    }
0804 }
0805 
0806 void MyMainView::calculatePowerups()
0807 {
0808    int i=0;
0809    PowerupSprite *sp;
0810    int listsize; // used for caching QtList::size()
0811    listsize = powerups.size();
0812    while (i<listsize)
0813    {
0814       sp = powerups[i];
0815       sp->setLifetime(sp->getLifetime()-config.gamespeed);
0816       if (sp->getLifetime()<0)
0817       {
0818          powerups.removeAt(i);
0819          delete sp;
0820          listsize--;
0821       }
0822       else
0823          i++;
0824    }
0825    timeToNextPowerup-=config.gamespeed;
0826    if(timeToNextPowerup<0)
0827    {
0828       int type,x,y;
0829       timeToNextPowerup= random.bounded(config.powerupRefreshTime);
0830       type= random.bounded(PowerupSprite::PowerupNum);
0831       sp=new PowerupSprite(svgrender,powerupelements[type],type,
0832                            config.powerupLifeTime);
0833       field.addItem(sp);
0834       do
0835       {
0836          x = random.bounded(width()-40)+20;
0837          y = random.bounded(height()-40)+20;
0838       }
0839       while(((x-width()/2)*(x-width()/2)+(y-height()/2)*(y-height()/2))<(50*50));
0840       sp->setPos(QPointF(x,y));
0841       powerups.append(sp);
0842       sp->show();
0843    }
0844 }
0845 
0846 void MyMainView::collisions()
0847 {
0848    int pl,hp,oldhp[2],ohp;
0849    QList<QGraphicsItem *> unexact;
0850    BulletSprite *bullet;
0851    MineSprite *mine;
0852    ExplosionSprite *expl;
0853    ShipSprite *s;
0854    PowerupSprite *power;
0855    QList<QGraphicsItem *> hitlist;
0856    double ndx[2],ndy[2];
0857    double en;
0858    int i;
0859    int listsize; // used for caching QtList::size()
0860 
0861    for(pl=0;pl<2;++pl)
0862    {
0863       if(!ship[pl]->isStopped())
0864       {
0865          unexact.clear();
0866      unexact = ship[pl]->collidingItems(Qt::IntersectsItemBoundingRect);
0867          oldhp[pl]=hp=ship[pl]->getHitPoints();
0868          hitlist.clear();
0869      for (QGraphicsItem *sprite : std::as_const(unexact)) {
0870             if((sprite->type()!=S_EXPLOSION)
0871                && !((sprite->type()!=S_SUN)&&(ship[pl]->getHitPoints()==0)))
0872                if(ship[pl]->collidesWithItem(sprite,Qt::IntersectsItemShape))
0873                   if(!hitlist.contains(sprite))
0874                      hitlist.append(sprite);
0875          }
0876 
0877          for (QGraphicsItem *sprite : std::as_const(hitlist)) {
0878             switch(sprite->type())
0879             {
0880                case S_SUN:
0881                   hp=0;
0882                   ship[pl]->stop();
0883                   break;
0884                case S_BULLET:
0885                   bullet=(BulletSprite *)sprite;
0886                   bullet->hide();
0887                   //bullets[bullet->getPlayerNumber()]->removeRef(bullet);
0888                   bullets[bullet->getPlayerNumber()]->removeAll(bullet);
0889                   delete bullet;
0890                   hp-=config.bulletDamage;
0891                   break;
0892                case S_SHIP:
0893                   s=(ShipSprite*)sprite;
0894                   ohp=s->getHitPoints();
0895                   if(ohp>0)
0896                   {
0897                      s->setHitPoints(ohp-hp-config.shipDamage);
0898                      Q_EMIT hitPoints(s->getPlayerNumber(),s->getHitPoints());
0899                      ndx[0]=((1-EPSILON)*ship[0]->xVelocity()+(1+EPSILON)*ship[1]->xVelocity())/2.0;
0900                      ndy[0]=((1-EPSILON)*ship[0]->yVelocity()+(1+EPSILON)*ship[1]->yVelocity())/2.0;
0901                      ndx[1]=((1-EPSILON)*ship[1]->xVelocity()+(1+EPSILON)*ship[0]->xVelocity())/2.0;
0902                      ndy[1]=((1-EPSILON)*ship[1]->yVelocity()+(1+EPSILON)*ship[0]->yVelocity())/2.0;
0903                      ship[0]->setVelocity(ndx[0],ndy[0]);
0904                      ship[1]->setVelocity(ndx[1],ndy[1]);
0905                      hp-=ohp+config.shipDamage;
0906                   }
0907                   break;
0908                case S_MINE:
0909                   mine=(MineSprite *)sprite;
0910                   if(mine->isActive()&& !mine->explodes())
0911                   {
0912                      mine->explode();
0913                      ndx[0]=(ship[pl]->xVelocity()+0.3*mine->xVelocity())/1.3;
0914                      ndy[0]=(ship[pl]->yVelocity()+0.3*mine->yVelocity())/1.3;
0915                      ship[pl]->setVelocity(ndx[0],ndy[0]);
0916                      mine->setVelocity(ndx[0],ndy[0]);
0917                      hp-=config.mineDamage;
0918                   }
0919                   break;
0920                case S_POWERUP:
0921                   power=(PowerupSprite *)sprite;
0922                   switch(power->getType())
0923                   {
0924                      case PowerupSprite::PowerupShield:
0925                         hp+=config.powerupShieldAmount;
0926                         break;
0927                      case PowerupSprite::PowerupEnergy:
0928                         en=ship[pl]->getEnergy()+config.powerupEnergyAmount;
0929                         if(en>MAX_ENERGY)
0930                            en=MAX_ENERGY;
0931                         ship[pl]->setEnergy(en);
0932                         break;
0933                      case PowerupSprite::PowerupMine:
0934                         ship[pl]->setMinePowerups(
0935                            ship[pl]->getMinePowerups()+1);
0936                         break;
0937                      case PowerupSprite::PowerupBullet:
0938                         ship[pl]->setBulletPowerups(
0939                            ship[pl]->getMinePowerups()+1);
0940                         break;
0941                   }
0942                   power->hide();
0943                   powerups.removeAll(power);
0944                   delete power;
0945                   break;
0946             }
0947          }
0948          if(hp>MAX_HP)
0949             hp=MAX_HP;
0950          ship[pl]->setHitPoints(hp);
0951       }
0952 
0953       listsize = mines[pl]->size();
0954       for (i=0; i<listsize; ++i)
0955       {
0956          mine = mines[pl]->value(i);
0957          if(!mine->explodes())
0958          {
0959             unexact.clear();
0960             unexact=mine->collidingItems(Qt::IntersectsItemBoundingRect);
0961             hitlist.clear();
0962         for (QGraphicsItem *sprite : std::as_const(unexact)) {
0963                if(sprite->type()==S_BULLET)
0964                   if(mine->collidesWithItem(sprite))
0965                      if(!hitlist.contains(sprite))
0966                         hitlist.append(sprite);
0967             }
0968             if(!hitlist.isEmpty())
0969             {
0970                mine->explode();
0971            for (QGraphicsItem *item : std::as_const(hitlist)) {
0972                   // FIXME: why does it crash with qgraphicsitem_cast?
0973           bullet = static_cast<BulletSprite*>(item);// qgraphicsitem_cast<BulletSprite*>(item);
0974 //                   bullets[bullet->getPlayerNumber()]->removeRef(bullet);
0975                   bullets[bullet->getPlayerNumber()]->removeAll(bullet);
0976                   delete bullet;
0977                }
0978             }
0979          }
0980       }
0981    }
0982 
0983    hitlist.clear();
0984    unexact.clear();
0985    unexact=sun->collidingItems(Qt::IntersectsItemBoundingRect);
0986    for (QGraphicsItem *sprite : std::as_const(unexact)) {
0987       switch(sprite->type())
0988       {
0989          case S_BULLET:
0990             if(sun->collidesWithItem(sprite))
0991                if(!hitlist.contains(sprite))
0992                   hitlist.append(sprite);
0993             break;
0994          case S_MINE:
0995             if(!((MobileSprite*)sprite)->isStopped())
0996                if(sun->collidesWithItem(sprite))
0997                   if(!hitlist.contains(sprite))
0998                      hitlist.append(sprite);
0999             break;
1000       }
1001    }
1002 
1003    for (QGraphicsItem *sprite : std::as_const(hitlist)) {
1004       switch(sprite->type())
1005       {
1006          case S_BULLET:
1007             bullet=(BulletSprite *)sprite;
1008             bullet->hide();
1009             bullets[bullet->getPlayerNumber()]->removeAll(bullet);
1010             delete bullet;
1011             break;
1012          case S_MINE:
1013             mine=(MineSprite*)sprite;
1014             mine->stop();
1015             if(!mine->explodes())
1016                mine->explode();
1017             break;
1018       }
1019    }
1020 
1021 
1022    for(pl=0;pl<2;++pl)
1023    {
1024       hp=ship[pl]->getHitPoints();
1025       if(hp!=oldhp[pl])
1026          Q_EMIT hitPoints(pl,hp);
1027       if((hp==0)&&(ship[pl]->getExplosion()<0))
1028       {
1029          ship[pl]->setExplosion((int)(EXPLOSION_TIME/config.gamespeed));
1030      expl = new ExplosionSprite(svgrender,animation[ID_EXPLOSION],ship[pl]);
1031      field.addItem(expl);
1032          expl->show();
1033          explosions.append(expl);
1034          gameEnd=Options::timeAfterKill()/config.gamespeed;
1035       }
1036    }
1037 }
1038 
1039 void MyMainView::gameSetup()
1040 {
1041   if(!waitForStart)
1042     pause();
1043 
1044   if (KConfigDialog::showDialog( QStringLiteral( "settings" )))
1045     return;
1046 
1047   SettingsDialog *settings=new SettingsDialog(&customConfig,this,"settings");
1048   connect(settings, &SettingsDialog::settingsUpdated, this, &MyMainView::closeSettings);
1049   settings->show();
1050 }
1051 
1052 void MyMainView::closeSettings(){
1053   if(Options::lastConfig()<predefinedConfigNum)
1054     config=modifyConfig(predefinedConfig[Options::lastConfig()]);
1055   else
1056     config=modifyConfig(customConfig);
1057 }
1058 
1059 void MyMainView::focusOutEvent (QFocusEvent * /*event*/)
1060 {
1061    /* FIXME: ugly hack, I think
1062       it's maybe better to declare something like QMyScene:public QGraphicsScene and process input there */
1063    setFocus(Qt::OtherFocusReason);
1064 }
1065 
1066