Integrating WorldGeneration (wip)

Fixed fillerSet dependencies impacting UndergroundBiomes fillerSets
Integrated XMLpreprocessor
Refactored mod requirement handling
Refactored RandomCollection
Fixed OrbShell name overwriting Orb name
Updated asteroid attributes:
- minCoreSize -> minCoreCount
- maxCoreSize -> maxCoreCount
- coreRad -> relativeCoreRadius
Updated default structures for stars and asteroids
This commit is contained in:
LemADEC 2016-01-17 23:07:14 +01:00
parent fa48d71699
commit 9fe205ba08
18 changed files with 837 additions and 726 deletions

View file

@ -0,0 +1,141 @@
package cr0s.warpdrive.config;
import java.util.ArrayList;
import java.util.NavigableMap;
import java.util.Random;
import java.util.TreeMap;
import org.w3c.dom.Element;
import cr0s.warpdrive.WarpDrive;
/**
* Collection of elements with weights. Helps to select element with controlled odds.
*
* @author ncrashed
*
* @param <E>
*/
public class RandomCollection<E extends XmlRepresentable> {
private final NavigableMap<Double, E> weightMap = new TreeMap<Double, E>();
private double totalWeight = 0;
private final NavigableMap<Double, E> ratioMap = new TreeMap<Double, E>();
private double totalRatio = 0;
private final ArrayList<E> list = new ArrayList<E>();
/**
* Add new object and its weight.
*
* @param weight
* Used for random pick. The higher the value is relatively to others, the higher odds of choosing the object.
* @param object
* Object to add
*/
public void addWeight(double weight, E object) {
if (weight <= 0) {
WarpDrive.logger.warn("Weight is negative or zero, skipping " + object);
return;
}
totalWeight += weight;
weightMap.put(totalWeight, object);
list.add(object);
}
/**
* Add new object and its ratio. Warning: if total ratio goes higher than 1.0, element won't be added to collection.
*
* @param ratio
* Chance of random pick in range (0, 1.0]. In contrast to weights, ratio is fixed and chances don't change if you add more elements.
* @param object
* Object to add
*/
public void addRatio(double ratio, E object) {
if (ratio <= 0 || ratio >= 1.0) {
WarpDrive.logger.warn("Ratio isn't in (0, 1.0] bounds, skipping " + object);
return;
}
if (totalRatio + ratio > 1.0) {
WarpDrive.logger.warn("Total ratio is greater than 1.0, skipping " + object);
return;
}
totalRatio += ratio;
ratioMap.put(totalRatio, object);
list.add(object);
}
/**
* Get a random object according weights and ratios
*
* @param random
* @return Random object or null if there is no objects to pick.
*/
public E getRandomEntry(Random random) {
double value = random.nextDouble();
if (value < totalRatio) { // hit ratio part of values
return ratioMap.ceilingEntry(value).getValue();
} else { // hit dynamic part of values, weighted ones
double weight = (value - totalRatio) * totalWeight;
return weightMap.ceilingEntry(weight).getValue();
}
}
/**
* Get a specific object through its name
*
* @param name Exact name of the object
* @return Named object or null if there is no object with that name
*/
public E getNamedEntry(final String name) {
for(E object : list) {
if (object.getName().equals(name)) {
return object;
}
}
return null;
}
/**
* @return All registered objects
*/
public ArrayList<E> elements() {
return list;
}
/**
* Loads object from given XML element and parses configurations for weighted pick.
*
* @param object
* Object to load into
* @param element
* Element of an XML file
* @throws InvalidXmlException
*/
public void loadFromXML(E object, Element element) throws InvalidXmlException {
object.loadFromXmlElement(element);
try {
String ratioStr = element.getAttribute("ratio");
if (!ratioStr.isEmpty()) {
double ratio = Double.parseDouble(ratioStr);
addRatio(ratio, object);
} else { // try weight
try {
int weight = 1;
String stringWeight = element.getAttribute("weight");
if (!stringWeight.isEmpty()) {
weight = Integer.parseInt(stringWeight);
weight = Math.max(1, weight);
}
addWeight(weight, object);
} catch (NumberFormatException exceptionWeight) {
throw new InvalidXmlException("Weight must be an integer!");
}
}
} catch (NumberFormatException exceptionRatio) {
throw new InvalidXmlException("Ratio must be double!");
}
}
}

View file

@ -1,9 +1,16 @@
package cr0s.warpdrive.config;
import java.io.File;
import java.util.ArrayList;
import java.util.Map.Entry;
import java.util.TreeMap;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
@ -13,44 +20,48 @@ import org.w3c.dom.NodeList;
import cpw.mods.fml.common.Loader;
import cr0s.warpdrive.WarpDrive;
import javax.xml.transform.dom.DOMSource;
public class XmlPreprocessor {
static boolean enableOuput = false;
static int outputCount = 1;
/**
* Will check the given element for a mod attribute and return a string of all the ones that are not loaded, separated by commas
* Check the given element for a mod attribute and return a string of all the ones that are not loaded, separated by commas
*
* @param element
* Element to check
* @return A string, which is empty if all the mods are loaded.
* @throws InvalidXmlException
*/
public static ModCheckResults checkModRequirements(Element element) {
public static String checkModRequirements(Element element) {
ModCheckResults modErrors = new ModCheckResults();
ModCheckResults modCheckResults = new ModCheckResults();
for (String mod : element.getAttribute("mods").split(",")) {
//TODO: add version check
if (mod.isEmpty())
if (mod.isEmpty()) {
continue;
}
if (mod.startsWith("!")) {
if (Loader.isModLoaded(mod.substring(1))) {
modCheckResults.addMod(mod, "loaded");
}
if (Loader.isModLoaded(mod.substring(1)))
modErrors.addMod(mod, "loaded");
} else if (!Loader.isModLoaded(mod))
modErrors.addMod(mod, "not loaded");
} else if (!Loader.isModLoaded(mod)) {
modCheckResults.addMod(mod, "not loaded");
}
}
return modErrors;
return modCheckResults.toString();
}
/**
* Goes through every child node of the given node, and if it is an element and fails checkModRequirements() it is removed
* Goes through every child node of the given node, and remove elements failing to checkModRequirements()
*
* @param base
* @throws InvalidXmlException
@ -64,11 +75,11 @@ public class XmlPreprocessor {
if (child instanceof Element) {
Element elementChild = (Element) child;
ModCheckResults res = checkModRequirements(elementChild);
if (!res.isEmpty()) {
String result = checkModRequirements(elementChild);
if (!result.isEmpty()) {
WarpDrive.logger.info("Skipping " + base.getNodeName() + "/" + elementChild.getNodeName()
+ " " + elementChild.getAttribute("group") + elementChild.getAttribute("name") + elementChild.getAttribute("block")
+ " due to " + res);
+ " due to " + result);
base.removeChild(child);
} else {
doModReqSanitation(child);
@ -77,163 +88,171 @@ public class XmlPreprocessor {
}
}
/**
* Develop 'for' elements
*
* @param base
* @throws InvalidXmlException
*/
public static void doLogicPreprocessing(Node root) throws InvalidXmlException {
// process child first
NodeList children = root.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
doLogicPreprocessing(children.item(i));
}
if (root.getNodeType() == Node.ELEMENT_NODE && ((Element) root).getTagName().equalsIgnoreCase("for")) {
// only process 'for' elements
if (root.getNodeType() != Node.ELEMENT_NODE || !((Element) root).getTagName().equalsIgnoreCase("for")) {
return;
}
Element elementFor = (Element) root;
// get variable name
String variableName = elementFor.getAttribute("variable");
if(variableName.isEmpty()) {
throw new InvalidXmlException("A for tag must include a variable attribute!");
}
// 'in' takes precedence over 'from' attribute
if (elementFor.hasAttribute("in")) {
String[] inOptions = elementFor.getAttribute("in").split(",");
Element forTag = (Element) root;
String varName = forTag.getAttribute("variable");
if(varName.isEmpty())
throw new InvalidXmlException("A for tag must include a variable attribute!");
//In supersedes from
if (forTag.hasAttribute("in")) {
String inOptions = forTag.getAttribute("in");
for(String input : inOptions.split(",")) {
NodeList allChildren = root.getChildNodes();
for(int chI = 0; chI < allChildren.getLength(); chI ++) {
Node copy = getCopyVarReplace(allChildren.item(chI), varName, input);
root.getParentNode().appendChild(copy);
}
// copy children with replaced variable
for(String variableValue : inOptions) {
NodeList allChildren = root.getChildNodes();
for(int childIndex = 0; childIndex < allChildren.getLength(); childIndex ++) {
Node copy = copyNodeAndReplaceVariable(allChildren.item(childIndex), variableName, variableValue);
root.getParentNode().appendChild(copy);
}
} else {
String fromStr = forTag.getAttribute("from");
String toStr = forTag.getAttribute("to");
}
} else {
String stringFrom = elementFor.getAttribute("from");
String stringTo = elementFor.getAttribute("to");
if (stringTo.isEmpty() || stringFrom.isEmpty()) {
throw new InvalidXmlException("For element with no 'in' attribute requires both 'from' and 'to' attributes! " + variableName);
}
int intFrom;
int intTo;
try {
intFrom = Integer.parseInt(stringFrom);
intTo = Integer.parseInt(stringTo);
} catch (NumberFormatException exception) {
throw new InvalidXmlException(exception);
}
// copy children with replaced variable
for (int variableValue = intFrom; variableValue <= intTo; variableValue++) {
NodeList allChildren = root.getChildNodes();
for (int childIndex = 0; childIndex < allChildren.getLength(); childIndex++) {
Node copy = copyNodeAndReplaceVariable(allChildren.item(childIndex), variableName, "" + variableValue);
root.getParentNode().appendChild(copy);
}
}
}
//Remove the old node
root.getParentNode().removeChild(root);
if (enableOuput) {
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Result output = new StreamResult(new File("output" + outputCount + ".xml"));
Source input = new DOMSource(root.getOwnerDocument());
if (toStr.isEmpty() || fromStr.isEmpty())
throw new InvalidXmlException("If a for doesnt have an in attr, it must have a from and to!");
int from, to;
try {
from = Integer.parseInt(fromStr);
to = Integer.parseInt(toStr);
} catch (NumberFormatException e) {
throw new InvalidXmlException(e);
}
for (; from <= to; from++) {
NodeList allChildren = root.getChildNodes();
for (int chI = 0; chI < allChildren.getLength(); chI++) {
Node copy = getCopyVarReplace(allChildren.item(chI), varName, "" + from);
root.getParentNode().appendChild(copy);
}
}
transformer.transform(input, output);
outputCount++;
} catch (Exception exception) {
exception.printStackTrace();
}
//Remove the old node
root.getParentNode().removeChild(root);
}
}
private static Node getCopyVarReplace(Node toCopy, String varName, String value) {
Node copy = toCopy.cloneNode(true);
replaceVar(copy, varName, value);
private static Node copyNodeAndReplaceVariable(Node nodeOriginal, String variableName, String variableValue) {
Node nodeCopy = nodeOriginal.cloneNode(true);
replaceVariable(nodeCopy, "%" + variableName + "%", variableValue);
return copy;
return nodeCopy;
}
private static void replaceVar(Node root, String varName, String value) {
ArrayList<String> toRemove = new ArrayList<String>();
ArrayList<Attr> toAdd = new ArrayList<Attr>();
if (root.getNodeType() == Node.ELEMENT_NODE) {
private static void replaceVariable(Node node, String keyword, String value) {
ArrayList<String> nameToRemove = new ArrayList<String>();
ArrayList<Attr> attrToAdd = new ArrayList<Attr>();
// process element's attributes first
if (node.getNodeType() == Node.ELEMENT_NODE) {
//First replace attributes
NamedNodeMap attrs = root.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
Attr attr = (Attr) attrs.item(i);
String name = attr.getName();
String newName = name.replace("%" + varName + "%", value);
if (name.equals(newName)) {
//Easy, just adjust value
attr.setValue(attr.getValue().replace("%" + varName + "%", value));
} else {
//The name changed
toRemove.add(name);
Attr newAttr = attr.getOwnerDocument().createAttribute(newName);
newAttr.setValue(attr.getValue().replace("%" + varName + "%", value));
toAdd.add(newAttr);
// compute the changes
NamedNodeMap attrs = node.getAttributes();
for (int indexAttr = 0; indexAttr < attrs.getLength(); indexAttr++) {
Attr oldAttr = (Attr) attrs.item(indexAttr);
String oldName = oldAttr.getName();
String newName = oldName.replace(keyword, value);
if (oldName.equals(newName)) {// same name, just adjust the value
oldAttr.setValue(oldAttr.getValue().replace(keyword, value));
} else {// different name, needs to defer the add/remove
nameToRemove.add(oldName);
Attr newAttr = oldAttr.getOwnerDocument().createAttribute(newName);
newAttr.setValue(oldAttr.getValue().replace(keyword, value));
attrToAdd.add(newAttr);
}
}
//Now do the adds and removals
for (String attr : toRemove)
// then apply them
for (String attr : nameToRemove) {
attrs.removeNamedItem(attr);
}
for (Attr attr : toAdd)
for (Attr attr : attrToAdd) {
attrs.setNamedItem(attr);
}
//Now that Attributes are done, go through all of the children
NodeList children = root.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
switch (child.getNodeType()) {
case Node.ELEMENT_NODE://Recurse on the element
replaceVar(child, varName, value);
break;
case Node.TEXT_NODE:
child.setTextContent(child.getTextContent().replace("%" + varName + "%", value));
break;
}
}
// attributes are done, moving through child elements now
NodeList children = node.getChildNodes();
for (int childIndex = 0; childIndex < children.getLength(); childIndex++) {
Node nodeChild = children.item(childIndex);
switch (nodeChild.getNodeType()) {
case Node.ELEMENT_NODE: // recurse through elements
replaceVariable(nodeChild, keyword, value);
break;
case Node.TEXT_NODE: // replace text in place
nodeChild.setTextContent(nodeChild.getTextContent().replace(keyword, value));
break;
default: // ignore others
// no operation
break;
}
}
}
public static class ModCheckResults {
private TreeMap<String, String> mods;
private TreeMap<String, String> modResults;
public ModCheckResults() {
mods = new TreeMap<String, String>();
modResults = new TreeMap<String, String>();
}
public void addMod(String name, String error) {
mods.put(name, error);
modResults.put(name, error);
}
public boolean isEmpty() {
return mods.isEmpty();
return modResults.isEmpty();
}
@Override
public String toString() {
String string = (mods.size() > 1 ? "{" : "");
String string = (modResults.size() > 1 ? "{" : "");
boolean isFirst = true;
for (Entry<String, String> entry : mods.entrySet()) {
for (Entry<String, String> entry : modResults.entrySet()) {
if (isFirst) {
isFirst = false;
} else {
@ -242,7 +261,7 @@ public class XmlPreprocessor {
string += entry.getKey() + ": " + entry.getValue();
}
return string + (mods.size() > 1 ? "}" : "");
return string + (modResults.size() > 1 ? "}" : "");
}
}
}

View file

@ -4,9 +4,9 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element;
public interface XmlRepresentable {
public void loadFromXmlElement(Element e) throws InvalidXmlException;
public void saveToXmlElement(Element e, Document d) throws InvalidXmlException;
public String getName();
public void loadFromXmlElement(Element element) throws InvalidXmlException;
public void saveToXmlElement(Element element, Document document) throws InvalidXmlException;
}

View file

@ -15,16 +15,17 @@ import org.xml.sax.SAXException;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.InvalidXmlException;
import cr0s.warpdrive.config.RandomCollection;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.config.XmlPreprocessor;
import cr0s.warpdrive.config.XmlPreprocessor.ModCheckResults;
public class FillerManager {
private static TreeMap<String, FillerSet> fillerSets = new TreeMap<String, FillerSet>();
private static TreeMap<String, FillerSet> fillerSetsByName = new TreeMap<String, FillerSet>();
private static TreeMap<String, RandomCollection<FillerSet>> fillerSetsByGroup = new TreeMap<String, RandomCollection<FillerSet>>();
// Stores extra dependency information
static TreeMap<FillerSet, ArrayList<String>> fillerSetsAdditions = new TreeMap<FillerSet, ArrayList<String>>();
static TreeMap<FillerSet, ArrayList<String>> fillerSetsDependencies = new TreeMap<FillerSet, ArrayList<String>>();
/* TODO dead code?
// FillerSets that are guaranteed to exist
@ -52,7 +53,6 @@ public class FillerManager {
for(File file : files) {
try {
WarpDrive.logger.info("Loading filler data file " + file.getName() + "...");
loadXmlFillerFile(file);
} catch (Exception exception) {
WarpDrive.logger.error("Error loading filler data file " + file.getName() + ": " + exception.getMessage());
@ -63,44 +63,57 @@ public class FillerManager {
}
private static void loadXmlFillerFile(File file) throws InvalidXmlException, SAXException, IOException {
WarpDrive.logger.info("Loading filler data file " + file.getName());
Document document = WarpDriveConfig.getXmlDocumentBuilder().parse(file);
Document base = WarpDriveConfig.getXmlDocumentBuilder().parse(file);
ModCheckResults res = XmlPreprocessor.checkModRequirements(base.getDocumentElement());
if (!res.isEmpty()) {
WarpDrive.logger.info("Skippping filler data file " + file.getName() + " due to " + res);
// pre-process the file
String result = XmlPreprocessor.checkModRequirements(document.getDocumentElement());
if (!result.isEmpty()) {
WarpDrive.logger.info("Skipping filler data file " + file.getName() + " due to " + result);
return;
}
// Remove elements based on mod reqs sanitation
XmlPreprocessor.doModReqSanitation(base);
XmlPreprocessor.doLogicPreprocessing(base);
XmlPreprocessor.doModReqSanitation(document);
XmlPreprocessor.doLogicPreprocessing(document);
// Initially add FillerSets
NodeList nodesFillerSet = base.getElementsByTagName("FillerSet");
for (int i = 0; i < nodesFillerSet.getLength(); i++) {
// only add FillerSets
NodeList nodesFillerSet = document.getElementsByTagName("FillerSet");
for (int fillerSetIndex = 0; fillerSetIndex < nodesFillerSet.getLength(); fillerSetIndex++) {
Element elementFillerSet = (Element) nodesFillerSet.item(i);
Element elementFillerSet = (Element) nodesFillerSet.item(fillerSetIndex);
String group = elementFillerSet.getAttribute("group");
if (group.isEmpty()) {
throw new InvalidXmlException("FillerSet " + i + " is missing a group attribute!");
throw new InvalidXmlException("FillerSet " + (fillerSetIndex + 1) + "/" + nodesFillerSet.getLength() + " is missing a group attribute!");
}
FillerSet fillerSet = fillerSets.get(group);
if (fillerSet == null) {
fillerSet = new FillerSet(group);
fillerSets.put(group, fillerSet);
String name = elementFillerSet.getAttribute("name");
if (name.isEmpty()) {
throw new InvalidXmlException("FillerSet " + (fillerSetIndex + 1) + "/" + nodesFillerSet.getLength() + " is missing a name attribute!");
}
WarpDrive.logger.info("- found FillerSet " + group + ":" + name);
FillerSet fillerSet = fillerSetsByName.get(name);
if (fillerSet == null) {
fillerSet = new FillerSet(group, name);
fillerSetsByName.put(name, fillerSet);
}
RandomCollection randomCollection = fillerSetsByGroup.get(group);
if (randomCollection == null) {
randomCollection = new RandomCollection<FillerSet>();
fillerSetsByGroup.put(group, randomCollection);
}
randomCollection.loadFromXML(fillerSet, elementFillerSet);
if (elementFillerSet.hasAttribute("fillerSets")) {
ArrayList<String> setUnresolvedDeps = fillerSetsAdditions.get(fillerSet);
if (setUnresolvedDeps == null) {
setUnresolvedDeps = new ArrayList<String>();
fillerSetsAdditions.put(fillerSet, setUnresolvedDeps);
ArrayList<String> dependencies = fillerSetsDependencies.get(fillerSet);
if (dependencies == null) {
dependencies = new ArrayList<String>();
fillerSetsDependencies.put(fillerSet, dependencies);
}
setUnresolvedDeps.addAll(Arrays.asList(elementFillerSet.getAttribute("import").split(",")));
dependencies.addAll(Arrays.asList(elementFillerSet.getAttribute("fillerSets").split(",")));
}
fillerSet.loadFromXmlElement(elementFillerSet);
@ -108,47 +121,48 @@ public class FillerManager {
}
public static void finishLoading() {
// import fillerSets into each others
propagateFillerSets();
while (!fillerSetsAdditions.isEmpty()) {
attemptDependencyFilling(fillerSetsAdditions);
}
// When everything is done, finalize
for (FillerSet fillerSet : fillerSets.values()) {
// compute fillerSets randomization tables
for (FillerSet fillerSet : fillerSetsByName.values()) {
fillerSet.finishContruction();
}
// compute groups randomization tables
for (RandomCollection randomCollection : fillerSetsByGroup.values()) {
// randomCollection.finishContruction();
}
}
private static void attemptDependencyFilling(TreeMap<FillerSet, ArrayList<String>> fillerSetsDeps) {
ArrayList<FillerSet> toRemove = new ArrayList<FillerSet>();
for (Entry<FillerSet, ArrayList<String>> entry : fillerSetsDeps.entrySet()) {
private static void propagateFillerSets() {
while (!fillerSetsDependencies.isEmpty()) {
TreeMap<FillerSet, ArrayList<String>> fillerSetsLeftToImport = new TreeMap<FillerSet, ArrayList<String>>();
for (String dep : entry.getValue()) {
if (!fillerSets.containsKey(dep)) {
WarpDrive.logger.error("Skipping FillerSet " + entry.getKey() + " due to missing dependency " + dep);
fillerSets.remove(entry.getKey().getName());
toRemove.add(entry.getKey());
} else if (fillerSetsDeps.containsKey(fillerSets.get(dep))) {
//Skip until it is loaded
} else {
entry.getKey().loadFrom(fillerSets.get(dep));
toRemove.add(entry.getKey());
for (Entry<FillerSet, ArrayList<String>> entry : fillerSetsDependencies.entrySet()) {
ArrayList<String> newDependencies = new ArrayList();
for (String dependency : entry.getValue()) {
if (!fillerSetsByName.containsKey(dependency)) {
WarpDrive.logger.error("Ignoring FillerSet " + dependency + " dependency in FillerSet " + entry.getKey());
} else if (fillerSetsDependencies.containsKey(fillerSetsByName.get(dependency))) {
// skip until it is loaded
newDependencies.add(dependency);
} else {
entry.getKey().loadFrom(fillerSetsByName.get(dependency));
}
}
if (!newDependencies.isEmpty()) {
fillerSetsLeftToImport.put(entry.getKey(), newDependencies);
}
}
}
for (FillerSet set : toRemove) {
fillerSetsDeps.remove(set);
fillerSetsDependencies = fillerSetsLeftToImport;
}
}
public static FillerSet getFillerSet(String name) {
return fillerSets.get(name);
public static boolean doesFillerSetExist(String groupOrName) {
return fillerSetsByName.containsKey(groupOrName) || fillerSetsByName.containsKey(groupOrName);
}
}

View file

@ -20,51 +20,57 @@ import net.minecraft.init.Blocks;
*
*/
public class FillerSet implements XmlRepresentable, Comparable {
private MetaBlock[] weightedFillerBlocks;
private FillerFactory factory;
protected String group;
protected String name;
public String getGroup() {
return group;
}
@Override
public String getName() {
return name;
}
public FillerSet(MetaBlock[] blocks) {
weightedFillerBlocks = blocks;
}
public FillerSet(String name) {
public FillerSet(final String group, final String name) {
this.group = group;
this.name = name;
weightedFillerBlocks = new MetaBlock[1];
factory = new FillerFactory();
}
public MetaBlock getRandomBlock(Random rand) {
return weightedFillerBlocks[rand.nextInt(weightedFillerBlocks.length)];
}
@Override
public void loadFromXmlElement(Element element) throws InvalidXmlException {
NodeList fillers = element.getElementsByTagName("filler");
for (int i = 0; i < fillers.getLength(); i++) {
Element filler = (Element) fillers.item(i);
// Check there is a block name
if (!filler.hasAttribute("block")) {
throw new InvalidXmlException("Filler " + filler + " is missing a block tag!");
}
String blockName = filler.getAttribute("block");
Block block = Block.getBlockFromName(blockName);
if (block == null) {
WarpDrive.logger.warn("Skipping missing block " + blockName);
continue;
}
// Get metadata attribute, defaults to 0
int intMetadata = 0;
String stringMetadata = filler.getAttribute("metadata");
@ -75,54 +81,58 @@ public class FillerSet implements XmlRepresentable, Comparable {
throw new InvalidXmlException("Invalid metadata for block " + blockName);
}
}
boolean hasWeightOrRatio = false;
// It is intentional that a filler could have both a ratio and a weight
// Check for a weight and add it to the factory
String stringWeight = filler.getAttribute("weight");
int weight;
if (!stringWeight.isEmpty()) {
hasWeightOrRatio = true;
try {
weight = Integer.parseInt(stringWeight);
factory.addWeightedBlock(block, intMetadata, weight);
} catch (NumberFormatException exception) {
throw new InvalidXmlException("Invalid weight for block " + blockName);
} catch (IllegalArgumentException exception) {
throw new InvalidXmlException(exception.getMessage());
}
}
// Check for a ratio attribute, and add it to the factory
String stringRatio = filler.getAttribute("ratio");
if (!stringRatio.isEmpty()) {
hasWeightOrRatio = true;
try {
factory.addRatioBlock(block, intMetadata, stringRatio);
} catch (IllegalArgumentException exception) {
throw new InvalidXmlException(exception.getMessage());
}
}
if (!hasWeightOrRatio) {
throw new InvalidXmlException("No ratio nor weight defined for block " + blockName + " " + stringMetadata);
}
}
}
/**
* @deprecated Not implemented
**/
@Deprecated
@Override
public void saveToXmlElement(Element element, Document document) throws InvalidXmlException {
throw new InvalidXmlException("Not supported");
throw new InvalidXmlException("Not implemented");
}
/**
* Uses the data that has been loaded thus far to construct the array in order to make the FillerSet functional. Must be called before calling getRandomBlock()
*
@ -131,27 +141,27 @@ public class FillerSet implements XmlRepresentable, Comparable {
public void finishContruction() {
WarpDrive.logger.info("Finishing construction of " + name);
weightedFillerBlocks = factory.constructWeightedMetaBlockList();
//For some reason some entries are null, so replace them with air
//For some reason some entries are null, so replace them with air FIXME
for (int i = 0; i < weightedFillerBlocks.length; i++) {
if (weightedFillerBlocks[i] == null)
if (weightedFillerBlocks[i] == null) {
weightedFillerBlocks[i] = MetaBlock.getMetaBlock(Blocks.air, 0);
}
}
factory = null;
}
@Override
public int compareTo(Object object) {
return name.compareTo(((FillerSet) object).name);
}
@Override
public String toString() {
return name;
}
/**
* Adds the blocks from the given fillerSet into this one. Must be pre-finishConstruction()
*

View file

@ -8,128 +8,132 @@ import org.w3c.dom.Element;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.InvalidXmlException;
import cr0s.warpdrive.config.MetaBlock;
import cr0s.warpdrive.data.VectorI;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.world.World;
public class Asteroid extends Orb {
private static final int MIN_RADIUS = 1;
private static final int CORE_MAX_TRIES = 10;
private Block coreBlock;
private int maxCoreSize, minCoreSize;
private double coreRad;
public Asteroid() {
super(0); //Diameter not relevant
private int maxCoreCount;
private int minCoreCount;
private double relativeCoreRadius;
public Asteroid(final String name) {
super(name);
}
@Override
public void loadFromXmlElement(Element e) throws InvalidXmlException {
super.loadFromXmlElement(e);
String coreBlockName = e.getAttribute("coreBlock");
if (coreBlockName.isEmpty())
throw new InvalidXmlException("Asteroid is missing a coreBlock!");
public void loadFromXmlElement(Element element) throws InvalidXmlException {
super.loadFromXmlElement(element);
String coreBlockName = element.getAttribute("coreBlock");
if (coreBlockName.isEmpty()) {
throw new InvalidXmlException("Asteroid " + name + " is missing a coreBlock!");
}
coreBlock = Block.getBlockFromName(coreBlockName);
if (coreBlock == null)
throw new InvalidXmlException("Asteroid coreBlock doesnt exist!");
try {
maxCoreSize = Integer.parseInt(e.getAttribute("maxCoreSize"));
minCoreSize = Integer.parseInt(e.getAttribute("minCoreSize"));
} catch (NumberFormatException gdbg) {
throw new InvalidXmlException("Asteroid core size dimensions are NaN!");
if (coreBlock == null) {
throw new InvalidXmlException("Asteroid " + name + " has an invalid/missing coreBlock " + coreBlockName);
}
try {
String coreRadStr = e.getAttribute("coreRad");
if(coreRadStr.isEmpty()) {
coreRad = 0.1;
minCoreCount = Integer.parseInt(element.getAttribute("minCoreCount"));
} catch (NumberFormatException exception) {
throw new InvalidXmlException("Asteroid " + name + " has an invalid minCoreCount " + element.getAttribute("minCoreCount") + ", expecting an integer");
}
if (minCoreCount < 1) {
throw new InvalidXmlException("Asteroid " + name + " has an invalid minCoreCount " + minCoreCount + ", expecting greater then 0");
}
try {
maxCoreCount = Integer.parseInt(element.getAttribute("maxCoreCount"));
} catch (NumberFormatException exception) {
throw new InvalidXmlException("Asteroid " + name + " has an invalid maxCoreCount " + element.getAttribute("maxCoreCount") + ", expecting an integer");
}
if (maxCoreCount < minCoreCount) {
throw new InvalidXmlException("Asteroid " + name + " has an invalid maxCoreCount " + maxCoreCount + ", expecting greater than or equal to minCoreCount " + minCoreCount);
}
try {
String stringCoreRad = element.getAttribute("relativeCoreRadius");
if (stringCoreRad.isEmpty()) {
relativeCoreRadius = 0.1;
} else {
coreRad = Double.parseDouble(e.getAttribute("coreRad"));
relativeCoreRadius = Double.parseDouble(element.getAttribute("relativeCoreRadius"));
}
} catch (NumberFormatException gdbg) {
throw new InvalidXmlException("Asteroid core rad must be double!");
throw new InvalidXmlException("Asteroid " + name + " has an invalid relativeCoreRadius " + element.getAttribute("relativeCoreRadius") + ", expecting a double");
}
if (relativeCoreRadius < 0.0D || relativeCoreRadius > 1.0D) {
throw new InvalidXmlException("Asteroid " + name + " has an invalid relativeCoreRadius " + relativeCoreRadius + ", expecting a value between 0.0 and 1.0 included");
}
}
@Override
public boolean generate(World world, Random rand, int x, int y, int z) {
int randRadius = MIN_RADIUS + rand.nextInt(Math.max(1, getRadius() - MIN_RADIUS));
int numberCoreBlocks = minCoreSize + rand.nextInt(Math.max(1, maxCoreSize - minCoreSize));
WarpDrive.logger.info("Asteroid generation: radius=" + randRadius + ", numCoreBlocks=" + numberCoreBlocks + ", coreRad=" + coreRad);
//Use this to generate a abstract form for the core.
ArrayList<Location> coreLocations = generateCore(world, rand, x, y, z, numberCoreBlocks, coreBlock, numberCoreBlocks, randRadius);
for (Location coreLocation: coreLocations) {
// Calculate mininum distance to borders of generation area
int maxRadX = Math.min(x+randRadius-coreLocation.x, coreLocation.x - (x - randRadius));
int maxRadY = Math.min(y+randRadius-coreLocation.y, coreLocation.y - (y - randRadius));
int maxRadZ = Math.min(z+randRadius-coreLocation.z, coreLocation.z - (z - randRadius));
int maxLocalRadius = Math.min(maxRadX, Math.min(maxRadY, maxRadZ));
// Generate shell
addShell(world, rand, coreLocation, maxLocalRadius);
public boolean generate(World world, Random random, int x, int y, int z) {
int[] thicknesses = randomize(random);
int totalThickness = 0;
for (int thickness : thicknesses) {
totalThickness += thickness;
}
int coreBlocksCount = minCoreCount + ((maxCoreCount > minCoreCount) ? random.nextInt(maxCoreCount - minCoreCount) : 0);
WarpDrive.logger.info("Generating asteroid " + name + " as radius " + totalThickness + " coreBlocksCount " + coreBlocksCount + " coreRad " + relativeCoreRadius);
// use this to generate an abstract form for the core.
double coreRadius = relativeCoreRadius * totalThickness;
ArrayList<VectorI> coreLocations = generateCore(world, random, x, y, z, coreBlocksCount, coreBlock, coreBlocksCount, coreRadius);
for (VectorI coreLocation: coreLocations) {
// Calculate minimum distance to borders of generation area
int maxRadX = totalThickness - Math.abs(x - coreLocation.x);
int maxRadY = totalThickness - Math.abs(y - coreLocation.y);
int maxRadZ = totalThickness - Math.abs(z - coreLocation.z);
int maxLocalRadius = Math.max(maxRadX, Math.max(maxRadY, maxRadZ));
// Generate shell
addShell(thicknesses, world, coreLocation, maxLocalRadius);
}
return true;
}
/**
* Creates a shell sphere around given core location.
*
* @param thicknesses Random generator
* @param world World to place shell
* @param rand Random generator
* @param l Location of core block
* @param location Location of core block
* @param maxRad Maximum radius of asteroid
*/
private void addShell(World world, Random rand, Location l, int maxRad) {
//int rad = MIN_RADIUS + rand.nextInt(Math.max(1, maxRad - MIN_RADIUS));
int rad = maxRad;
// Iterate all blocks withing cube with side 2*rad
for(int x = l.x - rad; x <= l.x + rad; ++x) {
for(int y = l.y - rad; y <= l.y + rad; ++y) {
for(int z = l.z - rad; z <= l.z + rad; ++z) {
private void addShell(int[] thicknesses, World world, VectorI location, int radius) {
// iterate all blocks within cube with side 2 * radius
for(int x = location.x - radius; x <= location.x + radius; x++) {
int dX2 = (x - location.x) * (x - location.x);
for(int y = location.y - radius; y <= location.y + radius; y++) {
int dX2Y2 = dX2 + (y - location.y) * (y - location.y);
for(int z = location.z - radius; z <= location.z + radius; z++) {
// current radius
int r = (int)Math.round(Math.sqrt((l.x - x)*(l.x - x) + (l.y - y)*(l.y - y) + (l.z - z)*(l.z - z)));
int range = (int)Math.round(Math.sqrt(dX2Y2 + (location.z - z) * (location.z - z)));
// if inside radius
if(r <= rad && isBlockEmpty(world, x, y, z)) {
OrbShell shell = getShellForRadius(r);
MetaBlock blType = shell.getRandomBlock(rand);
world.setBlock(x, y, z, blType.block, blType.metadata, 0);
if(range <= radius && isReplaceableOreGen(world, x, y, z)) {
OrbShell shell = getShellForRadius(thicknesses, range);
MetaBlock metaBlock = shell.getRandomBlock(world.rand);
world.setBlock(x, y, z, metaBlock.block, metaBlock.metadata, 0);
}
}
}
}
}
/**
* Represents a single point in space
*
*/
private class Location {
public int x, y, z;
public Location(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
}
/**
* Checks if given coordinate empty (air in terms of MC).
* @param world
@ -138,7 +142,7 @@ public class Asteroid extends Orb {
* @param z
* @return
*/
private static boolean isBlockEmpty(World world, int x, int y, int z) {
private static boolean isReplaceableOreGen(World world, int x, int y, int z) {
return world.getBlock(x, y, z).isReplaceableOreGen(world, x, y, z, Blocks.air);
}
@ -155,41 +159,36 @@ public class Asteroid extends Orb {
* @param numberOfBlocks - number of core blocks to place
* @param block - type of block to place
* @param metadata - metadata of bloeck to place
* @param maxRange - max radius of asteroid
* @param coreRadius - max radius of asteroid
* @return List of placed locations of cores
*/
private ArrayList<Location> generateCore(World world, Random rand, int x, int y, int z, int numberOfBlocks, Block block, int metadata,
int maxRange) {
ArrayList<Location> addedBlocks = new ArrayList<Location>();
int coreRange = (int)Math.round(coreRad * maxRange);
int maxX = x + coreRange;
int minX = x - coreRange;
int maxY = y + coreRange;
int minY = y - coreRange;
int maxZ = z + coreRange;
int minZ = z - coreRange;
private ArrayList<VectorI> generateCore(World world, Random rand, int x, int y, int z,
int numberOfBlocks, Block block, int metadata, double coreRadius) {
ArrayList<VectorI> addedBlocks = new ArrayList<VectorI>();
int coreDiameter = Math.max(1, (int)Math.round(2 * coreRadius));
int xMin = x - (int)Math.round(coreRadius);
int yMin = y - (int)Math.round(coreRadius);
int zMin = z - (int)Math.round(coreRadius);
for (int i = 0; i < numberOfBlocks; ++i) {
for (int coreBlockIndex = 0; coreBlockIndex < numberOfBlocks; coreBlockIndex++) {
int curX = x;
int curY = y;
int curZ = z;
boolean stopWalk = false;
boolean found = false;
for(int step = 0; step <= CORE_MAX_TRIES && !stopWalk; ++step) {
curX = rand.nextInt(Math.max(1, maxX - minX)) + minX;
curY = rand.nextInt(Math.max(1, maxY - minY)) + minY;
curZ = rand.nextInt(Math.max(1, maxZ - minZ)) + minZ;
for(int step = 0; step < CORE_MAX_TRIES && !found; step++) {
curX = xMin + rand.nextInt(coreDiameter);
curY = yMin + rand.nextInt(coreDiameter);
curZ = zMin + rand.nextInt(coreDiameter);
if (isBlockEmpty(world, curX, curY, curZ)) {
if (isReplaceableOreGen(world, curX, curY, curZ)) {
world.setBlock(curX, curY, curZ, block, metadata, 2);
addedBlocks.add(new Location(curX, curY, curZ));
stopWalk = true;
addedBlocks.add(new VectorI(curX, curY, curZ));
found = true;
}
}
}
return addedBlocks;
}
}

View file

@ -6,31 +6,29 @@ package cr0s.warpdrive.config.structures;
import net.minecraft.world.gen.feature.WorldGenerator;
/**
* @author Francesco
* @author Francesco, LemADEC
*
*/
public abstract class DeployableStructure extends WorldGenerator {
protected String name;
protected int sizeX;
protected int sizeY;
protected int sizeZ;
protected int height;
protected int width;
protected int length;
public DeployableStructure(int height, int width, int length) {
this.height = height;
this.width = width;
this.length = length;
public DeployableStructure(final String name) {
this.name = name;
}
public int getHeight() {
return height;
public void setDimensions(final int sizeX, final int sizeY, final int sizeZ) {
this.sizeX = sizeX;
this.sizeY = sizeY;
this.sizeZ = sizeZ;
}
public int getWidth() {
return width;
}
public int getLength() {
return length;
public void setRadius(final int radius) {
sizeX = radius * 2;
sizeY = radius * 2;
sizeZ = radius * 2;
}
}

View file

@ -15,164 +15,142 @@ import cr0s.warpdrive.config.XmlRepresentable;
import cr0s.warpdrive.config.filler.FillerManager;
import cr0s.warpdrive.config.filler.FillerSet;
import cr0s.warpdrive.world.EntitySphereGen;
import cr0s.warpdrive.world.EntityStarCore;
public abstract class Orb extends DeployableStructure implements XmlRepresentable {
private OrbShell[] shellRelative;
// private ArrayList<OrbShell> shells;
private String name;
/**
* @return the radius
*/
public int getRadius() {
return super.height / 2;
private OrbShell[] orbShells;
protected boolean hasStarCore = false;
private ArrayList<String> fillerSetGroupOrNames = new ArrayList<String>();
public Orb(final String name) {
super(name);
}
/**
* @param radius the radius to set
*/
public void setRadius(int radius) {
super.height = radius * 2;
super.length = radius * 2;
super.width = radius * 2;
}
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Orb(int radius) {
super(radius * 2, radius * 2, radius * 2);
setRadius(radius);
}
@Override
public void loadFromXmlElement(Element element) throws InvalidXmlException {
name = element.getAttribute("name");
ArrayList<OrbShell> newShells = new ArrayList<OrbShell>();
int totalThickness = 0;
NodeList shells = element.getElementsByTagName("shell");
for (int i = 0; i < shells.getLength(); i++) {
Element elementShell = (Element) shells.item(i);
OrbShell shell = new OrbShell();
shell.loadFromXmlElement(elementShell);
shell.finishContruction();
totalThickness += shell.thickness;
newShells.add(shell);
int maxThickness = 0;
NodeList nodeListShells = element.getElementsByTagName("shell");
orbShells = new OrbShell[nodeListShells.getLength()];
for (int shellIndex = 0; shellIndex < nodeListShells.getLength(); shellIndex++) {
Element elementShell = (Element) nodeListShells.item(shellIndex);
String orbShellName = element.getAttribute("name");
orbShells[shellIndex] = new OrbShell(name, orbShellName);
orbShells[shellIndex].loadFromXmlElement(elementShell);
orbShells[shellIndex].finishContruction();
maxThickness += orbShells[shellIndex].maxThickness;
}
int index = 0;
shellRelative = new OrbShell[totalThickness];
for (OrbShell shell : newShells) {
for (int i = 0; i < shell.thickness; i++)
shellRelative[index++] = shell;
}
setRadius(totalThickness - 1);
setRadius(maxThickness - 1);
}
/**
* @deprecated Not implemented
**/
@Deprecated
@Override
public void saveToXmlElement(Element element, Document document) {
/* TODO: dead code?
for (OrbShell shell : shells) {
Element tmp = document.createElement("shell");
shell.saveToXmlElement(tmp, document);
element.appendChild(tmp);
}
/**/
public void saveToXmlElement(Element element, Document document) throws InvalidXmlException {
throw new InvalidXmlException("Not implemented");
}
@Override
public boolean generate(World world, Random random, int x, int y, int z) {
EntitySphereGen entitySphereGen = new EntitySphereGen(world, x, y, z, getRadius(), this, true);
int[] thicknesses = randomize(random);
int totalThickness = 0;
for (int thickness : thicknesses) {
totalThickness += thickness;
}
EntitySphereGen entitySphereGen = new EntitySphereGen(world, x, y, z, this, thicknesses, totalThickness, true);
world.spawnEntityInWorld(entitySphereGen);
if (hasStarCore) {
return world.spawnEntityInWorld(new EntityStarCore(world, x, y, z, totalThickness));
}
return false;
}
/**
*
* @Deprecated pending addition of variables offsets in structure XML attributes
*/
@Deprecated
public boolean generate(World world, Random random, int x, int y, int z, final int radius) {
EntitySphereGen entitySphereGen = new EntitySphereGen(world, x, y, z, radius, this, true);
EntitySphereGen entitySphereGen = new EntitySphereGen(world, x, y, z, this, null, radius, true);
world.spawnEntityInWorld(entitySphereGen);
return false;
}
public OrbShell getShellForRadius(int r) {
return shellRelative[r];
public int[] randomize(Random random) {
int[] thicknesses = new int[orbShells.length];
for(int orbShellIndex = 0; orbShellIndex < orbShells.length; orbShellIndex++) {
OrbShell orbShell = orbShells[orbShellIndex];
thicknesses[orbShellIndex] = orbShell.minThickness
+ ((orbShell.maxThickness - orbShell.minThickness > 0) ? random.nextInt(orbShell.maxThickness - orbShell.minThickness) : 0);
}
return thicknesses;
}
public OrbShell getShellForRadius(final int[] thicknesses, final int range) {
int cumulatedRange = 0;
for (int shellIndex = 0; shellIndex < orbShells.length; shellIndex++) {
cumulatedRange += thicknesses[shellIndex];
if (range <= cumulatedRange) {
return orbShells[shellIndex];
}
}
return null;
}
public class OrbShell extends FillerSet {
private int thickness;
/**
* @return the thickness
*/
public int getThickness() {
return thickness;
private String parentName;
private int minThickness;
private int maxThickness;
public OrbShell(String parentName, String name) {
super(null, name);
this.parentName = parentName;
}
/**
* @param thickness
* the thickness to set
*/
public void setThickness(int thickness) {
this.thickness = thickness;
}
public OrbShell() {
super("");
}
@Override
public void loadFromXmlElement(Element element) throws InvalidXmlException {
WarpDrive.logger.info("Loading shell " + element.getAttribute("name"));
name = element.getAttribute("name");
super.loadFromXmlElement(element);
if (element.hasAttribute("fillerSets")) {
String[] imports = element.getAttribute("fillerSets").split(",");
for (String imp : imports) {
FillerSet fillSet = FillerManager.getFillerSet(imp);
if (fillSet == null)
throw new InvalidXmlException("Shell loading tries to import a non-existant fillerSet!");
super.loadFrom(fillSet);
String[] allFillerSetGroupOrNames = element.getAttribute("fillerSets").split(",");
for (String fillerSetGroupOrName : allFillerSetGroupOrNames) {
if (!FillerManager.doesFillerSetExist(fillerSetGroupOrName)) {
WarpDrive.logger.warn("Skipping missing FillerSet " + fillerSetGroupOrName + " in shell " + name);
} else {
fillerSetGroupOrNames.add(fillerSetGroupOrName);
}
}
}
try {
thickness = Integer.parseInt(element.getAttribute("maxThickness"));
minThickness = Integer.parseInt(element.getAttribute("minThickness"));
} catch (NumberFormatException ex) {
throw new InvalidXmlException("MaxThickness is not valid!");
throw new InvalidXmlException("Invalid minThickness in shell " + name + " of orb " + parentName);
}
try {
maxThickness = Integer.parseInt(element.getAttribute("maxThickness"));
} catch (NumberFormatException ex) {
throw new InvalidXmlException("Invalid maxThickness in shell " + name + " of orb " + parentName);
}
if (maxThickness < minThickness) {
throw new InvalidXmlException("Invalid maxThickness " + maxThickness + " lower than minThickness " + minThickness + " in shell " + name + " of orb " + parentName);
}
//TODO: Implement random thickness
}
@Override
public void saveToXmlElement(Element e, Document d) {
//Not needed
}
}
}

View file

@ -1,10 +1,7 @@
package cr0s.warpdrive.config.structures;
public class Planetoid extends Orb {
public Planetoid(int diameter) {
super(diameter);
// TODO Auto-generated constructor stub
public Planetoid(final String name) {
super(name);
}
}

View file

@ -6,8 +6,8 @@ import net.minecraft.world.World;
public class SchematicStructure extends DeployableStructure {
public SchematicStructure(int height, int width, int length) {
super(height, width, length);
public SchematicStructure(final String name) {
super(name);
// TODO Auto-generated constructor stub
}

View file

@ -1,27 +1,9 @@
package cr0s.warpdrive.config.structures;
import java.util.Random;
import net.minecraft.world.World;
import cr0s.warpdrive.world.EntityStarCore;
public class Star extends Orb {
public Star(int diameter) {
super(diameter);
public Star(final String name) {
super(name);
hasStarCore = true;
}
@Override
public boolean generate(World p_76484_1_, Random p_76484_2_, int p_76484_3_, int p_76484_4_, int p_76484_5_) {
boolean success = super.generate(p_76484_1_, p_76484_2_, p_76484_3_, p_76484_4_, p_76484_5_);
if (success)
return placeStarCore(p_76484_1_, p_76484_3_, p_76484_4_, p_76484_5_, super.getHeight() / 2);
return false;
}
public static boolean placeStarCore(World world, int x, int y, int z, int radius) {
return world.spawnEntityInWorld(new EntityStarCore(world, x, y, z, radius));
}
}

View file

@ -3,10 +3,7 @@ package cr0s.warpdrive.config.structures;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.NavigableMap;
import java.util.Random;
import java.util.TreeMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@ -15,27 +12,25 @@ import org.xml.sax.SAXException;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.InvalidXmlException;
import cr0s.warpdrive.config.RandomCollection;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.config.XmlPreprocessor;
import cr0s.warpdrive.config.XmlPreprocessor.ModCheckResults;
import cr0s.warpdrive.config.XmlRepresentable;
public class StructureManager {
public static final String GROUP_STARS = "star";
public static final String GROUP_MOONS = "moon";
public static final String GROUP_GASCLOUDS = "gascloud";
public static final String GROUP_ASTEROIDS = "asteroid";
private static RandomCollection<Star> stars = new RandomCollection<Star>();
private static RandomCollection<Planetoid> moons = new RandomCollection<Planetoid>();
private static RandomCollection<Planetoid> gasClouds = new RandomCollection<Planetoid>();
private static RandomCollection<Asteroid> gasClouds = new RandomCollection<Asteroid>();
private static RandomCollection<Asteroid> asteroids = new RandomCollection<Asteroid>();
public static void loadStructures(String structureConfDir) {
loadStructures(new File(structureConfDir));
}
public static void loadStructures(File dir) {
dir.mkdir();
if (!dir.isDirectory()) {
throw new IllegalArgumentException("File path " + dir.getPath() + " must be a directory!");
}
@ -49,13 +44,7 @@ public class StructureManager {
for (File file : files) {
try {
WarpDrive.logger.info("Loading structure data file " + file.getName());
loadXmlStructureFile(file);
WarpDrive.logger.info("Finished loading structure data file " + file.getName());
} catch (Exception exception) {
WarpDrive.logger.error("Error loading file " + file.getName() + ": " + exception.getMessage());
exception.printStackTrace();
@ -64,186 +53,97 @@ public class StructureManager {
}
private static void loadXmlStructureFile(File file) throws SAXException, IOException, InvalidXmlException {
Document base = WarpDriveConfig.getXmlDocumentBuilder().parse(file);
WarpDrive.logger.info("Loading structure data file " + file.getName());
Document document = WarpDriveConfig.getXmlDocumentBuilder().parse(file);
ModCheckResults res = XmlPreprocessor.checkModRequirements(base.getDocumentElement());
if (!res.isEmpty()) {
WarpDrive.logger.info("Skippping structure " + file.getName() + " due to " + res);
// pre-process the file
String result = XmlPreprocessor.checkModRequirements(document.getDocumentElement());
if (!result.isEmpty()) {
WarpDrive.logger.info("Skipping structure " + file.getName() + " due to " + result);
return;
}
XmlPreprocessor.doModReqSanitation(base);
XmlPreprocessor.doLogicPreprocessing(base);
XmlPreprocessor.doModReqSanitation(document);
XmlPreprocessor.doLogicPreprocessing(document);
NodeList structures = base.getElementsByTagName("structure");
for (int i = 0; i < structures.getLength(); i++) {
// only add FillerSets
NodeList nodeListStructures = document.getElementsByTagName("structure");
for (int structureIndex = 0; structureIndex < nodeListStructures.getLength(); structureIndex++) {
Element struct = (Element) structures.item(i);
Element elementStructure = (Element) nodeListStructures.item(structureIndex);
String group = struct.getAttribute("group");
String name = struct.getAttribute("name");
String group = elementStructure.getAttribute("group");
if (group.isEmpty()) {
throw new InvalidXmlException("Structure " + (structureIndex + 1) + "/" + nodeListStructures.getLength() + " is missing a group attribute!");
}
WarpDrive.logger.info("Loading structure " + name);
String name = elementStructure.getAttribute("name");
if (name.isEmpty()) {
throw new InvalidXmlException("Structure " + (structureIndex + 1) + "/" + nodeListStructures.getLength() + " is missing a name attribute!");
}
if (group.isEmpty())
throw new InvalidXmlException("Structure must have a group!");
WarpDrive.logger.info("- found structure " + group + ":" + name);
int radius = 0;
if (group.equalsIgnoreCase("star")) {
stars.loadFromXML(new Star(radius), struct);
} else if (group.equalsIgnoreCase("moon")) {
moons.loadFromXML(new Planetoid(radius), struct);
} else if (group.equalsIgnoreCase("asteroid")) {
asteroids.loadFromXML(new Asteroid(), struct);
switch (group) {
case GROUP_STARS:
stars.loadFromXML(new Star(name), elementStructure);
break;
case GROUP_MOONS:
moons.loadFromXML(new Planetoid(name), elementStructure);
break;
case GROUP_ASTEROIDS:
asteroids.loadFromXML(new Asteroid(name), elementStructure);
break;
case GROUP_GASCLOUDS:
gasClouds.loadFromXML(new Asteroid(name), elementStructure);
break;
default:
throw new InvalidXmlException("Structure " + (structureIndex + 1) + "/" + nodeListStructures.getLength() + " has invalid group " + group);
// break;
}
}
}
public static DeployableStructure getStructure(Random random, final String name, final String type) {
public static DeployableStructure getStructure(Random random, final String group, final String name) {
if (name == null || name.length() == 0) {
if (type == null || type.length() == 0) {
return stars.next(random);
} else if (type.equalsIgnoreCase("star")) {
return stars.next(random);
} else if (type.equalsIgnoreCase("moon")) {
return moons.next(random);
} else if (type.equalsIgnoreCase("asteroid")) {
return asteroids.next(random);
if (group == null || group.isEmpty()) {
return null;
} else if (group.equalsIgnoreCase(GROUP_STARS)) {
return stars.getRandomEntry(random);
} else if (group.equalsIgnoreCase(GROUP_MOONS)) {
return moons.getRandomEntry(random);
} else if (group.equalsIgnoreCase(GROUP_ASTEROIDS)) {
return asteroids.getRandomEntry(random);
}
} else {
for (Star star : stars.elements()) {
if (star.getName().equals(name))
return star;
if (group == null || group.isEmpty()) {
return null;
} else if (group.equalsIgnoreCase(GROUP_STARS)) {
return stars.getNamedEntry(name);
} else if (group.equalsIgnoreCase(GROUP_MOONS)) {
return moons.getNamedEntry(name);
} else if (group.equalsIgnoreCase(GROUP_ASTEROIDS)) {
return asteroids.getNamedEntry(name);
}
}
// not found or nothing defined => return null
// not found or nothing defined
return null;
}
public static DeployableStructure getStar(Random random, final String name) {
return getStructure(random, name, "star");
return getStructure(random, GROUP_STARS, name);
}
public static DeployableStructure getMoon(Random random, final String name) {
return getStructure(random, name, "moon");
return getStructure(random, GROUP_MOONS, name);
}
public static DeployableStructure getAsteroid(Random random, final String name) {
return getStructure(random, name, "asteroid");
return getStructure(random, GROUP_ASTEROIDS, name);
}
public static DeployableStructure getGasCloud(Random random, final String name) {
return getStructure(random, name, "cloud");
}
/**
* Collection of elements with weights. Helps to select element with controlled odds.
*
* @author ncrashed
*
* @param <E>
*/
private static class RandomCollection<E extends XmlRepresentable> {
private final NavigableMap<Double, E> weightMap = new TreeMap<Double, E>();
private double totalWeight = 0;
private final NavigableMap<Double, E> ratioMap = new TreeMap<Double, E>();
private double totalRatio = 0;
private final ArrayList<E> list = new ArrayList<E>();
/**
* Add new object and its weight.
* @param weight Used for random pick. The higher the value is relatively to others, the higher odds of choosing the object.
* @param obj Object to add
*/
public void add(double weight, E obj) {
if (weight <= 0) {
WarpDrive.logger.warn("Structure weight is negative or zero, skipping");
return;
}
totalWeight += weight;
weightMap.put(totalWeight, obj);
list.add(obj);
}
/**
* Add new object and its ratio.
* Warning: if total ratio goes higher than 1.0, element won't be added to collection.
* @param ratio Chance of random pick in range (0, 1.0]. In contrast to weights, ratio is fixed and chances don't change if you add more elements.
* @param obj Object to add
*/
public void addRatio(double ratio, E obj) {
if (ratio <= 0 || ratio >= 1.0) {
WarpDrive.logger.warn("Structure ratio isn't in (0, 1.0] bounds, skipping");
return;
}
if (totalRatio + ratio > 1.0) {
WarpDrive.logger.warn("Structures total ratio is greater than 1.0, skipping");
return;
}
totalRatio += ratio;
ratioMap.put(totalRatio, obj);
list.add(obj);
}
/**
* Pick random object according their weights
* @param random
* @return Random object or null if there is no objects to pick.
*/
public E next(Random random) {
double value = random.nextDouble();
if (value < totalRatio) { // hit ratio part of values
return ratioMap.ceilingEntry(value).getValue();
} else { // hit dynamic part of values, weighted ones
double weight = (value - totalRatio)*totalWeight;
return weightMap.ceilingEntry(weight).getValue();
}
}
/**
* @return All registered objects
*/
public ArrayList<E> elements() {
return list;
}
/**
* Loads object from given XML element and parses configurations for weighted pick.
* @param obj Object to load from *struct*
* @param struct Part of XML config
* @throws InvalidXmlException
*/
public void loadFromXML(E obj, Element struct) throws InvalidXmlException {
obj.loadFromXmlElement(struct);
try {
String ratioStr = struct.getAttribute("ratio");
if(!ratioStr.isEmpty()) {
double ratio = Double.parseDouble(ratioStr);
this.addRatio(ratio, obj);
} else { // try weight
try {
int weight = 1;
String weightStr = struct.getAttribute("weight");
if(!weightStr.isEmpty()) {
weight = Integer.parseInt(weightStr);
weight = Math.max(1, weight);
}
this.add(weight, obj);
} catch (NumberFormatException gdbg) {
throw new InvalidXmlException("Weight must be int!");
}
}
} catch (NumberFormatException gdbg) {
throw new InvalidXmlException("Ratio must be double!");
}
}
return getStructure(random, GROUP_GASCLOUDS, name);
}
}

View file

@ -65,13 +65,14 @@ public final class EntitySphereGen extends Entity {
private ArrayList<JumpBlock> blocks;
private Orb orb;
private int[] thicknesses;
private boolean replace;
public EntitySphereGen(World world) {
super(world);
}
public EntitySphereGen(World world, int x, int y, int z, int radius, Orb orb, boolean replace) {
public EntitySphereGen(World world, int x, int y, int z, Orb orb, int[] thicknesses, int radius, boolean replace) {
super(world);
this.xCoord = x;
this.posX = x;
@ -86,6 +87,7 @@ public final class EntitySphereGen extends Entity {
blocks = new ArrayList<JumpBlock>(this.pregenSize);
this.ticksDelay = world.rand.nextInt(60);
this.orb = orb;
this.thicknesses = thicknesses;
this.replace = replace;
}
@ -97,10 +99,10 @@ public final class EntitySphereGen extends Entity {
@Override
public void onUpdate() {
if (FMLCommonHandler.instance().getEffectiveSide().isClient()) {
if (worldObj.isRemote) {
return;
}
if (ticksDelay > 0) {
ticksDelay--;
return;
@ -171,10 +173,10 @@ public final class EntitySphereGen extends Entity {
if (dSq > radius)
continue;
int rad = (int) Math.ceil(dSq);
int range = (int) Math.ceil(dSq);
// Add blocks to memory
OrbShell orbShell = orb.getShellForRadius(rad);
OrbShell orbShell = orb.getShellForRadius(thicknesses, range);
MetaBlock metablock = orbShell.getRandomBlock(rand);
addBlock(new JumpBlock(metablock.block, metablock.metadata, xCoord + x, yCoord + y, zCoord + z));
@ -208,8 +210,9 @@ public final class EntitySphereGen extends Entity {
}
private void addBlock(JumpBlock jb) {
if (blocks == null)
if (blocks == null) {
return;
}
// Replace water with random gas (ship in moon)
if (worldObj.getBlock(jb.x, jb.y, jb.z).isAssociatedBlock(Blocks.water)) {
if (worldObj.rand.nextInt(50) != 1) {
@ -220,8 +223,9 @@ public final class EntitySphereGen extends Entity {
return;
}
// Do not replace existing blocks if fillingSphere is true
if (!replace && !worldObj.isAirBlock(jb.x, jb.y, jb.z))
if (!replace && !worldObj.isAirBlock(jb.x, jb.y, jb.z)) {
return;
}
blocks.add(jb);
}

View file

@ -269,7 +269,6 @@ public class SpaceWorldGenerator implements IWorldGenerator {
public static void generateRandomAsteroid(World world, int x, int y, int z) {
DeployableStructure asteroid = StructureManager.getAsteroid(world.rand, null);
WarpDrive.logger.info("Generating asteroid (class " + asteroid + ") at " + x + " " + y + " " + z);
asteroid.generate(world, world.rand, x, y, z);
@ -296,34 +295,37 @@ public class SpaceWorldGenerator implements IWorldGenerator {
}
*/
}
public static void generateSphereDirect(
World world, int xCoord, int yCoord, int zCoord, Orb orb, Random rand) {
double radiusC = orb.getHeight() / 2 + 0.5D; // Radius from center of block
final int[] thicknesses, Orb orb, World world, int xCoord, int yCoord, int zCoord) {
int totalThickness = 0;
for (int thickness : thicknesses) {
totalThickness += thickness;
}
double radiusC = totalThickness + 0.5D; // Radius from center of block
double radiusSq = radiusC * radiusC; // Optimization to avoid sqrts...
// sphere
int ceilRadius = (int) Math.ceil(radiusC);
// Pass the cube and check points for sphere equation x^2 + y^2 + z^2 = r^2
for (int x = 0; x <= ceilRadius; x++) {
double x2 = (x + 0.5D) * (x + 0.5D);
double dX2 = (x + 0.5D) * (x + 0.5D);
for (int y = 0; y <= ceilRadius; y++) {
double y2 = (y + 0.5D) * (y + 0.5D);
double dX2Y2 = dX2 + (y + 0.5D) * (y + 0.5D);
for (int z = 0; z <= ceilRadius; z++) {
double z2 = (z + 0.5D) * (z + 0.5D);
int dSq = (int) Math.sqrt(x2 + y2 + z2); // Distance from current position
//TODO: Find quicker form of sqrt
double dZ2 = (z + 0.5D) * (z + 0.5D);
double dSq = dX2Y2 + dZ2; // squared distance from current position
// Skip too far blocks
if (dSq > radiusSq) {
continue;
}
// Place blocks
// cheat by using axial symmetry so we don't create random numbers too frequently
OrbShell orbShell = orb.getShellForRadius(dSq);
MetaBlock metablock = orbShell.getRandomBlock(rand);
OrbShell orbShell = orb.getShellForRadius(thicknesses, (int)Math.sqrt(dSq));
MetaBlock metablock = orbShell.getRandomBlock(world.rand);
world.setBlock(xCoord + x, yCoord + y, zCoord + z, metablock.block, metablock.metadata, 2);
world.setBlock(xCoord - x, yCoord + y, zCoord + z, metablock.block, metablock.metadata, 2);
world.setBlock(xCoord + x, yCoord - y, zCoord + z, metablock.block, metablock.metadata, 2);

View file

@ -3,7 +3,7 @@
<warpdriveWorldGeneration version="1">
<FillerSet group="stone_ores">
<FillerSet group="stone_ores" name="default_stone_ores">
<filler block="minecraft:coal_ore" ratio=".08" />
<filler block="minecraft:iron_ore" ratio=".05" />
<filler block="minecraft:redstone_ore" ratio=".05" />
@ -32,20 +32,20 @@
<filler block="minecraft:bedrock" ratio=".001" />
</FillerSet>
<FillerSet group="commonOres">
<FillerSet group="commonOres" name="default_commonOres">
<filler block="minecraft:coal_ore" ratio=".08" />
<filler block="minecraft:iron_ore" ratio=".05" />
<filler block="IC2:blockOreTin" ratio=".05" />
<filler block="IC2:blockOreCopper" ratio=".06" />
</FillerSet>
<FillerSet group="uncommonOres">
<FillerSet group="uncommonOres" name="default_uncommonOres">
<filler block="minecraft:redstone_ore" ratio=".02" />
<filler block="IC2:blockOreUran" ratio=".001" mods="IC2" />
<filler block="ae2:quartz" ratio=".01" mods="appliedenergistics2" />
</FillerSet>
<FillerSet group="rareOres">
<FillerSet group="rareOres" name="default_rareOres">
<filler block="minecraft:gold_ore" ratio=".008" />
<filler block="minecraft:diamond_ore" ratio=".001" />
<filler block="minecraft:lapis_ore" ratio=".005" />
@ -53,19 +53,19 @@
<filler block="warpdrive:iridiumBlock" ratio=".0001" />
</FillerSet>
<FillerSet group="overworld">
<FillerSet group="overworld" name="default_overworld">
<filler block="minecraft:stone" weight="200" />
<filler block="minecraft:sand" weight="30" />
<filler block="minecraft:dirt" weight="50" />
</FillerSet>
<FillerSet group="end">
<FillerSet group="end" name="default_end">
<filler block="minecraft:end_stone" weight="200" />
<filler block="minecraft:sand" weight="20" />
<filler block="minecraft:obsidian" weight="20" />
</FillerSet>
<FillerSet group="nether">
<FillerSet group="nether" name="default_nether">
<filler block="minecraft:netherrack" weight="300" />
<filler block="minecraft:quartz_ore" weight="20" />
<filler block="minecraft:glowstone" weight="40" />

View file

@ -3,7 +3,7 @@
<warpdriveWorldGeneration version="1" mods="netherores">
<FillerSet group="nether" >
<FillerSet group="nether" name="netherores_nether">
<filler block="netherores:ore" metadata="0" weight="50" />
<filler block="netherores:ore" metadata="0" weight="40" />
<filler block="netherores:ore" metadata="0" weight="30" />

View file

@ -5,7 +5,7 @@
<for variable="type" in="igneous,metamorphic,sedimentary">
<for variable="metadata" from="0" to="7">
<FillerSet group="%type%_ores@%metadata%">
<FillerSet group="stone_ores" name="%type%_ores@%metadata%">
<filler block="UndergroundBiomes:%type%_oreCoal" metadata="%metadata%" ratio=".001" />
<filler block="UndergroundBiomes:%type%_oreDiamond" metadata="%metadata%" ratio=".001" />
<filler block="UndergroundBiomes:%type%_oreEmerald" metadata="%metadata%" ratio=".001" />
@ -15,11 +15,11 @@
<filler block="UndergroundBiomes:%type%_oreRedstone" metadata="%metadata%" ratio=".001" />
</FillerSet>
<FillerSet group="%type%_ores@%metadata%.normal" fillerSets="%type%_ores@%metadata%">
<FillerSet group="overworld" name="%type%_ores@%metadata%.normal" fillerSets="%type%_ores@%metadata%">
<filler block="UndergroundBiomes:%type%Stone" metadata="%metadata%" weight="10" />
</FillerSet>
<FillerSet group="%type%_ores@%metadata%.corrupted" fillerSets="%type%_ores@%metadata%">
<FillerSet group="overworld" name="%type%_ores@%metadata%.corrupted" fillerSets="%type%_ores@%metadata%">
<filler block="UndergroundBiomes:%type%Cobblestone" metadata="%metadata%" weight="10" />
</FillerSet>

View file

@ -3,22 +3,87 @@
<warpdriveWorldGeneration version="1">
<structure group="star" name="red_giant">
<shell name="core" maxThickness="10" minThickness="3">
<filler weight="1" block="minecraft:iron_ore" />
<structure group="star" name="red_dwarf">
<shell name="core" minThickness="40" maxThickness="50"><!-- legacy radius was 42 -->
<filler weight="1" block="minecraft:redstone_block" />
</shell>
<shell name="mantle" minThickness="10" maxThickness="25">
<filler weight="10" block="minecraft:sand" />
<filler weight="1" block="minecraft:iron_block" />
<shell name="low_altitude" minThickness="4" maxThickness="6">
<filler weight="1" block="WarpDrive:blockGas" metadata="1" />
</shell>
<shell name="surface" minThickness="10" maxThickness="20">
<filler weight="5" block="minecraft:glowstone" />
<filler weight="10" block="minecraft:lava" />
<shell name="atmosphere" minThickness="2" maxThickness="2">
<filler weight="1" block="WarpDrive:blockGas" metadata="1" />
</shell>
</structure>
<structure group="star" name="orange_dwarf">
<shell name="core" minThickness="1" maxThickness="1"><!-- mark the center -->
<filler weight="1" block="minecraft:obsidian" />
</shell>
<shell name="mantle_air" minThickness="35" maxThickness="45"><!-- add bedrock to prevent warping inside -->
<filler ratio="0.005" block="minecraft:bedrock" />
<filler ratio="0.005" block="minecraft:glowstone" />
<filler weight="1" block="minecraft:air" />
</shell>
<shell name="mantle_inner" minThickness="1" maxThickness="1"><!-- add light source and solid block to protect inner side -->
<filler weight="1" block="minecraft:glowstone" />
</shell>
<shell name="mantle_outer" minThickness="2" maxThickness="2">
<filler weight="1" block="minecraft:lava" />
</shell>
<shell name="surface" minThickness="1" maxThickness="1"><!-- add stained glass for color and holding fluid in place -->
<filler weight="1" block="minecraft:stained_glass" metadata="14" />
</shell>
</structure>
<structure group="star" name="yellow_giant">
<shell name="core" minThickness="1" maxThickness="1"><!-- mark the center -->
<filler weight="1" block="minecraft:obsidian" />
</shell>
<shell name="mantle_air" minThickness="55" maxThickness="70"><!-- add bedrock to prevent warping inside -->
<filler ratio="0.005" block="minecraft:bedrock" />
<filler ratio="0.005" block="minecraft:glowstone" />
<filler weight="1" block="minecraft:air" />
</shell>
<shell name="mantle_inner" minThickness="2" maxThickness="2"><!-- add light source and solid block to protect inner side -->
<filler weight="1" block="minecraft:glowstone" />
</shell>
<shell name="surface" minThickness="1" maxThickness="1"><!-- add stained glass for color -->
<filler weight="1" block="minecraft:stained_glass" metadata="1" />
</shell>
<shell name="low_altitude" minThickness="4" maxThickness="6">
<filler weight="1" block="minecraft:air" />
</shell>
<shell name="atmosphere" minThickness="1" maxThickness="1"><!-- add stained glass for color -->
<filler weight="1" block="minecraft:stained_glass" metadata="4" />
</shell>
</structure>
<structure group="star" name="yellow_supergiant">
<shell name="core" minThickness="1" maxThickness="1"><!-- mark the center -->
<filler weight="1" block="minecraft:obsidian" />
</shell>
<shell name="mantle_air" minThickness="70" maxThickness="90"><!-- add bedrock to prevent warping inside -->
<filler ratio="0.01" block="minecraft:bedrock" />
<filler weight="1" block="minecraft:air" />
</shell>
<shell name="mantle_inner" minThickness="2" maxThickness="2"><!-- add light source and solid block to protect inner side -->
<filler weight="1" block="minecraft:glowstone" />
</shell>
<shell name="surface" minThickness="1" maxThickness="1"><!-- add stained glass for color -->
<filler weight="1" block="minecraft:stained_glass" metadata="1" />
</shell>
<shell name="low_altitude" minThickness="4" maxThickness="6">
<filler weight="1" block="minecraft:air" />
</shell>
<shell name="atmosphere" minThickness="1" maxThickness="1"><!-- add stained glass for color -->
<filler weight="1" block="WarpDrive:blockGas" metadata="3" />
</shell>
</structure>
<structure group="moon" name="theMoon">
<shell name="core" minThickness="1" maxThickness="5" fillerSets="rareOres">
<filler weight="100" block="minecraft:iron_ore" />
@ -68,22 +133,24 @@
</shell>
</structure>
<structure group="asteroid" name="ClusteredAsteroid" weight="3" coreBlock="minecraft:iron_ore" maxCoreSize="10" minCoreSize="6" coreRad="0.5">
<shell name="mantle" minThickness="5" maxThickness="15" fillerSets="commonOres,uncommonOres">
<structure group="asteroid" name="ClusteredAsteroid" weight="3" coreBlock="minecraft:iron_ore" minCoreCount="6" maxCoreCount="10" relativeCoreRadius="0.5">
<shell name="mantle" minThickness="1" maxThickness="6" fillerSets="commonOres,uncommonOres">
<filler weight="10" block="minecraft:stone" />
<filler weight="5" block="minecraft:cobblestone" />
</shell>
</structure>
<structure group="asteroid" name="SphericalAsteroid" weight="10" coreBlock="minecraft:iron_ore" maxCoreSize="3" minCoreSize="1" coreRad="0.1">
<shell name="mantle" minThickness="5" maxThickness="10" fillerSets="commonOres,uncommonOres">
<structure group="asteroid" name="SphericalAsteroid" weight="10" coreBlock="minecraft:iron_ore" minCoreCount="1" maxCoreCount="3" relativeCoreRadius="0.1">
<shell name="mantle" minThickness="1" maxThickness="6" fillerSets="commonOres,uncommonOres">
<filler weight="10" block="minecraft:stone" />
<filler weight="5" block="minecraft:cobblestone" />
</shell>
</structure>
<structure group="asteroid" name="diamondGeode" weight="2" coreBlock="minecraft:diamond_ore" maxCoreSize="2" minCoreSize="1" coreRad="0.1">
<shell name="mantle" minThickness="5" maxThickness="10" fillerSets="commonOres">
<structure group="asteroid" name="diamondGeode" weight="2" coreBlock="minecraft:diamond_ore" minCoreCount="1" maxCoreCount="2" relativeCoreRadius="0.1">
<shell name="mantle" minThickness="1" maxThickness="6" fillerSets="commonOres">
<filler weight="10" block="minecraft:stone" />
<filler weight="5" block="minecraft:cobblestone" />
</shell>