summaryrefslogtreecommitdiffstats
path: root/pacman-c++/client
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2011-05-05 00:57:07 +0200
committermanuel <manuel@mausz.at>2011-05-05 00:57:07 +0200
commitce48af53646cd9e7ec762fc1ac176b3aa620b11d (patch)
treef8fbf2cae8c7d0cbac2696a8f4cf94410bfb4928 /pacman-c++/client
parente54ccad07e256ba877bd41d70bd358bd0085bd1e (diff)
downloadfoop-ce48af53646cd9e7ec762fc1ac176b3aa620b11d.tar.gz
foop-ce48af53646cd9e7ec762fc1ac176b3aa620b11d.tar.bz2
foop-ce48af53646cd9e7ec762fc1ac176b3aa620b11d.zip
- refactorized the whole project and made a few subprojects
- replaced tcp with enet - added connect dialog - some smaller bugfixes
Diffstat (limited to 'pacman-c++/client')
-rw-r--r--pacman-c++/client/clicklabel.cpp12
-rw-r--r--pacman-c++/client/clicklabel.h21
-rw-r--r--pacman-c++/client/client.cpp265
-rw-r--r--pacman-c++/client/client.h41
-rw-r--r--pacman-c++/client/client.pro12
-rw-r--r--pacman-c++/client/mainwidget.cpp390
-rw-r--r--pacman-c++/client/mainwidget.h80
7 files changed, 821 insertions, 0 deletions
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 @@
1#include "clicklabel.h"
2
3ClickLabel::ClickLabel(const QString &text, QWidget *parent, Qt::WindowFlags f)
4 : QLabel(text, parent, f)
5{
6}
7
8void ClickLabel::mouseReleaseEvent(QMouseEvent * /* event */)
9{
10 emit clicked();
11}
12
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 @@
1#ifndef CLICKLABEL_H
2#define CLICKLABEL_H
3
4#include <QLabel>
5
6class ClickLabel
7 : public QLabel
8{
9 Q_OBJECT
10
11public:
12 ClickLabel(const QString &text, QWidget *parent = 0, Qt::WindowFlags f = 0);
13
14signals:
15 void clicked();
16
17protected:
18 void mouseReleaseEvent(QMouseEvent *event);
19};
20
21#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 @@
1#include "client.h"
2#include "clicklabel.h"
3#include "audio.h"
4#include "util.h"
5#include "pacman.pb.h"
6
7extern "C" {
8#include "enet/enet.h"
9}
10
11Client::Client()
12 : m_ambientMuted(false)
13{
14 m_settings = new QSettings(qApp->organizationName(), qApp->applicationName(), this);
15 m_dialog = new QDialog(this);
16 m_mainWidget = new MainWidget(this);
17 createMenu();
18 m_mainWidget->setAmbientMuted(m_ambientMuted);
19 setCentralWidget(m_mainWidget);
20 showConnectDialog();
21 m_dialog->setFocus();
22}
23
24void Client::createMenu()
25{
26 QMenu *fileMenu = menuBar()->addMenu("&File");
27
28 bool sound = AudioManager::self()->isWorking();
29 bool muted = !sound || m_settings->value("muted", false).toBool();
30 AudioManager::self()->setMuted(muted);
31
32 /* toggle sound: corner icon */
33 ClickLabel *toggleSound = new ClickLabel("Toggle Sound", this);
34 toggleSound->setToolTip("Toggle Sound");
35 toggleSound->setFixedWidth(20);
36 toggleSound->setFixedHeight(16);
37 toggleSound->setAlignment(Qt::AlignBottom);
38 toggleSound->setPixmap(soundIcon(!muted));
39 if (sound)
40 {
41 connect(toggleSound, SIGNAL(clicked()), this, SLOT(toggleSound()));
42 connect(AudioManager::self(), SIGNAL(mutedChanged(bool)), this, SLOT(mutedChanged(bool)));
43 }
44 menuBar()->setCornerWidget(toggleSound);
45
46 /* toggle sound: menu */
47 QAction *toggleSoundAction = new QAction("&Sound", this);
48 toggleSoundAction->setToolTip("Toggle Sound");
49 toggleSoundAction->setCheckable(true);
50 toggleSoundAction->setChecked(!muted);
51 toggleSoundAction->setDisabled(!sound);
52 fileMenu->addAction(toggleSoundAction);
53 if (sound)
54 {
55 connect(toggleSoundAction, SIGNAL(triggered()), this, SLOT(toggleSound()));
56 connect(this, SIGNAL(setMuteActionsChecked(bool)), toggleSoundAction, SLOT(setChecked(bool)));
57 }
58
59 /* toggle ambient sound: menu */
60 m_ambientMuted = muted || m_settings->value("ambientMuted", false).toBool();
61 QAction *toggleAmbientAction = new QAction("&Ambient Sound", this);
62 toggleAmbientAction->setToolTip("Toggle Ambient Sound");
63 toggleAmbientAction->setCheckable(true);
64 toggleAmbientAction->setChecked(!m_ambientMuted);
65 toggleAmbientAction->setDisabled(muted);
66 fileMenu->addAction(toggleAmbientAction);
67 if (sound)
68 {
69 connect(toggleAmbientAction, SIGNAL(triggered(bool)), this, SLOT(enableAmbientSound(bool)));
70 connect(this, SIGNAL(setMuteActionsChecked(bool)), toggleAmbientAction, SLOT(setEnabled(bool)));
71 }
72
73 /* connect/disconnect entry */
74 fileMenu->addSeparator();
75 QAction *connectAction = new QAction("C&onnect", this);
76 fileMenu->addAction(connectAction);
77 connect(connectAction, SIGNAL(triggered()), this, SLOT(showConnectDialog()));
78 connect(m_mainWidget, SIGNAL(connected(bool)), connectAction, SLOT(setDisabled(bool)));
79
80 QAction *disconnectAction = new QAction("Di&sconnect", this);
81 disconnectAction->setDisabled(true);
82 fileMenu->addAction(disconnectAction);
83 connect(disconnectAction, SIGNAL(triggered()), m_mainWidget, SLOT(doDisconnect()));
84 connect(m_mainWidget, SIGNAL(connected(bool)), disconnectAction, SLOT(setEnabled(bool)));
85
86 /* exit entry */
87 fileMenu->addSeparator();
88 QAction *quitAction = new QAction("E&xit", this);
89 quitAction->setIcon(QIcon::fromTheme(QLatin1String("application-exit")));
90 fileMenu->addAction(quitAction);
91 connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
92
93 QAction *aboutAction= menuBar()->addAction("Ab&out");
94 connect(aboutAction, SIGNAL(triggered()), this, SLOT(showAbout()));
95}
96
97void Client::toggleSound()
98{
99 if (!AudioManager::self()->isWorking())
100 return;
101 bool muted = !AudioManager::self()->isMuted();
102 AudioManager::self()->setMuted(muted);
103 /* mute ambient sound again if explicitly muted */
104 if (!muted && m_ambientMuted)
105 m_mainWidget->setAmbientMuted(true);
106}
107
108void Client::mutedChanged(bool muted)
109{
110 ClickLabel *tmp = qobject_cast<ClickLabel *>(menuBar()->cornerWidget());
111 tmp->setPixmap(soundIcon(!muted));
112 m_settings->setValue("muted", muted);
113 emit setMuteActionsChecked(!muted);
114}
115
116void Client::enableAmbientSound(bool enabled)
117{
118 if (!AudioManager::self()->isWorking())
119 return;
120 m_ambientMuted = !enabled;
121 m_mainWidget->setAmbientMuted(m_ambientMuted);
122 m_settings->setValue("ambientMuted", m_ambientMuted);
123}
124
125QPixmap Client::soundIcon(bool enabled) const
126{
127 QImage img(enabled ? ":/soundon" : ":/soundoff");
128 img.setColor(1, menuBar()->palette().color(
129 enabled ? QPalette::Active : QPalette::Disabled,
130 QPalette::ButtonText).rgba());
131 return QPixmap::fromImage(img);
132}
133
134void Client::showAbout()
135{
136 if (m_dialog != NULL)
137 {
138 delete m_dialog;
139 m_dialog = new QDialog(this);
140 }
141 m_dialog->setWindowTitle("About Pacman");
142 m_dialog->setWindowFlags(m_dialog->windowFlags() & ~Qt::WindowContextHelpButtonHint);
143
144 QGridLayout *layout = new QGridLayout(m_dialog);
145 layout->setSizeConstraint(QLayout::SetFixedSize);
146
147 QString actoricons;
148 for(int i = 0; Color::order[i] != Color::none; ++i)
149 actoricons += QString("<img src=\":/actor%1icon\"/>").arg(i + 1);
150
151 const QString text = QString(
152 "<h3>Multiplayer Pacman %1</h3>"
153 "Authors: H. Demel, B. Mallinger, M. Mausz, M. Racz<br/>"
154 "<br/>"
155 "Gameplay based on <a href=\"http://en.wikipedia.org/wiki/Pac-Man\">Pacman</a>"
156 ", &copy; <a href=\"http://www.namco.co.jp/\">Namco</a> 1980<br/>"
157 "<br/>"
158 "Developed using Qt %2 (%3 bit)<br/>")
159 .arg(actoricons, QLatin1String(QT_VERSION_STR), QString::number(QSysInfo::WordSize));
160
161 QLabel *label = new QLabel(text, m_dialog);
162 label->setWordWrap(true);
163 label->setOpenExternalLinks(true);
164 label->setTextInteractionFlags(Qt::TextBrowserInteraction);
165
166 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, m_dialog);
167 QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close);
168 buttonBox->addButton(closeButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::RejectRole | QDialogButtonBox::AcceptRole));
169 connect(buttonBox , SIGNAL(rejected()), m_dialog, SLOT(reject()));
170
171 layout->addWidget(label, 0, 1, 4, 4);
172 layout->addWidget(buttonBox, 4, 0, 1, 5);
173 m_dialog->show();
174}
175
176void Client::showConnectDialog()
177{
178 if (m_dialog != NULL)
179 {
180 delete m_dialog;
181 m_dialog = new QDialog(this);
182 }
183 m_dialog->setWindowTitle("Connect");
184
185 QGridLayout *layout = new QGridLayout(m_dialog);
186 layout->setSizeConstraint(QLayout::SetFixedSize);
187
188 QLabel *srvLabel = new QLabel("Address:", m_dialog);
189 QLineEdit *srv = new QLineEdit(m_settings->value("address", "127.0.0.1").toString(), m_dialog);
190 QLabel *portLabel = new QLabel("Port:", m_dialog);
191 QDoubleSpinBox *port = new QDoubleSpinBox(m_dialog);
192 port->setDecimals(0);
193 port->setMinimum(1);
194 port->setMaximum(65535);
195 port->setValue(m_settings->value("port", Constants::Networking::port).toUInt());
196
197 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
198 Qt::Horizontal, m_dialog);
199 QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
200 QPushButton *cancelButton = buttonBox->button(QDialogButtonBox::Cancel);
201 buttonBox->addButton(okButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::AcceptRole));
202 buttonBox->addButton(cancelButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::RejectRole));
203 connect(buttonBox, SIGNAL(rejected()), m_dialog, SLOT(reject()));
204 connect(buttonBox, SIGNAL(accepted()), m_dialog, SLOT(accept()));
205
206 layout->addWidget(srvLabel, 0, 0);
207 layout->addWidget(srv, 0, 1);
208 layout->addWidget(portLabel, 1, 0);
209 layout->addWidget(port, 1, 1);
210 layout->addWidget(buttonBox, 4, 0, 1, 5);
211
212 connect(m_dialog, SIGNAL(accepted()), this, SLOT(onAcceptConnectDialog()));
213 m_dialog->show();
214}
215
216void Client::onAcceptConnectDialog()
217{
218 if (m_dialog == NULL)
219 return;
220 QGridLayout *layout = static_cast<QGridLayout *>(m_dialog->layout());
221 QLineEdit *srv = static_cast<QLineEdit *>(layout->itemAtPosition(0, 1)->widget());
222 QDoubleSpinBox *port = static_cast<QDoubleSpinBox *>(layout->itemAtPosition(1, 1)->widget());
223 m_settings->setValue("address", srv->text());
224 m_settings->setValue("port", int(port->value()));
225 m_mainWidget->doConnect(srv->text(), int(port->value()));
226}
227
228bool Constants::server = false;
229
230int main(int argc, char **argv)
231{
232 /* Verify that the version of the library that we linked against is
233 * compatible with the version of the headers we compiled against.
234 */
235 GOOGLE_PROTOBUF_VERIFY_VERSION;
236
237 if (enet_initialize () != 0)
238 {
239 qCritical() << "An error occurred while initializing ENet";
240 return EXIT_FAILURE;
241 }
242
243 Q_INIT_RESOURCE(pacman);
244 QApplication app(argc, argv, true);
245 app.setOrganizationName("TU Wien FOOP");
246 app.setApplicationName("Pacman Client");
247 app.setWindowIcon(QIcon(":/appicon"));
248
249 qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));
250
251 AudioManager::self();
252
253 Client client;
254 client.show();
255 client.setWindowTitle(app.applicationName());
256
257 int ret = app.exec();
258
259 enet_deinitialize();
260
261 /* Delete all global objects allocated by libprotobuf */
262 google::protobuf::ShutdownProtobufLibrary();
263
264 return ret;
265}
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 @@
1#ifndef CLIENT_H
2#define CLIENT_H
3
4#include "mainwidget.h"
5#include <QtGui>
6
7class Client
8 : public QMainWindow
9{
10 Q_OBJECT
11public:
12 Client();
13 QSettings *settings();
14
15signals:
16 /* signal gets emitted if mute buttons should update their checked-state */
17 void setMuteActionsChecked(bool enabled);
18
19private slots:
20 /* toggles sound */
21 void toggleSound();
22 /* mute was changed (emitted by audioplayer/phonon) */
23 void mutedChanged(bool);
24 /* enable ambient (emitted by action) */
25 void enableAmbientSound(bool);
26 void showAbout();
27 void showConnectDialog();
28 void onAcceptConnectDialog();
29
30private:
31 void createMenu();
32 QPixmap soundIcon(bool enabled = true) const;
33
34private:
35 MainWidget *m_mainWidget;
36 QDialog *m_dialog;
37 QSettings *m_settings;
38 bool m_ambientMuted;
39};
40
41#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 @@
1TEMPLATE = app
2TARGET = pacman
3
4SOURCES += clicklabel.cpp \
5 client.cpp \
6 mainwidget.cpp
7HEADERS += clicklabel.h \
8 client.h \
9 mainwidget.h
10
11include(../common.pri)
12PRE_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 @@
1#include "mainwidget.h"
2#include "actor.h"
3#include "block.h"
4#include "constants.h"
5#include "util.h"
6#include "pacman.pb.h"
7#include <QStringBuilder>
8
9MainWidget::MainWidget(QWidget *parent)
10 : QWidget(parent), m_currentKey(Transmission::none), m_running(false),
11 m_host(NULL), m_peer(NULL), m_scene(NULL), m_maxplayers(0)
12{
13 m_host = enet_host_create(NULL, 1, 1, 0, 0);
14 if (m_host == NULL)
15 {
16 QMessageBox::critical(this, "Error", "An error occurred while trying to create an ENet client host");
17 qCritical() << "An error occurred while trying to create an ENet client host";
18 return;
19 }
20
21 /* create audio player */
22 m_ambientPlayer = new GaplessAudioPlayer(Sound::Ambient, 100, this);
23
24 m_recvTimer = new QTimer(this);
25 m_recvTimer->setInterval(Constants::tick / 2);
26 connect(m_recvTimer, SIGNAL(timeout()), this, SLOT(tick()));
27}
28
29MainWidget::~MainWidget()
30{
31 doDisconnect();
32 if (m_host != NULL)
33 {
34 enet_host_destroy(m_host);
35 m_host = NULL;
36 }
37}
38
39void MainWidget::doConnect(QString srv, unsigned int port)
40{
41 Color::Color color = connectToServer(srv, port);
42 if (color == Color::none)
43 {
44 QMessageBox::critical(this, "Error", "Failed to connect to server");
45 return;
46 }
47
48 /* create our scene */
49 m_scene = new SceneHolder(this);
50 m_scene->setColor(color);
51 m_scene->showWaitingForPlayers(true);
52 createGui();
53
54 m_recvTimer->start();
55 emit connected(true);
56 qDebug() << "[Connect] mycolor=" << m_scene->color();
57}
58
59void MainWidget::deleteLayout(QLayout *layout)
60{
61 if (layout == NULL)
62 return;
63
64 foreach(QObject *obj, layout->children())
65 {
66 QLayout *inLayout = qobject_cast<QLayout*>(obj);
67 deleteLayout(inLayout);
68 }
69
70 QLayoutItem *child;
71 while ((child = layout->takeAt(0)) != NULL)
72 {
73 child->widget()->deleteLater();
74 delete child;
75 }
76
77 delete layout;
78}
79
80void MainWidget::doDisconnect()
81{
82 stopGame();
83 m_recvTimer->stop();
84 closeENetPeer();
85 deleteLayout(layout());
86 m_playerScoreLayouts.clear();
87 emit connected(false);
88}
89
90bool MainWidget::connected()
91{
92 return m_peer != NULL;
93}
94
95void MainWidget::createGui()
96{
97 setFocusPolicy(Qt::StrongFocus);
98
99 /* first one is always the own score */
100 QVBoxLayout *layout = new QVBoxLayout(this);
101 layout->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
102 QHBoxLayout *scoreLayout = new QHBoxLayout();
103 for (unsigned int i = 0; i < m_maxplayers; ++i)
104 {
105 QGridLayout *playerLayout = new QGridLayout();
106 playerLayout->addWidget(new QLabel("Current:", this), 0, 0);
107 playerLayout->addWidget(new QLabel("Total:", this), 1, 0);
108 playerLayout->addWidget(new QLabel("0", this), 0, 1, Qt::AlignLeft);
109 playerLayout->addWidget(new QLabel("0", this), 1, 1, Qt::AlignLeft);
110 playerLayout->setColumnStretch(1, 10);
111 playerLayout->setSizeConstraint(QLayout::SetMinimumSize);
112
113 QGroupBox *scoreBox = new QGroupBox(QString("Player %1").arg(i + 1), this);
114 scoreBox->setObjectName(QString("actor%1").arg(i + 1));
115 scoreBox->setCheckable(true);
116 scoreBox->setDisabled(i >= m_maxplayers);
117 connect(scoreBox, SIGNAL(clicked()), this, SLOT(playerScoreClicked()));
118
119 scoreBox->setLayout(playerLayout);
120 m_playerScoreLayouts.append(playerLayout);
121
122 if (Color::order[i] == m_scene->color())
123 scoreLayout->insertWidget(0, scoreBox);
124 else
125 scoreLayout->addWidget(scoreBox);
126 }
127 layout->addLayout(scoreLayout);
128
129 QGraphicsView *window = new QGraphicsView(m_scene, this);
130 window->setFrameStyle(0);
131 window->setFixedSize(m_scene->sceneRect().size().toSize());
132 window->setWindowFlags(window->windowFlags() & ~Qt::WindowMaximizeButtonHint);
133 window->setFocusPolicy(Qt::NoFocus);
134 layout->addWidget(window, 0, Qt::AlignCenter);
135
136 QFile css(":/stylesheet");
137 css.open(QFile::ReadOnly);
138 qApp->setStyleSheet(QLatin1String(css.readAll()));
139
140 /* add dummy layout at the end which gets streched when resizing */
141 QHBoxLayout *spacer = new QHBoxLayout();
142 layout->addLayout(spacer, 10);
143
144 setLayout(layout);
145}
146
147void MainWidget::updateScore(const ProtoBuf::MapUpdate& packet)
148{
149 for(unsigned i = 0; i < m_maxplayers; ++i)
150 {
151 QGridLayout *score = m_playerScoreLayouts.at(i);
152 QLabel *turnPointsLbl = dynamic_cast<QLabel *>(score->itemAtPosition(0, 1)->widget());
153 turnPointsLbl->setText(QString::number(packet.round_points(i)));
154
155 QLabel *allPointsLbl = dynamic_cast<QLabel *>(score->itemAtPosition(1, 1)->widget());
156 allPointsLbl->setText(QString::number(packet.game_points(i)));
157 }
158}
159
160Transmission::field_t MainWidget::translateKey(int key)
161{
162 switch(key)
163 {
164 case Qt::Key_W:
165 case Qt::Key_Up:
166 return Transmission::direction_up;
167 break;
168 case Qt::Key_S:
169 case Qt::Key_Down:
170 return Transmission::direction_down;
171 break;
172 case Qt::Key_A:
173 case Qt::Key_Left:
174 return Transmission::direction_left;
175 break;
176 case Qt::Key_D:
177 case Qt::Key_Right:
178 return Transmission::direction_right;
179 break;
180 default:
181 return Transmission::direction_none;
182 }
183}
184
185void MainWidget::tick()
186{
187 ENetEvent event;
188 while (enet_host_service(m_host, &event, 1) > 0)
189 {
190 switch (event.type)
191 {
192 case ENET_EVENT_TYPE_DISCONNECT:
193 m_peer = NULL;
194 doDisconnect();
195 break;
196 case ENET_EVENT_TYPE_RECEIVE:
197 tick(&event);
198 enet_packet_destroy(event.packet);
199 break;
200 default:
201 break;
202 }
203 }
204}
205
206void MainWidget::tick(ENetEvent *event)
207{
208 QSharedPointer<QByteArray> data = Util::receivePacket(event->packet);
209 bool worked = m_updatepacket.ParseFromArray(data->data(), data->size());
210 Q_ASSERT(worked);
211 Q_UNUSED(worked);
212
213 /* eating order data set indicates a new round */
214 if (m_updatepacket.eating_order_size() > 0)
215 {
216 Q_ASSERT(m_scene != NULL);
217 m_scene->reset();
218
219 /* fetch eating order */
220 QList<Color::Color> order;
221 for(int i = 0; i < m_updatepacket.eating_order_size(); ++i)
222 order.append(static_cast<Color::Color>(m_updatepacket.eating_order(i) & Transmission::color_mask));
223 m_scene->setEatingOrder(order);
224
225 /* stop game */
226 stopGame();
227
228 /* and restart game */
229 QTimer *timer = new QTimer(this);
230 timer->setSingleShot(true);
231 timer->setInterval(Sound::length[Sound::Intro] + Constants::tick);
232 connect(timer, SIGNAL(timeout()), this, SLOT(startGame()));
233 timer->start();
234 AudioManager::self()->play(Sound::Intro, true);
235 }
236
237 Transmission::map_t map = Util::createUninitialisedMap();
238 Q_ASSERT(m_updatepacket.field_size() == (int) (Constants::map_size.width * Constants::map_size.height));
239 int i = 0;
240 for (unsigned int x = 0; x < Constants::map_size.width; ++x)
241 {
242 for (unsigned int y = 0; y < Constants::map_size.height; ++y)
243 {
244 map[x][y] = m_updatepacket.field(i);
245 ++i;
246 }
247 }
248 m_scene->updateMap(map);
249 Util::deleteMap(map);
250 updateScore(m_updatepacket);
251
252 if (m_updatepacket.eating_order_size() > 0)
253 {
254 m_scene->showWaitingForPlayers(false);
255 m_scene->showEatingText();
256 }
257}
258
259void MainWidget::keyPressEvent(QKeyEvent* event)
260{
261 if (event->isAutoRepeat())
262 return;
263
264 QWidget::keyPressEvent(event);
265 Transmission::field_t newKey = translateKey(event->key());
266 if (newKey == Transmission::direction_none)
267 return;
268 bool sendUpdate = (m_currentKey != newKey);
269 m_currentKey = newKey;
270 if (sendUpdate)
271 sendKeyUpdate();
272}
273
274void MainWidget::sendKeyUpdate()
275{
276 if (m_currentKey == Transmission::direction_none)
277 return;
278 qDebug() << "[SendKey] key=" << m_currentKey;
279 ProtoBuf::KeyPressUpdate packet;
280 packet.set_newkey(m_currentKey);
281 Util::sendPacket(packet, m_peer, m_host);
282}
283
284void MainWidget::keyReleaseEvent(QKeyEvent* event)
285{
286 if (event->isAutoRepeat())
287 return;
288
289 QWidget::keyReleaseEvent(event);
290 m_currentKey = Transmission::none;
291 return;
292}
293
294void MainWidget::startGame()
295{
296 disconnect(AudioManager::self()->audioPlayer(), NULL, this, SLOT(startGame()));
297 m_scene->showEatingText(false);
298 m_running = true;
299 sendKeyUpdate();
300 m_ambientPlayer->play();
301}
302
303void MainWidget::stopGame()
304{
305 m_running = false;
306 m_ambientPlayer->pause();
307}
308
309void MainWidget::setAmbientMuted(bool muted)
310{
311 m_ambientPlayer->setMuted(muted);
312}
313
314void MainWidget::playerScoreClicked()
315{
316 QGroupBox *tmp = qobject_cast<QGroupBox *>(sender());
317 tmp->setChecked(true);
318 return;
319}
320
321Color::Color MainWidget::connectToServer(QString srv, unsigned int port)
322{
323 qDebug() << "[Connect] server=" << srv << "port=" << port;
324
325 /* connect to server */
326 closeENetPeer();
327 ENetAddress address;
328 enet_address_set_host(&address, qPrintable(srv));
329 address.port = Constants::Networking::port;
330 m_peer = enet_host_connect(m_host, &address, 1, 0);
331 if (m_peer == NULL)
332 {
333 qCritical() << "No available peers for initiating an ENet connection";
334 return Color::none;
335 }
336
337 ENetEvent event;
338 bool worked = (enet_host_service(m_host, &event, Constants::Networking::connection_timeout) > 0
339 && event.type == ENET_EVENT_TYPE_CONNECT);
340 if (worked)
341 {
342 /* additional init: first packet is our color */
343 worked = (enet_host_service(m_host, &event, Constants::Networking::packet_timeout) > 0
344 && event.type == ENET_EVENT_TYPE_RECEIVE);
345 if (worked)
346 {
347 /* receive color */
348 QSharedPointer<QByteArray> data = Util::receivePacket(event.packet);
349 enet_packet_destroy(event.packet);
350 ProtoBuf::Init packet;
351 worked = packet.ParseFromArray(data->data(), data->size());
352 Q_ASSERT(worked);
353 Q_UNUSED(worked);
354 m_maxplayers = packet.maxplayers();
355 return static_cast<Color::Color>(packet.color() & Transmission::color_mask);
356 }
357 }
358 enet_peer_reset(m_peer);
359 m_peer = NULL;
360 return Color::none;
361}
362
363void MainWidget::closeENetPeer()
364{
365 if (m_peer != NULL && m_peer->state != ENET_PEER_STATE_DISCONNECTED)
366 {
367 /* allow up to 3 seconds for the disconnect to succeed
368 * and drop any packets received packets
369 */
370 enet_peer_disconnect(m_peer, 0);
371 ENetEvent event;
372 while (enet_host_service(m_host, &event, 3000) > 0)
373 {
374 switch (event.type)
375 {
376 case ENET_EVENT_TYPE_RECEIVE:
377 enet_packet_destroy(event.packet);
378 break;
379 case ENET_EVENT_TYPE_DISCONNECT:
380 m_peer = NULL;
381 return;
382 default:
383 break;
384 }
385 }
386
387 enet_peer_reset(m_peer);
388 }
389 m_peer = NULL;
390}
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 @@
1#ifndef MAINWIDGET_H
2#define MAINWIDGET_H
3
4#include "sceneholder.h"
5#include "constants.h"
6#include "audio.h"
7#include "pacman.pb.h"
8#include <QtGui>
9#include <QtCore>
10
11extern "C" {
12#include "enet/enet.h"
13}
14
15class Actor;
16
17class MainWidget
18 : public QWidget
19{
20 Q_OBJECT
21
22public:
23 MainWidget(QWidget *parent = 0);
24 ~MainWidget();
25 bool connected();
26 void setAmbientMuted(bool muted);
27
28public slots:
29 void doConnect(QString srv = "127.0.0.1", unsigned int port = Constants::Networking::port);
30 void doDisconnect();
31
32protected:
33 /* handling of current key */
34 virtual void keyPressEvent(QKeyEvent *);
35 virtual void keyReleaseEvent(QKeyEvent *);
36
37signals:
38 void connected(bool connected);
39
40private slots:
41 void startGame();
42 void stopGame();
43 void playerScoreClicked();
44 void tick();
45 void tick(ENetEvent *event);
46 void sendKeyUpdate();
47
48private:
49 void createGui();
50 void createMenu();
51 void updateScore(const ProtoBuf::MapUpdate&);
52 bool isRunning();
53 Color::Color connectToServer(QString srv = "127.0.0.1", unsigned int port = Constants::Networking::port);
54 void closeENetPeer();
55 void deleteLayout(QLayout *layout);
56
57 /* GUI elements needed in the progress of the game */
58 QList<QGridLayout*> m_playerScoreLayouts;
59
60 /* key currently pressed by user */
61 Transmission::field_t m_currentKey;
62
63 /* translate Qt::Key to our key format */
64 Transmission::field_t translateKey(int key);
65
66 /* game running */
67 bool m_running;
68 GaplessAudioPlayer *m_ambientPlayer;
69
70 ENetHost *m_host;
71 ENetPeer *m_peer;
72 SceneHolder *m_scene;
73 unsigned int m_maxplayers;
74 QTimer *m_recvTimer;
75
76 /* allocate as member variable as this packet is large and used often */
77 ProtoBuf::MapUpdate m_updatepacket;
78};
79
80#endif // MAINWIDGET_H