package mcjty.rftools.dimension.description; import mcjty.lib.varia.Logging; import mcjty.rftools.blocks.dimlets.DimletConfiguration; import mcjty.rftools.blocks.teleporter.TeleportConfiguration; import mcjty.rftools.items.dimlets.*; import net.minecraft.nbt.NBTTagCompound; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * A unique descriptor of a dimension. */ public class DimensionDescriptor { private final String descriptionString; private final int rfCreateCost; private final int rfMaintainCost; private final int tickCost; private final long forcedSeed; public DimensionDescriptor(List<DimletKey> descriptors, long forcedSeed) { this.forcedSeed = forcedSeed; StringBuilder s = new StringBuilder(); // List of all non-modifier dimlets with all associated modifiers. List<Pair<DimletKey,List<DimletKey>>> dimlets = new ArrayList<Pair<DimletKey, List<DimletKey>>>(); // A list of all current modifier that haven't been fitted into a type yet. List<DimletKey> currentModifiers = new ArrayList<DimletKey>(); groupDimletsAndModifiers(descriptors, dimlets, currentModifiers); constructDescriptionStringNew(s, dimlets, currentModifiers); descriptionString = s.toString(); tickCost = calculateTickCost(dimlets); rfCreateCost = calculateCreationRfCost(dimlets, currentModifiers, tickCost); int rf = calculateMaintenanceRfCost(dimlets); int rfGain = calculateBonus(dimlets); if (rfGain > 0) { int rfMinimum = Math.max(10, rf * DimletConfiguration.minimumCostPercentage / 100); rf = rf - (rf * rfGain / 100); if (rf < rfMinimum) { rf = rfMinimum; // Never consume less then this } } rfMaintainCost = rf; } private void constructDescriptionStringNew(StringBuilder s, List<Pair<DimletKey,List<DimletKey>>> dimlets, List<DimletKey> currentModifiers) { s.append('@'); boolean first = true; for (Pair<DimletKey, List<DimletKey>> dimletWithModifiers : dimlets) { DimletKey key = dimletWithModifiers.getLeft(); List<DimletKey> mods = dimletWithModifiers.getRight(); if (mods != null) { for (DimletKey modifier : mods) { if (!first) { s.append(','); } first = false; s.append('#').append(modifier); } } if (!first) { s.append(','); } first = false; s.append(key); } // Now add all unused modifiers to the end. for (DimletKey modifier : currentModifiers) { if (s.length() > 0) { s.append(','); } s.append('?').append(modifier); } } private void groupDimletsAndModifiers(List<DimletKey> descriptors, List<Pair<DimletKey,List<DimletKey>>> dimlets, List<DimletKey> currentModifiers) { for (DimletKey key : descriptors) { DimletType type = key.getType(); if (type.dimletType.isModifier()) { // Keep the modifier here until we find a dimlet for which it fits. currentModifiers.add(key); } else { List<DimletKey> modifiers = new ArrayList<DimletKey>(); if (!currentModifiers.isEmpty()) { // Check if we collected modifiers that fit with this type. List<DimletKey> copy = new ArrayList<DimletKey>(currentModifiers); // Iterate over a copy so that we can delete from original list. for (DimletKey modifier : copy) { if (type.dimletType.isModifiedBy(modifier.getType())) { modifiers.add(modifier); currentModifiers.remove(modifier); } } } dimlets.add(Pair.of(key, modifiers)); } } } public DimensionDescriptor(NBTTagCompound tagCompound) { String ds = tagCompound.getString("descriptionString"); if (ds.startsWith("@")) { // New style already. descriptionString = ds; } else { // We need to convert. List<DimletKey> dimletDescriptors = parseOldDescriptionString(ds); List<Pair<DimletKey,List<DimletKey>>> dimlets = new ArrayList<Pair<DimletKey, List<DimletKey>>>(); // A list of all current modifier that haven't been fitted into a type yet. List<DimletKey> currentModifiers = new ArrayList<DimletKey>(); groupDimletsAndModifiers(dimletDescriptors, dimlets, currentModifiers); StringBuilder s = new StringBuilder(); constructDescriptionStringNew(s, dimlets, currentModifiers); Logging.log("Converting dimension descriptor from: " + ds + " to: " + s); descriptionString = s.toString(); } rfCreateCost = tagCompound.getInteger("rfCreateCost"); rfMaintainCost = tagCompound.getInteger("rfMaintainCost"); tickCost = tagCompound.getInteger("tickCost"); forcedSeed = tagCompound.getLong("forcedSeed"); } public List<Pair<DimletKey,List<DimletKey>>> getDimletsWithModifiers() { List<Pair<DimletKey,List<DimletKey>>> result = new ArrayList<Pair<DimletKey, List<DimletKey>>>(); String ds = descriptionString; if (ds.startsWith("@")) { ds = ds.substring(1); } if (!ds.isEmpty()) { List<DimletKey> modifiers = new ArrayList<DimletKey>(); String[] opcodes = StringUtils.split(ds, ","); for (String oc : opcodes) { DimletKey key; if (oc.startsWith("#")) { // First comes '#', then the type of the actual dimlet. key = DimletKey.parseKey(oc.substring(1)); modifiers.add(key); } else if (oc.startsWith("?")) { } else { key = DimletKey.parseKey(oc); result.add(Pair.of(key, modifiers)); modifiers = new ArrayList<DimletKey>(); } } } return result; } public static List<DimletKey> parseDescriptionString(String descriptionString) { if (descriptionString.startsWith("@")) { return parseNewDescriptionString(descriptionString); } else { return parseOldDescriptionString(descriptionString); } } private static List<DimletKey> parseNewDescriptionString(String descriptionString) { List<DimletKey> result = new ArrayList<DimletKey>(); String ds = descriptionString.substring(1); if (!ds.isEmpty()) { String[] opcodes = StringUtils.split(ds, ","); for (String oc : opcodes) { DimletKey key; if (oc.startsWith("#")) { // First comes '#', then the type of the actual dimlet. key = DimletKey.parseKey(oc.substring(1)); } else if (oc.startsWith("?")) { // First comes '?', then the type of the actual dimlet key = DimletKey.parseKey(oc.substring(1)); } else { key = DimletKey.parseKey(oc); } result.add(key); } } return result; } public static List<DimletKey> parseOldDescriptionString(String descriptionString) { DimletMapping mapping = DimletMapping.getInstance(); List<DimletKey> result = new ArrayList<DimletKey>(); if (!descriptionString.isEmpty()) { String[] opcodes = StringUtils.split(descriptionString, ","); for (String oc : opcodes) { Integer id; if (oc.startsWith("#")) { // First comes '#', then the type of the actual dimlet. id = Integer.parseInt(oc.substring(2)); } else if (oc.startsWith("?")) { // First comes '?', then the type of the actual dimlet. id = Integer.parseInt(oc.substring(2)); } else { id = Integer.parseInt(oc.substring(1)); } result.add(mapping.getKey(id)); } } return result; } public long calculateSeed(long seed) { DimletMapping mapping = DimletMapping.getInstance(); List<DimletKey> dimletDescriptors = parseDescriptionString(descriptionString); for (DimletKey key : dimletDescriptors) { seed = 31 * seed + mapping.getId(key); } return seed; } public String getDescriptionString() { return descriptionString; } public int getRfCreateCost() { return rfCreateCost; } public int getRfMaintainCost() { return rfMaintainCost; } public int getTickCost() { return tickCost; } public long getForcedSeed() { return forcedSeed; } public void writeToNBT(NBTTagCompound tagCompound) { tagCompound.setString("descriptionString", descriptionString); tagCompound.setInteger("rfCreateCost", rfCreateCost); tagCompound.setInteger("rfMaintainCost", rfMaintainCost); tagCompound.setInteger("tickCost", tickCost); tagCompound.setInteger("ticksLeft", tickCost); tagCompound.setLong("forcedSeed", forcedSeed); } private int getModifierMultiplier(Map<Pair<DimletType,DimletType>,Integer> modifierMap, DimletType type1, DimletType type2) { Integer multiplier = modifierMap.get(Pair.of(type1, type2)); if (multiplier == null) { return 1; } return multiplier; } private int getCreationCost(DimletType type, DimletKey key) { int cost = 0; DimletEntry entry = KnownDimletConfiguration.getEntry(key); if (entry != null) { cost = entry.getRfCreateCost(); if (cost == -1) { cost = type.dimletType.getCreationCost(); } } return cost; } private int calculateCreationRfCost(List<Pair<DimletKey,List<DimletKey>>> dimlets, List<DimletKey> unusedModifiers, int tickCost) { int rf = DimletCosts.baseDimensionCreationCost; for (Pair<DimletKey, List<DimletKey>> dimletWithModifier : dimlets) { DimletKey key = dimletWithModifier.getLeft(); DimletType type = key.getType(); List<DimletKey> list = dimletWithModifier.getRight(); if (list != null) { for (DimletKey modifier : list) { float mult = type.dimletType.getModifierCreateCostFactor(modifier.getType(), key); rf += (int) (getCreationCost(modifier.getType(), modifier) * mult); } } rf += getCreationCost(type, key); } for (DimletKey modifier : unusedModifiers) { rf += getCreationCost(modifier.getType(), modifier); } // Compensate createCost for the cost to fill the matter receiver at the destination end. rf += TeleportConfiguration.RECEIVER_MAXENERGY / tickCost; return rf; } private int getMaintenanceCost(DimletType type, DimletKey key) { int cost = 0; DimletEntry entry = KnownDimletConfiguration.getEntry(key); if (entry != null) { cost = entry.getRfMaintainCost(); if (cost == -1) { cost = type.dimletType.getMaintenanceCost(); } } return cost; } // Calculate the cost of this dimension without taking bonus dimlets into account. public int calculateNominalCost() { List<DimletKey> dimletKeys = parseDescriptionString(descriptionString); // List of all non-modifier dimlets with all associated modifiers. List<Pair<DimletKey,List<DimletKey>>> dimlets = new ArrayList<Pair<DimletKey, List<DimletKey>>>(); // A list of all current modifier that haven't been fitted into a type yet. List<DimletKey> currentModifiers = new ArrayList<DimletKey>(); groupDimletsAndModifiers(dimletKeys, dimlets, currentModifiers); return calculateMaintenanceRfCost(dimlets); } // Calculate the maintenance cost of a dimension without bonus dimlets. private int calculateMaintenanceRfCost(List<Pair<DimletKey,List<DimletKey>>> dimlets) { int rf = DimletCosts.baseDimensionMaintenanceCost; for (Pair<DimletKey, List<DimletKey>> dimletWithModifier : dimlets) { DimletKey key = dimletWithModifier.getLeft(); DimletType type = key.getType(); List<DimletKey> list = dimletWithModifier.getRight(); if (list != null) { for (DimletKey modifier : list) { float mult = type.dimletType.getModifierMaintainCostFactor(modifier.getType(), key); rf += (int) (getMaintenanceCost(modifier.getType(), modifier) * mult); } } int c = getMaintenanceCost(type, key); if (c > 0) { rf += c; } } return rf; } private int calculateBonus(List<Pair<DimletKey,List<DimletKey>>> dimlets) { int rfGain = 0; for (Pair<DimletKey, List<DimletKey>> dimletWithModifier : dimlets) { DimletKey key = dimletWithModifier.getLeft(); DimletType type = key.getType(); int c = getMaintenanceCost(type, key); if (c < 0) { rfGain -= c; // This dimlet gives a bonus in cost. This value is a percentage. } } return rfGain; } private int getTickCost(DimletType type, DimletKey key) { int cost = 0; DimletEntry entry = KnownDimletConfiguration.getEntry(key); if (entry != null) { cost = entry.getTickCost(); if (cost == -1) { cost = type.dimletType.getTickCost(); } } return cost; } private int calculateTickCost(List<Pair<DimletKey,List<DimletKey>>> dimlets) { int ticks = DimletCosts.baseDimensionTickCost; for (Pair<DimletKey, List<DimletKey>> dimletWithModifier : dimlets) { DimletKey key = dimletWithModifier.getLeft(); DimletType type = key.getType(); List<DimletKey> list = dimletWithModifier.getRight(); if (list != null) { for (DimletKey modifier : list) { float mult = type.dimletType.getModifierTickCostFactor(modifier.getType(), key); ticks += (int) (getTickCost(modifier.getType(), modifier) * mult); } } ticks += getTickCost(type, key); } return ticks; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DimensionDescriptor that = (DimensionDescriptor) o; if (!descriptionString.equals(that.descriptionString)) return false; return true; } @Override public int hashCode() { return descriptionString.hashCode(); } }