summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eblomfel@trolltech.com>2009-04-24 15:35:35 (GMT)
committerEskil Abrahamsen Blomfeldt <eblomfel@trolltech.com>2009-04-24 15:41:43 (GMT)
commitc3290381a1dba7971dd70b7220a71e6feceb3f4c (patch)
tree136f68ab9da00b4571118f18875b3d53928eee46
parentdfe6974c1e7103ad46fa1162d7c9fd6a426894b7 (diff)
downloadQt-c3290381a1dba7971dd70b7220a71e6feceb3f4c.zip
Qt-c3290381a1dba7971dd70b7220a71e6feceb3f4c.tar.gz
Qt-c3290381a1dba7971dd70b7220a71e6feceb3f4c.tar.bz2
Unfinished tank AI game. The idea is that you plug in AIs for the tanks, and
one such plugin will have a run time error, so the game server needs to use errorState for handling errors.
-rw-r--r--examples/statemachine/errorstate/errorstate.pro13
-rw-r--r--examples/statemachine/errorstate/main.cpp12
-rw-r--r--examples/statemachine/errorstate/mainwindow.cpp184
-rw-r--r--examples/statemachine/errorstate/mainwindow.h41
-rw-r--r--examples/statemachine/errorstate/plugin.h16
-rw-r--r--examples/statemachine/errorstate/tank.h31
-rw-r--r--examples/statemachine/errorstate/tankitem.cpp274
-rw-r--r--examples/statemachine/errorstate/tankitem.h48
8 files changed, 619 insertions, 0 deletions
diff --git a/examples/statemachine/errorstate/errorstate.pro b/examples/statemachine/errorstate/errorstate.pro
new file mode 100644
index 0000000..d937b02
--- /dev/null
+++ b/examples/statemachine/errorstate/errorstate.pro
@@ -0,0 +1,13 @@
+######################################################################
+# Automatically generated by qmake (2.01a) on 22. apr 14:11:33 2009
+######################################################################
+
+TEMPLATE = app
+TARGET =
+DEPENDPATH += .
+INCLUDEPATH += C:/dev/kinetic/examples/statemachine/errorstate/. .
+
+# Input
+HEADERS += mainwindow.h plugin.h tank.h tankitem.h
+SOURCES += main.cpp mainwindow.cpp tankitem.cpp
+CONFIG += console
diff --git a/examples/statemachine/errorstate/main.cpp b/examples/statemachine/errorstate/main.cpp
new file mode 100644
index 0000000..26fc1bb
--- /dev/null
+++ b/examples/statemachine/errorstate/main.cpp
@@ -0,0 +1,12 @@
+#include <QApplication>
+#include "mainwindow.h"
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+ MainWindow mainWindow;
+ mainWindow.show();
+
+ return app.exec();
+}
diff --git a/examples/statemachine/errorstate/mainwindow.cpp b/examples/statemachine/errorstate/mainwindow.cpp
new file mode 100644
index 0000000..598756f
--- /dev/null
+++ b/examples/statemachine/errorstate/mainwindow.cpp
@@ -0,0 +1,184 @@
+#include "mainwindow.h"
+#include "tankitem.h"
+#include "plugin.h"
+
+#include <QStateMachine>
+#include <QGraphicsView>
+#include <QAction>
+#include <QMenuBar>
+#include <QState>
+#include <QHistoryState>
+#include <QTimer>
+#include <QFileDialog>
+#include <QPluginLoader>
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent)
+{
+ init();
+}
+
+MainWindow::~MainWindow()
+{
+}
+
+void MainWindow::addWall(const QRectF &wall)
+{
+ QGraphicsRectItem *item = new QGraphicsRectItem;
+ item->setRect(wall);
+ item->setBrush(Qt::darkGreen);
+ item->setPen(QPen(Qt::black, 0));
+
+ m_scene->addItem(item);
+}
+
+void MainWindow::init()
+{
+ setWindowTitle("Pluggable Tank Game");
+
+ QGraphicsView *view = new QGraphicsView(this);
+ setCentralWidget(view);
+
+ m_scene = new QGraphicsScene(this);
+ view->setScene(m_scene);
+
+ QRectF sceneRect = QRectF(-200.0, -200.0, 400.0, 400.0);
+ m_scene->setSceneRect(sceneRect);
+
+ {
+ TankItem *item = new TankItem(this);
+
+ item->setPos(m_scene->sceneRect().topLeft() + QPointF(15.0, 15.0));
+ item->setDirection(45.0);
+ item->setColor(Qt::red);
+
+ m_spawns.append(item);
+ }
+
+ {
+ TankItem *item = new TankItem(this);
+
+ item->setPos(m_scene->sceneRect().topRight() + QPointF(-15.0, 15.0));
+ item->setDirection(135.0);
+ item->setColor(Qt::green);
+
+ m_spawns.append(item);
+ }
+
+ {
+ TankItem *item = new TankItem(this);
+
+ item->setPos(m_scene->sceneRect().bottomRight() + QPointF(-15.0, -15.0));
+ item->setDirection(225.0);
+ item->setColor(Qt::blue);
+
+ m_spawns.append(item);
+ }
+
+ {
+ TankItem *item = new TankItem(this);
+
+ item->setPos(m_scene->sceneRect().bottomLeft() + QPointF(15.0, -15.0));
+ item->setDirection(315.0);
+ item->setColor(Qt::yellow);
+
+ m_spawns.append(item);
+ }
+
+ QPointF centerOfMap = sceneRect.center();
+ addWall(QRectF(centerOfMap + QPointF(-50.0, -60.0), centerOfMap + QPointF(50.0, -50.0)));
+ addWall(QRectF(centerOfMap - QPointF(-50.0, -60.0), centerOfMap - QPointF(50.0, -50.0)));
+ addWall(QRectF(centerOfMap + QPointF(-50.0, -50.0), centerOfMap + QPointF(-40.0, 50.0)));
+ addWall(QRectF(centerOfMap - QPointF(-50.0, -50.0), centerOfMap - QPointF(-40.0, 50.0)));
+
+ addWall(QRectF(sceneRect.topLeft() + QPointF(sceneRect.width() / 2.0 - 5.0, 0.0),
+ sceneRect.topLeft() + QPointF(sceneRect.width() / 2.0 + 5.0, 100.0)));
+ addWall(QRectF(sceneRect.bottomLeft() + QPointF(sceneRect.width() / 2.0 - 5.0, 0.0),
+ sceneRect.bottomLeft() + QPointF(sceneRect.width() / 2.0 + 5.0, -100.0)));
+ addWall(QRectF(sceneRect.topLeft() + QPointF(0.0, sceneRect.height() / 2.0 - 5.0),
+ sceneRect.topLeft() + QPointF(100.0, sceneRect.height() / 2.0 + 5.0)));
+ addWall(QRectF(sceneRect.topRight() + QPointF(0.0, sceneRect.height() / 2.0 - 5.0),
+ sceneRect.topRight() + QPointF(-100.0, sceneRect.height() / 2.0 + 5.0)));
+
+
+ QAction *addTankAction = menuBar()->addAction("&Add tank");
+ QAction *runGameAction = menuBar()->addAction("&Run game");
+ QAction *stopGameAction = menuBar()->addAction("&Stop game");
+ menuBar()->addSeparator();
+ QAction *quitAction = menuBar()->addAction("&Quit");
+
+ connect(addTankAction, SIGNAL(triggered()), this, SLOT(addTank()));
+ connect(stopGameAction, SIGNAL(triggered()), this, SIGNAL(gameOver()));
+ connect(quitAction, SIGNAL(triggered()), this, SLOT(close()));
+
+ m_machine = new QStateMachine(this);
+ m_machine->setGlobalRestorePolicy(QStateMachine::RestoreProperties);
+
+ QState *stoppedState = new QState(m_machine->rootState());
+
+ stoppedState->assignProperty(runGameAction, "enabled", true);
+ stoppedState->assignProperty(stopGameAction, "enabled", false);
+ m_machine->setInitialState(stoppedState);
+
+ QState *spawnsAvailable = new QState(stoppedState);
+ spawnsAvailable->assignProperty(addTankAction, "enabled", true);
+
+ QState *noSpawnsAvailable = new QState(stoppedState);
+ noSpawnsAvailable->assignProperty(addTankAction, "enabled", false);
+
+ spawnsAvailable->addTransition(this, SIGNAL(mapFull()), noSpawnsAvailable);
+
+ QHistoryState *hs = stoppedState->addHistoryState();
+ hs->setDefaultState(spawnsAvailable);
+ stoppedState->setInitialState(spawnsAvailable);
+
+ m_runningState = new QState(QState::ParallelGroup, m_machine->rootState());
+ m_runningState->assignProperty(addTankAction, "enabled", false);
+ m_runningState->assignProperty(runGameAction, "enabled", false);
+ m_runningState->assignProperty(stopGameAction, "enabled", true);
+
+ stoppedState->addTransition(runGameAction, SIGNAL(triggered()), m_runningState);
+ m_runningState->addTransition(this, SIGNAL(gameOver()), stoppedState);
+
+ m_machine->start();
+
+ QTimer *timer = new QTimer(this);
+ timer->setInterval(100);
+ connect(timer, SIGNAL(timeout()), this, SLOT(runStep()));
+ connect(m_runningState, SIGNAL(entered()), timer, SLOT(start()));
+ connect(m_runningState, SIGNAL(exited()), timer, SLOT(stop()));
+
+ m_time.start();
+}
+
+void MainWindow::runStep()
+{
+ int elapsed = m_time.elapsed();
+ if (elapsed > 0) {
+ m_time.restart();
+ qreal elapsedSecs = elapsed / 1000.0;
+ QList<TankItem *> tankItems = qFindChildren<TankItem *>(this);
+ foreach (TankItem *tankItem, tankItems)
+ tankItem->idle(elapsedSecs);
+ }
+}
+
+void MainWindow::addTank()
+{
+ Q_ASSERT(!m_spawns.isEmpty());
+
+ QString fileName = QFileDialog::getOpenFileName(this, "Select plugin file",
+ "plugins/", "*.dll");
+ QPluginLoader loader(fileName);
+
+ Plugin *plugin = qobject_cast<Plugin *>(loader.instance());
+ if (plugin != 0) {
+ TankItem *tankItem = m_spawns.takeLast();
+ m_scene->addItem(tankItem);
+ if (m_spawns.isEmpty())
+ emit mapFull();
+
+ plugin->create(m_runningState, tankItem);
+ }
+}
+
diff --git a/examples/statemachine/errorstate/mainwindow.h b/examples/statemachine/errorstate/mainwindow.h
new file mode 100644
index 0000000..2bd574d
--- /dev/null
+++ b/examples/statemachine/errorstate/mainwindow.h
@@ -0,0 +1,41 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QTime>
+
+class QGraphicsScene;
+class QStateMachine;
+class QState;
+class TankItem;
+class MainWindow: public QMainWindow
+{
+ Q_OBJECT
+public:
+ MainWindow(QWidget *parent = 0);
+ ~MainWindow();
+
+public slots:
+ void addTank();
+ void runStep();
+
+signals:
+ void gameOver();
+ void mapFull();
+
+private:
+ void init();
+ void addWall(const QRectF &wall);
+
+ QGraphicsScene *m_scene;
+
+ QStateMachine *m_machine;
+ QState *m_runningState;
+
+ QList<TankItem *> m_spawns;
+ QTime m_time;
+
+};
+
+#endif
+
diff --git a/examples/statemachine/errorstate/plugin.h b/examples/statemachine/errorstate/plugin.h
new file mode 100644
index 0000000..1ac7e0f
--- /dev/null
+++ b/examples/statemachine/errorstate/plugin.h
@@ -0,0 +1,16 @@
+#ifndef PLUGIN_H
+#define PLUGIN_H
+
+class QState;
+class Tank;
+class Plugin
+{
+public:
+ virtual ~Plugin() {}
+
+ virtual QState *create(QState *parentState, Tank *tank) = 0;
+};
+
+Q_DECLARE_INTERFACE(Plugin, "TankPlugin")
+
+#endif
diff --git a/examples/statemachine/errorstate/tank.h b/examples/statemachine/errorstate/tank.h
new file mode 100644
index 0000000..9def6df
--- /dev/null
+++ b/examples/statemachine/errorstate/tank.h
@@ -0,0 +1,31 @@
+#ifndef TANK_H
+#define TANK_H
+
+#include <QObject>
+#include <QLineF>
+
+class Tank: public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal direction READ direction)
+ Q_PROPERTY(qreal distanceToObstacle READ distanceToObstacle)
+public:
+ Tank(QObject *parent = 0) : QObject(parent) {}
+
+ virtual qreal direction() const = 0;
+ virtual qreal distanceToObstacle() const = 0;
+
+signals:
+ void collision(const QLineF &collidedLine) const;
+ void actionCompleted();
+ void tankSpotted(qreal otherTankDirection, qreal distance);
+
+public slots:
+ virtual void moveForwards(qreal length) = 0;
+ virtual void moveBackwards(qreal length) = 0;
+ virtual void turn(qreal newDirection) = 0;
+ virtual void stop() = 0;
+ virtual void fireCannon(qreal distance) = 0;
+};
+
+#endif
diff --git a/examples/statemachine/errorstate/tankitem.cpp b/examples/statemachine/errorstate/tankitem.cpp
new file mode 100644
index 0000000..21d4a25
--- /dev/null
+++ b/examples/statemachine/errorstate/tankitem.cpp
@@ -0,0 +1,274 @@
+#include "tankitem.h"
+
+#include <QPainter>
+#include <QGraphicsScene>
+
+#include <math.h>
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+class Action
+{
+public:
+ Action(TankItem *item) : m_item(item)
+ {
+ }
+
+ TankItem *item() const { return m_item; }
+ void setItem(TankItem *item) { m_item = item; }
+
+ virtual bool apply(qreal timeDelta) = 0;
+
+private:
+ TankItem *m_item;
+};
+
+class MoveAction: public Action
+{
+public:
+ MoveAction(TankItem *item, qreal distance)
+ : Action(item), m_distance(distance)
+ {
+ }
+
+ bool apply(qreal timeDelta)
+ {
+ qreal dist = timeDelta * item()->speed();
+ m_distance -= dist;
+ if (qFuzzyCompare(m_distance, 0.0))
+ return false;
+
+ qreal a = item()->direction() * M_PI / 180.0;
+
+ qreal yd = dist * sin(a);
+ qreal xd = dist * sin(M_PI / 2.0 - a);
+
+ item()->setPos(item()->pos() + QPointF(xd, yd));
+ return true;
+ }
+
+private:
+ qreal m_distance;
+};
+
+class TurnAction: public Action
+{
+public:
+ TurnAction(TankItem *item, qreal distance)
+ : Action(item), m_distance(distance)
+ {
+ }
+
+ bool apply(qreal timeDelta)
+ {
+ qreal dist = timeDelta * item()->angularSpeed();
+ m_distance -= dist;
+ if (qFuzzyCompare(m_distance, 0.0))
+ return false;
+
+ item()->setDirection(item()->direction() + dist);
+ return true;
+ }
+
+private:
+ qreal m_distance;
+};
+
+class FireCannonAction: public Action
+{
+public:
+ FireCannonAction(TankItem *item, qreal distance)
+ : Action(item), m_distance(distance)
+ {
+ }
+
+ bool apply(qreal )
+ {
+ return false;
+ }
+
+private:
+ qreal m_distance;
+};
+
+TankItem::TankItem(QObject *parent) : Tank(parent), m_currentAction(0), m_currentDirection(0.0)
+{
+}
+
+void TankItem::idle(qreal elapsed)
+{
+ if (m_currentAction != 0) {
+ if (!m_currentAction->apply(elapsed)) {
+ setAction(0);
+ emit actionCompleted();
+ }
+ }
+}
+
+void TankItem::setAction(Action *newAction)
+{
+ if (m_currentAction != 0)
+ delete m_currentAction;
+
+ m_currentAction = newAction;
+}
+
+void TankItem::moveForwards(qreal length)
+{
+ setAction(new MoveAction(this, length));
+}
+
+void TankItem::moveBackwards(qreal length)
+{
+ setAction(new MoveAction(this, -length));
+}
+
+void TankItem::turn(qreal newDirection)
+{
+ setAction(new TurnAction(this, m_currentDirection - newDirection));
+}
+
+void TankItem::stop()
+{
+ setAction(0);
+}
+
+void TankItem::fireCannon(qreal distance)
+{
+ setAction(new FireCannonAction(this, distance));
+}
+
+QPointF TankItem::tryMove(const QPointF &requestedPosition) const
+{
+ QLineF movementPath(pos(), requestedPosition);
+
+ qreal cannonLength = 0.0;
+ {
+ QPointF p1 = boundingRect().center();
+ QPointF p2 = QPointF(boundingRect().right() + 10.0, p1.y());
+
+ cannonLength = QLineF(mapToScene(p1), mapToScene(p2)).length();
+ }
+
+ movementPath.setLength(movementPath.length() + cannonLength);
+
+ QRectF boundingRectPath(QPointF(qMin(movementPath.x1(), movementPath.x2()), qMin(movementPath.y1(), movementPath.y2())),
+ QPointF(qMax(movementPath.x1(), movementPath.x2()), qMax(movementPath.y1(), movementPath.y2())));
+
+ m_brp = mapFromScene(boundingRectPath);
+
+ QList<QGraphicsItem *> itemsInRect = scene()->items(boundingRectPath, Qt::IntersectsItemBoundingRect);
+
+ QPointF nextPoint = requestedPosition;
+ QRectF sceneRect = scene()->sceneRect();
+
+ QLineF collidedLine;
+ foreach (QGraphicsItem *item, itemsInRect) {
+ if (item == static_cast<const QGraphicsItem *>(this))
+ continue;
+
+ QPolygonF mappedBoundingRect = item->mapToScene(item->boundingRect());
+ for (int i=0; i<mappedBoundingRect.size(); ++i) {
+ QPointF p1 = mappedBoundingRect.at(i == 0 ? mappedBoundingRect.size()-1 : i-1);
+ QPointF p2 = mappedBoundingRect.at(i);
+
+ QLineF rectLine(p1, p2);
+ QPointF intersectionPoint;
+ QLineF::IntersectType intersectType = movementPath.intersect(rectLine, &intersectionPoint);
+
+ if (intersectType == QLineF::BoundedIntersection) {
+ movementPath.setP2(intersectionPoint);
+ movementPath.setLength(movementPath.length() - cannonLength);
+ nextPoint = movementPath.p2();
+ collidedLine = rectLine;
+ }
+ }
+ }
+
+ // Don't go outside of map
+ if (nextPoint.x() < sceneRect.left())
+ nextPoint.rx() = sceneRect.left();
+ if (nextPoint.x() > sceneRect.right())
+ nextPoint.rx() = sceneRect.right();
+ if (nextPoint.y() < sceneRect.top())
+ nextPoint.ry() = sceneRect.top();
+ if (nextPoint.y() > sceneRect.bottom())
+ nextPoint.ry() = sceneRect.bottom();
+
+ if (nextPoint != requestedPosition) {
+ emit collision(collidedLine);
+ }
+
+ return nextPoint;
+}
+
+QVariant TankItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
+{
+ if (change == ItemPositionChange && scene())
+ return tryMove(value.toPointF());
+ else
+ return QGraphicsItem::itemChange(change, value);
+}
+
+
+void TankItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
+{
+ QRectF brect = boundingRect();
+
+ painter->setBrush(m_color);
+ painter->setPen(Qt::black);
+
+ // body
+ painter->drawRect(brect.adjusted(0.0, 4.0, -2.0, -4.0));
+
+ // cannon
+ QRectF cannonBase = brect.adjusted(10.0, 6.0, -12.0, -6.0);
+ painter->drawEllipse(cannonBase);
+
+ painter->drawRect(QRectF(QPointF(cannonBase.center().x(), cannonBase.center().y() - 2.0),
+ QPointF(brect.right(), cannonBase.center().y() + 2.0)));
+
+ // left track
+ painter->setBrush(QBrush(Qt::black, Qt::VerPattern));
+ QRectF leftTrackRect = QRectF(brect.topLeft(), QPointF(brect.right() - 2.0, brect.top() + 4.0));
+ painter->fillRect(leftTrackRect, Qt::darkYellow);
+ painter->drawRect(leftTrackRect);
+
+ // right track
+ QRectF rightTrackRect = QRectF(QPointF(brect.left(), brect.bottom() - 4.0),
+ QPointF(brect.right() - 2.0, brect.bottom()));
+ painter->fillRect(rightTrackRect, Qt::darkYellow);
+ painter->drawRect(rightTrackRect);
+
+ painter->setBrush(QColor::fromRgb(255, 0, 0, 128));
+ painter->drawPolygon(m_brp);
+
+}
+
+QRectF TankItem::boundingRect() const
+{
+ return QRectF(-20.0, -10.0, 40.0, 20.0);
+}
+
+qreal TankItem::direction() const
+{
+ return m_currentDirection;
+}
+
+void TankItem::setDirection(qreal newDirection)
+{
+ m_currentDirection = newDirection;
+ rotate(newDirection);
+}
+
+qreal TankItem::distanceToObstacle() const
+{
+ // ###
+
+ return 0.0;
+}
+
+
+
diff --git a/examples/statemachine/errorstate/tankitem.h b/examples/statemachine/errorstate/tankitem.h
new file mode 100644
index 0000000..df13689
--- /dev/null
+++ b/examples/statemachine/errorstate/tankitem.h
@@ -0,0 +1,48 @@
+#ifndef TANKITEM_H
+#define TANKITEM_H
+
+#include "tank.h"
+
+#include <QGraphicsItem>
+#include <QColor>
+
+class Action;
+class TankItem: public Tank, public QGraphicsItem
+{
+ Q_OBJECT
+public:
+ TankItem(QObject *parent = 0);
+
+ virtual void moveForwards(qreal length);
+ virtual void moveBackwards(qreal length);
+ virtual void turn(qreal newDirection);
+ virtual void stop();
+ virtual void fireCannon(qreal distance);
+ virtual qreal direction() const;
+ virtual qreal distanceToObstacle() const;
+
+ void setColor(const QColor &color) { m_color = color; }
+ QColor color() const { return m_color; }
+
+ void idle(qreal elapsed);
+ void setDirection(qreal newDirection);
+
+ qreal speed() const { return 20.0; }
+ qreal angularSpeed() const { return 1.0; }
+
+protected:
+ virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
+ QRectF boundingRect() const;
+ QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value);
+
+private:
+ QPointF tryMove(const QPointF &requestedPosition) const;
+ void setAction(Action *newAction);
+
+ Action *m_currentAction;
+ qreal m_currentDirection;
+ QColor m_color;
+ mutable QPolygonF m_brp;
+};
+
+#endif