/*************************************************************************** level.cpp - description ------------------- begin : sam avr 06 21:19: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 #include #include #include #ifdef _WIN32 #include "zlib.h" #else #include #endif #include "level.h" #include "rand.h" #include "gamemanager.h" #include "guicommandline.h" #include "guicreature.h" #include "guiboard.h" #include "board.h" #include "variables.h" #include "levelcond.h" #include "levelfct.h" const double DTD_VERSION=1.05; Level::Level (GUICreature *guic, int *cmdPts, GUICommandLine *guicmd) : CmdPts(cmdPts), guiCreature(guic), guiCmdLine(guicmd) { } Level::~Level(void) { std::map::iterator t; for(t = name_table.begin() ; t != name_table.end() ; t++) { delete (*t).second; } } bool Level::InitFromFilename(const QString &filename, GUIBoard *guiboard) { QString content; if (filename=="") return false; gzFile f = gzopen(filename.latin1(),"rb"); if (!f) { cout << "Error when opening file " << filename.latin1() << endl; return false; } char car; while (gzread(f,&car,1)>0) { content+=car; } gzclose(f); return InitFromContent(content,guiboard); } bool Level::InitFromContent(const QString& doc, GUIBoard *guiboard) { int i; for(i = 0 ; i < NB_OBJECT_TYPE ; i++) { num_killed[i] = 0; } if (!xml.setContent(doc)) { cout << "Error when loading file." << endl; return false; } // First, locate the primary nodes level = xml.documentElement(); float version = level.attribute("version","+Inf").toFloat(); if (version > DTD_VERSION) { QString str; QTextStream res(&str,IO_WriteOnly); res << "DTD Version of the level is " << version << endl; res << "I can't read version superior to " << DTD_VERSION << endl; res << "Please update your program.\n"; guiCmdLine->printPopupMessage(str); return false; } QString edition = level.attribute("edition", "first"); if (edition=="first") { guiCmdLine->printPopupMessage("This level is designed for SpaceHulk - first edition.\nYou can't play it with this program."); return false; } genestealer_cfg = level.lastChild().toElement(); if((genestealer_cfg.isNull()) || (genestealer_cfg.tagName() != "genestealer_cfg")) { cout << "genestealer_cfg is at the wrong position or not present at all" << endl; return false; } marine_cfg = genestealer_cfg.previousSibling().toElement(); if((marine_cfg.isNull()) || (marine_cfg.tagName() != "marine_cfg")) { cout << "marine_cfg is at the wrong position or not present at all\n"; return false; } global = marine_cfg.previousSibling().toElement(); if((global.isNull()) || (global.tagName() != "global")) { cout << "global is at the wrong position or not present at all\n"; return false; } map = global.previousSibling().toElement(); if((map.isNull()) || (map.tagName() != "map")) { cout << "Map is at the wrong position or not present at all\n"; return false; } // Before anything, set the map size QDomElement current; QString str_h, str_w; int w,h; str_w = map.attribute("width"); str_h = map.attribute("height"); w = str_w.toInt(); h = str_h.toInt(); board->setMax(w,h); guiboard->Init(); // First, read the predefined zones and zonelists // First, create the named zone current = level.firstChild().toElement(); while(current.tagName() == "zone") { LvlZone* zone = LvlZone::Build(current, this); if(zone == NULL) { cout << "Error, invalid zone definition" << endl; return false; } else if(!zone->is_ref) { cout << "Warning, zones at the beginning must have names" << endl; } current = current.nextSibling().toElement(); } // Then, the named zone_set while(current.tagName() == "zone_set") { LvlZoneList* zone = LvlZoneList::Build(current, this); if(zone == NULL) { cout << "Error, invalid zone list definition" << endl; return false; } else if(!zone->is_ref) { cout << "Warning, zone lists at the beginning must have names" << endl; return false; } current = current.nextSibling().toElement(); } CreateMap(guiCreature, guiCmdLine); current = global.firstChild().toElement(); if (current.tagName() != "leveldesc") { cout << "Error, invalid level format : there is no level description.\n"; return false; } QDomElement end = current.lastChild().toElement(); current = current.firstChild().toElement(); if (current.tagName() != "title") { cout << "Error, invalid level format : there is no title.\n"; return false; } title = current.text(); while(current != end) { current = current.nextSibling().toElement(); if (current.tagName() == "author") author = current.text(); else if (current.tagName() == "narration") narration = current.text(); else if (current.tagName() == "objectives") objectives = current.text(); else if (current.tagName() == "forces") forces = current.text(); else if (current.tagName() == "conditions") conditions = current.text(); else if (current.tagName() == "objective_squares") { QDomElement ob = current.lastChild().toElement(); LvlZoneList* zone=LvlZoneList::Build(ob,this); if(zone == NULL) { cout << "Error, invalid zone list definition" << endl; return false; } objectiveCases = *(zone->getCases()); } } guiCmdLine->printLevelInfo(title,author,narration, objectives, forces, conditions); // See which player should init first and create the init structure. current = global.namedItem("first").toElement(); firstPlayerIsMarine = (current.attribute("player")=="marine"); // Init of the marine QDomElement init = marine_cfg.namedItem("init").toElement(); if(!init.isNull()) LevelAction::BuildActions(init_marine, init, this); init = marine_cfg.namedItem("begin").toElement(); if(!init.isNull()) LevelAction::BuildActions(begin_marine, init, this); // Init of the genestealer init = genestealer_cfg.namedItem("init").toElement(); if(!init.isNull()) LevelAction::BuildActions(init_alien, init, this); init = genestealer_cfg.namedItem("begin").toElement(); if(!init.isNull()) LevelAction::BuildActions(begin_alien, init, this); current = global.namedItem("winpriority").toElement(); if (current.attribute("player") == "marine") firstWinCheckerIsMarine = true; else firstWinCheckerIsMarine = false; turnmsg = global.namedItem("turnmsg").toElement(); // Create win conditions. QDomElement win = genestealer_cfg.namedItem("win").toElement(); if(!win.isNull()) { QDomElement cond = win.lastChild().toElement(); winner_alien = new LvlCond(cond, this); } else { winner_alien = NULL; } win = marine_cfg.namedItem("win").toElement(); if(!win.isNull()) { QDomElement cond = win.lastChild().toElement(); winner_marine = new LvlCond(cond, this); } else { winner_marine = NULL; } win = global.namedItem("draw").toElement(); if (!win.isNull()) { QDomElement cond = win.lastChild().toElement(); winner_draw = new LvlCond(cond, this); } else { winner_draw = NULL; } return true; } bool Level::InitFromSaveGame(QDomElement &xml) { // First, locate the primary nodes level = xml; genestealer_cfg = level.namedItem("genestealer_cfg").toElement(); if((genestealer_cfg.isNull()) || (genestealer_cfg.tagName() != "genestealer_cfg")) { cout << "genestealer_cfg is at the wrong position or not present at all" << endl; return false; } marine_cfg = level.namedItem("marine_cfg").toElement(); if((marine_cfg.isNull()) || (marine_cfg.tagName() != "marine_cfg")) { cout << "marine_cfg is at the wrong position or not present at all\n"; return false; } global = level.namedItem("global").toElement(); if((global.isNull()) || (global.tagName() != "global")) { cout << "global is at the wrong position or not present at all\n"; return false; } // Begin with global element. QDomElement current = global.firstChild().toElement(); if (current.tagName() != "leveldesc") { cout << "Error, invalid level format : there is no level description.\n"; return false; } QDomElement end = current.lastChild().toElement(); current = current.firstChild().toElement(); if (current.tagName() != "title") { cout << "Error, invalid level format : there is no title.\n"; return false; } title = current.text(); while(current != end) { current = current.nextSibling().toElement(); if (current.tagName() == "author") author = current.text(); else if (current.tagName() == "narration") narration = current.text(); else if (current.tagName() == "objectives") objectives = current.text(); else if (current.tagName() == "forces") forces = current.text(); else if (current.tagName() == "conditions") conditions = current.text(); else if (current.tagName() == "objective_squares") { QDomElement ob = current.lastChild().toElement(); LvlZoneList* zone=LvlZoneList::Build(ob,this); if(zone == NULL) { cout << "Error, invalid zone list definition" << endl; return false; } objectiveCases = *(zone->getCases()); } } current = global.namedItem("winpriority").toElement(); firstWinCheckerIsMarine = (current.attribute("player") == "marine"); current = global.namedItem("first").toElement(); firstPlayerIsMarine = (current.attribute("player")=="marine"); turnmsg = global.namedItem("turnmsg").toElement(); // Create actions for "init" and "begin" QDomElement init = marine_cfg.namedItem("init").toElement(); if(!init.isNull()) LevelAction::BuildActions(init_marine, init, this); init = marine_cfg.namedItem("begin").toElement(); if(!init.isNull()) LevelAction::BuildActions(begin_marine, init, this); init = genestealer_cfg.namedItem("init").toElement(); if(!init.isNull()) LevelAction::BuildActions(init_alien, init, this); init = genestealer_cfg.namedItem("begin").toElement(); if(!init.isNull()) LevelAction::BuildActions(begin_alien, init, this); // Create win conditions. QDomElement win = genestealer_cfg.namedItem("win").toElement(); if(!win.isNull()) { QDomElement cond = win.lastChild().toElement(); winner_alien = new LvlCond(cond, this); } else { winner_alien = NULL; } win = marine_cfg.namedItem("win").toElement(); if(!win.isNull()) { QDomElement cond = win.lastChild().toElement(); winner_marine = new LvlCond(cond, this); } else { winner_marine = NULL; } win = global.namedItem("draw").toElement(); if (!win.isNull()) { QDomElement cond = win.lastChild().toElement(); winner_draw = new LvlCond(cond, this); } else { winner_draw = NULL; } return true; } bool Level::getFirstWinCheckerIsMarine(void) {return firstWinCheckerIsMarine;} bool Level::getFirstPlayerIsMarine(void) {return firstPlayerIsMarine;} bool Level::EndMarineTurn(void) { return winner_marine->Eval(); } bool Level::EndGenestealerTurn(void) { return winner_alien->Eval(); } bool Level::EndDrawTurn(void) { if (winner_draw!=NULL) return winner_draw->Eval(); else return false; } QString Level::getMessage(void) { if (turnmsg.isNull()) return ""; QString str; QTextStream res(&str,IO_WriteOnly); QDomElement current = turnmsg.firstChild().toElement(); QDomElement end = turnmsg.lastChild().toElement(); if (current.tagName() == "fixedmsg") res << current.attribute("msg"); else { LvlFct *f = LvlFct::Build(current, this); res << f->Eval(); } while (current != end) { current = current.nextSibling().toElement(); if (current.tagName() == "fixedmsg") res << current.attribute("msg").latin1(); else { LvlFct *f = LvlFct::Build(current, this); res << f->Eval(); } } return str; } void Level::getLevelInfo(QString &tit, QString &aut, QString &nar, QString &obj, QString &forc, QString &cond) { tit=title; aut=author; nar=narration; obj=objectives; forc=forces; cond=conditions; } void Level::CreateMap(GUICreature *guic, GUICommandLine *guicmd) { QString name; QDomElement current,zone; current = map.firstChild().toElement(); while(!current.isNull()) { name = current.tagName(); if(name == "floor") { LvlFloor* floor = LvlFloor::Build(current, this); floor->ApplyOnBoard(); if(!floor->is_ref) { delete floor; } } else if(name == "door") { LvlDoor* door = LvlDoor::Build(current, this); door->ApplyOnBoard(); if(!door->is_ref) { delete door; } } else if(name == "bulkhead") { LvlBulkhead* bulk = LvlBulkhead::Build(current, this); bulk->ApplyOnBoard(); if(!bulk->is_ref) { delete bulk; } } else if(name == "entry") { LvlEntry* entry = LvlEntry::Build(current, this); entry->ApplyOnBoard(); if(!entry->is_ref) { delete entry; } } else if (name == "exit") { LvlExit* ex = LvlExit::Build(current, this); ex->ApplyOnBoard(); if(!ex->is_ref) { delete ex; } } else if (name == "extensible") { LvlExtensible* ext = LvlExtensible::Build(current, this); ext->ApplyOnBoard(); if (!ext->is_ref) { delete ext; } } current = current.nextSibling().toElement(); } } LevelRef* Level::getRef(const QString& name) { std::map::iterator it = name_table.find(name); if(it != name_table.end()) { return (*it).second; } return NULL; } void Level::InitMarine() { if(!init_marine.empty()) StepTeam(init_marine); } void Level::InitGenestealer() { if(!init_alien.empty()) StepTeam(init_alien); } bool Level::isInitMarineNull() { return init_marine.empty(); } bool Level::isInitGenestealerNull() { return init_alien.empty(); } bool Level::BeginMarineTurn() { *CmdPts = 1+Rand::roll(6); if(!begin_marine.empty()) { StepTeam(begin_marine); return true; } return false; } bool Level::BeginGenestealerTurn() { if(!begin_alien.empty()) { StepTeam(begin_alien); return true; } return false; } void Level::setRef(LevelRef* ref) { name_table[ref->getName()] = ref; } void Level::clearRef(const QString& name) { name_table.erase(name); } void Level::StepTeam(list &steps) { list::iterator it; for(it = steps.begin() ; it != steps.end() ; it++) { (*it)->Execute(); } } void Level::killed(ObjectType type) { //LvlObjectType ltype = LevelObject::Type2LvlType(type); num_killed[type]++; } int Level::getNbKilled(ObjectType type) { return num_killed[type]; } set * Level::getObjectiveCases(void) { return &(objectiveCases); } bool Level::IsOnFire(int caseId) { if (man->flames.find(caseId)!= man->flames.end()) { return true; } return false; } Level::CreatureIterator Level::beginCreature() const { return man->liste.begin(); } Level::CreatureIterator Level::endCreature() const { return man->liste.end(); } const Creature* Level::operator[](int caseId) const { return man->liste[caseId]; } bool& Level::getCond(QString name) { return cond_table[name]; } int& Level::getValue(QString name) { return value_table[name]; } void LevelAction::BuildActions(list &lst, const QDomElement &node, Level *l) { QDomElement cur = node.firstChild().toElement(); while(!cur.isNull()) { lst.push_back(new LevelAction(cur, l)); cur = cur.nextSibling().toElement(); } } LevelAction::LevelAction(const QDomElement &base, Level *l) { level = l; todo = base.firstChild().toElement(); if(todo.tagName() == "cond") { cond = new LvlCond(todo, l); todo = todo.nextSibling().toElement(); } else { cond = NULL; } } void LevelAction::Execute() { QDomElement object, zonelist; if(cond != NULL) { if(!cond->Eval()) return; } if(todo.tagName() == "place") { list to_place; list::iterator it; LvlUserPutObject* o; QString number, str_alone; bool alone; number = todo.attribute("number", "1"); str_alone = todo.attribute("alone", "no"); alone = (str_alone == "yes") ? true : false; //cout << "Number of element to place : " << number << endl; int n = number.toInt(); object = todo.firstChild().toElement(); zonelist = object.nextSibling().toElement(); LvlZoneList *list = LvlZoneList::Build(zonelist, level); LvlObjectType type; type = LevelObject::Name2LvlType(object.tagName().latin1()); if(type == OT_NONE) { cout << "Error, child of \"todo\" must cannot be " << object.tagName() << endl; exit(10); } level->guiCmdLine->Write("You have to place :"); for(int i = 0 ; i < n ; i++) { switch(type) { case OT_TERMINATOR: { o = LvlBolter::Build(object,level); } break; case OT_FLAMER: { o = LvlFlamer::Build(object,level); } break; case OT_SERGEANT: { o = LvlSergeant::Build(object,level); } break; case OT_SQUAD: { o = LvlSquad::Build(object,level); } break; case OT_GENESTEALER: { //o = LvlGenestealer::Build(object,level); } break; case OT_BLIP: { o = LvlBlip::Build(object,level); ((LvlBlip*)o)->nbGen = *man->currBlipCard; man->currBlipCard++; if (man->currBlipCard == man->blipCards.end()) man->InitBlipCards(); } break; case OT_CAT: { o = LvlCAT::Build(object,level); } break; case OT_EXTENSIBLE: { o = LvlExtensible::Build(object,level, true); } break; /* case OT_OBJECT: { o = LvlObject::Build(object,level); } break;*/ default: cout << "Error, " << object.tagName() << " cannot be placed" << endl; exit(10); } level->guiCmdLine->Write(o->getDesc()); to_place.push_back(o); } set *cases = list->getCases(); LvlUserPutObject *unit; for(it = to_place.begin() ; it != to_place.end() ; it++) { if(type == OT_SQUAD) { LvlSquad *squad = dynamic_cast(*it); std::list::iterator unit_it; for(unit_it = squad->members.begin() ; unit_it != squad->members.end() ; unit_it++) { unit = *unit_it; level->guiCmdLine->Write("Place : " + (*unit_it)->getDesc()); unit->getParameters(cases); unit->CreateObject(); if(alone) { LvlZone* zone = list->containing(unit->position); if(zone != NULL) { set *to_remove = zone->getCases(); set::iterator it_rem; for(it_rem = to_remove->begin() ; it_rem != to_remove->end() ; it_rem++) { cases->erase(*it_rem); } } } else { if(!board->isEntryZoneCase(unit->position)) { cases->erase(unit->position); } } } } else { unit = *it; level->guiCmdLine->Write("Place : " + unit->getDesc()); unit->getParameters(cases); if (unit->position == -1) level->guiCmdLine->Write("there is no more room : the unit is discarded."); else { unit->CreateObject(); if(alone) { LvlZone* zone = list->containing(unit->position); if(zone != NULL) { set *to_remove = zone->getCases(); set::iterator it_rem; for(it_rem = to_remove->begin() ; it_rem != to_remove->end() ; it_rem++) { cases->erase(*it_rem); } } } else { if(!board->isEntryZoneCase(unit->position)) { cases->erase(unit->position); } } } } } delete cases; } else if(todo.tagName() == "destroy") { QString name; QDomElement node_ref = todo.firstChild().toElement(); name = node_ref.attribute("name"); LevelRef *ref = level->getRef(name); if(ref != NULL) { if(!ref->size() == 0) { LevelRef::iterator it; for(it = ref->begin() ; it != ref->end() ; it++) { LvlObjectType type = (*it)->Type(); if(type & OT_UNIT) { man->deleteCreature(dynamic_cast(*it)->unit); } } } } } else { cout << "Error, action must include \"place\" or \"destroy\" element" << endl; exit(3); } } void Level::removeAll(void) { num_killed.clear(); }