#include "toplevel.h" #include "playground.h" #include "playcanvas.h" #include "network.h" #include "kmenubar.h" #include "kpopupmenu.h" #include "splash.h" #include "select.h" #include "level.h" #include "resource.h" #include "protocol.h" #include "rule.h" #include #include #include #include #include #include #include #include #include "config.h" #ifdef HAVE_KNEWSTUFF #include #endif TopLevel::TopLevel(int ggzmode) : KMainWindow() { QPixmap pix; m_ggzmode = ggzmode; network_enabled = true; m_isfullscreen = false; m_pause = false; kapp->config()->setGroup("Preferences"); int tilesize = kapp->config()->readNumEntry("Tilesize"); if(tilesize) Resource::setTilesize(tilesize); else Resource::setTilesize(40); QString theme = kapp->config()->readEntry("Theme"); if(!theme.isNull()) Resource::setTheme(theme); else Resource::setTheme("desert"); m_sock = NULL; m_s = NULL; network = NULL; playground = NULL; canvas = NULL; splash = new Splash(this); setCentralWidget(splash); gamemenu = new KPopupMenu(this); if(!m_ggzmode) { gamemenu->insertItem(KGlobal::iconLoader()->loadIcon("filenew", KIcon::Small), i18n("&New"), game_new); gamemenu->insertSeparator(); } else { network = new Network(); network->connect(); connect(network, SIGNAL(signalInput(int)), SLOT(slotInput(int))); } #ifdef HAVE_KNEWSTUFF gamemenu->insertItem(KGlobal::iconLoader()->loadIcon("knewstuff", KIcon::Small), i18n("&Get Levels"), game_newlevels); gamemenu->insertSeparator(); #endif gamemenu->insertItem(KGlobal::iconLoader()->loadIcon("exit", KIcon::Small), i18n("&Exit"), game_exit); displaymenu = new KPopupMenu(this); displaymenu->insertItem(KGlobal::iconLoader()->loadIcon("viewmag+", KIcon::Small), i18n("&Larger"), display_larger); displaymenu->insertItem(KGlobal::iconLoader()->loadIcon("viewmag-", KIcon::Small),i18n("&Smaller"), display_smaller); displaymenu->insertItem(KGlobal::iconLoader()->loadIcon("window_fullscreen", KIcon::Small),i18n("Show &Fullscreen"), display_fullscreen); displaymenu->setCheckable(true); menuBar()->insertItem(i18n("&Game"), gamemenu, game_menu); menuBar()->insertItem(i18n("&Display"), displaymenu, display_menu); menuBar()->insertItem(i18n("&Help"), helpMenu(), help_menu); menuBar()->setItemEnabled(display_menu, false); menuBar()->insertItem(pix, livescount); menuBar()->insertItem("", livestext); menuBar()->insertItem(pix, bombscount); menuBar()->insertItem("", bombstext); menuBar()->insertItem(pix, powercount); menuBar()->insertItem("", powertext); menuBar()->insertItem(pix, clockcount); menuBar()->insertItem("", clocktext); menuBar()->insertItem(pix, scorecount); menuBar()->insertItem("", scoretext); menuStatusBarSetup(); menuBar()->setItemEnabled(livestext, false); menuBar()->setItemEnabled(bombstext, false); menuBar()->setItemEnabled(powertext, false); menuBar()->setItemEnabled(clocktext, false); menuBar()->setItemEnabled(scoretext, false); connect(gamemenu, SIGNAL(activated(int)), SLOT(slotMenu(int))); connect(displaymenu, SIGNAL(activated(int)), SLOT(slotMenu(int))); if(ggzmode) setCaption(i18n("Kamikaze (playing via GGZ Gaming Zone)")); else setCaption(i18n("Kamikaze")); move(kapp->desktop()->width() / 2 - splash->autowidth() / 2, kapp->desktop()->height() / 2 - splash->autoheight() / 2); } TopLevel::~TopLevel() { if(network) { network->disconnect(); delete network; if(m_s) { delete m_s; delete m_sock; } } } void TopLevel::key(int keycode) { int gen = 0; bool moved; //kdDebug() << "key " << keycode << endl; if(!canvas) return; if(network) { gen = network->generator(); kdDebug() << "<** generator:out " << gen << endl; } switch(keycode) { case Playground::KeyLeft: if(!network) { moved = canvas->action(PlayCanvas::moveEvent, PlayCanvas::optLeft); //if(moved) playground->scrollBy(-32, 0); } if(network) *m_s << (Q_INT32) opcode_move << (Q_INT32) gen << (Q_INT32) move_left; break; case Playground::KeyRight: if(!network) { moved = canvas->action(PlayCanvas::moveEvent, PlayCanvas::optRight); //if(moved) playground->scrollBy(32, 0); } if(network) *m_s << (Q_INT32) opcode_move << (Q_INT32) gen << (Q_INT32) move_right; break; case Playground::KeyUp: if(!network) { moved = canvas->action(PlayCanvas::moveEvent, PlayCanvas::optUp); //if(moved) playground->scrollBy(0, -32); } if(network) *m_s << (Q_INT32) opcode_move << (Q_INT32) gen << (Q_INT32) move_up; break; case Playground::KeyDown: if(!network) { moved = canvas->action(PlayCanvas::moveEvent, PlayCanvas::optDown); //if(moved) playground->scrollBy(0, 32); } if(network) *m_s << (Q_INT32) opcode_move << (Q_INT32) gen << (Q_INT32) move_down; break; case Playground::KeyFire: if(!network) canvas->action(PlayCanvas::fireEvent, PlayCanvas::optNone); if(network) *m_s << (Q_INT32) opcode_action << (Q_INT32) action_newbomb; break; case Playground::KeyZoomOut: scale(-5); break; case Playground::KeyZoomIn: scale(5); break; case Playground::KeyWeapon: canvas->selectWeapon(); break; case Playground::KeyTimebomb: canvas->action(PlayCanvas::timebombEvent, PlayCanvas::optNone); break; case Playground::KeyFullscreen: slotMenu(display_fullscreen); break; case Playground::KeyPause: if(!network) { m_pause = !m_pause; if(m_pause) { if(canvas->enabled()) { canvas->enable(false); //canvas->enableLocal(false); canvas->setOverlay(PlayCanvas::pause); } } else { canvas->enable(true); //canvas->enableLocal(true); canvas->setOverlay(PlayCanvas::none); } } break; } } void TopLevel::newGame(QString level, QString theme, int players) { Q_UNUSED(theme); if(playground) { delete playground; playground = NULL; } if(canvas) { delete canvas; canvas = NULL; } if (!canvas && !playground) { kapp->config()->setGroup("Preferences"); int fullscreen = kapp->config()->readNumEntry("Fullscreen"); canvas = new PlayCanvas(this); connect(canvas, SIGNAL(position(int, int)), SLOT(slotPosition(int, int))); playground = new Playground(canvas, this); connect(playground, SIGNAL(key(int)), SLOT(key(int))); connect(canvas, SIGNAL(playerKilled()), SLOT(slotEndGame())); connect(canvas, SIGNAL(levelFinished()), SLOT(slotEndGame())); connect(canvas, SIGNAL(information(int, int, int)), SLOT(menuStatusBar(int, int, int))); connect(canvas, SIGNAL(informationScore(int, int)), SLOT(menuStatusBarScore(int, int))); //if(!fullscreen) canvas->setTheme(theme); canvas->setPlayers(players); canvas->buildLevel(level); playground->setFocus(); splash->hide(); playground->show(); setCentralWidget(playground); menuBar()->setItemEnabled(display_menu, true); menuStatusBarSetup(); menuStatusBar(0, 0, 0); menuStatusBarScore(0, 0); if((!fullscreen) && canvas->level()) { move(kapp->desktop()->width() / 2 - canvas->level()->width() * Resource::tilesize() / 2, kapp->desktop()->height() / 2 - (canvas->level()->height() * Resource::tilesize() + menuBar()->height()) / 2); scale(0); } if((fullscreen) && (!displaymenu->isItemChecked(display_fullscreen))) slotMenu(display_fullscreen); } } void TopLevel::selectNewGame(int bots) { int ret; Select sel; Rule *rule = NULL; if(network) sel.setBots(bots); if(network) sel.setBotsReadOnly(); ret = sel.exec(); if(ret == QDialog::Accepted) { kapp->config()->setGroup("Preferences"); kapp->config()->writeEntry("Theme", sel.theme()); kapp->config()->writeEntry("Bots", sel.players()); kapp->config()->writeEntry("Level", sel.level()); kapp->config()->sync(); rule = sel.rules(); if(network) { *m_s << (Q_INT32) opcode_map; *m_s << (Q_INT32) map_selected; *m_s << sel.level().latin1(); *m_s << (Q_INT32) sel.players(); // poor man's serialization *m_s << opcode_rules; *m_s << rule->custom(); *m_s << rule->player_lives; *m_s << rule->player_power; *m_s << rule->player_bombs; *m_s << rule->monster_lives; *m_s << rule->monster_power; *m_s << rule->monster_bombs; *m_s << rule->monster_blastfrequency; *m_s << rule->invisible_number; *m_s << rule->flash_number; *m_s << rule->grail_number; *m_s << rule->speed_number; *m_s << rule->power_number; *m_s << rule->life_number; *m_s << rule->bomb_number; *m_s << rule->bomb_destroy; *m_s << rule->bomb_lock; *m_s << rule->bomb_duration; *m_s << rule->timebomb_allowed; *m_s << rule->moveablebomb_allowed; *m_s << rule->scree_percentage; *m_s << rule->wall_damage; *m_s << rule->ground_damage; } else { newGame(sel.level(), sel.theme(), sel.players()); canvas->setRules(rule); canvas->enable(true); canvas->enableLocal(true); } } } void TopLevel::scale(int difference) { int w, h; if(!canvas) return; if(!canvas->level()) return; if(difference) { Resource::setTilesize(Resource::tilesize() + difference); canvas->setTheme(Resource::theme()); kapp->config()->setGroup("Preferences"); kapp->config()->writeEntry("Tilesize", Resource::tilesize()); kapp->config()->sync(); } if(!displaymenu->isItemChecked(display_fullscreen)) { w = canvas->level()->width() * Resource::tilesize(); h = canvas->level()->height() * Resource::tilesize() + menuBar()->height(); if(int(KApplication::desktop()->width() * 0.7) < w) w = int(KApplication::desktop()->width() * 0.8); if(int(KApplication::desktop()->height() * 0.7) < h) h = int(KApplication::desktop()->height() * 0.7); setFixedSize(w, h); } } void TopLevel::slotMenu(int id) { int w, h; switch(id) { case game_new: selectNewGame(8); break; case game_newlevels: #ifdef HAVE_KNEWSTUFF KNS::DownloadDialog::open("kamikaze/level"); #endif break; case game_exit: slotEndGame(); close(); break; case display_larger: scale(5); break; case display_smaller: scale(-5); break; case display_fullscreen: if(!canvas) return; if(!displaymenu->isItemChecked(display_fullscreen)) { m_geometry = frameGeometry(); m_tilesize = Resource::tilesize(); m_isfullscreen = true; displaymenu->setItemChecked(display_fullscreen, true); w = kapp->desktop()->width() / (canvas->level()->width() + 1); h = kapp->desktop()->height() / (canvas->level()->height() + 1); Resource::setTilesize((w < h ? w : h)); if(canvas->enabled()) canvas->setTheme(Resource::theme()); setFixedSize(kapp->desktop()->width(), kapp->desktop()->height()); showFullScreen(); kapp->config()->setGroup("Preferences"); kapp->config()->writeEntry("Fullscreen", 1); kapp->config()->sync(); } else { m_isfullscreen = false; displaymenu->setItemChecked(display_fullscreen, false); Resource::setTilesize(m_tilesize); if(canvas->enabled()) canvas->setTheme(Resource::theme()); scale(0); move(m_geometry.x(), m_geometry.y()); showNormal(); kapp->config()->setGroup("Preferences"); kapp->config()->writeEntry("Fullscreen", 0); kapp->config()->sync(); } break; } } void TopLevel::setTheme(QString theme) { if(canvas) canvas->setTheme(theme); } void TopLevel::slotEndGame() { if(!canvas) return; canvas->enable(false); canvas->enableLocal(false); menuStatusBar(0, 0, 0); } void TopLevel::keyPressEvent(QKeyEvent *e) { //kdDebug() << "toplevel: key pressed" << endl; switch(e->key()) { case Qt::Key_Down: key(Playground::KeyDown); break; case Qt::Key_Up: key(Playground::KeyUp); break; case Qt::Key_Left: key(Playground::KeyLeft); break; case Qt::Key_Right: key(Playground::KeyRight); break; case Qt::Key_Space: key(Playground::KeyFire); break; case Qt::Key_Minus: key(Playground::KeyZoomOut); break; case Qt::Key_Plus: key(Playground::KeyZoomIn); break; case Qt::Key_Tab: key(Playground::KeyWeapon); break; case Qt::Key_Return: key(Playground::KeyTimebomb); break; case Qt::Key_F: key(Playground::KeyFullscreen); break; case Qt::Key_P: key(Playground::KeyPause); break; } } void TopLevel::slotInput(int fd) { #ifdef WITH_GGZ int opcode, minor; char *map; int bots, type, count; int gen, direction; int x, y, id, id2, custom; Rule *rule; Q_UNUSED(fd); if(!network_enabled) return; if(!m_s) { m_sock = new QSocketDevice(QSocketDevice::Stream); m_sock->setSocket(network->channel(), QSocketDevice::Stream); m_s = new QDataStream(m_sock); m_s->setVersion(5); } kdDebug() << "INPUT FROM SERVER!" << endl; *m_s >> opcode; switch(opcode) { case opcode_map: *m_s >> minor; kdDebug() << "** map " << minor << endl; if(minor == map_select) { *m_s >> bots; selectNewGame(bots); } else if(minor == map_selected) { *m_s >> map; *m_s >> bots; kdDebug() << "received map of length " << strlen(map) << ": " << map << " with bots: " << bots << endl; newGame(map, QString::null, bots + 1); delete map; } break; case opcode_rules: rule = new Rule(); *m_s >> custom; if(custom) rule->setCustom(); *m_s >> rule->player_lives; *m_s >> rule->player_power; *m_s >> rule->player_bombs; *m_s >> rule->monster_lives; *m_s >> rule->monster_power; *m_s >> rule->monster_bombs; *m_s >> rule->monster_blastfrequency; *m_s >> rule->invisible_number; *m_s >> rule->flash_number; *m_s >> rule->grail_number; *m_s >> rule->speed_number; *m_s >> rule->power_number; *m_s >> rule->life_number; *m_s >> rule->bomb_number; *m_s >> rule->bomb_destroy; *m_s >> rule->bomb_lock; *m_s >> rule->bomb_duration; *m_s >> rule->timebomb_allowed; *m_s >> rule->moveablebomb_allowed; *m_s >> rule->scree_percentage; *m_s >> rule->wall_damage; *m_s >> rule->ground_damage; if(canvas) canvas->setRules(rule); break; case opcode_move: *m_s >> gen; *m_s >> type; *m_s >> id; *m_s >> direction; kdDebug() << "**> generator:in " << gen << endl; kdDebug() << "received move to " << direction << endl; if(type == move_player) { kdDebug() << "(player #" << id << ")" << endl; if(direction == move_down) direction = PlayCanvas::optDown; else if(direction == move_up) direction = PlayCanvas::optUp; else if(direction == move_left) direction = PlayCanvas::optLeft; else if(direction == move_right) direction = PlayCanvas::optRight; canvas->action(PlayCanvas::moveEvent, direction); } else if(type == move_monster) { kdDebug() << "(monster #" << id << ")" << endl; canvas->moveMonster(id, direction); } break; case opcode_action: *m_s >> minor; if(minor == action_newplayer) { *m_s >> id; *m_s >> x; *m_s >> y; kdDebug() << "*** ACTION ***: Created player " << id << " at " << x << "/" << y << endl; canvas->createPlayer(id, x, y); } else if(minor == action_newmonster) { *m_s >> id; *m_s >> x; *m_s >> y; kdDebug() << "*** ACTION ***: Created monster " << id << " at " << x << "/" << y << endl; canvas->createMonster(id, x, y); } else if(minor == action_newitem) { *m_s >> type; *m_s >> id; *m_s >> x; *m_s >> y; kdDebug() << "*** ACTION ***: Created item " << id << " (" << type << ") at " << x << "/" << y << endl; canvas->createItem(type, id, x, y); } else if(minor == action_newexit) { *m_s >> x; *m_s >> y; kdDebug() << "*** ACTION ***: Created exit at " << x << "/" << y << endl; canvas->createExit(x, y); } else if(minor == action_newbomb) { *m_s >> id2; *m_s >> id; *m_s >> x; *m_s >> y; kdDebug() << "*** ACTION ***: Created bomb at " << x << "/" << y << endl; canvas->createBomb(id2, id, x, y); } else if(minor == action_newblast) { *m_s >> id; *m_s >> x; *m_s >> y; kdDebug() << "*** ACTION ***: Created blast " << id << " at " << x << "/" << y << endl; canvas->createBlast(id, x, y); } else if(minor == action_item) { *m_s >> id; *m_s >> type; *m_s >> count; kdDebug() << "*** ACTION ***: " << id << " got " << count << " items of type " << type << endl; canvas->actionItem(id, type, count); } else if(minor == action_removeplayer) { *m_s >> id; kdDebug() << "*** ACTION ***: remove player " << id << endl; canvas->removePlayer(id); } else if(minor == action_removemonster) { *m_s >> id; kdDebug() << "*** ACTION ***: remove monster " << id << endl; canvas->removeMonster(id); } else if(minor == action_removebomb) { *m_s >> id; kdDebug() << "*** ACTION ***: remove bomb " << id << endl; canvas->removeBomb(id); } else if(minor == action_removeblast) { *m_s >> id; kdDebug() << "*** ACTION ***: remove blast " << id << endl; canvas->removeBlast(id); } else if(minor == action_removeitem) { *m_s >> id; kdDebug() << "*** ACTION ***: remove item " << id << endl; canvas->removeItem(id); } else if(minor == action_activateitem) { *m_s >> id; kdDebug() << "*** ACTION ***: activate item " << id << endl; canvas->activateItem(id); } else if(minor == action_activateexit) { kdDebug() << "*** ACTION ***: activate exit " << endl; canvas->activateExit(); } else if(minor == action_highlightitem) { *m_s >> id; kdDebug() << "*** ACTION ***: highlight item " << id << endl; canvas->highlightItem(id); } else if(minor == action_highlightexit) { kdDebug() << "*** ACTION ***: highlight exit " << endl; canvas->highlightExit(); } break; case opcode_cheat: kdDebug() << "whoops, cheater" << endl; disconnect(); KMessageBox::error(this, i18n("The server has identified you as a cheater."), i18n("Cheat detected")); /*network->disconnect();*/ break; case opcode_state: *m_s >> minor; kdDebug() << "** state " << minor << endl; if(minor == state_start) { kdDebug() << "GAME START" << endl; if(canvas) { canvas->enable(true); } kdDebug() << "GAME STARTED" << endl; } else if(minor == state_stop) { kdDebug() << "GAME STOPPED" << endl; if(canvas) canvas->enable(false); } break; default: disconnect(); KMessageBox::error(this, i18n("The game was interrupted due to unknown messages in the protocol."), i18n("Error detected")); /*network->disconnect();*/ } #endif } void TopLevel::disconnect() { network_enabled = false; delete m_s; m_s = NULL; /*delete m_sock;*/ // XXX EBADF for ~QSocketDevice (in close()) m_sock = NULL; network->disconnect(); kdDebug() << "network ::disconnect done" << endl; if(canvas) canvas->enable(false); kdDebug() << "::disconnect done" << endl; } void TopLevel::menuStatusBar(int lives, int bombs, int power) { menuBar()->changeItem(i18n("1 Life", "%n Lives", lives), livestext); menuBar()->changeItem(i18n("1 Bomb", "%n Bombs", bombs), bombstext); menuBar()->changeItem(i18n("1 Power", "%n Power", power), powertext); } void TopLevel::menuStatusBarScore(int time, int score) { menuBar()->changeItem(i18n("1 Second", "%n Seconds", time), clocktext); menuBar()->changeItem(i18n("1 Point", "%n Points", score), scoretext); } void TopLevel::menuStatusBarSetup() { QPixmap pix; QImage tmp; pix = QPixmap(Resource::path("menu_lives")); tmp = pix.convertToImage(); tmp = tmp.smoothScale(16, 16); pix.convertFromImage(tmp); menuBar()->changeItem(livescount, pix); pix = QPixmap(Resource::path("menu_bombs")); tmp = pix.convertToImage(); tmp = tmp.smoothScale(16, 16); pix.convertFromImage(tmp); menuBar()->changeItem(bombscount, pix); pix = QPixmap(Resource::path("menu_power")); tmp = pix.convertToImage(); tmp = tmp.smoothScale(16, 16); pix.convertFromImage(tmp); menuBar()->changeItem(powercount, pix); pix = QPixmap(Resource::path("clock")); tmp = pix.convertToImage(); tmp = tmp.smoothScale(16, 16); pix.convertFromImage(tmp); menuBar()->changeItem(clockcount, pix); pix = QPixmap(Resource::path("score")); tmp = pix.convertToImage(); tmp = tmp.smoothScale(16, 16); pix.convertFromImage(tmp); menuBar()->changeItem(scorecount, pix); } void TopLevel::slotPosition(int x, int y) { playground->ensureVisible(x * Resource::tilesize(), y * Resource::tilesize(), Resource::tilesize() * 4, Resource::tilesize() * 4); }