/*************************************************************************** gamemanager.cpp - description ------------------- begin : lun mar 04 15:20:00 CET 2002 copyright : (C) 2002 by Romain Vinot email : vinot@aist.enst.fr ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifdef _WIN32 #pragma warning (disable : 4786) #endif #ifdef _WIN32 #include "zlib.h" #else #include #endif #ifndef _WIN32 #include // For sleep #endif #include #include #include #include #include #include #include #include #include #include "gamemanager.h" #include "rand.h" #include "level.h" #include "board.h" #include "sound.h" #include "datastoring.h" #include "network.h" #include "sessionmanager.h" #include "guicommandline.h" #include "guicreature.h" #include "guiboard.h" int Rand::seedvalue; struct StdRand { StdRand() {} int operator()(int n) { return Rand::roll(n,false); } }; GameManager::GameManager(GUICreature *c, GUIBoard *b, GUICommandLine *cl, Turn t) : guic(c), guib (b), guicmd(cl), inGame(false), level(0), turn(t), hasCheated(false), selected(0), onView(0), selectableOnView(0), listChanged(true), maxCreaId(1), cmdPts(0), replaySpeed(1000), replayMode(false), recordMode(false) { level = new Level(guic, &cmdPts, guicmd); // Create the needed directories to store savegame and all infos. QDir userDir(homeDir); if (!userDir.exists()) if (!userDir.mkdir(homeDir) || !userDir.exists()) { cout << "The user directory for spacehulk \"" << homeDir << "\" cannot be created. Please fix the problem.\n"; exit(1); } if (!userDir.exists("savegame")) userDir.mkdir("savegame"); if (store->getPref("replayspeed")=="500") replaySpeed = 500; } GameManager::~GameManager(void) {} Turn GameManager::getTurn(void) {return turn;} int GameManager::getTurnNumber() {return nbTurns;} QString GameManager::getEmail(void) { return email; } gameType GameManager::getGameType(void) { return type; } void GameManager::setReplayRecordMode(bool replay) { if (replay) { replayMode=true; recordMode=false; } else { replayMode=false; recordMode=true; } } bool GameManager::isHeuristicTurn(void) {return heuris;} bool GameManager::InitChooseGameType(void) { // TODO : getGameType should be able to return false if dialog deleted. type = guicmd->GetGameType(&heuris, &cheatProtection); if (type==PBEM && !heuris) { guicmd->Write("For PBEM, you must use the heuristic." " It has been enabled by default."); heuris=true; } return true; } bool GameManager::InitChooseLevel(void) { inGame = true; QString filename = guicmd->GetLevelToPlay(); if (!level->InitFromFilename(filename, guib)) { inGame = false; guicmd->printPopupMessage("The level cannot be loaded. The file should be " "corrupted."); return false; } // Initialize the random generator for the game. unsigned int ti = time(0); Rand::setSeed(ti); InitBlipCards(); nbTurns=0; guicmd->DrawInterface(); if (type==NETWORK) { QDomDocument doc("savegame"); store->Save(doc); QString content = doc.toString(); net->sendSaveGame(content,true); } return true; } void GameManager::InitPlayerPlacement (void) { if (type==HOTSEAT) { guicmd->Write("Initialisation of marine units"); level->InitMarine(); guicmd->Write("Initialisation of genestealer units"); level->InitGenestealer(); if (level->getFirstPlayerIsMarine()) { guicmd->ClearInfoPanel(); guicmd->Write("\nThe game can begin. First turn is for marine player."); } else { guicmd->Write("\nThe game can begin. " "First turn is for genestealer player."); turn = GENESTEALER_TURN; } nbTurns=1; beginTurn(); } else if (type==NETWORK) { setReplayRecordMode(false); guicmd->Write("Initialisation of marine units"); level->InitMarine(); guicmd->DrawInterface(); setReplayRecordMode(true); guicmd->Write("Initialisation of genestealer units"); level->InitGenestealer(); if (level->getFirstPlayerIsMarine()) { guicmd->ClearInfoPanel(); guicmd->Write("\nThe game can begin. First turn is for marine player."); setReplayRecordMode(false); } else { guicmd->Write("\nThe game can begin. " "First turn is for genestealer player."); turn = GENESTEALER_TURN; setReplayRecordMode(true); } nbTurns=1; beginTurn(); } else if (type==PBEM) { guicmd->Write("Initialisation of marine units"); level->InitMarine(); pbemidMarine = store->getNewPbemId(); pbemidGenestealer = 0; store->addPbem(pbemidMarine,nbTurns,turn); store->SaveGameState(); if (!level->isInitGenestealerNull()) { guicmd->printPopupMessage("Your opponent will now initialize his genestealer units.\nPlease save and send the file."); session->SaveRecordingToEmail(); if (!level->getFirstPlayerIsMarine()) store->updatePbem(pbemidMarine,1,turn); board->removeAll(); man->removeAll(); return; } guicmd->Write("Genestealers have no initialisation."); nbTurns=1; man->InitPBEM(); store->updatePbem(pbemidMarine,nbTurns,turn); if (level->getFirstPlayerIsMarine()) { guicmd->Write("\nThe game can begin. First turn is for marine player."); man->beginTurn(); } else { guicmd->printPopupMessage("First turn is for genestealer player.\n" "Please save and send the file."); session->SaveRecordingToEmail(); board->removeAll(); man->removeAll(); } } } void GameManager::InitPBEM(void) { replays.clear(); store->SaveGameState(); recordMode = true; replayMode = false; } void GameManager::InitBlipCards(void) { blipCards.clear(); int i=0; for (i=0; i<9; i++) blipCards.push_back(1); for (i=0; i<3; i++) blipCards.push_back(2); for (i=0; i<9; i++) blipCards.push_back(3); blipCards.push_back(4); blipCards.push_back(5); blipCards.push_back(6); for (i=0; i<3; i++) blipCards.push_back(0); StdRand r; random_shuffle(blipCards.begin(), blipCards.end(), r ); currBlipCard = blipCards.begin(); } bool GameManager::isInGame(void) {return inGame;} bool GameManager::isInReplayMode(void) {return replayMode;} bool GameManager::isInRecordMode(void) {return recordMode;} void GameManager::SavePBEM(void) { session->SaveRecordingToEmail(); } void GameManager::changeReplaySpeed(void) { replaySpeed = (3 - replaySpeed/500) * 500; } void GameManager::Replay(void) { if (type==NETWORK) { while(replayMode==true) { QString cmd = getRecord(); if (selected) guicmd->CenterView(selected->getPosition()); guicmd->getReplayCommand(cmd.latin1()); guicmd->DrawInterface(); } return; } replayMode = true; // TODO : replace size==0 par empty ? if (replays.size()!=0) beginTurn(); while (replays.size()!=0) { QString curr = getRecord(); if (selected) guicmd->CenterView(selected->getPosition()); guicmd->getReplayCommand(curr.latin1()); guicmd->DrawInterface(); #ifdef _WIN32 Sleep(replaySpeed); #else usleep(replaySpeed*1000); #endif } replayMode = false; } bool GameManager::Undo(void) { if (canUndo) { board->move(selected,selected->getPosition(),previous.caseId); selected->setPosition(previous.caseId); selected->setOrientation(previous.dir); selected->setActionPts(previous.actionPts); cmdPts = previous.cmdPts; canUndo=false; return true; } return false; } void GameManager::initUndo(void) { if (selected) { canUndo = true; previous.caseId = selected->getPosition(); previous.dir = selected->getOrientation(); previous.actionPts = selected->getActionPts(); previous.cmdPts = cmdPts; } else canUndo = false; } void GameManager::disableUndo() { canUndo = false; } int GameManager::getCurrentMaxId(void) {return maxCreaId;} void GameManager::NewCreature(Creature *crea, bool force) { // set *objs = board->getObject(crea->getPosition()); if (! board->addObject(crea, force)) return; // Problem somewhere. The creature cannot be created. crea->begin(turn); liste[maxCreaId] = crea; maxCreaId++; listChanged = true; } void GameManager::deleteCreature(Object *crea) { crea->dropTakenObject(true); liste.erase(crea->getId()); board->delObject(crea); if (selected == crea) selected = 0; if (selectableOnView == crea) { selectableOnView = 0; onView=0; } delete crea; listChanged=true; } void GameManager::informKilled(ObjectType ty, int caseId) { level->killed(ty); if (ty==GENESTEALER || ty==BLIP) guib->setKilled(caseId,false); if (ty==BOLTERMARINE||ty==FLAMER||ty==SERGEANT) guib->setKilled(caseId,true); } QString GameManager::chooseAction(int x, int y) { char res[60]; if (selected==0) { sprintf(res,"select %d %d",x,y); return res; } int caseId = x+y*board->Xmax; int xsel = selected->getPosition()%board->Xmax; int ysel = selected->getPosition()/board->Xmax; list path = FindPath(xsel+ysel*board->Xmax,caseId,false,NODIRECTION); /* for (list::iterator ite=path.begin(); ite!=path.end(); ite++) { QString write; write.sprintf("move to [%d %d]",*ite%board->Xmax,*ite/board->Xmax); guicmd->Write(write); } */ Direction dir=selected->getOrientation(); if (board->isEmpty(caseId)) { // Case is empty. It should be a movement (move/enter/turn). if (board->isEntryZoneCase(selected->getPosition())) return "enter"; if (x==xsel && y==ysel+1) { if (dir==WEST || dir==EAST) return "turn south"; return "move south"; } if (x==xsel && y==ysel-1) { if (dir==WEST || dir==EAST) return "turn north"; return "move north"; } if (x==xsel+1 && y==ysel) { if (dir==NORTH || dir==SOUTH) return "turn east"; return "move east"; } if (x==xsel-1 && y==ysel) { if (dir==NORTH || dir==SOUTH) return "turn west"; return "move west"; } if (x==xsel+1 && y==ysel+1) return "move south-east"; if (x==xsel-1 && y==ysel+1) return "move south-west"; if (x==xsel+1 && y==ysel-1) return "move north-east"; if (x==xsel-1 && y==ysel-1) return "move north-west"; } if (x==xsel || y==ysel) { // If click is just the side of the unit, turn it. if (x==xsel && y==ysel-1 && (dir==WEST || dir==EAST)) return "turn north"; if (x==xsel && y==ysel+1 && (dir==WEST || dir==EAST)) return "turn south"; if (x==xsel-1 && y==ysel && (dir==NORTH || dir==SOUTH)) return "turn west"; if (x==xsel+1 && y==ysel && (dir==NORTH || dir==SOUTH)) return "turn east"; } set *objs = board->getObject(caseId); set::iterator it; if (objs==0) return ""; if (turn==MARINE_TURN) // If there is a genestealer shoot it, if there is a marine select it. for (it=objs->begin(); it!=objs->end(); it++) if ((*it)->Type()==GENESTEALER) { if (selected->Type()==FLAMER) sprintf(res,"fire %d %d",x,y); else sprintf(res,"shoot %d %d",x,y); return res; } else if ((*it)->Type()==BOLTERMARINE || (*it)->Type()==SERGEANT || (*it)->Type()==FLAMER) { sprintf(res,"select %d %d",x,y); return res; } if (turn==GENESTEALER_TURN) // If there is a marine attack it, if there is a genest or blip select it for (it=objs->begin(); it!=objs->end(); it++) if ((*it)->Type()==BOLTERMARINE || (*it)->Type()==SERGEANT || (*it)->Type()==FLAMER) { sprintf(res,"attack %d %d",x,y); return res; } else if ((*it)->Type()==GENESTEALER || (*it)->Type()==BLIP) { sprintf(res,"select %d %d",x,y); return res; } // If there is a door, open it (you can't close it with this smart click). for (it=objs->begin(); it!=objs->end(); it++) { if ((*it)->canBeOpened()) { sprintf(res,"open %d %d",x,y); return res; } } // Can't find anything smart to do... sprintf(res,"%d %d",x,y); return res; } void GameManager::beginTurn(void) { switch(turn) { case MARINE_TURN: level->BeginMarineTurn(); break; case GENESTEALER_TURN: level->BeginGenestealerTurn(); break; case CAT_TURN: for (map::iterator ite = liste.begin(); ite != liste.end(); ite++) if (ite->second->Type()==CAT) { ite->second->select(); selected = ite->second; } } for (map::iterator it = liste.begin(); it != liste.end(); it++) { it->second->beginNewTurn(turn); } const set *entries = board->getEntryZoneCases(); for (set::const_iterator ite=entries->begin(); ite!=entries->end(); ite++) { if (man->nearestMarine(*ite)<=6) currentWaitingEntries.insert(*ite); } listChanged=true; guicmd->DrawInterface(); } QString GameManager::getLevelMessage(void) { if (!inGame) return "No Game."; QString str; QTextStream info(&str,IO_WriteOnly); info << "Turn " << nbTurns << " : "; if (turn==MARINE_TURN) { info << "Marine player"; } else if (turn==GENESTEALER_TURN) info << "Genestealer player"; else if (turn==CAT_TURN) info << "Cat module"; QString tmp = level->getMessage(); if (tmp!="") info << ", " << tmp; return str; } void GameManager::getLevelInfo(QString &title, QString &auth, QString &nar, QString &obj, QString &forc, QString &cond) { if (level) level->getLevelInfo(title, auth, nar, obj, forc, cond); } set * GameManager::getObjectiveCases(void) { return level->getObjectiveCases(); } const list* GameManager::getExitedUnits(ObjectType type) { return &exitedUnits[type]; } bool GameManager::isVisibleFromMarine(int caseId) { for (map::iterator it=liste.begin(); it!=liste.end();it++) { if (it->second->isMarinePlayer() && it->second->canViewAt(caseId) && it->second->NoObstacleTo(caseId)) return true; } return false; } int GameManager::nearestMarine(int caseId) { int mindist = board->Xmax+board->Ymax; for (map::iterator it=liste.begin(); it!=liste.end();it++) { if (it->second->isMarinePlayer() && board->DistanceSum(it->second->getPosition(),caseId)DistanceSum(it->second->getPosition(),caseId); } return mindist; } bool GameManager::isWaitingEntry(int caseId) { return currentWaitingEntries.find(caseId)!=currentWaitingEntries.end(); } void GameManager::RevertBlip(Blip *crea, bool isVoluntary, int viewedPos) { int caseId = crea->getPosition(); int Xcoord = crea->getXPosition(); int Ycoord = crea->getYPosition(); int nbToRevert = crea->getNbGen(); int currActionPts = crea->getActionPts(); int prevInitTurn = crea->getInitTurn(); if (nbToRevert == 0) {// This is a false blip. deleteCreature(crea); return; } if (board->isEntryZoneCase(caseId)) { bool reuseDir=false; Direction dir=NORTH; for (int idx=0; idx dirinfo = guicmd->GetDirection(tmp); dir = dirinfo.first; reuseDir = dirinfo.second; QString reuse=(reuseDir)?QString(" reuse"):QString(" noreuse"); man->recordCommand(QString("get direction ")+ dir2string(dir) + reuse); } Genestealer *nw = new Genestealer(guic, guicmd, caseId, maxCreaId, dir, currActionPts); NewCreature(nw); nw->setInitTurn(prevInitTurn); } deleteCreature(crea); return; } set reversionCases; crea->getEmptyNeighbors(&reversionCases); set toremove; for (set::iterator it=reversionCases.begin(); it!= reversionCases.end(); it++) { if (isVoluntary && isVisibleFromMarine(*it)) toremove.insert(*it); } for (set::iterator it2=toremove.begin(); it2!= toremove.end(); it2++) { reversionCases.erase(*it2); } deleteCreature(crea); QString str; QTextStream tmp(&str,IO_WriteOnly); if (isVoluntary) tmp << "Voluntary reversion in [" << Xcoord << " " << Ycoord << "].\n"; else tmp << "Unvoluntary reversion in [" << Xcoord << " " << Ycoord << "]. Marine choice.\n"; tmp << "Revert the blip. " << nbToRevert << " genestealers to place.\n" << "First one must be on the blip case.\nPlease give the orientation."; pair dirinfo = guicmd->GetDirection(str); Direction dir = dirinfo.first; bool reuse = dirinfo.second; QString write = (reuse)?QString(" reuse"):QString(" noreuse"); man->recordCommand(QString("get direction ")+dir2string(dir)+write); Genestealer *nw = new Genestealer(guic, guicmd, caseId, maxCreaId, dir, currActionPts); NewCreature(nw,true); nw->setInitTurn(prevInitTurn); nbToRevert--; guicmd->DrawInterface(); while(nbToRevert>0 && reversionCases.size()>0) { QString tmp2; tmp2.sprintf("Revert the blip. %d genestealers to place.\n" "Please choose a case.", nbToRevert); int caseId=-1; if (reuse) { caseId = guicmd->GetCase(&reversionCases,tmp2); man->recordCommand(QString("get case ")+QString::number(caseId)); } else { pair > res = guicmd->GetCaseAndDirection(&reversionCases, tmp2); caseId = res.first; dir = res.second.first; reuse = res.second.second; QString write = (reuse)?" reuse":" noreuse"; man->recordCommand(QString("get casedir ")+QString::number(caseId) + " " + dir2string(dir) + write); } Genestealer *anw = new Genestealer(guic, guicmd, caseId, maxCreaId, dir, currActionPts); NewCreature(anw,true); anw->setInitTurn(prevInitTurn); guicmd->DrawInterface(); reversionCases.erase(caseId); nbToRevert--; } guicmd->Write("Reversion done."); } bool GameManager::areGenestealerInEntryZone(void) { for (map::iterator it=liste.begin(); it!=liste.end(); it++) if (it->second->Type() == GENESTEALER && board->isEntryZoneCase(it->second->getPosition())) return true; return false; } void GameManager::deleteGenestealerInEntryZone(void) { list toDelete; for (map::iterator it=liste.begin(); it!=liste.end(); it++) { if (it->second->Type() == GENESTEALER && board->isEntryZoneCase(it->second->getPosition())) toDelete.push_back (it->second); } for (list::iterator ite=toDelete.begin(); ite!=toDelete.end(); ite++) { deleteCreature(*ite); } } bool GameManager::ThrowFireDice (void) { int val = 1+Rand::roll(6); if (val<6) return true; return false; } void GameManager::CheckNonVisibleBlip(void) { vector toRevert; vector viewedFrom; for (map::iterator it=liste.begin(); it!=liste.end(); it++){ if (it->second->Type()==BLIP && !board->isEntryZoneCase(it->second->getPosition())) { // The blip can be visible. Check if one Marine can view it. int viewed = CaseIsVisibleFromMarine(it->second->getPosition()); if (viewed!=-1) { // This Blip is visible, must be reverted. toRevert.push_back((Blip *)it->second); viewedFrom.push_back(viewed); man->disableUndo(); break; } } } for(unsigned int i=0; i::iterator it_marine=liste.begin(); it_marine!=liste.end(); it_marine++) if (it_marine->second->isMarinePlayer() && it_marine->second->canViewAt(caseId) && it_marine->second->NoObstacleTo(caseId)) { // This Blip is visible, must be reverted. return it_marine->second->getPosition(); } return -1; } void GameManager::CheckOverwatchTarget(void) { if (!selected || turn==MARINE_TURN) return; int caseId = selected->getPosition(); for (map::iterator it=liste.begin(); it!=liste.end(); it++){ bool res = it->second->OverwatchShoot(caseId); if (res && selected) { // We check if the unit has not been removed // from a previous successful overwatch shot. if (selected->Type()==BLIP) { informKilled(GENESTEALER,caseId); if (selected->shootHasKilledObject()) deleteCreature(selected); } else { informKilled(selected->Type(),caseId); deleteCreature(selected); } } } CheckNonVisibleBlip(); } void GameManager::CheckExit(void) { set *exits = board->getExits(); set todel; for (set::iterator it=exits->begin(); it!=exits->end(); it++) { set *obj = board->getObject(*it); if (obj->size()!=1) { // There is a creature in the exit. Find the exit object // and add the creatures to it. Exit *exit=0; for (set::iterator it1=obj->begin();it1!=obj->end(); it1++) if ((*it1)->Type()==EXIT) exit = dynamic_cast (*it1); if (exit==0) { cerr << "Square " << *it << " should have an exit. We don't find it."; return; // There is a problem... } for (set::iterator it2=obj->begin();it2!=obj->end(); it2++) if ((*it2)->Type()!=EXIT) { exit->addCreatureOut((*it2)->Type(), (*it2)->getId()); exitedUnits[(*it2)->Type()].push_back((*it2)->getId()); todel.insert(*it2); } disableUndo(); } } for (set::iterator it3=todel.begin(); it3!=todel.end(); it3++) deleteCreature(*it3); } // endTurn return a boolean indicating if the game is finished or not // and the type of player which must play the next turn // (marine or genestealer). Only useful to decide for the CAT turn. pair GameManager::endTurn(void) { if (selected) selected->deselect(); selected = 0; onView = 0; currentWaitingEntries.clear(); for(int i=0; i<2; i++) if (XOR(level->getFirstWinCheckerIsMarine(),(i==1))) { if (level->EndMarineTurn()) { guicmd->endOfGame("The Marine player won the game.\nThe game is now " "finish. We hope you enjoy playing it.\n"); return pair(true,MARINE_TURN); } } else { if(level->EndGenestealerTurn()) { guicmd->endOfGame("The Genestealer player won the game.\nThe game is " "now finish. We hope you enjoy playing it.\n"); return pair(true,MARINE_TURN); } } if (level->EndDrawTurn()) { guicmd->endOfGame("The game is a draw. No winner, no looser !\n" "The game is now finish. We hope you enjoy playing it."); if (getGameType() == PBEM && !isInReplayMode()) { guicmd->printPopupMessage("You must now send and save the file for your opponent."); man->SavePBEM(); } return pair(true,MARINE_TURN); } switch (turn) { case CAT_TURN: turn = GENESTEALER_TURN; break; case MARINE_TURN: { for (map::iterator it=liste.begin(); it!=liste.end();it++) if (it->second->Type()==CAT && it->second->canBeTaken()) { // The CAT is free at end of marine turn. turn=CAT_TURN; int choice = 1+Rand::roll(6); if (choice<=3) { guicmd->Write("Cat module is free. Genestealer player can control it for this turn."); return pair(false,GENESTEALER_TURN); } else { guicmd->Write("Cat module is free. Marine player can control it for this turn."); return pair(false,MARINE_TURN); } } turn = GENESTEALER_TURN; break; } case GENESTEALER_TURN: deleteGenestealerInEntryZone(); checkGenestealerInFlame(); turn = MARINE_TURN; nbTurns++; break; } deleteFlame(); return pair(false,turn); } Creature * GameManager::getCreature(int creatureId) { map::iterator it = liste.find(creatureId); if (it == liste.end()) return 0; return it->second; } const map *GameManager::getList(void) {return &liste;} bool GameManager::selectCreature(Creature *c) { if (selected == c) { return true; } if (c->select()) { if (selected) selected->deselect(); selected = c; if (selectableOnView==c) { selectableOnView=0; onView=0; previousView=-1; } listChanged=true; return true; } return false; } bool GameManager::selectCreature(int caseId) { set *obj=board->getObject(caseId); if (obj) for (set::iterator it=obj->begin(); it!=obj->end(); it++) { if ((*it)->isSelectable() && selectCreature((Creature *) *it)) return true; } return false; } Creature * GameManager::getSelected(void) { return selected; } // See GUICommandLine for the 'returned code'. int GameManager::Attack(Creature *crea) { AttackRes res = crea->Attack(); if (!res.attackEnable) return 0; Direction currDir = crea->getOrientation(); if(crea->Type() == GENESTEALER) sound->playSound(SoundSystem::GenestealerAttack); else sound->playSound(SoundSystem::MarineAttack); switch (res.resAttack) { case 0: // Ex-aequo. Nothing happens. Eventually the defenser can turn. turnDefenser(res.def, crea, currDir); return 1; case -1: // Defenser was successfull. if (res.def->canAttack(currDir)) { ObjectType ty = crea->Type(); informKilled(ty,crea->getPosition()); if (ty==GENESTEALER) sound->playSound(SoundSystem::GenestealerDie); if (ty==BOLTERMARINE || ty==SERGEANT || ty==FLAMER) sound->playSound(SoundSystem::MarineDie); deleteCreature(crea); return 2; } turnDefenser(res.def, crea, currDir); return 3; case 1: { // Attacker was successfull, defenser is killed. ObjectType ty = res.def->Type(); informKilled(ty, res.def->getPosition()); if (ty==GENESTEALER) sound->playSound(SoundSystem::GenestealerDie); if (ty==BOLTERMARINE || ty==SERGEANT || ty==FLAMER) sound->playSound(SoundSystem::MarineDie); deleteCreature(res.def); return 4; } } // Error somewhere, we should not be here... return 0; } void GameManager::turnDefenser(Object *def, Creature *att, Direction attackDir) { Direction dir=def->getOrientation(); Direction targetDir; switch(attackDir) { case NORTH: targetDir=SOUTH; break; case SOUTH: targetDir=NORTH; break; case WEST: targetDir=EAST;break; case EAST: targetDir=WEST;break; default: return; } if (dir==targetDir || dir==NODIRECTION) return; bool res; if (heuris) res = (att->getActionPts() >= 1); else { res=guicmd->GetYesOrNo("Defenser is still alive. He can turn toward the attacker.\n Do you want to do it ?"); man->recordCommand(QString("get yesorno ") + QString::number((int)res)); } if (res) def->turnOrientation(targetDir); } int GameManager::Shoot(Creature *crea, int caseId) { AttackRes res = crea->Shoot(caseId); if (res.attackEnable) sound->playSound(SoundSystem::MarineShoot); if (res.resAttack <= 0) return res.resAttack; if (res.def->shootHasKilledObject()) { if (res.def->Type()==GENESTEALER) sound->playSound(SoundSystem::GenestealerDie); else if (res.def->Type()==DOOR) sound->playSound(SoundSystem::MarineShootDoor); informKilled(res.def->Type(), res.def->getPosition()); deleteCreature(res.def); if (res.resAttack == 1) return 1; if (res.resAttack == 2) { CheckNonVisibleBlip(); set adjGen; res.def->getAdjacentGenestealers(&adjGen, crea); int ln = adjGen.size(); if (ln==0) guic->Message("Too bad, there is no other target near the first one."); else if (ln==1) { Object *crea = *(adjGen.begin()); informKilled(crea->Type(), crea->getPosition()); deleteCreature(crea); } else { set adjCases; for (set::iterator it=adjGen.begin(); it!=adjGen.end(); it++) adjCases.insert((*it)->getPosition()); int kill = guicmd->GetCase(&adjCases,"Double shoot : you kill another " "genestealer.\nChoose the one to die.\n"); man->recordCommand(QString("get case ") + QString::number(kill)); for (set::iterator ite=adjGen.begin(); ite!=adjGen.end();ite++) if((*ite)->getPosition()==kill) { informKilled((*ite)->Type(),(*ite)->getPosition()); deleteCreature(*ite); } } return 2; } } else // The shoot is correct but the object is not killed. return 1; return 0; // This expression should be never reached. } Flame * GameManager::addFlame(int caseId) { sound->playSound(SoundSystem::MarineFlame); if (flames.find(caseId)!=flames.end()) { // There is already a flame in this case. flames[caseId]->addBigFlame(); } else { flames[caseId] = new Flame(guic, guicmd, caseId); board->addObject(flames[caseId]); } return flames[caseId]; } bool GameManager::deleteFlame(void) { bool res=false; list todel; for (map::iterator it=flames.begin(); it!=flames.end(); it++) { if (it->second->reduceFlame()) { board->delObject(it->second); todel.push_back(it->first); res=true; } } for (list::iterator ite=todel.begin(); ite!=todel.end(); ite++) flames.erase(*ite); return res; } void GameManager::checkGenestealerInFlame(void) { for (map::iterator it=liste.begin(); it!=liste.end(); it++) { if (it->second->Type() == GENESTEALER && flames.find(it->second->getPosition())!=flames.end() && ThrowFireDice()) { guicmd->Write("A genestealer has been killed by flame."); sound->playSound(SoundSystem::GenestealerDie); informKilled(GENESTEALER, it->second->getPosition()); deleteCreature(it->second); } } } void GameManager::printInterface (void) {guicmd->DrawInterface();} void GameManager::printMissionObjectives(void) { QString tmp, obj; level->getLevelInfo(tmp,tmp,tmp,obj,tmp,tmp); guicmd->Write(obj.latin1()); } void GameManager::printAllCreaturesInfo(void) { if (listChanged) { guicmd->ClearListCreatures(); for (map::iterator it = liste.begin(); it != liste.end(); it ++) { it->second->printInfo(1); } listChanged=false; } } void GameManager::printSelectedCreature(void) { if (selected!=0) selected->printInfo(2); } void GameManager::printOnViewCreature(void) { if (onView!=0) for (set::iterator it=onView->begin(); it!=onView->end(); it++) (*it)->printInfo(3); else if (selectableOnView!=0) selectableOnView->printInfo(3); } bool GameManager::onViewIsSelectable(void) { return (selectableOnView && selectableOnView->isSelectable()); } void GameManager::setOnView(int caseId) { set *objs = board->getObject(caseId); if (board->isEntryZoneCase(caseId)) { int count=0; for (set::iterator itx=objs->begin(); itx!=objs->end(); itx++) if ((*itx)->Type()!=ENTRYZONE && (*itx)->Type()!=BULKHEAD) count++; if (previousView!=caseId) { // This is the first time we click on this square. Print all infos on // main info panel. for (set::iterator it1=objs->begin(); it1!=objs->end(); it1++) if ((*it1)->Type()==ENTRYZONE || (*it1)->Type()==BULKHEAD) (*it1)->printInfo(0); for (set::iterator it2=objs->begin(); it2!=objs->end(); it2++) if ((*it2)->Type()!=ENTRYZONE && (*it2)->Type()!=BULKHEAD) { (*it2)->printInfo(0); } selectableOnView=0; } previousView=caseId; if (count==0) { onView=objs; selectableOnView=0; } else { onView=0; set::iterator it; if (selectableOnView) it=objs->find(selectableOnView); else it=objs->end(); while (it==objs->end() || (*it==selectableOnView) || (*it)->Type()==ENTRYZONE || (*it)->Type()==BULKHEAD) { if (it==objs->end()) it=objs->begin(); else it++; } selectableOnView = *it; } } else if (objs) { onView=objs; selectableOnView = 0; for (set::iterator it=objs->begin(); it!=objs->end(); it++) if ((*it)->isSelectable()) selectableOnView = *it; previousView=caseId; } else { onView=0; selectableOnView=0; previousView=-1; } } void GameManager::selectOnView(void) { if (selectableOnView!=0) { char cmd[20]; sprintf(cmd,"select %d", selectableOnView->getId()); selectableOnView=0; onView=0; guicmd->getCommand(cmd); } } void GameManager::recordCommand(QString command) { replaysMutex.lock(); if (recordMode) { if (type==NETWORK) net->sendCommand(command); else if (!command.contains("record")) replays.push_back(command); } replaysMutex.unlock(); } void GameManager::removeLatestRecord(void) { replaysMutex.lock(); if (recordMode && replays.size()!=0) replays.pop_back(); replaysMutex.unlock(); } QString GameManager::getRecord(void) { if (type==NETWORK) { replaysMutex.lock(); int nb = replays.size(); replaysMutex.unlock(); if (nb==0) guicmd->GetNetworkCommand(); replaysMutex.lock(); QString cmd = replays.front(); replays.pop_front(); replaysMutex.unlock(); return cmd; } else { replaysMutex.lock(); QString cmd = replays.front(); replays.pop_front(); replaysMutex.unlock(); return cmd; } } void GameManager::Connect(void) { // Called when you need to connect to the server QString ip = guicmd->GetIPAdress(); net = new NetworkInterface(guicmd,ip); type = NETWORK; } void GameManager::setConnection(int socket) { // Called when you are the server and you received a connection. net = new NetworkInterface(guicmd,socket); } void GameManager::removeAll(void) { inGame = false; exitedUnits.clear(); selected = 0; onView = 0; selectableOnView = 0; liste.clear(); listChanged=true; blipCards.clear(); currentWaitingEntries.clear(); replays.clear(); replayMode=false; recordMode=false; email = ""; hasCheated=false; delete level; level = new Level(guic,&(man->cmdPts),guicmd); guib->removeAll(); } FullDirection cheapTurn(Direction dir, FullDirection toward) { switch(toward) { case NORTHEAST: switch(dir) { case WEST: case NORTH: return NORTHNORTH; case EAST: case SOUTH: return EASTEAST; case NODIRECTION: return NORTHNORTH; } case NORTHNORTH: return NORTHNORTH; case NORTHWEST: switch(dir) { case NORTH: case EAST: return NORTHNORTH; case WEST: case SOUTH: return WESTWEST; case NODIRECTION: return NORTHNORTH; } case EASTEAST: return EASTEAST; case WESTWEST: return WESTWEST; case SOUTHEAST: switch(dir) { case NORTH: case EAST: return EASTEAST; case WEST: case SOUTH: return SOUTHSOUTH; case NODIRECTION: return SOUTHSOUTH; } case SOUTHSOUTH: return SOUTHSOUTH; case SOUTHWEST: switch(dir) { case WEST: case NORTH: return WESTWEST; case SOUTH: case EAST: return SOUTHSOUTH; case NODIRECTION: return SOUTHSOUTH; } } return SOUTHSOUTH; } list GameManager::FindPath(int start, int end, bool isCreatureOriented, Direction dir) { multimap current; set alreadyVisited; int dist = board->Distance(start,end); PathFinderNode *node = new PathFinderNode(start,dist,dist,0); current.insert(pair(dist,node)); alreadyVisited.insert(start); while (current.size() != 0) { // Find the new best choice multimap::iterator it = current.begin(); int minCost = it->first; int minFutureCost = it->second->futureCost; multimap::iterator best=it; do { if (minFutureCost>it->second->futureCost) { minFutureCost=it->second->futureCost; best=it; } it++; } while (it!=current.end() && it->first==minCost); int currCaseId = best->second->caseId; int previousCost = best->second->fullCost - best->second->futureCost; // Try every 8 possibility and add the working ones in the multimap. for(int idx=0;idx<8;idx++) { int square=-1; switch (idx) { case 0: if (currCaseId < (board->Xmax-1)*board->Ymax) square = currCaseId + board->Xmax; break; case 1: if (currCaseId%board->Xmax != board->Xmax-1) square = currCaseId + 1; break; case 2: if (currCaseId >= board->Xmax) square = currCaseId - board->Xmax; break; case 3: if (currCaseId%board->Xmax != 0) square = currCaseId - 1; break; case 4: if (currCaseId < (board->Xmax-1)*board->Ymax && currCaseId%board->Xmax != board->Xmax-1) square = currCaseId + board->Xmax + 1; break; case 5: if (currCaseId>=board->Xmax&&currCaseId%board->Xmax!=board->Xmax-1) square = currCaseId - board->Xmax + 1; break; case 6: if (currCaseId >= board->Xmax && currCaseId%board->Xmax != 0) square = currCaseId - board->Xmax - 1; break; case 7: if (currCaseId < (board->Xmax-1)*board->Ymax && currCaseId%board->Xmax != 0) square = currCaseId + board->Xmax - 1; break; } // Check if the new square is empty. bool ok=false; if (square!=-1 && board->isEmpty(square) && alreadyVisited.find(square)==alreadyVisited.end()) { if (idx<4) ok=true; if (idx==4 && (board->isEmpty(currCaseId+board->Xmax) || board->isEmpty(currCaseId+1))) ok=true; if (idx==5 && (board->isEmpty(currCaseId-board->Xmax) || board->isEmpty(currCaseId+1))) ok=true; if (idx==6 && (board->isEmpty(currCaseId-board->Xmax) || board->isEmpty(currCaseId-1))) ok=true; if (idx==7 && (board->isEmpty(currCaseId+board->Xmax) || board->isEmpty(currCaseId-1))) ok=true; } if (ok) { // Add the new possibility. if (square==end) { // TODO : This could be not the best solution. return best->second->path; } int newFutureCost = board->Distance(square,end); int newFullCost = previousCost+1+newFutureCost; list newpath = best->second->path; newpath.push_back(square); PathFinderNode *n = new PathFinderNode(square,newFullCost, newFutureCost,&newpath); current.insert(pair(newFullCost,n)); alreadyVisited.insert(square); } } // Remove the current square. delete best->second; current.erase(best); } return list(); } // /* // list* GameManager::getActionsFromPath(Node *end, Direction dir) // { // ActionList AL(end, Xmax), *L, *tmp; // L=&AL; // FullDirection newDir=cheapTurn(dir,L -> dir); // if (newDir!= Dir2FullDir(dir)) // { // this -> todo = TURN; // this -> dir = newDir; // this -> next = L; // dir=FullDir2Dir(newDir); // }else{ // this -> todo = MOVE; // this -> dir = L -> dir; // this -> next = L -> next; // delete L; // L = this; // } // while(L -> next != NULL) // { // tmp = L -> next; // if ((newDir=cheapTurn(dir,L -> next -> dir)) // != Dir2FullDir(dir)) // { // L -> next = new ActionList(TURN,newDir,tmp); // dir=FullDir2Dir(newDir); // } // L = tmp; // } // } // */ // /* // list* GameManager::getActionsFromPath(PathFinderNode *end) // { // list *actions = new list; // while (end->parent != NULL) { // int diff = end->coord - end->parent->coord; // if (diff == board->Xmax+1) // actions->push_back("move northeast"); // else if (diff == board->Xmax) // actions->push_back("move northnorth"); // else if (diff == board->Xmax-1) // actions->push_back("move northwest"); // else if (diff == 1) // actions->push_back("move easteast"); // else if (diff == -1) // actions->push_back("move westwest"); // else if (diff == 1-board->Xmax) // actions->push_back("move southeast"); // else if (diff == -board->Xmax) // actions->push_back("move southsouth"); // else // actions->push_back("move northwest"); // end=end->parent; // } // return actions; // } // */ PathFinderNode::PathFinderNode(int c, int fc1, int fc2, list *p) : caseId(c), fullCost(fc1), futureCost(fc2) { if (p) path = *p; } PathFinderNode::~PathFinderNode() {}