1/*
2 Copyright 2003 Russell Steffen <rsteffen@bayarea.net>
3 Copyright 2003 Stephan Zehetner <s.zehetner@nevox.org>
4 Copyright 2006 Dmitry Suzdalev <dimsuz@gmail.com>
5 Copyright 2006 Inge Wallin <inge@lysator.liu.se>
6 Copyright 2006 Pierre Ducroquet <pinaraf@gmail.com>
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23#include "game.h"
24#include "players/player.h"
25#include "planet.h"
26#include <KLocalizedString>
27#include <KDebug>
28#include <cmath>
29
30KRandomSequence Game::random = KRandomSequence();
31
32Game::Game(QObject *parent) :
33 QObject(parent)
34{
35 m_finalState = new QFinalState();
36 m_turnCounter = 0;
37 m_map = new Map(10, 10);
38 m_gameMachine.addState(m_finalState);
39 m_neutral = new NeutralPlayer(this);
40 connect(&m_gameMachine, SIGNAL(finished()), this, SIGNAL(finished()));
41}
42
43QList<Planet*> Game::planets()
44{
45 return m_map->planets();
46}
47
48bool Game::isRunning()
49{
50 return m_gameMachine.isRunning();
51}
52
53Coordinate Game::generatePlanetCoordinates(int x, int y)
54{
55 return Coordinate(random.getLong(x), random.getLong(y));
56}
57
58double Game::generateKillPercentage()
59{
60 // 0.30 - 0.90
61 return 0.30 + random.getDouble()*0.60;
62}
63
64int Game::generatePlanetProduction()
65{
66 // 5 - 15
67 return 5 + random.getLong(10);
68}
69
70bool Game::attack(Planet *sourcePlanet, Planet *destPlanet, int shipCount, bool standingOrder)
71{
72 int arrival = int(std::ceil(m_map->distance(sourcePlanet, destPlanet))) + m_turnCounter;
73 if(standingOrder)
74 {
75 m_currentPlayer->addStandingOrder(new AttackFleet(sourcePlanet, destPlanet, shipCount, arrival));
76 return true;
77 }
78 else
79 {
80 AttackFleet *fleet = sourcePlanet->fleet().spawnAttackFleet(destPlanet, shipCount, arrival);
81 if (fleet) {
82 m_currentPlayer->addAttackFleet(fleet);
83 return true;
84 }
85 return false;
86 }
87}
88
89void Game::setPlayers(const QList<Player *> &players)
90{
91 m_players = players;
92}
93
94void Game::setCurrentPlayer(Player *player)
95{
96 m_currentPlayer = player;
97}
98
99bool Game::doFleetArrival(AttackFleet *fleet)
100{
101 // First, sanity check
102 if (fleet->arrivalTurn != m_turnCounter)
103 return false;
104
105 // Check to see of (fleet owner) == (planet owner)
106 // if the planet and fleet owner are the same, then merge the fleets
107 // otherwise attack.
108 if( fleet->owner == fleet->destination->player()) {
109 fleet->destination->fleet().absorb(fleet);
110 if ( !fleet->owner->isAiPlayer() )
111 emit gameMsg(ki18np("Reinforcements (1 ship) have arrived for planet %2.",
112 "Reinforcements (%1 ships) have arrived for planet %2.")
113 .subs(fleet->shipCount()), 0, fleet->destination);
114 } else {
115 // let's get ready to rumble...
116 AttackFleet *attacker = fleet;
117 Planet *attackerPlanet = attacker->source;
118 Planet *defenderPlanet = attacker->destination;
119 DefenseFleet &defender = defenderPlanet->fleet();
120
121 bool haveVictor = false;
122 bool planetHolds = true;
123
124 while( !haveVictor ) {
125 double attackerRoll = random.getDouble();
126 double defenderRoll = random.getDouble();
127
128 /* special case if both have 0 kill percentages */
129 if( defenderPlanet->killPercentage() == 0 &&
130 attackerPlanet->killPercentage() == 0) {
131 if(attackerRoll < defenderRoll )
132 makeKill(&defender, attackerPlanet->player());
133 else
134 makeKill(attacker, defenderPlanet->player());
135 }
136
137 if( defenderRoll < defenderPlanet->killPercentage() )
138 makeKill(attacker, defenderPlanet->player());
139
140 if( attacker->shipCount() <= 0 ) {
141 haveVictor = true;
142 planetHolds = true;
143 continue;
144 }
145 if( attackerRoll < attackerPlanet->killPercentage() )
146 makeKill(&defender, attackerPlanet->player());
147
148 if( defender.shipCount() <= 0 ) {
149 haveVictor = true;
150 planetHolds = false;
151 }
152 }
153
154 if( planetHolds ) {
155 defenderPlanet->player()->statEnemyFleetsDestroyed(1);
156 emit gameMsg(ki18n("Planet %2 has held against an attack from %1."),
157 attacker->owner, defenderPlanet);
158 } else {
159 Player *defender = defenderPlanet->player();
160 attacker->owner->statEnemyFleetsDestroyed( 1 );
161
162 defenderPlanet->conquer( attacker );
163
164 emit gameMsg(ki18n("Planet %2 has fallen to %1."),
165 attacker->owner, defenderPlanet, defender);
166 }
167 }
168 return true;
169}
170
171void Game::makeKill(Fleet *fleet, Player *player)
172{
173 fleet->removeShips( 1 );
174 player->statEnemyShipsDestroyed( 1 );
175}
176
177void Game::findWinner()
178{
179 kDebug() << "Searching for survivors";
180 // Check for survivors
181 Player *winner = 0;
182 foreach (Player *player, m_players) {
183 if (player->isNeutral() || player->isSpectator()) {
184 continue;
185 }
186 if (!player->isDead()) {
187 if (winner) {
188 kDebug() << "Ok, returning 0";
189 return;
190 } else {
191 winner = player;
192 }
193 }
194 }
195 kDebug() << "Ok, returning " << winner;
196 if (winner)
197 {
198 // We got a winner
199 kDebug() << "Trying to stop";
200 this->stop();
201 emit(finished());
202 }
203}
204