/*************************************************************************** * Copyright (C) 2004 by Raphael Langerhorst * * raphael-langerhorst@gmx.at * * * * Permission is hereby granted, free of charge, to any person obtaining * * a copy of this software and associated documentation files (the * * "Software"), to deal in the Software without restriction, including * * without limitation the rights to use, copy, modify, merge, publish, * * distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to * * the following conditions: * * * * The above copyright notice and this permission notice shall be * * included in all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.* * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * * OTHER DEALINGS IN THE SOFTWARE. * ***************************************************************************/ #include "GDynamicGeneratorAgent.h" #include "GSolarsystemCategory.h" #include "GPlanetCategory.h" #include #include #include #include #include #include #include using namespace GCS; using namespace GBE::Util; namespace GBE { // GDynamicGeneratorOctreeNode CLASS DECLARATION /** * \class GDynamicGeneratorOctreeNode GDynamicGeneratorAgent.cpp * \brief The generation space is structured with octrees * @author Raphael Langerhorst * Basically a node serves three purposes: * 1) It serves as starting point for generating all child nodes in the nodes area, * 2) it stores the seed value for the pseudo RNG to generate the child nodes and * 3) it stores the size of the area in which a child element would potentially be created. */ class GDynamicGeneratorOctreeNode { protected: /** * Each octreee node directly has access to its data in the * element data document. This makes fast updates easily possible. * In particular the element data can be kept in sync with the * octree with low overhead. */ QDomElement Data; /** * The RNG seed number for this node. * THIS is the actual data we need for child generation * at the node's position. */ unsigned long RandomSeed; /** * The position relative to the element centre. * @todo it should somehow be possible to respond to radius changes of the element's form. */ GCS::GVector3 Position; /** * Segment size of the enclosing cube. * This value is both required to generate positions and sizes of * child nodes as well as to be able to "randomly" place the * created child element somewhere in this cube. If this second * functionality were not given, a randomly created element content * (=children of one specific element) could look like a grid. * @see Position */ double SegmentSize; /** * Set to true when this node is an end node and the element for this * node has been created. * If this node is not an end node but all child nodes have been used * for creating elements then this is also set to true. * * This means that if Generated is set to true then the whole cubic * area this node covers had been generated. Thus it is possible to * reduce nodes and save memory. In the end, when the whole element * was created, there will be only one node left: the top node, * and its Generated attribute will be set to true. */ bool Generated; /** * Array of child nodes. Seen FROM positive y: * the first four are the first four (mathematical) quadrants * and the second four children are the last four quadrants. */ GDynamicGeneratorOctreeNode* Children[8]; public: /** * Constructor. */ GDynamicGeneratorOctreeNode(QDomElement data, unsigned long rand_seed, const GCS::GVector3& position, double segment_size); /** * Destructor, deletes all child nodes. */ ~GDynamicGeneratorOctreeNode(); /** * Reads the random seed from the element data. * The random seed is stored in the rs attribute. * Additionally all child nodes are initialized * from element data as well. * * This is the usual procedure when reinitializing * an element that is opend - or woken - from the * persistent storage. */ void initFromData(); /** * @Returns the seed for the RNG for this node. */ unsigned long getRandomSeed(); /** * @Returns the position of this node. */ GCS::GVector3 getPosition(); /** * @Returns the segment size of the enclosing node cube. */ double getSegmentSize(); /** * @Returns true when given position is inside the cube of this octree node. */ bool isPositionInsideNode(const GCS::GVector3& position); /** * @Returns true when given area is enclosed by the cube of this octree node. */ bool isAreaInsideNode(const GVector3& position, double radius); /** * @Returns true when given area touches the cube of this octree node. */ bool touchesArea(const GCS::GVector3& position, double radius); /** * Sets Generated to true. * @see Generated */ void setGenerated(); /** * @Returns true when an element was already created at this node position. * @see Generated */ bool isGenerated(); /** * @Returns a pointer list with all child nodes. */ QPtrList getChildNodes(); /** * @returns true when child nodes are created (so they are in memory). */ bool isNodeExpanded(); /** * Creates all 8 child nodes for this octree node. * * @param rng specifies the random number generator to use. * @return true when all child nodes have been successfully created. * @return false on out of memory errors. */ bool expandNode(Util::PseudoRNG& rng); /** * Deletes all child nodes. */ void reduceNode(); /** * Reduces all child nodes that have been used for element creation (isGenerated()==true) */ void reduceGenerated(bool recursive = true); /** * @Returns the child node of this octree node that covers given position. * @Returns NULL if position is completely outside of this node! * @todo write a test case for this. */ GDynamicGeneratorOctreeNode* getChildNodeForPosition(const GCS::GVector3& position); /** * @Returns all child nodes that cover part of given area; * @todo write a test case for this. */ QPtrList getChildNodesForArea(const GCS::GVector3& position, double radius); /** * @returns a deterministic random position based on RandomSeed inside this node's cube. */ GCS::GVector3 getRandomPositionInCube(Util::PseudoRNG& rng); }; // GDynamicGeneratorOctreeNode IMPLEMENTATION GDynamicGeneratorOctreeNode::GDynamicGeneratorOctreeNode(QDomElement data, unsigned long rand_seed, const GCS::GVector3& position, double segment_size) : Data(data), RandomSeed(rand_seed), Position(position), SegmentSize(segment_size), Generated(false) { Children[0]=NULL; Children[1]=NULL; Children[2]=NULL; Children[3]=NULL; Children[4]=NULL; Children[5]=NULL; Children[6]=NULL; Children[7]=NULL; if (data.attribute("done","0")=="1") { setGenerated(); } } GDynamicGeneratorOctreeNode::~GDynamicGeneratorOctreeNode() { if (this->isNodeExpanded()) this->reduceNode(); } unsigned long GDynamicGeneratorOctreeNode::getRandomSeed() { return RandomSeed; } GVector3 GDynamicGeneratorOctreeNode::getPosition() { return Position; } double GDynamicGeneratorOctreeNode::getSegmentSize() { return SegmentSize; } bool GDynamicGeneratorOctreeNode::isPositionInsideNode(const GVector3& position) { double half_seg_size = this->SegmentSize*0.5; if (position.x >= this->Position.x - half_seg_size && position.x <= this->Position.x + half_seg_size && position.y >= this->Position.y - half_seg_size && position.y <= this->Position.y + half_seg_size && position.z >= this->Position.z - half_seg_size && position.z <= this->Position.z + half_seg_size) return true; else return false; } bool GDynamicGeneratorOctreeNode::isAreaInsideNode(const GVector3& position, double radius) { double half_seg_size = this->SegmentSize*0.5; if (position.x + radius >= this->Position.x - half_seg_size && position.x - radius <= this->Position.x + half_seg_size && position.y + radius >= this->Position.y - half_seg_size && position.y - radius <= this->Position.y + half_seg_size && position.z + radius >= this->Position.z - half_seg_size && position.z - radius <= this->Position.z + half_seg_size) return true; else return false; } bool GDynamicGeneratorOctreeNode::touchesArea(const GVector3& position, double radius) { if (this->isPositionInsideNode(position)) return true; //@todo this testing testes spheres!!! but a node is a cube! if (this->Position.distanceTo(position) < radius + this->SegmentSize*0.8) return true; return false; // // // if (position.x + radius >= this->Position.x - half_seg_size && position.x - radius <= this->Position.x + half_seg_size || // position.y + radius >= this->Position.y - half_seg_size && position.y - radius <= this->Position.y + half_seg_size || // position.z + radius >= this->Position.z - half_seg_size && position.z - radius <= this->Position.z + half_seg_size) // return true; // // else // return false; } void GDynamicGeneratorOctreeNode::setGenerated() { Data.setAttribute("done","1"); this->Generated = true; } bool GDynamicGeneratorOctreeNode::isGenerated() { return Generated; } QPtrList GDynamicGeneratorOctreeNode::getChildNodes() { QPtrList list; for (short i = 0; i< 8; i++) { list.append(Children[i]); } return list; } bool GDynamicGeneratorOctreeNode::isNodeExpanded() { // if (Children[0]==NULL) // return false; // else // return true; return Children[0] ? true : false; } bool GDynamicGeneratorOctreeNode::expandNode(PseudoRNG& rng) { if (isGenerated()) { qWarning("Node is expanding ALTOUGH it is already generated! This is madness! - Please contact the developers!"); } if (!isNodeExpanded()) { //@todo 1) set RNG, 2) create 1 through 8 with correct positions and segment sizes rng.setNumber(this->RandomSeed); double child_segment_size = this->SegmentSize*0.5; GVector3 child_pos_array[8]; GVector3 child_pos(this->Position.x + child_segment_size*0.5, this->Position.y + child_segment_size*0.5, this->Position.z + child_segment_size*0.5); // BEGIN TOPLEVEL NODES child_pos_array[0] = child_pos; child_pos.x -= child_segment_size; child_pos_array[1] = child_pos; child_pos.z -= child_segment_size; child_pos_array[2] = child_pos; child_pos.x += child_segment_size; child_pos_array[3] = child_pos; // END TOPLEVEL NODES // BEGIN BOTTOMLEVEL NODES child_pos.y -= child_segment_size; child_pos.z += child_segment_size; child_pos_array[4] = child_pos; child_pos.x -= child_segment_size; child_pos_array[5] = child_pos; child_pos.z -= child_segment_size; child_pos_array[6] = child_pos; child_pos.x += child_segment_size; child_pos_array[7] = child_pos; // END BOTTOMLEVEL NODES QDomElement gs[8]; if (Data.hasChildNodes()) { QDomNodeList list = Data.childNodes(); if (list.count()==8) { int i; for (i = 0; i<8; i++) { QDomElement e = list.item(i).toElement(); Q_ASSERT(!gs[i].isNull()); bool ok; int n = e.attribute("n",QString::number(i)).toInt(&ok); if (!ok) n = i; gs[n] = e; } //let's do a sanity check bool consistent = true; for (i = 0; i<8; i++) { if (gs[i].isNull()) { qWarning("INCONSISTENCY DETECTED: found existing octree node data, but not all child elements could be found! Replacing existing node data."); consistent = false; } } if (!consistent) { for (i = 0; i<8; i++) { if (!gs[i].isNull()) { gs[i].clear(); } } } } else { qWarning(QString("INCONSISTENCY DETECTED: octree nodes for world generation must either have 0 or 8 child nodes! We have %1. Replacing existing nodes").arg(list.count())); while(Data.hasChildNodes()) { qWarning("clearing node"); Data.firstChild().clear(); } } } if (!Data.hasChildNodes()) { //if there is nothing we create the child nodes now. for (int i = 0; i<8; i++) { QDomElement e = Data.ownerDocument().createElement("gs"); Data.appendChild(e); e.setAttribute("n",QString::number(i)); int done = this->isGenerated() ? 1 : 0; e.setAttribute("done",QString::number(done)); // in case we're expanding although we are already generated... gs[i] = e; } } for (short child_number = 0; child_number < 8; child_number++) { unsigned long child_random_seed = rng.getNumberInt(); Children[child_number] = new GDynamicGeneratorOctreeNode(gs[child_number],child_random_seed,child_pos_array[child_number],child_segment_size); if (Children[child_number]==NULL) { for (short i=0; ireduceGenerated(recursive); } } for (short i = 0; i<8; i++) { if (Children[i]->isGenerated() == false) { all_childs_generated = false; } } if (all_childs_generated) { this->reduceNode(); this->setGenerated(); } } } GDynamicGeneratorOctreeNode* GDynamicGeneratorOctreeNode::getChildNodeForPosition(const GVector3& position) { if (!this->isNodeExpanded()) { qWarning("Node not expanded, can't return children"); return NULL; } if (this->isPositionInsideNode(position)) { for (short i = 0; i<8; i++) { if (Children[i]->isPositionInsideNode(position)) return Children[i]; } qWarning("Position was inside node but no appropriate child node found!"); return NULL; } else return NULL; } QPtrList GDynamicGeneratorOctreeNode::getChildNodesForArea(const GVector3& position, double radius) { QPtrList list; if (!this->isNodeExpanded()) { qWarning("Node is not expanded, can't get children for area!!"); return list; } // qDebug("Getting child nodes in area"); for (short i = 0; i<8; i++) { if (Children[i]->touchesArea(position,radius)) { // qDebug("Found child in area"); list.append(Children[i]); } } return list; } GVector3 GDynamicGeneratorOctreeNode::getRandomPositionInCube(PseudoRNG& rng) { rng.setNumber(this->RandomSeed+10); //pick some other seed value than for child nodes. GVector3 pos(this->Position.x + (rng.getNumberDouble()-0.5)*this->SegmentSize, this->Position.y + (rng.getNumberDouble()-0.5)*this->SegmentSize, this->Position.z + (rng.getNumberDouble()-0.5)*this->SegmentSize); return pos; } // GDynamicGeneratorAgent IMPLEMENTATION GDynamicGeneratorAgent::GDynamicGeneratorAgent() : EnergyFractionChildCreation(0), EnergyFractionDirtyFlag(true), DataLoaded(FALSE), RandomSeed(1000), Categories(), CreationTime(QDateTime::currentDateTime()), Density(5), CreateAllAtOnce(false), TopNode(NULL) { this->Categories.setAutoDelete(true); } GDynamicGeneratorAgent::~GDynamicGeneratorAgent() { if (TopNode) { delete TopNode; //deletes all child nodes recursively. TopNode=NULL; } } void GDynamicGeneratorAgent::generateInArea(const GCS::GVector3& position, double radius) { //implement recursive search until smallest node is found that still encompasses the whole area - then generate this node (might STILL hold childnodes that do the actual generation!!) // qDebug("Generating child elements in area"); GForm* f = requestForm(); if (f->getRadiusMax() < position.length() - radius) { // qDebug("Area completely outside of this element"); return; //if target area is completely outside of this element's form } //generate top node if it does not exist if (TopNode == NULL) { // qDebug("TopNode did not exist, generating top node..."); RNG.setNumber(this->RandomSeed); unsigned long rs = RNG.getNumberInt(); bool ok; QDomElement data = xmlGetElement("/worldgeneration/gs",ok); Q_ASSERT(!data.isNull()); if (!data.hasAttribute("n")) data.setAttribute("n","0"); if (!data.hasAttribute("done")) data.setAttribute("done","0"); if (data.hasAttribute("rs")) { bool ok; unsigned long rs_ = data.attribute("rs",QString::number(rs)).toULong(&ok); if (ok) rs = rs_; } else { data.setAttribute("rs",QString::number(rs)); } TopNode = new GDynamicGeneratorOctreeNode(data,rs,GVector3(0,0,0),f->getRadiusMax()*2); if (TopNode==NULL) { qWarning("not enough memory to generate top node"); return; } TopNode->expandNode(RNG); } if (TopNode->isGenerated()) { // qDebug("TopNode is generated, thus the whole element is already generated"); return; //Whole element is generated. } //here we store the current depth of the tree, when depth == density() //then we are at the lowest level - here actual element generation happens short unsigned depth = 0; //start recursive search until the smallest node is found that still covers the whole given area recursiveGeneration(TopNode,position,radius,depth); } void GDynamicGeneratorAgent::recursiveGeneration(GDynamicGeneratorOctreeNode* node, const GCS::GVector3& position, double radius, short unsigned depth) { // qDebug("Recursive generation, depth = " + QString::number(depth)); if (node->isGenerated()) //if node is already generated there is nothing to do, note that we currently double check this { // qDebug("Node is generated, returning"); return; } if (depth>=this->Density) { //@todo determine category - if a category is determined (and not empty space) then we generate with that category at this node //mark node as already being used to generate elements node->setGenerated(); RNG.setNumber(node->getRandomSeed()); double random_number = RNG.getNumberDouble(); // qDebug("Double number generated: " + QString::number(random_number)); double range_sum = 0; for (GDynamicGeneratorCategory* category = Categories.first(); category != NULL; category = Categories.next()) { range_sum += category->getRange(); if (range_sum > 1) qWarning("Range sum bigger than 1: " + QString::number(range_sum)); if (range_sum >= random_number) //category HIT, we generate a child element!! { // qDebug("Category found, creating element"); GVector3 position = node->getRandomPositionInCube(RNG); GElement* element = this->createElement(*category,position); Q_CHECK_PTR(element); if (element) { emit this->childElementCreated(element); element->executeElement(this->CreationTime.secsTo(QDateTime::currentDateTime())); } return; } } //if we get here then no generation happened //that means no category applied as the sum of category ranges //did not exceed the generated random number. } else { if (!node->isNodeExpanded()) { node->expandNode(RNG); } if (node->isNodeExpanded()) //we check again in case node can't be expanded (memory!!) { unsigned short new_depth = depth+1; QPtrList nodes = node->getChildNodesForArea(position, radius); for (GDynamicGeneratorOctreeNode* current = nodes.first(); current != NULL; current = current = nodes.next()) { if (!current->isGenerated()) { recursiveGeneration(current,position,radius,new_depth); //reduce generated nodes, saves lots of memory. //this means that if a whole (higher level) node had been //completely generated then all child nodes can be removed and this higher node can be set to generated //does not need to be recursive, as we call it at each level anyway... current->reduceGenerated(false); } } } } } GElement* GDynamicGeneratorAgent::createElement(const GDynamicGeneratorCategory& category, const GCS::GVector3& position) { //hmm.... how to get deterministic energy? I think THIS will be _different_ for each generation... // at least if the element is not generated in "all at once" mode. //@todo use a member variable for storing the fraction of energy we take, this takes too much cpu time if (this->EnergyFractionDirtyFlag) { double range_sum = 0; for (GDynamicGeneratorCategory* category = Categories.first(); category != NULL; category = Categories.next()) { range_sum += category->getRange(); } if (range_sum <= 0) { qWarning("Generating child elements will not work: range sum is 0 or less"); this->EnergyFractionChildCreation=0; } else { this->EnergyFractionChildCreation = 1/(std::pow((double)8,(double)this->Density)*range_sum); } this->EnergyFractionDirtyFlag=false; } GEnergy* e = new GEnergy(requestEnergy()->take(this->EnergyFractionChildCreation)); double level_shift = (e->sigma()*(RNG.getNumberDouble()-0.5)); double level_shift_positive = level_shift; if (level_shift < 0) level_shift_positive *= -1; e->set(e->level()+level_shift,e->amount(),e->sigma()-level_shift_positive); //@todo this energy change *should* be reflected in the original energy, // but if it's used for all child elements it probably doesn't matter much Q_CHECK_PTR(e); if (e==NULL) return NULL; //@todo check if there is already a child element that covers this area if chosen category does not allow overlapping //@todo how to set parent ID for created element? Additional parameter? GForm* form = category.createForm(*e,position,RNG); Q_CHECK_PTR(form); if (form==NULL) { delete e; return NULL; } GObject* object = new GObject(e,form,getElementID(),GElementID::getFreeID(),GElementID(0),new QDomDocument(),requestObject()->getWorldData()); Q_CHECK_PTR(object); GElement* element = new GElement(object); Q_CHECK_PTR(element); category.postProcess(element,RNG); // DO NOT(!!!) execute agents in postprocessing return element; } void GDynamicGeneratorAgent::loadData(bool force) { if (force) this->DataLoaded = false; if (this->DataLoaded) return; bool ok; unsigned long rs = xmlGetULongInteger("/worldgeneration/randomseed",ok); if (ok) this->RandomSeed = rs; else { qWarning("Random Seed was not found!"); initRandomSeed(RandomSeed); } QDateTime time; time.fromString(xmlGetElement("/worldgeneration/creationtime",ok).text()); if (ok) this->CreationTime = time; else { qWarning("Creation Time was not found!"); initCreationTime(CreationTime); } int density = xmlGetInteger("/worldgeneration/density",ok); if (density < 0) { qWarning("Density < 0; setting density to 3"); density = 3; ok=false; } if (ok) this->Density = density; else { qWarning("Density was not found!"); initDensity(Density); } int all_at_once = xmlGetInteger("/worldgeneration/allatonce",ok); if (ok) { if (all_at_once == 1) this->CreateAllAtOnce = true; else this->CreateAllAtOnce = false; } else { qWarning("CreateAllAtOnce was not found!"); initAllAtOnce(this->CreateAllAtOnce); } bool existed; QDomElement categories_element = xmlGetElement("/worldgeneration/categories",existed); if (!categories_element.hasChildNodes()) { qWarning("No categories found in element data, this agent is quite useless without these."); if (!this->Categories.isEmpty()) { } } else { this->Categories.clear(); //autodelete = true QDomNodeList categories = categories_element.childNodes(); for (int i=0; iCategories.append(c_object); } } } } this->DataLoaded = true; } void GDynamicGeneratorAgent::receiveInfluence(const GCS::GElementInfluence& influence) { if (this->requestObject()->hasForm()) { if (influence.source() == this->requestObject()->getParent() || influence.source() == this->getElementID()) { // qDebug("Recieved influence from myself or parent, not creating content"); return; //don't react to parent influence or own influence, since that would automatically create ALL the content } // qDebug("Generating element content"); if (!this->DataLoaded) loadData(); //make sure we've all data initialized //the criteria on which we create child elements is based on whether the //influence comes from an external source (= a sibling and neither the parent, //nor any of the existing children) const GElement* sender = this->requestObject()->getWorldData()->read(influence.source()); Q_CHECK_PTR(sender); if (sender==NULL) return; const GObject* sender_object = sender->getObject(); Q_CHECK_PTR(sender_object); if (!sender_object->hasForm()) return; const GForm* sender_form = sender_object->getForm(); Q_CHECK_PTR(sender_form); //@todo continue from here // GActiveElementInfluence& active_element_influence = dynamic_cast(influence); // qDebug("Received element influence, checking if additional elements should be created."); GVector3 pos = sender_form->Position; GVector3 rot = sender_form->Rotation; //@todo add a factor to element data double arearadius = sender_form->getRadiusMax(); double detail_arearadius = sender_form->getRadiusMin(); // Generating contents for active elements INSIDE this element. This would // mean for example to see the planets within a solar system when the // source element is inside the solar system itself. // Note that for this generation the normal area size is used as // the source element moves inside this element. if (sender_object->getParent() == this->getElementID()) { // qDebug("Influence source is a child element"); //no transformation needed generateInArea(pos,arearadius); } // Generating contents for active elements OUTSIDE this element. This would // mean for example to see the planets within a solar system even when being // far away from the solar system itself. // Note that for this generation the detail area radius is used as the source // element is not really inside this element. But it can be useful to see // element content if the element is getting nearer. else if (sender_object->getParent() == this->requestObject()->getParent() /* && active_element_influence.parent() != this->getElementID()*/ ) { // qDebug("Influence source on the same hierarchy level"); GForm* f = requestForm(); //transformation is just influence source position - this position, and rotation!! pos.sub(f->Position); rot.sub(f->Rotation); pos.turnAroundAxis(GVector3(1,0,0),rot.x); pos.turnAroundAxis(GVector3(0,1,0),rot.y); pos.turnAroundAxis(GVector3(0,0,1),rot.x); generateInArea(pos,detail_arearadius); } } } //BEGIN XML Element Data initialization /* * * number * datetime * double value * bool * * * * double value * double value * double value * bool * * * double value * double value * double value * bool * * * */ void GDynamicGeneratorAgent::initRandomSeed(unsigned long random_seed) { this->RandomSeed = random_seed; bool existed; xmlSetULongInteger("/worldgeneration/randomseed",random_seed,existed); } void GDynamicGeneratorAgent::initCreationTime(QDateTime creation_time) { this->CreationTime = creation_time; bool existed; QDomElement e = xmlGetElement("/worldgeneration/creationtime",existed); if (e.isNull()) { qWarning("Can't set creation time because element is NULL!"); return; } xmlRemoveNodeContent(e); e.appendChild(e.ownerDocument().createTextNode(creation_time.toString(Qt::ISODate))); } void GDynamicGeneratorAgent::initDensity(unsigned short density) { this->Density = density; this->EnergyFractionDirtyFlag = true; bool existed; xmlSetInteger("/worldgeneration/density",density,existed); } void GDynamicGeneratorAgent::initAllAtOnce(bool create_all_at_once) { this->CreateAllAtOnce = create_all_at_once; bool existed; int n = create_all_at_once==true ? 1 : 0; // qDebug(QString("all at once creation is set to %1").arg(n)); xmlSetInteger("/worldgeneration/allatonce",n,existed); } //@todo this is currently a bit... well, we should think of some kind of factory or plugin system for categories void GDynamicGeneratorAgent::initCategory(GDynamicGeneratorCategory* category) { this->Categories.append(category); this->EnergyFractionDirtyFlag = true; //recalculate energy fraction for child creation QString classname = "category"; try { GSolarsystemCategory* c = dynamic_cast(category); classname = "solarsystem"; } catch(std::exception e) { try { GPlanetCategory* c = dynamic_cast(category); classname = "planet"; } catch(std::exception e) { } } if (classname=="category") { qWarning("unknown category, can't add it to element data"); return; } bool existed; xmlSetDouble(QString("/worldgeneration/") + classname + "/range",category->getRange(),existed); //radiusmin, radiusmax, allowoverlap are not used for now... solar systems and planets both don't need them } //END XML Element Data initialization };