diff --git a/src/main/java/cr0s/warpdrive/config/Filler.java b/src/main/java/cr0s/warpdrive/config/Filler.java index a9c1cb15..cdb011f9 100644 --- a/src/main/java/cr0s/warpdrive/config/Filler.java +++ b/src/main/java/cr0s/warpdrive/config/Filler.java @@ -7,6 +7,9 @@ import cr0s.warpdrive.data.JumpBlock; import javax.annotation.Nonnull; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import net.minecraft.block.state.IBlockState; import net.minecraft.util.math.BlockPos; import org.w3c.dom.Element; @@ -89,7 +92,55 @@ public class Filler implements IXmlRepresentableUnit { } } - name = nameBlock + "@" + metadata + "{" + tagCompound + "}"; + name = nameBlock + "@" + metadata + (tagCompound == null ? "" : "{" + tagCompound + "}"); + + return true; + } + + public boolean loadFromName(final String nameToLoad) { + final Pattern patternNameToLoadWithoutNBT = Pattern.compile("(.*)@(\\d*)"); + final Pattern patternNameToLoadWithNBT = Pattern.compile("(.*)@(\\d*)(\\{.*)"); + final boolean hasNBT = nameToLoad.contains("{"); + final Matcher matcher = hasNBT ? patternNameToLoadWithNBT.matcher(nameToLoad) : patternNameToLoadWithoutNBT.matcher(nameToLoad); + if (!matcher.matches()) { + throw new RuntimeException(String.format("Failed to load filler from name %s: unrecognized format", + nameToLoad)); + } + + final String nameBlock = matcher.group(1); + block = Block.getBlockFromName(nameBlock); + if (block == null) { + WarpDrive.logger.warn(String.format("Failed to load filler from name %s: block %s is missing", + nameToLoad, nameBlock)); + return false; + } + + // Get metadata attribute, defaults to 0 + metadata = 0; + final String stringMetadata = matcher.group(2); + if (!stringMetadata.isEmpty()) { + try { + metadata = Integer.parseInt(stringMetadata); + } catch (final NumberFormatException exception) { + throw new RuntimeException(String.format("Failed to load filler from name %s: invalid metadata %s", + nameToLoad, stringMetadata)); + } + } + + // Get nbt attribute, default to null/none + tagCompound = null; + final String stringNBT = hasNBT ? matcher.group(3) : ""; + if (!stringNBT.isEmpty()) { + try { + tagCompound = JsonToNBT.getTagFromJson(stringNBT); + } catch (final NBTException exception) { + WarpDrive.logger.error(exception.getMessage()); + throw new RuntimeException(String.format("Failed to load filler from name %s: invalid nbt %s", + nameToLoad, stringNBT)); + } + } + + name = nameBlock + "@" + metadata + (tagCompound == null ? "" : "{" + tagCompound + "}"); return true; } @@ -151,7 +202,7 @@ public class Filler implements IXmlRepresentableUnit { @Override public String toString() { - return "Filler(" + block.getTranslationKey() + "@" + metadata + ")"; + return "Filler(" + block.getRegistryName() + "@" + metadata + ")"; } @Override diff --git a/src/main/java/cr0s/warpdrive/config/GenericSet.java b/src/main/java/cr0s/warpdrive/config/GenericSet.java index ff911ba1..9300b829 100644 --- a/src/main/java/cr0s/warpdrive/config/GenericSet.java +++ b/src/main/java/cr0s/warpdrive/config/GenericSet.java @@ -3,6 +3,7 @@ package cr0s.warpdrive.config; import cr0s.warpdrive.WarpDrive; import cr0s.warpdrive.api.IXmlRepresentable; import cr0s.warpdrive.api.IXmlRepresentableUnit; + import org.w3c.dom.Element; import javax.annotation.Nonnull; @@ -11,6 +12,12 @@ import java.util.Collection; import java.util.List; import java.util.Random; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.nbt.NBTTagString; + +import net.minecraftforge.common.util.Constants.NBT; + /** * Represents a set of 'units' that will be chosen randomly during world generation. **/ @@ -44,6 +51,67 @@ public class GenericSet implements IXmlRepresen importGroups = new ArrayList<>(); } + public GenericSet(final NBTTagCompound tagCompound, final E unitDefault, final String nameElementUnit) { + if (tagCompound.hasKey("group")) { + group = tagCompound.getString("group"); + } else { + group = null; + } + name = tagCompound.getString("name"); + this.unitDefault = unitDefault; + this.nameElementUnit = nameElementUnit; + units = new XmlRandomCollection<>(); + units.loadFromNBT(tagCompound.getCompoundTag("units"), (String name) -> { + if (unitDefault instanceof Filler) { + final Filler filler = new Filler(); + if (filler.loadFromName(name)) { + return (E) filler; + } + } + return unitDefault; // TODO not implemented + }); + + final NBTTagList listImportGroupNames = tagCompound.getTagList("importGroupNames", NBT.TAG_STRING); + importGroupNames = new ArrayList<>(); + for (int indexImportGroupName = 0; indexImportGroupName < listImportGroupNames.tagCount(); indexImportGroupName++) { + final String importGroupName = listImportGroupNames.getStringTagAt(indexImportGroupName); + importGroupNames.add(importGroupName); + } + + final NBTTagList listImportGroups = tagCompound.getTagList("importGroups", NBT.TAG_STRING); + importGroups = new ArrayList<>(); + for (int indexImportGroup = 0; indexImportGroup < listImportGroups.tagCount(); indexImportGroup++) { + final String importGroup = listImportGroups.getStringTagAt(indexImportGroup); + importGroups.add(importGroup); + } + } + + public NBTTagCompound writeToNBT(final NBTTagCompound tagCompound) { + if (group != null) { + tagCompound.setString("group", group); + } + tagCompound.setString("name", name); + tagCompound.setTag("units", units.writeToNBT(new NBTTagCompound())); + + if (!importGroupNames.isEmpty()) { + final NBTTagList listImportGroupNames = new NBTTagList(); + for (final String importGroupName : importGroupNames) { + listImportGroupNames.appendTag(new NBTTagString(importGroupName)); + } + tagCompound.setTag("importGroupNames", listImportGroupNames); + } + + if (!importGroups.isEmpty()) { + final NBTTagList listImportGroups = new NBTTagList(); + for (final String importGroup : importGroups) { + listImportGroups.appendTag(new NBTTagString(importGroup)); + } + tagCompound.setTag("importGroups", listImportGroups); + } + + return tagCompound; + } + public boolean isEmpty() { return units.isEmpty(); } diff --git a/src/main/java/cr0s/warpdrive/config/RandomCollection.java b/src/main/java/cr0s/warpdrive/config/RandomCollection.java index d5fda7b1..5a09dce9 100644 --- a/src/main/java/cr0s/warpdrive/config/RandomCollection.java +++ b/src/main/java/cr0s/warpdrive/config/RandomCollection.java @@ -9,8 +9,13 @@ import java.util.NavigableMap; import java.util.Random; import java.util.TreeMap; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; import net.minecraft.util.IStringSerializable; +import net.minecraftforge.common.util.Constants.NBT; + /** * Collection of elements with ratios and weights. Helps to select element with controlled odds. * @@ -19,12 +24,65 @@ import net.minecraft.util.IStringSerializable; * @param **/ public class RandomCollection { + + public interface StringDeserializable { + E deserialize(final String name); + } + private final NavigableMap weightMap = new TreeMap<>(); private int totalWeight = 0; private final NavigableMap ratioMap = new TreeMap<>(); private double totalRatio = 0; private final ArrayList list = new ArrayList<>(); + public void loadFromNBT(final NBTTagCompound tagCompound, final StringDeserializable deserializer) { + final NBTTagList tagListWeights = tagCompound.getTagList("weights", NBT.TAG_COMPOUND); + for (final NBTBase tagBase : tagListWeights) { + final NBTTagCompound tagCompoundWeight = (NBTTagCompound) tagBase; + final int weight = tagCompoundWeight.getInteger("key"); + final String name = tagCompoundWeight.getString("name"); + final E object = deserializer.deserialize(name); + addWeight(weight, object); + } + + final NBTTagList tagListRatios = tagCompound.getTagList("ratios", NBT.TAG_COMPOUND); + for (final NBTBase tagBase : tagListRatios) { + final NBTTagCompound tagCompoundWeight = (NBTTagCompound) tagBase; + final double ratio = tagCompoundWeight.getDouble("key"); + final String name = tagCompoundWeight.getString("name"); + final E object = deserializer.deserialize(name); + addRatio(ratio, object); + } + } + + public NBTTagCompound writeToNBT(final NBTTagCompound tagCompound) { + final NBTTagList tagListWeights = new NBTTagList(); + int weightPrevious = 0; + for (final Entry entry : weightMap.entrySet()) { + final NBTTagCompound tagCompoundWeight = new NBTTagCompound(); + final int weightEntry = entry.getKey(); + tagCompoundWeight.setInteger("key", weightEntry - weightPrevious); + tagCompoundWeight.setString("name", entry.getValue().getName()); + tagListWeights.appendTag(tagCompoundWeight); + weightPrevious = weightEntry; + } + tagCompound.setTag("weights", tagListWeights); + + final NBTTagList tagListRatios = new NBTTagList(); + double ratioPrevious = 0.0D; + for (final Entry entry : ratioMap.entrySet()) { + final NBTTagCompound tagCompoundRatio = new NBTTagCompound(); + final double ratioEntry = entry.getKey(); + tagCompoundRatio.setDouble("key", ratioEntry - ratioPrevious); + tagCompoundRatio.setString("name", entry.getValue().getName()); + tagListRatios.appendTag(tagCompoundRatio); + ratioPrevious = ratioEntry; + } + tagCompound.setTag("ratios", tagListRatios); + + return tagCompound; + } + /** * Add new object and its weight. * @@ -132,17 +190,17 @@ public class RandomCollection { * @return Formatted string list separated by commas **/ public String getNames() { - String names = ""; if (list.isEmpty()) { return "-none defined-"; } + final StringBuilder names = new StringBuilder(); for (final E object : list) { - if (!names.isEmpty()) { - names += ", "; + if (names.length() > 0) { + names.append(", "); } - names += object.getName(); + names.append(object.getName()); } - return names; + return names.toString(); } /** diff --git a/src/main/java/cr0s/warpdrive/config/structures/AbstractStructureInstance.java b/src/main/java/cr0s/warpdrive/config/structures/AbstractStructureInstance.java index 4098182b..75908b3f 100644 --- a/src/main/java/cr0s/warpdrive/config/structures/AbstractStructureInstance.java +++ b/src/main/java/cr0s/warpdrive/config/structures/AbstractStructureInstance.java @@ -12,6 +12,7 @@ import net.minecraft.world.gen.feature.WorldGenerator; * */ public abstract class AbstractStructureInstance extends WorldGenerator { + protected AbstractStructure structure; protected HashMap variables = new HashMap<>(); @@ -52,28 +53,31 @@ public abstract class AbstractStructureInstance extends WorldGenerator { } public AbstractStructureInstance(final NBTTagCompound tagCompound) { - // FIXME to be implemented - - // get deployable - // String deployableName = tagCompound.getString("wd_structureName"); + // get structure + final String groupStructure = tagCompound.getString("group"); + final String nameStructure = tagCompound.getString("name"); + structure = StructureManager.getStructure(null, groupStructure, nameStructure); // get variables values - /* - final NBTTagCompound tagVariables = tagCompound.getCompoundTag("wd_variables"); - final NBTTagList names = tagVariables.getTagList("x", 0); - for (final Entry entry : tagVariables.getTagList("x", 0)) { - tagVariables.setDouble(entry.getKey(), entry.getValue()); + final NBTTagCompound tagVariables = tagCompound.getCompoundTag("variables"); + for (final String key : tagVariables.getKeySet()) { + final double value = tagVariables.getDouble(key); + variables.put(key, value); } - /**/ } - public void WriteToNBT(final NBTTagCompound tagCompound) { - tagCompound.setString("wd_structureGroup", structure.group); - tagCompound.setString("wd_structureName", structure.name); - final NBTTagCompound tagVariables = new NBTTagCompound(); - for (final Entry entry : variables.entrySet()) { - tagVariables.setDouble(entry.getKey(), entry.getValue()); + public NBTTagCompound writeToNBT(final NBTTagCompound tagCompound) { + tagCompound.setString("group", structure.group); + tagCompound.setString("name", structure.name); + + if (!variables.isEmpty()) { + final NBTTagCompound tagVariables = new NBTTagCompound(); + for (final Entry entry : variables.entrySet()) { + tagVariables.setDouble(entry.getKey(), entry.getValue()); + } + tagCompound.setTag("variables", tagVariables); } - tagCompound.setTag("wd_variables", tagVariables); + + return tagCompound; } } diff --git a/src/main/java/cr0s/warpdrive/config/structures/AsteroidFieldInstance.java b/src/main/java/cr0s/warpdrive/config/structures/AsteroidFieldInstance.java index 642d197f..2b0f895d 100644 --- a/src/main/java/cr0s/warpdrive/config/structures/AsteroidFieldInstance.java +++ b/src/main/java/cr0s/warpdrive/config/structures/AsteroidFieldInstance.java @@ -27,9 +27,10 @@ public class AsteroidFieldInstance extends AbstractStructureInstance { } @Override - public void WriteToNBT(final NBTTagCompound tagCompound) { - super.WriteToNBT(tagCompound); + public NBTTagCompound writeToNBT(final NBTTagCompound tagCompound) { + super.writeToNBT(tagCompound); // TODO not implemented + return tagCompound; } private static float binomialRandom(final World world) { diff --git a/src/main/java/cr0s/warpdrive/config/structures/MetaOrbInstance.java b/src/main/java/cr0s/warpdrive/config/structures/MetaOrbInstance.java index 9e9c9ef8..992a4074 100644 --- a/src/main/java/cr0s/warpdrive/config/structures/MetaOrbInstance.java +++ b/src/main/java/cr0s/warpdrive/config/structures/MetaOrbInstance.java @@ -3,9 +3,9 @@ package cr0s.warpdrive.config.structures; import cr0s.warpdrive.Commons; import cr0s.warpdrive.LocalProfiler; import cr0s.warpdrive.WarpDrive; +import cr0s.warpdrive.config.GenericSet; import cr0s.warpdrive.config.WarpDriveConfig; import cr0s.warpdrive.config.Filler; -import cr0s.warpdrive.config.structures.Orb.OrbShell; import cr0s.warpdrive.data.VectorI; import javax.annotation.Nonnull; @@ -41,8 +41,9 @@ public class MetaOrbInstance extends OrbInstance { final int y2 = Math.min(WarpDriveConfig.SPACE_GENERATOR_Y_MAX_BORDER - totalThickness - (int) metaShell.radius, Math.max(blockPos.getY(), WarpDriveConfig.SPACE_GENERATOR_Y_MIN_BORDER + totalThickness + (int) metaShell.radius)); + final BlockPos blockPosUpdated = y2 == blockPos.getY() ? blockPos : new BlockPos(blockPos.getX(), y2, blockPos.getZ()); if (((MetaOrb) structure).metaShell == null) { - return super.generate(world, random, new BlockPos(blockPos.getX(), y2, blockPos.getZ())); + return super.generate(world, random, blockPosUpdated); } // generate an abstract form for the core @@ -85,7 +86,7 @@ public class MetaOrbInstance extends OrbInstance { } private void addShell(final World world, final VectorI location, final int radius) { - final double sqRadius = radius * radius; + final int sqRadius = radius * radius; final BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(location.x, location.y, location.z); // iterate all blocks within cube with side 2 * radius for (int x = location.x - radius; x <= location.x + radius; x++) { @@ -99,8 +100,8 @@ public class MetaOrbInstance extends OrbInstance { mutableBlockPos.setPos(x, y, z); // if inside radius if (sqRange <= sqRadius && isReplaceableOreGen(world, mutableBlockPos)) { - final OrbShell shell = getShellForSqRadius(sqRange); - final Filler filler = shell.getRandomUnit(world.rand); + final GenericSet fillerSet = getFillerSetFromSquareRange(sqRange); + final Filler filler = fillerSet.getRandomUnit(world.rand); filler.setBlock(world, mutableBlockPos); } } diff --git a/src/main/java/cr0s/warpdrive/config/structures/Orb.java b/src/main/java/cr0s/warpdrive/config/structures/Orb.java index 22bfaf01..5e16cca2 100644 --- a/src/main/java/cr0s/warpdrive/config/structures/Orb.java +++ b/src/main/java/cr0s/warpdrive/config/structures/Orb.java @@ -71,13 +71,11 @@ public class Orb extends AbstractStructure { public class OrbShell extends GenericSet { - private final String parentFullName; protected int minThickness; protected int maxThickness; public OrbShell(final String parentFullName, final String name) { - super(null, name, Filler.DEFAULT, "filler"); - this.parentFullName = parentFullName; + super(parentFullName, name, Filler.DEFAULT, "filler"); } @Override @@ -94,7 +92,7 @@ public class Orb extends AbstractStructure { final GenericSet fillerSet = WarpDriveConfig.FillerManager.getGenericSet(importGroupName); if (fillerSet == null) { WarpDrive.logger.warn(String.format("Skipping missing FillerSet %s in shell %s:%s", - importGroupName, parentFullName, name)); + importGroupName, group, name)); } else { loadFrom(fillerSet); } @@ -104,7 +102,7 @@ public class Orb extends AbstractStructure { for (final String importGroup : getImportGroups()) { if (!WarpDriveConfig.FillerManager.doesGroupExist(importGroup)) { WarpDrive.logger.warn(String.format("An invalid FillerSet group %s is referenced in shell %s:%s", - importGroup, parentFullName, name)); + importGroup, group, name)); } } @@ -113,26 +111,26 @@ public class Orb extends AbstractStructure { minThickness = Integer.parseInt(element.getAttribute("minThickness")); } catch (final NumberFormatException exception) { throw new InvalidXmlException(String.format("Invalid minThickness in shell %s of structure %s", - name, parentFullName)); + name, group)); } try { maxThickness = Integer.parseInt(element.getAttribute("maxThickness")); } catch (final NumberFormatException exception) { throw new InvalidXmlException(String.format("Invalid maxThickness in shell %s of structure %s", - name, parentFullName)); + name, group)); } if (maxThickness < minThickness) { throw new InvalidXmlException(String.format("Invalid maxThickness %d lower than minThickness %s in shell %s of orb %s", - maxThickness, minThickness, name, parentFullName)); + maxThickness, minThickness, name, group)); } return true; } public OrbShell instantiate(final Random random) { - final OrbShell orbShell = new OrbShell(parentFullName, name); + final OrbShell orbShell = new OrbShell(group, name); orbShell.minThickness = minThickness; orbShell.maxThickness = maxThickness; try { @@ -141,24 +139,24 @@ public class Orb extends AbstractStructure { final GenericSet fillerSet = WarpDriveConfig.FillerManager.getRandomSetFromGroup(random, importGroup); if (fillerSet == null) { WarpDrive.logger.warn(String.format("Ignoring invalid group %s in shell %s of structure %s", - importGroup, name, parentFullName)); + importGroup, name, group)); continue; } if (WarpDriveConfig.LOGGING_WORLD_GENERATION) { WarpDrive.logger.info(String.format("Filling %s:%s with %s:%s", - parentFullName, name, importGroup, fillerSet.getName())); + group, name, importGroup, fillerSet.getName())); } orbShell.loadFrom(fillerSet); } } catch (final Exception exception) { exception.printStackTrace(); WarpDrive.logger.error(String.format("Failed to instantiate shell %s from structure %s", - name, parentFullName)); + name, group)); } if (orbShell.isEmpty()) { if (WarpDriveConfig.LOGGING_WORLD_GENERATION) { WarpDrive.logger.info(String.format("Ignoring empty shell %s in structure %s", - name, parentFullName)); + name, group)); } return null; } diff --git a/src/main/java/cr0s/warpdrive/config/structures/OrbInstance.java b/src/main/java/cr0s/warpdrive/config/structures/OrbInstance.java index 25875b07..145ae85c 100644 --- a/src/main/java/cr0s/warpdrive/config/structures/OrbInstance.java +++ b/src/main/java/cr0s/warpdrive/config/structures/OrbInstance.java @@ -1,6 +1,8 @@ package cr0s.warpdrive.config.structures; import cr0s.warpdrive.Commons; +import cr0s.warpdrive.config.Filler; +import cr0s.warpdrive.config.GenericSet; import cr0s.warpdrive.config.WarpDriveConfig; import cr0s.warpdrive.config.structures.Orb.OrbShell; import cr0s.warpdrive.world.EntitySphereGen; @@ -8,34 +10,41 @@ import cr0s.warpdrive.world.EntityStarCore; import cr0s.warpdrive.world.WorldGenSmallShip; import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Random; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import net.minecraftforge.common.util.Constants; + public class OrbInstance extends AbstractStructureInstance { - protected OrbShell[] orbShells; - protected int[] orbShellThicknesses; + protected ArrayList> orbShellInstances; + private int[] orbShellThicknesses; protected int totalThickness; protected int minThickness; protected String schematicName; // internal look-up table to accelerate computations - private OrbShell[] sqRadiusToOrbShell; + private ArrayList> sqRadiusToOrbShell; public OrbInstance(final Orb orb, final Random random) { super(orb, random); - orbShells = new OrbShell[orb.orbShells.length]; + + orbShellInstances = new ArrayList<>(orb.orbShells.length); orbShellThicknesses = new int[orb.orbShells.length]; totalThickness = 0; minThickness = 0; int orbShellIndexOut = 0; for (int orbShellIndexIn = 0; orbShellIndexIn < orb.orbShells.length; orbShellIndexIn++) { final OrbShell orbShell = orb.orbShells[orbShellIndexIn].instantiate(random); + // skip if it's an empty filler set if (orbShell != null) { - orbShells[orbShellIndexOut] = orbShell; + orbShellInstances.add(orbShell); final int thickness = Commons.randomRange(random, orbShell.minThickness, orbShell.maxThickness); orbShellThicknesses[orbShellIndexOut] = thickness; totalThickness += thickness; @@ -43,31 +52,84 @@ public class OrbInstance extends AbstractStructureInstance { orbShellIndexOut++; } } + // resize array in case we had one or more empty filler set + if (orbShellThicknesses.length != orbShellIndexOut) { + orbShellThicknesses = Arrays.copyOf(orbShellThicknesses, orbShellIndexOut); + } - sqRadiusToOrbShell = new OrbShell[totalThickness * totalThickness]; - for (int sqRadius = 0; sqRadius < sqRadiusToOrbShell.length; sqRadius++) { - int cumulatedRange = 0; - for (int shellIndex = 0; shellIndex < orbShells.length; shellIndex++) { - cumulatedRange += orbShellThicknesses[shellIndex]; - if (sqRadius <= cumulatedRange * cumulatedRange) { - sqRadiusToOrbShell[sqRadius] = orbShells[shellIndex]; + schematicName = orb.schematicName; + + constructionFinalizer(); + } + + private void constructionFinalizer() { + final int sqRadius = totalThickness * totalThickness; + sqRadiusToOrbShell = new ArrayList<>(sqRadius); + for (int sqRange = 0; sqRange < sqRadius; sqRange++) {// FIXME should we loop the orb shells instead of the range here? + int range = 0; + for (int indexShell = 0; indexShell < orbShellInstances.size(); indexShell++) { + range += orbShellThicknesses[indexShell]; + if (sqRange <= range * range) { + sqRadiusToOrbShell.add(orbShellInstances.get(indexShell)); break; } } } - - schematicName = orb.schematicName; } public OrbInstance(final NBTTagCompound tagCompound) { super(tagCompound); - // TODO not implemented + + final NBTTagList listOrbShells = tagCompound.getTagList("orbShellInstances", Constants.NBT.TAG_COMPOUND); + if (listOrbShells.isEmpty()) { + throw new RuntimeException(String.format("Empty orbShellInstances list isn't supported: %s", + tagCompound)); + } + orbShellInstances = new ArrayList<>(listOrbShells.tagCount()); + for (int indexOrbShell = 0; indexOrbShell < listOrbShells.tagCount(); indexOrbShell++) { + final NBTTagCompound tagCompoundOrbShell = listOrbShells.getCompoundTagAt(indexOrbShell); + final GenericSet orbShell = new GenericSet<>(tagCompoundOrbShell, Filler.DEFAULT, "filler"); + orbShellInstances.add(orbShell); + } + orbShellThicknesses = tagCompound.getIntArray("orbShellThicknesses"); + if (orbShellInstances.size() != orbShellThicknesses.length) { + throw new RuntimeException(String.format("Inconsistent orbShell and thicknesses sizes: %d != %d\n%s", + orbShellInstances.size(), orbShellThicknesses.length, tagCompound)); + } + totalThickness = tagCompound.getInteger("totalThickness"); + minThickness = tagCompound.getInteger("minThickness"); + + if (tagCompound.hasKey("schematicName")) { + schematicName = tagCompound.getString("schematicName"); + } else { + schematicName = null; + } + + constructionFinalizer(); } @Override - public void WriteToNBT(final NBTTagCompound tagCompound) { - super.WriteToNBT(tagCompound); - // TODO not implemented + public NBTTagCompound writeToNBT(final NBTTagCompound tagCompound) { + super.writeToNBT(tagCompound); + + final NBTTagList listOrbShells = new NBTTagList(); + if (orbShellInstances.isEmpty()) { + throw new RuntimeException(String.format("Empty orbShellInstances list isn't supported in %s", + this)); + } + for (final GenericSet orbShellInstance : orbShellInstances) { + final NBTTagCompound tagCompoundOrbShell = orbShellInstance.writeToNBT(new NBTTagCompound()); + listOrbShells.appendTag(tagCompoundOrbShell); + } + tagCompound.setTag("orbShellInstances", listOrbShells); + tagCompound.setIntArray("orbShellThicknesses", orbShellThicknesses); + tagCompound.setInteger("totalThickness", totalThickness); + tagCompound.setInteger("minThickness", minThickness); + if (schematicName != null) { + tagCompound.setString("schematicName", schematicName); + } + + return tagCompound; } public int getTotalThickness() { @@ -79,8 +141,9 @@ public class OrbInstance extends AbstractStructureInstance { final boolean hasShip = schematicName != null && !schematicName.isEmpty(); final int y2 = Math.min(WarpDriveConfig.SPACE_GENERATOR_Y_MAX_BORDER - totalThickness, Math.max(blockPos.getY(), WarpDriveConfig.SPACE_GENERATOR_Y_MIN_BORDER + totalThickness)); + final BlockPos blockPosUpdated = y2 == blockPos.getY() ? blockPos : new BlockPos(blockPos.getX(), y2, blockPos.getZ()); if (hasShip) { - new WorldGenSmallShip(random.nextFloat() < 0.2F, false).generate(world, random, new BlockPos(blockPos.getX(), y2, blockPos.getZ())); + new WorldGenSmallShip(random.nextFloat() < 0.2F, false).generate(world, random, blockPosUpdated); } final EntitySphereGen entitySphereGen = new EntitySphereGen(world, blockPos.getX(), y2, blockPos.getZ(), this, !hasShip); world.spawnEntity(entitySphereGen); @@ -90,12 +153,11 @@ public class OrbInstance extends AbstractStructureInstance { return true; } - public OrbShell getShellForSqRadius(final double sqRadius) { - final int intSqRadius = (int) Math.round(sqRadius); - if (intSqRadius < sqRadiusToOrbShell.length) { - return sqRadiusToOrbShell[intSqRadius]; + public GenericSet getFillerSetFromSquareRange(final int sqRadius) { + if (sqRadius < sqRadiusToOrbShell.size()) { + return sqRadiusToOrbShell.get(sqRadius); } else { - return sqRadiusToOrbShell[sqRadiusToOrbShell.length - 1]; + return sqRadiusToOrbShell.get(sqRadiusToOrbShell.size() - 1); } } } diff --git a/src/main/java/cr0s/warpdrive/config/structures/SchematicInstance.java b/src/main/java/cr0s/warpdrive/config/structures/SchematicInstance.java index 30bab650..e33dc56c 100644 --- a/src/main/java/cr0s/warpdrive/config/structures/SchematicInstance.java +++ b/src/main/java/cr0s/warpdrive/config/structures/SchematicInstance.java @@ -60,9 +60,10 @@ public class SchematicInstance extends AbstractStructureInstance { } @Override - public void WriteToNBT(final NBTTagCompound tagCompound) { - super.WriteToNBT(tagCompound); + public NBTTagCompound writeToNBT(final NBTTagCompound tagCompound) { + super.writeToNBT(tagCompound); // TODO not implemented + return tagCompound; } @Override diff --git a/src/main/java/cr0s/warpdrive/event/CommonWorldGenerator.java b/src/main/java/cr0s/warpdrive/event/CommonWorldGenerator.java index 69c4da9c..c38c4f24 100644 --- a/src/main/java/cr0s/warpdrive/event/CommonWorldGenerator.java +++ b/src/main/java/cr0s/warpdrive/event/CommonWorldGenerator.java @@ -1,9 +1,9 @@ package cr0s.warpdrive.event; +import cr0s.warpdrive.config.GenericSet; import cr0s.warpdrive.data.CelestialObjectManager; import cr0s.warpdrive.config.WarpDriveConfig; import cr0s.warpdrive.config.Filler; -import cr0s.warpdrive.config.structures.Orb.OrbShell; import cr0s.warpdrive.config.structures.OrbInstance; import cr0s.warpdrive.config.structures.StructureGroup; import cr0s.warpdrive.data.CelestialObject; @@ -58,10 +58,10 @@ public class CommonWorldGenerator implements IWorldGenerator { @Deprecated public static void generateSphereDirect( final OrbInstance orbInstance, final World world, final int xCoord, final int yCoord, final int zCoord) { - final double radiusC = orbInstance.getTotalThickness() + 0.5D; // Radius from center of block - final double radiusSq = radiusC * radiusC; // Optimization to avoid square roots... + final double dRadius = orbInstance.getTotalThickness() + 0.5D; // Radius from center of block + final double dSqRadius = dRadius * dRadius; // Optimization to avoid square roots... // sphere - final int ceilRadius = (int) Math.ceil(radiusC); + final int ceilRadius = (int) Math.ceil(dRadius); // Pass the cube and check points for sphere equation x^2 + y^2 + z^2 = r^2 final BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(xCoord, yCoord, zCoord); @@ -71,18 +71,18 @@ public class CommonWorldGenerator implements IWorldGenerator { final double dX2Y2 = dX2 + (y + 0.5D) * (y + 0.5D); for (int z = 0; z <= ceilRadius; z++) { final double dZ2 = (z + 0.5D) * (z + 0.5D); - final double dSq = dX2Y2 + dZ2; // squared distance from current position + final double dSqRange = dX2Y2 + dZ2; // squared distance from current position // Skip too far blocks - if (dSq > radiusSq) { + if (dSqRange > dSqRadius) { continue; } // Place blocks // cheat by using axial symmetry so we don't create random numbers too frequently - - final OrbShell orbShell = orbInstance.getShellForSqRadius(dSq); - final Filler filler = orbShell.getRandomUnit(world.rand); + final int intSqRange = (int) Math.round(dSqRange); + final GenericSet fillerSet = orbInstance.getFillerSetFromSquareRange(intSqRange); + final Filler filler = fillerSet.getRandomUnit(world.rand); filler.setBlock(world, mutableBlockPos.setPos(xCoord + x, yCoord + y, zCoord + z)); filler.setBlock(world, mutableBlockPos.setPos(xCoord - x, yCoord + y, zCoord + z)); filler.setBlock(world, mutableBlockPos.setPos(xCoord + x, yCoord - y, zCoord + z)); diff --git a/src/main/java/cr0s/warpdrive/world/EntitySphereGen.java b/src/main/java/cr0s/warpdrive/world/EntitySphereGen.java index 3cff9f23..d061ba3c 100644 --- a/src/main/java/cr0s/warpdrive/world/EntitySphereGen.java +++ b/src/main/java/cr0s/warpdrive/world/EntitySphereGen.java @@ -2,7 +2,8 @@ package cr0s.warpdrive.world; import cr0s.warpdrive.LocalProfiler; import cr0s.warpdrive.WarpDrive; -import cr0s.warpdrive.config.structures.Orb.OrbShell; +import cr0s.warpdrive.config.Filler; +import cr0s.warpdrive.config.GenericSet; import cr0s.warpdrive.config.structures.OrbInstance; import cr0s.warpdrive.data.JumpBlock; @@ -88,14 +89,9 @@ public final class EntitySphereGen extends Entity { this.posZ = z; this.orbInstance = orbInstance; this.gasColor = world.rand.nextInt(12); - this.radius = orbInstance.getTotalThickness(); - - this.state = STATE_SAVING; - this.pregenSize = (int) Math.ceil(Math.PI * 4.0F / 3.0F * Math.pow(radius + 1, 3)); - blocks = new ArrayList<>(this.pregenSize); - isSurfaces = new ArrayList<>(this.pregenSize); - this.ticksDelay = world.rand.nextInt(60); this.replace = replace; + + constructionFinalizer(); } public void killEntity() { @@ -191,17 +187,20 @@ public final class EntitySphereGen extends Entity { for (int y = 0; y <= ceilRadius; y++) { final double x2y2 = x2 + (y + 0.5D) * (y + 0.5D); for (int z = 0; z <= ceilRadius; z++) { - final double sqRange = x2y2 + (z + 0.5D) * (z + 0.5D); // Square distance from current position to center + final double dSqRange = x2y2 + (z + 0.5D) * (z + 0.5D); // Square distance from current position to center // Skip too far blocks - if (sqRange > sqRadiusHigh) { + if (dSqRange > sqRadiusHigh) { continue; } - final boolean isSurface = sqRange > sqRadiusLow; + final boolean isSurface = dSqRange > sqRadiusLow; // Add blocks to memory - final OrbShell orbShell = orbInstance.getShellForSqRadius(sqRange); - // WarpDrive.logger.info(String.format("sqRange %d sqRadius %d", sqRange, sqRadius)); + final int intSqRadius = (int) Math.round(dSqRange); + final GenericSet orbShell = orbInstance.getFillerSetFromSquareRange(intSqRadius); + + // WarpDrive.logger.info(String.format("dSqRange %.3f sqRadiusHigh %.3f %.3f", + // dSqRange, sqRadiusHigh, sqRadiusLow)); addBlock(isSurface, new JumpBlock(orbShell.getRandomUnit(rand), xCoord + x, yCoord + y, zCoord + z)); if (x != 0) { addBlock(isSurface, new JumpBlock(orbShell.getRandomUnit(rand), xCoord - x, yCoord + y, zCoord + z)); @@ -257,13 +256,42 @@ public final class EntitySphereGen extends Entity { } @Override - protected void readEntityFromNBT(@Nonnull final NBTTagCompound tagCompound) { - // FIXME not implemented + protected void entityInit() { + noClip = true; + } + + private void constructionFinalizer() { + radius = orbInstance.getTotalThickness(); + pregenSize = (int) Math.ceil(Math.PI * 4.0F / 3.0F * Math.pow(radius + 1, 3)); + blocks = new ArrayList<>(this.pregenSize); + isSurfaces = new ArrayList<>(this.pregenSize); + + state = STATE_SAVING; + ticksDelay = world.rand.nextInt(60); } @Override - protected void entityInit() { - noClip = true; + public void readEntityFromNBT(@Nonnull final NBTTagCompound tagCompound) { + xCoord = tagCompound.getInteger("warpdrive:xCoord"); + yCoord = tagCompound.getInteger("warpdrive:yCoord"); + zCoord = tagCompound.getInteger("warpdrive:zCoord"); + orbInstance = new OrbInstance(tagCompound.getCompoundTag("warpdrive:orbInstance")); + gasColor = tagCompound.getInteger("warpdrive:gasColor"); + replace = tagCompound.getBoolean("warpdrive:replace"); + + constructionFinalizer(); + WarpDrive.logger.info(String.format("%s Reloaded from NBT", + this)); + } + + @Override + public void writeEntityToNBT(final NBTTagCompound tagCompound) { + tagCompound.setInteger("warpdrive:xCoord", xCoord); + tagCompound.setInteger("warpdrive:yCoord", yCoord); + tagCompound.setInteger("warpdrive:zCoord", zCoord); + tagCompound.setTag("warpdrive:orbInstance", orbInstance.writeToNBT(new NBTTagCompound())); + tagCompound.setInteger("warpdrive:gasColor", gasColor); + tagCompound.setBoolean("warpdrive:replace", replace); } // override to skip the block bounding override on client side @@ -274,11 +302,6 @@ public final class EntitySphereGen extends Entity { this.setRotation(yaw, pitch); } - @Override - protected void writeEntityToNBT(@Nonnull final NBTTagCompound tagCompound) { - // FIXME not implemented - } - @Override public boolean shouldRenderInPass(final int pass) { return false;