From ce48af53646cd9e7ec762fc1ac176b3aa620b11d Mon Sep 17 00:00:00 2001 From: manuel Date: Thu, 5 May 2011 00:57:07 +0200 Subject: - refactorized the whole project and made a few subprojects - replaced tcp with enet - added connect dialog - some smaller bugfixes --- pacman-c++/actor.cpp | 375 -------- pacman-c++/actor.h | 81 -- pacman-c++/anyoption.cpp | 1175 ----------------------- pacman-c++/anyoption.h | 270 ------ pacman-c++/audio.cpp | 355 ------- pacman-c++/audio.h | 148 --- pacman-c++/block.cpp | 56 -- pacman-c++/block.h | 43 - pacman-c++/bonuspoint.cpp | 38 - pacman-c++/bonuspoint.h | 24 - pacman-c++/clicklabel.cpp | 12 - pacman-c++/clicklabel.h | 21 - pacman-c++/client.cpp | 179 ---- pacman-c++/client.h | 38 - pacman-c++/client/clicklabel.cpp | 12 + pacman-c++/client/clicklabel.h | 21 + pacman-c++/client/client.cpp | 265 +++++ pacman-c++/client/client.h | 41 + pacman-c++/client/client.pro | 12 + pacman-c++/client/mainwidget.cpp | 390 ++++++++ pacman-c++/client/mainwidget.h | 80 ++ pacman-c++/common.pri | 21 + pacman-c++/common/actor.cpp | 375 ++++++++ pacman-c++/common/actor.h | 81 ++ pacman-c++/common/audio.cpp | 355 +++++++ pacman-c++/common/audio.h | 148 +++ pacman-c++/common/block.cpp | 56 ++ pacman-c++/common/block.h | 43 + pacman-c++/common/bonuspoint.cpp | 38 + pacman-c++/common/bonuspoint.h | 24 + pacman-c++/common/common.pro | 30 + pacman-c++/common/constants.h | 102 ++ pacman-c++/common/gameentity.cpp | 9 + pacman-c++/common/gameentity.h | 72 ++ pacman-c++/common/pacman.qrc | 31 + pacman-c++/common/pacman.rc | 1 + pacman-c++/common/pics/actor1.png | Bin 0 -> 1231 bytes pacman-c++/common/pics/actor1icon.png | Bin 0 -> 232 bytes pacman-c++/common/pics/actor2.png | Bin 0 -> 1257 bytes pacman-c++/common/pics/actor2icon.png | Bin 0 -> 233 bytes pacman-c++/common/pics/actor3.png | Bin 0 -> 1226 bytes pacman-c++/common/pics/actor3icon.png | Bin 0 -> 231 bytes pacman-c++/common/pics/actor4.png | Bin 0 -> 1536 bytes pacman-c++/common/pics/actor4icon.png | Bin 0 -> 233 bytes pacman-c++/common/pics/app.ico | Bin 0 -> 174 bytes pacman-c++/common/pics/block0.png | Bin 0 -> 479 bytes pacman-c++/common/pics/block1.png | Bin 0 -> 453 bytes pacman-c++/common/pics/block2.png | Bin 0 -> 476 bytes pacman-c++/common/pics/block3.png | Bin 0 -> 452 bytes pacman-c++/common/pics/block4.png | Bin 0 -> 471 bytes pacman-c++/common/pics/bonuspoints.png | Bin 0 -> 642 bytes pacman-c++/common/pics/pacman10-hp-sprite-2.png | Bin 0 -> 6857 bytes pacman-c++/common/pics/points.png | Bin 0 -> 188 bytes pacman-c++/common/pics/soundoff.xpm | 15 + pacman-c++/common/pics/soundon.xpm | 15 + pacman-c++/common/pixmapitem.cpp | 77 ++ pacman-c++/common/pixmapitem.h | 32 + pacman-c++/common/point.cpp | 33 + pacman-c++/common/point.h | 24 + pacman-c++/common/sceneholder.cpp | 364 +++++++ pacman-c++/common/sceneholder.h | 74 ++ pacman-c++/common/sound/ambient.ogg | Bin 0 -> 23243 bytes pacman-c++/common/sound/die.ogg | Bin 0 -> 20271 bytes pacman-c++/common/sound/eating_fruit.ogg | Bin 0 -> 8774 bytes pacman-c++/common/sound/eating_ghost.ogg | Bin 0 -> 9238 bytes pacman-c++/common/sound/intro.ogg | Bin 0 -> 45705 bytes pacman-c++/common/sound/waka_waka.ogg | Bin 0 -> 25855 bytes pacman-c++/common/style.qss | 31 + pacman-c++/common/util.cpp | 342 +++++++ pacman-c++/common/util.h | 39 + pacman-c++/constants.h | 101 -- pacman-c++/gameentity.cpp | 9 - pacman-c++/gameentity.h | 72 -- pacman-c++/mainwidget.cpp | 279 ------ pacman-c++/mainwidget.h | 64 -- pacman-c++/pacman.pro | 53 +- pacman-c++/pacman.proto | 18 - pacman-c++/pacman.qrc | 31 - pacman-c++/pacman.rc | 1 - pacman-c++/pacman.server.pro | 50 - pacman-c++/pics/actor1.png | Bin 1231 -> 0 bytes pacman-c++/pics/actor1icon.png | Bin 232 -> 0 bytes pacman-c++/pics/actor2.png | Bin 1257 -> 0 bytes pacman-c++/pics/actor2icon.png | Bin 233 -> 0 bytes pacman-c++/pics/actor3.png | Bin 1226 -> 0 bytes pacman-c++/pics/actor3icon.png | Bin 231 -> 0 bytes pacman-c++/pics/actor4.png | Bin 1536 -> 0 bytes pacman-c++/pics/actor4icon.png | Bin 233 -> 0 bytes pacman-c++/pics/app.ico | Bin 174 -> 0 bytes pacman-c++/pics/block0.png | Bin 479 -> 0 bytes pacman-c++/pics/block1.png | Bin 453 -> 0 bytes pacman-c++/pics/block2.png | Bin 476 -> 0 bytes pacman-c++/pics/block3.png | Bin 452 -> 0 bytes pacman-c++/pics/block4.png | Bin 471 -> 0 bytes pacman-c++/pics/bonuspoints.png | Bin 642 -> 0 bytes pacman-c++/pics/pacman10-hp-sprite-2.png | Bin 6857 -> 0 bytes pacman-c++/pics/points.png | Bin 188 -> 0 bytes pacman-c++/pics/soundoff.xpm | 15 - pacman-c++/pics/soundon.xpm | 15 - pacman-c++/pixmapitem.cpp | 77 -- pacman-c++/pixmapitem.h | 32 - pacman-c++/point.cpp | 33 - pacman-c++/point.h | 24 - pacman-c++/proto/pacman.proto | 18 + pacman-c++/proto/proto.pro | 9 + pacman-c++/proto/protobuf.pri | 33 + pacman-c++/protobuf.pri | 33 - pacman-c++/rebuild-client.sh | 4 - pacman-c++/rebuild-server.sh | 4 - pacman-c++/rebuild.sh | 4 + pacman-c++/sceneholder.cpp | 336 ------- pacman-c++/sceneholder.h | 73 -- pacman-c++/server.cpp | 900 ----------------- pacman-c++/server.h | 78 -- pacman-c++/server/anyoption.cpp | 1175 +++++++++++++++++++++++ pacman-c++/server/anyoption.h | 270 ++++++ pacman-c++/server/server.cpp | 971 +++++++++++++++++++ pacman-c++/server/server.h | 85 ++ pacman-c++/server/server.pro | 10 + pacman-c++/sound/ambient.ogg | Bin 23243 -> 0 bytes pacman-c++/sound/die.ogg | Bin 20271 -> 0 bytes pacman-c++/sound/eating_fruit.ogg | Bin 8774 -> 0 bytes pacman-c++/sound/eating_ghost.ogg | Bin 9238 -> 0 bytes pacman-c++/sound/intro.ogg | Bin 45705 -> 0 bytes pacman-c++/sound/waka_waka.ogg | Bin 25855 -> 0 bytes pacman-c++/style.qss | 31 - pacman-c++/util.cpp | 357 ------- pacman-c++/util.h | 37 - 128 files changed, 5834 insertions(+), 5536 deletions(-) delete mode 100644 pacman-c++/actor.cpp delete mode 100644 pacman-c++/actor.h delete mode 100644 pacman-c++/anyoption.cpp delete mode 100644 pacman-c++/anyoption.h delete mode 100644 pacman-c++/audio.cpp delete mode 100644 pacman-c++/audio.h delete mode 100644 pacman-c++/block.cpp delete mode 100644 pacman-c++/block.h delete mode 100644 pacman-c++/bonuspoint.cpp delete mode 100644 pacman-c++/bonuspoint.h delete mode 100644 pacman-c++/clicklabel.cpp delete mode 100644 pacman-c++/clicklabel.h delete mode 100644 pacman-c++/client.cpp delete mode 100644 pacman-c++/client.h create mode 100644 pacman-c++/client/clicklabel.cpp create mode 100644 pacman-c++/client/clicklabel.h create mode 100644 pacman-c++/client/client.cpp create mode 100644 pacman-c++/client/client.h create mode 100644 pacman-c++/client/client.pro create mode 100644 pacman-c++/client/mainwidget.cpp create mode 100644 pacman-c++/client/mainwidget.h create mode 100644 pacman-c++/common.pri create mode 100644 pacman-c++/common/actor.cpp create mode 100644 pacman-c++/common/actor.h create mode 100644 pacman-c++/common/audio.cpp create mode 100644 pacman-c++/common/audio.h create mode 100644 pacman-c++/common/block.cpp create mode 100644 pacman-c++/common/block.h create mode 100644 pacman-c++/common/bonuspoint.cpp create mode 100644 pacman-c++/common/bonuspoint.h create mode 100644 pacman-c++/common/common.pro create mode 100644 pacman-c++/common/constants.h create mode 100644 pacman-c++/common/gameentity.cpp create mode 100644 pacman-c++/common/gameentity.h create mode 100644 pacman-c++/common/pacman.qrc create mode 100644 pacman-c++/common/pacman.rc create mode 100644 pacman-c++/common/pics/actor1.png create mode 100644 pacman-c++/common/pics/actor1icon.png create mode 100644 pacman-c++/common/pics/actor2.png create mode 100644 pacman-c++/common/pics/actor2icon.png create mode 100644 pacman-c++/common/pics/actor3.png create mode 100644 pacman-c++/common/pics/actor3icon.png create mode 100644 pacman-c++/common/pics/actor4.png create mode 100644 pacman-c++/common/pics/actor4icon.png create mode 100644 pacman-c++/common/pics/app.ico create mode 100644 pacman-c++/common/pics/block0.png create mode 100644 pacman-c++/common/pics/block1.png create mode 100644 pacman-c++/common/pics/block2.png create mode 100644 pacman-c++/common/pics/block3.png create mode 100644 pacman-c++/common/pics/block4.png create mode 100644 pacman-c++/common/pics/bonuspoints.png create mode 100644 pacman-c++/common/pics/pacman10-hp-sprite-2.png create mode 100644 pacman-c++/common/pics/points.png create mode 100644 pacman-c++/common/pics/soundoff.xpm create mode 100644 pacman-c++/common/pics/soundon.xpm create mode 100644 pacman-c++/common/pixmapitem.cpp create mode 100644 pacman-c++/common/pixmapitem.h create mode 100644 pacman-c++/common/point.cpp create mode 100644 pacman-c++/common/point.h create mode 100644 pacman-c++/common/sceneholder.cpp create mode 100644 pacman-c++/common/sceneholder.h create mode 100644 pacman-c++/common/sound/ambient.ogg create mode 100644 pacman-c++/common/sound/die.ogg create mode 100644 pacman-c++/common/sound/eating_fruit.ogg create mode 100644 pacman-c++/common/sound/eating_ghost.ogg create mode 100644 pacman-c++/common/sound/intro.ogg create mode 100644 pacman-c++/common/sound/waka_waka.ogg create mode 100644 pacman-c++/common/style.qss create mode 100644 pacman-c++/common/util.cpp create mode 100644 pacman-c++/common/util.h delete mode 100644 pacman-c++/constants.h delete mode 100644 pacman-c++/gameentity.cpp delete mode 100644 pacman-c++/gameentity.h delete mode 100644 pacman-c++/mainwidget.cpp delete mode 100644 pacman-c++/mainwidget.h delete mode 100644 pacman-c++/pacman.proto delete mode 100644 pacman-c++/pacman.qrc delete mode 100644 pacman-c++/pacman.rc delete mode 100644 pacman-c++/pacman.server.pro delete mode 100644 pacman-c++/pics/actor1.png delete mode 100644 pacman-c++/pics/actor1icon.png delete mode 100644 pacman-c++/pics/actor2.png delete mode 100644 pacman-c++/pics/actor2icon.png delete mode 100644 pacman-c++/pics/actor3.png delete mode 100644 pacman-c++/pics/actor3icon.png delete mode 100644 pacman-c++/pics/actor4.png delete mode 100644 pacman-c++/pics/actor4icon.png delete mode 100644 pacman-c++/pics/app.ico delete mode 100644 pacman-c++/pics/block0.png delete mode 100644 pacman-c++/pics/block1.png delete mode 100644 pacman-c++/pics/block2.png delete mode 100644 pacman-c++/pics/block3.png delete mode 100644 pacman-c++/pics/block4.png delete mode 100644 pacman-c++/pics/bonuspoints.png delete mode 100644 pacman-c++/pics/pacman10-hp-sprite-2.png delete mode 100644 pacman-c++/pics/points.png delete mode 100644 pacman-c++/pics/soundoff.xpm delete mode 100644 pacman-c++/pics/soundon.xpm delete mode 100644 pacman-c++/pixmapitem.cpp delete mode 100644 pacman-c++/pixmapitem.h delete mode 100644 pacman-c++/point.cpp delete mode 100644 pacman-c++/point.h create mode 100644 pacman-c++/proto/pacman.proto create mode 100644 pacman-c++/proto/proto.pro create mode 100644 pacman-c++/proto/protobuf.pri delete mode 100644 pacman-c++/protobuf.pri delete mode 100755 pacman-c++/rebuild-client.sh delete mode 100755 pacman-c++/rebuild-server.sh create mode 100755 pacman-c++/rebuild.sh delete mode 100644 pacman-c++/sceneholder.cpp delete mode 100644 pacman-c++/sceneholder.h delete mode 100644 pacman-c++/server.cpp delete mode 100644 pacman-c++/server.h create mode 100644 pacman-c++/server/anyoption.cpp create mode 100644 pacman-c++/server/anyoption.h create mode 100644 pacman-c++/server/server.cpp create mode 100644 pacman-c++/server/server.h create mode 100644 pacman-c++/server/server.pro delete mode 100644 pacman-c++/sound/ambient.ogg delete mode 100644 pacman-c++/sound/die.ogg delete mode 100644 pacman-c++/sound/eating_fruit.ogg delete mode 100644 pacman-c++/sound/eating_ghost.ogg delete mode 100644 pacman-c++/sound/intro.ogg delete mode 100644 pacman-c++/sound/waka_waka.ogg delete mode 100644 pacman-c++/style.qss delete mode 100644 pacman-c++/util.cpp delete mode 100644 pacman-c++/util.h (limited to 'pacman-c++') diff --git a/pacman-c++/actor.cpp b/pacman-c++/actor.cpp deleted file mode 100644 index de8d77e..0000000 --- a/pacman-c++/actor.cpp +++ /dev/null @@ -1,375 +0,0 @@ -#include "actor.h" -#include "util.h" -#include -#include -#include - -static QVariant myBooleanInterpolator(const bool &start, const bool &end, qreal progress) -{ - return (progress == 1.0) ? end : start; -} - -Actor::Actor(Color::Color color, bool local, QGraphicsItem *parent) - : GameEntity(color, parent), m_direction(Actor::None), m_local(local), m_reset(true), - m_wakaPlayer(NULL), m_roundPoints(0), m_gamePoints(0) -{ - m_type = Type; - - /* DON'T set any pixmap here. we've a pixmap in the animation - * but we need a sprite for the collision detection - */ - setSprite(Constants::sprite_margin, Constants::sprite_margin, Constants::field_size.width, Constants::field_size.height); - /* higher player "over" lower player */ - setZValue(m_color * 10); - - /* rest of the ctor is only for clients */ - if (Constants::server) - return; - - /* our actor pixmap. created after server part */ - m_pix = ":/" + QString("actor%1").arg(Util::floorLog2(color) + 1); - - /* setup icon for player */ - m_icon.setPixmap(m_pix); - m_icon.setSprite(Constants::sprite_margin, Constants::sprite_margin, Constants::field_size.width, Constants::field_size.height); - - /* setup static images first - * they are visible if no animation is running - * and will be the first in m_images - */ - for (int i = 0; i < 5; i++) - { - PixmapItem *img = new PixmapItem(m_pix, this); - m_images.append(img); - int x = i * Constants::sprite_offset + Constants::sprite_margin; - int y = Actor::None * Constants::sprite_offset + Constants::sprite_margin; - img->setSprite(x, y, Constants::field_size.width, Constants::field_size.height); - img->setVisible(false); - } - - /* setup animation stuff */ - qRegisterAnimationInterpolator(myBooleanInterpolator); - m_moving = new QParallelAnimationGroup(this); - m_eating.append(NULL); // Actor::None - m_eating.append(setupEatingAnimation(Actor::Left)); - m_eating.append(setupEatingAnimation(Actor::Right)); - m_eating.append(setupEatingAnimation(Actor::Up)); - m_eating.append(setupEatingAnimation(Actor::Down)); - - /* dieing animation */ - m_dieing = new QSequentialAnimationGroup(this); - for (int i = 0; i < 11; i++) - { - PixmapItem *img = new PixmapItem(m_pix, this); - m_images.append(img); - int x = i * Constants::sprite_offset + Constants::sprite_margin; - int y = 5 * Constants::sprite_offset + Constants::sprite_margin; - img->setSprite(x, y, Constants::field_size.width, Constants::field_size.height); - img->setVisible(false); - - QPropertyAnimation *fadein = new QPropertyAnimation(img, "visible", m_dieing); - fadein->setDuration(0); - fadein->setEndValue(true); - - m_dieing->addPause(130); - - QPropertyAnimation *fadeout = new QPropertyAnimation(img, "visible", m_dieing); - fadeout->setDuration(0); - fadeout->setEndValue(false); - } - - /* setup waka sound */ - if (local) - m_wakaPlayer = new GaplessAudioPlayer(Sound::WakaWaka, 100, this); - - /* make the picture showing the current direction visible */ - m_images[m_direction]->setVisible(true); -} - -QSequentialAnimationGroup *Actor::setupEatingAnimation(Actor::Movement direction) -{ - QSequentialAnimationGroup *eating = new QSequentialAnimationGroup(this); - eating->setLoopCount(-1); - for (int i = 0; i < 4; i++) - { - PixmapItem *img = new PixmapItem(m_pix, this); - m_images.append(img); - int x = i * Constants::sprite_offset + Constants::sprite_margin; - int y = direction * Constants::sprite_offset + Constants::sprite_margin; - img->setSprite(x, y, Constants::field_size.width, Constants::field_size.height); - img->setVisible(false); - - QPropertyAnimation *fadein = new QPropertyAnimation(img, "visible", eating); - fadein->setDuration(0); - fadein->setEndValue(true); - - eating->addPause(100); - - QPropertyAnimation *fadeout = new QPropertyAnimation(img, "visible", eating); - fadeout->setDuration(0); - fadeout->setEndValue(false); - - QPropertyAnimation *move = new QPropertyAnimation(img, "pos", m_moving); - move->setDuration(Constants::tick - 30); - move->setEndValue(QPoint(0, 0)); - } - - return eating; -} - -PixmapItem &Actor::icon() -{ - return m_icon; -} - -const QString Actor::iconStr() -{ - return QString(":/actor%1icon").arg(Util::floorLog2(m_color) + 1); -} - -Actor::Movement Actor::direction() -{ - return m_direction; -} - - -void Actor::setDirection(Movement direction) -{ - m_direction = direction; -} - -bool Actor::isLocal() -{ - return m_local; -} - -bool Actor::hadReset() -{ - if (!m_reset) - return false; - m_reset = false; - return true; -} - -void Actor::reset() -{ - m_reset = true; - if (Constants::server) - { - m_direction = Actor::None; - return; - } - - stopEating(); - m_moving->stop(); - setZValue(m_color * 10); - m_dieing->stop(); - /* hide all pictures */ - for (int i = 0; i < m_images.size(); ++i) - m_images.at(i)->setVisible(false); - - if (m_eating[m_direction] != NULL) - m_eating[m_direction]->stop(); - - m_direction = Actor::None; - m_images[m_direction]->setVisible(true); -} - -void Actor::move(QPoint newpos) -{ - QPoint oldpos = pos().toPoint(); - Actor::Movement direction = Actor::None; - if (oldpos.x() - newpos.x() < 0) - direction = Actor::Right; - else if (oldpos.x() - newpos.x() > 0) - direction = Actor::Left; - else if (oldpos.y() - newpos.y() < 0) - direction = Actor::Down; - else if (oldpos.y() - newpos.y() > 0) - direction = Actor::Up; - move(direction); -} - -void Actor::move(Actor::Movement direction) -{ - if (Constants::server) - return moveByServer(direction); - - /* stop current animation if direction changed */ - if (direction != m_direction) - { - /* hide all pictures */ - for (int i = 0; i < m_images.size(); ++i) - m_images.at(i)->setVisible(false); - - if (m_eating[m_direction] != NULL) - m_eating[m_direction]->stop(); - } - - QPointF endpos = movementToPoint(direction); - switch(direction) - { - case Actor::Left: - case Actor::Right: - endpos *= Constants::field_size.width; - break; - case Actor::Up: - case Actor::Down: - endpos *= Constants::field_size.height; - break; - case Actor::None: - default: - break; - } - - for(int i = 0; i < m_moving->animationCount(); ++i) - { - QPropertyAnimation *move = dynamic_cast(m_moving->animationAt(i)); - move->setStartValue(QPoint(0, 0) - endpos); - } - setPos(pos() + endpos); - - /* start new animation if direction changed */ - if (direction != m_direction) - { - if (direction == Actor::None) - m_images[m_direction]->setVisible(true); - else - m_eating[direction]->start(); - } - - /* start moving animation */ - if (direction != Actor::None) - m_moving->start(); - - m_direction = direction; -} - -void Actor::moveByServer(Actor::Movement direction) -{ - QPointF endpos = movementToPoint(direction); - switch(direction) - { - case Actor::Left: - case Actor::Right: - endpos *= Constants::field_size.width; - break; - case Actor::Up: - case Actor::Down: - endpos *= Constants::field_size.height; - break; - case Actor::None: - default: - break; - } - setPos(pos() + endpos); - m_direction = direction; -} - -bool Actor::isMoving() -{ - return (m_moving->state() == QAbstractAnimation::Running); -} - -bool Actor::canEat(Actor *other, const QList &order) -{ - if (other == NULL || order.empty() || m_color == other->color()) - return false; - - int idx = order.indexOf(m_color); - return (order.at(idx + 1) == other->color()); -} - -void Actor::onDie(Actor *other) -{ - other->eatingPacman(); - die(); -} - -void Actor::die() -{ - if (Constants::server) - return; - - reset(); - m_images[m_direction]->setVisible(false); - setZValue(zValue() * 10); - m_dieing->start(); - if (m_local) - AudioManager::self()->play(Sound::Die); -} - -void Actor::eatingFruit() -{ - if (!m_local) - return; - AudioManager::self()->play(Sound::EatingFruit); -} - -void Actor::eatingPacman() -{ - if (!m_local) - return; - AudioManager::self()->play(Sound::EatingGhost); -} - -void Actor::startEating() -{ - if (!m_local || !m_wakaPlayer->isWorking()) - return; - m_wakaPlayer->play(); -} - -void Actor::stopEating() -{ - if (!m_local || !m_wakaPlayer->isWorking()) - return; - m_wakaPlayer->pause(); -} - -unsigned int Actor::getRoundPoints() -{ - return m_roundPoints; -} - -unsigned int Actor::getGamePoints() -{ - return m_gamePoints; -} - -void Actor::addRoundPoints(unsigned int amount) -{ - m_roundPoints += amount; -} - -void Actor::finishRound(bool died) -{ - if (!died) - m_gamePoints += m_roundPoints; - m_roundPoints = 0; -} - -QPoint Actor::movementToPoint(const Actor::Movement direction) -{ - QPoint endpos(0,0); - switch (direction) - { - case Actor::Up: - endpos = QPoint(0, -1); - break; - case Actor::Down: - endpos = QPoint(0, 1); - break; - case Actor::Left: - endpos = QPoint(-1, 0); - break; - case Actor::Right: - endpos = QPoint(1, 0); - break; - case Actor::None: - break; - default: - Q_ASSERT(false); - } - return endpos; -} diff --git a/pacman-c++/actor.h b/pacman-c++/actor.h deleted file mode 100644 index c30c62a..0000000 --- a/pacman-c++/actor.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef ACTOR_H -#define ACTOR_H - -#include "gameentity.h" -#include "constants.h" -#include "audio.h" -#include -#include -#include - -class Actor - : public GameEntity -{ -Q_OBJECT - -public: - enum Movement - { - None = 0, - Left, - Right, - Up, - Down, - }; - - enum - { - Type = UserType + Transmission::pacman - }; - - Actor(Color::Color color, bool local = false, QGraphicsItem *parent = 0); - virtual ~Actor() - {}; - - PixmapItem &icon(); - const QString iconStr(); - Movement direction(); - void setDirection(Movement direction); - void reset(); - bool hadReset(); - bool isLocal(); - void move(Movement direction); - void move(QPoint newpos); - bool isMoving(); - void die(); - void eatingFruit(); - void eatingPacman(); - void startEating(); - void stopEating(); - bool canEat(Actor *other, const QList &order); - virtual void onDie(Actor *); - - unsigned int getRoundPoints(); - unsigned int getGamePoints(); - void addRoundPoints(unsigned int amount); - void finishRound(bool died = false); - - static QPoint movementToPoint(const Movement direction); - -private: - void moveByServer(Movement direction); - QSequentialAnimationGroup *setupEatingAnimation(Actor::Movement direction); - -private: - QPixmap m_pix; - Movement m_direction; - PixmapItem m_icon; - bool m_local; - bool m_reset; - GaplessAudioPlayer *m_wakaPlayer; - - unsigned int m_roundPoints; - unsigned int m_gamePoints; - - QList m_images; - QList m_eating; - QParallelAnimationGroup *m_moving; - QSequentialAnimationGroup *m_dieing; -}; - -#endif // ACTOR_H diff --git a/pacman-c++/anyoption.cpp b/pacman-c++/anyoption.cpp deleted file mode 100644 index 4e3828e..0000000 --- a/pacman-c++/anyoption.cpp +++ /dev/null @@ -1,1175 +0,0 @@ -/* - * AnyOption 1.3 - * - * kishan at hackorama dot com www.hackorama.com JULY 2001 - * - * + Acts as a common facade class for reading - * commandline options as well as options from - * an optionfile with delimited type value pairs - * - * + Handles the POSIX style single character options ( -w ) - * as well as the newer GNU long options ( --width ) - * - * + The option file assumes the traditional format of - * first character based comment lines and type value - * pairs with a delimiter , and flags which are not pairs - * - * # this is a coment - * # next line is an option value pair - * width : 100 - * # next line is a flag - * noimages - * - * + Supports printing out Help and Usage - * - * + Why not just use getopt() ? - * - * getopt() Its a POSIX standard not part of ANSI-C. - * So it may not be available on platforms like Windows. - * - * + Why it is so long ? - * - * The actual code which does command line parsing - * and option file parsing are done in few methods. - * Most of the extra code are for providing a flexible - * common public interface to both a resourcefile and - * and command line supporting POSIX style and - * GNU long option as well as mixing of both. - * - * + Please see "anyoption.h" for public method descriptions - * - */ - -/* Updated Auguest 2004 - * Fix from Michael D Peters (mpeters at sandia.gov) - * to remove static local variables, allowing multiple instantiations - * of the reader (for using multiple configuration files). There is - * an error in the destructor when using multiple instances, so you - * cannot delete your objects (it will crash), but not calling the - * destructor only introduces a small memory leak, so I - * have not bothered tracking it down. - * - * Also updated to use modern C++ style headers, rather than - * depricated iostream.h (it was causing my compiler problems) -*/ - -/* - * Updated September 2006 - * Fix from Boyan Asenov for a bug in mixing up option indexes - * leading to exception when mixing different options types - */ - -#include "anyoption.h" -#include - -AnyOption::AnyOption() -{ - init(); -} - -AnyOption::AnyOption(int maxopt) -{ - init( maxopt , maxopt ); -} - -AnyOption::AnyOption(int maxopt, int maxcharopt) -{ - init( maxopt , maxcharopt ); -} - -AnyOption::~AnyOption() -{ - if( mem_allocated ) - cleanup(); -} - -void -AnyOption::init() -{ - init( DEFAULT_MAXOPTS , DEFAULT_MAXOPTS ); -} - -void -AnyOption::init(int maxopt, int maxcharopt ) -{ - - max_options = maxopt; - max_char_options = maxcharopt; - max_usage_lines = DEFAULT_MAXUSAGE; - usage_lines = 0 ; - argc = 0; - argv = NULL; - posix_style = true; - verbose = false; - filename = NULL; - appname = NULL; - option_counter = 0; - optchar_counter = 0; - new_argv = NULL; - new_argc = 0 ; - max_legal_args = 0 ; - command_set = false; - file_set = false; - values = NULL; - g_value_counter = 0; - mem_allocated = false; - command_set = false; - file_set = false; - opt_prefix_char = '-'; - file_delimiter_char = ':'; - file_comment_char = '#'; - equalsign = '='; - comment = '#' ; - delimiter = ':' ; - endofline = '\n'; - whitespace = ' ' ; - nullterminate = '\0'; - set = false; - once = true; - hasoptions = false; - autousage = false; - - strcpy( long_opt_prefix , "--" ); - - if( alloc() == false ){ - cout << endl << "OPTIONS ERROR : Failed allocating memory" ; - cout << endl ; - cout << "Exiting." << endl ; - exit (0); - } -} - -bool -AnyOption::alloc() -{ - int i = 0 ; - int size = 0 ; - - if( mem_allocated ) - return true; - - size = (max_options+1) * sizeof(const char*); - options = (const char**)malloc( size ); - optiontype = (int*) malloc( (max_options+1)*sizeof(int) ); - optionindex = (int*) malloc( (max_options+1)*sizeof(int) ); - if( options == NULL || optiontype == NULL || optionindex == NULL ) - return false; - else - mem_allocated = true; - for( i = 0 ; i < max_options ; i++ ){ - options[i] = NULL; - optiontype[i] = 0 ; - optionindex[i] = -1 ; - } - optionchars = (char*) malloc( (max_char_options+1)*sizeof(char) ); - optchartype = (int*) malloc( (max_char_options+1)*sizeof(int) ); - optcharindex = (int*) malloc( (max_char_options+1)*sizeof(int) ); - if( optionchars == NULL || - optchartype == NULL || - optcharindex == NULL ) - { - mem_allocated = false; - return false; - } - for( i = 0 ; i < max_char_options ; i++ ){ - optionchars[i] = '0'; - optchartype[i] = 0 ; - optcharindex[i] = -1 ; - } - - size = (max_usage_lines+1) * sizeof(const char*) ; - usage = (const char**) malloc( size ); - - if( usage == NULL ){ - mem_allocated = false; - return false; - } - for( i = 0 ; i < max_usage_lines ; i++ ) - usage[i] = NULL; - - return true; -} - -bool -AnyOption::doubleOptStorage() -{ - options = (const char**)realloc( options, - ((2*max_options)+1) * sizeof( const char*) ); - optiontype = (int*) realloc( optiontype , - ((2 * max_options)+1)* sizeof(int) ); - optionindex = (int*) realloc( optionindex, - ((2 * max_options)+1) * sizeof(int) ); - if( options == NULL || optiontype == NULL || optionindex == NULL ) - return false; - /* init new storage */ - for( int i = max_options ; i < 2*max_options ; i++ ){ - options[i] = NULL; - optiontype[i] = 0 ; - optionindex[i] = -1 ; - } - max_options = 2 * max_options ; - return true; -} - -bool -AnyOption::doubleCharStorage() -{ - optionchars = (char*) realloc( optionchars, - ((2*max_char_options)+1)*sizeof(char) ); - optchartype = (int*) realloc( optchartype, - ((2*max_char_options)+1)*sizeof(int) ); - optcharindex = (int*) realloc( optcharindex, - ((2*max_char_options)+1)*sizeof(int) ); - if( optionchars == NULL || - optchartype == NULL || - optcharindex == NULL ) - return false; - /* init new storage */ - for( int i = max_char_options ; i < 2*max_char_options ; i++ ){ - optionchars[i] = '0'; - optchartype[i] = 0 ; - optcharindex[i] = -1 ; - } - max_char_options = 2 * max_char_options; - return true; -} - -bool -AnyOption::doubleUsageStorage() -{ - usage = (const char**)realloc( usage, - ((2*max_usage_lines)+1) * sizeof( const char*) ); - if ( usage == NULL ) - return false; - for( int i = max_usage_lines ; i < 2*max_usage_lines ; i++ ) - usage[i] = NULL; - max_usage_lines = 2 * max_usage_lines ; - return true; - -} - - -void -AnyOption::cleanup() -{ - free (options); - free (optiontype); - free (optionindex); - free (optionchars); - free (optchartype); - free (optcharindex); - free (usage); - if( values != NULL ) - free (values); - if( new_argv != NULL ) - free (new_argv); -} - -void -AnyOption::setCommandPrefixChar( char _prefix ) -{ - opt_prefix_char = _prefix; -} - -void -AnyOption::setCommandLongPrefix( char *_prefix ) -{ - if( strlen( _prefix ) > MAX_LONG_PREFIX_LENGTH ){ - *( _prefix + MAX_LONG_PREFIX_LENGTH ) = '\0'; - } - - strcpy (long_opt_prefix, _prefix); -} - -void -AnyOption::setFileCommentChar( char _comment ) -{ - file_delimiter_char = _comment; -} - - -void -AnyOption::setFileDelimiterChar( char _delimiter ) -{ - file_comment_char = _delimiter ; -} - -bool -AnyOption::CommandSet() -{ - return( command_set ); -} - -bool -AnyOption::FileSet() -{ - return( file_set ); -} - -void -AnyOption::noPOSIX() -{ - posix_style = false; -} - -bool -AnyOption::POSIX() -{ - return posix_style; -} - - -void -AnyOption::setVerbose() -{ - verbose = true ; -} - -void -AnyOption::printVerbose() -{ - if( verbose ) - cout << endl ; -} -void -AnyOption::printVerbose( const char *msg ) -{ - if( verbose ) - cout << msg ; -} - -void -AnyOption::printVerbose( char *msg ) -{ - if( verbose ) - cout << msg ; -} - -void -AnyOption::printVerbose( char ch ) -{ - if( verbose ) - cout << ch ; -} - -bool -AnyOption::hasOptions() -{ - return hasoptions; -} - -void -AnyOption::autoUsagePrint(bool _autousage) -{ - autousage = _autousage; -} - -void -AnyOption::useCommandArgs( int _argc, char **_argv ) -{ - argc = _argc; - argv = _argv; - command_set = true; - appname = argv[0]; - if(argc > 1) hasoptions = true; -} - -void -AnyOption::useFiileName( const char *_filename ) -{ - filename = _filename; - file_set = true; -} - -/* - * set methods for options - */ - -void -AnyOption::setCommandOption( const char *opt ) -{ - addOption( opt , COMMAND_OPT ); - g_value_counter++; -} - -void -AnyOption::setCommandOption( char opt ) -{ - addOption( opt , COMMAND_OPT ); - g_value_counter++; -} - -void -AnyOption::setCommandOption( const char *opt , char optchar ) -{ - addOption( opt , COMMAND_OPT ); - addOption( optchar , COMMAND_OPT ); - g_value_counter++; -} - -void -AnyOption::setCommandFlag( const char *opt ) -{ - addOption( opt , COMMAND_FLAG ); - g_value_counter++; -} - -void -AnyOption::setCommandFlag( char opt ) -{ - addOption( opt , COMMAND_FLAG ); - g_value_counter++; -} - -void -AnyOption::setCommandFlag( const char *opt , char optchar ) -{ - addOption( opt , COMMAND_FLAG ); - addOption( optchar , COMMAND_FLAG ); - g_value_counter++; -} - -void -AnyOption::setFileOption( const char *opt ) -{ - addOption( opt , FILE_OPT ); - g_value_counter++; -} - -void -AnyOption::setFileOption( char opt ) -{ - addOption( opt , FILE_OPT ); - g_value_counter++; -} - -void -AnyOption::setFileOption( const char *opt , char optchar ) -{ - addOption( opt , FILE_OPT ); - addOption( optchar, FILE_OPT ); - g_value_counter++; -} - -void -AnyOption::setFileFlag( const char *opt ) -{ - addOption( opt , FILE_FLAG ); - g_value_counter++; -} - -void -AnyOption::setFileFlag( char opt ) -{ - addOption( opt , FILE_FLAG ); - g_value_counter++; -} - -void -AnyOption::setFileFlag( const char *opt , char optchar ) -{ - addOption( opt , FILE_FLAG ); - addOption( optchar , FILE_FLAG ); - g_value_counter++; -} - -void -AnyOption::setOption( const char *opt ) -{ - addOption( opt , COMMON_OPT ); - g_value_counter++; -} - -void -AnyOption::setOption( char opt ) -{ - addOption( opt , COMMON_OPT ); - g_value_counter++; -} - -void -AnyOption::setOption( const char *opt , char optchar ) -{ - addOption( opt , COMMON_OPT ); - addOption( optchar , COMMON_OPT ); - g_value_counter++; -} - -void -AnyOption::setFlag( const char *opt ) -{ - addOption( opt , COMMON_FLAG ); - g_value_counter++; -} - -void -AnyOption::setFlag( const char opt ) -{ - addOption( opt , COMMON_FLAG ); - g_value_counter++; -} - -void -AnyOption::setFlag( const char *opt , char optchar ) -{ - addOption( opt , COMMON_FLAG ); - addOption( optchar , COMMON_FLAG ); - g_value_counter++; -} - -void -AnyOption::addOption( const char *opt, int type ) -{ - if( option_counter >= max_options ){ - if( doubleOptStorage() == false ){ - addOptionError( opt ); - return; - } - } - options[ option_counter ] = opt ; - optiontype[ option_counter ] = type ; - optionindex[ option_counter ] = g_value_counter; - option_counter++; -} - -void -AnyOption::addOption( char opt, int type ) -{ - if( !POSIX() ){ - printVerbose("Ignoring the option character \""); - printVerbose( opt ); - printVerbose( "\" ( POSIX options are turned off )" ); - printVerbose(); - return; - } - - - if( optchar_counter >= max_char_options ){ - if( doubleCharStorage() == false ){ - addOptionError( opt ); - return; - } - } - optionchars[ optchar_counter ] = opt ; - optchartype[ optchar_counter ] = type ; - optcharindex[ optchar_counter ] = g_value_counter; - optchar_counter++; -} - -void -AnyOption::addOptionError( const char *opt ) -{ - cout << endl ; - cout << "OPTIONS ERROR : Failed allocating extra memory " << endl ; - cout << "While adding the option : \""<< opt << "\"" << endl; - cout << "Exiting." << endl ; - cout << endl ; - exit(0); -} - -void -AnyOption::addOptionError( char opt ) -{ - cout << endl ; - cout << "OPTIONS ERROR : Failed allocating extra memory " << endl ; - cout << "While adding the option: \""<< opt << "\"" << endl; - cout << "Exiting." << endl ; - cout << endl ; - exit(0); -} - -void -AnyOption::processOptions() -{ - if( ! valueStoreOK() ) - return; -} - -void -AnyOption::processCommandArgs(int max_args) -{ - max_legal_args = max_args; - processCommandArgs(); -} - -void -AnyOption::processCommandArgs( int _argc, char **_argv, int max_args ) -{ - max_legal_args = max_args; - processCommandArgs( _argc, _argv ); -} - -void -AnyOption::processCommandArgs( int _argc, char **_argv ) -{ - useCommandArgs( _argc, _argv ); - processCommandArgs(); -} - -void -AnyOption::processCommandArgs() -{ - if( ! ( valueStoreOK() && CommandSet() ) ) - return; - - if( max_legal_args == 0 ) - max_legal_args = argc; - new_argv = (int*) malloc( (max_legal_args+1) * sizeof(int) ); - for( int i = 1 ; i < argc ; i++ ){/* ignore first argv */ - if( argv[i][0] == long_opt_prefix[0] && - argv[i][1] == long_opt_prefix[1] ) { /* long GNU option */ - int match_at = parseGNU( argv[i]+2 ); /* skip -- */ - if( match_at >= 0 && i < argc-1 ) /* found match */ - setValue( options[match_at] , argv[++i] ); - }else if( argv[i][0] == opt_prefix_char ) { /* POSIX char */ - if( POSIX() ){ - char ch = parsePOSIX( argv[i]+1 );/* skip - */ - if( ch != '0' && i < argc-1 ) /* matching char */ - setValue( ch , argv[++i] ); - } else { /* treat it as GNU option with a - */ - int match_at = parseGNU( argv[i]+1 ); /* skip - */ - if( match_at >= 0 && i < argc-1 ) /* found match */ - setValue( options[match_at] , argv[++i] ); - } - }else { /* not option but an argument keep index */ - if( new_argc < max_legal_args ){ - new_argv[ new_argc ] = i ; - new_argc++; - }else{ /* ignore extra arguments */ - printVerbose( "Ignoring extra argument: " ); - printVerbose( argv[i] ); - printVerbose( ); - printAutoUsage(); - } - printVerbose( "Unknown command argument option : " ); - printVerbose( argv[i] ); - printVerbose( ); - printAutoUsage(); - } - } -} - -char -AnyOption::parsePOSIX( char* arg ) -{ - - for( unsigned int i = 0 ; i < strlen(arg) ; i++ ){ - char ch = arg[i] ; - if( matchChar(ch) ) { /* keep matching flags till an option */ - /*if last char argv[++i] is the value */ - if( i == strlen(arg)-1 ){ - return ch; - }else{/* else the rest of arg is the value */ - i++; /* skip any '=' and ' ' */ - while( arg[i] == whitespace - || arg[i] == equalsign ) - i++; - setValue( ch , arg+i ); - return '0'; - } - } - } - printVerbose( "Unknown command argument option : " ); - printVerbose( arg ); - printVerbose( ); - printAutoUsage(); - return '0'; -} - -int -AnyOption::parseGNU( char *arg ) -{ - int split_at = 0; - /* if has a '=' sign get value */ - for( unsigned int i = 0 ; i < strlen(arg) ; i++ ){ - if(arg[i] == equalsign ){ - split_at = i ; /* store index */ - i = strlen(arg); /* get out of loop */ - } - } - if( split_at > 0 ){ /* it is an option value pair */ - char* tmp = (char*) malloc( (split_at+1)*sizeof(char) ); - for( int i = 0 ; i < split_at ; i++ ) - tmp[i] = arg[i]; - tmp[split_at] = '\0'; - - if ( matchOpt( tmp ) >= 0 ){ - setValue( options[matchOpt(tmp)] , arg+split_at+1 ); - free (tmp); - }else{ - printVerbose( "Unknown command argument option : " ); - printVerbose( arg ); - printVerbose( ); - printAutoUsage(); - free (tmp); - return -1; - } - }else{ /* regular options with no '=' sign */ - return matchOpt(arg); - } - return -1; -} - - -int -AnyOption::matchOpt( char *opt ) -{ - for( int i = 0 ; i < option_counter ; i++ ){ - if( strcmp( options[i], opt ) == 0 ){ - if( optiontype[i] == COMMON_OPT || - optiontype[i] == COMMAND_OPT ) - { /* found option return index */ - return i; - }else if( optiontype[i] == COMMON_FLAG || - optiontype[i] == COMMAND_FLAG ) - { /* found flag, set it */ - setFlagOn( opt ); - return -1; - } - } - } - printVerbose( "Unknown command argument option : " ); - printVerbose( opt ) ; - printVerbose( ); - printAutoUsage(); - return -1; -} -bool -AnyOption::matchChar( char c ) -{ - for( int i = 0 ; i < optchar_counter ; i++ ){ - if( optionchars[i] == c ) { /* found match */ - if(optchartype[i] == COMMON_OPT || - optchartype[i] == COMMAND_OPT ) - { /* an option store and stop scanning */ - return true; - }else if( optchartype[i] == COMMON_FLAG || - optchartype[i] == COMMAND_FLAG ) { /* a flag store and keep scanning */ - setFlagOn( c ); - return true; - } - } - } - //printVerbose( "Unknown command argument option : " ); - //printVerbose( c ) ; - //printVerbose( ); - printAutoUsage(); - return false; -} - -bool -AnyOption::valueStoreOK( ) -{ - int size= 0; - if( !set ){ - if( g_value_counter > 0 ){ - size = g_value_counter * sizeof(char*); - values = (char**)malloc( size ); - for( int i = 0 ; i < g_value_counter ; i++) - values[i] = NULL; - set = true; - } - } - return set; -} - -/* - * public get methods - */ -char* -AnyOption::getValue( const char *option ) -{ - if( !valueStoreOK() ) - return NULL; - - for( int i = 0 ; i < option_counter ; i++ ){ - if( strcmp( options[i], option ) == 0 ) - return values[ optionindex[i] ]; - } - return NULL; -} - -bool -AnyOption::getFlag( const char *option ) -{ - if( !valueStoreOK() ) - return false; - for( int i = 0 ; i < option_counter ; i++ ){ - if( strcmp( options[i], option ) == 0 ) - return findFlag( values[ optionindex[i] ] ); - } - return false; -} - -char* -AnyOption::getValue( char option ) -{ - if( !valueStoreOK() ) - return NULL; - for( int i = 0 ; i < optchar_counter ; i++ ){ - if( optionchars[i] == option ) - return values[ optcharindex[i] ]; - } - return NULL; -} - -bool -AnyOption::getFlag( char option ) -{ - if( !valueStoreOK() ) - return false; - for( int i = 0 ; i < optchar_counter ; i++ ){ - if( optionchars[i] == option ) - return findFlag( values[ optcharindex[i] ] ) ; - } - return false; -} - -bool -AnyOption::findFlag( char* val ) -{ - if( val == NULL ) - return false; - - if( strcmp( TRUE_FLAG , val ) == 0 ) - return true; - - return false; -} - -/* - * private set methods - */ -bool -AnyOption::setValue( const char *option , char *value ) -{ - if( !valueStoreOK() ) - return false; - for( int i = 0 ; i < option_counter ; i++ ){ - if( strcmp( options[i], option ) == 0 ){ - values[ optionindex[i] ] = (char*) malloc((strlen(value)+1)*sizeof(char)); - strcpy( values[ optionindex[i] ], value ); - return true; - } - } - return false; -} - -bool -AnyOption::setFlagOn( const char *option ) -{ - if( !valueStoreOK() ) - return false; - for( int i = 0 ; i < option_counter ; i++ ){ - if( strcmp( options[i], option ) == 0 ){ - values[ optionindex[i] ] = (char*) malloc((strlen(TRUE_FLAG)+1)*sizeof(char)); - strcpy( values[ optionindex[i] ] , TRUE_FLAG ); - return true; - } - } - return false; -} - -bool -AnyOption::setValue( char option , char *value ) -{ - if( !valueStoreOK() ) - return false; - for( int i = 0 ; i < optchar_counter ; i++ ){ - if( optionchars[i] == option ){ - values[ optcharindex[i] ] = (char*) malloc((strlen(value)+1)*sizeof(char)); - strcpy( values[ optcharindex[i] ], value ); - return true; - } - } - return false; -} - -bool -AnyOption::setFlagOn( char option ) -{ - if( !valueStoreOK() ) - return false; - for( int i = 0 ; i < optchar_counter ; i++ ){ - if( optionchars[i] == option ){ - values[ optcharindex[i] ] = (char*) malloc((strlen(TRUE_FLAG)+1)*sizeof(char)); - strcpy( values[ optcharindex[i] ] , TRUE_FLAG ); - return true; - } - } - return false; -} - - -int -AnyOption::getArgc( ) -{ - return new_argc; -} - -char* -AnyOption::getArgv( int index ) -{ - if( index < new_argc ){ - return ( argv[ new_argv[ index ] ] ); - } - return NULL; -} - -/* dotfile sub routines */ - -bool -AnyOption::processFile() -{ - if( ! (valueStoreOK() && FileSet()) ) - return false; - return ( consumeFile(readFile()) ); -} - -bool -AnyOption::processFile( const char *filename ) -{ - useFiileName(filename ); - return ( processFile() ); -} - -char* -AnyOption::readFile() -{ - return ( readFile(filename) ); -} - -/* - * read the file contents to a character buffer - */ - -char* -AnyOption::readFile( const char* fname ) -{ - int length; - char *buffer; - ifstream is; - is.open ( fname , ifstream::in ); - if( ! is.good() ){ - is.close(); - return NULL; - } - is.seekg (0, ios::end); - length = is.tellg(); - is.seekg (0, ios::beg); - buffer = (char*) malloc(length*sizeof(char)); - is.read (buffer,length); - is.close(); - return buffer; -} - -/* - * scans a char* buffer for lines that does not - * start with the specified comment character. - */ -bool -AnyOption::consumeFile( char *buffer ) -{ - - if( buffer == NULL ) - return false; - - char *cursor = buffer;/* preserve the ptr */ - char *pline = NULL ; - int linelength = 0; - bool newline = true; - for( unsigned int i = 0 ; i < strlen( buffer ) ; i++ ){ - if( *cursor == endofline ) { /* end of line */ - if( pline != NULL ) /* valid line */ - processLine( pline, linelength ); - pline = NULL; - newline = true; - }else if( newline ){ /* start of line */ - newline = false; - if( (*cursor != comment ) ){ /* not a comment */ - pline = cursor ; - linelength = 0 ; - } - } - cursor++; /* keep moving */ - linelength++; - } - free (buffer); - return true; -} - - -/* - * find a valid type value pair separated by a delimiter - * character and pass it to valuePairs() - * any line which is not valid will be considered a value - * and will get passed on to justValue() - * - * assuming delimiter is ':' the behaviour will be, - * - * width:10 - valid pair valuePairs( width, 10 ); - * width : 10 - valid pair valuepairs( width, 10 ); - * - * :::: - not valid - * width - not valid - * :10 - not valid - * width: - not valid - * :: - not valid - * : - not valid - * - */ - -void -AnyOption::processLine( char *theline, int length ) -{ - bool found = false; - char *pline = (char*) malloc( (length+1)*sizeof(char) ); - for( int i = 0 ; i < length ; i ++ ) - pline[i]= *(theline++); - pline[length] = nullterminate; - char *cursor = pline ; /* preserve the ptr */ - if( *cursor == delimiter || *(cursor+length-1) == delimiter ){ - justValue( pline );/* line with start/end delimiter */ - }else{ - for( int i = 1 ; i < length-1 && !found ; i++){/* delimiter */ - if( *cursor == delimiter ){ - *(cursor-1) = nullterminate; /* two strings */ - found = true; - valuePairs( pline , cursor+1 ); - } - cursor++; - } - cursor++; - if( !found ) /* not a pair */ - justValue( pline ); - } - free (pline); -} - -/* - * removes trailing and preceeding whitespaces from a string - */ -char* -AnyOption::chomp( char *str ) -{ - while( *str == whitespace ) - str++; - char *end = str+strlen(str)-1; - while( *end == whitespace ) - end--; - *(end+1) = nullterminate; - return str; -} - -void -AnyOption::valuePairs( char *type, char *value ) -{ - if ( strlen(chomp(type)) == 1 ){ /* this is a char option */ - for( int i = 0 ; i < optchar_counter ; i++ ){ - if( optionchars[i] == type[0] ){ /* match */ - if( optchartype[i] == COMMON_OPT || - optchartype[i] == FILE_OPT ) - { - setValue( type[0] , chomp(value) ); - return; - } - } - } - } - /* if no char options matched */ - for( int i = 0 ; i < option_counter ; i++ ){ - if( strcmp( options[i], type ) == 0 ){ /* match */ - if( optiontype[i] == COMMON_OPT || - optiontype[i] == FILE_OPT ) - { - setValue( type , chomp(value) ); - return; - } - } - } - printVerbose( "Unknown option in resourcefile : " ); - printVerbose( type ); - printVerbose( ); -} - -void -AnyOption::justValue( char *type ) -{ - - if ( strlen(chomp(type)) == 1 ){ /* this is a char option */ - for( int i = 0 ; i < optchar_counter ; i++ ){ - if( optionchars[i] == type[0] ){ /* match */ - if( optchartype[i] == COMMON_FLAG || - optchartype[i] == FILE_FLAG ) - { - setFlagOn( type[0] ); - return; - } - } - } - } - /* if no char options matched */ - for( int i = 0 ; i < option_counter ; i++ ){ - if( strcmp( options[i], type ) == 0 ){ /* match */ - if( optiontype[i] == COMMON_FLAG || - optiontype[i] == FILE_FLAG ) - { - setFlagOn( type ); - return; - } - } - } - printVerbose( "Unknown option in resourcefile : " ); - printVerbose( type ); - printVerbose( ); -} - -/* - * usage and help - */ - - -void -AnyOption::printAutoUsage() -{ - if( autousage ) printUsage(); -} - -void -AnyOption::printUsage() -{ - - if( once ) { - once = false ; - cout << endl ; - for( int i = 0 ; i < usage_lines ; i++ ) - cout << usage[i] << endl ; - cout << endl ; - } -} - - -void -AnyOption::addUsage( const char *line ) -{ - if( usage_lines >= max_usage_lines ){ - if( doubleUsageStorage() == false ){ - addUsageError( line ); - exit(1); - } - } - usage[ usage_lines ] = line ; - usage_lines++; -} - -void -AnyOption::addUsageError( const char *line ) -{ - cout << endl ; - cout << "OPTIONS ERROR : Failed allocating extra memory " << endl ; - cout << "While adding the usage/help : \""<< line << "\"" << endl; - cout << "Exiting." << endl ; - cout << endl ; - exit(0); - -} diff --git a/pacman-c++/anyoption.h b/pacman-c++/anyoption.h deleted file mode 100644 index 3f7a5de..0000000 --- a/pacman-c++/anyoption.h +++ /dev/null @@ -1,270 +0,0 @@ -#ifndef _ANYOPTION_H -#define _ANYOPTION_H - -#include -#include -#include -#include - -#define COMMON_OPT 1 -#define COMMAND_OPT 2 -#define FILE_OPT 3 -#define COMMON_FLAG 4 -#define COMMAND_FLAG 5 -#define FILE_FLAG 6 - -#define COMMAND_OPTION_TYPE 1 -#define COMMAND_FLAG_TYPE 2 -#define FILE_OPTION_TYPE 3 -#define FILE_FLAG_TYPE 4 -#define UNKNOWN_TYPE 5 - -#define DEFAULT_MAXOPTS 10 -#define MAX_LONG_PREFIX_LENGTH 2 - -#define DEFAULT_MAXUSAGE 3 -#define DEFAULT_MAXHELP 10 - -#define TRUE_FLAG "true" - -using namespace std; - -class AnyOption -{ - -public: /* the public interface */ - AnyOption(); - AnyOption(int maxoptions ); - AnyOption(int maxoptions , int maxcharoptions); - ~AnyOption(); - - /* - * following set methods specifies the - * special characters and delimiters - * if not set traditional defaults will be used - */ - - void setCommandPrefixChar( char _prefix ); /* '-' in "-w" */ - void setCommandLongPrefix( char *_prefix ); /* '--' in "--width" */ - void setFileCommentChar( char _comment ); /* '#' in shellscripts */ - void setFileDelimiterChar( char _delimiter );/* ':' in "width : 100" */ - - /* - * provide the input for the options - * like argv[] for commndline and the - * option file name to use; - */ - - void useCommandArgs( int _argc, char **_argv ); - void useFiileName( const char *_filename ); - - /* - * turn off the POSIX style options - * this means anything starting with a '-' or "--" - * will be considered a valid option - * which alo means you cannot add a bunch of - * POIX options chars together like "-lr" for "-l -r" - * - */ - - void noPOSIX(); - - /* - * prints warning verbose if you set anything wrong - */ - void setVerbose(); - - - /* - * there are two types of options - * - * Option - has an associated value ( -w 100 ) - * Flag - no value, just a boolean flag ( -nogui ) - * - * the options can be either a string ( GNU style ) - * or a character ( traditional POSIX style ) - * or both ( --width, -w ) - * - * the options can be common to the commandline and - * the optionfile, or can belong only to either of - * commandline and optionfile - * - * following set methods, handle all the aboove - * cases of options. - */ - - /* options comman to command line and option file */ - void setOption( const char *opt_string ); - void setOption( char opt_char ); - void setOption( const char *opt_string , char opt_char ); - void setFlag( const char *opt_string ); - void setFlag( char opt_char ); - void setFlag( const char *opt_string , char opt_char ); - - /* options read from commandline only */ - void setCommandOption( const char *opt_string ); - void setCommandOption( char opt_char ); - void setCommandOption( const char *opt_string , char opt_char ); - void setCommandFlag( const char *opt_string ); - void setCommandFlag( char opt_char ); - void setCommandFlag( const char *opt_string , char opt_char ); - - /* options read from an option file only */ - void setFileOption( const char *opt_string ); - void setFileOption( char opt_char ); - void setFileOption( const char *opt_string , char opt_char ); - void setFileFlag( const char *opt_string ); - void setFileFlag( char opt_char ); - void setFileFlag( const char *opt_string , char opt_char ); - - /* - * process the options, registerd using - * useCommandArgs() and useFileName(); - */ - void processOptions(); - void processCommandArgs(); - void processCommandArgs( int max_args ); - bool processFile(); - - /* - * process the specified options - */ - void processCommandArgs( int _argc, char **_argv ); - void processCommandArgs( int _argc, char **_argv, int max_args ); - bool processFile( const char *_filename ); - - /* - * get the value of the options - * will return NULL if no value is set - */ - char *getValue( const char *_option ); - bool getFlag( const char *_option ); - char *getValue( char _optchar ); - bool getFlag( char _optchar ); - - /* - * Print Usage - */ - void printUsage(); - void printAutoUsage(); - void addUsage( const char *line ); - void printHelp(); - /* print auto usage printing for unknown options or flag */ - void autoUsagePrint(bool flag); - - /* - * get the argument count and arguments sans the options - */ - int getArgc(); - char* getArgv( int index ); - bool hasOptions(); - -private: /* the hidden data structure */ - int argc; /* commandline arg count */ - char **argv; /* commndline args */ - const char* filename; /* the option file */ - char* appname; /* the application name from argv[0] */ - - int *new_argv; /* arguments sans options (index to argv) */ - int new_argc; /* argument count sans the options */ - int max_legal_args; /* ignore extra arguments */ - - - /* option strings storage + indexing */ - int max_options; /* maximum number of options */ - const char **options; /* storage */ - int *optiontype; /* type - common, command, file */ - int *optionindex; /* index into value storage */ - int option_counter; /* counter for added options */ - - /* option chars storage + indexing */ - int max_char_options; /* maximum number options */ - char *optionchars; /* storage */ - int *optchartype; /* type - common, command, file */ - int *optcharindex; /* index into value storage */ - int optchar_counter; /* counter for added options */ - - /* values */ - char **values; /* common value storage */ - int g_value_counter; /* globally updated value index LAME! */ - - /* help and usage */ - const char **usage; /* usage */ - int max_usage_lines; /* max usage lines reseverd */ - int usage_lines; /* number of usage lines */ - - bool command_set; /* if argc/argv were provided */ - bool file_set; /* if a filename was provided */ - bool mem_allocated; /* if memory allocated in init() */ - bool posix_style; /* enables to turn off POSIX style options */ - bool verbose; /* silent|verbose */ - bool print_usage; /* usage verbose */ - bool print_help; /* help verbose */ - - char opt_prefix_char; /* '-' in "-w" */ - char long_opt_prefix[MAX_LONG_PREFIX_LENGTH]; /* '--' in "--width" */ - char file_delimiter_char; /* ':' in width : 100 */ - char file_comment_char; /* '#' in "#this is a comment" */ - char equalsign; - char comment; - char delimiter; - char endofline; - char whitespace; - char nullterminate; - - bool set; //was static member - bool once; //was static member - - bool hasoptions; - bool autousage; - -private: /* the hidden utils */ - void init(); - void init(int maxopt, int maxcharopt ); - bool alloc(); - void cleanup(); - bool valueStoreOK(); - - /* grow storage arrays as required */ - bool doubleOptStorage(); - bool doubleCharStorage(); - bool doubleUsageStorage(); - - bool setValue( const char *option , char *value ); - bool setFlagOn( const char *option ); - bool setValue( char optchar , char *value); - bool setFlagOn( char optchar ); - - void addOption( const char* option , int type ); - void addOption( char optchar , int type ); - void addOptionError( const char *opt); - void addOptionError( char opt); - bool findFlag( char* value ); - void addUsageError( const char *line ); - bool CommandSet(); - bool FileSet(); - bool POSIX(); - - char parsePOSIX( char* arg ); - int parseGNU( char *arg ); - bool matchChar( char c ); - int matchOpt( char *opt ); - - /* dot file methods */ - char *readFile(); - char *readFile( const char* fname ); - bool consumeFile( char *buffer ); - void processLine( char *theline, int length ); - char *chomp( char *str ); - void valuePairs( char *type, char *value ); - void justValue( char *value ); - - void printVerbose( const char *msg ); - void printVerbose( char *msg ); - void printVerbose( char ch ); - void printVerbose( ); - - -}; - -#endif /* ! _ANYOPTION_H */ diff --git a/pacman-c++/audio.cpp b/pacman-c++/audio.cpp deleted file mode 100644 index 70cd37e..0000000 --- a/pacman-c++/audio.cpp +++ /dev/null @@ -1,355 +0,0 @@ -#include "audio.h" -#include "constants.h" -#include -#include -#include -#include -#include -#include - -/* the universe's only audio manager */ -AudioManager *AudioManager::m_instance = NULL; -bool AudioManager::m_working = false; - -AudioManager::AudioManager() - : m_muted(true) -{ - if (Constants::server) - { - qDebug() << "Server has no sound"; - m_players.append(new AudioPlayer(this)); - } - else - { - preload(); - AudioPlayer *firstplayer = new AudioPlayer(this); - firstplayer->test(m_sounds[Sound::WakaWaka]); - m_working = firstplayer->m_working; - m_players.append(firstplayer); - } - m_muted = false; -} - -AudioManager *AudioManager::self() -{ - if (m_instance == NULL) - m_instance = new AudioManager(); - return m_instance; -} - -bool AudioManager::isWorking() const -{ - return m_working; -} - -void AudioManager::setMuted(bool mute) -{ - if (!isWorking()) - return; - - if (mute == m_muted) - return; - - for(int i = 0; i < m_players.count(); ++i) - m_players.at(i)->setMuted(mute); - m_muted = mute; - emit mutedChanged(mute); -} - -bool AudioManager::isMuted() const -{ - return m_muted; -} - -void AudioManager::pause() -{ - if (!isWorking()) - return; - for(int i = 0; i < m_players.count(); ++i) - m_players.at(i)->pause(); -} - -void AudioManager::stop() -{ - if (!isWorking()) - return; - for(int i = 0; i < m_players.count(); ++i) - m_players.at(i)->stop(); -} - -void AudioManager::clear() -{ - if (!isWorking()) - return; - for(int i = 0; i < m_players.count(); ++i) - m_players.at(i)->clear(); -} - -void AudioManager::clearQueue() const -{ - if (!isWorking()) - return; - for(int i = 0; i < m_players.count(); ++i) - m_players.at(i)->clearQueue(); -} - -AudioPlayer *AudioManager::audioPlayer() -{ - return m_players.at(0); -} - -void AudioManager::play(Sound::Type sound, bool wait) -{ - audioPlayer()->play(sound, wait); -} - -void AudioManager::enqueue(Sound::Type sound) -{ - if (!isWorking()) - return; - audioPlayer()->enqueue(Phonon::MediaSource(m_sounds[sound])); -} - -void AudioManager::registerAudioPlayer(AudioPlayer *player) -{ - player->setMuted(m_muted); - connect(player, SIGNAL(destroyed(QObject *)), this, SLOT(unregisterAudioPlayer_helper(QObject *))); - m_players.append(player); -} - -void AudioManager::unregisterAudioPlayer(AudioPlayer *player) -{ - disconnect(player, SIGNAL(destroyed(QObject *)), this, SLOT(unregisterAudioPlayer_helper(QObject *))); - m_players.removeAll(player); -} - -void AudioManager::unregisterAudioPlayer_helper(QObject *player) -{ - unregisterAudioPlayer(static_cast(player)); -} - -void AudioManager::preload() -{ - m_sounds.clear(); - QDir sounds(":/sound"); - for(unsigned i = 1; i <= sounds.count(); ++i) - m_sounds.append(new QFile(QString(":/sound/sound%1").arg(i), this)); -} - -QFile *AudioManager::sound(Sound::Type sound) -{ - if (!isWorking()) - return NULL; - return m_sounds.at(sound); -} - -/* --------------------------------------------------------------- */ - -AudioPlayer::AudioPlayer(QObject *parent) - : Phonon::MediaObject(parent), m_working(false) -{ - if (!Constants::server) - { - m_working = AudioManager::m_working; - m_output = new Phonon::AudioOutput(Phonon::MusicCategory, this); - Phonon::createPath(this, m_output); - } -} - -bool AudioPlayer::isWorking() const -{ - return m_working; -} - -void AudioPlayer::setMuted(bool mute) -{ - m_output->setMuted(mute); -} - -bool AudioPlayer::isMuted() const -{ - return m_output->isMuted(); -} - -void AudioPlayer::setLoop(QFile *sound) -{ - if (!isWorking()) - return; - - if (sound == NULL) - { - disconnect(this, SIGNAL(aboutToFinish()), this, SLOT(loopEnqueue())); - return; - } - - m_loopsound = sound; - connect(this, SIGNAL(aboutToFinish()), this, SLOT(loopEnqueue())); - setCurrentSource(Phonon::MediaSource(m_loopsound)); - enqueue(Phonon::MediaSource(m_loopsound)); -} - -void AudioPlayer::setLoop(Sound::Type sound) -{ - setLoop(AudioManager::self()->sound(sound)); -} - -void AudioPlayer::play() -{ - Phonon::MediaObject::play(); -} - -void AudioPlayer::play(Sound::Type sound, bool wait) -{ - if (m_working) - { - setCurrentSource(Phonon::MediaSource(AudioManager::self()->sound(sound))); - play(); - } - else if (wait) - { - QTimer *timer = new QTimer(this); - timer->setSingleShot(true); - unsigned int interval = Sound::length[sound]; - /* add a small delay server side only */ - if (Constants::server) - interval += Constants::tick; - timer->setInterval(interval); - connect(timer, SIGNAL(timeout()), this, SLOT(finished_ex())); - timer->start(); - } -} - -/* this is a simple hack to check if phonon can actually play sounds.. */ -void AudioPlayer::test(QFile *testsound) -{ - stop(); - m_output->setVolume(0); - setCurrentSource(Phonon::MediaSource(testsound)); - connect(this, SIGNAL(stateChanged(Phonon::State,Phonon::State)), this, SLOT(stateChanged_ex(Phonon::State,Phonon::State))); - play(); - - QTimer timer; - timer.setSingleShot(true); - connect(&timer, SIGNAL(timeout()), this, SLOT(testFinished())); - timer.start(500); - while(timer.isActive()) - { - qApp->processEvents(); - Sleeper::msleep(1); - } - clear(); -} - -void AudioPlayer::finished_ex() -{ - emit finished(); -} - -void AudioPlayer::stateChanged_ex(Phonon::State newstate, Phonon::State /* oldstate */) -{ - if (newstate != Phonon::ErrorState) - { - m_working = true; - m_output->setVolume(1); - qDebug() << "Sound is working for you!"; - } - disconnect(this, SIGNAL(stateChanged(Phonon::State, Phonon::State)), this, SLOT(stateChanged_ex(Phonon::State, Phonon::State))); - stop(); -} - -void AudioPlayer::testFinished() -{ - if (!m_working) - qWarning() << "There's no sound for you :("; - disconnect(this, SIGNAL(stateChanged(Phonon::State, Phonon::State)), this, SLOT(stateChanged_ex(Phonon::State, Phonon::State))); -} - -void AudioPlayer::loopEnqueue() -{ - enqueue(Phonon::MediaSource(m_loopsound)); -} - -/* --------------------------------------------------------------- */ - -GaplessAudioPlayer::GaplessAudioPlayer(Sound::Type sound, qint32 mark, QObject *parent) - : QObject(parent), m_sound(sound) -{ - m_working = AudioManager::m_working; - if (!m_working) - return; - - m_player1 = new AudioPlayer(this); - m_player2 = new AudioPlayer(this); - - m_player2->setPrefinishMark(mark); - m_player1->setPrefinishMark(mark); - - connect(m_player1, SIGNAL(prefinishMarkReached(qint32)), this, SLOT(startPlayer2())); - connect(m_player2, SIGNAL(prefinishMarkReached(qint32)), this, SLOT(startPlayer1())); - - AudioManager::self()->registerAudioPlayer(m_player1); - AudioManager::self()->registerAudioPlayer(m_player2); -} - -bool GaplessAudioPlayer::isWorking() const -{ - return m_working; -} - -void GaplessAudioPlayer::setMuted(bool mute) -{ - if (!m_working) - return; - m_player1->setMuted(mute); - m_player2->setMuted(mute); -} - -bool GaplessAudioPlayer::isMuted() const -{ - return m_player1->isMuted() && m_player2->isMuted(); -} - -void GaplessAudioPlayer::play() -{ - if (!m_working) - return; - if (m_player1->state() != Phonon::PlayingState && m_player2->state() != Phonon::PlayingState) - startPlayer1(); -} - -void GaplessAudioPlayer::pause() -{ - if (!m_working) - return; - if (m_player1->state() != Phonon::PausedState) - m_player1->pause(); - if (m_player2->state() != Phonon::PausedState) - m_player2->pause(); -} - -void GaplessAudioPlayer::startPlayer1() -{ - m_player1->play(m_sound); -} - -void GaplessAudioPlayer::startPlayer2() -{ - m_player2->play(m_sound); -} - -/* --------------------------------------------------------------- */ - -void AudioPlayer::Sleeper::sleep(unsigned long secs) -{ - QThread::sleep(secs); -} - -void AudioPlayer::Sleeper::msleep(unsigned long msecs) -{ - QThread::msleep(msecs); -} - -void AudioPlayer::Sleeper::usleep(unsigned long usecs) -{ - QThread::usleep(usecs); -} diff --git a/pacman-c++/audio.h b/pacman-c++/audio.h deleted file mode 100644 index 6aec42d..0000000 --- a/pacman-c++/audio.h +++ /dev/null @@ -1,148 +0,0 @@ -#ifndef AUDIO_H -#define AUDIO_H - -#include -#include -#include -#include -#include - -namespace Phonon -{ - class AudioOutput; -} - -namespace Sound -{ - enum Type - { - Intro = 0, - WakaWaka, - EatingFruit, - EatingGhost, - Die, - Ambient - }; - - const unsigned int length[] = { - 4310, 2090, 570, 570, 1720, - }; -}; - -/* --------------------------------------------------------------- */ - -class AudioPlayer - : public Phonon::MediaObject -{ - Q_OBJECT - friend class AudioManager; - -private: - class Sleeper - : public QThread - { - public: - static void sleep(unsigned long secs); - static void msleep(unsigned long msecs); - static void usleep(unsigned long usecs); - }; - -public: - AudioPlayer(QObject *parent = 0); - bool isWorking() const; - void setMuted(bool mute = true); - bool isMuted() const; - void setLoop(Sound::Type sound); - void play(); - void play(Sound::Type sound, bool wait = false); - -protected: - void test(QFile *testsound); - void setLoop(QFile *sound); - -public slots: - void loopEnqueue(); - -protected slots: - void finished_ex(); - void testFinished(); - void stateChanged_ex(Phonon::State newstate, Phonon::State oldstate); - -protected: - bool m_working; - QFile *m_loopsound; - Phonon::AudioOutput *m_output; -}; - -/* --------------------------------------------------------------- */ - -class GaplessAudioPlayer - : public QObject -{ - Q_OBJECT - -public: - GaplessAudioPlayer(Sound::Type sound, qint32 mark, QObject *parent = 0); - bool isWorking() const; - void setMuted(bool mute = true); - bool isMuted() const; - void play(); - void pause(); - -protected slots: - void startPlayer1(); - void startPlayer2(); - -protected: - bool m_working; - Sound::Type m_sound; - AudioPlayer *m_player1; - AudioPlayer *m_player2; -}; - -/* --------------------------------------------------------------- */ - -class AudioManager - : public QObject -{ - Q_OBJECT - friend class AudioPlayer; - friend class GaplessAudioPlayer; - -public: - AudioManager(); - static AudioManager *self(); - bool isWorking() const; - void setMuted(bool mute = true); - bool isMuted() const; - void pause(); - void stop(); - void clear(); - void clearQueue() const; - - AudioPlayer *audioPlayer(); - void play(Sound::Type sound, bool wait = false); - void enqueue(Sound::Type sound); - - void registerAudioPlayer(AudioPlayer *player); - void unregisterAudioPlayer(AudioPlayer *player); - -signals: - void mutedChanged(bool muted); - -private slots: - void unregisterAudioPlayer_helper(QObject *player); - -private: - void preload(); - QFile *sound(Sound::Type sound); - -private: - bool m_muted; - static bool m_working; - static AudioManager *m_instance; - QList m_sounds; - QList m_players; -}; - -#endif // AUDIOPLAYER_H diff --git a/pacman-c++/block.cpp b/pacman-c++/block.cpp deleted file mode 100644 index 7f9dd14..0000000 --- a/pacman-c++/block.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "block.h" -#include "constants.h" -#include "actor.h" -#include "util.h" -#include - -QMap Block::m_pixmaps; - -Block::Block(Color::Color color, unsigned int neighbours, QGraphicsItem *parent) - : GameEntity(color, parent), m_neighbours(neighbours) -{ - m_type = Type; - - /* empty object for servers */ - if (Constants::server) - return; - - if (m_pixmaps.find(color) == m_pixmaps.end()) - { - unsigned int colid = (color == Color::none) ? 0 : Util::floorLog2(color) + 1; - QString pixmapName = ":/" + QString("block%1").arg(colid); - m_pixmaps[color] = QPixmap(pixmapName); - } - setPixmap(m_pixmaps.find(color).value()); - setNeighbours(m_neighbours); -} - -unsigned int Block::neighbours() -{ - return m_neighbours; -} - -void Block::setNeighbours(unsigned int neighbours) -{ - m_neighbours = neighbours; - setSprite(neighbours * Constants::sprite_offset, 0, Constants::field_size.width, Constants::field_size.height); -} - -bool Block::checkEnter(Actor * /* actor */) -{ - if (m_color == Color::none) - return false; - return true; -} - -GameEntity::EnteredState Block::enter(Actor *actor) -{ - if (m_color != Color::none && m_color != actor->color()) - return DestroyedActor; - return Nothing; -} - -void Block::onDie(Actor *actor) -{ - actor->die(); -} diff --git a/pacman-c++/block.h b/pacman-c++/block.h deleted file mode 100644 index abfbc5a..0000000 --- a/pacman-c++/block.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef BLOCK_H -#define BLOCK_H - -#include "gameentity.h" -#include "constants.h" -#include - -class Block - : public GameEntity -{ -public: - enum Neighbour - { - None = 0, - Left = (1 << 0), - Right = (1 << 1), - Up = (1 << 2), - Down = (1 << 3) - }; - - enum - { - Type = UserType + Transmission::block - }; - -public: - Block(Color::Color color, unsigned int neighbours = None, QGraphicsItem *parent = 0); - virtual ~Block() - {}; - - unsigned int neighbours(); - void setNeighbours(unsigned int neighbours); - virtual bool checkEnter(Actor *actor); - virtual EnteredState enter(Actor *actor); - virtual void onDie(Actor *); - -private: - // map for saving QPixmaps for reuse - static QMap m_pixmaps; - unsigned int m_neighbours; -}; - -#endif // BLOCK_H diff --git a/pacman-c++/bonuspoint.cpp b/pacman-c++/bonuspoint.cpp deleted file mode 100644 index 033b7c8..0000000 --- a/pacman-c++/bonuspoint.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "bonuspoint.h" -#include "constants.h" -#include "actor.h" - -#define BONUSPOINTS_NUM_SPRITES 4 - -namespace -{ - QPixmap *pixmap = NULL; -} - -BonusPoint::BonusPoint(QGraphicsItem *parent) - : GameEntity(parent) -{ - m_type = Type; - - /* empty object for servers */ - if (Constants::server) - return; - - if (pixmap == NULL) - pixmap = new QPixmap(":/bonuspoints"); - setPixmap(*pixmap); - - int rand = (int) (BONUSPOINTS_NUM_SPRITES * (qrand() / (RAND_MAX + 1.0))); - setSprite(rand * 20 + Constants::sprite_margin, Constants::sprite_margin, Constants::field_size.width, Constants::field_size.height); -} - -GameEntity::EnteredState BonusPoint::enter(Actor *actor) -{ - actor->addRoundPoints(Constants::Game::bonus_point_value); - return DestroyedEntity; -} - -void BonusPoint::onDie(Actor *actor) -{ - actor->eatingFruit(); -} diff --git a/pacman-c++/bonuspoint.h b/pacman-c++/bonuspoint.h deleted file mode 100644 index a72a4db..0000000 --- a/pacman-c++/bonuspoint.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef BONUSPOINT_H -#define BONUSPOINT_H - -#include "gameentity.h" - -class BonusPoint - : public GameEntity -{ -public: - enum - { - Type = UserType + Transmission::bonuspoint - }; - -public: - BonusPoint(QGraphicsItem *parent=0); - virtual ~BonusPoint() - {}; - - virtual EnteredState enter(Actor *actor); - virtual void onDie(Actor *actor); -}; - -#endif // BONUSPOINT_H diff --git a/pacman-c++/clicklabel.cpp b/pacman-c++/clicklabel.cpp deleted file mode 100644 index 87b06b8..0000000 --- a/pacman-c++/clicklabel.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "clicklabel.h" - -ClickLabel::ClickLabel(const QString &text, QWidget *parent, Qt::WindowFlags f) - : QLabel(text, parent, f) -{ -} - -void ClickLabel::mouseReleaseEvent(QMouseEvent * /* event */) -{ - emit clicked(); -} - diff --git a/pacman-c++/clicklabel.h b/pacman-c++/clicklabel.h deleted file mode 100644 index 494b1ee..0000000 --- a/pacman-c++/clicklabel.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef CLICKLABEL_H -#define CLICKLABEL_H - -#include - -class ClickLabel - : public QLabel -{ - Q_OBJECT - -public: - ClickLabel(const QString &text, QWidget *parent = 0, Qt::WindowFlags f = 0); - -signals: - void clicked(); - -protected: - void mouseReleaseEvent(QMouseEvent *event); -}; - -#endif // CLICKLABEL_H diff --git a/pacman-c++/client.cpp b/pacman-c++/client.cpp deleted file mode 100644 index 581778e..0000000 --- a/pacman-c++/client.cpp +++ /dev/null @@ -1,179 +0,0 @@ -#include "client.h" -#include "clicklabel.h" -#include "audio.h" -#include "util.h" -#include "pacman.pb.h" - -Client::Client() - : m_ambientMuted(false) -{ - m_settings = new QSettings(qApp->organizationName(), qApp->applicationName(), this); - createMenu(); - m_mainWidget = new MainWidget(this); - m_mainWidget->setAmbientMuted(m_ambientMuted); - setCentralWidget(m_mainWidget); -} - -void Client::createMenu() -{ - QMenu *fileMenu = menuBar()->addMenu("File"); - - bool sound = AudioManager::self()->isWorking(); - bool muted = !sound || m_settings->value("muted", false).toBool(); - AudioManager::self()->setMuted(muted); - - /* toggle sound: corner icon */ - ClickLabel *toggleSound = new ClickLabel("Toggle Sound", this); - toggleSound->setToolTip("Toggle Sound"); - toggleSound->setFixedWidth(20); - toggleSound->setFixedHeight(16); - toggleSound->setAlignment(Qt::AlignBottom); - toggleSound->setPixmap(soundIcon(!muted)); - if (sound) - { - connect(toggleSound, SIGNAL(clicked()), this, SLOT(toggleSound())); - connect(AudioManager::self(), SIGNAL(mutedChanged(bool)), this, SLOT(mutedChanged(bool))); - } - menuBar()->setCornerWidget(toggleSound); - - /* toggle sound: menu */ - QAction *toggleSoundAction = new QAction("&Sound", this); - toggleSoundAction->setToolTip("Toggle Sound"); - toggleSoundAction->setCheckable(true); - toggleSoundAction->setChecked(!muted); - toggleSoundAction->setDisabled(!sound); - fileMenu->addAction(toggleSoundAction); - if (sound) - { - connect(toggleSoundAction, SIGNAL(triggered()), this, SLOT(toggleSound())); - connect(this, SIGNAL(setMuteActionsChecked(bool)), toggleSoundAction, SLOT(setChecked(bool))); - } - - /* toggle ambient sound: menu */ - m_ambientMuted = muted || m_settings->value("ambientMuted", false).toBool(); - QAction *toggleAmbientAction = new QAction("&Ambient Sound", this); - toggleAmbientAction->setToolTip("Toggle Ambient Sound"); - toggleAmbientAction->setCheckable(true); - toggleAmbientAction->setChecked(!m_ambientMuted); - toggleAmbientAction->setDisabled(!sound); - fileMenu->addAction(toggleAmbientAction); - if (sound) - { - connect(toggleAmbientAction, SIGNAL(triggered(bool)), this, SLOT(enableAmbientSound(bool))); - connect(this, SIGNAL(setMuteActionsChecked(bool)), toggleAmbientAction, SLOT(setEnabled(bool))); - } - - /* exit entry */ - fileMenu->addSeparator(); - QAction *quitAction = new QAction("E&xit", this); - quitAction->setIcon(QIcon::fromTheme(QLatin1String("application-exit"))); - fileMenu->addAction(quitAction); - connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); - - - QAction *aboutAction= menuBar()->addAction("Ab&out"); - connect(aboutAction, SIGNAL(triggered()), this, SLOT(showAbout())); -} - -void Client::toggleSound() -{ - if (!AudioManager::self()->isWorking()) - return; - bool muted = !AudioManager::self()->isMuted(); - AudioManager::self()->setMuted(muted); - /* mute ambient sound again if explicitly muted */ - if (!muted && m_ambientMuted) - m_mainWidget->setAmbientMuted(true); -} - -void Client::mutedChanged(bool muted) -{ - ClickLabel *tmp = qobject_cast(menuBar()->cornerWidget()); - tmp->setPixmap(soundIcon(!muted)); - m_settings->setValue("muted", muted); - emit setMuteActionsChecked(!muted); -} - -void Client::enableAmbientSound(bool enabled) -{ - if (!AudioManager::self()->isWorking()) - return; - m_ambientMuted = !enabled; - m_mainWidget->setAmbientMuted(m_ambientMuted); - m_settings->setValue("ambientMuted", m_ambientMuted); -} - -QPixmap Client::soundIcon(bool enabled) const -{ - QImage img(enabled ? ":/soundon" : ":/soundoff"); - img.setColor(1, menuBar()->palette().color( - enabled ? QPalette::Active : QPalette::Disabled, - QPalette::ButtonText).rgba()); - return QPixmap::fromImage(img); -} - -void Client::showAbout() -{ - QDialog *about = new QDialog(this); - about->setWindowTitle("About Pacman"); - about->setWindowFlags(about->windowFlags() & ~Qt::WindowContextHelpButtonHint); - - QGridLayout *layout = new QGridLayout(about); - layout->setSizeConstraint(QLayout::SetFixedSize); - - QString actoricons; - for(int i = 0; Color::order[i] != Color::none; ++i) - actoricons += QString("").arg(i + 1); - - const QString text = QString( - "

Multiplayer Pacman %1

" - "Authors: H. Demel, B. Mallinger, M. Mausz, M. Racz
" - "
" - "Gameplay based on Pacman" - ", © Namco 1980
" - "
" - "Developed using Qt %2 (%3 bit)
") - .arg(actoricons, QLatin1String(QT_VERSION_STR), QString::number(QSysInfo::WordSize)); - - QLabel *label = new QLabel(text); - label->setWordWrap(true); - label->setOpenExternalLinks(true); - label->setTextInteractionFlags(Qt::TextBrowserInteraction); - - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); - QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close); - buttonBox->addButton(closeButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::RejectRole | QDialogButtonBox::AcceptRole)); - connect(buttonBox , SIGNAL(rejected()), about, SLOT(reject())); - - layout->addWidget(label, 0, 1, 4, 4); - layout->addWidget(buttonBox, 4, 0, 1, 5); - about->show(); -} - -bool Constants::server = false; - -int main(int argc, char **argv) -{ - /* Verify that the version of the library that we linked against is - * compatible with the version of the headers we compiled against. - */ - GOOGLE_PROTOBUF_VERIFY_VERSION; - - QApplication app(argc, argv, true); - app.setOrganizationName("TU Wien FOOP"); - app.setApplicationName("Pacman Client"); - app.setWindowIcon(QIcon(":/appicon")); - - qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime())); - - Client client; - client.show(); - client.setWindowTitle(app.applicationName()); - - int ret = app.exec(); - - /* Delete all global objects allocated by libprotobuf */ - google::protobuf::ShutdownProtobufLibrary(); - - return ret; -} diff --git a/pacman-c++/client.h b/pacman-c++/client.h deleted file mode 100644 index 7030683..0000000 --- a/pacman-c++/client.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef CLIENT_H -#define CLIENT_H - -#include "mainwidget.h" -#include - -class Client - : public QMainWindow -{ - Q_OBJECT -public: - Client(); - QSettings *settings(); - -signals: - /* signal gets emitted if mute buttons should update their checked-state */ - void setMuteActionsChecked(bool enabled); - -private slots: - /* toggles sound */ - void toggleSound(); - /* mute was changed (emitted by audioplayer/phonon) */ - void mutedChanged(bool); - /* enable ambient (emitted by action) */ - void enableAmbientSound(bool); - void showAbout(); - -private: - void createMenu(); - QPixmap soundIcon(bool enabled = true) const; - -private: - MainWidget *m_mainWidget; - QSettings *m_settings; - bool m_ambientMuted; -}; - -#endif // CLIENT_H diff --git a/pacman-c++/client/clicklabel.cpp b/pacman-c++/client/clicklabel.cpp new file mode 100644 index 0000000..87b06b8 --- /dev/null +++ b/pacman-c++/client/clicklabel.cpp @@ -0,0 +1,12 @@ +#include "clicklabel.h" + +ClickLabel::ClickLabel(const QString &text, QWidget *parent, Qt::WindowFlags f) + : QLabel(text, parent, f) +{ +} + +void ClickLabel::mouseReleaseEvent(QMouseEvent * /* event */) +{ + emit clicked(); +} + diff --git a/pacman-c++/client/clicklabel.h b/pacman-c++/client/clicklabel.h new file mode 100644 index 0000000..494b1ee --- /dev/null +++ b/pacman-c++/client/clicklabel.h @@ -0,0 +1,21 @@ +#ifndef CLICKLABEL_H +#define CLICKLABEL_H + +#include + +class ClickLabel + : public QLabel +{ + Q_OBJECT + +public: + ClickLabel(const QString &text, QWidget *parent = 0, Qt::WindowFlags f = 0); + +signals: + void clicked(); + +protected: + void mouseReleaseEvent(QMouseEvent *event); +}; + +#endif // CLICKLABEL_H diff --git a/pacman-c++/client/client.cpp b/pacman-c++/client/client.cpp new file mode 100644 index 0000000..d064d9e --- /dev/null +++ b/pacman-c++/client/client.cpp @@ -0,0 +1,265 @@ +#include "client.h" +#include "clicklabel.h" +#include "audio.h" +#include "util.h" +#include "pacman.pb.h" + +extern "C" { +#include "enet/enet.h" +} + +Client::Client() + : m_ambientMuted(false) +{ + m_settings = new QSettings(qApp->organizationName(), qApp->applicationName(), this); + m_dialog = new QDialog(this); + m_mainWidget = new MainWidget(this); + createMenu(); + m_mainWidget->setAmbientMuted(m_ambientMuted); + setCentralWidget(m_mainWidget); + showConnectDialog(); + m_dialog->setFocus(); +} + +void Client::createMenu() +{ + QMenu *fileMenu = menuBar()->addMenu("&File"); + + bool sound = AudioManager::self()->isWorking(); + bool muted = !sound || m_settings->value("muted", false).toBool(); + AudioManager::self()->setMuted(muted); + + /* toggle sound: corner icon */ + ClickLabel *toggleSound = new ClickLabel("Toggle Sound", this); + toggleSound->setToolTip("Toggle Sound"); + toggleSound->setFixedWidth(20); + toggleSound->setFixedHeight(16); + toggleSound->setAlignment(Qt::AlignBottom); + toggleSound->setPixmap(soundIcon(!muted)); + if (sound) + { + connect(toggleSound, SIGNAL(clicked()), this, SLOT(toggleSound())); + connect(AudioManager::self(), SIGNAL(mutedChanged(bool)), this, SLOT(mutedChanged(bool))); + } + menuBar()->setCornerWidget(toggleSound); + + /* toggle sound: menu */ + QAction *toggleSoundAction = new QAction("&Sound", this); + toggleSoundAction->setToolTip("Toggle Sound"); + toggleSoundAction->setCheckable(true); + toggleSoundAction->setChecked(!muted); + toggleSoundAction->setDisabled(!sound); + fileMenu->addAction(toggleSoundAction); + if (sound) + { + connect(toggleSoundAction, SIGNAL(triggered()), this, SLOT(toggleSound())); + connect(this, SIGNAL(setMuteActionsChecked(bool)), toggleSoundAction, SLOT(setChecked(bool))); + } + + /* toggle ambient sound: menu */ + m_ambientMuted = muted || m_settings->value("ambientMuted", false).toBool(); + QAction *toggleAmbientAction = new QAction("&Ambient Sound", this); + toggleAmbientAction->setToolTip("Toggle Ambient Sound"); + toggleAmbientAction->setCheckable(true); + toggleAmbientAction->setChecked(!m_ambientMuted); + toggleAmbientAction->setDisabled(muted); + fileMenu->addAction(toggleAmbientAction); + if (sound) + { + connect(toggleAmbientAction, SIGNAL(triggered(bool)), this, SLOT(enableAmbientSound(bool))); + connect(this, SIGNAL(setMuteActionsChecked(bool)), toggleAmbientAction, SLOT(setEnabled(bool))); + } + + /* connect/disconnect entry */ + fileMenu->addSeparator(); + QAction *connectAction = new QAction("C&onnect", this); + fileMenu->addAction(connectAction); + connect(connectAction, SIGNAL(triggered()), this, SLOT(showConnectDialog())); + connect(m_mainWidget, SIGNAL(connected(bool)), connectAction, SLOT(setDisabled(bool))); + + QAction *disconnectAction = new QAction("Di&sconnect", this); + disconnectAction->setDisabled(true); + fileMenu->addAction(disconnectAction); + connect(disconnectAction, SIGNAL(triggered()), m_mainWidget, SLOT(doDisconnect())); + connect(m_mainWidget, SIGNAL(connected(bool)), disconnectAction, SLOT(setEnabled(bool))); + + /* exit entry */ + fileMenu->addSeparator(); + QAction *quitAction = new QAction("E&xit", this); + quitAction->setIcon(QIcon::fromTheme(QLatin1String("application-exit"))); + fileMenu->addAction(quitAction); + connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + + QAction *aboutAction= menuBar()->addAction("Ab&out"); + connect(aboutAction, SIGNAL(triggered()), this, SLOT(showAbout())); +} + +void Client::toggleSound() +{ + if (!AudioManager::self()->isWorking()) + return; + bool muted = !AudioManager::self()->isMuted(); + AudioManager::self()->setMuted(muted); + /* mute ambient sound again if explicitly muted */ + if (!muted && m_ambientMuted) + m_mainWidget->setAmbientMuted(true); +} + +void Client::mutedChanged(bool muted) +{ + ClickLabel *tmp = qobject_cast(menuBar()->cornerWidget()); + tmp->setPixmap(soundIcon(!muted)); + m_settings->setValue("muted", muted); + emit setMuteActionsChecked(!muted); +} + +void Client::enableAmbientSound(bool enabled) +{ + if (!AudioManager::self()->isWorking()) + return; + m_ambientMuted = !enabled; + m_mainWidget->setAmbientMuted(m_ambientMuted); + m_settings->setValue("ambientMuted", m_ambientMuted); +} + +QPixmap Client::soundIcon(bool enabled) const +{ + QImage img(enabled ? ":/soundon" : ":/soundoff"); + img.setColor(1, menuBar()->palette().color( + enabled ? QPalette::Active : QPalette::Disabled, + QPalette::ButtonText).rgba()); + return QPixmap::fromImage(img); +} + +void Client::showAbout() +{ + if (m_dialog != NULL) + { + delete m_dialog; + m_dialog = new QDialog(this); + } + m_dialog->setWindowTitle("About Pacman"); + m_dialog->setWindowFlags(m_dialog->windowFlags() & ~Qt::WindowContextHelpButtonHint); + + QGridLayout *layout = new QGridLayout(m_dialog); + layout->setSizeConstraint(QLayout::SetFixedSize); + + QString actoricons; + for(int i = 0; Color::order[i] != Color::none; ++i) + actoricons += QString("").arg(i + 1); + + const QString text = QString( + "

Multiplayer Pacman %1

" + "Authors: H. Demel, B. Mallinger, M. Mausz, M. Racz
" + "
" + "Gameplay based on Pacman" + ", © Namco 1980
" + "
" + "Developed using Qt %2 (%3 bit)
") + .arg(actoricons, QLatin1String(QT_VERSION_STR), QString::number(QSysInfo::WordSize)); + + QLabel *label = new QLabel(text, m_dialog); + label->setWordWrap(true); + label->setOpenExternalLinks(true); + label->setTextInteractionFlags(Qt::TextBrowserInteraction); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, m_dialog); + QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close); + buttonBox->addButton(closeButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::RejectRole | QDialogButtonBox::AcceptRole)); + connect(buttonBox , SIGNAL(rejected()), m_dialog, SLOT(reject())); + + layout->addWidget(label, 0, 1, 4, 4); + layout->addWidget(buttonBox, 4, 0, 1, 5); + m_dialog->show(); +} + +void Client::showConnectDialog() +{ + if (m_dialog != NULL) + { + delete m_dialog; + m_dialog = new QDialog(this); + } + m_dialog->setWindowTitle("Connect"); + + QGridLayout *layout = new QGridLayout(m_dialog); + layout->setSizeConstraint(QLayout::SetFixedSize); + + QLabel *srvLabel = new QLabel("Address:", m_dialog); + QLineEdit *srv = new QLineEdit(m_settings->value("address", "127.0.0.1").toString(), m_dialog); + QLabel *portLabel = new QLabel("Port:", m_dialog); + QDoubleSpinBox *port = new QDoubleSpinBox(m_dialog); + port->setDecimals(0); + port->setMinimum(1); + port->setMaximum(65535); + port->setValue(m_settings->value("port", Constants::Networking::port).toUInt()); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, + Qt::Horizontal, m_dialog); + QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); + QPushButton *cancelButton = buttonBox->button(QDialogButtonBox::Cancel); + buttonBox->addButton(okButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::AcceptRole)); + buttonBox->addButton(cancelButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::RejectRole)); + connect(buttonBox, SIGNAL(rejected()), m_dialog, SLOT(reject())); + connect(buttonBox, SIGNAL(accepted()), m_dialog, SLOT(accept())); + + layout->addWidget(srvLabel, 0, 0); + layout->addWidget(srv, 0, 1); + layout->addWidget(portLabel, 1, 0); + layout->addWidget(port, 1, 1); + layout->addWidget(buttonBox, 4, 0, 1, 5); + + connect(m_dialog, SIGNAL(accepted()), this, SLOT(onAcceptConnectDialog())); + m_dialog->show(); +} + +void Client::onAcceptConnectDialog() +{ + if (m_dialog == NULL) + return; + QGridLayout *layout = static_cast(m_dialog->layout()); + QLineEdit *srv = static_cast(layout->itemAtPosition(0, 1)->widget()); + QDoubleSpinBox *port = static_cast(layout->itemAtPosition(1, 1)->widget()); + m_settings->setValue("address", srv->text()); + m_settings->setValue("port", int(port->value())); + m_mainWidget->doConnect(srv->text(), int(port->value())); +} + +bool Constants::server = false; + +int main(int argc, char **argv) +{ + /* Verify that the version of the library that we linked against is + * compatible with the version of the headers we compiled against. + */ + GOOGLE_PROTOBUF_VERIFY_VERSION; + + if (enet_initialize () != 0) + { + qCritical() << "An error occurred while initializing ENet"; + return EXIT_FAILURE; + } + + Q_INIT_RESOURCE(pacman); + QApplication app(argc, argv, true); + app.setOrganizationName("TU Wien FOOP"); + app.setApplicationName("Pacman Client"); + app.setWindowIcon(QIcon(":/appicon")); + + qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime())); + + AudioManager::self(); + + Client client; + client.show(); + client.setWindowTitle(app.applicationName()); + + int ret = app.exec(); + + enet_deinitialize(); + + /* Delete all global objects allocated by libprotobuf */ + google::protobuf::ShutdownProtobufLibrary(); + + return ret; +} diff --git a/pacman-c++/client/client.h b/pacman-c++/client/client.h new file mode 100644 index 0000000..9b231ca --- /dev/null +++ b/pacman-c++/client/client.h @@ -0,0 +1,41 @@ +#ifndef CLIENT_H +#define CLIENT_H + +#include "mainwidget.h" +#include + +class Client + : public QMainWindow +{ + Q_OBJECT +public: + Client(); + QSettings *settings(); + +signals: + /* signal gets emitted if mute buttons should update their checked-state */ + void setMuteActionsChecked(bool enabled); + +private slots: + /* toggles sound */ + void toggleSound(); + /* mute was changed (emitted by audioplayer/phonon) */ + void mutedChanged(bool); + /* enable ambient (emitted by action) */ + void enableAmbientSound(bool); + void showAbout(); + void showConnectDialog(); + void onAcceptConnectDialog(); + +private: + void createMenu(); + QPixmap soundIcon(bool enabled = true) const; + +private: + MainWidget *m_mainWidget; + QDialog *m_dialog; + QSettings *m_settings; + bool m_ambientMuted; +}; + +#endif // CLIENT_H diff --git a/pacman-c++/client/client.pro b/pacman-c++/client/client.pro new file mode 100644 index 0000000..849cca2 --- /dev/null +++ b/pacman-c++/client/client.pro @@ -0,0 +1,12 @@ +TEMPLATE = app +TARGET = pacman + +SOURCES += clicklabel.cpp \ + client.cpp \ + mainwidget.cpp +HEADERS += clicklabel.h \ + client.h \ + mainwidget.h + +include(../common.pri) +PRE_TARGETDEPS += ../common/libcommon.a diff --git a/pacman-c++/client/mainwidget.cpp b/pacman-c++/client/mainwidget.cpp new file mode 100644 index 0000000..f6f088b --- /dev/null +++ b/pacman-c++/client/mainwidget.cpp @@ -0,0 +1,390 @@ +#include "mainwidget.h" +#include "actor.h" +#include "block.h" +#include "constants.h" +#include "util.h" +#include "pacman.pb.h" +#include + +MainWidget::MainWidget(QWidget *parent) + : QWidget(parent), m_currentKey(Transmission::none), m_running(false), + m_host(NULL), m_peer(NULL), m_scene(NULL), m_maxplayers(0) +{ + m_host = enet_host_create(NULL, 1, 1, 0, 0); + if (m_host == NULL) + { + QMessageBox::critical(this, "Error", "An error occurred while trying to create an ENet client host"); + qCritical() << "An error occurred while trying to create an ENet client host"; + return; + } + + /* create audio player */ + m_ambientPlayer = new GaplessAudioPlayer(Sound::Ambient, 100, this); + + m_recvTimer = new QTimer(this); + m_recvTimer->setInterval(Constants::tick / 2); + connect(m_recvTimer, SIGNAL(timeout()), this, SLOT(tick())); +} + +MainWidget::~MainWidget() +{ + doDisconnect(); + if (m_host != NULL) + { + enet_host_destroy(m_host); + m_host = NULL; + } +} + +void MainWidget::doConnect(QString srv, unsigned int port) +{ + Color::Color color = connectToServer(srv, port); + if (color == Color::none) + { + QMessageBox::critical(this, "Error", "Failed to connect to server"); + return; + } + + /* create our scene */ + m_scene = new SceneHolder(this); + m_scene->setColor(color); + m_scene->showWaitingForPlayers(true); + createGui(); + + m_recvTimer->start(); + emit connected(true); + qDebug() << "[Connect] mycolor=" << m_scene->color(); +} + +void MainWidget::deleteLayout(QLayout *layout) +{ + if (layout == NULL) + return; + + foreach(QObject *obj, layout->children()) + { + QLayout *inLayout = qobject_cast(obj); + deleteLayout(inLayout); + } + + QLayoutItem *child; + while ((child = layout->takeAt(0)) != NULL) + { + child->widget()->deleteLater(); + delete child; + } + + delete layout; +} + +void MainWidget::doDisconnect() +{ + stopGame(); + m_recvTimer->stop(); + closeENetPeer(); + deleteLayout(layout()); + m_playerScoreLayouts.clear(); + emit connected(false); +} + +bool MainWidget::connected() +{ + return m_peer != NULL; +} + +void MainWidget::createGui() +{ + setFocusPolicy(Qt::StrongFocus); + + /* first one is always the own score */ + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setAlignment(Qt::AlignHCenter | Qt::AlignTop); + QHBoxLayout *scoreLayout = new QHBoxLayout(); + for (unsigned int i = 0; i < m_maxplayers; ++i) + { + QGridLayout *playerLayout = new QGridLayout(); + playerLayout->addWidget(new QLabel("Current:", this), 0, 0); + playerLayout->addWidget(new QLabel("Total:", this), 1, 0); + playerLayout->addWidget(new QLabel("0", this), 0, 1, Qt::AlignLeft); + playerLayout->addWidget(new QLabel("0", this), 1, 1, Qt::AlignLeft); + playerLayout->setColumnStretch(1, 10); + playerLayout->setSizeConstraint(QLayout::SetMinimumSize); + + QGroupBox *scoreBox = new QGroupBox(QString("Player %1").arg(i + 1), this); + scoreBox->setObjectName(QString("actor%1").arg(i + 1)); + scoreBox->setCheckable(true); + scoreBox->setDisabled(i >= m_maxplayers); + connect(scoreBox, SIGNAL(clicked()), this, SLOT(playerScoreClicked())); + + scoreBox->setLayout(playerLayout); + m_playerScoreLayouts.append(playerLayout); + + if (Color::order[i] == m_scene->color()) + scoreLayout->insertWidget(0, scoreBox); + else + scoreLayout->addWidget(scoreBox); + } + layout->addLayout(scoreLayout); + + QGraphicsView *window = new QGraphicsView(m_scene, this); + window->setFrameStyle(0); + window->setFixedSize(m_scene->sceneRect().size().toSize()); + window->setWindowFlags(window->windowFlags() & ~Qt::WindowMaximizeButtonHint); + window->setFocusPolicy(Qt::NoFocus); + layout->addWidget(window, 0, Qt::AlignCenter); + + QFile css(":/stylesheet"); + css.open(QFile::ReadOnly); + qApp->setStyleSheet(QLatin1String(css.readAll())); + + /* add dummy layout at the end which gets streched when resizing */ + QHBoxLayout *spacer = new QHBoxLayout(); + layout->addLayout(spacer, 10); + + setLayout(layout); +} + +void MainWidget::updateScore(const ProtoBuf::MapUpdate& packet) +{ + for(unsigned i = 0; i < m_maxplayers; ++i) + { + QGridLayout *score = m_playerScoreLayouts.at(i); + QLabel *turnPointsLbl = dynamic_cast(score->itemAtPosition(0, 1)->widget()); + turnPointsLbl->setText(QString::number(packet.round_points(i))); + + QLabel *allPointsLbl = dynamic_cast(score->itemAtPosition(1, 1)->widget()); + allPointsLbl->setText(QString::number(packet.game_points(i))); + } +} + +Transmission::field_t MainWidget::translateKey(int key) +{ + switch(key) + { + case Qt::Key_W: + case Qt::Key_Up: + return Transmission::direction_up; + break; + case Qt::Key_S: + case Qt::Key_Down: + return Transmission::direction_down; + break; + case Qt::Key_A: + case Qt::Key_Left: + return Transmission::direction_left; + break; + case Qt::Key_D: + case Qt::Key_Right: + return Transmission::direction_right; + break; + default: + return Transmission::direction_none; + } +} + +void MainWidget::tick() +{ + ENetEvent event; + while (enet_host_service(m_host, &event, 1) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_DISCONNECT: + m_peer = NULL; + doDisconnect(); + break; + case ENET_EVENT_TYPE_RECEIVE: + tick(&event); + enet_packet_destroy(event.packet); + break; + default: + break; + } + } +} + +void MainWidget::tick(ENetEvent *event) +{ + QSharedPointer data = Util::receivePacket(event->packet); + bool worked = m_updatepacket.ParseFromArray(data->data(), data->size()); + Q_ASSERT(worked); + Q_UNUSED(worked); + + /* eating order data set indicates a new round */ + if (m_updatepacket.eating_order_size() > 0) + { + Q_ASSERT(m_scene != NULL); + m_scene->reset(); + + /* fetch eating order */ + QList order; + for(int i = 0; i < m_updatepacket.eating_order_size(); ++i) + order.append(static_cast(m_updatepacket.eating_order(i) & Transmission::color_mask)); + m_scene->setEatingOrder(order); + + /* stop game */ + stopGame(); + + /* and restart game */ + QTimer *timer = new QTimer(this); + timer->setSingleShot(true); + timer->setInterval(Sound::length[Sound::Intro] + Constants::tick); + connect(timer, SIGNAL(timeout()), this, SLOT(startGame())); + timer->start(); + AudioManager::self()->play(Sound::Intro, true); + } + + Transmission::map_t map = Util::createUninitialisedMap(); + Q_ASSERT(m_updatepacket.field_size() == (int) (Constants::map_size.width * Constants::map_size.height)); + int i = 0; + for (unsigned int x = 0; x < Constants::map_size.width; ++x) + { + for (unsigned int y = 0; y < Constants::map_size.height; ++y) + { + map[x][y] = m_updatepacket.field(i); + ++i; + } + } + m_scene->updateMap(map); + Util::deleteMap(map); + updateScore(m_updatepacket); + + if (m_updatepacket.eating_order_size() > 0) + { + m_scene->showWaitingForPlayers(false); + m_scene->showEatingText(); + } +} + +void MainWidget::keyPressEvent(QKeyEvent* event) +{ + if (event->isAutoRepeat()) + return; + + QWidget::keyPressEvent(event); + Transmission::field_t newKey = translateKey(event->key()); + if (newKey == Transmission::direction_none) + return; + bool sendUpdate = (m_currentKey != newKey); + m_currentKey = newKey; + if (sendUpdate) + sendKeyUpdate(); +} + +void MainWidget::sendKeyUpdate() +{ + if (m_currentKey == Transmission::direction_none) + return; + qDebug() << "[SendKey] key=" << m_currentKey; + ProtoBuf::KeyPressUpdate packet; + packet.set_newkey(m_currentKey); + Util::sendPacket(packet, m_peer, m_host); +} + +void MainWidget::keyReleaseEvent(QKeyEvent* event) +{ + if (event->isAutoRepeat()) + return; + + QWidget::keyReleaseEvent(event); + m_currentKey = Transmission::none; + return; +} + +void MainWidget::startGame() +{ + disconnect(AudioManager::self()->audioPlayer(), NULL, this, SLOT(startGame())); + m_scene->showEatingText(false); + m_running = true; + sendKeyUpdate(); + m_ambientPlayer->play(); +} + +void MainWidget::stopGame() +{ + m_running = false; + m_ambientPlayer->pause(); +} + +void MainWidget::setAmbientMuted(bool muted) +{ + m_ambientPlayer->setMuted(muted); +} + +void MainWidget::playerScoreClicked() +{ + QGroupBox *tmp = qobject_cast(sender()); + tmp->setChecked(true); + return; +} + +Color::Color MainWidget::connectToServer(QString srv, unsigned int port) +{ + qDebug() << "[Connect] server=" << srv << "port=" << port; + + /* connect to server */ + closeENetPeer(); + ENetAddress address; + enet_address_set_host(&address, qPrintable(srv)); + address.port = Constants::Networking::port; + m_peer = enet_host_connect(m_host, &address, 1, 0); + if (m_peer == NULL) + { + qCritical() << "No available peers for initiating an ENet connection"; + return Color::none; + } + + ENetEvent event; + bool worked = (enet_host_service(m_host, &event, Constants::Networking::connection_timeout) > 0 + && event.type == ENET_EVENT_TYPE_CONNECT); + if (worked) + { + /* additional init: first packet is our color */ + worked = (enet_host_service(m_host, &event, Constants::Networking::packet_timeout) > 0 + && event.type == ENET_EVENT_TYPE_RECEIVE); + if (worked) + { + /* receive color */ + QSharedPointer data = Util::receivePacket(event.packet); + enet_packet_destroy(event.packet); + ProtoBuf::Init packet; + worked = packet.ParseFromArray(data->data(), data->size()); + Q_ASSERT(worked); + Q_UNUSED(worked); + m_maxplayers = packet.maxplayers(); + return static_cast(packet.color() & Transmission::color_mask); + } + } + enet_peer_reset(m_peer); + m_peer = NULL; + return Color::none; +} + +void MainWidget::closeENetPeer() +{ + if (m_peer != NULL && m_peer->state != ENET_PEER_STATE_DISCONNECTED) + { + /* allow up to 3 seconds for the disconnect to succeed + * and drop any packets received packets + */ + enet_peer_disconnect(m_peer, 0); + ENetEvent event; + while (enet_host_service(m_host, &event, 3000) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_RECEIVE: + enet_packet_destroy(event.packet); + break; + case ENET_EVENT_TYPE_DISCONNECT: + m_peer = NULL; + return; + default: + break; + } + } + + enet_peer_reset(m_peer); + } + m_peer = NULL; +} diff --git a/pacman-c++/client/mainwidget.h b/pacman-c++/client/mainwidget.h new file mode 100644 index 0000000..99ff7d7 --- /dev/null +++ b/pacman-c++/client/mainwidget.h @@ -0,0 +1,80 @@ +#ifndef MAINWIDGET_H +#define MAINWIDGET_H + +#include "sceneholder.h" +#include "constants.h" +#include "audio.h" +#include "pacman.pb.h" +#include +#include + +extern "C" { +#include "enet/enet.h" +} + +class Actor; + +class MainWidget + : public QWidget +{ + Q_OBJECT + +public: + MainWidget(QWidget *parent = 0); + ~MainWidget(); + bool connected(); + void setAmbientMuted(bool muted); + +public slots: + void doConnect(QString srv = "127.0.0.1", unsigned int port = Constants::Networking::port); + void doDisconnect(); + +protected: + /* handling of current key */ + virtual void keyPressEvent(QKeyEvent *); + virtual void keyReleaseEvent(QKeyEvent *); + +signals: + void connected(bool connected); + +private slots: + void startGame(); + void stopGame(); + void playerScoreClicked(); + void tick(); + void tick(ENetEvent *event); + void sendKeyUpdate(); + +private: + void createGui(); + void createMenu(); + void updateScore(const ProtoBuf::MapUpdate&); + bool isRunning(); + Color::Color connectToServer(QString srv = "127.0.0.1", unsigned int port = Constants::Networking::port); + void closeENetPeer(); + void deleteLayout(QLayout *layout); + + /* GUI elements needed in the progress of the game */ + QList m_playerScoreLayouts; + + /* key currently pressed by user */ + Transmission::field_t m_currentKey; + + /* translate Qt::Key to our key format */ + Transmission::field_t translateKey(int key); + + /* game running */ + bool m_running; + GaplessAudioPlayer *m_ambientPlayer; + + ENetHost *m_host; + ENetPeer *m_peer; + SceneHolder *m_scene; + unsigned int m_maxplayers; + QTimer *m_recvTimer; + + /* allocate as member variable as this packet is large and used often */ + ProtoBuf::MapUpdate m_updatepacket; +}; + +#endif // MAINWIDGET_H diff --git a/pacman-c++/common.pri b/pacman-c++/common.pri new file mode 100644 index 0000000..fed2973 --- /dev/null +++ b/pacman-c++/common.pri @@ -0,0 +1,21 @@ +LANGUAGE = C++ +VERSION = 0.1 + +OBJECTS_DIR = .obj +MOC_DIR = .moc + +QT += phonon network + +INCLUDEPATH += ../common +DEPENDPATH += ../common +LIBS += -L../common -lcommon + +INCLUDEPATH += $$OUT_PWD/../proto +LIBS += -L../proto -lproto -lprotobuf + +INCLUDEPATH += ../enet/include +LIBS += -L$$PWD/enet/.libs -lenet + +CONFIG(release, debug|release) { + DEFINES += QT_NO_DEBUG_OUTPUT +} diff --git a/pacman-c++/common/actor.cpp b/pacman-c++/common/actor.cpp new file mode 100644 index 0000000..de8d77e --- /dev/null +++ b/pacman-c++/common/actor.cpp @@ -0,0 +1,375 @@ +#include "actor.h" +#include "util.h" +#include +#include +#include + +static QVariant myBooleanInterpolator(const bool &start, const bool &end, qreal progress) +{ + return (progress == 1.0) ? end : start; +} + +Actor::Actor(Color::Color color, bool local, QGraphicsItem *parent) + : GameEntity(color, parent), m_direction(Actor::None), m_local(local), m_reset(true), + m_wakaPlayer(NULL), m_roundPoints(0), m_gamePoints(0) +{ + m_type = Type; + + /* DON'T set any pixmap here. we've a pixmap in the animation + * but we need a sprite for the collision detection + */ + setSprite(Constants::sprite_margin, Constants::sprite_margin, Constants::field_size.width, Constants::field_size.height); + /* higher player "over" lower player */ + setZValue(m_color * 10); + + /* rest of the ctor is only for clients */ + if (Constants::server) + return; + + /* our actor pixmap. created after server part */ + m_pix = ":/" + QString("actor%1").arg(Util::floorLog2(color) + 1); + + /* setup icon for player */ + m_icon.setPixmap(m_pix); + m_icon.setSprite(Constants::sprite_margin, Constants::sprite_margin, Constants::field_size.width, Constants::field_size.height); + + /* setup static images first + * they are visible if no animation is running + * and will be the first in m_images + */ + for (int i = 0; i < 5; i++) + { + PixmapItem *img = new PixmapItem(m_pix, this); + m_images.append(img); + int x = i * Constants::sprite_offset + Constants::sprite_margin; + int y = Actor::None * Constants::sprite_offset + Constants::sprite_margin; + img->setSprite(x, y, Constants::field_size.width, Constants::field_size.height); + img->setVisible(false); + } + + /* setup animation stuff */ + qRegisterAnimationInterpolator(myBooleanInterpolator); + m_moving = new QParallelAnimationGroup(this); + m_eating.append(NULL); // Actor::None + m_eating.append(setupEatingAnimation(Actor::Left)); + m_eating.append(setupEatingAnimation(Actor::Right)); + m_eating.append(setupEatingAnimation(Actor::Up)); + m_eating.append(setupEatingAnimation(Actor::Down)); + + /* dieing animation */ + m_dieing = new QSequentialAnimationGroup(this); + for (int i = 0; i < 11; i++) + { + PixmapItem *img = new PixmapItem(m_pix, this); + m_images.append(img); + int x = i * Constants::sprite_offset + Constants::sprite_margin; + int y = 5 * Constants::sprite_offset + Constants::sprite_margin; + img->setSprite(x, y, Constants::field_size.width, Constants::field_size.height); + img->setVisible(false); + + QPropertyAnimation *fadein = new QPropertyAnimation(img, "visible", m_dieing); + fadein->setDuration(0); + fadein->setEndValue(true); + + m_dieing->addPause(130); + + QPropertyAnimation *fadeout = new QPropertyAnimation(img, "visible", m_dieing); + fadeout->setDuration(0); + fadeout->setEndValue(false); + } + + /* setup waka sound */ + if (local) + m_wakaPlayer = new GaplessAudioPlayer(Sound::WakaWaka, 100, this); + + /* make the picture showing the current direction visible */ + m_images[m_direction]->setVisible(true); +} + +QSequentialAnimationGroup *Actor::setupEatingAnimation(Actor::Movement direction) +{ + QSequentialAnimationGroup *eating = new QSequentialAnimationGroup(this); + eating->setLoopCount(-1); + for (int i = 0; i < 4; i++) + { + PixmapItem *img = new PixmapItem(m_pix, this); + m_images.append(img); + int x = i * Constants::sprite_offset + Constants::sprite_margin; + int y = direction * Constants::sprite_offset + Constants::sprite_margin; + img->setSprite(x, y, Constants::field_size.width, Constants::field_size.height); + img->setVisible(false); + + QPropertyAnimation *fadein = new QPropertyAnimation(img, "visible", eating); + fadein->setDuration(0); + fadein->setEndValue(true); + + eating->addPause(100); + + QPropertyAnimation *fadeout = new QPropertyAnimation(img, "visible", eating); + fadeout->setDuration(0); + fadeout->setEndValue(false); + + QPropertyAnimation *move = new QPropertyAnimation(img, "pos", m_moving); + move->setDuration(Constants::tick - 30); + move->setEndValue(QPoint(0, 0)); + } + + return eating; +} + +PixmapItem &Actor::icon() +{ + return m_icon; +} + +const QString Actor::iconStr() +{ + return QString(":/actor%1icon").arg(Util::floorLog2(m_color) + 1); +} + +Actor::Movement Actor::direction() +{ + return m_direction; +} + + +void Actor::setDirection(Movement direction) +{ + m_direction = direction; +} + +bool Actor::isLocal() +{ + return m_local; +} + +bool Actor::hadReset() +{ + if (!m_reset) + return false; + m_reset = false; + return true; +} + +void Actor::reset() +{ + m_reset = true; + if (Constants::server) + { + m_direction = Actor::None; + return; + } + + stopEating(); + m_moving->stop(); + setZValue(m_color * 10); + m_dieing->stop(); + /* hide all pictures */ + for (int i = 0; i < m_images.size(); ++i) + m_images.at(i)->setVisible(false); + + if (m_eating[m_direction] != NULL) + m_eating[m_direction]->stop(); + + m_direction = Actor::None; + m_images[m_direction]->setVisible(true); +} + +void Actor::move(QPoint newpos) +{ + QPoint oldpos = pos().toPoint(); + Actor::Movement direction = Actor::None; + if (oldpos.x() - newpos.x() < 0) + direction = Actor::Right; + else if (oldpos.x() - newpos.x() > 0) + direction = Actor::Left; + else if (oldpos.y() - newpos.y() < 0) + direction = Actor::Down; + else if (oldpos.y() - newpos.y() > 0) + direction = Actor::Up; + move(direction); +} + +void Actor::move(Actor::Movement direction) +{ + if (Constants::server) + return moveByServer(direction); + + /* stop current animation if direction changed */ + if (direction != m_direction) + { + /* hide all pictures */ + for (int i = 0; i < m_images.size(); ++i) + m_images.at(i)->setVisible(false); + + if (m_eating[m_direction] != NULL) + m_eating[m_direction]->stop(); + } + + QPointF endpos = movementToPoint(direction); + switch(direction) + { + case Actor::Left: + case Actor::Right: + endpos *= Constants::field_size.width; + break; + case Actor::Up: + case Actor::Down: + endpos *= Constants::field_size.height; + break; + case Actor::None: + default: + break; + } + + for(int i = 0; i < m_moving->animationCount(); ++i) + { + QPropertyAnimation *move = dynamic_cast(m_moving->animationAt(i)); + move->setStartValue(QPoint(0, 0) - endpos); + } + setPos(pos() + endpos); + + /* start new animation if direction changed */ + if (direction != m_direction) + { + if (direction == Actor::None) + m_images[m_direction]->setVisible(true); + else + m_eating[direction]->start(); + } + + /* start moving animation */ + if (direction != Actor::None) + m_moving->start(); + + m_direction = direction; +} + +void Actor::moveByServer(Actor::Movement direction) +{ + QPointF endpos = movementToPoint(direction); + switch(direction) + { + case Actor::Left: + case Actor::Right: + endpos *= Constants::field_size.width; + break; + case Actor::Up: + case Actor::Down: + endpos *= Constants::field_size.height; + break; + case Actor::None: + default: + break; + } + setPos(pos() + endpos); + m_direction = direction; +} + +bool Actor::isMoving() +{ + return (m_moving->state() == QAbstractAnimation::Running); +} + +bool Actor::canEat(Actor *other, const QList &order) +{ + if (other == NULL || order.empty() || m_color == other->color()) + return false; + + int idx = order.indexOf(m_color); + return (order.at(idx + 1) == other->color()); +} + +void Actor::onDie(Actor *other) +{ + other->eatingPacman(); + die(); +} + +void Actor::die() +{ + if (Constants::server) + return; + + reset(); + m_images[m_direction]->setVisible(false); + setZValue(zValue() * 10); + m_dieing->start(); + if (m_local) + AudioManager::self()->play(Sound::Die); +} + +void Actor::eatingFruit() +{ + if (!m_local) + return; + AudioManager::self()->play(Sound::EatingFruit); +} + +void Actor::eatingPacman() +{ + if (!m_local) + return; + AudioManager::self()->play(Sound::EatingGhost); +} + +void Actor::startEating() +{ + if (!m_local || !m_wakaPlayer->isWorking()) + return; + m_wakaPlayer->play(); +} + +void Actor::stopEating() +{ + if (!m_local || !m_wakaPlayer->isWorking()) + return; + m_wakaPlayer->pause(); +} + +unsigned int Actor::getRoundPoints() +{ + return m_roundPoints; +} + +unsigned int Actor::getGamePoints() +{ + return m_gamePoints; +} + +void Actor::addRoundPoints(unsigned int amount) +{ + m_roundPoints += amount; +} + +void Actor::finishRound(bool died) +{ + if (!died) + m_gamePoints += m_roundPoints; + m_roundPoints = 0; +} + +QPoint Actor::movementToPoint(const Actor::Movement direction) +{ + QPoint endpos(0,0); + switch (direction) + { + case Actor::Up: + endpos = QPoint(0, -1); + break; + case Actor::Down: + endpos = QPoint(0, 1); + break; + case Actor::Left: + endpos = QPoint(-1, 0); + break; + case Actor::Right: + endpos = QPoint(1, 0); + break; + case Actor::None: + break; + default: + Q_ASSERT(false); + } + return endpos; +} diff --git a/pacman-c++/common/actor.h b/pacman-c++/common/actor.h new file mode 100644 index 0000000..c30c62a --- /dev/null +++ b/pacman-c++/common/actor.h @@ -0,0 +1,81 @@ +#ifndef ACTOR_H +#define ACTOR_H + +#include "gameentity.h" +#include "constants.h" +#include "audio.h" +#include +#include +#include + +class Actor + : public GameEntity +{ +Q_OBJECT + +public: + enum Movement + { + None = 0, + Left, + Right, + Up, + Down, + }; + + enum + { + Type = UserType + Transmission::pacman + }; + + Actor(Color::Color color, bool local = false, QGraphicsItem *parent = 0); + virtual ~Actor() + {}; + + PixmapItem &icon(); + const QString iconStr(); + Movement direction(); + void setDirection(Movement direction); + void reset(); + bool hadReset(); + bool isLocal(); + void move(Movement direction); + void move(QPoint newpos); + bool isMoving(); + void die(); + void eatingFruit(); + void eatingPacman(); + void startEating(); + void stopEating(); + bool canEat(Actor *other, const QList &order); + virtual void onDie(Actor *); + + unsigned int getRoundPoints(); + unsigned int getGamePoints(); + void addRoundPoints(unsigned int amount); + void finishRound(bool died = false); + + static QPoint movementToPoint(const Movement direction); + +private: + void moveByServer(Movement direction); + QSequentialAnimationGroup *setupEatingAnimation(Actor::Movement direction); + +private: + QPixmap m_pix; + Movement m_direction; + PixmapItem m_icon; + bool m_local; + bool m_reset; + GaplessAudioPlayer *m_wakaPlayer; + + unsigned int m_roundPoints; + unsigned int m_gamePoints; + + QList m_images; + QList m_eating; + QParallelAnimationGroup *m_moving; + QSequentialAnimationGroup *m_dieing; +}; + +#endif // ACTOR_H diff --git a/pacman-c++/common/audio.cpp b/pacman-c++/common/audio.cpp new file mode 100644 index 0000000..70cd37e --- /dev/null +++ b/pacman-c++/common/audio.cpp @@ -0,0 +1,355 @@ +#include "audio.h" +#include "constants.h" +#include +#include +#include +#include +#include +#include + +/* the universe's only audio manager */ +AudioManager *AudioManager::m_instance = NULL; +bool AudioManager::m_working = false; + +AudioManager::AudioManager() + : m_muted(true) +{ + if (Constants::server) + { + qDebug() << "Server has no sound"; + m_players.append(new AudioPlayer(this)); + } + else + { + preload(); + AudioPlayer *firstplayer = new AudioPlayer(this); + firstplayer->test(m_sounds[Sound::WakaWaka]); + m_working = firstplayer->m_working; + m_players.append(firstplayer); + } + m_muted = false; +} + +AudioManager *AudioManager::self() +{ + if (m_instance == NULL) + m_instance = new AudioManager(); + return m_instance; +} + +bool AudioManager::isWorking() const +{ + return m_working; +} + +void AudioManager::setMuted(bool mute) +{ + if (!isWorking()) + return; + + if (mute == m_muted) + return; + + for(int i = 0; i < m_players.count(); ++i) + m_players.at(i)->setMuted(mute); + m_muted = mute; + emit mutedChanged(mute); +} + +bool AudioManager::isMuted() const +{ + return m_muted; +} + +void AudioManager::pause() +{ + if (!isWorking()) + return; + for(int i = 0; i < m_players.count(); ++i) + m_players.at(i)->pause(); +} + +void AudioManager::stop() +{ + if (!isWorking()) + return; + for(int i = 0; i < m_players.count(); ++i) + m_players.at(i)->stop(); +} + +void AudioManager::clear() +{ + if (!isWorking()) + return; + for(int i = 0; i < m_players.count(); ++i) + m_players.at(i)->clear(); +} + +void AudioManager::clearQueue() const +{ + if (!isWorking()) + return; + for(int i = 0; i < m_players.count(); ++i) + m_players.at(i)->clearQueue(); +} + +AudioPlayer *AudioManager::audioPlayer() +{ + return m_players.at(0); +} + +void AudioManager::play(Sound::Type sound, bool wait) +{ + audioPlayer()->play(sound, wait); +} + +void AudioManager::enqueue(Sound::Type sound) +{ + if (!isWorking()) + return; + audioPlayer()->enqueue(Phonon::MediaSource(m_sounds[sound])); +} + +void AudioManager::registerAudioPlayer(AudioPlayer *player) +{ + player->setMuted(m_muted); + connect(player, SIGNAL(destroyed(QObject *)), this, SLOT(unregisterAudioPlayer_helper(QObject *))); + m_players.append(player); +} + +void AudioManager::unregisterAudioPlayer(AudioPlayer *player) +{ + disconnect(player, SIGNAL(destroyed(QObject *)), this, SLOT(unregisterAudioPlayer_helper(QObject *))); + m_players.removeAll(player); +} + +void AudioManager::unregisterAudioPlayer_helper(QObject *player) +{ + unregisterAudioPlayer(static_cast(player)); +} + +void AudioManager::preload() +{ + m_sounds.clear(); + QDir sounds(":/sound"); + for(unsigned i = 1; i <= sounds.count(); ++i) + m_sounds.append(new QFile(QString(":/sound/sound%1").arg(i), this)); +} + +QFile *AudioManager::sound(Sound::Type sound) +{ + if (!isWorking()) + return NULL; + return m_sounds.at(sound); +} + +/* --------------------------------------------------------------- */ + +AudioPlayer::AudioPlayer(QObject *parent) + : Phonon::MediaObject(parent), m_working(false) +{ + if (!Constants::server) + { + m_working = AudioManager::m_working; + m_output = new Phonon::AudioOutput(Phonon::MusicCategory, this); + Phonon::createPath(this, m_output); + } +} + +bool AudioPlayer::isWorking() const +{ + return m_working; +} + +void AudioPlayer::setMuted(bool mute) +{ + m_output->setMuted(mute); +} + +bool AudioPlayer::isMuted() const +{ + return m_output->isMuted(); +} + +void AudioPlayer::setLoop(QFile *sound) +{ + if (!isWorking()) + return; + + if (sound == NULL) + { + disconnect(this, SIGNAL(aboutToFinish()), this, SLOT(loopEnqueue())); + return; + } + + m_loopsound = sound; + connect(this, SIGNAL(aboutToFinish()), this, SLOT(loopEnqueue())); + setCurrentSource(Phonon::MediaSource(m_loopsound)); + enqueue(Phonon::MediaSource(m_loopsound)); +} + +void AudioPlayer::setLoop(Sound::Type sound) +{ + setLoop(AudioManager::self()->sound(sound)); +} + +void AudioPlayer::play() +{ + Phonon::MediaObject::play(); +} + +void AudioPlayer::play(Sound::Type sound, bool wait) +{ + if (m_working) + { + setCurrentSource(Phonon::MediaSource(AudioManager::self()->sound(sound))); + play(); + } + else if (wait) + { + QTimer *timer = new QTimer(this); + timer->setSingleShot(true); + unsigned int interval = Sound::length[sound]; + /* add a small delay server side only */ + if (Constants::server) + interval += Constants::tick; + timer->setInterval(interval); + connect(timer, SIGNAL(timeout()), this, SLOT(finished_ex())); + timer->start(); + } +} + +/* this is a simple hack to check if phonon can actually play sounds.. */ +void AudioPlayer::test(QFile *testsound) +{ + stop(); + m_output->setVolume(0); + setCurrentSource(Phonon::MediaSource(testsound)); + connect(this, SIGNAL(stateChanged(Phonon::State,Phonon::State)), this, SLOT(stateChanged_ex(Phonon::State,Phonon::State))); + play(); + + QTimer timer; + timer.setSingleShot(true); + connect(&timer, SIGNAL(timeout()), this, SLOT(testFinished())); + timer.start(500); + while(timer.isActive()) + { + qApp->processEvents(); + Sleeper::msleep(1); + } + clear(); +} + +void AudioPlayer::finished_ex() +{ + emit finished(); +} + +void AudioPlayer::stateChanged_ex(Phonon::State newstate, Phonon::State /* oldstate */) +{ + if (newstate != Phonon::ErrorState) + { + m_working = true; + m_output->setVolume(1); + qDebug() << "Sound is working for you!"; + } + disconnect(this, SIGNAL(stateChanged(Phonon::State, Phonon::State)), this, SLOT(stateChanged_ex(Phonon::State, Phonon::State))); + stop(); +} + +void AudioPlayer::testFinished() +{ + if (!m_working) + qWarning() << "There's no sound for you :("; + disconnect(this, SIGNAL(stateChanged(Phonon::State, Phonon::State)), this, SLOT(stateChanged_ex(Phonon::State, Phonon::State))); +} + +void AudioPlayer::loopEnqueue() +{ + enqueue(Phonon::MediaSource(m_loopsound)); +} + +/* --------------------------------------------------------------- */ + +GaplessAudioPlayer::GaplessAudioPlayer(Sound::Type sound, qint32 mark, QObject *parent) + : QObject(parent), m_sound(sound) +{ + m_working = AudioManager::m_working; + if (!m_working) + return; + + m_player1 = new AudioPlayer(this); + m_player2 = new AudioPlayer(this); + + m_player2->setPrefinishMark(mark); + m_player1->setPrefinishMark(mark); + + connect(m_player1, SIGNAL(prefinishMarkReached(qint32)), this, SLOT(startPlayer2())); + connect(m_player2, SIGNAL(prefinishMarkReached(qint32)), this, SLOT(startPlayer1())); + + AudioManager::self()->registerAudioPlayer(m_player1); + AudioManager::self()->registerAudioPlayer(m_player2); +} + +bool GaplessAudioPlayer::isWorking() const +{ + return m_working; +} + +void GaplessAudioPlayer::setMuted(bool mute) +{ + if (!m_working) + return; + m_player1->setMuted(mute); + m_player2->setMuted(mute); +} + +bool GaplessAudioPlayer::isMuted() const +{ + return m_player1->isMuted() && m_player2->isMuted(); +} + +void GaplessAudioPlayer::play() +{ + if (!m_working) + return; + if (m_player1->state() != Phonon::PlayingState && m_player2->state() != Phonon::PlayingState) + startPlayer1(); +} + +void GaplessAudioPlayer::pause() +{ + if (!m_working) + return; + if (m_player1->state() != Phonon::PausedState) + m_player1->pause(); + if (m_player2->state() != Phonon::PausedState) + m_player2->pause(); +} + +void GaplessAudioPlayer::startPlayer1() +{ + m_player1->play(m_sound); +} + +void GaplessAudioPlayer::startPlayer2() +{ + m_player2->play(m_sound); +} + +/* --------------------------------------------------------------- */ + +void AudioPlayer::Sleeper::sleep(unsigned long secs) +{ + QThread::sleep(secs); +} + +void AudioPlayer::Sleeper::msleep(unsigned long msecs) +{ + QThread::msleep(msecs); +} + +void AudioPlayer::Sleeper::usleep(unsigned long usecs) +{ + QThread::usleep(usecs); +} diff --git a/pacman-c++/common/audio.h b/pacman-c++/common/audio.h new file mode 100644 index 0000000..6aec42d --- /dev/null +++ b/pacman-c++/common/audio.h @@ -0,0 +1,148 @@ +#ifndef AUDIO_H +#define AUDIO_H + +#include +#include +#include +#include +#include + +namespace Phonon +{ + class AudioOutput; +} + +namespace Sound +{ + enum Type + { + Intro = 0, + WakaWaka, + EatingFruit, + EatingGhost, + Die, + Ambient + }; + + const unsigned int length[] = { + 4310, 2090, 570, 570, 1720, + }; +}; + +/* --------------------------------------------------------------- */ + +class AudioPlayer + : public Phonon::MediaObject +{ + Q_OBJECT + friend class AudioManager; + +private: + class Sleeper + : public QThread + { + public: + static void sleep(unsigned long secs); + static void msleep(unsigned long msecs); + static void usleep(unsigned long usecs); + }; + +public: + AudioPlayer(QObject *parent = 0); + bool isWorking() const; + void setMuted(bool mute = true); + bool isMuted() const; + void setLoop(Sound::Type sound); + void play(); + void play(Sound::Type sound, bool wait = false); + +protected: + void test(QFile *testsound); + void setLoop(QFile *sound); + +public slots: + void loopEnqueue(); + +protected slots: + void finished_ex(); + void testFinished(); + void stateChanged_ex(Phonon::State newstate, Phonon::State oldstate); + +protected: + bool m_working; + QFile *m_loopsound; + Phonon::AudioOutput *m_output; +}; + +/* --------------------------------------------------------------- */ + +class GaplessAudioPlayer + : public QObject +{ + Q_OBJECT + +public: + GaplessAudioPlayer(Sound::Type sound, qint32 mark, QObject *parent = 0); + bool isWorking() const; + void setMuted(bool mute = true); + bool isMuted() const; + void play(); + void pause(); + +protected slots: + void startPlayer1(); + void startPlayer2(); + +protected: + bool m_working; + Sound::Type m_sound; + AudioPlayer *m_player1; + AudioPlayer *m_player2; +}; + +/* --------------------------------------------------------------- */ + +class AudioManager + : public QObject +{ + Q_OBJECT + friend class AudioPlayer; + friend class GaplessAudioPlayer; + +public: + AudioManager(); + static AudioManager *self(); + bool isWorking() const; + void setMuted(bool mute = true); + bool isMuted() const; + void pause(); + void stop(); + void clear(); + void clearQueue() const; + + AudioPlayer *audioPlayer(); + void play(Sound::Type sound, bool wait = false); + void enqueue(Sound::Type sound); + + void registerAudioPlayer(AudioPlayer *player); + void unregisterAudioPlayer(AudioPlayer *player); + +signals: + void mutedChanged(bool muted); + +private slots: + void unregisterAudioPlayer_helper(QObject *player); + +private: + void preload(); + QFile *sound(Sound::Type sound); + +private: + bool m_muted; + static bool m_working; + static AudioManager *m_instance; + QList m_sounds; + QList m_players; +}; + +#endif // AUDIOPLAYER_H diff --git a/pacman-c++/common/block.cpp b/pacman-c++/common/block.cpp new file mode 100644 index 0000000..7f9dd14 --- /dev/null +++ b/pacman-c++/common/block.cpp @@ -0,0 +1,56 @@ +#include "block.h" +#include "constants.h" +#include "actor.h" +#include "util.h" +#include + +QMap Block::m_pixmaps; + +Block::Block(Color::Color color, unsigned int neighbours, QGraphicsItem *parent) + : GameEntity(color, parent), m_neighbours(neighbours) +{ + m_type = Type; + + /* empty object for servers */ + if (Constants::server) + return; + + if (m_pixmaps.find(color) == m_pixmaps.end()) + { + unsigned int colid = (color == Color::none) ? 0 : Util::floorLog2(color) + 1; + QString pixmapName = ":/" + QString("block%1").arg(colid); + m_pixmaps[color] = QPixmap(pixmapName); + } + setPixmap(m_pixmaps.find(color).value()); + setNeighbours(m_neighbours); +} + +unsigned int Block::neighbours() +{ + return m_neighbours; +} + +void Block::setNeighbours(unsigned int neighbours) +{ + m_neighbours = neighbours; + setSprite(neighbours * Constants::sprite_offset, 0, Constants::field_size.width, Constants::field_size.height); +} + +bool Block::checkEnter(Actor * /* actor */) +{ + if (m_color == Color::none) + return false; + return true; +} + +GameEntity::EnteredState Block::enter(Actor *actor) +{ + if (m_color != Color::none && m_color != actor->color()) + return DestroyedActor; + return Nothing; +} + +void Block::onDie(Actor *actor) +{ + actor->die(); +} diff --git a/pacman-c++/common/block.h b/pacman-c++/common/block.h new file mode 100644 index 0000000..abfbc5a --- /dev/null +++ b/pacman-c++/common/block.h @@ -0,0 +1,43 @@ +#ifndef BLOCK_H +#define BLOCK_H + +#include "gameentity.h" +#include "constants.h" +#include + +class Block + : public GameEntity +{ +public: + enum Neighbour + { + None = 0, + Left = (1 << 0), + Right = (1 << 1), + Up = (1 << 2), + Down = (1 << 3) + }; + + enum + { + Type = UserType + Transmission::block + }; + +public: + Block(Color::Color color, unsigned int neighbours = None, QGraphicsItem *parent = 0); + virtual ~Block() + {}; + + unsigned int neighbours(); + void setNeighbours(unsigned int neighbours); + virtual bool checkEnter(Actor *actor); + virtual EnteredState enter(Actor *actor); + virtual void onDie(Actor *); + +private: + // map for saving QPixmaps for reuse + static QMap m_pixmaps; + unsigned int m_neighbours; +}; + +#endif // BLOCK_H diff --git a/pacman-c++/common/bonuspoint.cpp b/pacman-c++/common/bonuspoint.cpp new file mode 100644 index 0000000..033b7c8 --- /dev/null +++ b/pacman-c++/common/bonuspoint.cpp @@ -0,0 +1,38 @@ +#include "bonuspoint.h" +#include "constants.h" +#include "actor.h" + +#define BONUSPOINTS_NUM_SPRITES 4 + +namespace +{ + QPixmap *pixmap = NULL; +} + +BonusPoint::BonusPoint(QGraphicsItem *parent) + : GameEntity(parent) +{ + m_type = Type; + + /* empty object for servers */ + if (Constants::server) + return; + + if (pixmap == NULL) + pixmap = new QPixmap(":/bonuspoints"); + setPixmap(*pixmap); + + int rand = (int) (BONUSPOINTS_NUM_SPRITES * (qrand() / (RAND_MAX + 1.0))); + setSprite(rand * 20 + Constants::sprite_margin, Constants::sprite_margin, Constants::field_size.width, Constants::field_size.height); +} + +GameEntity::EnteredState BonusPoint::enter(Actor *actor) +{ + actor->addRoundPoints(Constants::Game::bonus_point_value); + return DestroyedEntity; +} + +void BonusPoint::onDie(Actor *actor) +{ + actor->eatingFruit(); +} diff --git a/pacman-c++/common/bonuspoint.h b/pacman-c++/common/bonuspoint.h new file mode 100644 index 0000000..a72a4db --- /dev/null +++ b/pacman-c++/common/bonuspoint.h @@ -0,0 +1,24 @@ +#ifndef BONUSPOINT_H +#define BONUSPOINT_H + +#include "gameentity.h" + +class BonusPoint + : public GameEntity +{ +public: + enum + { + Type = UserType + Transmission::bonuspoint + }; + +public: + BonusPoint(QGraphicsItem *parent=0); + virtual ~BonusPoint() + {}; + + virtual EnteredState enter(Actor *actor); + virtual void onDie(Actor *actor); +}; + +#endif // BONUSPOINT_H diff --git a/pacman-c++/common/common.pro b/pacman-c++/common/common.pro new file mode 100644 index 0000000..5903f9d --- /dev/null +++ b/pacman-c++/common/common.pro @@ -0,0 +1,30 @@ +TEMPLATE = lib +CONFIG += staticlib + +include(../common.pri) + +SOURCES += pixmapitem.cpp \ + actor.cpp \ + block.cpp \ + bonuspoint.cpp \ + point.cpp \ + audio.cpp \ + sceneholder.cpp \ + util.cpp \ + gameentity.cpp +HEADERS += pixmapitem.h \ + actor.h \ + block.h \ + bonuspoint.h \ + constants.h \ + point.h \ + audio.h \ + sceneholder.h \ + util.h \ + gameentity.h + +OTHER_FILES += style.qss \ + pacman.rc + +RESOURCES += pacman.qrc +RC_FILE = pacman.rc diff --git a/pacman-c++/common/constants.h b/pacman-c++/common/constants.h new file mode 100644 index 0000000..d40c39e --- /dev/null +++ b/pacman-c++/common/constants.h @@ -0,0 +1,102 @@ +#ifndef CONSTANTS_H +#define CONSTANTS_H +#include + +namespace Constants { + const struct + { + const unsigned int width; + const unsigned int height; + } + field_size = { 16, 16 }, + map_size = { 35, 17 }, + map_size_pixel = { field_size.width * map_size.width, + field_size.height * map_size.height }; + const unsigned int sprite_margin = 2; + const unsigned int sprite_offset = 20; + const unsigned int tick = 250; // ms + extern bool server; + + namespace Networking + { + const unsigned int port = 7321; + const unsigned int connection_timeout = 3000; + const unsigned int packet_timeout = 3000; + } + + namespace Game + { + const unsigned int bonus_point_value = 100; + const unsigned int point_value = 10; + /* players will be placed with this minimum manhattan distance */ + const unsigned int player_minimum_distance = 5; + /* if the distance above isn't possible, decrease the distance by this value */ + const unsigned int player_distance_decr = 2; + /* there's a chance of 1:30 that a bonus point will be added (with one actor) */ + const unsigned int bouns_point_chance = 30; + /* every additional player will raise the chance of a bonus point by that factor */ + const unsigned int bouns_point_chance_playerfactor = 3; + /* there's a chance of 1:5 that a block will be colorized */ + const unsigned int colorize_block_chance = 5; + /* how long colorized blocks will stay colorized */ + const unsigned int colorize_block_tickcount_min = 50; + const unsigned int colorize_block_tickcount_max = 100; + } + + namespace AI + { + /* bots minimum manhatten distance before other players will be recognized */ + const unsigned int player_minimum_distance = 4; + /* weight values used to determine new direction of bot */ + const unsigned int weight_afraid = 50; + const unsigned int weight_hunt = 10; + const unsigned int weight_bonus_point = 3; + const unsigned int weight_point = 1; + const unsigned int weight_colorblock = 5; + } +} + +namespace Color +{ + enum Color + { + none = 0, + red = (1 << 0), + blue = (1 << 1), + green = (1 << 2), + yellow = (1 << 3) + }; + + /* colororder used in protocol and gui */ + const Color order[] = + { Color::red, Color::blue, Color::green, Color::yellow, Color::none }; +} + +// constants for data transmission to client +namespace Transmission +{ + typedef unsigned int field_t; + typedef unsigned int mask_t; + + const field_t none = 0; + const field_t block = (1 << 4); + const field_t point = (1 << 5); + const field_t bonuspoint = (1 << 6); + const field_t pacman = (1 << 7); + const field_t empty = (1 << 8); // explicit empty for update + const field_t death = (1 << 9); + + const field_t direction_none = 0; + const field_t direction_left = (1 << 10); + const field_t direction_right = (1 << 11); + const field_t direction_up = (1 << 12); + const field_t direction_down = (1 << 13); + + const mask_t color_mask = Color::none | Color::red | Color::blue | Color::green | Color::yellow; + const mask_t type_mask = block | bonuspoint; + const mask_t direction_mask = direction_none | direction_left | direction_right | direction_up | direction_down; + + typedef field_t** map_t; +} + +#endif // CONSTANTS_H diff --git a/pacman-c++/common/gameentity.cpp b/pacman-c++/common/gameentity.cpp new file mode 100644 index 0000000..e73e759 --- /dev/null +++ b/pacman-c++/common/gameentity.cpp @@ -0,0 +1,9 @@ +#include "gameentity.h" + +GameEntity::GameEntity(Color::Color color, QGraphicsItem *parent) + : PixmapItem(parent), m_type(Type), m_color(color) +{} + +GameEntity::GameEntity(QGraphicsItem *parent) + : PixmapItem(parent), m_type(Type), m_color(Color::none) +{} diff --git a/pacman-c++/common/gameentity.h b/pacman-c++/common/gameentity.h new file mode 100644 index 0000000..116fae5 --- /dev/null +++ b/pacman-c++/common/gameentity.h @@ -0,0 +1,72 @@ +#ifndef GAMEENTITY_H +#define GAMEENTITY_H + +#include "constants.h" +#include "pixmapitem.h" +#include +#include + +class Actor; + +/** + * Base class for entities that interact in the game + */ +class GameEntity + : public PixmapItem +{ +public: + enum + { + Type = UserType + 1 + }; + + enum EnteredState + { + Nothing, + DestroyedEntity, + DestroyedActor + }; + +public: + GameEntity(Color::Color color = Color::none, QGraphicsItem *parent = 0); + GameEntity(QGraphicsItem *parent); + virtual ~GameEntity() + {}; + + /* color of entity */ + virtual Color::Color color() + { + return m_color; + } + + /* returns whether the actor may enter this field */ + virtual bool checkEnter(Actor *) + { + return true; + } + + /* performs action when this actor acctually enters + * returns whether this entity survives the entering + */ + virtual EnteredState enter(Actor *) + { + /* default to no action/survive */ + return Nothing; + } + + /* called when an instance acctually dies for creating effects */ + virtual void onDie(Actor *) + {}; + + /* enable the use of qgraphicsitem_cast with this item */ + int type() const + { + return m_type; + } + +protected: + int m_type; + Color::Color m_color; +}; + +#endif // GAMEENTITY_H diff --git a/pacman-c++/common/pacman.qrc b/pacman-c++/common/pacman.qrc new file mode 100644 index 0000000..6654fd1 --- /dev/null +++ b/pacman-c++/common/pacman.qrc @@ -0,0 +1,31 @@ + + + pics/actor1.png + pics/actor2.png + pics/actor3.png + pics/actor4.png + pics/block0.png + pics/block1.png + pics/block2.png + pics/block3.png + pics/block4.png + pics/bonuspoints.png + pics/points.png + style.qss + pics/app.ico + pics/actor1icon.png + pics/actor2icon.png + pics/actor3icon.png + pics/actor4icon.png + pics/soundon.xpm + pics/soundoff.xpm + + + sound/intro.ogg + sound/waka_waka.ogg + sound/eating_fruit.ogg + sound/eating_ghost.ogg + sound/die.ogg + sound/ambient.ogg + + diff --git a/pacman-c++/common/pacman.rc b/pacman-c++/common/pacman.rc new file mode 100644 index 0000000..8c9f03b --- /dev/null +++ b/pacman-c++/common/pacman.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "pics/app.ico" diff --git a/pacman-c++/common/pics/actor1.png b/pacman-c++/common/pics/actor1.png new file mode 100644 index 0000000..5a37b4f Binary files /dev/null and b/pacman-c++/common/pics/actor1.png differ diff --git a/pacman-c++/common/pics/actor1icon.png b/pacman-c++/common/pics/actor1icon.png new file mode 100644 index 0000000..4407cef Binary files /dev/null and b/pacman-c++/common/pics/actor1icon.png differ diff --git a/pacman-c++/common/pics/actor2.png b/pacman-c++/common/pics/actor2.png new file mode 100644 index 0000000..736a6e7 Binary files /dev/null and b/pacman-c++/common/pics/actor2.png differ diff --git a/pacman-c++/common/pics/actor2icon.png b/pacman-c++/common/pics/actor2icon.png new file mode 100644 index 0000000..eaf2a4c Binary files /dev/null and b/pacman-c++/common/pics/actor2icon.png differ diff --git a/pacman-c++/common/pics/actor3.png b/pacman-c++/common/pics/actor3.png new file mode 100644 index 0000000..f963cff Binary files /dev/null and b/pacman-c++/common/pics/actor3.png differ diff --git a/pacman-c++/common/pics/actor3icon.png b/pacman-c++/common/pics/actor3icon.png new file mode 100644 index 0000000..2558cbc Binary files /dev/null and b/pacman-c++/common/pics/actor3icon.png differ diff --git a/pacman-c++/common/pics/actor4.png b/pacman-c++/common/pics/actor4.png new file mode 100644 index 0000000..e500dc5 Binary files /dev/null and b/pacman-c++/common/pics/actor4.png differ diff --git a/pacman-c++/common/pics/actor4icon.png b/pacman-c++/common/pics/actor4icon.png new file mode 100644 index 0000000..82bf637 Binary files /dev/null and b/pacman-c++/common/pics/actor4icon.png differ diff --git a/pacman-c++/common/pics/app.ico b/pacman-c++/common/pics/app.ico new file mode 100644 index 0000000..4a73cd4 Binary files /dev/null and b/pacman-c++/common/pics/app.ico differ diff --git a/pacman-c++/common/pics/block0.png b/pacman-c++/common/pics/block0.png new file mode 100644 index 0000000..76bc4b7 Binary files /dev/null and b/pacman-c++/common/pics/block0.png differ diff --git a/pacman-c++/common/pics/block1.png b/pacman-c++/common/pics/block1.png new file mode 100644 index 0000000..99e8633 Binary files /dev/null and b/pacman-c++/common/pics/block1.png differ diff --git a/pacman-c++/common/pics/block2.png b/pacman-c++/common/pics/block2.png new file mode 100644 index 0000000..963207e Binary files /dev/null and b/pacman-c++/common/pics/block2.png differ diff --git a/pacman-c++/common/pics/block3.png b/pacman-c++/common/pics/block3.png new file mode 100644 index 0000000..6662de4 Binary files /dev/null and b/pacman-c++/common/pics/block3.png differ diff --git a/pacman-c++/common/pics/block4.png b/pacman-c++/common/pics/block4.png new file mode 100644 index 0000000..00fbd74 Binary files /dev/null and b/pacman-c++/common/pics/block4.png differ diff --git a/pacman-c++/common/pics/bonuspoints.png b/pacman-c++/common/pics/bonuspoints.png new file mode 100644 index 0000000..b5714c9 Binary files /dev/null and b/pacman-c++/common/pics/bonuspoints.png differ diff --git a/pacman-c++/common/pics/pacman10-hp-sprite-2.png b/pacman-c++/common/pics/pacman10-hp-sprite-2.png new file mode 100644 index 0000000..7354b9d Binary files /dev/null and b/pacman-c++/common/pics/pacman10-hp-sprite-2.png differ diff --git a/pacman-c++/common/pics/points.png b/pacman-c++/common/pics/points.png new file mode 100644 index 0000000..6ba4496 Binary files /dev/null and b/pacman-c++/common/pics/points.png differ diff --git a/pacman-c++/common/pics/soundoff.xpm b/pacman-c++/common/pics/soundoff.xpm new file mode 100644 index 0000000..ba33f5f --- /dev/null +++ b/pacman-c++/common/pics/soundoff.xpm @@ -0,0 +1,15 @@ +/* XPM */ +static char * soundoff_xpm[] = { +"12 10 2 1", +" c None", +". c #000000", +" ", +" . ", +" .. . .", +".... . . ", +".... .. ", +".... .. ", +".... . . ", +" .. . .", +" . ", +" "}; diff --git a/pacman-c++/common/pics/soundon.xpm b/pacman-c++/common/pics/soundon.xpm new file mode 100644 index 0000000..e6ca41e --- /dev/null +++ b/pacman-c++/common/pics/soundon.xpm @@ -0,0 +1,15 @@ +/* XPM */ +static char * soundoff_xpm[] = { +"12 10 2 1", +" c None", +". c #000000", +" . ", +" . . ", +" .. . . ", +".... . . ", +".... . . ", +".... . . ", +".... . . ", +" .. . . ", +" . . ", +" . "}; diff --git a/pacman-c++/common/pixmapitem.cpp b/pacman-c++/common/pixmapitem.cpp new file mode 100644 index 0000000..1ceeec1 --- /dev/null +++ b/pacman-c++/common/pixmapitem.cpp @@ -0,0 +1,77 @@ +#include "pixmapitem.h" +#include + +PixmapItem::PixmapItem(QGraphicsItem *parent) + : QGraphicsObject(parent), m_x(0), m_y(0) +{ +} + +PixmapItem::PixmapItem(const QString &fileName, QGraphicsItem *parent) + : QGraphicsObject(parent), m_x(0), m_y(0) +{ + m_pix = ":/" + fileName; + m_width = m_pix.width(); + m_height = m_pix.height(); +} + +PixmapItem::PixmapItem(const QString &fileName, QGraphicsScene *scene) + : QGraphicsObject(), m_x(0), m_y(0) +{ + m_pix = ":/" + fileName; + m_width = m_pix.width(); + m_height = m_pix.height(); + scene->addItem(this); +} + +PixmapItem::PixmapItem(const QPixmap &pix, QGraphicsItem *parent) + : QGraphicsObject(parent), m_pix(pix), m_x(0), m_y(0) +{ + m_width = m_pix.width(); + m_height = m_pix.height(); +} + +PixmapItem::PixmapItem(const QPixmap &pix, QGraphicsScene *scene) + : QGraphicsObject(), m_pix(pix), m_x(0), m_y(0) +{ + m_width = m_pix.width(); + m_height = m_pix.height(); + scene->addItem(this); +} + +void PixmapItem::setPixmap(const QPixmap &pix) +{ + m_pix = pix; + m_width = m_pix.width(); + m_height = m_pix.height(); +} + +void PixmapItem::setSprite(int x, int y, int width, int height) +{ + m_x = x; + m_y = y; + m_width = width; + m_height = height; +} + +QSizeF PixmapItem::size() const +{ + return QSizeF(m_width, m_height); +} + +QRectF PixmapItem::boundingRect() const +{ + return QRectF(QPointF(0, 0), size()); +} + +QPainterPath PixmapItem::shape() const +{ + QPainterPath path; + path.addRect(boundingRect()); + return path; +} + +void PixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + painter->drawPixmap(QPoint(0, 0), m_pix, QRect(m_x, m_y, m_width, m_height)); +} + diff --git a/pacman-c++/common/pixmapitem.h b/pacman-c++/common/pixmapitem.h new file mode 100644 index 0000000..f57c22a --- /dev/null +++ b/pacman-c++/common/pixmapitem.h @@ -0,0 +1,32 @@ +#ifndef PIXMAPITEM__H +#define PIXMAPITEM__H + +#include +#include + +class PixmapItem + : public QGraphicsObject +{ +public: + PixmapItem(QGraphicsItem *parent = 0); + PixmapItem(const QString &fileName, QGraphicsItem *parent = 0); + PixmapItem(const QString &fileName, QGraphicsScene *scene); + PixmapItem(const QPixmap &pix, QGraphicsItem *parent = 0); + PixmapItem(const QPixmap &pix, QGraphicsScene *scene); + virtual ~PixmapItem() + {}; + + void setPixmap(const QPixmap &pix); + void setSprite(int x, int y, int width, int height); + QSizeF size() const; + QRectF boundingRect() const; + QPainterPath shape() const; + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + +private: + QPixmap m_pix; + int m_x, m_y; + int m_width, m_height; +}; + +#endif diff --git a/pacman-c++/common/point.cpp b/pacman-c++/common/point.cpp new file mode 100644 index 0000000..d7ebdb1 --- /dev/null +++ b/pacman-c++/common/point.cpp @@ -0,0 +1,33 @@ +#include "point.h" +#include "constants.h" +#include "actor.h" + +namespace +{ + QPixmap *pixmap = NULL; +} + +Point::Point(QGraphicsItem *parent) + : GameEntity(parent) +{ + m_type = Type; + + /* empty object for servers */ + if (Constants::server) + return; + + if (pixmap == NULL) + pixmap = new QPixmap(":/points"); + setPixmap(*pixmap); +} + +GameEntity::EnteredState Point::enter(Actor *actor) +{ + actor->addRoundPoints(Constants::Game::point_value); + return DestroyedEntity; +} + +void Point::onDie(Actor *actor) +{ + actor->startEating(); +} diff --git a/pacman-c++/common/point.h b/pacman-c++/common/point.h new file mode 100644 index 0000000..7739554 --- /dev/null +++ b/pacman-c++/common/point.h @@ -0,0 +1,24 @@ +#ifndef POINT_H +#define POINT_H + +#include "gameentity.h" + +class Point + : public GameEntity +{ +public: + enum + { + Type = UserType + Transmission::point + }; + +public: + Point(QGraphicsItem *parent=0); + virtual ~Point() + {}; + + virtual EnteredState enter(Actor *actor); + virtual void onDie(Actor *actor); +}; + +#endif // POINT_H diff --git a/pacman-c++/common/sceneholder.cpp b/pacman-c++/common/sceneholder.cpp new file mode 100644 index 0000000..bd9b01b --- /dev/null +++ b/pacman-c++/common/sceneholder.cpp @@ -0,0 +1,364 @@ +#include "sceneholder.h" +#include "constants.h" +#include "gameentity.h" +#include "block.h" +#include "actor.h" +#include "bonuspoint.h" +#include "point.h" +#include "util.h" + +SceneHolder::SceneHolder(QObject *parent) + : QGraphicsScene(parent), m_color(Color::none), m_pointsLeft(0) +{ + setSceneRect(0, 0, Constants::map_size_pixel.width, Constants::map_size_pixel.height); + setBackgroundBrush(Qt::black); + + m_overlayText = new QGraphicsTextItem(); + + visualMap.resize(Constants::map_size.width); + for (int i = 0; i < visualMap.size(); ++i) + visualMap[i].resize(Constants::map_size.height); +} + +void SceneHolder::reset() +{ + processDelayedItems(); + showEatingText(false); + + /* remove actors from scene so they don't get deleted during clear */ + foreach(Actor *actor, m_actors) + { + actor->reset(); + removeItem(actor); + } + + /* clear our stuff */ + clear(); + m_pointsLeft = 0; + for (int i = 0; i < visualMap.size(); ++i) + { + visualMap[i].clear(); + visualMap[i].resize(Constants::map_size.height); + } + + /* add actors again */ + foreach(Actor *actor, m_actors) + addItem(actor); +} + +void SceneHolder::processDelayedItems() +{ + /* remove items that got marked for removal from scene */ + foreach(GameEntity *item, m_oldItems) + { + removeItem(item); + delete item; + } + m_oldItems.clear(); + + /* process death */ + foreach(const Color::Color color, m_death.keys()) + { + Q_ASSERT(m_death[color] != NULL); + m_death[color]->onDie(m_actors[color]); + } + m_death.clear(); +} + +void SceneHolder::updateMap(const Transmission::map_t& map) +{ + processDelayedItems(); + + /* process update */ + for (unsigned int x = 0; x < Constants::map_size.width; ++x) + { + for (unsigned int y = 0; y < Constants::map_size.height; ++y) + { + const Transmission::field_t &cur = map[x][y]; + if (cur == Transmission::none) + continue; + updateMap(map, x, y); + } + } + + if (m_pointsLeft == 0) + emit allPointsRemoved(); +} + +void SceneHolder::updateMap(const Transmission::map_t& map, const unsigned int x, unsigned int y) +{ + const Transmission::field_t &cur = map[x][y]; + if (cur == Transmission::none) + return; + + /* we may have multiple colors in one position (e.g during eating another pacman) */ + Color::Color color = static_cast(cur & Transmission::color_mask); + QList colors; + if (color == Color::none) + colors.append(Color::none); + foreach(Color::Color col, m_eatingorder.toSet()) + { + if (color & col) + colors.append(col); + } + Q_ASSERT(colors.count() > 0); + + /* for now complain if there are more colors or it's a Transmission::death packet */ + Q_ASSERT(colors.count() == 1 || cur & Transmission::death); + + if (cur & Transmission::empty) + { + GameEntity *oldItem = visualMap[x][y]; + /* special handling for purging field */ + if (oldItem != NULL) + { + /* remove item from visualmap and register item for removal in next update */ + visualMap[x][y] = NULL; + m_oldItems.append(oldItem); + + /* an item must be removed by an actor */ + Actor *actor = m_actors[colors.at(0)]; + Q_ASSERT(actor != NULL); + oldItem->onDie(actor); + } + } + + GameEntity *item = NULL; + if (cur == Transmission::none) + { + /* no update */ + } + else + { + if (cur & Transmission::block) + { + unsigned int neighbours = Block::None; + /* check for old block first */ + if (visualMap[x][y] != NULL) + { + Block *oldItem = qgraphicsitem_cast(visualMap[x][y]); + if (oldItem != NULL) + neighbours = oldItem->neighbours(); + } + /* check left side */ + if (x > 0 && map[x - 1][y] & Transmission::block) + neighbours |= Block::Left; + /* check right side */ + if (x < Constants::map_size.width && map[x + 1][y] & Transmission::block) + neighbours |= Block::Right; + /* check upside */ + if (y > 0 && map[x][y - 1] & Transmission::block) + neighbours |= Block::Up; + /* check down side */ + if (y < Constants::map_size.height && map[x][y + 1] & Transmission::block) + neighbours |= Block::Down; + item = new Block(colors.at(0), neighbours); + } + + if (cur & Transmission::bonuspoint) + item = new BonusPoint(); + + if (cur & Transmission::point) + { + item = new Point(); + connect(item, SIGNAL(destroyed()), this, SLOT(decrementPoints())); + ++m_pointsLeft; + } + + if (cur & Transmission::pacman) + { + foreach(color, colors) + { + /* WARNING: do NOT add actor to visualMap as visualMap-items may + * get deleted during update and actors are referenced in_mactors too + * if you REALLY need that you need to changed updateMap so that all actors + * will be moved before any new items get allocated (totally untested) + */ + Actor *actor = m_actors.value(color, NULL); + if (actor == NULL) + { + actor = new Actor(color, (color == m_color)); + m_actors[color] = actor; + addItem(actor); + actor->setPos(mapPositionToCoord(x, y)); + actor->hadReset(); + } + else + { + /* check for death */ + if (cur & Transmission::death) + { + foreach(Color::Color col, colors) + { + if (color == col) + continue; + if (m_actors[col]->canEat(actor, m_eatingorder)) + { + m_death[col] = actor; + break; + } + } + } + + /* move actor */ + if (actor->hadReset()) + actor->setPos(mapPositionToCoord(x, y)); + else + actor->move(mapPositionToCoord(x, y)); + + /* that's kind a hack but working right now */ + if (!(cur & Transmission::empty)) + actor->stopEating(); + qDebug() << "[SceneUpdate] actor moves: color=" << color + << "newpos=" << QPoint(x, y); + } + + if ((cur & Transmission::death) && visualMap[x][y] != NULL) + m_death[color] = visualMap[x][y]; + } + } + + if (cur & Transmission::empty) + { + /* already handled */ + } + } + + /* add new created item to scene + * remove old item on that location if there's one + */ + if (item != NULL) + { + GameEntity *oldItem = visualMap[x][y]; + if (oldItem != NULL) + { + removeItem(oldItem); + delete oldItem; + } + + addItem(item); + item->setPos(mapPositionToCoord(x, y)); + visualMap[x][y] = item; + } +} + +void SceneHolder::setColor(Color::Color color) +{ + m_color = color; +} + +Color::Color SceneHolder::color() +{ + return m_color; +} + +unsigned int SceneHolder::pointsLeft() +{ + return m_pointsLeft; +} + +void SceneHolder::decrementPoints() +{ + --m_pointsLeft; +} + +void SceneHolder::setEatingOrder(QList &order) +{ + m_eatingorder = order; +} + +QList &SceneHolder::eatingOrder() +{ + return m_eatingorder; +} + +void SceneHolder::showEatingText(bool show) +{ + if (!show) + { + if (m_overlayText->scene() == this) + removeItem(m_overlayText); + return; + } + + m_overlayText->setDefaultTextColor(Qt::black); + QString text = QString( + "
" + "
" + ); + unsigned int lines = 1; + + text = text % QString("Your Pacman:

") + .arg(m_actors[m_color]->iconStr()); + lines += 2; + + foreach(Actor *actor, m_actors) + { + foreach(Actor *other, m_actors) + { + if (!actor->canEat(other, m_eatingorder)) + continue; + text = text % QString("  can eat
") + .arg(actor->iconStr(), other->iconStr()); + ++lines; + } + } + text = text % "
"; + m_overlayText->setHtml(text); + m_overlayText->setTextWidth(150); + m_overlayText->setOpacity(0.9); + + QFontMetrics metrics(m_overlayText->font()); + m_overlayText->setPos((width() - m_overlayText->textWidth()) / 2, (height() - metrics.height() * lines) / 2); + m_overlayText->setZValue(100); + + addItem(m_overlayText); +} + +void SceneHolder::showWaitingForPlayers(bool show) +{ + if (!show) + { + if (m_overlayText->scene() == this) + removeItem(m_overlayText); + return; + } + + m_overlayText->setDefaultTextColor(Qt::black); + QString text = QString( + "
" + "
" + "Waiting for other Players...
" + "
" + ); + unsigned int lines = 3; + m_overlayText->setHtml(text); + m_overlayText->setTextWidth(150); + m_overlayText->setOpacity(0.9); + + QFontMetrics metrics(m_overlayText->font()); + m_overlayText->setPos((width() - m_overlayText->textWidth()) / 2, (height() - metrics.height() * lines) / 2); + m_overlayText->setZValue(100); + + addItem(m_overlayText); +} + +QPoint SceneHolder::mapPositionToCoord(unsigned int x, unsigned int y) +{ + return QPoint(x * Constants::field_size.width, y * Constants::field_size.height); +} + +QPoint SceneHolder::mapPositionToCoord(QPoint point) +{ + return mapPositionToCoord(point.x(), point.y()); +} + +QPoint SceneHolder::CoordToMapPosition(unsigned int x, unsigned int y) +{ + return QPoint(x / Constants::field_size.width, y / Constants::field_size.height); +} + +QPoint SceneHolder::CoordToMapPosition(QPoint point) +{ + return CoordToMapPosition(point.x(), point.y()); +} diff --git a/pacman-c++/common/sceneholder.h b/pacman-c++/common/sceneholder.h new file mode 100644 index 0000000..0872837 --- /dev/null +++ b/pacman-c++/common/sceneholder.h @@ -0,0 +1,74 @@ +#ifndef SCENEHOLDER_H +#define SCENEHOLDER_H + +#include "constants.h" +#include + +class GameEntity; +class Actor; + +class SceneHolder + : public QGraphicsScene +{ + Q_OBJECT + +public: + SceneHolder(QObject *parent = 0); + virtual ~SceneHolder() + {}; + void reset(); + unsigned int pointsLeft(); + void updateMap(const Transmission::map_t& map); + void updateMap(const Transmission::map_t& map, const unsigned int x, const unsigned int y); + void setColor(Color::Color color = Color::none); + Color::Color color(); + void setEatingOrder(QList &order); + QList &eatingOrder(); + void showEatingText(bool show = true); + void showWaitingForPlayers(bool show = true); + +signals: + void allPointsRemoved(); + +private slots: + void decrementPoints(); + +protected: + /* process items that got delayed by one tick */ + void processDelayedItems(); + /* data conversion */ + QPoint mapPositionToCoord(unsigned int x, unsigned int y); + QPoint mapPositionToCoord(QPoint point); + QPoint CoordToMapPosition(unsigned int x, unsigned int y); + QPoint CoordToMapPosition(QPoint point); + +protected: + /* map of all pixmap instances */ + QVector< QVector > visualMap; + + /* map of actors in order to keep track of those instances */ + QMap m_actors; + + /* items that got removed/has been eaten + * must be remove one tick later + */ + QList m_oldItems; + /* we need to store items that killed an actor too (e.g. colored blocks) */ + QMap m_death; + + /* my local color */ + Color::Color m_color; + + /* a actor can only eat his upper neighbour + * the order of the neighbours is determined by the colors order (sent from server) + * please note that !pl1.canEat(pl2, ...) doesn't necessarily mean that pl2 can eat pl1 + * order MUST be in this format: [col1, [col2 ... colN], col1] + */ + QList m_eatingorder; + + /* points left before round ends */ + unsigned int m_pointsLeft; + QGraphicsTextItem *m_overlayText; +}; + +#endif // SCENEHOLDER_H diff --git a/pacman-c++/common/sound/ambient.ogg b/pacman-c++/common/sound/ambient.ogg new file mode 100644 index 0000000..2919513 Binary files /dev/null and b/pacman-c++/common/sound/ambient.ogg differ diff --git a/pacman-c++/common/sound/die.ogg b/pacman-c++/common/sound/die.ogg new file mode 100644 index 0000000..f6af2e0 Binary files /dev/null and b/pacman-c++/common/sound/die.ogg differ diff --git a/pacman-c++/common/sound/eating_fruit.ogg b/pacman-c++/common/sound/eating_fruit.ogg new file mode 100644 index 0000000..752864a Binary files /dev/null and b/pacman-c++/common/sound/eating_fruit.ogg differ diff --git a/pacman-c++/common/sound/eating_ghost.ogg b/pacman-c++/common/sound/eating_ghost.ogg new file mode 100644 index 0000000..a204a2c Binary files /dev/null and b/pacman-c++/common/sound/eating_ghost.ogg differ diff --git a/pacman-c++/common/sound/intro.ogg b/pacman-c++/common/sound/intro.ogg new file mode 100644 index 0000000..d38f16b Binary files /dev/null and b/pacman-c++/common/sound/intro.ogg differ diff --git a/pacman-c++/common/sound/waka_waka.ogg b/pacman-c++/common/sound/waka_waka.ogg new file mode 100644 index 0000000..def0d96 Binary files /dev/null and b/pacman-c++/common/sound/waka_waka.ogg differ diff --git a/pacman-c++/common/style.qss b/pacman-c++/common/style.qss new file mode 100644 index 0000000..1a24459 --- /dev/null +++ b/pacman-c++/common/style.qss @@ -0,0 +1,31 @@ +QGroupBox#actor1::indicator { + background-image: url(:/actor1icon); + background-position: top left; + background-repeat: no-repeat; + width: 16px; + height: 14px; +} + +QGroupBox#actor2::indicator { + background-image: url(:/actor2icon); + background-position: top left; + background-repeat: no-repeat; + width: 16px; + height: 14px; +} + +QGroupBox#actor3::indicator { + background-image: url(:/actor3icon); + background-position: top left; + background-repeat: no-repeat; + width: 16px; + height: 14px; +} + +QGroupBox#actor4::indicator { + background-image: url(:/actor4icon); + background-position: top left; + background-repeat: no-repeat; + width: 16px; + height: 14px; +} diff --git a/pacman-c++/common/util.cpp b/pacman-c++/common/util.cpp new file mode 100644 index 0000000..a3426b6 --- /dev/null +++ b/pacman-c++/common/util.cpp @@ -0,0 +1,342 @@ +#include "util.h" + +namespace Util +{ + Transmission::map_t createUninitialisedMap() + { + Transmission::map_t map; + map = new Transmission::field_t*[Constants::map_size.width]; + for (unsigned int i = 0; i < Constants::map_size.width; ++i) + map[i] = new Transmission::field_t[Constants::map_size.height]; + return map; + } + + Transmission::map_t createEmptyMap() + { + Transmission::map_t map = createUninitialisedMap(); + for (unsigned int x = 0; x < Constants::map_size.width; ++x) + { + for (unsigned int y = 0; y < Constants::map_size.height; ++y) + { + Transmission::field_t &cur = map[x][y]; + cur = Transmission::none; + } + } + return map; + } + + void deleteMap(Transmission::map_t map) + { + for (unsigned int x = 0; x < Constants::map_size.width; ++x) + delete[] map[x]; + delete[] map; + } + + Transmission::map_t createDemoMap() + { + Transmission::map_t map = createEmptyMap(); + + const char *tmpl[] = { + " # # ", + " #### ###### # #### # # ###### ### ", + " # # ", + " # ##### # # # # # ### # # # ", + " # # # # # # # # # # ## # # ", + " # # # # # # # # ### # # # # ", + " # # # # # # # # # # # # ## # ", + " # # ### ##### # ### # # # ", + " ### # ", + " # # ### #### #### #### ##### ", + " #### # #..# #..# #..# # # ", + " # # ### #..# #..# #### # # # # ", + " # # # #..# #..# # # ", + " # #### # #### #### # # ##### # ", + " # # ", + " #### ###### # ##### # ####### ### ", + " # # " + }; + + for (unsigned int x = 0; x < Constants::map_size.width; ++x) + { + for (unsigned int y = 0; y < Constants::map_size.height; ++y) + { + Transmission::field_t &cur = map[x][y]; + cur = Transmission::none; + if (tmpl[y][x] == '#') + cur |= Color::none | Transmission::block; + /* this is a simple hack to create areas where no + * autoplaced points/actors will be placed (see makePoints) + */ + else if (tmpl[y][x] == '.') + cur |= Transmission::point; + } + } + return map; + } + + void placeActors(Transmission::map_t map, unsigned int players, const Color::Color *colors) + { +#if 0 + for(unsigned int i = 0; i < players; ++i) + map[i][0] = colors[i] | Transmission::pacman; + return; +#endif + + int mindistance = Constants::Game::player_minimum_distance; + /* this outer loop is used if there are no more valid places left + * so we can change mindistance + */ + QList actors; + for(unsigned int i = 0; i < players; ++i) + { + /* first remove formerly placed actors from map */ + foreach(QPoint pos, actors) + map[pos.x()][pos.y()] = Transmission::none; + actors.clear(); + + /* get list of valid positions */ + QList validpos; + for (unsigned int x = 0; x < Constants::map_size.width; ++x) + { + for (unsigned int y = 0; y < Constants::map_size.height; ++y) + { + Transmission::field_t &cur = map[x][y]; + if (cur == Transmission::none) + validpos.append(QPoint(x, y)); + } + } + + /* place actors at map */ + for(i = 0; i < players; ++i) + { + int rand = (int) (validpos.size() * (qrand() / (RAND_MAX + 1.0))); + QPoint newpos = validpos.at(rand); + map[newpos.x()][newpos.y()] = colors[i] | Transmission::pacman; + qDebug() << "[Place] Actor" << i << "at" << newpos; + actors.append(newpos); + validpos.removeAt(rand); + + QMutableListIterator j(validpos); + while(j.hasNext()) + { + j.next(); + QPoint tmp = j.value() - newpos; + if (tmp.manhattanLength() < mindistance) + j.remove(); + } + + if (validpos.empty()) + { + qWarning() << "There are no more valid positions for actors left on the map"; + mindistance -= Constants::Game::player_distance_decr; + break; + } + } + } + } + + void fillPoints(Transmission::map_t map, Transmission::field_t type) + { + /* auto place normal points*/ + for (unsigned int x = 0; x < Constants::map_size.width; ++x) + { + for (unsigned int y = 0; y < Constants::map_size.height; ++y) + { + Transmission::field_t &cur = map[x][y]; + if (cur == Transmission::none) + { +#if 0 + /* use for endround testing */ + if (x > 0) + continue; +#endif + cur = type; + } + else if (cur == Transmission::point) + cur = Transmission::none; + } + } + } + + Transmission::field_t actorMovementToTransmission(Actor::Movement mov, Transmission::field_t def) + { + switch (mov) + { + case Actor::None: + return Transmission::direction_none; + break; + case Actor::Left: + return Transmission::direction_left; + break; + case Actor::Right: + return Transmission::direction_right; + break; + case Actor::Up: + return Transmission::direction_up; + break; + case Actor::Down: + return Transmission::direction_down; + break; + default: + return def; + break; + } + return def; + } + + Actor::Movement transmissionMovementToActor(Transmission::field_t field, Actor::Movement def) + { + switch (field) + { + case Transmission::direction_none: + return Actor::None; + break; + case Transmission::direction_left: + return Actor::Left; + break; + case Transmission::direction_right: + return Actor::Right; + break; + case Transmission::direction_up: + return Actor::Up; + break; + case Transmission::direction_down: + return Actor::Down; + break; + default: + return def; + break; + } + return def; + } + + const QString colorToString(Color::Color color) + { + switch(color) + { + case Color::none: + return "none"; + break; + case Color::red: + return "red"; + break; + case Color::blue: + return "blue"; + break; + case Color::green: + return "green"; + break; + case Color::yellow: + return "yellow"; + break; + default: + return "unknown"; + break; + } + } + + QSharedPointer createPacket(const ::google::protobuf::MessageLite& packet) + { + qint64 packetlen = packet.ByteSize(); + QSharedPointer data = QSharedPointer(new QByteArray); + data->resize(packetlen); + + /* use protobuf.SerializeWithCachedSizesToArray() to avoid calling protobuf.ByteSize() again */ + ::google::protobuf::uint8 *dataptr = reinterpret_cast(data->data()); + packet.SerializeWithCachedSizesToArray(dataptr); + return data; + } + + bool sendPacket(QByteArray *data, ENetPeer *peer, ENetHost *host) + { + ENetPacket *packet = enet_packet_create(data->data(), data->length(), ENET_PACKET_FLAG_RELIABLE); + if (enet_peer_send(peer, 0, packet) < 0) + { + qDebug() << "[sendPacket] Error while sending packet"; + return false; + } + enet_host_flush(host); + return true; + } + + bool sendPacket(const ::google::protobuf::MessageLite& packet, ENetPeer *peer, ENetHost *host) + { + return sendPacket(createPacket(packet).data(), peer, host); + } + + QSharedPointer receivePacket(ENetPacket *packet) + { + const char *pdata = reinterpret_cast(packet->data); + QSharedPointer data = QSharedPointer(new QByteArray(pdata, packet->dataLength)); + return data; + } + + int floorLog2(unsigned int n) + { + if (n == 0) + return -1; + + int pos = 0; + if (n >= 1<<16) { n >>= 16; pos += 16; } + if (n >= 1<< 8) { n >>= 8; pos += 8; } + if (n >= 1<< 4) { n >>= 4; pos += 4; } + if (n >= 1<< 2) { n >>= 2; pos += 2; } + if (n >= 1<< 1) { pos += 1; } + return pos; + } + +#if 0 + void hexdump(void *pAddressIn, long lSize) + { + char szBuf[100]; + long lIndent = 1; + long lOutLen, lIndex, lIndex2, lOutLen2; + long lRelPos; + struct { char *pData; unsigned long lSize; } buf; + unsigned char *pTmp,ucTmp; + unsigned char *pAddress = (unsigned char *)pAddressIn; + + buf.pData = (char *)pAddress; + buf.lSize = lSize; + + while (buf.lSize > 0) + { + pTmp = (unsigned char *)buf.pData; + lOutLen = (int)buf.lSize; + if (lOutLen > 16) + lOutLen = 16; + + // create a 64-character formatted output line: + sprintf(szBuf, " > " + " " + " %08lX", pTmp-pAddress); + lOutLen2 = lOutLen; + + for(lIndex = 1+lIndent, lIndex2 = 53-15+lIndent, lRelPos = 0; + lOutLen2; + lOutLen2--, lIndex += 2, lIndex2++ + ) + { + ucTmp = *pTmp++; + + sprintf(szBuf + lIndex, "%02X ", (unsigned short)ucTmp); + if(!isprint(ucTmp)) ucTmp = '.'; // nonprintable char + szBuf[lIndex2] = ucTmp; + + if (!(++lRelPos & 3)) // extra blank after 4 bytes + { lIndex++; szBuf[lIndex+2] = ' '; } + } + + if (!(lRelPos & 3)) lIndex--; + + szBuf[lIndex ] = '<'; + szBuf[lIndex+1] = ' '; + + printf("%s\n", szBuf); + + buf.pData += lOutLen; + buf.lSize -= lOutLen; + } + } +#endif +} diff --git a/pacman-c++/common/util.h b/pacman-c++/common/util.h new file mode 100644 index 0000000..1ec82b3 --- /dev/null +++ b/pacman-c++/common/util.h @@ -0,0 +1,39 @@ +#ifndef UTIL_H +#define UTIL_H + +#include "constants.h" +#include "actor.h" +#include "pacman.pb.h" + +extern "C" { +#include "enet/enet.h" +} + +namespace Util +{ + Transmission::map_t createUninitialisedMap(); + Transmission::map_t createDemoMap(); + void placeActors(Transmission::map_t map, unsigned int players, const Color::Color *colors); + void fillPoints(Transmission::map_t map, Transmission::field_t type = Transmission::point); + Transmission::map_t createEmptyMap(); + void deleteMap(Transmission::map_t map); + + Transmission::field_t actorMovementToTransmission(Actor::Movement mov, + Transmission::field_t def = Transmission::none); + Actor::Movement transmissionMovementToActor(Transmission::field_t field, + Actor::Movement def = Actor::None); + const QString colorToString(Color::Color color); + + /* send packet with error check and flush */ + QSharedPointer createPacket(const ::google::protobuf::MessageLite& packet); + bool sendPacket(QByteArray *data, ENetPeer *peer, ENetHost *host); + bool sendPacket(const ::google::protobuf::MessageLite& packet, ENetPeer *peer, ENetHost *host); + QSharedPointer receivePacket(ENetPacket *packet); + + int floorLog2(unsigned int n); + +#if 0 + void hexdump(void *pAddressIn, long lSize); +#endif +} +#endif // UTIL_H diff --git a/pacman-c++/constants.h b/pacman-c++/constants.h deleted file mode 100644 index e7dedbf..0000000 --- a/pacman-c++/constants.h +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef CONSTANTS_H -#define CONSTANTS_H -#include - -namespace Constants { - const struct - { - const unsigned int width; - const unsigned int height; - } - field_size = { 16, 16 }, - map_size = { 35, 17 }, - map_size_pixel = { field_size.width * map_size.width, - field_size.height * map_size.height }; - const unsigned int sprite_margin = 2; - const unsigned int sprite_offset = 20; - const unsigned int tick = 250; // ms - extern bool server; - - namespace Networking - { - const unsigned int port = 7321; - const unsigned int connection_timeout = 3000; - } - - namespace Game - { - const unsigned int bonus_point_value = 100; - const unsigned int point_value = 10; - /* players will be placed with this minimum manhattan distance */ - const unsigned int player_minimum_distance = 5; - /* if the distance above isn't possible, decrease the distance by this value */ - const unsigned int player_distance_decr = 2; - /* there's a chance of 1:30 that a bonus point will be added (with one actor) */ - const unsigned int bouns_point_chance = 30; - /* every additional player will raise the chance of a bonus point by that factor */ - const unsigned int bouns_point_chance_playerfactor = 3; - /* there's a chance of 1:5 that a block will be colorized */ - const unsigned int colorize_block_chance = 5; - /* how long colorized blocks will stay colorized */ - const unsigned int colorize_block_tickcount_min = 50; - const unsigned int colorize_block_tickcount_max = 100; - } - - namespace AI - { - /* bots minimum manhatten distance before other players will be recognized */ - const unsigned int player_minimum_distance = 4; - /* weight values used to determine new direction of bot */ - const unsigned int weight_afraid = 50; - const unsigned int weight_hunt = 10; - const unsigned int weight_bonus_point = 3; - const unsigned int weight_point = 1; - const unsigned int weight_colorblock = 5; - } -} - -namespace Color -{ - enum Color - { - none = 0, - red = (1 << 0), - blue = (1 << 1), - green = (1 << 2), - yellow = (1 << 3) - }; - - /* colororder used in protocol and gui */ - const Color order[] = - { Color::red, Color::blue, Color::green, Color::yellow, Color::none }; -} - -// constants for data transmission to client -namespace Transmission -{ - typedef unsigned int field_t; - typedef unsigned int mask_t; - - const field_t none = 0; - const field_t block = (1 << 4); - const field_t point = (1 << 5); - const field_t bonuspoint = (1 << 6); - const field_t pacman = (1 << 7); - const field_t empty = (1 << 8); // explicit empty for update - const field_t death = (1 << 9); - - const field_t direction_none = 0; - const field_t direction_left = (1 << 10); - const field_t direction_right = (1 << 11); - const field_t direction_up = (1 << 12); - const field_t direction_down = (1 << 13); - - const mask_t color_mask = Color::none | Color::red | Color::blue | Color::green | Color::yellow; - const mask_t type_mask = block | bonuspoint; - const mask_t direction_mask = direction_none | direction_left | direction_right | direction_up | direction_down; - - typedef field_t** map_t; -} - -#endif // CONSTANTS_H diff --git a/pacman-c++/gameentity.cpp b/pacman-c++/gameentity.cpp deleted file mode 100644 index e73e759..0000000 --- a/pacman-c++/gameentity.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "gameentity.h" - -GameEntity::GameEntity(Color::Color color, QGraphicsItem *parent) - : PixmapItem(parent), m_type(Type), m_color(color) -{} - -GameEntity::GameEntity(QGraphicsItem *parent) - : PixmapItem(parent), m_type(Type), m_color(Color::none) -{} diff --git a/pacman-c++/gameentity.h b/pacman-c++/gameentity.h deleted file mode 100644 index 116fae5..0000000 --- a/pacman-c++/gameentity.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef GAMEENTITY_H -#define GAMEENTITY_H - -#include "constants.h" -#include "pixmapitem.h" -#include -#include - -class Actor; - -/** - * Base class for entities that interact in the game - */ -class GameEntity - : public PixmapItem -{ -public: - enum - { - Type = UserType + 1 - }; - - enum EnteredState - { - Nothing, - DestroyedEntity, - DestroyedActor - }; - -public: - GameEntity(Color::Color color = Color::none, QGraphicsItem *parent = 0); - GameEntity(QGraphicsItem *parent); - virtual ~GameEntity() - {}; - - /* color of entity */ - virtual Color::Color color() - { - return m_color; - } - - /* returns whether the actor may enter this field */ - virtual bool checkEnter(Actor *) - { - return true; - } - - /* performs action when this actor acctually enters - * returns whether this entity survives the entering - */ - virtual EnteredState enter(Actor *) - { - /* default to no action/survive */ - return Nothing; - } - - /* called when an instance acctually dies for creating effects */ - virtual void onDie(Actor *) - {}; - - /* enable the use of qgraphicsitem_cast with this item */ - int type() const - { - return m_type; - } - -protected: - int m_type; - Color::Color m_color; -}; - -#endif // GAMEENTITY_H diff --git a/pacman-c++/mainwidget.cpp b/pacman-c++/mainwidget.cpp deleted file mode 100644 index 49ddf7a..0000000 --- a/pacman-c++/mainwidget.cpp +++ /dev/null @@ -1,279 +0,0 @@ -#include "mainwidget.h" -#include "actor.h" -#include "block.h" -#include "constants.h" -#include "util.h" -#include "pacman.pb.h" -#include - -MainWidget::MainWidget(QWidget *parent) - : QWidget(parent), m_currentKey(Transmission::none), m_running(false), m_scene(NULL), - m_maxplayers(0) -{ - /* create audio player */ - m_ambientPlayer = new GaplessAudioPlayer(Sound::Ambient, 100, this); - - Color::Color color = connectToServer(); - if (color == Color::none) - { - QMessageBox::critical(this, "Error", "Failed to connect to server, falling back to local test mode"); - // TODO: quit application here or sth - return; - } - - /* create our scene */ - m_scene = new SceneHolder(this); - m_scene->setColor(color); - - /* call updateMap after m_color ist set! */ - createGui(); - - /* wait for the server to send the first map update (initial map) - * WARNING: this will block the gui - */ - m_socket->waitForReadyRead(-1); - tick(); - - qDebug() << "[Connect] mycolor=" << m_scene->color(); - connect(m_socket, SIGNAL(readyRead()), this, SLOT(tick())); -} - -bool MainWidget::connected() -{ - return m_socket != NULL; -} - -void MainWidget::createGui() -{ - setFocusPolicy(Qt::StrongFocus); - - /* first one is always the own score */ - QVBoxLayout *layout = new QVBoxLayout(this); - layout->setAlignment(Qt::AlignHCenter | Qt::AlignTop); - QHBoxLayout *scoreLayout = new QHBoxLayout(); - for (unsigned int i = 0; i < m_maxplayers; ++i) - { - QGridLayout *playerLayout = new QGridLayout(); - playerLayout->addWidget(new QLabel("Current:", this), 0, 0); - playerLayout->addWidget(new QLabel("Total:", this), 1, 0); - playerLayout->addWidget(new QLabel("0", this), 0, 1, Qt::AlignLeft); - playerLayout->addWidget(new QLabel("0", this), 1, 1, Qt::AlignLeft); - playerLayout->setColumnStretch(1, 10); - playerLayout->setSizeConstraint(QLayout::SetMinimumSize); - - QGroupBox *scoreBox = new QGroupBox(QString("Player %1").arg(i + 1), this); - scoreBox->setObjectName(QString("actor%1").arg(i + 1)); - scoreBox->setCheckable(true); - scoreBox->setDisabled(i >= m_maxplayers); - connect(scoreBox, SIGNAL(clicked()), this, SLOT(playerScoreClicked())); - - scoreBox->setLayout(playerLayout); - m_playerScoreLayouts.append(playerLayout); - - if (Color::order[i] == m_scene->color()) - scoreLayout->insertWidget(0, scoreBox); - else - scoreLayout->addWidget(scoreBox); - } - layout->addLayout(scoreLayout); - - QGraphicsView *window = new QGraphicsView(m_scene, this); - window->setFrameStyle(0); - window->setFixedSize(m_scene->sceneRect().size().toSize()); - window->setWindowFlags(window->windowFlags() & ~Qt::WindowMaximizeButtonHint); - window->setFocusPolicy(Qt::NoFocus); - layout->addWidget(window, 0, Qt::AlignCenter); - - QFile css(":/stylesheet"); - css.open(QFile::ReadOnly); - qApp->setStyleSheet(QLatin1String(css.readAll())); - - /* add dummy layout at the end which gets streched when resizing */ - QHBoxLayout *spacer = new QHBoxLayout(); - layout->addLayout(spacer, 10); - - setLayout(layout); -} - -void MainWidget::updateScore(const ProtoBuf::MapUpdate& packet) -{ - for(unsigned i = 0; i < m_maxplayers; ++i) - { - QGridLayout *score = m_playerScoreLayouts.at(i); - QLabel *turnPointsLbl = dynamic_cast(score->itemAtPosition(0, 1)->widget()); - turnPointsLbl->setText(QString::number(packet.round_points(i))); - - QLabel *allPointsLbl = dynamic_cast(score->itemAtPosition(1, 1)->widget()); - allPointsLbl->setText(QString::number(packet.game_points(i))); - } -} - -Transmission::field_t MainWidget::translateKey(int key) -{ - switch(key) - { - case Qt::Key_W: - case Qt::Key_Up: - return Transmission::direction_up; - break; - case Qt::Key_S: - case Qt::Key_Down: - return Transmission::direction_down; - break; - case Qt::Key_A: - case Qt::Key_Left: - return Transmission::direction_left; - break; - case Qt::Key_D: - case Qt::Key_Right: - return Transmission::direction_right; - break; - default: - return Transmission::direction_none; - } -} - -void MainWidget::tick() -{ - while (m_socket->bytesAvailable() > (qint64)sizeof(qint64)) - { - QSharedPointer data = Util::receivePacket(m_socket); - bool worked = m_updatepacket.ParseFromArray(data->data(), data->size()); - Q_ASSERT(worked); - Q_UNUSED(worked); - - /* eating order data set indicates a new round */ - if (m_updatepacket.eating_order_size() > 0) - { - Q_ASSERT(m_scene != NULL); - m_scene->reset(); - - /* fetch eating order */ - QList order; - for(int i = 0; i < m_updatepacket.eating_order_size(); ++i) - order.append(static_cast(m_updatepacket.eating_order(i) & Transmission::color_mask)); - m_scene->setEatingOrder(order); - - /* stop game */ - stopGame(); - - /* and restart game */ - QTimer *timer = new QTimer(this); - timer->setSingleShot(true); - timer->setInterval(Sound::length[Sound::Intro] + Constants::tick); - connect(timer, SIGNAL(timeout()), this, SLOT(startGame())); - timer->start(); - AudioManager::self()->play(Sound::Intro, true); - } - - Transmission::map_t map = Util::createUninitialisedMap(); - Q_ASSERT(m_updatepacket.field_size() == (int) (Constants::map_size.width * Constants::map_size.height)); - int i = 0; - for (unsigned int x = 0; x < Constants::map_size.width; ++x) - { - for (unsigned int y = 0; y < Constants::map_size.height; ++y) - { - map[x][y] = m_updatepacket.field(i); - ++i; - } - } - m_scene->updateMap(map); - Util::deleteMap(map); - updateScore(m_updatepacket); - - if (m_updatepacket.eating_order_size() > 0) - m_scene->showEatingText(); - } -} - -void MainWidget::keyPressEvent(QKeyEvent* event) -{ - if (event->isAutoRepeat()) - return; - - QWidget::keyPressEvent(event); - Transmission::field_t newKey = translateKey(event->key()); - if (newKey == Transmission::direction_none) - return; - bool sendUpdate = (m_currentKey != newKey); - m_currentKey = newKey; - if (sendUpdate) - sendKeyUpdate(); -} - -void MainWidget::sendKeyUpdate() -{ - if (m_currentKey == Transmission::direction_none) - return; - qDebug() << "[SendKey] key=" << m_currentKey; - ProtoBuf::KeyPressUpdate packet; - packet.set_newkey(m_currentKey); - Util::sendPacket(packet, m_socket); -} - -void MainWidget::keyReleaseEvent(QKeyEvent* event) -{ - if (event->isAutoRepeat()) - return; - - QWidget::keyReleaseEvent(event); - m_currentKey = Transmission::none; - return; -} - -void MainWidget::startGame() -{ - disconnect(AudioManager::self()->audioPlayer(), NULL, this, SLOT(startGame())); - m_scene->showEatingText(false); - m_running = true; - sendKeyUpdate(); - m_ambientPlayer->play(); -} - -void MainWidget::stopGame() -{ - m_running = false; - m_ambientPlayer->pause(); -} - -void MainWidget::setAmbientMuted(bool muted) -{ - m_ambientPlayer->setMuted(muted); -} - -void MainWidget::playerScoreClicked() -{ - QGroupBox *tmp = qobject_cast(sender()); - tmp->setChecked(true); - return; -} - -Color::Color MainWidget::connectToServer() -{ - /* TODO: check command line arguments for server port */ - const QStringList &args = QCoreApplication::arguments(); - QString srv = args.value(1, "127.0.0.1"); - qDebug() << "[Connect] server=" << srv; - - /* connect to server */ - m_socket = new QTcpSocket(this); - m_socket->connectToHost(srv, Constants::Networking::port); - bool worked = m_socket->waitForConnected(Constants::Networking::connection_timeout); - if (worked) - { - /* additional init: first packet is our color */ - worked = m_socket->waitForReadyRead(); - if (worked) - { - /* receive color */ - QSharedPointer data = Util::receivePacket(m_socket); - ProtoBuf::Init packet; - bool worked = packet.ParseFromArray(data->data(), data->size()); - Q_ASSERT(worked); - Q_UNUSED(worked); - m_maxplayers = packet.maxplayers(); - return static_cast(packet.color() & Transmission::color_mask); - } - } - return Color::none; -} diff --git a/pacman-c++/mainwidget.h b/pacman-c++/mainwidget.h deleted file mode 100644 index d5695ed..0000000 --- a/pacman-c++/mainwidget.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef MAINWIDGET_H -#define MAINWIDGET_H - -#include "sceneholder.h" -#include "constants.h" -#include "audio.h" -#include "pacman.pb.h" -#include -#include -#include - -class Actor; - -class MainWidget - : public QWidget -{ - Q_OBJECT - -public: - MainWidget(QWidget *parent = 0); - bool connected(); - void setAmbientMuted(bool muted); - -protected: - /* handling of current key */ - virtual void keyPressEvent(QKeyEvent *); - virtual void keyReleaseEvent(QKeyEvent *); - -private slots: - void startGame(); - void stopGame(); - void playerScoreClicked(); - void tick(); - void sendKeyUpdate(); - -private: - void createGui(); - void createMenu(); - void updateScore(const ProtoBuf::MapUpdate&); - bool isRunning(); - Color::Color connectToServer(); - - /* GUI elements needed in the progress of the game */ - QList m_playerScoreLayouts; - - /* key currently pressed by user */ - Transmission::field_t m_currentKey; - - /* translate Qt::Key to our key format */ - Transmission::field_t translateKey(int key); - - /* game running */ - bool m_running; - GaplessAudioPlayer *m_ambientPlayer; - - QTcpSocket *m_socket; - SceneHolder *m_scene; - unsigned int m_maxplayers; - - /* allocate as member variable as this packet is large and used often */ - ProtoBuf::MapUpdate m_updatepacket; -}; - -#endif // MAINWIDGET_H diff --git a/pacman-c++/pacman.pro b/pacman-c++/pacman.pro index f5187f9..ed5f303 100644 --- a/pacman-c++/pacman.pro +++ b/pacman-c++/pacman.pro @@ -1,48 +1,7 @@ -TEMPLATE = app -LANGUAGE = C++ -VERSION = 0.1 +TEMPLATE = subdirs +SUBDIRS = proto \ + common \ + client \ + server +CONFIG += ordered -QT += phonon network -SOURCES += pixmapitem.cpp \ - actor.cpp \ - block.cpp \ - client.cpp \ - bonuspoint.cpp \ - mainwidget.cpp \ - point.cpp \ - audio.cpp \ - clicklabel.cpp \ - sceneholder.cpp \ - util.cpp \ - gameentity.cpp -HEADERS += pixmapitem.h \ - actor.h \ - block.h \ - client.h \ - bonuspoint.h \ - mainwidget.h \ - constants.h \ - point.h \ - audio.h \ - clicklabel.h \ - sceneholder.h \ - util.h \ - gameentity.h -RESOURCES += pacman.qrc - -OBJECTS_DIR = .obj -MOC_DIR = .moc -RC_FILE = pacman.rc - -OTHER_FILES += \ - style.qss \ - pacman.rc \ - pacman.proto - -PROTOS = pacman.proto -include(protobuf.pri) -LIBS += -lprotobuf - -CONFIG(release, debug|release) { - DEFINES += QT_NO_DEBUG_OUTPUT -} diff --git a/pacman-c++/pacman.proto b/pacman-c++/pacman.proto deleted file mode 100644 index 8e088e8..0000000 --- a/pacman-c++/pacman.proto +++ /dev/null @@ -1,18 +0,0 @@ -package ProtoBuf; - -message KeyPressUpdate { - required uint32 newKey = 1; -} - -message Init { - required uint32 color = 1; - required uint32 maxplayers = 2; -} - -message MapUpdate { - repeated uint32 field = 1 [packed=true]; - repeated uint32 round_points = 2; - repeated uint32 game_points = 3; - repeated uint32 eating_order = 4; -} - diff --git a/pacman-c++/pacman.qrc b/pacman-c++/pacman.qrc deleted file mode 100644 index 6654fd1..0000000 --- a/pacman-c++/pacman.qrc +++ /dev/null @@ -1,31 +0,0 @@ - - - pics/actor1.png - pics/actor2.png - pics/actor3.png - pics/actor4.png - pics/block0.png - pics/block1.png - pics/block2.png - pics/block3.png - pics/block4.png - pics/bonuspoints.png - pics/points.png - style.qss - pics/app.ico - pics/actor1icon.png - pics/actor2icon.png - pics/actor3icon.png - pics/actor4icon.png - pics/soundon.xpm - pics/soundoff.xpm - - - sound/intro.ogg - sound/waka_waka.ogg - sound/eating_fruit.ogg - sound/eating_ghost.ogg - sound/die.ogg - sound/ambient.ogg - - diff --git a/pacman-c++/pacman.rc b/pacman-c++/pacman.rc deleted file mode 100644 index 8c9f03b..0000000 --- a/pacman-c++/pacman.rc +++ /dev/null @@ -1 +0,0 @@ -IDI_ICON1 ICON DISCARDABLE "pics/app.ico" diff --git a/pacman-c++/pacman.server.pro b/pacman-c++/pacman.server.pro deleted file mode 100644 index 0b4c568..0000000 --- a/pacman-c++/pacman.server.pro +++ /dev/null @@ -1,50 +0,0 @@ -TEMPLATE = app -LANGUAGE = C++ -VERSION = 0.1 -DEFINES += SERVER - -TARGET = pacman-server - -QT += phonon network -SOURCES += pixmapitem.cpp \ - actor.cpp \ - block.cpp \ - server.cpp \ - bonuspoint.cpp \ - point.cpp \ - audio.cpp \ - sceneholder.cpp \ - util.cpp \ - clicklabel.cpp \ - gameentity.cpp \ - anyoption.cpp -HEADERS += pixmapitem.h \ - actor.h \ - block.h \ - server.h \ - bonuspoint.h \ - constants.h \ - point.h \ - audio.h \ - sceneholder.h \ - util.h \ - clicklabel.h \ - gameentity.h \ - anyoption.h -RESOURCES += pacman.qrc - -OBJECTS_DIR = .obj -MOC_DIR = .moc -RC_FILE = pacman.rc - -OTHER_FILES += \ - style.qss \ - pacman.rc - -PROTOS = pacman.proto -include(protobuf.pri) -LIBS += -lprotobuf - -CONFIG(release, debug|release) { - DEFINES += QT_NO_DEBUG_OUTPUT -} diff --git a/pacman-c++/pics/actor1.png b/pacman-c++/pics/actor1.png deleted file mode 100644 index 5a37b4f..0000000 Binary files a/pacman-c++/pics/actor1.png and /dev/null differ diff --git a/pacman-c++/pics/actor1icon.png b/pacman-c++/pics/actor1icon.png deleted file mode 100644 index 4407cef..0000000 Binary files a/pacman-c++/pics/actor1icon.png and /dev/null differ diff --git a/pacman-c++/pics/actor2.png b/pacman-c++/pics/actor2.png deleted file mode 100644 index 736a6e7..0000000 Binary files a/pacman-c++/pics/actor2.png and /dev/null differ diff --git a/pacman-c++/pics/actor2icon.png b/pacman-c++/pics/actor2icon.png deleted file mode 100644 index eaf2a4c..0000000 Binary files a/pacman-c++/pics/actor2icon.png and /dev/null differ diff --git a/pacman-c++/pics/actor3.png b/pacman-c++/pics/actor3.png deleted file mode 100644 index f963cff..0000000 Binary files a/pacman-c++/pics/actor3.png and /dev/null differ diff --git a/pacman-c++/pics/actor3icon.png b/pacman-c++/pics/actor3icon.png deleted file mode 100644 index 2558cbc..0000000 Binary files a/pacman-c++/pics/actor3icon.png and /dev/null differ diff --git a/pacman-c++/pics/actor4.png b/pacman-c++/pics/actor4.png deleted file mode 100644 index e500dc5..0000000 Binary files a/pacman-c++/pics/actor4.png and /dev/null differ diff --git a/pacman-c++/pics/actor4icon.png b/pacman-c++/pics/actor4icon.png deleted file mode 100644 index 82bf637..0000000 Binary files a/pacman-c++/pics/actor4icon.png and /dev/null differ diff --git a/pacman-c++/pics/app.ico b/pacman-c++/pics/app.ico deleted file mode 100644 index 4a73cd4..0000000 Binary files a/pacman-c++/pics/app.ico and /dev/null differ diff --git a/pacman-c++/pics/block0.png b/pacman-c++/pics/block0.png deleted file mode 100644 index 76bc4b7..0000000 Binary files a/pacman-c++/pics/block0.png and /dev/null differ diff --git a/pacman-c++/pics/block1.png b/pacman-c++/pics/block1.png deleted file mode 100644 index 99e8633..0000000 Binary files a/pacman-c++/pics/block1.png and /dev/null differ diff --git a/pacman-c++/pics/block2.png b/pacman-c++/pics/block2.png deleted file mode 100644 index 963207e..0000000 Binary files a/pacman-c++/pics/block2.png and /dev/null differ diff --git a/pacman-c++/pics/block3.png b/pacman-c++/pics/block3.png deleted file mode 100644 index 6662de4..0000000 Binary files a/pacman-c++/pics/block3.png and /dev/null differ diff --git a/pacman-c++/pics/block4.png b/pacman-c++/pics/block4.png deleted file mode 100644 index 00fbd74..0000000 Binary files a/pacman-c++/pics/block4.png and /dev/null differ diff --git a/pacman-c++/pics/bonuspoints.png b/pacman-c++/pics/bonuspoints.png deleted file mode 100644 index b5714c9..0000000 Binary files a/pacman-c++/pics/bonuspoints.png and /dev/null differ diff --git a/pacman-c++/pics/pacman10-hp-sprite-2.png b/pacman-c++/pics/pacman10-hp-sprite-2.png deleted file mode 100644 index 7354b9d..0000000 Binary files a/pacman-c++/pics/pacman10-hp-sprite-2.png and /dev/null differ diff --git a/pacman-c++/pics/points.png b/pacman-c++/pics/points.png deleted file mode 100644 index 6ba4496..0000000 Binary files a/pacman-c++/pics/points.png and /dev/null differ diff --git a/pacman-c++/pics/soundoff.xpm b/pacman-c++/pics/soundoff.xpm deleted file mode 100644 index ba33f5f..0000000 --- a/pacman-c++/pics/soundoff.xpm +++ /dev/null @@ -1,15 +0,0 @@ -/* XPM */ -static char * soundoff_xpm[] = { -"12 10 2 1", -" c None", -". c #000000", -" ", -" . ", -" .. . .", -".... . . ", -".... .. ", -".... .. ", -".... . . ", -" .. . .", -" . ", -" "}; diff --git a/pacman-c++/pics/soundon.xpm b/pacman-c++/pics/soundon.xpm deleted file mode 100644 index e6ca41e..0000000 --- a/pacman-c++/pics/soundon.xpm +++ /dev/null @@ -1,15 +0,0 @@ -/* XPM */ -static char * soundoff_xpm[] = { -"12 10 2 1", -" c None", -". c #000000", -" . ", -" . . ", -" .. . . ", -".... . . ", -".... . . ", -".... . . ", -".... . . ", -" .. . . ", -" . . ", -" . "}; diff --git a/pacman-c++/pixmapitem.cpp b/pacman-c++/pixmapitem.cpp deleted file mode 100644 index 1ceeec1..0000000 --- a/pacman-c++/pixmapitem.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "pixmapitem.h" -#include - -PixmapItem::PixmapItem(QGraphicsItem *parent) - : QGraphicsObject(parent), m_x(0), m_y(0) -{ -} - -PixmapItem::PixmapItem(const QString &fileName, QGraphicsItem *parent) - : QGraphicsObject(parent), m_x(0), m_y(0) -{ - m_pix = ":/" + fileName; - m_width = m_pix.width(); - m_height = m_pix.height(); -} - -PixmapItem::PixmapItem(const QString &fileName, QGraphicsScene *scene) - : QGraphicsObject(), m_x(0), m_y(0) -{ - m_pix = ":/" + fileName; - m_width = m_pix.width(); - m_height = m_pix.height(); - scene->addItem(this); -} - -PixmapItem::PixmapItem(const QPixmap &pix, QGraphicsItem *parent) - : QGraphicsObject(parent), m_pix(pix), m_x(0), m_y(0) -{ - m_width = m_pix.width(); - m_height = m_pix.height(); -} - -PixmapItem::PixmapItem(const QPixmap &pix, QGraphicsScene *scene) - : QGraphicsObject(), m_pix(pix), m_x(0), m_y(0) -{ - m_width = m_pix.width(); - m_height = m_pix.height(); - scene->addItem(this); -} - -void PixmapItem::setPixmap(const QPixmap &pix) -{ - m_pix = pix; - m_width = m_pix.width(); - m_height = m_pix.height(); -} - -void PixmapItem::setSprite(int x, int y, int width, int height) -{ - m_x = x; - m_y = y; - m_width = width; - m_height = height; -} - -QSizeF PixmapItem::size() const -{ - return QSizeF(m_width, m_height); -} - -QRectF PixmapItem::boundingRect() const -{ - return QRectF(QPointF(0, 0), size()); -} - -QPainterPath PixmapItem::shape() const -{ - QPainterPath path; - path.addRect(boundingRect()); - return path; -} - -void PixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) -{ - painter->drawPixmap(QPoint(0, 0), m_pix, QRect(m_x, m_y, m_width, m_height)); -} - diff --git a/pacman-c++/pixmapitem.h b/pacman-c++/pixmapitem.h deleted file mode 100644 index f57c22a..0000000 --- a/pacman-c++/pixmapitem.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef PIXMAPITEM__H -#define PIXMAPITEM__H - -#include -#include - -class PixmapItem - : public QGraphicsObject -{ -public: - PixmapItem(QGraphicsItem *parent = 0); - PixmapItem(const QString &fileName, QGraphicsItem *parent = 0); - PixmapItem(const QString &fileName, QGraphicsScene *scene); - PixmapItem(const QPixmap &pix, QGraphicsItem *parent = 0); - PixmapItem(const QPixmap &pix, QGraphicsScene *scene); - virtual ~PixmapItem() - {}; - - void setPixmap(const QPixmap &pix); - void setSprite(int x, int y, int width, int height); - QSizeF size() const; - QRectF boundingRect() const; - QPainterPath shape() const; - void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); - -private: - QPixmap m_pix; - int m_x, m_y; - int m_width, m_height; -}; - -#endif diff --git a/pacman-c++/point.cpp b/pacman-c++/point.cpp deleted file mode 100644 index d7ebdb1..0000000 --- a/pacman-c++/point.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "point.h" -#include "constants.h" -#include "actor.h" - -namespace -{ - QPixmap *pixmap = NULL; -} - -Point::Point(QGraphicsItem *parent) - : GameEntity(parent) -{ - m_type = Type; - - /* empty object for servers */ - if (Constants::server) - return; - - if (pixmap == NULL) - pixmap = new QPixmap(":/points"); - setPixmap(*pixmap); -} - -GameEntity::EnteredState Point::enter(Actor *actor) -{ - actor->addRoundPoints(Constants::Game::point_value); - return DestroyedEntity; -} - -void Point::onDie(Actor *actor) -{ - actor->startEating(); -} diff --git a/pacman-c++/point.h b/pacman-c++/point.h deleted file mode 100644 index 7739554..0000000 --- a/pacman-c++/point.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef POINT_H -#define POINT_H - -#include "gameentity.h" - -class Point - : public GameEntity -{ -public: - enum - { - Type = UserType + Transmission::point - }; - -public: - Point(QGraphicsItem *parent=0); - virtual ~Point() - {}; - - virtual EnteredState enter(Actor *actor); - virtual void onDie(Actor *actor); -}; - -#endif // POINT_H diff --git a/pacman-c++/proto/pacman.proto b/pacman-c++/proto/pacman.proto new file mode 100644 index 0000000..8e088e8 --- /dev/null +++ b/pacman-c++/proto/pacman.proto @@ -0,0 +1,18 @@ +package ProtoBuf; + +message KeyPressUpdate { + required uint32 newKey = 1; +} + +message Init { + required uint32 color = 1; + required uint32 maxplayers = 2; +} + +message MapUpdate { + repeated uint32 field = 1 [packed=true]; + repeated uint32 round_points = 2; + repeated uint32 game_points = 3; + repeated uint32 eating_order = 4; +} + diff --git a/pacman-c++/proto/proto.pro b/pacman-c++/proto/proto.pro new file mode 100644 index 0000000..7c0b334 --- /dev/null +++ b/pacman-c++/proto/proto.pro @@ -0,0 +1,9 @@ +TEMPLATE = lib +CONFIG += staticlib + +include(../common.pri) + +PROTOS = pacman.proto +include(protobuf.pri) +LIBS += -lprotobuf +OTHER_FILES += $${PROTOS} diff --git a/pacman-c++/proto/protobuf.pri b/pacman-c++/proto/protobuf.pri new file mode 100644 index 0000000..718cb70 --- /dev/null +++ b/pacman-c++/proto/protobuf.pri @@ -0,0 +1,33 @@ +# +# Qt qmake integration with Google Protocol Buffers compiler protoc +# +# To compile protocol buffers with qt qmake, specify PROTOS variable and +# include this file +# +# Example: +# PROTOS = a.proto b.proto +# include(protobuf.pri) +# +# By default protoc looks for .proto files (including the imported ones) in +# the current directory where protoc is run. If you need to include additional +# paths specify the PROTOPATH variable +# + +PROTOPATH += . +PROTOPATHS = +for(p, PROTOPATH):PROTOPATHS += --proto_path=$${p} + +protobuf_decl.name = protobuf header +protobuf_decl.input = PROTOS +protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h +protobuf_decl.commands = protoc --cpp_out="." $${PROTOPATHS} $(INCPATH) ${QMAKE_FILE_IN} +protobuf_decl.variable_out = GENERATED_FILES +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf implementation +protobuf_impl.input = PROTOS +protobuf_impl.output = ${QMAKE_FILE_BASE}.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h +protobuf_impl.commands = $$escape_expand(\\n) +protobuf_impl.variable_out = GENERATED_SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl diff --git a/pacman-c++/protobuf.pri b/pacman-c++/protobuf.pri deleted file mode 100644 index 718cb70..0000000 --- a/pacman-c++/protobuf.pri +++ /dev/null @@ -1,33 +0,0 @@ -# -# Qt qmake integration with Google Protocol Buffers compiler protoc -# -# To compile protocol buffers with qt qmake, specify PROTOS variable and -# include this file -# -# Example: -# PROTOS = a.proto b.proto -# include(protobuf.pri) -# -# By default protoc looks for .proto files (including the imported ones) in -# the current directory where protoc is run. If you need to include additional -# paths specify the PROTOPATH variable -# - -PROTOPATH += . -PROTOPATHS = -for(p, PROTOPATH):PROTOPATHS += --proto_path=$${p} - -protobuf_decl.name = protobuf header -protobuf_decl.input = PROTOS -protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h -protobuf_decl.commands = protoc --cpp_out="." $${PROTOPATHS} $(INCPATH) ${QMAKE_FILE_IN} -protobuf_decl.variable_out = GENERATED_FILES -QMAKE_EXTRA_COMPILERS += protobuf_decl - -protobuf_impl.name = protobuf implementation -protobuf_impl.input = PROTOS -protobuf_impl.output = ${QMAKE_FILE_BASE}.pb.cc -protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h -protobuf_impl.commands = $$escape_expand(\\n) -protobuf_impl.variable_out = GENERATED_SOURCES -QMAKE_EXTRA_COMPILERS += protobuf_impl diff --git a/pacman-c++/rebuild-client.sh b/pacman-c++/rebuild-client.sh deleted file mode 100755 index 17cf206..0000000 --- a/pacman-c++/rebuild-client.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -#make distclean -qmake CONFIG+=DEBUG pacman.pro -make -j2 diff --git a/pacman-c++/rebuild-server.sh b/pacman-c++/rebuild-server.sh deleted file mode 100755 index 8d2adb9..0000000 --- a/pacman-c++/rebuild-server.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -#make distclean -qmake CONFIG+=DEBUG pacman.server.pro -o Makefile.server -make -f Makefile.server -j2 diff --git a/pacman-c++/rebuild.sh b/pacman-c++/rebuild.sh new file mode 100755 index 0000000..58ec7c4 --- /dev/null +++ b/pacman-c++/rebuild.sh @@ -0,0 +1,4 @@ +#!/bin/sh +#make distclean +qmake CONFIG+=debug pacman.pro +make -j2 diff --git a/pacman-c++/sceneholder.cpp b/pacman-c++/sceneholder.cpp deleted file mode 100644 index 51496ca..0000000 --- a/pacman-c++/sceneholder.cpp +++ /dev/null @@ -1,336 +0,0 @@ -#include "sceneholder.h" -#include "constants.h" -#include "gameentity.h" -#include "block.h" -#include "actor.h" -#include "bonuspoint.h" -#include "point.h" -#include "util.h" - -SceneHolder::SceneHolder(QObject *parent) - : QGraphicsScene(parent), m_color(Color::none), m_pointsLeft(0) -{ - setSceneRect(0, 0, Constants::map_size_pixel.width, Constants::map_size_pixel.height); - setBackgroundBrush(Qt::black); - - m_overlayText = new QGraphicsTextItem(); - - visualMap.resize(Constants::map_size.width); - for (int i = 0; i < visualMap.size(); ++i) - visualMap[i].resize(Constants::map_size.height); -} - -void SceneHolder::reset() -{ - processDelayedItems(); - showEatingText(false); - - /* remove actors from scene so they don't get deleted during clear */ - foreach(Actor *actor, m_actors) - { - actor->reset(); - removeItem(actor); - } - - /* clear our stuff */ - clear(); - m_pointsLeft = 0; - for (int i = 0; i < visualMap.size(); ++i) - { - visualMap[i].clear(); - visualMap[i].resize(Constants::map_size.height); - } - - /* add actors again */ - foreach(Actor *actor, m_actors) - addItem(actor); -} - -void SceneHolder::processDelayedItems() -{ - /* remove items that got marked for removal from scene */ - foreach(GameEntity *item, m_oldItems) - { - removeItem(item); - delete item; - } - m_oldItems.clear(); - - /* process death */ - foreach(const Color::Color color, m_death.keys()) - { - Q_ASSERT(m_death[color] != NULL); - m_death[color]->onDie(m_actors[color]); - } - m_death.clear(); -} - -void SceneHolder::updateMap(const Transmission::map_t& map) -{ - processDelayedItems(); - - /* process update */ - for (unsigned int x = 0; x < Constants::map_size.width; ++x) - { - for (unsigned int y = 0; y < Constants::map_size.height; ++y) - { - const Transmission::field_t &cur = map[x][y]; - if (cur == Transmission::none) - continue; - updateMap(map, x, y); - } - } - - if (m_pointsLeft == 0) - emit allPointsRemoved(); -} - -void SceneHolder::updateMap(const Transmission::map_t& map, const unsigned int x, unsigned int y) -{ - const Transmission::field_t &cur = map[x][y]; - if (cur == Transmission::none) - return; - - /* we may have multiple colors in one position (e.g during eating another pacman) */ - Color::Color color = static_cast(cur & Transmission::color_mask); - QList colors; - if (color == Color::none) - colors.append(Color::none); - foreach(Color::Color col, m_eatingorder.toSet()) - { - if (color & col) - colors.append(col); - } - Q_ASSERT(colors.count() > 0); - - /* for now complain if there are more colors or it's a Transmission::death packet */ - Q_ASSERT(colors.count() == 1 || cur & Transmission::death); - - if (cur & Transmission::empty) - { - GameEntity *oldItem = visualMap[x][y]; - /* special handling for purging field */ - if (oldItem != NULL) - { - /* remove item from visualmap and register item for removal in next update */ - visualMap[x][y] = NULL; - m_oldItems.append(oldItem); - - /* an item must be removed by an actor */ - Actor *actor = m_actors[colors.at(0)]; - Q_ASSERT(actor != NULL); - oldItem->onDie(actor); - } - } - - GameEntity *item = NULL; - if (cur == Transmission::none) - { - /* no update */ - } - else - { - if (cur & Transmission::block) - { - unsigned int neighbours = Block::None; - /* check for old block first */ - if (visualMap[x][y] != NULL) - { - Block *oldItem = qgraphicsitem_cast(visualMap[x][y]); - if (oldItem != NULL) - neighbours = oldItem->neighbours(); - } - /* check left side */ - if (x > 0 && map[x - 1][y] & Transmission::block) - neighbours |= Block::Left; - /* check right side */ - if (x < Constants::map_size.width && map[x + 1][y] & Transmission::block) - neighbours |= Block::Right; - /* check upside */ - if (y > 0 && map[x][y - 1] & Transmission::block) - neighbours |= Block::Up; - /* check down side */ - if (y < Constants::map_size.height && map[x][y + 1] & Transmission::block) - neighbours |= Block::Down; - item = new Block(colors.at(0), neighbours); - } - - if (cur & Transmission::bonuspoint) - item = new BonusPoint(); - - if (cur & Transmission::point) - { - item = new Point(); - connect(item, SIGNAL(destroyed()), this, SLOT(decrementPoints())); - ++m_pointsLeft; - } - - if (cur & Transmission::pacman) - { - foreach(color, colors) - { - /* WARNING: do NOT add actor to visualMap as visualMap-items may - * get deleted during update and actors are referenced in_mactors too - * if you REALLY need that you need to changed updateMap so that all actors - * will be moved before any new items get allocated (totally untested) - */ - Actor *actor = m_actors.value(color, NULL); - if (actor == NULL) - { - actor = new Actor(color, (color == m_color)); - m_actors[color] = actor; - addItem(actor); - actor->setPos(mapPositionToCoord(x, y)); - actor->hadReset(); - } - else - { - /* check for death */ - if (cur & Transmission::death) - { - foreach(Color::Color col, colors) - { - if (color == col) - continue; - if (m_actors[col]->canEat(actor, m_eatingorder)) - { - m_death[col] = actor; - break; - } - } - } - - /* move actor */ - if (actor->hadReset()) - actor->setPos(mapPositionToCoord(x, y)); - else - actor->move(mapPositionToCoord(x, y)); - - /* that's kind a hack but working right now */ - if (!(cur & Transmission::empty)) - actor->stopEating(); - qDebug() << "[SceneUpdate] actor moves: color=" << color - << "newpos=" << QPoint(x, y); - } - - if ((cur & Transmission::death) && visualMap[x][y] != NULL) - m_death[color] = visualMap[x][y]; - } - } - - if (cur & Transmission::empty) - { - /* already handled */ - } - } - - /* add new created item to scene - * remove old item on that location if there's one - */ - if (item != NULL) - { - GameEntity *oldItem = visualMap[x][y]; - if (oldItem != NULL) - { - removeItem(oldItem); - delete oldItem; - } - - addItem(item); - item->setPos(mapPositionToCoord(x, y)); - visualMap[x][y] = item; - } -} - -void SceneHolder::setColor(Color::Color color) -{ - m_color = color; -} - -Color::Color SceneHolder::color() -{ - return m_color; -} - -unsigned int SceneHolder::pointsLeft() -{ - return m_pointsLeft; -} - -void SceneHolder::decrementPoints() -{ - --m_pointsLeft; -} - -void SceneHolder::setEatingOrder(QList &order) -{ - m_eatingorder = order; -} - -QList &SceneHolder::eatingOrder() -{ - return m_eatingorder; -} - -void SceneHolder::showEatingText(bool show) -{ - if (!show) - { - if (m_overlayText->scene() == this) - removeItem(m_overlayText); - return; - } - - m_overlayText->setDefaultTextColor(Qt::black); - QString text = QString( - "
" - "
" - ); - unsigned int lines = 1; - - text = text % QString("Your Pacman:

") - .arg(m_actors[m_color]->iconStr()); - lines += 2; - - foreach(Actor *actor, m_actors) - { - foreach(Actor *other, m_actors) - { - if (!actor->canEat(other, m_eatingorder)) - continue; - text = text % QString("  can eat
") - .arg(actor->iconStr(), other->iconStr()); - ++lines; - } - } - text = text % "
"; - m_overlayText->setHtml(text); - m_overlayText->setTextWidth(150); - m_overlayText->setOpacity(0.9); - - QFontMetrics metrics(m_overlayText->font()); - m_overlayText->setPos((width() - m_overlayText->textWidth()) / 2, (height() - metrics.height() * lines) / 2); - m_overlayText->setZValue(100); - - addItem(m_overlayText); -} - -QPoint SceneHolder::mapPositionToCoord(unsigned int x, unsigned int y) -{ - return QPoint(x * Constants::field_size.width, y * Constants::field_size.height); -} - -QPoint SceneHolder::mapPositionToCoord(QPoint point) -{ - return mapPositionToCoord(point.x(), point.y()); -} - -QPoint SceneHolder::CoordToMapPosition(unsigned int x, unsigned int y) -{ - return QPoint(x / Constants::field_size.width, y / Constants::field_size.height); -} - -QPoint SceneHolder::CoordToMapPosition(QPoint point) -{ - return CoordToMapPosition(point.x(), point.y()); -} diff --git a/pacman-c++/sceneholder.h b/pacman-c++/sceneholder.h deleted file mode 100644 index 7d6e556..0000000 --- a/pacman-c++/sceneholder.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef SCENEHOLDER_H -#define SCENEHOLDER_H - -#include "constants.h" -#include - -class GameEntity; -class Actor; - -class SceneHolder - : public QGraphicsScene -{ - Q_OBJECT - -public: - SceneHolder(QObject *parent = 0); - virtual ~SceneHolder() - {}; - void reset(); - unsigned int pointsLeft(); - void updateMap(const Transmission::map_t& map); - void updateMap(const Transmission::map_t& map, const unsigned int x, const unsigned int y); - void setColor(Color::Color color = Color::none); - Color::Color color(); - void setEatingOrder(QList &order); - QList &eatingOrder(); - void showEatingText(bool show = true); - -signals: - void allPointsRemoved(); - -private slots: - void decrementPoints(); - -protected: - /* process items that got delayed by one tick */ - void processDelayedItems(); - /* data conversion */ - QPoint mapPositionToCoord(unsigned int x, unsigned int y); - QPoint mapPositionToCoord(QPoint point); - QPoint CoordToMapPosition(unsigned int x, unsigned int y); - QPoint CoordToMapPosition(QPoint point); - -protected: - /* map of all pixmap instances */ - QVector< QVector > visualMap; - - /* map of actors in order to keep track of those instances */ - QMap m_actors; - - /* items that got removed/has been eaten - * must be remove one tick later - */ - QList m_oldItems; - /* we need to store items that killed an actor too (e.g. colored blocks) */ - QMap m_death; - - /* my local color */ - Color::Color m_color; - - /* a actor can only eat his upper neighbour - * the order of the neighbours is determined by the colors order (sent from server) - * please note that !pl1.canEat(pl2, ...) doesn't necessarily mean that pl2 can eat pl1 - * order MUST be in this format: [col1, [col2 ... colN], col1] - */ - QList m_eatingorder; - - /* points left before round ends */ - unsigned int m_pointsLeft; - QGraphicsTextItem *m_overlayText; -}; - -#endif // SCENEHOLDER_H diff --git a/pacman-c++/server.cpp b/pacman-c++/server.cpp deleted file mode 100644 index 2311c81..0000000 --- a/pacman-c++/server.cpp +++ /dev/null @@ -1,900 +0,0 @@ -#include "server.h" -#include "util.h" -#include "pacman.pb.h" -#include "block.h" -#include "anyoption.h" -#include "bonuspoint.h" -#include "point.h" -#include -#include -#include -#include - -Server::Server(QWidget *parent) - : SceneHolder(parent), m_bindaddress(QHostAddress::Any), - m_port(Constants::Networking::port), m_numbots(0), - m_rounds(3), m_curRound(0), m_running(false), m_finishRound(false) -{ - /* determine max players by using order array */ - for(m_maxplayers = 0; Color::order[m_maxplayers] != Color::none; ++m_maxplayers); -} - -bool Server::run() -{ - /* create eating order list first - this can also be created dynamically per round (theoretically) */ - for(unsigned int i = 0; i < m_maxplayers; ++i) - m_eatingorder.append(Color::order[i]); - m_eatingorder.append(m_eatingorder.at(0)); - - m_tickTimer = new QTimer(this); - m_tickTimer->setInterval(Constants::tick); - connect(m_tickTimer, SIGNAL(timeout()), this, SLOT(tick())); - - std::cout << "[Server] Running server..." << std::endl - << "[Server] Max players: " << m_maxplayers << std::endl - << "[Server] Number of bots: " << m_numbots << std::endl - << "[Server] Number of rounds: " << m_rounds << std::endl; - if (!waitForClientConnections()) - return false; - - connect(this, SIGNAL(allPointsRemoved()), this, SLOT(setFinishRound())); - initRoundMap(); - return true; -} - -void Server::tick() -{ - //qDebug() << "[Tick] Doing server update"; - if (m_finishRound) - stopGame(true); - if (!m_running) - { - Transmission::map_t map = Util::createEmptyMap(); - sendUpdate(map); - Util::deleteMap(map); - return; - } - - /* let the bots move */ - foreach (Color::Color color, m_bots) - botCalculate(m_actors[color]); - - /* move on the virtual map */ - Transmission::map_t map = calculateUpdates(); - updateMap(map); - - /* add a random bonus point */ - QPoint pos = addRandomPoint(map, Transmission::bonuspoint); - if (!pos.isNull()) - updateMap(map, pos.x(), pos.y()); - - /* add/remove random colorized block */ - if (this->property("coloredblocks").toBool()) - colorizeBlocks(map); - - sendUpdate(map); - Util::deleteMap(map); -} - -Transmission::map_t Server::calculateUpdates() -{ - QMap > movements; - Transmission::map_t map = Util::createEmptyMap(); - - QMutableMapIterator i(m_actorMovements); - while (i.hasNext()) - { - i.next(); - Color::Color color = i.key(); - Actor *actor = m_actors[color]; - QPoint mapPosition = CoordToMapPosition(actor->pos().toPoint()); - Actor::Movement direction = i.value(); - int turn = 0; - -invalid_direction: - ++turn; - qDebug() << "[Calc] Actor wants to move: color=" << color - << "pos=" << mapPosition << "direction=" << direction; - - QPoint newMapPosition = mapPosition + Actor::movementToPoint(direction); - if (newMapPosition.x() < 0) - newMapPosition.setX(0); - if (newMapPosition.x() >= visualMap.size()) - newMapPosition.setX(visualMap.size() - 1); - if (newMapPosition.y() < 0) - newMapPosition.setY(0); - if (newMapPosition.y() >= visualMap[newMapPosition.x()].size()) - newMapPosition.setY(visualMap[newMapPosition.x()].size() - 1); - - /* check if there's an item at new location of actor */ - GameEntity *item = visualMap[newMapPosition.x()][newMapPosition.y()]; - GameEntity *oldItem = visualMap[mapPosition.x()][mapPosition.y()]; - if (item != NULL && oldItem != item) - { - qDebug() << "[Calc] Found item at new actor location"; - if (!item->checkEnter(actor)) - { - /* movement invalid. e.g. move against wall */ - newMapPosition = mapPosition; - qDebug() << "[Calc] Item blocks actor"; - } - else - { - /* apply actions of entering this field */ - GameEntity::EnteredState survive = item->enter(actor); - if (survive == GameEntity::DestroyedEntity) - map[newMapPosition.x()][newMapPosition.y()] = Transmission::empty | actor->color(); - else if (survive == GameEntity::DestroyedActor) - { - map[newMapPosition.x()][newMapPosition.y()] = Transmission::death | actor->color(); - m_actors[item->color()]->addRoundPoints(actor->getRoundPoints()); - actor->finishRound(true); - setFinishRound(); - } - } - } - - /* movement didn't work - e.g. was blocked */ - if (mapPosition == newMapPosition) - { - /* check turn */ - if (turn == 1 && direction != actor->direction()) - { - /* set direction back to last known direction and try again */ - qDebug() << "[Calc] Movement was blocked. Trying last known actor direction"; - direction = actor->direction(); - goto invalid_direction; - } - else - { - /* second turn didn't work too -> stop movement */ - direction = Actor::None; - qDebug() << "[Calc] No good direction known. Movement stopped"; - } - } - - /* store movement for collision */ - movements.insert(color, QPair(mapPosition, newMapPosition)); - - /* set direction (used for collision detection) */ - actor->setDirection(direction); - - /* DEBUG: uncomments to disable auto-movement */ - //direction = Actor::None; - - /* actor is not moving anymore: remove from list */ - if (direction == Actor::None) - i.remove(); - } - - /* check for actor collision */ - QList blocked; - foreach(Color::Color color, movements.keys()) - { - Actor *actor = m_actors[color]; - QPoint oldpos = movements[color].first; - QPoint newpos = movements[color].second; - QPoint scenepos = actor->pos().toPoint(); - - /* first move actor to new position */ - actor->move(actor->direction()); - - /* next check for collisions */ - Actor *orderer = NULL; - Actor *meal = NULL; - foreach(Actor *other, m_actors) - { - if (actor == other) - continue; - if (!actor->collidesWithItem(other)) - continue; - /* both move in the same direction */ - if (actor->direction() == other->direction()) - continue; - - if (other->canEat(actor, m_eatingorder)) - { - qDebug() << "[Collision] Actor" << actor->color() << "got EATEN by" << other->color(); - orderer = other; - meal = actor; - break; - } - else if (actor->canEat(other, m_eatingorder)) - { - qDebug() << "[Collision] Actor" << actor->color() << "EATS" << other->color(); - orderer = actor; - meal = other; - blocked.append(other); - break; - } - else - { - qDebug() << "[Collision] Actor" << actor->color() << "got BLOCKED by" << other->color(); - blocked.append(actor); - /* no break here */ - } - } - - /* update map depending on collision */ - if (orderer != NULL && meal != NULL) - { - map[newpos.x()][newpos.y()] |= Transmission::pacman | Transmission::death - | orderer->color() | meal->color(); - orderer->addRoundPoints(meal->getRoundPoints()); - meal->finishRound(true); - setFinishRound(); - } - else if (blocked.contains(actor)) - { - /* move actor back early cause he won't move */ - actor->setPos(scenepos); - map[oldpos.x()][oldpos.y()] |= Transmission::pacman | color; - } - else - { - map[newpos.x()][newpos.y()] |= Transmission::pacman | color; - } - } - - /* move all actors back to their origin position */ - foreach(Color::Color color, movements.keys()) - m_actors[color]->setPos(mapPositionToCoord(movements[color].first)); - -#ifndef QT_NO_DEBUG - /* collision sanity check: colors must be unique */ - foreach(Color::Color col, m_eatingorder.toSet()) - { - QList found; - for (unsigned int x = 0; x < Constants::map_size.width; ++x) - { - for (unsigned int y = 0; y < Constants::map_size.height; ++y) - { - if ((map[x][y] & Transmission::color_mask) & col) - found.append(QPoint(x, y)); - } - } - if (found.count() > 1) - qCritical() << "[Collision] found" << found.count() << "fields with color=" << col; - } -#endif - - return map; -} - -QPoint Server::addRandomPoint(Transmission::map_t map, Transmission::field_t type) -{ - int chance = Constants::Game::bouns_point_chance - - Constants::Game::bouns_point_chance_playerfactor * (m_maxplayers - 1); - int rand = (int) (chance * (qrand() / (RAND_MAX + 1.0))); - if (rand != 0) - return QPoint(); - - /* get list of valid positions */ - QList validpos; - for (unsigned int x = 0; x < Constants::map_size.width; ++x) - { - for (unsigned int y = 0; y < Constants::map_size.height; ++y) - { - if (visualMap[x][y] == NULL) - validpos.append(QPoint(x, y)); - } - } - - /* remove actors positions from list - * performance would be better if actors would be listed in visualMap too - * but this isn't possible that easily. see comment in updateMap(map) - */ - foreach (Actor *actor, m_actors) - { - QPoint actorpos = CoordToMapPosition(actor->pos().toPoint()); - validpos.removeAll(actorpos); - } - - if (validpos.empty()) - return QPoint(); - - rand = (int) (validpos.size() * (qrand() / (RAND_MAX + 1.0))); - QPoint pos = validpos.at(rand); - map[pos.x()][pos.y()] = type; - return pos; -} - -void Server::colorizeBlocks(Transmission::map_t map) -{ - /* decrement tickcount of colored blocks */ - if (!m_coloredBlocks.empty()) - { - QMutableMapIterator i(m_coloredBlocks); - while(i.hasNext()) - { - i.next(); - unsigned val = i.value(); - if (val > 0) - i.setValue(--val); - else - { - QPoint block = i.key(); - /* check for actor collision */ - bool skip = false; - foreach (Actor *actor, m_actors) - { - if (CoordToMapPosition(actor->pos().toPoint()) == block) - skip = true; - if (skip) - break; - } - if (skip) - continue; - - map[block.x()][block.y()] |= Transmission::block | Color::none; - updateMap(map, block.x(), block.y()); - m_blocks.append(block); - i.remove(); - } - } - } - - /* colorize a random block */ - int rand = (int) (Constants::Game::colorize_block_chance * (qrand() / (RAND_MAX + 1.0))); - if (rand == 0) - { - rand = (int) (m_blocks.size() * (qrand() / (RAND_MAX + 1.0))); - QPoint block = m_blocks.at(rand); - m_blocks.removeAt(rand); - - unsigned int min = Constants::Game::colorize_block_tickcount_min; - unsigned int max = Constants::Game::colorize_block_tickcount_max; - int tickcount = min + (int) ((max - min + 1) * (qrand() / (RAND_MAX + 1.0))); - m_coloredBlocks.insert(block, tickcount); - - unsigned int color = (int) (m_maxplayers * (qrand() / (RAND_MAX + 1.0))); - map[block.x()][block.y()] |= Transmission::block | Color::order[color]; - updateMap(map, block.x(), block.y()); - } -} - -bool Server::waitForClientConnections() -{ - // server must stay alive as long as sockets (qt parent mem mechanism) - QTcpServer *tcpSrv = new QTcpServer(this); - tcpSrv->listen(m_bindaddress, m_port); - if (!tcpSrv->isListening()) - { - qCritical() << "Error while creating socket:" << qPrintable(tcpSrv->errorString()); - return false; - } - std::cout << "[Server] Listening on: " - << qPrintable(QString("%1:%2").arg(tcpSrv->serverAddress().toString()) - .arg(tcpSrv->serverPort())) << std::endl; - - std::cout << "[Server] Waiting for clients" << std::endl; - ProtoBuf::Init packet; - packet.set_maxplayers(m_maxplayers); - for (unsigned int i = 0; i < (m_maxplayers - m_numbots); ++i) - { - bool connectionAvailable = tcpSrv->waitForNewConnection(-1); - Q_ASSERT(connectionAvailable); - Q_UNUSED(connectionAvailable); - QTcpSocket *socket = tcpSrv->nextPendingConnection(); - connect(socket, SIGNAL(readyRead()), this, SLOT(keyPressUpdate())); - - /* assign color and notify client */ - Color::Color color = Color::order[i]; - m_clientConnections[color] = socket; - packet.set_color(color); - Util::sendPacket(packet, socket); - std::cout << "[Connect] New Player: color=" << qPrintable(Util::colorToString(color)) << std::endl; - } - - for (unsigned int i = (m_maxplayers - m_numbots); i < m_maxplayers; ++i) - { - m_bots.append(Color::order[i]); - m_actorMovements[Color::order[i]] = Actor::None; - } - - qDebug() << "[Server] All Clients connected"; - return true; -} - -void Server::sendUpdate(Transmission::map_t map, bool firstPacket) -{ - m_updatepacket.Clear(); - - for (unsigned int x = 0; x < Constants::map_size.width; ++x) - { - for (unsigned int y = 0; y < Constants::map_size.height; ++y) - m_updatepacket.add_field(map[x][y]); - } - - for(unsigned int i = 0; i < m_maxplayers; ++i) - { - m_updatepacket.add_round_points(m_actors.value(Color::order[i])->getRoundPoints()); - m_updatepacket.add_game_points(m_actors.value(Color::order[i])->getGamePoints()); - } - - /* we send the eating_order inside the first packet */ - if (firstPacket) - { - foreach(Color::Color color, m_eatingorder) - m_updatepacket.add_eating_order(color); - } - - QSharedPointer data = Util::createPacket(m_updatepacket); - QMutableMapIterator i(m_clientConnections); - while(i.hasNext()) - { - i.next(); - QTcpSocket *socket = i.value(); - - if (!Util::sendPacket(data.data(), socket)) - { - qWarning() << "[Connect] Error while sending data to client" << i.key() << "-> Disconnecting..."; - socket->close(); - std::cout << "[Connect] Actor color=" << qPrintable(Util::colorToString(i.key())) - << " is now a bot" << std::endl; - m_bots.append(i.key()); - ++m_numbots; - i.remove(); - } - } - - if (m_maxplayers == m_numbots) - { - std::cout << "[Connect] No more real players left. Exiting..." << std::endl; - qApp->quit(); - } -} - -void Server::botCalculate(Actor *actor) -{ - QPoint actorpos = CoordToMapPosition(actor->pos().toPoint()); - - /* first make list of possible directions based on current actor position */ - QHash directions; - if (actorpos.x() > 0) - directions.insert(Actor::Left, 0); - if (actorpos.x() < visualMap.size() - 1) - directions.insert(Actor::Right, 0); - if (actorpos.y() > 0) - directions.insert(Actor::Up, 0); - if (actorpos.y() < visualMap[actorpos.x()].size() - 1) - directions.insert(Actor::Down, 0); - - /* check neighbours for blocks first */ - QMutableHashIterator i(directions); - while(i.hasNext()) - { - i.next(); - QPoint newpos = actorpos + Actor::movementToPoint(i.key()); - if (newpos.x() < 0 || newpos.x() >= visualMap.size()) - continue; - if (newpos.y() < 0 || newpos.y() >= visualMap[newpos.x()].size()) - continue; - GameEntity *item = visualMap[newpos.x()][newpos.y()]; - - /* check if neighbour is a block */ - Block *block = qgraphicsitem_cast(item); - if (block != NULL && block->color() != actor->color()) - i.remove(); - } - - /* we're enclosed by blocks */ - if (directions.empty()) - return; - - /* determine if other actors are in range to be afraid/to hunt */ - int mindistance = Constants::AI::player_minimum_distance; - QList pos_afraid; - QList pos_hunt; - foreach (Actor *other, m_actors) - { - if (actor == other) - continue; - - QList *ptr = NULL; - if (other->canEat(actor, m_eatingorder)) - ptr = &pos_afraid; - else if (actor->canEat(other, m_eatingorder)) - ptr = &pos_hunt; - if (ptr == NULL) - continue; - - QPoint otherpos = CoordToMapPosition(other->pos().toPoint()); - QPoint distance = actorpos - otherpos; - if (distance.manhattanLength() < mindistance) - ptr->append(otherpos); - } - - /* check new directions and change direction-weight */ - i = directions; - while(i.hasNext()) - { - i.next(); - QPoint newpos = actorpos + Actor::movementToPoint(i.key()); - - /* check for new positions in afraid list */ - foreach(QPoint otherpos, pos_afraid) - { - int olddistance = (actorpos - otherpos).manhattanLength(); - int newdistance = (newpos - otherpos).manhattanLength(); - if (newdistance >= olddistance) - i.setValue(i.value() + Constants::AI::weight_afraid); - - /* check for blocks of own color: other pacman can't follow their */ - GameEntity *item = visualMap[newpos.x()][newpos.y()]; - Block *block = qgraphicsitem_cast(item); - if (block != NULL && block->color() == actor->color()) - i.setValue(i.value() + Constants::AI::weight_colorblock); - } - - /* check for new positions in hunt list */ - foreach(QPoint otherpos, pos_hunt) - { - int olddistance = (actorpos - otherpos).manhattanLength(); - int newdistance = (newpos - otherpos).manhattanLength(); - if (newdistance <= olddistance) - i.setValue(i.value() + Constants::AI::weight_hunt); - } - - /* check for bonuspoint */ - GameEntity *item = visualMap[newpos.x()][newpos.y()]; - BonusPoint *bpoint = qgraphicsitem_cast(item); - if (bpoint != NULL) - i.setValue(i.value() + Constants::AI::weight_bonus_point); - - /* check for normal point */ - Point *point = qgraphicsitem_cast(item); - if (point != NULL) - i.setValue(i.value() + Constants::AI::weight_point); - } - - /* sort directions */ - QList weightlist = directions.values(); - qSort(weightlist.begin(), weightlist.end(), qGreater()); - - /* remove directions with lesser weight */ - unsigned int max = weightlist.at(0); - i = directions; - while(i.hasNext()) - { - i.next(); - if (i.value() < max) - i.remove(); - } - - QList list = directions.keys(); - - /* default is no direction change */ - if (list.contains(actor->direction())) - return; - - /* random direction */ - int rand = (int) (list.size() * (qrand() / (RAND_MAX + 1.0))); - m_actorMovements[actor->color()] = list.at(rand); -} - -void Server::keyPressUpdate() -{ - ProtoBuf::KeyPressUpdate packet; - QMapIterator i(m_clientConnections); - while (i.hasNext()) - { - i.next(); - Color::Color color = i.key(); - QTcpSocket *socket = i.value(); - QDataStream in(i.value()); - while (socket->bytesAvailable() > (qint64)sizeof(qint64)) - { - QSharedPointer data = Util::receivePacket(socket); - bool worked = packet.ParseFromArray(data->data(), data->size()); - Q_ASSERT(worked); - Q_UNUSED(worked); - Transmission::field_t direction = packet.newkey(); - qDebug() << "[KeyPress] actor=" << color << "direction=" << direction; - m_actorMovements[color] = Util::transmissionMovementToActor(direction); - } - } -} - -void Server::initRoundMap() -{ - std::cout << "[Game] New round starts..." << std::endl; - m_tickTimer->stop(); - - /* reset scene and clean up items */ - reset(); - - /* randomize color eating order */ - m_eatingorder.removeLast(); - random_shuffle(m_eatingorder.begin(), m_eatingorder.end()); - m_eatingorder.append(m_eatingorder.at(0)); - - /* create new map */ - Transmission::map_t map = Util::createDemoMap(); - Util::placeActors(map, m_maxplayers, Color::order); - Util::fillPoints(map); - -#if 0 // actor eating actor tests - TODO: remove - m_actorMovements.clear(); -#if 0 - //works - map[0][0] = Color::order[0] | Transmission::pacman; - map[0][1] = Color::order[1] | Transmission::pacman; - m_actorMovements.insert(Color::order[0], Actor::Down); -#elif 0 - //works - map[0][0] = Color::order[0] | Transmission::pacman; - map[0][3] = Color::order[1] | Transmission::pacman; - m_actorMovements.insert(Color::order[0], Actor::Down); -#elif 0 - //works - map[0][0] = Color::order[0] | Transmission::pacman; - map[0][4] = Color::order[1] | Transmission::pacman; - m_actorMovements.insert(Color::order[0], Actor::Down); -#elif 0 - //works - map[0][0] = Color::order[0] | Transmission::pacman; - map[0][5] = Color::order[1] | Transmission::pacman; - m_actorMovements.insert(Color::order[0], Actor::Down); -#elif 0 - //works - map[0][0] = Color::order[1] | Transmission::pacman; - map[0][5] = Color::order[0] | Transmission::pacman; - m_actorMovements.insert(Color::order[1], Actor::Down); -#elif 0 - //works - map[0][0] = Color::order[0] | Transmission::pacman; - map[0][5] = Color::order[1] | Transmission::pacman; - m_actorMovements.insert(Color::order[0], Actor::Down); - m_actorMovements.insert(Color::order[1], Actor::Up); -#elif 0 - //works - map[0][0] = Color::order[0] | Transmission::pacman; - map[0][6] = Color::order[1] | Transmission::pacman; - m_actorMovements.insert(Color::order[0], Actor::Down); - m_actorMovements.insert(Color::order[1], Actor::Up); -#elif 0 - //works - map[0][0] = Color::order[0] | Transmission::pacman; - map[0][7] = Color::order[1] | Transmission::pacman; - m_actorMovements.insert(Color::order[0], Actor::Down); - m_actorMovements.insert(Color::order[1], Actor::Up); -#elif 1 - //works - map[0][0] = Color::order[0] | Transmission::pacman; - map[0][1] = Color::order[1] | Transmission::pacman; - m_actorMovements.insert(Color::order[0], Actor::Down); - m_actorMovements.insert(Color::order[1], Actor::Down); -#endif -#endif - - /* save positions of blocks for later usage */ - m_blocks.clear(); - for (unsigned int x = 0; x < Constants::map_size.width; ++x) - { - for (unsigned int y = 0; y < Constants::map_size.height; ++y) - { - Transmission::field_t &cur = map[x][y]; - if (cur & Transmission::block) - m_blocks.append(QPoint(x, y)); - } - } - - updateMap(map); - sendUpdate(map, true); - Util::deleteMap(map); - map = NULL; - - m_actorMovements.clear(); - - disconnect(AudioManager::self()->audioPlayer(), NULL, this, NULL); - connect(AudioManager::self()->audioPlayer(), SIGNAL(finished()), this, SLOT(startGame())); - AudioManager::self()->play(Sound::Intro, true); - m_tickTimer->start(); -} - -void Server::startGame() -{ - m_running = true; -} - -void Server::stopGame(bool delay) -{ - /* first finish previous round */ - foreach(Actor *actor, m_actors) - actor->finishRound(); - m_finishRound = false; - m_running = false; - - /* add delay if requested */ - if (delay) - { - disconnect(AudioManager::self()->audioPlayer(), NULL, this, NULL); - connect(AudioManager::self()->audioPlayer(), SIGNAL(finished()), this, SLOT(stopGame())); - AudioManager::self()->play(Sound::Die, true); - return; - } - - std::cout << "[Game] Round finished..." << std::endl; - - /* do next-round work */ - ++m_curRound; - if(m_rounds == 0 || m_curRound < m_rounds) - initRoundMap(); - else - { - /* end of game */ - std::cout << "[Game] All round finished. Exiting..." << std::endl; - qApp->quit(); - } -} - -void Server::setFinishRound() -{ - m_finishRound = true; -} - -bool Server::parseCommandline() -{ - AnyOption opt; - opt.setVerbose(); - - /* usage strings must remain valid until parsing is done */ - QString exec = QFileInfo(qApp->applicationFilePath()).fileName(); - QByteArray usage; - QTextStream out(&usage, QIODevice::ReadWrite | QIODevice::Text); - out << "Usage: " << exec << " [OPTION]" << endl - << "Usage: " << exec << " -h" << endl - << endl; - out << " -b, --bind " << endl - << " Specifies the ip address on which the server listens for connections" << endl - << " Default: " << m_bindaddress.toString() << endl - << endl; - opt.setOption("bind", 'b'); - out << " -p, --port " << endl - << " Specifies the port on which the server listens for connections" << endl - << " Default: " << m_port << endl - << endl; - opt.setOption("port", 'p'); - out << " -m, --maxplayers [1.." << m_maxplayers << "]" << endl - << " Specifies the maximum number of players/pacmans" << endl - << " Default: " << m_maxplayers << endl - << endl; - opt.setOption("maxplayers", 'm'); - out << " --bots [0..maxplayers-1]" << endl - << " Specifies the number of AI pacmans/bots" << endl - << " Default: " << m_numbots << endl - << endl; - opt.setOption("bots"); - out << " --nocolorblocks" << endl - << " Disable random colorized blocks" << endl - << endl; - opt.setOption("rounds", 'r'); - out << " -r, --rounds [0 | 1..n]" << endl - << " Number of rounds to play" << endl - << " Default: " << m_rounds << endl - << endl; - opt.setFlag("nocolorblocks"); - out << " -h, --help" << endl - << " Prints this help message" << endl; - opt.setFlag("help", 'h'); - out.flush(); - opt.addUsage(usage.constData()); - opt.processCommandArgs(qApp->argc(), qApp->argv()); - - if (opt.getFlag("help") || opt.getFlag('h')) - { - opt.printUsage(); - return false; - } - - if (opt.getValue("port") != NULL) - { - bool ok; - m_port = QString(opt.getValue("port")).toUInt(&ok); - if (!ok || m_port < 1 || m_port > 65535) - { - qCritical() << "Invalid port-option:" << opt.getValue("port") << endl - << "Port must be between 1 and 65535"; - return false; - } - } - - if (opt.getValue("bind") != NULL) - { - m_bindaddress = opt.getValue("bind"); - if (m_bindaddress.isNull()) - { - qCritical() << "Invalid bind-option:" << opt.getValue("bind") << endl - << "Bind address must be an ip address"; - return false; - } - } - - if (opt.getValue("maxplayers") != NULL) - { - bool ok; - unsigned int maxplayers = QString(opt.getValue("maxplayers")).toUInt(&ok); - if (!ok || maxplayers < 1 || maxplayers > m_maxplayers) - { - qCritical() << "Invalid maxplayers-option:" << opt.getValue("maxplayers") << endl - << "Maxplayers must be between 1 and" << m_maxplayers; - return false; - } - m_maxplayers = maxplayers; - if (m_maxplayers == 2) - { - qCritical() << "2 player game is not supported (who wins if a player gets eaten?)"; - return false; - } - } - - if (opt.getValue("bots") != NULL) - { - bool ok; - unsigned int numbots = QString(opt.getValue("bots")).toUInt(&ok); - if (!ok || numbots >= m_maxplayers) - { - qCritical() << "Invalid numbots-options:" << opt.getValue("bots") << endl - << "AI pacmans/bots must be between 0 and" << m_maxplayers - 1; - return false; - } - m_numbots = numbots; - } - - if (opt.getValue("rounds") != NULL) - { - bool ok; - unsigned int rounds = QString(opt.getValue("rounds")).toUInt(&ok); - if (!ok) - { - qCritical() << "Invalid number of rounds: " << opt.getValue("rounds") << endl; - return false; - } - m_rounds = rounds; - } - - this->setProperty("coloredblocks", !opt.getFlag("nocolorblocks")); - - return true; -} - -bool operator<(const QPoint& lhs, const QPoint& rhs) -{ - if (lhs.x() < rhs.x()) - return true; - else if (lhs.x() == rhs.x()) - return lhs.y() < rhs.y(); - else - return false; -} - -bool Constants::server = true; - -int main(int argc, char **argv) -{ - /* Verify that the version of the library that we linked against is - * compatible with the version of the headers we compiled against. - */ - GOOGLE_PROTOBUF_VERIFY_VERSION; - - QApplication app(argc, argv, false); - app.setApplicationName("Pacman Server"); - app.setWindowIcon(QIcon(":/appicon")); - - qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime())); - - int ret = 0; - Server server; - if (!ret && !server.parseCommandline()) - ret = 1; - if (!ret && !server.run()) - ret = 1; - if (!ret) - ret = app.exec(); - - /* Delete all global objects allocated by libprotobuf */ - google::protobuf::ShutdownProtobufLibrary(); - - return ret; -} diff --git a/pacman-c++/server.h b/pacman-c++/server.h deleted file mode 100644 index 41b800e..0000000 --- a/pacman-c++/server.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef SERVER_H -#define SERVER_H - -#include "sceneholder.h" -#include "actor.h" -#include "pacman.pb.h" -#include -#include - -class QTcpSocket; - -class Server - : public SceneHolder -{ - Q_OBJECT -public: - Server(QWidget *parent = 0); - bool parseCommandline(); - bool run(); - -protected slots: - void tick(); - - /* receive updates of client */ - void keyPressUpdate(); - -protected: - /* block until we have connections from all clients */ - bool waitForClientConnections(); - - /* calculate updates of current tick for sending to client */ - Transmission::map_t calculateUpdates(); - - /* update client maps */ - void sendUpdate(Transmission::map_t map, bool firstPacket = false); - - QPoint addRandomPoint(Transmission::map_t map, Transmission::field_t type = Transmission::bonuspoint); - void colorizeBlocks(Transmission::map_t map); - void botCalculate(Actor *actor); - void initRoundMap(); - -protected slots: - /* called when a round is started/finished */ - void startGame(); - void stopGame(bool delay = false); - void setFinishRound(); - -protected: - QMap m_clientConnections; - QList m_bots; - - /* current movements. required to make pacmans continue their movement */ - QMap m_actorMovements; - - /* allocate as member variable as this packet is large and used often */ - ProtoBuf::MapUpdate m_updatepacket; - - /* list of blocks */ - QList m_blocks; - /* currently colored blocks + tickcount before they will turn to non-colored back */ - QMap m_coloredBlocks; - - QHostAddress m_bindaddress; - unsigned int m_port; - unsigned int m_maxplayers; - unsigned int m_numbots; - /* number of rounds (>= 1) */ - unsigned int m_rounds; - /* current round, starting at 0 */ - unsigned int m_curRound; - bool m_running; - bool m_finishRound; - - QTimer *m_tickTimer; - -}; - -#endif // SERVER_H diff --git a/pacman-c++/server/anyoption.cpp b/pacman-c++/server/anyoption.cpp new file mode 100644 index 0000000..4e3828e --- /dev/null +++ b/pacman-c++/server/anyoption.cpp @@ -0,0 +1,1175 @@ +/* + * AnyOption 1.3 + * + * kishan at hackorama dot com www.hackorama.com JULY 2001 + * + * + Acts as a common facade class for reading + * commandline options as well as options from + * an optionfile with delimited type value pairs + * + * + Handles the POSIX style single character options ( -w ) + * as well as the newer GNU long options ( --width ) + * + * + The option file assumes the traditional format of + * first character based comment lines and type value + * pairs with a delimiter , and flags which are not pairs + * + * # this is a coment + * # next line is an option value pair + * width : 100 + * # next line is a flag + * noimages + * + * + Supports printing out Help and Usage + * + * + Why not just use getopt() ? + * + * getopt() Its a POSIX standard not part of ANSI-C. + * So it may not be available on platforms like Windows. + * + * + Why it is so long ? + * + * The actual code which does command line parsing + * and option file parsing are done in few methods. + * Most of the extra code are for providing a flexible + * common public interface to both a resourcefile and + * and command line supporting POSIX style and + * GNU long option as well as mixing of both. + * + * + Please see "anyoption.h" for public method descriptions + * + */ + +/* Updated Auguest 2004 + * Fix from Michael D Peters (mpeters at sandia.gov) + * to remove static local variables, allowing multiple instantiations + * of the reader (for using multiple configuration files). There is + * an error in the destructor when using multiple instances, so you + * cannot delete your objects (it will crash), but not calling the + * destructor only introduces a small memory leak, so I + * have not bothered tracking it down. + * + * Also updated to use modern C++ style headers, rather than + * depricated iostream.h (it was causing my compiler problems) +*/ + +/* + * Updated September 2006 + * Fix from Boyan Asenov for a bug in mixing up option indexes + * leading to exception when mixing different options types + */ + +#include "anyoption.h" +#include + +AnyOption::AnyOption() +{ + init(); +} + +AnyOption::AnyOption(int maxopt) +{ + init( maxopt , maxopt ); +} + +AnyOption::AnyOption(int maxopt, int maxcharopt) +{ + init( maxopt , maxcharopt ); +} + +AnyOption::~AnyOption() +{ + if( mem_allocated ) + cleanup(); +} + +void +AnyOption::init() +{ + init( DEFAULT_MAXOPTS , DEFAULT_MAXOPTS ); +} + +void +AnyOption::init(int maxopt, int maxcharopt ) +{ + + max_options = maxopt; + max_char_options = maxcharopt; + max_usage_lines = DEFAULT_MAXUSAGE; + usage_lines = 0 ; + argc = 0; + argv = NULL; + posix_style = true; + verbose = false; + filename = NULL; + appname = NULL; + option_counter = 0; + optchar_counter = 0; + new_argv = NULL; + new_argc = 0 ; + max_legal_args = 0 ; + command_set = false; + file_set = false; + values = NULL; + g_value_counter = 0; + mem_allocated = false; + command_set = false; + file_set = false; + opt_prefix_char = '-'; + file_delimiter_char = ':'; + file_comment_char = '#'; + equalsign = '='; + comment = '#' ; + delimiter = ':' ; + endofline = '\n'; + whitespace = ' ' ; + nullterminate = '\0'; + set = false; + once = true; + hasoptions = false; + autousage = false; + + strcpy( long_opt_prefix , "--" ); + + if( alloc() == false ){ + cout << endl << "OPTIONS ERROR : Failed allocating memory" ; + cout << endl ; + cout << "Exiting." << endl ; + exit (0); + } +} + +bool +AnyOption::alloc() +{ + int i = 0 ; + int size = 0 ; + + if( mem_allocated ) + return true; + + size = (max_options+1) * sizeof(const char*); + options = (const char**)malloc( size ); + optiontype = (int*) malloc( (max_options+1)*sizeof(int) ); + optionindex = (int*) malloc( (max_options+1)*sizeof(int) ); + if( options == NULL || optiontype == NULL || optionindex == NULL ) + return false; + else + mem_allocated = true; + for( i = 0 ; i < max_options ; i++ ){ + options[i] = NULL; + optiontype[i] = 0 ; + optionindex[i] = -1 ; + } + optionchars = (char*) malloc( (max_char_options+1)*sizeof(char) ); + optchartype = (int*) malloc( (max_char_options+1)*sizeof(int) ); + optcharindex = (int*) malloc( (max_char_options+1)*sizeof(int) ); + if( optionchars == NULL || + optchartype == NULL || + optcharindex == NULL ) + { + mem_allocated = false; + return false; + } + for( i = 0 ; i < max_char_options ; i++ ){ + optionchars[i] = '0'; + optchartype[i] = 0 ; + optcharindex[i] = -1 ; + } + + size = (max_usage_lines+1) * sizeof(const char*) ; + usage = (const char**) malloc( size ); + + if( usage == NULL ){ + mem_allocated = false; + return false; + } + for( i = 0 ; i < max_usage_lines ; i++ ) + usage[i] = NULL; + + return true; +} + +bool +AnyOption::doubleOptStorage() +{ + options = (const char**)realloc( options, + ((2*max_options)+1) * sizeof( const char*) ); + optiontype = (int*) realloc( optiontype , + ((2 * max_options)+1)* sizeof(int) ); + optionindex = (int*) realloc( optionindex, + ((2 * max_options)+1) * sizeof(int) ); + if( options == NULL || optiontype == NULL || optionindex == NULL ) + return false; + /* init new storage */ + for( int i = max_options ; i < 2*max_options ; i++ ){ + options[i] = NULL; + optiontype[i] = 0 ; + optionindex[i] = -1 ; + } + max_options = 2 * max_options ; + return true; +} + +bool +AnyOption::doubleCharStorage() +{ + optionchars = (char*) realloc( optionchars, + ((2*max_char_options)+1)*sizeof(char) ); + optchartype = (int*) realloc( optchartype, + ((2*max_char_options)+1)*sizeof(int) ); + optcharindex = (int*) realloc( optcharindex, + ((2*max_char_options)+1)*sizeof(int) ); + if( optionchars == NULL || + optchartype == NULL || + optcharindex == NULL ) + return false; + /* init new storage */ + for( int i = max_char_options ; i < 2*max_char_options ; i++ ){ + optionchars[i] = '0'; + optchartype[i] = 0 ; + optcharindex[i] = -1 ; + } + max_char_options = 2 * max_char_options; + return true; +} + +bool +AnyOption::doubleUsageStorage() +{ + usage = (const char**)realloc( usage, + ((2*max_usage_lines)+1) * sizeof( const char*) ); + if ( usage == NULL ) + return false; + for( int i = max_usage_lines ; i < 2*max_usage_lines ; i++ ) + usage[i] = NULL; + max_usage_lines = 2 * max_usage_lines ; + return true; + +} + + +void +AnyOption::cleanup() +{ + free (options); + free (optiontype); + free (optionindex); + free (optionchars); + free (optchartype); + free (optcharindex); + free (usage); + if( values != NULL ) + free (values); + if( new_argv != NULL ) + free (new_argv); +} + +void +AnyOption::setCommandPrefixChar( char _prefix ) +{ + opt_prefix_char = _prefix; +} + +void +AnyOption::setCommandLongPrefix( char *_prefix ) +{ + if( strlen( _prefix ) > MAX_LONG_PREFIX_LENGTH ){ + *( _prefix + MAX_LONG_PREFIX_LENGTH ) = '\0'; + } + + strcpy (long_opt_prefix, _prefix); +} + +void +AnyOption::setFileCommentChar( char _comment ) +{ + file_delimiter_char = _comment; +} + + +void +AnyOption::setFileDelimiterChar( char _delimiter ) +{ + file_comment_char = _delimiter ; +} + +bool +AnyOption::CommandSet() +{ + return( command_set ); +} + +bool +AnyOption::FileSet() +{ + return( file_set ); +} + +void +AnyOption::noPOSIX() +{ + posix_style = false; +} + +bool +AnyOption::POSIX() +{ + return posix_style; +} + + +void +AnyOption::setVerbose() +{ + verbose = true ; +} + +void +AnyOption::printVerbose() +{ + if( verbose ) + cout << endl ; +} +void +AnyOption::printVerbose( const char *msg ) +{ + if( verbose ) + cout << msg ; +} + +void +AnyOption::printVerbose( char *msg ) +{ + if( verbose ) + cout << msg ; +} + +void +AnyOption::printVerbose( char ch ) +{ + if( verbose ) + cout << ch ; +} + +bool +AnyOption::hasOptions() +{ + return hasoptions; +} + +void +AnyOption::autoUsagePrint(bool _autousage) +{ + autousage = _autousage; +} + +void +AnyOption::useCommandArgs( int _argc, char **_argv ) +{ + argc = _argc; + argv = _argv; + command_set = true; + appname = argv[0]; + if(argc > 1) hasoptions = true; +} + +void +AnyOption::useFiileName( const char *_filename ) +{ + filename = _filename; + file_set = true; +} + +/* + * set methods for options + */ + +void +AnyOption::setCommandOption( const char *opt ) +{ + addOption( opt , COMMAND_OPT ); + g_value_counter++; +} + +void +AnyOption::setCommandOption( char opt ) +{ + addOption( opt , COMMAND_OPT ); + g_value_counter++; +} + +void +AnyOption::setCommandOption( const char *opt , char optchar ) +{ + addOption( opt , COMMAND_OPT ); + addOption( optchar , COMMAND_OPT ); + g_value_counter++; +} + +void +AnyOption::setCommandFlag( const char *opt ) +{ + addOption( opt , COMMAND_FLAG ); + g_value_counter++; +} + +void +AnyOption::setCommandFlag( char opt ) +{ + addOption( opt , COMMAND_FLAG ); + g_value_counter++; +} + +void +AnyOption::setCommandFlag( const char *opt , char optchar ) +{ + addOption( opt , COMMAND_FLAG ); + addOption( optchar , COMMAND_FLAG ); + g_value_counter++; +} + +void +AnyOption::setFileOption( const char *opt ) +{ + addOption( opt , FILE_OPT ); + g_value_counter++; +} + +void +AnyOption::setFileOption( char opt ) +{ + addOption( opt , FILE_OPT ); + g_value_counter++; +} + +void +AnyOption::setFileOption( const char *opt , char optchar ) +{ + addOption( opt , FILE_OPT ); + addOption( optchar, FILE_OPT ); + g_value_counter++; +} + +void +AnyOption::setFileFlag( const char *opt ) +{ + addOption( opt , FILE_FLAG ); + g_value_counter++; +} + +void +AnyOption::setFileFlag( char opt ) +{ + addOption( opt , FILE_FLAG ); + g_value_counter++; +} + +void +AnyOption::setFileFlag( const char *opt , char optchar ) +{ + addOption( opt , FILE_FLAG ); + addOption( optchar , FILE_FLAG ); + g_value_counter++; +} + +void +AnyOption::setOption( const char *opt ) +{ + addOption( opt , COMMON_OPT ); + g_value_counter++; +} + +void +AnyOption::setOption( char opt ) +{ + addOption( opt , COMMON_OPT ); + g_value_counter++; +} + +void +AnyOption::setOption( const char *opt , char optchar ) +{ + addOption( opt , COMMON_OPT ); + addOption( optchar , COMMON_OPT ); + g_value_counter++; +} + +void +AnyOption::setFlag( const char *opt ) +{ + addOption( opt , COMMON_FLAG ); + g_value_counter++; +} + +void +AnyOption::setFlag( const char opt ) +{ + addOption( opt , COMMON_FLAG ); + g_value_counter++; +} + +void +AnyOption::setFlag( const char *opt , char optchar ) +{ + addOption( opt , COMMON_FLAG ); + addOption( optchar , COMMON_FLAG ); + g_value_counter++; +} + +void +AnyOption::addOption( const char *opt, int type ) +{ + if( option_counter >= max_options ){ + if( doubleOptStorage() == false ){ + addOptionError( opt ); + return; + } + } + options[ option_counter ] = opt ; + optiontype[ option_counter ] = type ; + optionindex[ option_counter ] = g_value_counter; + option_counter++; +} + +void +AnyOption::addOption( char opt, int type ) +{ + if( !POSIX() ){ + printVerbose("Ignoring the option character \""); + printVerbose( opt ); + printVerbose( "\" ( POSIX options are turned off )" ); + printVerbose(); + return; + } + + + if( optchar_counter >= max_char_options ){ + if( doubleCharStorage() == false ){ + addOptionError( opt ); + return; + } + } + optionchars[ optchar_counter ] = opt ; + optchartype[ optchar_counter ] = type ; + optcharindex[ optchar_counter ] = g_value_counter; + optchar_counter++; +} + +void +AnyOption::addOptionError( const char *opt ) +{ + cout << endl ; + cout << "OPTIONS ERROR : Failed allocating extra memory " << endl ; + cout << "While adding the option : \""<< opt << "\"" << endl; + cout << "Exiting." << endl ; + cout << endl ; + exit(0); +} + +void +AnyOption::addOptionError( char opt ) +{ + cout << endl ; + cout << "OPTIONS ERROR : Failed allocating extra memory " << endl ; + cout << "While adding the option: \""<< opt << "\"" << endl; + cout << "Exiting." << endl ; + cout << endl ; + exit(0); +} + +void +AnyOption::processOptions() +{ + if( ! valueStoreOK() ) + return; +} + +void +AnyOption::processCommandArgs(int max_args) +{ + max_legal_args = max_args; + processCommandArgs(); +} + +void +AnyOption::processCommandArgs( int _argc, char **_argv, int max_args ) +{ + max_legal_args = max_args; + processCommandArgs( _argc, _argv ); +} + +void +AnyOption::processCommandArgs( int _argc, char **_argv ) +{ + useCommandArgs( _argc, _argv ); + processCommandArgs(); +} + +void +AnyOption::processCommandArgs() +{ + if( ! ( valueStoreOK() && CommandSet() ) ) + return; + + if( max_legal_args == 0 ) + max_legal_args = argc; + new_argv = (int*) malloc( (max_legal_args+1) * sizeof(int) ); + for( int i = 1 ; i < argc ; i++ ){/* ignore first argv */ + if( argv[i][0] == long_opt_prefix[0] && + argv[i][1] == long_opt_prefix[1] ) { /* long GNU option */ + int match_at = parseGNU( argv[i]+2 ); /* skip -- */ + if( match_at >= 0 && i < argc-1 ) /* found match */ + setValue( options[match_at] , argv[++i] ); + }else if( argv[i][0] == opt_prefix_char ) { /* POSIX char */ + if( POSIX() ){ + char ch = parsePOSIX( argv[i]+1 );/* skip - */ + if( ch != '0' && i < argc-1 ) /* matching char */ + setValue( ch , argv[++i] ); + } else { /* treat it as GNU option with a - */ + int match_at = parseGNU( argv[i]+1 ); /* skip - */ + if( match_at >= 0 && i < argc-1 ) /* found match */ + setValue( options[match_at] , argv[++i] ); + } + }else { /* not option but an argument keep index */ + if( new_argc < max_legal_args ){ + new_argv[ new_argc ] = i ; + new_argc++; + }else{ /* ignore extra arguments */ + printVerbose( "Ignoring extra argument: " ); + printVerbose( argv[i] ); + printVerbose( ); + printAutoUsage(); + } + printVerbose( "Unknown command argument option : " ); + printVerbose( argv[i] ); + printVerbose( ); + printAutoUsage(); + } + } +} + +char +AnyOption::parsePOSIX( char* arg ) +{ + + for( unsigned int i = 0 ; i < strlen(arg) ; i++ ){ + char ch = arg[i] ; + if( matchChar(ch) ) { /* keep matching flags till an option */ + /*if last char argv[++i] is the value */ + if( i == strlen(arg)-1 ){ + return ch; + }else{/* else the rest of arg is the value */ + i++; /* skip any '=' and ' ' */ + while( arg[i] == whitespace + || arg[i] == equalsign ) + i++; + setValue( ch , arg+i ); + return '0'; + } + } + } + printVerbose( "Unknown command argument option : " ); + printVerbose( arg ); + printVerbose( ); + printAutoUsage(); + return '0'; +} + +int +AnyOption::parseGNU( char *arg ) +{ + int split_at = 0; + /* if has a '=' sign get value */ + for( unsigned int i = 0 ; i < strlen(arg) ; i++ ){ + if(arg[i] == equalsign ){ + split_at = i ; /* store index */ + i = strlen(arg); /* get out of loop */ + } + } + if( split_at > 0 ){ /* it is an option value pair */ + char* tmp = (char*) malloc( (split_at+1)*sizeof(char) ); + for( int i = 0 ; i < split_at ; i++ ) + tmp[i] = arg[i]; + tmp[split_at] = '\0'; + + if ( matchOpt( tmp ) >= 0 ){ + setValue( options[matchOpt(tmp)] , arg+split_at+1 ); + free (tmp); + }else{ + printVerbose( "Unknown command argument option : " ); + printVerbose( arg ); + printVerbose( ); + printAutoUsage(); + free (tmp); + return -1; + } + }else{ /* regular options with no '=' sign */ + return matchOpt(arg); + } + return -1; +} + + +int +AnyOption::matchOpt( char *opt ) +{ + for( int i = 0 ; i < option_counter ; i++ ){ + if( strcmp( options[i], opt ) == 0 ){ + if( optiontype[i] == COMMON_OPT || + optiontype[i] == COMMAND_OPT ) + { /* found option return index */ + return i; + }else if( optiontype[i] == COMMON_FLAG || + optiontype[i] == COMMAND_FLAG ) + { /* found flag, set it */ + setFlagOn( opt ); + return -1; + } + } + } + printVerbose( "Unknown command argument option : " ); + printVerbose( opt ) ; + printVerbose( ); + printAutoUsage(); + return -1; +} +bool +AnyOption::matchChar( char c ) +{ + for( int i = 0 ; i < optchar_counter ; i++ ){ + if( optionchars[i] == c ) { /* found match */ + if(optchartype[i] == COMMON_OPT || + optchartype[i] == COMMAND_OPT ) + { /* an option store and stop scanning */ + return true; + }else if( optchartype[i] == COMMON_FLAG || + optchartype[i] == COMMAND_FLAG ) { /* a flag store and keep scanning */ + setFlagOn( c ); + return true; + } + } + } + //printVerbose( "Unknown command argument option : " ); + //printVerbose( c ) ; + //printVerbose( ); + printAutoUsage(); + return false; +} + +bool +AnyOption::valueStoreOK( ) +{ + int size= 0; + if( !set ){ + if( g_value_counter > 0 ){ + size = g_value_counter * sizeof(char*); + values = (char**)malloc( size ); + for( int i = 0 ; i < g_value_counter ; i++) + values[i] = NULL; + set = true; + } + } + return set; +} + +/* + * public get methods + */ +char* +AnyOption::getValue( const char *option ) +{ + if( !valueStoreOK() ) + return NULL; + + for( int i = 0 ; i < option_counter ; i++ ){ + if( strcmp( options[i], option ) == 0 ) + return values[ optionindex[i] ]; + } + return NULL; +} + +bool +AnyOption::getFlag( const char *option ) +{ + if( !valueStoreOK() ) + return false; + for( int i = 0 ; i < option_counter ; i++ ){ + if( strcmp( options[i], option ) == 0 ) + return findFlag( values[ optionindex[i] ] ); + } + return false; +} + +char* +AnyOption::getValue( char option ) +{ + if( !valueStoreOK() ) + return NULL; + for( int i = 0 ; i < optchar_counter ; i++ ){ + if( optionchars[i] == option ) + return values[ optcharindex[i] ]; + } + return NULL; +} + +bool +AnyOption::getFlag( char option ) +{ + if( !valueStoreOK() ) + return false; + for( int i = 0 ; i < optchar_counter ; i++ ){ + if( optionchars[i] == option ) + return findFlag( values[ optcharindex[i] ] ) ; + } + return false; +} + +bool +AnyOption::findFlag( char* val ) +{ + if( val == NULL ) + return false; + + if( strcmp( TRUE_FLAG , val ) == 0 ) + return true; + + return false; +} + +/* + * private set methods + */ +bool +AnyOption::setValue( const char *option , char *value ) +{ + if( !valueStoreOK() ) + return false; + for( int i = 0 ; i < option_counter ; i++ ){ + if( strcmp( options[i], option ) == 0 ){ + values[ optionindex[i] ] = (char*) malloc((strlen(value)+1)*sizeof(char)); + strcpy( values[ optionindex[i] ], value ); + return true; + } + } + return false; +} + +bool +AnyOption::setFlagOn( const char *option ) +{ + if( !valueStoreOK() ) + return false; + for( int i = 0 ; i < option_counter ; i++ ){ + if( strcmp( options[i], option ) == 0 ){ + values[ optionindex[i] ] = (char*) malloc((strlen(TRUE_FLAG)+1)*sizeof(char)); + strcpy( values[ optionindex[i] ] , TRUE_FLAG ); + return true; + } + } + return false; +} + +bool +AnyOption::setValue( char option , char *value ) +{ + if( !valueStoreOK() ) + return false; + for( int i = 0 ; i < optchar_counter ; i++ ){ + if( optionchars[i] == option ){ + values[ optcharindex[i] ] = (char*) malloc((strlen(value)+1)*sizeof(char)); + strcpy( values[ optcharindex[i] ], value ); + return true; + } + } + return false; +} + +bool +AnyOption::setFlagOn( char option ) +{ + if( !valueStoreOK() ) + return false; + for( int i = 0 ; i < optchar_counter ; i++ ){ + if( optionchars[i] == option ){ + values[ optcharindex[i] ] = (char*) malloc((strlen(TRUE_FLAG)+1)*sizeof(char)); + strcpy( values[ optcharindex[i] ] , TRUE_FLAG ); + return true; + } + } + return false; +} + + +int +AnyOption::getArgc( ) +{ + return new_argc; +} + +char* +AnyOption::getArgv( int index ) +{ + if( index < new_argc ){ + return ( argv[ new_argv[ index ] ] ); + } + return NULL; +} + +/* dotfile sub routines */ + +bool +AnyOption::processFile() +{ + if( ! (valueStoreOK() && FileSet()) ) + return false; + return ( consumeFile(readFile()) ); +} + +bool +AnyOption::processFile( const char *filename ) +{ + useFiileName(filename ); + return ( processFile() ); +} + +char* +AnyOption::readFile() +{ + return ( readFile(filename) ); +} + +/* + * read the file contents to a character buffer + */ + +char* +AnyOption::readFile( const char* fname ) +{ + int length; + char *buffer; + ifstream is; + is.open ( fname , ifstream::in ); + if( ! is.good() ){ + is.close(); + return NULL; + } + is.seekg (0, ios::end); + length = is.tellg(); + is.seekg (0, ios::beg); + buffer = (char*) malloc(length*sizeof(char)); + is.read (buffer,length); + is.close(); + return buffer; +} + +/* + * scans a char* buffer for lines that does not + * start with the specified comment character. + */ +bool +AnyOption::consumeFile( char *buffer ) +{ + + if( buffer == NULL ) + return false; + + char *cursor = buffer;/* preserve the ptr */ + char *pline = NULL ; + int linelength = 0; + bool newline = true; + for( unsigned int i = 0 ; i < strlen( buffer ) ; i++ ){ + if( *cursor == endofline ) { /* end of line */ + if( pline != NULL ) /* valid line */ + processLine( pline, linelength ); + pline = NULL; + newline = true; + }else if( newline ){ /* start of line */ + newline = false; + if( (*cursor != comment ) ){ /* not a comment */ + pline = cursor ; + linelength = 0 ; + } + } + cursor++; /* keep moving */ + linelength++; + } + free (buffer); + return true; +} + + +/* + * find a valid type value pair separated by a delimiter + * character and pass it to valuePairs() + * any line which is not valid will be considered a value + * and will get passed on to justValue() + * + * assuming delimiter is ':' the behaviour will be, + * + * width:10 - valid pair valuePairs( width, 10 ); + * width : 10 - valid pair valuepairs( width, 10 ); + * + * :::: - not valid + * width - not valid + * :10 - not valid + * width: - not valid + * :: - not valid + * : - not valid + * + */ + +void +AnyOption::processLine( char *theline, int length ) +{ + bool found = false; + char *pline = (char*) malloc( (length+1)*sizeof(char) ); + for( int i = 0 ; i < length ; i ++ ) + pline[i]= *(theline++); + pline[length] = nullterminate; + char *cursor = pline ; /* preserve the ptr */ + if( *cursor == delimiter || *(cursor+length-1) == delimiter ){ + justValue( pline );/* line with start/end delimiter */ + }else{ + for( int i = 1 ; i < length-1 && !found ; i++){/* delimiter */ + if( *cursor == delimiter ){ + *(cursor-1) = nullterminate; /* two strings */ + found = true; + valuePairs( pline , cursor+1 ); + } + cursor++; + } + cursor++; + if( !found ) /* not a pair */ + justValue( pline ); + } + free (pline); +} + +/* + * removes trailing and preceeding whitespaces from a string + */ +char* +AnyOption::chomp( char *str ) +{ + while( *str == whitespace ) + str++; + char *end = str+strlen(str)-1; + while( *end == whitespace ) + end--; + *(end+1) = nullterminate; + return str; +} + +void +AnyOption::valuePairs( char *type, char *value ) +{ + if ( strlen(chomp(type)) == 1 ){ /* this is a char option */ + for( int i = 0 ; i < optchar_counter ; i++ ){ + if( optionchars[i] == type[0] ){ /* match */ + if( optchartype[i] == COMMON_OPT || + optchartype[i] == FILE_OPT ) + { + setValue( type[0] , chomp(value) ); + return; + } + } + } + } + /* if no char options matched */ + for( int i = 0 ; i < option_counter ; i++ ){ + if( strcmp( options[i], type ) == 0 ){ /* match */ + if( optiontype[i] == COMMON_OPT || + optiontype[i] == FILE_OPT ) + { + setValue( type , chomp(value) ); + return; + } + } + } + printVerbose( "Unknown option in resourcefile : " ); + printVerbose( type ); + printVerbose( ); +} + +void +AnyOption::justValue( char *type ) +{ + + if ( strlen(chomp(type)) == 1 ){ /* this is a char option */ + for( int i = 0 ; i < optchar_counter ; i++ ){ + if( optionchars[i] == type[0] ){ /* match */ + if( optchartype[i] == COMMON_FLAG || + optchartype[i] == FILE_FLAG ) + { + setFlagOn( type[0] ); + return; + } + } + } + } + /* if no char options matched */ + for( int i = 0 ; i < option_counter ; i++ ){ + if( strcmp( options[i], type ) == 0 ){ /* match */ + if( optiontype[i] == COMMON_FLAG || + optiontype[i] == FILE_FLAG ) + { + setFlagOn( type ); + return; + } + } + } + printVerbose( "Unknown option in resourcefile : " ); + printVerbose( type ); + printVerbose( ); +} + +/* + * usage and help + */ + + +void +AnyOption::printAutoUsage() +{ + if( autousage ) printUsage(); +} + +void +AnyOption::printUsage() +{ + + if( once ) { + once = false ; + cout << endl ; + for( int i = 0 ; i < usage_lines ; i++ ) + cout << usage[i] << endl ; + cout << endl ; + } +} + + +void +AnyOption::addUsage( const char *line ) +{ + if( usage_lines >= max_usage_lines ){ + if( doubleUsageStorage() == false ){ + addUsageError( line ); + exit(1); + } + } + usage[ usage_lines ] = line ; + usage_lines++; +} + +void +AnyOption::addUsageError( const char *line ) +{ + cout << endl ; + cout << "OPTIONS ERROR : Failed allocating extra memory " << endl ; + cout << "While adding the usage/help : \""<< line << "\"" << endl; + cout << "Exiting." << endl ; + cout << endl ; + exit(0); + +} diff --git a/pacman-c++/server/anyoption.h b/pacman-c++/server/anyoption.h new file mode 100644 index 0000000..3f7a5de --- /dev/null +++ b/pacman-c++/server/anyoption.h @@ -0,0 +1,270 @@ +#ifndef _ANYOPTION_H +#define _ANYOPTION_H + +#include +#include +#include +#include + +#define COMMON_OPT 1 +#define COMMAND_OPT 2 +#define FILE_OPT 3 +#define COMMON_FLAG 4 +#define COMMAND_FLAG 5 +#define FILE_FLAG 6 + +#define COMMAND_OPTION_TYPE 1 +#define COMMAND_FLAG_TYPE 2 +#define FILE_OPTION_TYPE 3 +#define FILE_FLAG_TYPE 4 +#define UNKNOWN_TYPE 5 + +#define DEFAULT_MAXOPTS 10 +#define MAX_LONG_PREFIX_LENGTH 2 + +#define DEFAULT_MAXUSAGE 3 +#define DEFAULT_MAXHELP 10 + +#define TRUE_FLAG "true" + +using namespace std; + +class AnyOption +{ + +public: /* the public interface */ + AnyOption(); + AnyOption(int maxoptions ); + AnyOption(int maxoptions , int maxcharoptions); + ~AnyOption(); + + /* + * following set methods specifies the + * special characters and delimiters + * if not set traditional defaults will be used + */ + + void setCommandPrefixChar( char _prefix ); /* '-' in "-w" */ + void setCommandLongPrefix( char *_prefix ); /* '--' in "--width" */ + void setFileCommentChar( char _comment ); /* '#' in shellscripts */ + void setFileDelimiterChar( char _delimiter );/* ':' in "width : 100" */ + + /* + * provide the input for the options + * like argv[] for commndline and the + * option file name to use; + */ + + void useCommandArgs( int _argc, char **_argv ); + void useFiileName( const char *_filename ); + + /* + * turn off the POSIX style options + * this means anything starting with a '-' or "--" + * will be considered a valid option + * which alo means you cannot add a bunch of + * POIX options chars together like "-lr" for "-l -r" + * + */ + + void noPOSIX(); + + /* + * prints warning verbose if you set anything wrong + */ + void setVerbose(); + + + /* + * there are two types of options + * + * Option - has an associated value ( -w 100 ) + * Flag - no value, just a boolean flag ( -nogui ) + * + * the options can be either a string ( GNU style ) + * or a character ( traditional POSIX style ) + * or both ( --width, -w ) + * + * the options can be common to the commandline and + * the optionfile, or can belong only to either of + * commandline and optionfile + * + * following set methods, handle all the aboove + * cases of options. + */ + + /* options comman to command line and option file */ + void setOption( const char *opt_string ); + void setOption( char opt_char ); + void setOption( const char *opt_string , char opt_char ); + void setFlag( const char *opt_string ); + void setFlag( char opt_char ); + void setFlag( const char *opt_string , char opt_char ); + + /* options read from commandline only */ + void setCommandOption( const char *opt_string ); + void setCommandOption( char opt_char ); + void setCommandOption( const char *opt_string , char opt_char ); + void setCommandFlag( const char *opt_string ); + void setCommandFlag( char opt_char ); + void setCommandFlag( const char *opt_string , char opt_char ); + + /* options read from an option file only */ + void setFileOption( const char *opt_string ); + void setFileOption( char opt_char ); + void setFileOption( const char *opt_string , char opt_char ); + void setFileFlag( const char *opt_string ); + void setFileFlag( char opt_char ); + void setFileFlag( const char *opt_string , char opt_char ); + + /* + * process the options, registerd using + * useCommandArgs() and useFileName(); + */ + void processOptions(); + void processCommandArgs(); + void processCommandArgs( int max_args ); + bool processFile(); + + /* + * process the specified options + */ + void processCommandArgs( int _argc, char **_argv ); + void processCommandArgs( int _argc, char **_argv, int max_args ); + bool processFile( const char *_filename ); + + /* + * get the value of the options + * will return NULL if no value is set + */ + char *getValue( const char *_option ); + bool getFlag( const char *_option ); + char *getValue( char _optchar ); + bool getFlag( char _optchar ); + + /* + * Print Usage + */ + void printUsage(); + void printAutoUsage(); + void addUsage( const char *line ); + void printHelp(); + /* print auto usage printing for unknown options or flag */ + void autoUsagePrint(bool flag); + + /* + * get the argument count and arguments sans the options + */ + int getArgc(); + char* getArgv( int index ); + bool hasOptions(); + +private: /* the hidden data structure */ + int argc; /* commandline arg count */ + char **argv; /* commndline args */ + const char* filename; /* the option file */ + char* appname; /* the application name from argv[0] */ + + int *new_argv; /* arguments sans options (index to argv) */ + int new_argc; /* argument count sans the options */ + int max_legal_args; /* ignore extra arguments */ + + + /* option strings storage + indexing */ + int max_options; /* maximum number of options */ + const char **options; /* storage */ + int *optiontype; /* type - common, command, file */ + int *optionindex; /* index into value storage */ + int option_counter; /* counter for added options */ + + /* option chars storage + indexing */ + int max_char_options; /* maximum number options */ + char *optionchars; /* storage */ + int *optchartype; /* type - common, command, file */ + int *optcharindex; /* index into value storage */ + int optchar_counter; /* counter for added options */ + + /* values */ + char **values; /* common value storage */ + int g_value_counter; /* globally updated value index LAME! */ + + /* help and usage */ + const char **usage; /* usage */ + int max_usage_lines; /* max usage lines reseverd */ + int usage_lines; /* number of usage lines */ + + bool command_set; /* if argc/argv were provided */ + bool file_set; /* if a filename was provided */ + bool mem_allocated; /* if memory allocated in init() */ + bool posix_style; /* enables to turn off POSIX style options */ + bool verbose; /* silent|verbose */ + bool print_usage; /* usage verbose */ + bool print_help; /* help verbose */ + + char opt_prefix_char; /* '-' in "-w" */ + char long_opt_prefix[MAX_LONG_PREFIX_LENGTH]; /* '--' in "--width" */ + char file_delimiter_char; /* ':' in width : 100 */ + char file_comment_char; /* '#' in "#this is a comment" */ + char equalsign; + char comment; + char delimiter; + char endofline; + char whitespace; + char nullterminate; + + bool set; //was static member + bool once; //was static member + + bool hasoptions; + bool autousage; + +private: /* the hidden utils */ + void init(); + void init(int maxopt, int maxcharopt ); + bool alloc(); + void cleanup(); + bool valueStoreOK(); + + /* grow storage arrays as required */ + bool doubleOptStorage(); + bool doubleCharStorage(); + bool doubleUsageStorage(); + + bool setValue( const char *option , char *value ); + bool setFlagOn( const char *option ); + bool setValue( char optchar , char *value); + bool setFlagOn( char optchar ); + + void addOption( const char* option , int type ); + void addOption( char optchar , int type ); + void addOptionError( const char *opt); + void addOptionError( char opt); + bool findFlag( char* value ); + void addUsageError( const char *line ); + bool CommandSet(); + bool FileSet(); + bool POSIX(); + + char parsePOSIX( char* arg ); + int parseGNU( char *arg ); + bool matchChar( char c ); + int matchOpt( char *opt ); + + /* dot file methods */ + char *readFile(); + char *readFile( const char* fname ); + bool consumeFile( char *buffer ); + void processLine( char *theline, int length ); + char *chomp( char *str ); + void valuePairs( char *type, char *value ); + void justValue( char *value ); + + void printVerbose( const char *msg ); + void printVerbose( char *msg ); + void printVerbose( char ch ); + void printVerbose( ); + + +}; + +#endif /* ! _ANYOPTION_H */ diff --git a/pacman-c++/server/server.cpp b/pacman-c++/server/server.cpp new file mode 100644 index 0000000..c9e4fff --- /dev/null +++ b/pacman-c++/server/server.cpp @@ -0,0 +1,971 @@ +#include "server.h" +#include "util.h" +#include "pacman.pb.h" +#include "block.h" +#include "anyoption.h" +#include "bonuspoint.h" +#include "point.h" +#include +#include + +Server::Server(QWidget *parent) + : SceneHolder(parent), m_host(NULL), m_bindaddress(QHostAddress::Any), + m_port(Constants::Networking::port), m_numbots(0), + m_rounds(3), m_curRound(0), m_running(false), m_finishRound(false) +{ + /* determine max players by using order array */ + for(m_maxplayers = 0; Color::order[m_maxplayers] != Color::none; ++m_maxplayers); +} + +Server::~Server() +{ + if (m_host != NULL) + { + foreach(ENetPeer *peer, m_clientConnections.keys()) + enet_peer_disconnect(peer, 0); + + /* allow up to 3 seconds for the disconnect to succeed + * and drop any packets received packets + */ + ENetEvent event; + while (m_clientConnections.count() > 0 + && enet_host_service(m_host, &event, 3000) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_RECEIVE: + enet_packet_destroy(event.packet); + break; + case ENET_EVENT_TYPE_DISCONNECT: + m_clientConnections.remove(event.peer); + return; + default: + break; + } + } + + enet_host_destroy(m_host); + } +} + +bool Server::run() +{ + /* create eating order list first */ + for(unsigned int i = 0; i < m_maxplayers; ++i) + m_eatingorder.append(Color::order[i]); + m_eatingorder.append(m_eatingorder.at(0)); + + m_tickTimer = new QTimer(this); + m_tickTimer->setInterval(Constants::tick); + connect(m_tickTimer, SIGNAL(timeout()), this, SLOT(tick())); + + std::cout << "[Server] Running server..." << std::endl + << "[Server] Max players: " << m_maxplayers << std::endl + << "[Server] Number of bots: " << m_numbots << std::endl + << "[Server] Number of rounds: " << m_rounds << std::endl; + if (!waitForClientConnections()) + return false; + + connect(this, SIGNAL(allPointsRemoved()), this, SLOT(setFinishRound())); + initRoundMap(); + return true; +} + +void Server::tick() +{ + //qDebug() << "[Tick] Doing server update"; + if (m_finishRound) + stopGame(true); + if (!m_running) + { + Transmission::map_t map = Util::createEmptyMap(); + sendUpdate(map); + Util::deleteMap(map); + return; + } + + /* fetch key updates */ + keyPressUpdate(); + + /* let the bots move */ + foreach (Color::Color color, m_bots) + botCalculate(m_actors[color]); + + /* move on the virtual map */ + Transmission::map_t map = calculateUpdates(); + updateMap(map); + + /* add a random bonus point */ + QPoint pos = addRandomPoint(map, Transmission::bonuspoint); + if (!pos.isNull()) + updateMap(map, pos.x(), pos.y()); + + /* add/remove random colorized block */ + if (this->property("coloredblocks").toBool()) + colorizeBlocks(map); + + sendUpdate(map); + Util::deleteMap(map); +} + +Transmission::map_t Server::calculateUpdates() +{ + QMap > movements; + Transmission::map_t map = Util::createEmptyMap(); + + QMutableMapIterator i(m_actorMovements); + while (i.hasNext()) + { + i.next(); + Color::Color color = i.key(); + Actor *actor = m_actors[color]; + QPoint mapPosition = CoordToMapPosition(actor->pos().toPoint()); + Actor::Movement direction = i.value(); + int turn = 0; + +invalid_direction: + ++turn; + qDebug() << "[Calc] Actor wants to move: color=" << color + << "pos=" << mapPosition << "direction=" << direction; + + QPoint newMapPosition = mapPosition + Actor::movementToPoint(direction); + if (newMapPosition.x() < 0) + newMapPosition.setX(0); + if (newMapPosition.x() >= visualMap.size()) + newMapPosition.setX(visualMap.size() - 1); + if (newMapPosition.y() < 0) + newMapPosition.setY(0); + if (newMapPosition.y() >= visualMap[newMapPosition.x()].size()) + newMapPosition.setY(visualMap[newMapPosition.x()].size() - 1); + + /* check if there's an item at new location of actor */ + GameEntity *item = visualMap[newMapPosition.x()][newMapPosition.y()]; + GameEntity *oldItem = visualMap[mapPosition.x()][mapPosition.y()]; + if (item != NULL && oldItem != item) + { + qDebug() << "[Calc] Found item at new actor location"; + if (!item->checkEnter(actor)) + { + /* movement invalid. e.g. move against wall */ + newMapPosition = mapPosition; + qDebug() << "[Calc] Item blocks actor"; + } + else + { + /* apply actions of entering this field */ + GameEntity::EnteredState survive = item->enter(actor); + if (survive == GameEntity::DestroyedEntity) + map[newMapPosition.x()][newMapPosition.y()] = Transmission::empty | actor->color(); + else if (survive == GameEntity::DestroyedActor) + { + map[newMapPosition.x()][newMapPosition.y()] = Transmission::death | actor->color(); + m_actors[item->color()]->addRoundPoints(actor->getRoundPoints()); + actor->finishRound(true); + setFinishRound(); + } + } + } + + /* movement didn't work - e.g. was blocked */ + if (mapPosition == newMapPosition) + { + /* check turn */ + if (turn == 1 && direction != actor->direction()) + { + /* set direction back to last known direction and try again */ + qDebug() << "[Calc] Movement was blocked. Trying last known actor direction"; + direction = actor->direction(); + goto invalid_direction; + } + else + { + /* second turn didn't work too -> stop movement */ + direction = Actor::None; + qDebug() << "[Calc] No good direction known. Movement stopped"; + } + } + + /* store movement for collision */ + movements.insert(color, QPair(mapPosition, newMapPosition)); + + /* set direction (used for collision detection) */ + actor->setDirection(direction); + + /* DEBUG: uncomments to disable auto-movement */ + //direction = Actor::None; + + /* actor is not moving anymore: remove from list */ + if (direction == Actor::None) + i.remove(); + } + + /* check for actor collision */ + QList blocked; + foreach(Color::Color color, movements.keys()) + { + Actor *actor = m_actors[color]; + QPoint oldpos = movements[color].first; + QPoint newpos = movements[color].second; + QPoint scenepos = actor->pos().toPoint(); + + /* first move actor to new position */ + actor->move(actor->direction()); + + /* next check for collisions */ + Actor *orderer = NULL; + Actor *meal = NULL; + foreach(Actor *other, m_actors) + { + if (actor == other) + continue; + if (!actor->collidesWithItem(other)) + continue; + /* both move in the same direction */ + if (actor->direction() == other->direction()) + continue; + + if (other->canEat(actor, m_eatingorder)) + { + qDebug() << "[Collision] Actor" << actor->color() << "got EATEN by" << other->color(); + orderer = other; + meal = actor; + break; + } + else if (actor->canEat(other, m_eatingorder)) + { + qDebug() << "[Collision] Actor" << actor->color() << "EATS" << other->color(); + orderer = actor; + meal = other; + blocked.append(other); + break; + } + else + { + qDebug() << "[Collision] Actor" << actor->color() << "got BLOCKED by" << other->color(); + blocked.append(actor); + /* no break here */ + } + } + + /* update map depending on collision */ + if (orderer != NULL && meal != NULL) + { + map[newpos.x()][newpos.y()] |= Transmission::pacman | Transmission::death + | orderer->color() | meal->color(); + orderer->addRoundPoints(meal->getRoundPoints()); + meal->finishRound(true); + setFinishRound(); + } + else if (blocked.contains(actor)) + { + /* move actor back early cause he won't move */ + actor->setPos(scenepos); + map[oldpos.x()][oldpos.y()] |= Transmission::pacman | color; + } + else + { + map[newpos.x()][newpos.y()] |= Transmission::pacman | color; + } + } + + /* move all actors back to their origin position */ + foreach(Color::Color color, movements.keys()) + m_actors[color]->setPos(mapPositionToCoord(movements[color].first)); + +#ifndef QT_NO_DEBUG + /* collision sanity check: colors must be unique */ + foreach(Color::Color col, m_eatingorder.toSet()) + { + QList found; + for (unsigned int x = 0; x < Constants::map_size.width; ++x) + { + for (unsigned int y = 0; y < Constants::map_size.height; ++y) + { + if ((map[x][y] & Transmission::color_mask) & col) + found.append(QPoint(x, y)); + } + } + if (found.count() > 1) + qCritical() << "[Collision] found" << found.count() << "fields with color=" << col; + } +#endif + + return map; +} + +QPoint Server::addRandomPoint(Transmission::map_t map, Transmission::field_t type) +{ + int chance = Constants::Game::bouns_point_chance + - Constants::Game::bouns_point_chance_playerfactor * (m_maxplayers - 1); + int rand = (int) (chance * (qrand() / (RAND_MAX + 1.0))); + if (rand != 0) + return QPoint(); + + /* get list of valid positions */ + QList validpos; + for (unsigned int x = 0; x < Constants::map_size.width; ++x) + { + for (unsigned int y = 0; y < Constants::map_size.height; ++y) + { + if (visualMap[x][y] == NULL) + validpos.append(QPoint(x, y)); + } + } + + /* remove actors positions from list + * performance would be better if actors would be listed in visualMap too + * but this isn't possible that easily. see comment in updateMap(map) + */ + foreach (Actor *actor, m_actors) + { + QPoint actorpos = CoordToMapPosition(actor->pos().toPoint()); + validpos.removeAll(actorpos); + } + + if (validpos.empty()) + return QPoint(); + + rand = (int) (validpos.size() * (qrand() / (RAND_MAX + 1.0))); + QPoint pos = validpos.at(rand); + map[pos.x()][pos.y()] = type; + return pos; +} + +void Server::colorizeBlocks(Transmission::map_t map) +{ + /* decrement tickcount of colored blocks */ + if (!m_coloredBlocks.empty()) + { + QMutableMapIterator i(m_coloredBlocks); + while(i.hasNext()) + { + i.next(); + unsigned val = i.value(); + if (val > 0) + i.setValue(--val); + else + { + QPoint block = i.key(); + /* check for actor collision */ + bool skip = false; + foreach (Actor *actor, m_actors) + { + if (CoordToMapPosition(actor->pos().toPoint()) == block) + skip = true; + if (skip) + break; + } + if (skip) + continue; + + map[block.x()][block.y()] |= Transmission::block | Color::none; + updateMap(map, block.x(), block.y()); + m_blocks.append(block); + i.remove(); + } + } + } + + /* colorize a random block */ + int rand = (int) (Constants::Game::colorize_block_chance * (qrand() / (RAND_MAX + 1.0))); + if (rand == 0) + { + rand = (int) (m_blocks.size() * (qrand() / (RAND_MAX + 1.0))); + QPoint block = m_blocks.at(rand); + m_blocks.removeAt(rand); + + unsigned int min = Constants::Game::colorize_block_tickcount_min; + unsigned int max = Constants::Game::colorize_block_tickcount_max; + int tickcount = min + (int) ((max - min + 1) * (qrand() / (RAND_MAX + 1.0))); + m_coloredBlocks.insert(block, tickcount); + + unsigned int color = (int) (m_maxplayers * (qrand() / (RAND_MAX + 1.0))); + map[block.x()][block.y()] |= Transmission::block | Color::order[color]; + updateMap(map, block.x(), block.y()); + } +} + +bool Server::waitForClientConnections() +{ + // server must stay alive as long as sockets (qt parent mem mechanism) + ENetAddress address; + address.host = ENET_HOST_ANY; + if (m_bindaddress != QHostAddress::Any) + enet_address_set_host(&address, qPrintable(m_bindaddress.toString())); + address.port = m_port; + + m_host = enet_host_create(&address, m_maxplayers - m_numbots, 1, 0, 0); + if (m_host == NULL) + { + qCritical() << "An error occurred while trying to create an ENet server host"; + return false; + } + + char buf[1024]; + enet_address_get_host_ip(&m_host->address, buf, 1024); + std::cout << "[Server] Listening on: " + << qPrintable(QString("%1:%2").arg(buf).arg(m_host->address.port)) << std::endl; + + /* add bots first */ + for (unsigned int i = (m_maxplayers - m_numbots); i < m_maxplayers; ++i) + { + m_bots.append(Color::order[i]); + m_actorMovements[Color::order[i]] = Actor::None; + } + + std::cout << "[Server] Waiting for clients" << std::endl; + ProtoBuf::Init packet; + packet.set_maxplayers(m_maxplayers); + unsigned int i = 0; + while(m_clientConnections.count() < int(m_maxplayers - m_numbots)) + { + ENetEvent event; + if (enet_host_service(m_host, &event, 3000) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_CONNECT: + { + /* assign color and notify client */ + Color::Color color = Color::order[i++]; + m_clientConnections[event.peer] = color; + packet.set_color(color); + Util::sendPacket(packet, event.peer, m_host); + std::cout << "[Connect] New Player: color=" << qPrintable(Util::colorToString(color)) << std::endl; + } + break; + case ENET_EVENT_TYPE_RECEIVE: + keyPressUpdate(&event); + break; + case ENET_EVENT_TYPE_DISCONNECT: + keyPressUpdate(&event); + break; + default: + break; + } + } + } + + qDebug() << "[Server] All Clients connected"; + return true; +} + +void Server::sendUpdate(Transmission::map_t map, bool firstPacket) +{ + m_updatepacket.Clear(); + + for (unsigned int x = 0; x < Constants::map_size.width; ++x) + { + for (unsigned int y = 0; y < Constants::map_size.height; ++y) + m_updatepacket.add_field(map[x][y]); + } + + for(unsigned int i = 0; i < m_maxplayers; ++i) + { + m_updatepacket.add_round_points(m_actors.value(Color::order[i])->getRoundPoints()); + m_updatepacket.add_game_points(m_actors.value(Color::order[i])->getGamePoints()); + } + + /* we send the eating_order inside the first packet */ + if (firstPacket) + { + foreach(Color::Color color, m_eatingorder) + m_updatepacket.add_eating_order(color); + } + + QSharedPointer data = Util::createPacket(m_updatepacket); + QMutableMapIterator i(m_clientConnections); + while(i.hasNext()) + { + i.next(); + ENetPeer *peer = i.key(); + + if (!Util::sendPacket(data.data(), peer, m_host)) + { + qWarning() << "[Connect] Error while sending data to client" << i.value() << "-> Disconnecting..."; + std::cout << "[Connect] Actor color=" << qPrintable(Util::colorToString(i.value())) + << " is now a bot" << std::endl; + m_bots.append(i.value()); + ++m_numbots; + i.remove(); + } + } + + if (m_maxplayers == m_numbots) + { + std::cout << "[Connect] No more real players left. Exiting..." << std::endl; + qApp->quit(); + } +} + +void Server::botCalculate(Actor *actor) +{ + QPoint actorpos = CoordToMapPosition(actor->pos().toPoint()); + + /* first make list of possible directions based on current actor position */ + QHash directions; + if (actorpos.x() > 0) + directions.insert(Actor::Left, 0); + if (actorpos.x() < visualMap.size() - 1) + directions.insert(Actor::Right, 0); + if (actorpos.y() > 0) + directions.insert(Actor::Up, 0); + if (actorpos.y() < visualMap[actorpos.x()].size() - 1) + directions.insert(Actor::Down, 0); + + /* check neighbours for blocks first */ + QMutableHashIterator i(directions); + while(i.hasNext()) + { + i.next(); + QPoint newpos = actorpos + Actor::movementToPoint(i.key()); + if (newpos.x() < 0 || newpos.x() >= visualMap.size()) + continue; + if (newpos.y() < 0 || newpos.y() >= visualMap[newpos.x()].size()) + continue; + GameEntity *item = visualMap[newpos.x()][newpos.y()]; + + /* check if neighbour is a block */ + Block *block = qgraphicsitem_cast(item); + if (block != NULL && block->color() != actor->color()) + i.remove(); + } + + /* we're enclosed by blocks */ + if (directions.empty()) + return; + + /* determine if other actors are in range to be afraid/to hunt */ + int mindistance = Constants::AI::player_minimum_distance; + QList pos_afraid; + QList pos_hunt; + foreach (Actor *other, m_actors) + { + if (actor == other) + continue; + + QList *ptr = NULL; + if (other->canEat(actor, m_eatingorder)) + ptr = &pos_afraid; + else if (actor->canEat(other, m_eatingorder)) + ptr = &pos_hunt; + if (ptr == NULL) + continue; + + QPoint otherpos = CoordToMapPosition(other->pos().toPoint()); + QPoint distance = actorpos - otherpos; + if (distance.manhattanLength() < mindistance) + ptr->append(otherpos); + } + + /* check new directions and change direction-weight */ + i = directions; + while(i.hasNext()) + { + i.next(); + QPoint newpos = actorpos + Actor::movementToPoint(i.key()); + + /* check for new positions in afraid list */ + foreach(QPoint otherpos, pos_afraid) + { + int olddistance = (actorpos - otherpos).manhattanLength(); + int newdistance = (newpos - otherpos).manhattanLength(); + if (newdistance >= olddistance) + i.setValue(i.value() + Constants::AI::weight_afraid); + + /* check for blocks of own color: other pacman can't follow their */ + GameEntity *item = visualMap[newpos.x()][newpos.y()]; + Block *block = qgraphicsitem_cast(item); + if (block != NULL && block->color() == actor->color()) + i.setValue(i.value() + Constants::AI::weight_colorblock); + } + + /* check for new positions in hunt list */ + foreach(QPoint otherpos, pos_hunt) + { + int olddistance = (actorpos - otherpos).manhattanLength(); + int newdistance = (newpos - otherpos).manhattanLength(); + if (newdistance <= olddistance) + i.setValue(i.value() + Constants::AI::weight_hunt); + } + + /* check for bonuspoint */ + GameEntity *item = visualMap[newpos.x()][newpos.y()]; + BonusPoint *bpoint = qgraphicsitem_cast(item); + if (bpoint != NULL) + i.setValue(i.value() + Constants::AI::weight_bonus_point); + + /* check for normal point */ + Point *point = qgraphicsitem_cast(item); + if (point != NULL) + i.setValue(i.value() + Constants::AI::weight_point); + } + + /* sort directions */ + QList weightlist = directions.values(); + qSort(weightlist.begin(), weightlist.end(), qGreater()); + + /* remove directions with lesser weight */ + unsigned int max = weightlist.at(0); + i = directions; + while(i.hasNext()) + { + i.next(); + if (i.value() < max) + i.remove(); + } + + QList list = directions.keys(); + + /* default is no direction change */ + if (list.contains(actor->direction())) + return; + + /* random direction */ + int rand = (int) (list.size() * (qrand() / (RAND_MAX + 1.0))); + m_actorMovements[actor->color()] = list.at(rand); +} + +void Server::keyPressUpdate() +{ + ProtoBuf::KeyPressUpdate packet; + ENetEvent event; + while (enet_host_service(m_host, &event, 1) > 0) + keyPressUpdate(&event); +} + +void Server::keyPressUpdate(ENetEvent *event) +{ + ProtoBuf::KeyPressUpdate packet; + switch(event->type) + { + case ENET_EVENT_TYPE_RECEIVE: + { + QSharedPointer data = Util::receivePacket(event->packet); + enet_packet_destroy(event->packet); + bool worked = packet.ParseFromArray(data->data(), data->size()); + Q_ASSERT(worked); + Q_UNUSED(worked); + Transmission::field_t direction = packet.newkey(); + Color::Color color = m_clientConnections[event->peer]; + qDebug() << "[KeyPress] actor=" << color << "direction=" << direction; + m_actorMovements[color] = Util::transmissionMovementToActor(direction); + } + break; + case ENET_EVENT_TYPE_DISCONNECT: + { + Color::Color color = m_clientConnections[event->peer]; + std::cout << "[Connect] Actor color=" << qPrintable(Util::colorToString(color)) + << " is now a bot" << std::endl; + m_bots.append(color); + ++m_numbots; + m_clientConnections.remove(event->peer); + } + break; + default: + break; + } +} + +void Server::initRoundMap() +{ + std::cout << "[Game] New round starts..." << std::endl; + m_tickTimer->stop(); + + /* reset scene and clean up items */ + reset(); + + /* randomize color eating order */ + m_eatingorder.removeLast(); + random_shuffle(m_eatingorder.begin(), m_eatingorder.end()); + m_eatingorder.append(m_eatingorder.at(0)); + + /* create new map */ + Transmission::map_t map = Util::createDemoMap(); + Util::placeActors(map, m_maxplayers, Color::order); + Util::fillPoints(map); + +#if 0 // actor eating actor tests - TODO: remove + m_actorMovements.clear(); +#if 0 + //works + map[0][0] = Color::order[0] | Transmission::pacman; + map[0][1] = Color::order[1] | Transmission::pacman; + m_actorMovements.insert(Color::order[0], Actor::Down); +#elif 0 + //works + map[0][0] = Color::order[0] | Transmission::pacman; + map[0][3] = Color::order[1] | Transmission::pacman; + m_actorMovements.insert(Color::order[0], Actor::Down); +#elif 0 + //works + map[0][0] = Color::order[0] | Transmission::pacman; + map[0][4] = Color::order[1] | Transmission::pacman; + m_actorMovements.insert(Color::order[0], Actor::Down); +#elif 0 + //works + map[0][0] = Color::order[0] | Transmission::pacman; + map[0][5] = Color::order[1] | Transmission::pacman; + m_actorMovements.insert(Color::order[0], Actor::Down); +#elif 0 + //works + map[0][0] = Color::order[1] | Transmission::pacman; + map[0][5] = Color::order[0] | Transmission::pacman; + m_actorMovements.insert(Color::order[1], Actor::Down); +#elif 0 + //works + map[0][0] = Color::order[0] | Transmission::pacman; + map[0][5] = Color::order[1] | Transmission::pacman; + m_actorMovements.insert(Color::order[0], Actor::Down); + m_actorMovements.insert(Color::order[1], Actor::Up); +#elif 0 + //works + map[0][0] = Color::order[0] | Transmission::pacman; + map[0][6] = Color::order[1] | Transmission::pacman; + m_actorMovements.insert(Color::order[0], Actor::Down); + m_actorMovements.insert(Color::order[1], Actor::Up); +#elif 0 + //works + map[0][0] = Color::order[0] | Transmission::pacman; + map[0][7] = Color::order[1] | Transmission::pacman; + m_actorMovements.insert(Color::order[0], Actor::Down); + m_actorMovements.insert(Color::order[1], Actor::Up); +#elif 1 + //works + map[0][0] = Color::order[0] | Transmission::pacman; + map[0][1] = Color::order[1] | Transmission::pacman; + m_actorMovements.insert(Color::order[0], Actor::Down); + m_actorMovements.insert(Color::order[1], Actor::Down); +#endif +#endif + + /* save positions of blocks for later usage */ + m_blocks.clear(); + for (unsigned int x = 0; x < Constants::map_size.width; ++x) + { + for (unsigned int y = 0; y < Constants::map_size.height; ++y) + { + Transmission::field_t &cur = map[x][y]; + if (cur & Transmission::block) + m_blocks.append(QPoint(x, y)); + } + } + + updateMap(map); + sendUpdate(map, true); + Util::deleteMap(map); + map = NULL; + + m_actorMovements.clear(); + + disconnect(AudioManager::self()->audioPlayer(), NULL, this, NULL); + connect(AudioManager::self()->audioPlayer(), SIGNAL(finished()), this, SLOT(startGame())); + AudioManager::self()->play(Sound::Intro, true); + m_tickTimer->start(); +} + +void Server::startGame() +{ + m_running = true; +} + +void Server::stopGame(bool delay) +{ + /* first finish previous round */ + foreach(Actor *actor, m_actors) + actor->finishRound(); + m_finishRound = false; + m_running = false; + + /* add delay if requested */ + if (delay) + { + disconnect(AudioManager::self()->audioPlayer(), NULL, this, NULL); + connect(AudioManager::self()->audioPlayer(), SIGNAL(finished()), this, SLOT(stopGame())); + AudioManager::self()->play(Sound::Die, true); + return; + } + + std::cout << "[Game] Round finished..." << std::endl; + + /* do next-round work */ + ++m_curRound; + if(m_rounds == 0 || m_curRound < m_rounds) + initRoundMap(); + else + { + /* end of game */ + std::cout << "[Game] All round finished. Exiting..." << std::endl; + qApp->quit(); + } +} + +void Server::setFinishRound() +{ + m_finishRound = true; +} + +bool Server::parseCommandline() +{ + AnyOption opt; + opt.setVerbose(); + + /* usage strings must remain valid until parsing is done */ + QString exec = QFileInfo(qApp->applicationFilePath()).fileName(); + QByteArray usage; + QTextStream out(&usage, QIODevice::ReadWrite | QIODevice::Text); + out << "Usage: " << exec << " [OPTION]" << endl + << "Usage: " << exec << " -h" << endl + << endl; + out << " -b, --bind " << endl + << " Specifies the ip address on which the server listens for connections" << endl + << " Default: " << m_bindaddress.toString() << endl + << endl; + opt.setOption("bind", 'b'); + out << " -p, --port " << endl + << " Specifies the port on which the server listens for connections" << endl + << " Default: " << m_port << endl + << endl; + opt.setOption("port", 'p'); + out << " -m, --maxplayers [1.." << m_maxplayers << "]" << endl + << " Specifies the maximum number of players/pacmans" << endl + << " Default: " << m_maxplayers << endl + << endl; + opt.setOption("maxplayers", 'm'); + out << " --bots [0..maxplayers-1]" << endl + << " Specifies the number of AI pacmans/bots" << endl + << " Default: " << m_numbots << endl + << endl; + opt.setOption("bots"); + out << " --nocolorblocks" << endl + << " Disable random colorized blocks" << endl + << endl; + opt.setOption("rounds", 'r'); + out << " -r, --rounds [0 | 1..n]" << endl + << " Number of rounds to play" << endl + << " Default: " << m_rounds << endl + << endl; + opt.setFlag("nocolorblocks"); + out << " -h, --help" << endl + << " Prints this help message" << endl; + opt.setFlag("help", 'h'); + out.flush(); + opt.addUsage(usage.constData()); + opt.processCommandArgs(qApp->argc(), qApp->argv()); + + if (opt.getFlag("help") || opt.getFlag('h')) + { + opt.printUsage(); + return false; + } + + if (opt.getValue("port") != NULL) + { + bool ok; + m_port = QString(opt.getValue("port")).toUInt(&ok); + if (!ok || m_port < 1 || m_port > 65535) + { + qCritical() << "Invalid port-option:" << opt.getValue("port") << endl + << "Port must be between 1 and 65535"; + return false; + } + } + + if (opt.getValue("bind") != NULL) + { + m_bindaddress = opt.getValue("bind"); + if (m_bindaddress.isNull()) + { + qCritical() << "Invalid bind-option:" << opt.getValue("bind") << endl + << "Bind address must be an ip address"; + return false; + } + } + + if (opt.getValue("maxplayers") != NULL) + { + bool ok; + unsigned int maxplayers = QString(opt.getValue("maxplayers")).toUInt(&ok); + if (!ok || maxplayers < 1 || maxplayers > m_maxplayers) + { + qCritical() << "Invalid maxplayers-option:" << opt.getValue("maxplayers") << endl + << "Maxplayers must be between 1 and" << m_maxplayers; + return false; + } + m_maxplayers = maxplayers; + if (m_maxplayers == 2) + { + qCritical() << "2 player game is not supported (who wins if a player gets eaten?)"; + return false; + } + } + + if (opt.getValue("bots") != NULL) + { + bool ok; + unsigned int numbots = QString(opt.getValue("bots")).toUInt(&ok); + if (!ok || numbots >= m_maxplayers) + { + qCritical() << "Invalid numbots-options:" << opt.getValue("bots") << endl + << "AI pacmans/bots must be between 0 and" << m_maxplayers - 1; + return false; + } + m_numbots = numbots; + } + + if (opt.getValue("rounds") != NULL) + { + bool ok; + unsigned int rounds = QString(opt.getValue("rounds")).toUInt(&ok); + if (!ok) + { + qCritical() << "Invalid number of rounds: " << opt.getValue("rounds") << endl; + return false; + } + m_rounds = rounds; + } + + this->setProperty("coloredblocks", !opt.getFlag("nocolorblocks")); + + return true; +} + +bool operator<(const QPoint& lhs, const QPoint& rhs) +{ + if (lhs.x() < rhs.x()) + return true; + else if (lhs.x() == rhs.x()) + return lhs.y() < rhs.y(); + else + return false; +} + +bool Constants::server = true; + +int main(int argc, char **argv) +{ + /* Verify that the version of the library that we linked against is + * compatible with the version of the headers we compiled against. + */ + GOOGLE_PROTOBUF_VERIFY_VERSION; + + QApplication app(argc, argv, false); + app.setApplicationName("Pacman Server"); + app.setWindowIcon(QIcon(":/appicon")); + + qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime())); + + int ret = 0; + Server server; + if (!ret && !server.parseCommandline()) + ret = 1; + if (!ret && !server.run()) + ret = 1; + if (!ret) + ret = app.exec(); + + /* Delete all global objects allocated by libprotobuf */ + google::protobuf::ShutdownProtobufLibrary(); + + return ret; +} diff --git a/pacman-c++/server/server.h b/pacman-c++/server/server.h new file mode 100644 index 0000000..857f23d --- /dev/null +++ b/pacman-c++/server/server.h @@ -0,0 +1,85 @@ +#ifndef SERVER_H +#define SERVER_H + +#include "sceneholder.h" +#include "actor.h" +#include "pacman.pb.h" +#include +#include + +extern "C" { +#include "enet/enet.h" +} + +class QTcpSocket; + +class Server + : public SceneHolder +{ + Q_OBJECT +public: + Server(QWidget *parent = 0); + ~Server(); + bool parseCommandline(); + bool run(); + +protected slots: + void tick(); + + /* receive updates of client */ + void keyPressUpdate(); + void keyPressUpdate(ENetEvent *event); + +protected: + /* block until we have connections from all clients */ + bool waitForClientConnections(); + + /* calculate updates of current tick for sending to client */ + Transmission::map_t calculateUpdates(); + + /* update client maps */ + void sendUpdate(Transmission::map_t map, bool firstPacket = false); + + QPoint addRandomPoint(Transmission::map_t map, Transmission::field_t type = Transmission::bonuspoint); + void colorizeBlocks(Transmission::map_t map); + void botCalculate(Actor *actor); + void initRoundMap(); + +protected slots: + /* called when a round is started/finished */ + void startGame(); + void stopGame(bool delay = false); + void setFinishRound(); + +protected: + ENetHost *m_host; + QMap m_clientConnections; + QList m_bots; + + /* current movements. required to make pacmans continue their movement */ + QMap m_actorMovements; + + /* allocate as member variable as this packet is large and used often */ + ProtoBuf::MapUpdate m_updatepacket; + + /* list of blocks */ + QList m_blocks; + /* currently colored blocks + tickcount before they will turn to non-colored back */ + QMap m_coloredBlocks; + + QHostAddress m_bindaddress; + unsigned int m_port; + unsigned int m_maxplayers; + unsigned int m_numbots; + /* number of rounds (>= 1) */ + unsigned int m_rounds; + /* current round, starting at 0 */ + unsigned int m_curRound; + bool m_running; + bool m_finishRound; + + QTimer *m_tickTimer; + +}; + +#endif // SERVER_H diff --git a/pacman-c++/server/server.pro b/pacman-c++/server/server.pro new file mode 100644 index 0000000..05d5c9d --- /dev/null +++ b/pacman-c++/server/server.pro @@ -0,0 +1,10 @@ +TEMPLATE = app +TARGET = pacman-server + +SOURCES += anyoption.cpp \ + server.cpp +HEADERS += anyoption.h \ + server.h + +include(../common.pri) +PRE_TARGETDEPS += ../common/libcommon.a diff --git a/pacman-c++/sound/ambient.ogg b/pacman-c++/sound/ambient.ogg deleted file mode 100644 index 2919513..0000000 Binary files a/pacman-c++/sound/ambient.ogg and /dev/null differ diff --git a/pacman-c++/sound/die.ogg b/pacman-c++/sound/die.ogg deleted file mode 100644 index f6af2e0..0000000 Binary files a/pacman-c++/sound/die.ogg and /dev/null differ diff --git a/pacman-c++/sound/eating_fruit.ogg b/pacman-c++/sound/eating_fruit.ogg deleted file mode 100644 index 752864a..0000000 Binary files a/pacman-c++/sound/eating_fruit.ogg and /dev/null differ diff --git a/pacman-c++/sound/eating_ghost.ogg b/pacman-c++/sound/eating_ghost.ogg deleted file mode 100644 index a204a2c..0000000 Binary files a/pacman-c++/sound/eating_ghost.ogg and /dev/null differ diff --git a/pacman-c++/sound/intro.ogg b/pacman-c++/sound/intro.ogg deleted file mode 100644 index d38f16b..0000000 Binary files a/pacman-c++/sound/intro.ogg and /dev/null differ diff --git a/pacman-c++/sound/waka_waka.ogg b/pacman-c++/sound/waka_waka.ogg deleted file mode 100644 index def0d96..0000000 Binary files a/pacman-c++/sound/waka_waka.ogg and /dev/null differ diff --git a/pacman-c++/style.qss b/pacman-c++/style.qss deleted file mode 100644 index 1a24459..0000000 --- a/pacman-c++/style.qss +++ /dev/null @@ -1,31 +0,0 @@ -QGroupBox#actor1::indicator { - background-image: url(:/actor1icon); - background-position: top left; - background-repeat: no-repeat; - width: 16px; - height: 14px; -} - -QGroupBox#actor2::indicator { - background-image: url(:/actor2icon); - background-position: top left; - background-repeat: no-repeat; - width: 16px; - height: 14px; -} - -QGroupBox#actor3::indicator { - background-image: url(:/actor3icon); - background-position: top left; - background-repeat: no-repeat; - width: 16px; - height: 14px; -} - -QGroupBox#actor4::indicator { - background-image: url(:/actor4icon); - background-position: top left; - background-repeat: no-repeat; - width: 16px; - height: 14px; -} diff --git a/pacman-c++/util.cpp b/pacman-c++/util.cpp deleted file mode 100644 index 09b2be1..0000000 --- a/pacman-c++/util.cpp +++ /dev/null @@ -1,357 +0,0 @@ -#include "util.h" -#include - -namespace Util -{ - Transmission::map_t createUninitialisedMap() - { - Transmission::map_t map; - map = new Transmission::field_t*[Constants::map_size.width]; - for (unsigned int i = 0; i < Constants::map_size.width; ++i) - map[i] = new Transmission::field_t[Constants::map_size.height]; - return map; - } - - Transmission::map_t createEmptyMap() - { - Transmission::map_t map = createUninitialisedMap(); - for (unsigned int x = 0; x < Constants::map_size.width; ++x) - { - for (unsigned int y = 0; y < Constants::map_size.height; ++y) - { - Transmission::field_t &cur = map[x][y]; - cur = Transmission::none; - } - } - return map; - } - - void deleteMap(Transmission::map_t map) - { - for (unsigned int x = 0; x < Constants::map_size.width; ++x) - delete[] map[x]; - delete[] map; - } - - Transmission::map_t createDemoMap() - { - Transmission::map_t map = createEmptyMap(); - - const char *tmpl[] = { - " # # ", - " #### ###### # #### # # ###### ### ", - " # # ", - " # ##### # # # # # ### # # # ", - " # # # # # # # # # # ## # # ", - " # # # # # # # # ### # # # # ", - " # # # # # # # # # # # # ## # ", - " # # ### ##### # ### # # # ", - " ### # ", - " # # ### #### #### #### ##### ", - " #### # #..# #..# #..# # # ", - " # # ### #..# #..# #### # # # # ", - " # # # #..# #..# # # ", - " # #### # #### #### # # ##### # ", - " # # ", - " #### ###### # ##### # ####### ### ", - " # # " - }; - - for (unsigned int x = 0; x < Constants::map_size.width; ++x) - { - for (unsigned int y = 0; y < Constants::map_size.height; ++y) - { - Transmission::field_t &cur = map[x][y]; - cur = Transmission::none; - if (tmpl[y][x] == '#') - cur |= Color::none | Transmission::block; - /* this is a simple hack to create areas where no - * autoplaced points/actors will be placed (see makePoints) - */ - else if (tmpl[y][x] == '.') - cur |= Transmission::point; - } - } - return map; - } - - void placeActors(Transmission::map_t map, unsigned int players, const Color::Color *colors) - { -#if 0 - for(unsigned int i = 0; i < players; ++i) - map[i][0] = colors[i] | Transmission::pacman; - return; -#endif - - int mindistance = Constants::Game::player_minimum_distance; - /* this outer loop is used if there are no more valid places left - * so we can change mindistance - */ - QList actors; - for(unsigned int i = 0; i < players; ++i) - { - /* first remove formerly placed actors from map */ - foreach(QPoint pos, actors) - map[pos.x()][pos.y()] = Transmission::none; - actors.clear(); - - /* get list of valid positions */ - QList validpos; - for (unsigned int x = 0; x < Constants::map_size.width; ++x) - { - for (unsigned int y = 0; y < Constants::map_size.height; ++y) - { - Transmission::field_t &cur = map[x][y]; - if (cur == Transmission::none) - validpos.append(QPoint(x, y)); - } - } - - /* place actors at map */ - for(i = 0; i < players; ++i) - { - int rand = (int) (validpos.size() * (qrand() / (RAND_MAX + 1.0))); - QPoint newpos = validpos.at(rand); - map[newpos.x()][newpos.y()] = colors[i] | Transmission::pacman; - qDebug() << "[Place] Actor" << i << "at" << newpos; - actors.append(newpos); - validpos.removeAt(rand); - - QMutableListIterator j(validpos); - while(j.hasNext()) - { - j.next(); - QPoint tmp = j.value() - newpos; - if (tmp.manhattanLength() < mindistance) - j.remove(); - } - - if (validpos.empty()) - { - qWarning() << "There are no more valid positions for actors left on the map"; - mindistance -= Constants::Game::player_distance_decr; - break; - } - } - } - } - - void fillPoints(Transmission::map_t map, Transmission::field_t type) - { - /* auto place normal points*/ - for (unsigned int x = 0; x < Constants::map_size.width; ++x) - { - for (unsigned int y = 0; y < Constants::map_size.height; ++y) - { - Transmission::field_t &cur = map[x][y]; - if (cur == Transmission::none) - { -#if 0 - /* use for endround testing */ - if (x > 0) - continue; -#endif - cur = type; - } - else if (cur == Transmission::point) - cur = Transmission::none; - } - } - } - - Transmission::field_t actorMovementToTransmission(Actor::Movement mov, Transmission::field_t def) - { - switch (mov) - { - case Actor::None: - return Transmission::direction_none; - break; - case Actor::Left: - return Transmission::direction_left; - break; - case Actor::Right: - return Transmission::direction_right; - break; - case Actor::Up: - return Transmission::direction_up; - break; - case Actor::Down: - return Transmission::direction_down; - break; - default: - return def; - break; - } - return def; - } - - Actor::Movement transmissionMovementToActor(Transmission::field_t field, Actor::Movement def) - { - switch (field) - { - case Transmission::direction_none: - return Actor::None; - break; - case Transmission::direction_left: - return Actor::Left; - break; - case Transmission::direction_right: - return Actor::Right; - break; - case Transmission::direction_up: - return Actor::Up; - break; - case Transmission::direction_down: - return Actor::Down; - break; - default: - return def; - break; - } - return def; - } - - const QString colorToString(Color::Color color) - { - switch(color) - { - case Color::none: - return "none"; - break; - case Color::red: - return "red"; - break; - case Color::blue: - return "blue"; - break; - case Color::green: - return "green"; - break; - case Color::yellow: - return "yellow"; - break; - default: - return "unknown"; - break; - } - } - - QSharedPointer createPacket(const ::google::protobuf::MessageLite& packet) - { - qint64 packetlen = packet.ByteSize(); - /* datalen = packet with length prepended */ - qint64 datalen = sizeof(qint64) + packetlen; - - QSharedPointer data = QSharedPointer(new QByteArray); - data->resize(datalen); - - /* use QDataStream for length to avoid endianess shit */ - QDataStream out(data.data(), QIODevice::WriteOnly); - out << packetlen; - - /* use protobuf.SerializeWithCachedSizesToArray() to avoid calling protobuf.ByteSize() again */ - ::google::protobuf::uint8 *dataptr = reinterpret_cast(data->data()); - packet.SerializeWithCachedSizesToArray(dataptr + sizeof(qint64)); - - return data; - } - - bool sendPacket(QByteArray *data, QTcpSocket *socket) - { - int bytesWritten = socket->write(*data); - if (bytesWritten != data->size()) - { - qDebug() << "[sendPacket] Not all data has been sent:" - << "written=" << bytesWritten << ", length=" << data->size(); - return false; - } - socket->flush(); - return true; - } - - bool sendPacket(const ::google::protobuf::MessageLite& packet, QTcpSocket *socket) - { - return sendPacket(createPacket(packet).data(), socket); - } - - QSharedPointer receivePacket(QTcpSocket *socket) - { - QDataStream in(socket); - qint64 datalen; - in >> datalen; - - QSharedPointer data = QSharedPointer(new QByteArray); - data->resize(datalen); - socket->read(data->data(), data->size()); - return data; - } - - int floorLog2(unsigned int n) - { - if (n == 0) - return -1; - - int pos = 0; - if (n >= 1<<16) { n >>= 16; pos += 16; } - if (n >= 1<< 8) { n >>= 8; pos += 8; } - if (n >= 1<< 4) { n >>= 4; pos += 4; } - if (n >= 1<< 2) { n >>= 2; pos += 2; } - if (n >= 1<< 1) { pos += 1; } - return pos; - } - -#if 0 - void hexdump(void *pAddressIn, long lSize) - { - char szBuf[100]; - long lIndent = 1; - long lOutLen, lIndex, lIndex2, lOutLen2; - long lRelPos; - struct { char *pData; unsigned long lSize; } buf; - unsigned char *pTmp,ucTmp; - unsigned char *pAddress = (unsigned char *)pAddressIn; - - buf.pData = (char *)pAddress; - buf.lSize = lSize; - - while (buf.lSize > 0) - { - pTmp = (unsigned char *)buf.pData; - lOutLen = (int)buf.lSize; - if (lOutLen > 16) - lOutLen = 16; - - // create a 64-character formatted output line: - sprintf(szBuf, " > " - " " - " %08lX", pTmp-pAddress); - lOutLen2 = lOutLen; - - for(lIndex = 1+lIndent, lIndex2 = 53-15+lIndent, lRelPos = 0; - lOutLen2; - lOutLen2--, lIndex += 2, lIndex2++ - ) - { - ucTmp = *pTmp++; - - sprintf(szBuf + lIndex, "%02X ", (unsigned short)ucTmp); - if(!isprint(ucTmp)) ucTmp = '.'; // nonprintable char - szBuf[lIndex2] = ucTmp; - - if (!(++lRelPos & 3)) // extra blank after 4 bytes - { lIndex++; szBuf[lIndex+2] = ' '; } - } - - if (!(lRelPos & 3)) lIndex--; - - szBuf[lIndex ] = '<'; - szBuf[lIndex+1] = ' '; - - printf("%s\n", szBuf); - - buf.pData += lOutLen; - buf.lSize -= lOutLen; - } - } -#endif -} diff --git a/pacman-c++/util.h b/pacman-c++/util.h deleted file mode 100644 index d20eaa5..0000000 --- a/pacman-c++/util.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef UTIL_H -#define UTIL_H - -#include "constants.h" -#include "actor.h" -#include "pacman.pb.h" - -class QTcpSocket; - -namespace Util -{ - Transmission::map_t createUninitialisedMap(); - Transmission::map_t createDemoMap(); - void placeActors(Transmission::map_t map, unsigned int players, const Color::Color *colors); - void fillPoints(Transmission::map_t map, Transmission::field_t type = Transmission::point); - Transmission::map_t createEmptyMap(); - void deleteMap(Transmission::map_t map); - - Transmission::field_t actorMovementToTransmission(Actor::Movement mov, - Transmission::field_t def = Transmission::none); - Actor::Movement transmissionMovementToActor(Transmission::field_t field, - Actor::Movement def = Actor::None); - const QString colorToString(Color::Color color); - - /* send packet with error check and flush */ - QSharedPointer createPacket(const ::google::protobuf::MessageLite& packet); - bool sendPacket(QByteArray *data, QTcpSocket *socket); - bool sendPacket(const ::google::protobuf::MessageLite& packet, QTcpSocket *socket); - QSharedPointer receivePacket(QTcpSocket *socket); - - int floorLog2(unsigned int n); - -#if 0 - void hexdump(void *pAddressIn, long lSize); -#endif -} -#endif // UTIL_H -- cgit v1.2.3