diff -Nru knavalbattle-4.12.3/data/pictures/default.desktop knavalbattle-4.12.90/data/pictures/default.desktop --- knavalbattle-4.12.3/data/pictures/default.desktop 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/data/pictures/default.desktop 2014-03-10 19:23:40.000000000 +0000 @@ -15,6 +15,7 @@ Name[hu]=Tengeri csata alapértelmezett Name[it]=Predefinito di Battaglia navale Name[kk]=Теңіз соғысының әдеттегісі +Name[ko]=전함 대전 기본 Name[mr]=नाविक युद्ध मूलभूत Name[nb]=Standard Sjøslag Name[nl]=Naval Battle standaard @@ -51,6 +52,7 @@ Description[hu]=Alapértelmezett téma a Tengeri csatához Description[it]=Il tema predefinito di Battaglia navale Description[kk]=Теңіз соғысының әдеттегі нақышы +Description[ko]=전함 대전 기본 테마 Description[mr]=नाविक युद्धाकरिता मूलभूत शैली Description[nb]=Standardtemaet for Sjøslag Description[nl]=Het standaardthema van Naval Battle diff -Nru knavalbattle-4.12.3/debian/changelog knavalbattle-4.12.90/debian/changelog --- knavalbattle-4.12.3/debian/changelog 2014-03-04 19:54:59.000000000 +0000 +++ knavalbattle-4.12.90/debian/changelog 2014-03-19 11:05:06.000000000 +0000 @@ -1,3 +1,9 @@ +knavalbattle (4:4.12.90-0ubuntu1) trusty; urgency=medium + + * New upstream beta release + + -- Jonathan Riddell Wed, 19 Mar 2014 11:05:05 +0000 + knavalbattle (4:4.12.3-0ubuntu1) trusty; urgency=medium * New upstream bugfix release diff -Nru knavalbattle-4.12.3/debian/control knavalbattle-4.12.90/debian/control --- knavalbattle-4.12.3/debian/control 2014-03-04 19:54:59.000000000 +0000 +++ knavalbattle-4.12.90/debian/control 2014-03-19 11:05:06.000000000 +0000 @@ -13,8 +13,8 @@ Maximiliano Curia Build-Depends: kde-sc-dev-latest (>= 4:4.10.2), cmake, debhelper (>= 9), pkg-kde-tools (>= 0.14), - kdelibs5-dev (>= 4:4.12.3), - libkdegames-dev (>= 4:4.12.3) + kdelibs5-dev (>= 4:4.12.90), + libkdegames-dev (>= 4:4.12.90) Standards-Version: 3.9.4 Homepage: http://games.kde.org/ Vcs-Browser: http://bazaar.launchpad.net/~kubuntu-packagers/kubuntu-packaging/knavalbattle diff -Nru knavalbattle-4.12.3/src/ai/ai.cpp knavalbattle-4.12.90/src/ai/ai.cpp --- knavalbattle-4.12.3/src/ai/ai.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/ai/ai.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -9,9 +9,10 @@ #include "ai.h" -AI::AI(Sea::Player player, Sea* sea) +AI::AI(Sea::Player player, Sea* sea, const BattleShipsConfiguration* config) : m_player(player) , m_sea(sea) +, m_config(config) { } @@ -27,3 +28,29 @@ return Coord::invalid(); } +void AI::setShips() +{ + // set up computer ships + // set first the biggest ship, it is more difficult to reach impossible combinations + // TODO: Another placing algorithm, create a list of available places and choose randomly from them. + // number of repetitions because the random place is over a previous ship = 0 + bool canFinish = true; + do { + for (int size = m_config->longestShip(); size >= 1; size--) { + for (unsigned int j = 1; j <= m_config->numberOfShipsOfSize(size); j++) { + Ship* ship = 0; + while (ship == 0 && canFinish) { + Coord c(rand() % m_sea->size().x, rand() % m_sea->size().y); + Ship::Direction dir = rand() % 2 == 0 ? Ship::LEFT_TO_RIGHT : Ship::TOP_DOWN; + if (m_sea->canAddShip(m_player, c, size, dir)) { + ship = new Ship(size, dir, c); + m_sea->add(m_player, ship); + } + else { + canFinish = m_sea->canAddShipOfSize(m_player, size); + } + } + } + } + } while ( !canFinish ); +} diff -Nru knavalbattle-4.12.3/src/ai/ai.h knavalbattle-4.12.90/src/ai/ai.h --- knavalbattle-4.12.3/src/ai/ai.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/ai/ai.h 2014-03-10 19:23:40.000000000 +0000 @@ -11,17 +11,19 @@ #define AI__AI_H #include "sea.h" +#include "ships.h" class AI { protected: Sea::Player m_player; Sea* m_sea; + const BattleShipsConfiguration* m_config; public: - AI(Sea::Player player, Sea* sea); + AI(Sea::Player player, Sea* sea, const BattleShipsConfiguration* config); virtual ~AI() { } virtual Coord getMove() = 0; - virtual void setShips() = 0; + virtual void setShips(); virtual void notify(Sea::Player player, const Coord& c, const HitInfo& hit) = 0; Coord desperateMove() const; }; diff -Nru knavalbattle-4.12.3/src/ai/dummyai.cpp knavalbattle-4.12.90/src/ai/dummyai.cpp --- knavalbattle-4.12.3/src/ai/dummyai.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/ai/dummyai.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -15,8 +15,8 @@ #include #include -DummyAI::DummyAI(Sea::Player player, Sea* sea) -: AI(player, sea) +DummyAI::DummyAI(Sea::Player player, Sea* sea, const BattleShipsConfiguration* config) +: AI(player, sea, config) { srand(time(0)); } @@ -35,19 +35,3 @@ return desperateMove(); } -void DummyAI::setShips() -{ - // set up computer ships - for (int i = 1; i <= 4; i++) { - Ship* ship = 0; - while (ship == 0) { - Coord c(rand() % m_sea->size().x, rand() % m_sea->size().y); - Ship::Direction dir = rand() % 2 == 0 ? Ship::LEFT_TO_RIGHT : Ship::TOP_DOWN; - if (m_sea->canAddShip(m_player, c, i, dir)) { - ship = new Ship(i, dir, c); - m_sea->add(m_player, ship); - } - } - } -} - diff -Nru knavalbattle-4.12.3/src/ai/dummyai.h knavalbattle-4.12.90/src/ai/dummyai.h --- knavalbattle-4.12.3/src/ai/dummyai.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/ai/dummyai.h 2014-03-10 19:23:40.000000000 +0000 @@ -15,9 +15,8 @@ class DummyAI : public AI { public: - DummyAI(Sea::Player player, Sea* sea); + DummyAI(Sea::Player player, Sea* sea, const BattleShipsConfiguration* config); virtual Coord getMove(); - virtual void setShips(); virtual void notify(Sea::Player, const Coord&, const HitInfo&) { } }; diff -Nru knavalbattle-4.12.3/src/ai/smartai.cpp knavalbattle-4.12.90/src/ai/smartai.cpp --- knavalbattle-4.12.3/src/ai/smartai.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/ai/smartai.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -29,20 +29,19 @@ , m_state(state) { } - + virtual ~Strategy() { } virtual Coord getMove() = 0; virtual Strategy* notify(const Coord& c, const HitInfo& info) = 0; }; - class DestroyStrategy : public Strategy { Coord m_original; Coord m_begin; Coord m_end; int m_direction; - + inline Coord direction() const { switch(m_direction) { @@ -57,14 +56,14 @@ return Coord(0, -1); } } - + bool next_try() { // if it only hit once if (m_begin == m_end) { // change direction m_direction++; - + if (m_direction > 3) { return false; // no more to do } @@ -81,12 +80,12 @@ else { // change direction on the same line m_direction += 2; - + // swap begin and end std::swap(m_begin, m_end); } } - + return true; } public: @@ -98,7 +97,7 @@ , m_direction(0) { } - + virtual Coord getMove() { for (;;) { @@ -118,7 +117,7 @@ } } } - + Strategy* notify(const Coord& c, const HitInfo& info) { if (info.shipDestroyed) { @@ -149,7 +148,7 @@ : Strategy(player, sea, state) { } - + virtual Coord getMove() { for (int i = 0; i < 10000; i++) { @@ -160,7 +159,7 @@ } return Coord::invalid(); } - + virtual Strategy* notify(const Coord& c, const HitInfo& info) { if (info.type == HitInfo::HIT && @@ -179,7 +178,7 @@ int m_gap; int m_offset; int m_range; - + // following a diagonal, return true if there is water at the enemy's panel. bool movesAvailable() const { Sea::Player opp = Sea::opponent(m_player); @@ -192,7 +191,7 @@ } return false; } - + Coord getMoveHelper() { int index = rand() % m_range; @@ -224,14 +223,14 @@ return Coord::invalid(); } - + void setup() { do { m_offset = rand() % m_gap; kDebug() << "offset =" << m_offset << " / " << m_gap; } while (!movesAvailable()); - + m_range = 0; for (int y = m_offset; y < m_sea->size().y; y += m_gap) { int diag = m_sea->size().y - y; @@ -258,14 +257,14 @@ virtual Coord getMove() { - + if (!movesAvailable()) { kDebug() << "no moves available"; setup(); } for (int i = 0; i < 50; i++) { Coord c = getMoveHelper(); - + if (m_sea->canHit(m_player, c)) { return c; } @@ -273,7 +272,7 @@ return Coord::invalid(); } - + virtual Strategy* notify(const Coord& c, const HitInfo& info) { if (info.type == HitInfo::HIT && @@ -287,9 +286,9 @@ } }; -SmartAI::SmartAI(Sea::Player player, Sea* sea, bool random) -: AI(player, sea) -, m_state(random) +SmartAI::SmartAI(Sea::Player player, Sea* sea, bool random, const BattleShipsConfiguration* config) +: AI(player, sea, config) +, m_state(random, config) { srand(time(0)); m_strategy = std::auto_ptr(m_state.defaultStrategy(player, sea)); @@ -323,27 +322,13 @@ } } -void SmartAI::setShips() -{ - for (int i = 1; i <= 4; i++) { - Ship* ship = 0; - while (ship == 0) { - Coord c(rand() % m_sea->size().x, rand() % m_sea->size().y); - Ship::Direction dir = rand() % 2 == 0 ? Ship::LEFT_TO_RIGHT : Ship::TOP_DOWN; - if (m_sea->canAddShip(m_player, c, i, dir)) { - ship = new Ship(i, dir, c); - m_sea->add(m_player, ship); - } - } - } -} - - -SmartAI::State::State(bool random) -: m_random(random) +SmartAI::State::State(bool random, const BattleShipsConfiguration* config) +: m_ships() +, m_random(random) +, m_config(config) { - for (int i = 0; i < LARGEST_SHIP; i++) { - m_ships[i] = 1; + for (unsigned int i = 0; i < m_config->longestShip(); i++) { + m_ships[i] = m_config->numberOfShipsOfSize(i+1); } } @@ -353,12 +338,12 @@ return new RandomStrategy(player, sea, *this); } else { - for (int i = LARGEST_SHIP - 1; i >= 0; i--) { + for (int i = m_config->longestShip() - 1; i >= 0; i--) { if (m_ships[i] > 0 || i == 0) { return new DiagonalStrategy(player, sea, *this, i + 1); } } - + // unreachable return 0; } @@ -366,14 +351,10 @@ void SmartAI::State::destroyed(int size) { - if (size <= LARGEST_SHIP) { + if ( size <= static_cast(m_config->longestShip()) ) { int index = size - 1; if (m_ships[index] > 0) { m_ships[index]--; } } } - - - - diff -Nru knavalbattle-4.12.3/src/ai/smartai.h knavalbattle-4.12.90/src/ai/smartai.h --- knavalbattle-4.12.3/src/ai/smartai.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/ai/smartai.h 2014-03-10 19:23:40.000000000 +0000 @@ -11,6 +11,7 @@ #define SMARTAI_H #include +#include #include "ai.h" #include "sea.h" @@ -22,11 +23,11 @@ public: class State { - static const int LARGEST_SHIP = 4; - int m_ships[LARGEST_SHIP]; + QHash m_ships; bool m_random; + const BattleShipsConfiguration* m_config; public: - explicit State(bool random); + explicit State(bool random, const BattleShipsConfiguration* config); Strategy* defaultStrategy(Sea::Player player, Sea*); void destroyed(int size); }; @@ -34,10 +35,9 @@ std::auto_ptr m_strategy; State m_state; public: - SmartAI(Sea::Player player, Sea* sea, bool random); - + SmartAI(Sea::Player player, Sea* sea, bool random, const BattleShipsConfiguration* config); + virtual Coord getMove(); - virtual void setShips(); virtual void notify(Sea::Player player, const Coord& c, const HitInfo& hit); }; diff -Nru knavalbattle-4.12.3/src/aientity.cpp knavalbattle-4.12.90/src/aientity.cpp --- knavalbattle-4.12.3/src/aientity.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/aientity.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -18,18 +18,18 @@ #include AIEntity::AIEntity(Sea::Player player, Sea* sea, SeaView *seaview) -: Entity(player, seaview) +: Entity(player, seaview, sea->battleShipsConfiguration()) , m_sea(sea) { switch (Kg::difficultyLevel()) { case KgDifficultyLevel::Easy: - m_ai = new DummyAI(m_player, m_sea); + m_ai = new DummyAI(m_player, m_sea, sea->battleShipsConfiguration()); break; case KgDifficultyLevel::Medium: - m_ai = new SmartAI(m_player, m_sea, true); + m_ai = new SmartAI(m_player, m_sea, true, sea->battleShipsConfiguration()); break; default: // hard - m_ai = new SmartAI(m_player, m_sea, false); + m_ai = new SmartAI(m_player, m_sea, false, sea->battleShipsConfiguration()); break; } } @@ -55,16 +55,27 @@ getShoot(); } +void AIEntity::notifyGameOptions(bool ask) +{ + emit gameOptionsInterchanged(); +} + void AIEntity::start(bool) { - kDebug() << m_player << ": starting"; - m_ai->setShips(); emit ready(m_player); } +void AIEntity::startPlacing(bool ask) +{ + m_seaview->setStatus(Sea::PLACING_SHIPS); + m_ai->setShips(); + emit shipsPlaced(m_player); +} + void AIEntity::startPlaying() { getShoot(); + m_seaview->setStatus(Sea::PLAYING); } void AIEntity::hit(Shot* shot) diff -Nru knavalbattle-4.12.3/src/aientity.h knavalbattle-4.12.90/src/aientity.h --- knavalbattle-4.12.3/src/aientity.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/aientity.h 2014-03-10 19:23:40.000000000 +0000 @@ -31,12 +31,15 @@ virtual void notifyChat(const Entity*, const QString&) { } virtual void notifyNick(Sea::Player, const QString&) { } virtual void start(bool); + virtual void startPlacing(bool ask); virtual void startPlaying(); virtual void hit(Shot* shot); + virtual void notifyGameOptions(bool ask); virtual KIcon icon() const; public slots: virtual void notifyAbort() { } + virtual void notifyRestartPlacing(Sea::Player player) { }; }; diff -Nru knavalbattle-4.12.3/src/animation.cpp knavalbattle-4.12.90/src/animation.cpp --- knavalbattle-4.12.3/src/animation.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/animation.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -63,7 +63,7 @@ } } -FadeAnimation::FadeAnimation(QGraphicsItem* sprite, int from, int to, int time) +FadeAnimation::FadeAnimation(QGraphicsItem* sprite, int from, qreal to, int time) : m_sprite(sprite) , m_from(from) , m_to(to) @@ -85,11 +85,12 @@ return true; } else { - if (m_to > m_from) - m_sprite->setOpacity(m_from + t * (1.0 / m_time)); - else - m_sprite->setOpacity(m_from - t * (1.0 / m_time)); - + if (m_to > m_from) { + m_sprite->setOpacity(m_from + t * (m_to / m_time)); + } + else { + m_sprite->setOpacity(m_from - t * (m_to / m_time)); + } return false; } } diff -Nru knavalbattle-4.12.3/src/animation.h knavalbattle-4.12.90/src/animation.h --- knavalbattle-4.12.3/src/animation.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/animation.h 2014-03-10 19:23:40.000000000 +0000 @@ -49,11 +49,11 @@ Q_OBJECT QGraphicsItem* m_sprite; int m_from; - int m_to; + qreal m_to; int m_time; int m_start; public: - FadeAnimation(QGraphicsItem* sprite, int from, int to, int time); + FadeAnimation(QGraphicsItem* sprite, int from, qreal to, int time); virtual void start(int t); virtual bool step(int t); }; diff -Nru knavalbattle-4.12.3/src/battlefield.cpp knavalbattle-4.12.90/src/battlefield.cpp --- knavalbattle-4.12.3/src/battlefield.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/battlefield.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -14,24 +14,29 @@ #include "sea.h" #include "settings.h" -BattleField::BattleField(Sea* parent, const Coord& size) +BattleField::BattleField(Sea* parent, const Coord& size, const bool allow_adjacent_ships) : QObject(parent) , m_size(size) , m_board(size) +, m_secondary_board(size) +, m_allow_adjacent_ships(allow_adjacent_ships) , m_ships(0) { + setUpSecondaryBoard(); +} + +void BattleField::setUpSecondaryBoard() +{ + for (int i = 0; i < m_size.x; i++) { + for (int j = 0; j < m_size.y ; j++) { + m_secondary_board[Coord(i,j)] = false; + } + } } BattleField::~BattleField() { - QSet deleted_ships; - FOREACH_SQUARE(p, m_board) { - Ship* ship = m_board[p].parent(); - if (ship && !deleted_ships.contains(ship)) { - delete ship; - deleted_ships.insert(ship); - } - } + clear(); } bool BattleField::valid(const Coord& pos) const @@ -69,6 +74,9 @@ p = p + ship->increment(); } m_ships++; + + addSecondaryBoard(ship); + addBorderSecondaryBoard(ship); } void BattleField::addBorder(const Coord& pos) @@ -88,6 +96,40 @@ } } +void BattleField::addSecondaryBoard(Ship* ship) +{ + Coord p = ship->position(); + for (unsigned int i = 0; i < ship->size(); i++) { + m_secondary_board[p]=true; + p += ship->increment(); + } +} + +void BattleField::addBorderSecondaryBoard(Ship* ship) +{ + if ( !m_allow_adjacent_ships ) + { + Coord inc = ship->increment(); + Coord orth(inc.y, inc.x); + Coord p = ship->position() - inc; + if ( valid (p) ) { + m_secondary_board[p]=true; + } + for (; p != ship->position() + inc * (ship->size() + 1); p += inc) { + if ( valid( p + orth ) ) { + m_secondary_board[p + orth]=true; + } + if ( valid( p - orth ) ) { + m_secondary_board[p - orth]=true; + } + } + p -= inc; + if ( valid (p) ) { + m_secondary_board[p]=true; + } + } +} + bool BattleField::canAddShip(const Coord& pos, unsigned int size, Ship::Direction direction) const { Coord p = pos; @@ -133,6 +175,56 @@ return true; } +bool BattleField::canAddShipOfSizeInHorizontal(unsigned int size) const +{ + unsigned int maxLenAvailable = 0; + for (int j=0; j( contiguousLen, maxLenAvailable ); + if (maxLenAvailable >= size) + { + return true; + } + } + } + } + return false; +} + +bool BattleField::canAddShipOfSizeInVertical(unsigned int size) const +{ + unsigned int maxLenAvailable = 0; + for (int i=0; i( contiguousLen, maxLenAvailable ); + if (maxLenAvailable >= size) + { + return true; + } + } + } + } + return false; +} + +bool BattleField::canAddShipOfSize(unsigned int size) const +{ + return canAddShipOfSizeInHorizontal(size) || canAddShipOfSizeInVertical(size); +} + + HitInfo BattleField::hit(const Coord& pos) { Element& e = get(pos); @@ -199,4 +291,25 @@ return false; } +void BattleField::clear() +{ + for (int i = 0; i < m_size.x; i++) { + for (int j = 0; j < m_size.y ; j++) { + m_secondary_board[Coord(i,j)] = false; + } + } + QSet deleted_ships; + FOREACH_SQUARE(p, m_board) { + Ship* ship = m_board[p].parent(); + if (ship && !deleted_ships.contains(ship)) { + delete ship; + deleted_ships.insert(ship); + } + m_board[p].setType(Element::WATER); + m_board[p].setParent(0); + } + m_ships=0; +} + + #include "battlefield.moc" diff -Nru knavalbattle-4.12.3/src/battlefield.h knavalbattle-4.12.90/src/battlefield.h --- knavalbattle-4.12.3/src/battlefield.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/battlefield.h 2014-03-10 19:23:40.000000000 +0000 @@ -23,12 +23,21 @@ typedef Grid Board; Coord m_size; Board m_board; + // Using a second board to annotate the real space used by the ships + // Another solution would be to annotate the borders along with the ships, + // and then send a signal to clean the borders when the shooting is going to start. + Grid m_secondary_board; bool m_allow_adjacent_ships; unsigned int m_ships; inline int convert(const Coord& c) const { return c.x + m_size.x * c.y; } + void setUpSecondaryBoard(); + void addSecondaryBoard(Ship* ship); + void addBorderSecondaryBoard(Ship *ship); + bool canAddShipOfSizeInHorizontal(unsigned int size) const; + bool canAddShipOfSizeInVertical(unsigned int size) const; public: - BattleField(Sea* parent, const Coord& size); + BattleField(Sea* parent, const Coord& size, const bool allow_adjacent_ships); ~BattleField(); bool valid(const Coord& pos) const; @@ -40,12 +49,14 @@ void add(int n); void addBorder(const Coord& pos); bool canAddShip(const Coord& pos, unsigned int size, Ship::Direction direction) const; + bool canAddShipOfSize(unsigned int size) const; HitInfo hit(const Coord& pos); void forceHit(const Coord& pos, const HitInfo& info); const Element& at(const Coord& c) const; Coord find(Ship* ship) const; bool isNearShip(const Coord& c) const; - + void setAllowAdjacentShips(const bool adjacent) { m_allow_adjacent_ships = adjacent; }; + void clear(); // to start placing the ships again in a clean BattleField inline unsigned int ships() const { return m_ships; } signals: void shipDestroyed(Ship*); diff -Nru knavalbattle-4.12.3/src/battlefieldview.cpp knavalbattle-4.12.90/src/battlefieldview.cpp --- knavalbattle-4.12.3/src/battlefieldview.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/battlefieldview.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -149,21 +149,43 @@ } } -void BattleFieldView::setPreview(const QPointF& pos, Ship* ship) +void BattleFieldView::setPreview(const QPoint & pos) { - if (!m_preview.sprite) { - m_preview.ship = ship; - m_preview.sprite = m_factory.createShip(ship); - kDebug() << "created preview: dir =" << ship->direction(); - m_preview.sprite->setOpacity(PREVIEW_OPACITY); - scene()->addItem(m_preview.sprite); - m_preview.sprite->stackBefore(m_screen); + if (!m_delegate) { + return; } - - m_preview.pos = m_renderer->toLogical(pos); + Ship * ship = m_delegate->nextShip(); + + if (!ship) { + return; + } + + loadPreviewSprite(ship); + Coord coordinate = m_renderer->toLogical(pos); + + if (m_delegate->canAddShip(m_player, coordinate)) { + m_preview.sprite->turnGreen(); + } else { + m_preview.sprite->turnRed(); + } + + QPointF scenePos = mapToScene(pos); + m_preview.pos = m_renderer->toLogical(scenePos); m_preview.sprite->setPos(m_renderer->toReal(m_preview.pos)); } +void BattleFieldView::loadPreviewSprite(Ship * ship) +{ + if (m_preview.ship) { + return; + } + + m_preview.ship = ship; + m_preview.sprite = m_factory.createShip(ship); + + m_preview.sprite->setOpacity(PREVIEW_OPACITY); + scene()->addItem(m_preview.sprite); +} void BattleFieldView::cancelPreview() { delete m_preview.sprite; @@ -174,7 +196,6 @@ void BattleFieldView::addSprite(const Coord& c, Sprite* sprite) { m_sprites.insert(c, sprite); - sprite->setPos(m_renderer->toReal(c)); scene()->addItem(sprite); } @@ -182,17 +203,21 @@ void BattleFieldView::add(Ship* ship) { Sprite* sprite = m_factory.createShip(ship); + sprite->setZValue(BACKGROUND); addSprite(ship->position(), sprite); // fading preview in - Animation* a = new FadeAnimation(sprite, PREVIEW_OPACITY, 1, 1000); - Animator::instance()->add(a); + if (ship->alive()) { + Animation* a = new FadeAnimation(sprite, PREVIEW_OPACITY, 1, 1000); + Animator::instance()->add(a); + } if (ship == m_preview.ship) { cancelPreview(); } else if (!ship->alive()) { - Animation* a = new FadeAnimation(sprite, 0, 1, 1000); + sprite->setZValue(BACKGROUND); + Animation* a = new FadeAnimation(sprite, 0, 0.5, 1000); Animator::instance()->add(a); } } @@ -200,7 +225,6 @@ void BattleFieldView::sink(Ship* ship) { m_last_hit = 0; - Sprite* ship_sprite = 0; Coord p = ship->position(); for (unsigned int i = 0; @@ -208,26 +232,22 @@ i++, p += ship->increment()) { foreach (Sprite* s, m_sprites.values(p)) { if (s->spriteKey().startsWith("ship")) { - ship_sprite = s; + s->setZValue(BACKGROUND); + s->setOpacity(0.5); } else if (s->spriteKey().startsWith("hit")) { s->setSpriteKey("hit-end"); - s->refresh(m_renderer); - s->setZValue(BACKGROUND); - s->setOpacity(0.5); } } } - if (ship_sprite) { - ship_sprite->stackBefore(m_background); - } } void BattleFieldView::hit(const Coord& c) { removeImpact(); m_last_hit = m_factory.createHit(); - m_last_hit->setZValue(BACKGROUND); + m_last_hit->setZValue(FOREGROUND); + m_last_hit->setOpacity(1.0); addSprite(c, m_last_hit); } @@ -235,7 +255,8 @@ { removeImpact(); m_impact = m_factory.createImpact(); - m_impact->setZValue(BACKGROUND); + m_impact->setZValue(FOREGROUND); + m_impact->setOpacity(1.0); addSprite(c, m_impact); } @@ -254,13 +275,15 @@ void BattleFieldView::clear() { + // fixes a crash when the ships can not be placed. + Animator::instance()->stop(); delete m_preview.sprite; m_preview.sprite = 0; m_preview.ship = 0; m_impact = 0; m_last_hit = 0; - + qDeleteAll(m_sprites); m_sprites.clear(); } @@ -281,11 +304,7 @@ else if (ev->button() == Qt::RightButton && m_delegate) { m_delegate->changeDirection(m_player); - - Coord c = m_renderer->toLogical(ev->pos()); - - if(Ship *ship = m_delegate->canAddShip(m_player, c)) - setPreview(mapToScene(ev->pos()), ship); + setPreview(ev->pos()); } } @@ -313,10 +332,7 @@ else { cancelPreview(); - Coord c = m_renderer->toLogical(ev->pos()); - - if(Ship *ship = m_delegate->canAddShip(m_player, c)) - setPreview(mapToScene(ev->pos()), ship); + setPreview(ev->pos()); } } @@ -344,4 +360,4 @@ m_player = player; } -const qreal BattleFieldView::PREVIEW_OPACITY = 0.5; +const qreal BattleFieldView::PREVIEW_OPACITY = 0.7; diff -Nru knavalbattle-4.12.3/src/battlefieldview.h knavalbattle-4.12.90/src/battlefieldview.h --- knavalbattle-4.12.3/src/battlefieldview.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/battlefieldview.h 2014-03-10 19:23:40.000000000 +0000 @@ -54,7 +54,8 @@ Delegate *m_delegate; Sea::Player m_player; - + Sea::Status m_status; + struct Preview { Coord pos; Ship* ship; @@ -70,14 +71,18 @@ typedef QMultiHash Sprites; Sprites m_sprites; + +private: void addSprite(const Coord& c, Sprite* ship); + void loadPreviewSprite(Ship * ship); + public: BattleFieldView(QWidget* parent, KBSRenderer* renderer, const QString& bgID, int gridSize); void toggleGrid(bool show); void refresh(); - void setPreview(const QPointF& pos, Ship* ship); + void setPreview(const QPoint &pos); void cancelPreview(); void add(Ship* ship); void hit(const Coord& c); @@ -90,7 +95,7 @@ void setPlayer(Sea::Player player); WelcomeScreen* screen() const; - + inline void setStatus(Sea::Status status) { m_status = status; }; protected: void drawGrid(); diff -Nru knavalbattle-4.12.3/src/CMakeLists.txt knavalbattle-4.12.90/src/CMakeLists.txt --- knavalbattle-4.12.3/src/CMakeLists.txt 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/CMakeLists.txt 2014-03-10 19:23:40.000000000 +0000 @@ -30,6 +30,7 @@ seaview.cpp settings.cpp ship.cpp + ships.cpp shot.cpp simplemenu.cpp sprite.cpp diff -Nru knavalbattle-4.12.3/src/controller.cpp knavalbattle-4.12.90/src/controller.cpp --- knavalbattle-4.12.3/src/controller.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/controller.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -16,17 +16,18 @@ #include "seaview.h" #include "shot.h" #include "audioplayer.h" +#include "ships.h" -Controller::Controller(QObject* parent, AudioPlayer* player, const bool allow_adjacent_ships) +Controller::Controller(QObject* parent, AudioPlayer* audioPlayer, const BattleShipsConfiguration& battleShipsConfiguration) : QObject(parent) , m_shot(0) , m_ready(0) -, m_player(player) +, m_player(audioPlayer) , m_has_ai(false) -, m_allow_adjacent_ships(allow_adjacent_ships) +, mBattleShipsConfiguration(battleShipsConfiguration) { m_ui = 0; - m_sea = new Sea(this, Coord(10, 10), allow_adjacent_ships); + m_sea = new Sea(this, battleShipsConfiguration); } PlayerEntity* Controller::createPlayer(Sea::Player player, SeaView* view, @@ -68,17 +69,21 @@ void Controller::setupEntity(Entity* entity) { entity->setParent(this); - + connect(entity, SIGNAL(shoot(int,Coord)), this, SLOT(shoot(int,Coord)), Qt::QueuedConnection); connect(entity, SIGNAL(ready(int)), - this, SLOT(ready(int)), Qt::QueuedConnection); + this, SLOT(ready(int))); + connect(entity, SIGNAL(shipsPlaced(int)), + this, SLOT(shipsPlaced(int))); connect(entity, SIGNAL(chat(QString)), this, SLOT(receivedChat(QString))); connect(entity, SIGNAL(nick(int,QString)), this, SLOT(nick(int,QString))); connect(entity, SIGNAL(compatibility(int)), this, SIGNAL(compatibility(int))); + connect(entity, SIGNAL(gameOptionsInterchanged(bool)), + this, SLOT(placing(bool))); foreach (Entity* e, m_entities) { connect(e, SIGNAL(compatibility(int)), @@ -90,11 +95,21 @@ entity, SLOT(notifyAbort())); connect(entity, SIGNAL(abortGame()), e, SLOT(notifyAbort())); + connect(e, SIGNAL(restartPlacingShips(Sea::Player)), + this, SIGNAL(restartPlacingShips(Sea::Player))); + connect(e, SIGNAL(restartPlacingShips(Sea::Player)), + this, SLOT(notifyRestartPlacingShips(Sea::Player))); } m_entities.append(entity); } +void Controller::setBattleShipsConfiguration(const BattleShipsConfiguration& battleConfiguration) +{ + mBattleShipsConfiguration = battleConfiguration; +} + + bool Controller::allPlayers() const { unsigned char bitmap = 0; @@ -113,14 +128,14 @@ if (!allPlayers()) { return false; } - + if (!m_ui) { m_ui = new UIEntity(Sea::NO_PLAYER, m_sea, view); setupEntity(m_ui); } - + foreach (Entity* entity, m_entities) { - entity->start(ask); + entity->notifyGameOptions(ask); } foreach (Entity* source, m_entities) { @@ -131,9 +146,40 @@ } } } + return true; } +void Controller::restart(bool ask) +{ + m_ready = 0; + if (ask) + { + foreach (Entity* entity, m_entities) { + entity->notifyRestart(entity->player()); + } + } + + m_sea->clear(Sea::PLAYER_A); + m_sea->clear(Sea::PLAYER_B); + + foreach (Entity* entity, m_entities) { + m_sea->clear(entity->player()); + emit startPlacingShips(Sea::PLAYER_A); + entity->startPlacing(false); + } +} + + +// It is sure the entities has interchanged the GameOptions (if any) +// when the opposite nick is received +void Controller::placing(bool ask) +{ + foreach (Entity* entity, m_entities) { + entity->startPlacing(ask); + } +} + void Controller::shoot(int player, const Coord& c) { Entity* entity = findEntity(Sea::opponent(Sea::Player(player))); @@ -147,7 +193,7 @@ // shot in progress return; } - + if (m_sea->status() == Sea::PLAYING) { entity->hit(m_shot = new Shot(this, Sea::Player(player), c)); // kind of CPS } @@ -158,12 +204,12 @@ if (info.type != HitInfo::INVALID) { // notify entities notify(player, c, info); - + // play sounds if (m_player) { m_player->play(player, info); } - + if (m_sea->status() == Sea::A_WINS) { finalizeGame(Sea::PLAYER_A); } @@ -192,17 +238,29 @@ } } +void Controller::shipsPlaced(int player) +{ + m_ready++; + if (m_ready >= 2 ) + { + foreach (Entity* entity, m_entities) { + entity->start(false); + } + } +} + + void Controller::ready(int player) { m_ready++; foreach (Entity* entity, m_entities) { entity->notifyReady(Sea::Player(player)); } - - // when two entities are ready, start - // all engines - if (m_ready >= 2) { + // when two entities are ready (ships placed and ready) + // start all engines + if (m_ready >= 4) { m_sea->startPlaying(); + foreach (Entity* entity, m_entities) { entity->startPlaying(); } @@ -224,7 +282,16 @@ entity->notifyGameOver(winner); } emit gameOver(winner); - } +} + +void Controller::notifyRestartPlacingShips(Sea::Player player) +{ + foreach (Entity* entity, m_entities) { + if (entity->player() == player) { + entity->notifyRestartPlacing(player); + } + } +} Entity* Controller::findEntity(Sea::Player player) const { diff -Nru knavalbattle-4.12.3/src/controller.h knavalbattle-4.12.90/src/controller.h --- knavalbattle-4.12.3/src/controller.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/controller.h 2014-03-10 19:23:40.000000000 +0000 @@ -22,6 +22,7 @@ class Shot; class AudioPlayer; class Protocol; +class BattleShipsConfiguration; class Controller : public QObject { @@ -33,7 +34,7 @@ int m_ready; AudioPlayer* m_player; bool m_has_ai; - bool m_allow_adjacent_ships; + BattleShipsConfiguration mBattleShipsConfiguration; void notify(Sea::Player player, const Coord& c, const HitInfo& info); void setupEntity(Entity*); @@ -44,7 +45,7 @@ friend class Shot; public: - explicit Controller(QObject* parent, AudioPlayer* audioPlayer = 0, const bool allow_adjacent_ships = false); + explicit Controller(QObject* parent, AudioPlayer* audioPlayer = 0, const BattleShipsConfiguration& battleConfiguration = BattleShipsConfiguration::defaultSingleShipsConfiguration(true)); PlayerEntity* createPlayer(Sea::Player player, SeaView* view, ChatWidget* chat, const QString& nick); @@ -56,14 +57,22 @@ Sea::Player turn() const; bool hasAI() const; inline Sea* getSea() const { return m_sea; } + void setBattleShipsConfiguration(const BattleShipsConfiguration& battleConfiguration); + inline BattleShipsConfiguration& getBattleShipsConfiguration() { return mBattleShipsConfiguration; } public slots: void shoot(int player, const Coord& c); void ready(int player); + void shipsPlaced(int player); void receivedChat(const QString& text); void nick(int player, const QString& nick); + void notifyRestartPlacingShips(Sea::Player player); + void placing(bool ask = false); + void restart(bool ask = false); signals: void gameOver(Sea::Player); void restartRequested(); + void startPlacingShips(int player); + void restartPlacingShips(Sea::Player player); // in case it is impossible to finish with the current board void compatibility(int); void nickChanged(int, const QString&); void turnChanged(int); diff -Nru knavalbattle-4.12.3/src/delegate.h knavalbattle-4.12.90/src/delegate.h --- knavalbattle-4.12.3/src/delegate.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/delegate.h 2014-03-10 19:23:40.000000000 +0000 @@ -16,7 +16,8 @@ virtual ~Delegate() { } virtual void action(Sea::Player player, const Coord& c) = 0; virtual void changeDirection(Sea::Player player) = 0; - virtual Ship* canAddShip(Sea::Player player, const Coord& c) = 0; + virtual bool canAddShip(Sea::Player player, const Coord& c) = 0; + virtual Ship * nextShip() = 0; }; #endif // DELEGATE_H diff -Nru knavalbattle-4.12.3/src/entity.cpp knavalbattle-4.12.90/src/entity.cpp --- knavalbattle-4.12.3/src/entity.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/entity.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -10,10 +10,12 @@ #include "entity.h" -Entity::Entity(Sea::Player player, SeaView* seaview) +Entity::Entity(Sea::Player player, SeaView* seaview, const BattleShipsConfiguration* battleShipsConfiguration) : m_player(player) , m_seaview(seaview) , m_level(COMPAT_KBS4) +, m_battleShipsConfiguration(battleShipsConfiguration) +, m_restarted(false) { } diff -Nru knavalbattle-4.12.3/src/entity.h knavalbattle-4.12.90/src/entity.h --- knavalbattle-4.12.3/src/entity.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/entity.h 2014-03-10 19:23:40.000000000 +0000 @@ -14,6 +14,7 @@ #include "sea.h" #include "seaview.h" #include "stats.h" +#include "ships.h" class Shot; class KIcon; @@ -33,19 +34,26 @@ QString m_nick; CompatibilityLevel m_level; Stats m_stats; + // not owned by the entity + const BattleShipsConfiguration* m_battleShipsConfiguration; + bool m_restarted; public: - Entity(Sea::Player player, SeaView* seaview); + Entity(Sea::Player player, SeaView* seaview, const BattleShipsConfiguration* battleShipsConfiguration); virtual ~Entity(); virtual void notify(Sea::Player player, const Coord& c, const HitInfo& info) = 0; virtual void notifyChat(const Entity* entity, const QString& text) = 0; virtual void notifyNick(Sea::Player player, const QString& nick) = 0; virtual void hit(Shot* shot) = 0; + virtual void notifyGameOptions(bool ask) = 0; + virtual void startPlacing(bool) = 0; virtual void start(bool) = 0; virtual void startPlaying() { } virtual void notifyReady(Sea::Player) { } virtual void notifyShips(Sea::Player) { } virtual void notifyGameOver(Sea::Player) { } + virtual void notifyRestart(Sea::Player) { } + Stats* stats(); virtual Sea::Player player() const { return m_player; } @@ -56,14 +64,18 @@ virtual void setNick(const QString& nick); signals: void shoot(int player, const Coord& c); + void shipsPlaced(int player); void ready(int player); void chat(const QString& text); void nick(int player, const QString& nickname); void compatibility(int level); void abortGame(); + void restartPlacingShips(Sea::Player player); + void gameOptionsInterchanged(bool ask=false); public slots: virtual void setCompatibilityLevel(int level); virtual void notifyAbort() = 0; + virtual void notifyRestartPlacing(Sea::Player player) = 0; }; #endif // ENTITY_H diff -Nru knavalbattle-4.12.3/src/knavalbattle.desktop knavalbattle-4.12.90/src/knavalbattle.desktop --- knavalbattle-4.12.3/src/knavalbattle.desktop 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/knavalbattle.desktop 2014-03-10 19:23:40.000000000 +0000 @@ -17,6 +17,7 @@ Name[it]=Battaglia navale Name[kk]=Теңіз соғысы Name[km]=ចម្បាំង​តាម​នាវា +Name[ko]=전함 대전 Name[lt]=Jūrų mūšis Name[lv]=Naval Battle Name[mr]=नाविक युद्ध @@ -64,6 +65,7 @@ GenericName[it]=Gioco di affondamento navi GenericName[kk]=Теңіздегі соғыс ойны GenericName[km]=ល្បែង​ពន្លិច​កប៉ាល់ +GenericName[ko]=전함 침몰 게임 GenericName[lt]=Laivų skandinimo žaidimas GenericName[lv]=Kuģu gremdēšanas spēle GenericName[mr]=नाव बुडविण्याचा खेळ diff -Nru knavalbattle-4.12.3/src/knavalbattle.kcfg knavalbattle-4.12.90/src/knavalbattle.kcfg --- knavalbattle-4.12.3/src/knavalbattle.kcfg 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/knavalbattle.kcfg 2014-03-10 19:23:40.000000000 +0000 @@ -26,5 +26,9 @@ true + + + false + diff -Nru knavalbattle-4.12.3/src/knavalbattle.protocol knavalbattle-4.12.90/src/knavalbattle.protocol --- knavalbattle-4.12.3/src/knavalbattle.protocol 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/knavalbattle.protocol 2014-03-10 19:23:40.000000000 +0000 @@ -23,6 +23,7 @@ Description[it]=Un protocollo per Battaglia navale Description[kk]=Теңіздегі соғыс ойынның протоколы Description[km]=ពិធីការ​សម្រាប់​ល្បែង​ នាវាចំបាំង +Description[ko]=전함 침몰 게임 프로토콜 Description[lv]=Protokols spēlei Naval Battle Description[mr]=नाविक युद्ध खेळाकरिता शिष्टाचार Description[nb]=En protokoll for spillet Sjøslag diff -Nru knavalbattle-4.12.3/src/knavalbattleui.rc knavalbattle-4.12.90/src/knavalbattleui.rc --- knavalbattle-4.12.3/src/knavalbattleui.rc 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/knavalbattleui.rc 2014-03-10 19:23:40.000000000 +0000 @@ -1,6 +1,6 @@ + @@ -40,6 +41,9 @@ + + + diff -Nru knavalbattle-4.12.3/src/mainwindow.cpp knavalbattle-4.12.90/src/mainwindow.cpp --- knavalbattle-4.12.3/src/mainwindow.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/mainwindow.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -43,7 +43,7 @@ setupActions(); connect(m_main, SIGNAL(welcomeScreen()), this, SLOT(welcomeScreen())); - connect(m_main, SIGNAL(startingGame()), this, SLOT(startingGame())); + connect(m_main, SIGNAL(placeShips()), this, SLOT(startingGame())); m_main->newGame(); @@ -54,7 +54,7 @@ void MainWindow::setupActions() { KStandardGameAction::gameNew(m_main, SLOT(newGame()), actionCollection()); - KStandardGameAction::restart(m_main, SLOT(restart()), actionCollection()); + KStandardGameAction::restart(m_main, SLOT(restart()), actionCollection()); KStandardGameAction::highscores(m_main, SLOT(highscores()), actionCollection()); KStandardGameAction::quit(this, SLOT(close()), actionCollection()); @@ -81,6 +81,11 @@ action->setChecked(Settings::adjacentShips()); actionCollection()->addAction("options_adjacent", action); connect(action, SIGNAL(triggered(bool)), m_main, SLOT(toggleAdjacent(bool))); + // This action will be disabled when a game is being run + action = new KToggleAction(i18n("&Multiple Ships"), this); + action->setChecked(Settings::severalShips()); + actionCollection()->addAction("options_multiple_ships", action); + connect(action, SIGNAL(triggered(bool)), m_main, SLOT(toggleMultiple(bool))); // config end of game message action = new KToggleAction(i18n("Show End-of-Game Message"), this); action->setChecked(true); diff -Nru knavalbattle-4.12.3/src/message.cpp knavalbattle-4.12.90/src/message.cpp --- knavalbattle-4.12.3/src/message.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/message.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -117,9 +117,19 @@ visitor.visit(*this); } -GameOptionsMessage::GameOptionsMessage(const QString& enableAdjacentShips, const QString& oneOrSeveralShips) +GameOptionsMessage::GameOptionsMessage(const QString& enableAdjacentShips, const QString& oneOrSeveralShips, + const BattleShipsConfiguration* configuration) : m_enabledAdjacentShipsString(enableAdjacentShips) , m_oneOrSeveralShipsString(oneOrSeveralShips) +, m_battleShipsConfiguration(configuration) +{ +} + +GameOptionsMessage::GameOptionsMessage(const bool enableAdjacentShips, const bool oneOrSeveralShips, + BattleShipsConfiguration configuration) +: m_enabledAdjacentShipsString(enableAdjacentShips) +, m_oneOrSeveralShipsString(oneOrSeveralShips) +, m_battleShipsConfiguration(new BattleShipsConfiguration(configuration)) { } diff -Nru knavalbattle-4.12.3/src/message.h knavalbattle-4.12.90/src/message.h --- knavalbattle-4.12.3/src/message.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/message.h 2014-03-10 19:23:40.000000000 +0000 @@ -13,6 +13,7 @@ #include #include "ship.h" +#include "ships.h" class MessageVisitor; @@ -39,6 +40,7 @@ const QString& clientDescription); HeaderMessage(); virtual void accept(MessageVisitor& visitor) const; + static QString messageType() { return QLatin1String("Header"); } const QString& protocolVersion() const { return m_protocol_version; } const QString& clientName() const { return m_client_name; } @@ -54,6 +56,7 @@ static const int MSGTYPE = 1; RejectMessage(bool versionMismatch, const QString& reason); virtual void accept(MessageVisitor& visitor) const; + static QString messageType() { return QLatin1String("Reject"); } }; class NickMessage : public Message @@ -65,6 +68,7 @@ virtual void accept(MessageVisitor& visitor) const; const QString& nickname() const { return m_nickname; } + static QString messageType() { return QLatin1String("Nick"); } }; class BeginMessage : public Message @@ -72,6 +76,7 @@ public: static const int MSGTYPE = 3; virtual void accept(MessageVisitor& visitor) const; + static QString messageType() { return QLatin1String("Begin"); } }; class MoveMessage : public Message @@ -83,6 +88,7 @@ virtual void accept(MessageVisitor& visitor) const; const Coord& move() const { return m_move; } + static QString messageType() { return QLatin1String("Move"); } }; class NotificationMessage : public Message @@ -97,6 +103,7 @@ NotificationMessage(const Coord& m_move, bool hit, bool death, const Coord& start = Coord::invalid(), const Coord& stop = Coord::invalid()); virtual void accept(MessageVisitor& visitor) const; + static QString messageType() { return QLatin1String("Notification"); } const Coord& move() const { return m_move; } bool hit() const { return m_hit; } @@ -129,6 +136,7 @@ void addShip(const Coord& pos, int size, Ship::Direction direction); virtual void accept(MessageVisitor& visitor) const; + static QString messageType() { return QLatin1String("GameOver"); } const QList& ships() const { return m_ships; } }; @@ -138,6 +146,7 @@ public: static const int MSGTYPE = 7; virtual void accept(MessageVisitor& visitor) const; + static QString messageType() { return QLatin1String("Restart"); } }; class ChatMessage : public Message @@ -148,6 +157,7 @@ static const int MSGTYPE = 8; explicit ChatMessage(const QString& nick, const QString& chat); virtual void accept(MessageVisitor& visitor) const; + static QString messageType() { return QLatin1String("Chat"); } const QString& chat() const { return m_chat; } const QString& nickname() const { return m_nickname; } @@ -159,13 +169,21 @@ private: QString m_enabledAdjacentShipsString; QString m_oneOrSeveralShipsString; + const BattleShipsConfiguration* m_battleShipsConfiguration; public: static const int MSGTYPE = 9; - GameOptionsMessage(const QString& enableAdjacentShips, const QString& oneOrSeveralShips); + GameOptionsMessage(const QString& enableAdjacentShips, const QString& oneOrSeveralShips, + const BattleShipsConfiguration* configuration); + GameOptionsMessage(const bool enableAdjacentShips, const bool oneOrSeveralShips, + BattleShipsConfiguration configuration); const QString & enabledAdjacentShips() const { return m_enabledAdjacentShipsString; } const QString & oneOrSeveralShips() const { return m_oneOrSeveralShipsString; } + unsigned int gridWidth() const { return m_battleShipsConfiguration->boardWidth(); } + unsigned int gridHeight() const { return m_battleShipsConfiguration->boardHeight(); } + const BattleShipsConfiguration* shipsConfiguration() const { return m_battleShipsConfiguration; } virtual void accept(MessageVisitor& visitor) const; + static QString messageType() { return QLatin1String("GameOptions"); } }; diff -Nru knavalbattle-4.12.3/src/networkentity.cpp knavalbattle-4.12.90/src/networkentity.cpp --- knavalbattle-4.12.3/src/networkentity.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/networkentity.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -20,7 +20,7 @@ #include NetworkEntity::NetworkEntity(Sea::Player player, Sea* sea, SeaView* seaview, Protocol* protocol, bool client) -: Entity(player, seaview) +: Entity(player, seaview, sea->battleShipsConfiguration()) , m_sea(sea) , m_protocol(protocol) , m_pending_shot(0) @@ -35,16 +35,6 @@ void NetworkEntity::start(bool ask) { - connect(m_protocol, SIGNAL(received(MessagePtr)), this, SLOT(received(MessagePtr))); - connect(m_protocol, SIGNAL(disconnected()), this, SIGNAL(abortGame())); - if (ask) { - m_protocol->send(MessagePtr(new RestartMessage())); - } - else { - m_protocol->send(MessagePtr(new HeaderMessage())); - - m_protocol->send(MessagePtr(new GameOptionsMessage(QString(Settings::adjacentShips() ? "true" : "false"), /* TODO */"true"))); - } } void NetworkEntity::notifyReady(Sea::Player player) @@ -73,6 +63,41 @@ { } +void NetworkEntity::notifyRestart(Sea::Player) +{ + if (!m_restarted) { + m_protocol->send(MessagePtr(new RestartMessage())); + m_restarted = true; + } +} + + +void NetworkEntity::notifyGameOptions(bool ask) +{ + connect(m_protocol, SIGNAL(received(MessagePtr)), this, SLOT(received(MessagePtr))); + connect(m_protocol, SIGNAL(disconnected()), this, SIGNAL(abortGame())); + if (ask || m_restarted) { + m_protocol->send(MessagePtr(new RestartMessage())); + m_restarted = true; + } + else { + m_protocol->send(MessagePtr(new HeaderMessage())); + + m_protocol->send(MessagePtr(new GameOptionsMessage(QString(Settings::adjacentShips() ? "true" : "false"), QString(Settings::severalShips() ? "true" : "false"), m_battleShipsConfiguration ))); + } +} + +void NetworkEntity::startPlacing(bool ask) +{ + m_restarted = false; + m_sea->clear(m_player); + + // Number of ships to sink again in the new game + m_sea->add(m_player, m_battleShipsConfiguration->totalNumberOfShipsToPlay()); + + emit ready(m_player); +} + void NetworkEntity::startPlaying() { } @@ -160,11 +185,46 @@ { setNick(msg.nickname()); emit nick(m_player, m_nick); + // This is a dirty hack caused by the introduction of GameOptionsMessage. + // If that had extended BeginMessage, the following instructions will + // be in the right place. + // It is done here because the nickMessage is sent after GameOptionsMessage + // (if sent) and before start placing ships. + if ( !m_battleShipsConfiguration->isFromXML() && !m_restarted) + { + m_sea->setBattleShipsConfiguration(BattleShipsConfiguration::defaultSingleShipsConfiguration(true)); + m_battleShipsConfiguration = m_sea->battleShipsConfiguration(); + // TODO: Message explaining why the network game uses this configuration + } + // form the chat message telling the number of ships of each type to place and shink + QString message=i18n("You have "); + bool comma=false; + for (unsigned int size = 1; size <= m_battleShipsConfiguration->longestShip(); size++) + { + if (comma) + { + message.append(i18n(", ")); + } + comma=true; + if ( m_battleShipsConfiguration->numberOfShipsOfSize(size) == 1 ) + { + message.append(QLatin1String("1 ")).append(m_battleShipsConfiguration->nameOfShipsOfSize(size)); + } + else + { + message.append(QString::number(m_battleShipsConfiguration->numberOfShipsOfSize(size))) + .append(" ") + .append(i18n(m_battleShipsConfiguration->pluralNameOfShipsOfSize(size).toLatin1())); + } + } + emit chat(message); + emit gameOptionsInterchanged(); + // Number of ships to sink + m_sea->add(m_player, m_battleShipsConfiguration->totalNumberOfShipsToPlay()); } void NetworkEntity::visit(const BeginMessage&) { - m_sea->add(m_player, 4); emit ready(m_player); } @@ -216,7 +276,11 @@ void NetworkEntity::visit(const RestartMessage&) { - emit restartRequested(); + // Keep the current configuration in a restarted game. + if (!m_restarted) + { + emit restartRequested(); + } } void NetworkEntity::visit(const ChatMessage& msg) @@ -227,14 +291,30 @@ void NetworkEntity::visit(const GameOptionsMessage& msg) { bool enabledAdjacentShips = (msg.enabledAdjacentShips() == QString("true")); - m_sea->allowAdjacentShips( enabledAdjacentShips ); + if (m_client) { + m_sea->allowAdjacentShips( enabledAdjacentShips ); + } - if (enabledAdjacentShips) { + if (m_sea->isAdjacentShipsAllowed()) { emit chat(i18n("You can place ships adjacent to each other")); } else { emit chat(i18n("You must leave a space between ships")); } + + if (m_client) + { + m_battleShipsConfiguration = msg.shipsConfiguration(); + m_sea->setBattleShipsConfiguration(*m_battleShipsConfiguration); + } + else + { + if (msg.shipsConfiguration()->isFromXML()) + { + // if the client understood GameOptions, then the global preferences in the server are used. + const_cast(m_battleShipsConfiguration)->setFromXML(true); + } + } } @@ -244,5 +324,3 @@ } #include "networkentity.moc" - - diff -Nru knavalbattle-4.12.3/src/networkentity.h knavalbattle-4.12.90/src/networkentity.h --- knavalbattle-4.12.3/src/networkentity.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/networkentity.h 2014-03-10 19:23:40.000000000 +0000 @@ -25,7 +25,7 @@ Shot* m_pending_shot; bool m_client; bool m_winner; - + public: NetworkEntity(Sea::Player player, Sea* sea, SeaView* seaview, Protocol* device, bool client); ~NetworkEntity(); @@ -34,16 +34,20 @@ virtual void notifyChat(const Entity* entity, const QString& text); virtual void notifyNick(Sea::Player player, const QString& nick); virtual void start(bool ask); + virtual void startPlacing(bool ask); virtual void startPlaying(); virtual void notifyReady(Sea::Player player); virtual void notifyShips(Sea::Player winner); virtual void notifyGameOver(Sea::Player winner); + virtual void notifyGameOptions(bool ask); + virtual void notifyRestart(Sea::Player); virtual void hit(Shot* shot); virtual KIcon icon() const; private slots: void received(MessagePtr msg); virtual void notifyAbort(); + virtual void notifyRestartPlacing(Sea::Player player) { }; protected: virtual void visit(const HeaderMessage& msg); virtual void visit(const RejectMessage& msg); diff -Nru knavalbattle-4.12.3/src/playerentity.cpp knavalbattle-4.12.90/src/playerentity.cpp --- knavalbattle-4.12.3/src/playerentity.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/playerentity.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -13,12 +13,14 @@ #include #include "seaview.h" #include "shot.h" +#include "coord.h" #include "chatwidget.h" PlayerEntity::PlayerEntity(Sea::Player player, Sea* sea, SeaView* view, ChatWidget* chat) : UIEntity(player, sea, view) , m_chat(chat) { + m_seaview->setStatus(Sea::PLACING_SHIPS); } Ship* PlayerEntity::nextShip() @@ -34,20 +36,20 @@ Ship* PlayerEntity::canAddShip(const Coord& c) { Ship* ship = nextShip(); - ship->setPosition(c); - Q_ASSERT(ship); - - if (m_sea->canAddShip(m_player, c, ship->size(), ship->direction())) { - // check if it is near any other ship - // in KBS3 mode - if (m_level == COMPAT_KBS3) { - for (unsigned int i = 0; i < ship->size(); i++) { - if (m_sea->isNearShip(m_player, c + ship->increment() * i)) { - return 0; + if (ship) { + ship->setPosition(c); + if (m_sea->canAddShip(m_player, c, ship->size(), ship->direction())) { + // check if it is near any other ship + // in KBS3 mode + if (m_level == COMPAT_KBS3) { + for (unsigned int i = 0; i < ship->size(); i++) { + if (m_sea->isNearShip(m_player, c + ship->increment() * i)) { + return 0; + } } } + return ship; } - return ship; } return 0; } @@ -55,44 +57,79 @@ void PlayerEntity::action(Sea::Player player, const Coord& c) { - if (nextShip()) { - if (player == m_player) { - // placing ships - // First check if the ship can be placed anywhere - Ship* ship = canAddShip(c); - if (ship) { - // remove ship from the list - m_ships.removeFirst(); - - // add ship to the sea - m_sea->add(m_player, ship); - m_seaview->add(m_player, ship); - - if (!nextShip()) { - emit ready(m_player); + Sea::Player opponent = Sea::opponent(m_player); + switch ( m_sea->status() ) + { + case Sea::PLACING_SHIPS: + if (!m_ships.empty()) { + if (player == m_player) { + // placing ships + // First check if the ship can be placed anywhere + Ship* ship = canAddShip(c); + if (ship) { + // remove ship from the list + m_ships.removeFirst(); + + // add ship to the sea + m_sea->add(m_player, ship); + m_seaview->add(m_player, ship); + if (!m_ships.empty()) { + ship=m_ships.first(); + // when multiple ships and an space between them are enabled, + // it is possible to reach impossible combinations + if ( !m_sea->canAddShipOfSize(m_player, ship->size()) ) { + emit restartPlacingShips(m_player); + } + } + else { + emit shipsPlaced(m_player); + } + } } } - } - } - else { - Sea::Player opponent = Sea::opponent(m_player); - if (player == opponent && m_sea->canHit(m_player, c)) { - emit shoot(m_player, c); - } + break; + case Sea::PLAYING: + if (player == opponent && m_sea->canHit(m_player, c)) { + emit shoot(m_player, c); + } + break; + default: + // SHOULD NEVER HAPPEN + break; } } -void PlayerEntity::start(bool ask) +void PlayerEntity::startPlacing(bool restart) { + UIEntity::startPlacing(restart); + m_battleShipsConfiguration = m_sea->battleShipsConfiguration(); + Coord origin(0, 0); - m_ships.append(new Ship(1, Ship::LEFT_TO_RIGHT, origin)); - m_ships.append(new Ship(2, Ship::LEFT_TO_RIGHT, origin)); - m_ships.append(new Ship(3, Ship::LEFT_TO_RIGHT, origin)); - m_ships.append(new Ship(4, Ship::LEFT_TO_RIGHT, origin)); + m_sea->clear(m_player); + m_ships.clear(); + for (unsigned int len=1; len <= m_battleShipsConfiguration->longestShip(); len++) + { + for (unsigned int i=0; inumberOfShipsOfSize(len); i++) + { + m_ships.append(new Ship(len, Ship::LEFT_TO_RIGHT, origin)); + } + } + m_seaview->setDelegate(this); + m_seaview->setStatus(Sea::PLACING_SHIPS); +} + +void PlayerEntity::start(bool ask) +{ UIEntity::start(ask); - m_seaview->setDelegate(this); + emit ready(m_player); +} + +void PlayerEntity::startPlaying() +{ + UIEntity::startPlaying(); + m_seaview->setStatus(Sea::PLAYING); } void PlayerEntity::hit(Shot* shot) @@ -122,19 +159,19 @@ } } -Ship* PlayerEntity::canAddShip(Sea::Player player, const Coord& c) +bool PlayerEntity::canAddShip(Sea::Player player, const Coord& c) { - Ship* next = nextShip(); - if (next == 0 || player != m_player) { - return 0; + if (m_ships.isEmpty() || player != m_player) { + return false; } + + Ship * next = m_ships.at(0); + if (m_sea->canAddShip(player, c, next->size(), next->direction())) { - next->setPosition(c); - return next; - } - else { - return 0; + return true; } + + return false; } void PlayerEntity::registerHit(Sea::Player player, const Coord&) @@ -175,6 +212,14 @@ m_chat->setNick(nick); } +void PlayerEntity::notifyRestartPlacing(Sea::Player player) +{ + UIEntity::notifyRestartPlacing(player); + m_seaview->clear(); + m_sea->clear(player); + startPlacing(false); +} + #include "playerentity.moc" diff -Nru knavalbattle-4.12.3/src/playerentity.h knavalbattle-4.12.90/src/playerentity.h --- knavalbattle-4.12.3/src/playerentity.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/playerentity.h 2014-03-10 19:23:40.000000000 +0000 @@ -23,7 +23,6 @@ Q_OBJECT QList m_ships; - Ship* nextShip(); Ship* canAddShip(const Coord& c); ChatWidget* m_chat; @@ -32,15 +31,18 @@ // entity interface virtual void start(bool); + virtual void startPlacing(bool); + virtual void startPlaying(); virtual void hit(Shot* shot); virtual void notify(Sea::Player player, const Coord& c, const HitInfo& info); virtual void notifyChat(const Entity* entity, const QString& text); virtual void notifyNick(Sea::Player player, const QString& text); - + virtual void notifyGameOptions(bool ask) { }; // delegate interface virtual void action(Sea::Player player, const Coord& c); virtual void changeDirection(Sea::Player player); - virtual Ship* canAddShip(Sea::Player player, const Coord& c); + virtual bool canAddShip(Sea::Player player, const Coord& c); + virtual Ship * nextShip(); virtual void setNick(const QString& nick); protected: @@ -49,6 +51,7 @@ virtual void registerMiss(Sea::Player player, const Coord& c); public slots: virtual void notifyAbort(); + virtual void notifyRestartPlacing(Sea::Player player); }; #endif // PLAYERENTITY_H diff -Nru knavalbattle-4.12.3/src/playfield.cpp knavalbattle-4.12.90/src/playfield.cpp --- knavalbattle-4.12.3/src/playfield.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/playfield.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -18,6 +18,7 @@ #include #include #include +#include #include "aientity.h" #include "audioplayer.h" @@ -67,7 +68,12 @@ Controller* PlayField::createController() { - Controller* controller = new Controller(this, m_player, Settings::adjacentShips()); + // The client or server will overwrite this default configuration when + // the network messages are interchanged + m_battle_ships_configuration = Settings::severalShips() ? + BattleShipsConfiguration::defaultMultipleShipsConfiguration(Settings::adjacentShips()): + BattleShipsConfiguration::defaultSingleShipsConfiguration(Settings::adjacentShips()); + Controller* controller = new Controller(this, m_player, m_battle_ships_configuration); connect(controller, SIGNAL(gameOver(Sea::Player)), this, SLOT(gameOver(Sea::Player))); connect(controller, SIGNAL(restartRequested()), @@ -80,6 +86,10 @@ this, SLOT(changeTurn(int))); connect(controller, SIGNAL(playerReady(int)), this, SLOT(playerReady(int))); + connect(controller, SIGNAL(restartPlacingShips(Sea::Player)), + this, SLOT(restartPlacingShips(Sea::Player))); + connect(controller, SIGNAL(startPlacingShips(int)), + this, SLOT(startPlacingShips())); return controller; } @@ -105,11 +115,13 @@ Entity* old_opponent = m_controller->findEntity(Sea::Player(1)); if (old_opponent) { old_opponent->setParent(0); - } + } + BattleShipsConfiguration oldBSC=m_controller->getBattleShipsConfiguration(); delete m_controller; // create new controller m_controller = createController(); + m_controller->setBattleShipsConfiguration(oldBSC); m_menu->setupController(m_controller, old_opponent, m_seaView, m_chat, ask); delete old_opponent; @@ -138,6 +150,7 @@ m_menu = new SimpleMenu(this, m_seaView->screen(Sea::Player(0))); connect(m_menu, SIGNAL(done()), this, SLOT(setupController())); + m_status_bar->showMessage(QLatin1String("")); emit welcomeScreen(); } @@ -145,7 +158,8 @@ { Animator::instance()->restart(); m_seaView->clear(); - resetupController(ask); + startGame(); + m_controller->restart(ask); } @@ -236,6 +250,12 @@ Settings::self()->writeConfig(); } +void PlayField::toggleMultiple(bool enable) +{ + Settings::setSeveralShips(enable); + Settings::self()->writeConfig(); +} + void PlayField::restartRequested() { int ans = KMessageBox::questionYesNo(this, i18n("Restart game"), @@ -248,7 +268,7 @@ void PlayField::setCompatibility(int level) { if (level == Entity::COMPAT_KBS3) { - KMessageBox::information(this, i18n("Your opponent is using a pre-KDE4 version of Naval Battle. Note that, according to the rules enforced by old clients, ships cannot be placed adjacent to one another.")); + KMessageBox::information(this, i18n("Your opponent is using a pre-KDE4 version of Naval Battle. Note that, according to the rules enforced by old clients, ships cannot be placed adjacent to one another and only one ship of each size is allowed.")); } } @@ -289,8 +309,30 @@ { //the game has a fixed difficulty level only if there is an AI Kg::difficulty()->setGameRunning(m_controller->hasAI()); - m_status_bar->showMessage(i18n("Place your ships. Use the right mouse button to rotate them.")); - emit startingGame(); + startPlacingShips(); + emit placeShips(); +} + +void PlayField::startPlacingShips() +{ + m_status_bar->showMessage(i18n("Place your %1 ships. Use the right mouse button to rotate them.", m_battle_ships_configuration.totalNumberOfShipsToPlay())); +} + + +void PlayField::restartPlacingShips(Sea::Player player) +{ + m_status_bar->showMessage(i18n("You can't place your remaining ships.")); + KMessageBox restartYesNo; + KGuiItem buttonRestart=KStandardGuiItem::yes(); buttonRestart.setText(i18n("Restart")); + KGuiItem buttonAbort=KStandardGuiItem::no(); buttonAbort.setText(i18n("Abort")); + int res=restartYesNo.warningYesNo(this, i18n("You can't place your remaining ships. Please restart placing ships or abort game"), i18n("Restart placing ships"), buttonRestart, buttonAbort); + if (res == KMessageBox::Yes) { + startPlacingShips(); + m_controller->notifyRestartPlacingShips(player); + } + else { + newGame(); + } } diff -Nru knavalbattle-4.12.3/src/playfield.h knavalbattle-4.12.90/src/playfield.h --- knavalbattle-4.12.3/src/playfield.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/playfield.h 2014-03-10 19:23:40.000000000 +0000 @@ -13,6 +13,7 @@ #include #include #include "sea.h" +#include "ships.h" class SeaView; class Controller; @@ -31,6 +32,7 @@ SimpleMenu* m_menu; QStatusBar* m_status_bar; bool m_show_endofgame_message; + BattleShipsConfiguration m_battle_ships_configuration; void startGame(); void endGame(); @@ -51,13 +53,16 @@ void changeNick(); void toggleSounds(bool); void toggleAdjacent(bool); + void toggleMultiple(bool); void restartRequested(); + void startPlacingShips(); + void restartPlacingShips(Sea::Player player); void setCompatibility(int); void updateNick(int, const QString&); void changeTurn(int); void playerReady(int); void levelChanged(); - + void auxMenuDone(); void localGame(); void createServer(); @@ -68,8 +73,9 @@ void toggleRightGrid(bool show); signals: void gameFinished(); + void abortGame(Sea::Player player); void welcomeScreen(); - void startingGame(); + void placeShips(); }; #endif // PLAYFIELD_H diff -Nru knavalbattle-4.12.3/src/protocol.cpp knavalbattle-4.12.90/src/protocol.cpp --- knavalbattle-4.12.3/src/protocol.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/protocol.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -20,19 +20,22 @@ { QDomDocument m_doc; QDomElement m_main; - - void addField(const QString& key, const QString& value) + + QDomElement addField(const QString& key, const QString& value) { QDomElement element = m_doc.createElement(key); QDomText text = m_doc.createTextNode(value); element.appendChild(text); m_main.appendChild(element); + return element; } - + template void setType(const Msg&) { - addField("msgtype", QString::number(Msg::MSGTYPE)); + QDomElement element = addField("msgtype", QString::number(Msg::MSGTYPE)); + // only useful for debuging, just add the name of the message type + element.setAttribute(QLatin1String("type"), Msg::messageType()); } public: MessageSender() @@ -111,9 +114,20 @@ virtual void visit(const GameOptionsMessage& msg) { + // create the message XML contents setType(msg); ADD_FIELD(msg, enabledAdjacentShips); - ADD_FIELD(msg, oneOrSeveralShips); + QDomElement oneOrElement=addField ("oneOrSeveralShips", QString(msg.oneOrSeveralShips())); + oneOrElement.setAttribute(QLatin1String("longestShip"),QString::number(msg.shipsConfiguration()->longestShip())); + addField("boardWidth", QString::number(msg.gridWidth())); + addField("boardHeight", QString::number(msg.gridHeight())); + for (unsigned int i=1; i<=msg.shipsConfiguration()->longestShip(); i++) { + QDomElement element=addField(QString("ships"), QLatin1String( "" )); + element.setAttribute(QLatin1String("size"),QString::number(i)); + element.setAttribute(QLatin1String("number"),QString::number(msg.shipsConfiguration()->numberOfShipsOfSize(i))); + element.setAttribute(QLatin1String("name"),msg.shipsConfiguration()->nameOfShipsOfSize(i)); + element.setAttribute(QLatin1String("pluralName"),msg.shipsConfiguration()->pluralNameOfShipsOfSize(i)); + } } }; @@ -244,9 +258,42 @@ } case GameOptionsMessage::MSGTYPE: { + // get values from the xml message DEF_ELEMENT(enabledAdjacentShips); - DEF_ELEMENT(oneOrSeveralShips); - return MessagePtr(new GameOptionsMessage(enabledAdjacentShips, oneOrSeveralShips)); + bool adjacentShips = enabledAdjacentShips=="true"; + QDomElement oneOrElement=main.namedItem(QLatin1String("oneOrSeveralShips")).toElement(); + QString oneOrSeveralShips = oneOrElement.text(); + bool severalShips = oneOrSeveralShips=="true"; + unsigned int longestShip=0; + // if the node oneOrSeveralShips does not have the attribute, then it is the single ships configuration. + if ( !oneOrElement.hasAttribute(QLatin1String("longestShip")) ) { + return MessagePtr(new GameOptionsMessage(adjacentShips, severalShips, BattleShipsConfiguration::defaultSingleShipsConfiguration(adjacentShips, true))); + } + else { + longestShip = oneOrElement.attribute(QLatin1String("longestShip")).toUInt(); + } + DEF_ELEMENT(boardWidth); + DEF_ELEMENT(boardHeight); + unsigned int width=boardWidth.toUInt(); + unsigned int height=boardHeight.toUInt(); + // and get the ships configuration + QDomNodeList nodes = main.childNodes(); + BattleShipsConfiguration battleShipsConfiguration(longestShip, adjacentShips, width, height, true); + for (int i = 0; i < nodes.count(); i++) { + QDomElement element = nodes.item(i).toElement(); + if (!element.isNull() && element.tagName()==QLatin1String("ships")) { + QString name=element.attribute(QLatin1String("name")); + QString pluralName=element.attribute(QLatin1String("pluralName")); + unsigned int size=element.attribute(QLatin1String("size")).toUInt(); + unsigned int number=element.attribute(QLatin1String("number")).toUInt(); + battleShipsConfiguration.addShips(size,number,name,pluralName); + } + } + if ( !battleShipsConfiguration.isAValidConfiguration() ) + { + return MessagePtr(new GameOptionsMessage(adjacentShips, severalShips, BattleShipsConfiguration::defaultSingleShipsConfiguration(adjacentShips, true))); + } + return MessagePtr(new GameOptionsMessage(adjacentShips, severalShips, battleShipsConfiguration)); } default: emit parseError("Unknown message type"); diff -Nru knavalbattle-4.12.3/src/sea.cpp knavalbattle-4.12.90/src/sea.cpp --- knavalbattle-4.12.3/src/sea.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/sea.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -10,19 +10,22 @@ #include "sea.h" #include "battlefield.h" -Sea::Sea(QObject* parent, const Coord& size, const bool allow_adjacent_ships) +Sea::Sea(QObject* parent, const BattleShipsConfiguration& battleShipsConfiguration) : QObject(parent) -, m_size(size) +, m_size(Coord(battleShipsConfiguration.boardWidth(), battleShipsConfiguration.boardHeight())) , m_turn(PLAYER_A) , m_status(PLACING_SHIPS) -, m_allow_adjacent_ships(allow_adjacent_ships) +, m_battle_ships_configuration(battleShipsConfiguration) { - m_fields[0] = new BattleField(this, m_size); - m_fields[1] = new BattleField(this, m_size); + m_fields[0] = new BattleField(this, m_size, battleShipsConfiguration.isAllowedAdjacentShips()); + m_fields[1] = new BattleField(this, m_size, battleShipsConfiguration.isAllowedAdjacentShips()); } Sea::~Sea() { + m_enemyShips.clear(); + m_myShips.clear(); + delete m_fields[0]; delete m_fields[1]; } @@ -35,6 +38,28 @@ return m_fields[p]->canAddShip(pos, size, direction); } +bool Sea::canAddShipOfSize(Player p, int size) const +{ + if (m_status != PLACING_SHIPS) { + return false; + } + return m_fields[p]->canAddShipOfSize(size); +} + +void Sea::clear(Sea::Player p) +{ + if (m_status == PLACING_SHIPS) { + m_fields[p]->clear(); + if(p == PLAYER_B) { + m_enemyShips.clear(); + } + else { + m_myShips.clear(); + } + } + m_status = PLACING_SHIPS; +} + void Sea::add(Player p, Ship* ship) { if(p == PLAYER_B) { @@ -94,7 +119,9 @@ void Sea::allowAdjacentShips(const bool allow_adjacent_ships) { - m_allow_adjacent_ships = allow_adjacent_ships; + m_battle_ships_configuration.setAllowAdjacentShips(allow_adjacent_ships); + m_fields[0]->setAllowAdjacentShips(allow_adjacent_ships); + m_fields[1]->setAllowAdjacentShips(allow_adjacent_ships); } const Element& Sea::at(Sea::Player player, const Coord& c) const @@ -153,5 +180,13 @@ return m_myShips; } +void Sea::setBattleShipsConfiguration(const BattleShipsConfiguration& configuration) +{ + m_battle_ships_configuration=configuration; + m_fields[0]->setAllowAdjacentShips(configuration.isAllowedAdjacentShips()); + m_fields[1]->setAllowAdjacentShips(configuration.isAllowedAdjacentShips()); +} + + #include "sea.moc" diff -Nru knavalbattle-4.12.3/src/sea.h knavalbattle-4.12.90/src/sea.h --- knavalbattle-4.12.3/src/sea.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/sea.h 2014-03-10 19:23:40.000000000 +0000 @@ -14,6 +14,7 @@ #include #include "ship.h" +#include "ships.h" #include "hitinfo.h" class BattleField; @@ -42,17 +43,18 @@ QList m_enemyShips; QList m_myShips; Status m_status; - bool m_allow_adjacent_ships; + BattleShipsConfiguration m_battle_ships_configuration; inline BattleField* currentField() const { return m_fields[m_turn]; } inline BattleField* otherField() const { return m_fields[opponent(m_turn)]; } void checkGameOver(); public: - Sea(QObject* parent, const Coord& size, const bool allow_adjacent_ships); + Sea(QObject* parent, const BattleShipsConfiguration& battleShipsConfiguration); ~Sea(); bool canAddShip(Player p, const Coord& pos, int size, Ship::Direction direction) const; + bool canAddShipOfSize(Player p, int size) const; void add(Player p, int n); void add(Player p, Ship* ship); void addBorder(Player p, const Coord& pos); @@ -66,7 +68,9 @@ void switchTurn(); bool isNearShip(Sea::Player, const Coord& pos) const; void allowAdjacentShips(const bool allow_adjacent_ships); - + void clear(Sea::Player); + void setBattleShipsConfiguration(const BattleShipsConfiguration& configuration); + const QList enemyShips() const; const QList myShips() const; @@ -74,7 +78,9 @@ inline Player turn() const { return m_turn; } static Player opponent(Player p); inline Coord size() const { return m_size; } - inline bool isAdjacentShipsAllowed() const { return m_allow_adjacent_ships; } + inline bool isAdjacentShipsAllowed() const { return m_battle_ships_configuration.isAllowedAdjacentShips(); } + inline bool isSeveralShipsAllowed() const { return m_battle_ships_configuration.multipleShips(); } + inline const BattleShipsConfiguration* battleShipsConfiguration() const { return &m_battle_ships_configuration; } }; #endif // Sea_H diff -Nru knavalbattle-4.12.3/src/seaview.cpp knavalbattle-4.12.90/src/seaview.cpp --- knavalbattle-4.12.3/src/seaview.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/seaview.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -162,14 +162,9 @@ { QPoint p = pos - m_fields[player]->pos(); Coord c = m_renderer->toLogical(p); - if (Ship* ship = m_delegate->canAddShip(player, c)) { - m_fields[player]->setPreview(p, ship); - return true; - } - else { - m_fields[player]->cancelPreview(); - return false; - } + + m_fields[player]->setPreview(p); + return m_delegate->canAddShip(player, c); } void SeaView::cancelPreview() @@ -199,6 +194,12 @@ m_fields[p]->sink(ship); } +void SeaView::setStatus(Sea::Status status) +{ + m_fields[0]->setStatus(status); + m_fields[1]->setStatus(status); +} + void SeaView::clear() { m_fields[0]->clear(); diff -Nru knavalbattle-4.12.3/src/seaview.h knavalbattle-4.12.90/src/seaview.h --- knavalbattle-4.12.3/src/seaview.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/seaview.h 2014-03-10 19:23:40.000000000 +0000 @@ -57,6 +57,7 @@ void miss(Sea::Player p, const Coord& c); void sink(Sea::Player p, Ship* ship); void clear(); + void setStatus(Sea::Status status); void setStats(Sea::Player p, const QString& icon, const QString& text, Stats* stats); diff -Nru knavalbattle-4.12.3/src/ships.cpp knavalbattle-4.12.90/src/ships.cpp --- knavalbattle-4.12.3/src/ships.cpp 1970-01-01 00:00:00.000000000 +0000 +++ knavalbattle-4.12.90/src/ships.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -0,0 +1,143 @@ +/* + Copyright (c) 2013 Jaime Torres + + 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. +*/ +#include "ships.h" + +Ships::Ships(unsigned int size, unsigned int number, QString& shipsName, QString& shipsPluralName) +:m_size(size) +,m_number(number) +,m_shipsName(shipsName) +,m_shipsPluralName(shipsPluralName) +{ +} + +BattleShipsConfiguration::BattleShipsConfiguration(const bool fromXML) +: m_longestShip(0) +, m_allowAdjacentShips(true) +, m_boardWidth(0) +, m_boardHeight(0) +, m_fromXML(fromXML) +, m_ships() +{ +} + +BattleShipsConfiguration::BattleShipsConfiguration(unsigned int longestShipSize, const bool allowAdjacentShips, const unsigned int boardWidth, const unsigned int boardHeight, const bool fromXML) +: m_longestShip(longestShipSize) +, m_allowAdjacentShips(allowAdjacentShips) +, m_boardWidth(boardWidth) +, m_boardHeight(boardHeight) +, m_fromXML(fromXML) +, m_ships() +{ +} + +BattleShipsConfiguration::BattleShipsConfiguration(const BattleShipsConfiguration& copy) +: m_longestShip(copy.longestShip()) +, m_allowAdjacentShips(copy.isAllowedAdjacentShips()) +, m_boardWidth(copy.boardWidth()) +, m_boardHeight(copy.boardHeight()) +, m_fromXML(copy.m_fromXML) +, m_ships(copy.m_ships) +{ +} + +void BattleShipsConfiguration::setLongestShipSize(unsigned int longestShipSize) +{ + m_longestShip = longestShipSize; + m_ships.reserve(m_longestShip); +} + +BattleShipsConfiguration& BattleShipsConfiguration::addShips(unsigned int size, unsigned int number, QString shipsName, QString shipsPluralName) +{ + if ( size<=m_longestShip ) + { + Ships toInsert(size, number, shipsName, shipsPluralName); + m_ships[size]=toInsert; + } + return *this; +} + +BattleShipsConfiguration& BattleShipsConfiguration::addShips(Ships& ships) +{ + if ( ships.size()<=m_longestShip ) + { + m_ships[ships.size()]=ships; + } + return *this; +} + +unsigned int BattleShipsConfiguration::numberOfShipsOfSize(unsigned int size) const +{ + return size <= m_longestShip ? m_ships[size].number() : 0; +} + +QString BattleShipsConfiguration::nameOfShipsOfSize(unsigned int size) const +{ + return size <= m_longestShip ? m_ships[size].shipsName() : QString(); +} + +QString BattleShipsConfiguration::pluralNameOfShipsOfSize(unsigned int size) const +{ + return size <= m_longestShip ? m_ships[size].pluralShipsName() : QString(); +} + +bool BattleShipsConfiguration::multipleShips() const +{ + bool res = false; + for (unsigned int i=0; i1); + } + return res; +} + +bool BattleShipsConfiguration::isAValidConfiguration() const +{ + if ( m_longestShip==0 || m_boardHeight==0 || m_boardWidth==0 + || m_longestShip>qMax(m_boardHeight, m_boardWidth) ) + { + return false; + } + for (unsigned int size=1; size <= m_longestShip; size++) + { + if ( m_ships[size].number() ==0 ) + { + return false; + } + } + return true; +} + +unsigned int BattleShipsConfiguration::totalNumberOfShipsToPlay() const +{ + unsigned int sum=0; + for (unsigned int size=1; size <= m_longestShip; size++) + { + sum += m_ships[size].number(); + } + return sum; +} + + +BattleShipsConfiguration BattleShipsConfiguration::defaultSingleShipsConfiguration(const bool allowAdjacent, const bool fromXML) +{ + BattleShipsConfiguration res(4, allowAdjacent, 10, 10, fromXML); + return res.addShips(1, 1, QLatin1String("minesweeper"), QLatin1String("minesweepers")) + .addShips(2, 1, QLatin1String("frigate"), QLatin1String("frigates")) + .addShips(3, 1, QLatin1String("cruise"), QLatin1String("cruises")) + .addShips(4, 1, QLatin1String("carrier"), QLatin1String("carriers")); +} + +BattleShipsConfiguration BattleShipsConfiguration::defaultMultipleShipsConfiguration(const bool allowAdjacent, const bool fromXML) +{ + BattleShipsConfiguration res(4, allowAdjacent, 10, 10, fromXML); + return res.addShips(1, 4, QLatin1String("minesweeper"), QLatin1String("minesweepers")) + .addShips(2, 3, QLatin1String("frigate"), QLatin1String("frigates")) + .addShips(3, 2, QLatin1String("cruise"), QLatin1String("cruises")) + .addShips(4, 1, QLatin1String("carrier"), QLatin1String("carriers")); +} diff -Nru knavalbattle-4.12.3/src/ships.h knavalbattle-4.12.90/src/ships.h --- knavalbattle-4.12.3/src/ships.h 1970-01-01 00:00:00.000000000 +0000 +++ knavalbattle-4.12.90/src/ships.h 2014-03-10 19:23:40.000000000 +0000 @@ -0,0 +1,78 @@ +/* + Copyright (c) 2013 Jaime Torres + + 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. +*/ + +#ifndef Ships_H +#define Ships_H + +#include +#include +#include "ship.h" + +class Ships +{ +private: + unsigned int m_size; // of the ships + unsigned int m_number; // number of ships of this size + QString m_shipsName; // the english singular name of the ships of this size + QString m_shipsPluralName; // the english plural of the ships of this size +public: + Ships(): m_size(0), m_number(0), m_shipsName(), m_shipsPluralName() { } + Ships(unsigned int size, unsigned int number, QString& shipsName, QString& shipsPluralName); + inline unsigned int size() const { return m_size; } + inline unsigned int number() const { return m_number; } + inline QString shipsName() const { return m_shipsName; } + inline QString pluralShipsName() const { return m_shipsPluralName; } +}; + +// This configuration is interchanged in the network games. +// In the versions previous to KDE SC 4.13, only adjacent and multiple where interchanged. +class BattleShipsConfiguration +{ +private: + unsigned int m_longestShip; + bool m_allowAdjacentShips; + unsigned int m_boardWidth; + unsigned int m_boardHeight; + bool m_fromXML; + QHash m_ships; +public: + + BattleShipsConfiguration(const bool fromXML=false); + explicit BattleShipsConfiguration(unsigned int longestShipSize, const bool allowAdjacentShips, const unsigned int boardWidth, const unsigned int boardHeight, const bool fromXML=false); + BattleShipsConfiguration(const BattleShipsConfiguration& copy); + // does not add any ship longer than longestShip + // overwrites any previous configuration for ships of the requested size + BattleShipsConfiguration& addShips(unsigned int size, unsigned int number, QString shipsName, QString shipsPluralName); + BattleShipsConfiguration& addShips(Ships &ships); + unsigned int numberOfShipsOfSize(unsigned int size) const; // 0 if size is invalid + QString nameOfShipsOfSize(unsigned int size) const; // QString() if size is invalid + QString pluralNameOfShipsOfSize(unsigned int size) const; // QString() if size is invalid + bool multipleShips() const; // return true if any ship size has more than one ship + + void setLongestShipSize(unsigned int longestShipSize); + void setAllowAdjacentShips(const bool allow) { m_allowAdjacentShips = allow; } + void setBoardWidth(const unsigned int boardWidth) { m_boardWidth = boardWidth; } + void setBoardHeight(const unsigned int boardHeight) { m_boardWidth = boardHeight; } + void setFromXML(bool fromXML) { m_fromXML=fromXML; } + inline unsigned int boardWidth() const { return m_boardWidth; } + inline unsigned int boardHeight() const { return m_boardHeight; } + inline bool isAllowedAdjacentShips() const { return m_allowAdjacentShips; } + inline bool isFromXML() const { return m_fromXML; } + // ships are of 0 < size <= longestShip() + inline unsigned int longestShip() const { return m_longestShip; } + unsigned int totalNumberOfShipsToPlay() const; + bool isAValidConfiguration() const; + + static BattleShipsConfiguration defaultSingleShipsConfiguration(const bool allowAdjacent, const bool fromXML = false); + static BattleShipsConfiguration defaultMultipleShipsConfiguration(const bool allowAdjacent, const bool fromXML = false); +}; + + + +#endif // Ships_H diff -Nru knavalbattle-4.12.3/src/sprite.cpp knavalbattle-4.12.90/src/sprite.cpp --- knavalbattle-4.12.3/src/sprite.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/sprite.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -10,6 +10,7 @@ #include "sprite.h" #include +#include #include "kbsrenderer.h" @@ -26,6 +27,20 @@ { } +void Sprite::turnGreen() +{ + QGraphicsColorizeEffect * fx = new QGraphicsColorizeEffect(); + fx->setColor(QColor(Qt::green)); + setGraphicsEffect(fx); +} + +void Sprite::turnRed() +{ + QGraphicsColorizeEffect * fx = new QGraphicsColorizeEffect(); + fx->setColor(QColor(Qt::red)); + setGraphicsEffect(fx); +} + void Sprite::refresh(KBSRenderer* renderer) { if (m_rotated) diff -Nru knavalbattle-4.12.3/src/sprite.h knavalbattle-4.12.90/src/sprite.h --- knavalbattle-4.12.3/src/sprite.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/sprite.h 2014-03-10 19:23:40.000000000 +0000 @@ -26,6 +26,8 @@ ~Sprite(); void refresh(KBSRenderer* renderer); + void turnRed(); + void turnGreen(); }; #endif // SPRITE_H diff -Nru knavalbattle-4.12.3/src/uientity.cpp knavalbattle-4.12.90/src/uientity.cpp --- knavalbattle-4.12.3/src/uientity.cpp 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/uientity.cpp 2014-03-10 19:23:40.000000000 +0000 @@ -16,7 +16,7 @@ #include UIEntity::UIEntity(Sea::Player player, Sea* sea, SeaView* view) -: Entity(player, view) +: Entity(player, view, sea->battleShipsConfiguration()) , m_sea(sea) { } @@ -38,7 +38,7 @@ void UIEntity::start(bool) { - m_seaview->clear(); + } void UIEntity::hit(Shot* shot) diff -Nru knavalbattle-4.12.3/src/uientity.h knavalbattle-4.12.90/src/uientity.h --- knavalbattle-4.12.3/src/uientity.h 2014-01-08 13:13:24.000000000 +0000 +++ knavalbattle-4.12.90/src/uientity.h 2014-03-10 19:23:40.000000000 +0000 @@ -30,7 +30,9 @@ virtual void notifyGameOver(Sea::Player winner); virtual void notifyChat(const Entity*, const QString&) { } virtual void notifyNick(Sea::Player, const QString&) { } + virtual void notifyGameOptions(bool ask) { emit gameOptionsInterchanged(); } virtual void start(bool); + virtual void startPlacing(bool) { }; virtual void hit(Shot*); virtual void registerHit(Sea::Player, const Coord&) { } @@ -39,6 +41,7 @@ virtual KIcon icon() const; public slots: virtual void notifyAbort() { } + virtual void notifyRestartPlacing(Sea::Player player) { }; }; #endif // UIENTITY_H