File indexing completed on 2024-12-01 03:47:55

0001 /*
0002     SPDX-FileCopyrightText: 2003 Russell Steffen <rsteffen@bayarea.net>
0003     SPDX-FileCopyrightText: 2003 Stephan Zehetner <s.zehetner@nevox.org>
0004     SPDX-FileCopyrightText: 2006 Dmitry Suzdalev <dimsuz@gmail.com>
0005     SPDX-FileCopyrightText: 2006 Inge Wallin <inge@lysator.liu.se>
0006     SPDX-FileCopyrightText: 2006 Pierre Ducroquet <pinaraf@gmail.com>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "game.h"
0012 #include "planet.h"
0013 #include <KLocalizedString>
0014 #include <QDebug>
0015 #include <cmath>
0016 
0017 QRandomGenerator Game::random = QRandomGenerator(QRandomGenerator::global()->generate());
0018 
0019 Game::Game(QObject *parent) :
0020     QObject(parent)
0021 {
0022     m_finalState = new QFinalState();
0023     m_turnCounter = 0;
0024     m_map = new Map(10, 10);
0025     m_gameMachine.addState(m_finalState);
0026     m_neutral = new NeutralPlayer(this);
0027     connect(&m_gameMachine, &QStateMachine::finished, this, &Game::finished);
0028 }
0029 
0030 Game::~Game()
0031 {
0032     delete m_map;
0033 }
0034 
0035 const QList<Planet*> Game::planets()
0036 {
0037     return m_map->planets();
0038 }
0039 
0040 bool Game::isRunning()
0041 {
0042     return m_gameMachine.isRunning();
0043 }
0044 
0045 Coordinate Game::generatePlanetCoordinates(int x, int y)
0046 {
0047     return Coordinate(random.bounded(x), random.bounded(y));
0048 }
0049 
0050 double Game::generateKillPercentage()
0051 {
0052     // 0.30 - 0.90
0053     return 0.30 + random.bounded(0.60);
0054 }
0055 
0056 int Game::generatePlanetProduction()
0057 {
0058     // 5 - 15
0059     return 5 + random.bounded(10);
0060 }
0061 
0062 bool Game::attack(Planet *sourcePlanet, Planet *destPlanet, long long shipCount, bool standingOrder)
0063 {
0064     int arrival = int(std::ceil(m_map->distance(sourcePlanet, destPlanet))) + m_turnCounter;
0065     if(standingOrder)
0066     {
0067         m_currentPlayer->addStandingOrder(new AttackFleet(sourcePlanet, destPlanet, shipCount, arrival));
0068         return true;
0069     }
0070     else
0071     {
0072         AttackFleet *fleet = sourcePlanet->fleet().spawnAttackFleet(destPlanet, shipCount, arrival);
0073         if (fleet) {
0074             m_currentPlayer->addAttackFleet(fleet);
0075             return true;
0076         }
0077         return false;
0078     }
0079 }
0080 
0081 void Game::setPlayers(const QList<Player *> &players)
0082 {
0083     m_players = players;
0084 }
0085 
0086 void Game::setCurrentPlayer(Player *player)
0087 {
0088     m_currentPlayer = player;
0089 }
0090 
0091 bool Game::doFleetArrival(AttackFleet *fleet)
0092 {
0093     // First, sanity check
0094     if (fleet->arrivalTurn != m_turnCounter)
0095         return false;
0096 
0097     // Check to see of (fleet owner) == (planet owner)
0098     // if the planet and fleet owner are the same, then merge the fleets
0099     // otherwise attack.
0100     if( fleet->owner == fleet->destination->player()) {
0101         fleet->destination->fleet().absorb(fleet);
0102         if ( !fleet->owner->isAiPlayer() )
0103             Q_EMIT gameMsg(ki18np("Reinforcements (1 ship) have arrived for planet %2.",
0104                             "Reinforcements (%1 ships) have arrived for planet %2.")
0105                 .subs(fleet->shipCount()), nullptr, fleet->destination);
0106     } else {
0107         // let's get ready to rumble...
0108         AttackFleet   *attacker       = fleet;
0109         Planet        *attackerPlanet = attacker->source;
0110         Planet        *defenderPlanet = attacker->destination;
0111         DefenseFleet  &defender       = defenderPlanet->fleet();
0112 
0113         bool  haveVictor  = false;
0114         bool  planetHolds = true;
0115 
0116         while( !haveVictor ) {
0117             double  attackerRoll = random.bounded(1.0);
0118             double  defenderRoll = random.bounded(1.0);
0119 
0120             /* special case if both have 0 kill percentages */
0121             if( defenderPlanet->killPercentage() == 0 &&
0122                 attackerPlanet->killPercentage() == 0) {
0123                 if(attackerRoll <  defenderRoll )
0124                     makeKill(&defender, attackerPlanet->player());
0125                 else
0126                     makeKill(attacker, defenderPlanet->player());
0127             }
0128 
0129             if( defenderRoll < defenderPlanet->killPercentage() )
0130                 makeKill(attacker, defenderPlanet->player());
0131 
0132             if( attacker->shipCount() <= 0 ) {
0133                 haveVictor  = true;
0134                 planetHolds = true;
0135                 continue;
0136             }
0137             if( attackerRoll < attackerPlanet->killPercentage() )
0138                 makeKill(&defender, attackerPlanet->player());
0139 
0140             if( defender.shipCount() <= 0 ) {
0141                 haveVictor  = true;
0142                 planetHolds = false;
0143             }
0144         }
0145 
0146         if( planetHolds ) {
0147             defenderPlanet->player()->statEnemyFleetsDestroyed(1);
0148             Q_EMIT gameMsg(ki18n("Planet %2 has held against an attack from %1."),
0149                          attacker->owner, defenderPlanet);
0150         } else {
0151             Player *defender = defenderPlanet->player();
0152             attacker->owner->statEnemyFleetsDestroyed( 1 );
0153 
0154             defenderPlanet->conquer( attacker );
0155 
0156             Q_EMIT gameMsg(ki18n("Planet %2 has fallen to %1."),
0157                          attacker->owner, defenderPlanet, defender);
0158         }
0159     }
0160     return true;
0161 }
0162 
0163 void Game::makeKill(Fleet *fleet, Player *player)
0164 {
0165     fleet->removeShips( 1 );
0166     player->statEnemyShipsDestroyed( 1 );
0167 }
0168 
0169 void Game::findWinner()
0170 {
0171     //qDebug() << "Searching for survivors";
0172     // Check for survivors
0173     Player *winner = nullptr;
0174     for (Player *player : players()) {
0175         if (player->isNeutral() || player->isSpectator()) {
0176             continue;
0177         }
0178         if (!player->isDead()) {
0179             if (winner) {
0180                 //qDebug() << "Ok, returning 0";
0181                 return;
0182             } else {
0183                 winner = player;
0184             }
0185         }
0186     }
0187     //qDebug() << "Ok, returning " << winner;
0188     if (winner)
0189     {
0190         // We got a winner
0191         //qDebug() << "Trying to stop";
0192         this->stop();
0193         Q_EMIT finished();
0194     }
0195 }
0196 
0197 #include "moc_game.cpp"