diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/C.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/C.java index 7df62a6d0..517678835 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/C.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/C.java @@ -831,7 +831,11 @@ public enum C { HELP_FOOTER("$3&m---------&r $1Plot\u00B2 Help $3&m---------", "Help"), HELP_INFO_ITEM("$1/plot help %category% $3- $2%category_desc%", "Help"), HELP_ITEM( - "$1%usage% [%alias%]&- $3- $2%desc%&-", "Help"), /* + "$1%usage% [%alias%]&- $3- $2%desc%&-", "Help"), + + BUCKET_ENTRIES_IGNORED("$2Total bucket values add up to 1 or more. Blocks without a spcified chance will be ignored", "Generator_Bucket"), + + /* * Direction */ diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockBucket.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockBucket.java new file mode 100644 index 000000000..49d921ad8 --- /dev/null +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockBucket.java @@ -0,0 +1,154 @@ +package com.github.intellectualsites.plotsquared.plot.object; + +import com.github.intellectualsites.plotsquared.plot.config.C; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +/** + * A block bucket is a container of block types, where each block + * has a specified chance of being randomly picked + */ +@SuppressWarnings({"unused", "WeakerAccess"}) +public final class BlockBucket implements Iterable { + + private final Random random = new Random(); + private final Map ranges = new HashMap<>(); + private final Map blocks; + private final BucketIterator bucketIterator = new BucketIterator(); + private boolean compiled; + + public BlockBucket() { + this.blocks = new HashMap<>(); + } + + public void addBlock(@NonNull final PlotBlock block) { + this.addBlock(block, -1); + } + + public void addBlock(@NonNull final PlotBlock block, final int chance) { + this.blocks.put(block, chance); + this.compiled = false; + } + + public void compile() { + if (isCompiled()) { + return; + } + final Map temp = new HashMap<>(blocks.size()); + final List unassigned = new ArrayList<>(blocks.size()); + + int sum = 0; + for (final Map.Entry entry : blocks.entrySet()) { + if (entry.getValue() == -1) { + unassigned.add(entry.getKey()); + } else { + sum += entry.getValue(); + } + } + // + // If this doesn't amount to 100 add it up to exactly 100. + // + if (sum < 100) { + final int remaining = 100 - sum; + final int perUnassigned = remaining / unassigned.size(); + for (final PlotBlock block : unassigned) { + temp.put(block, perUnassigned); + sum += perUnassigned; + } + // Make sure there isn't a tiny difference remaining + if (sum < 100) { + final int difference = 100 - sum; + temp.put(unassigned.get(0), perUnassigned + difference); + sum = 100; + } + } else if (!unassigned.isEmpty()) { + C.BUCKET_ENTRIES_IGNORED.send(ConsolePlayer.getConsole()); + } + // + // If the sum adds up to more than 100, divide all values + // + if (sum > 100) { + final double ratio = 100D / sum; + for (final Map.Entry entry : blocks.entrySet()) { + if (entry.getValue() == -1) { + continue; + } + temp.put(entry.getKey(), (int)(entry.getValue() * ratio)); + } + } else { + temp.forEach(temp::put); + } + int start = 0; + for (final Map.Entry entry : temp.entrySet()) { + final int rangeStart = start; + final int rangeEnd = rangeStart + entry.getValue(); + start = rangeEnd + 1; + final Range range = new Range(rangeStart, rangeEnd); + this.ranges.put(range, entry.getKey()); + } + this.blocks.clear(); + this.compiled = true; + } + + @Override + public Iterator iterator() { + return this.bucketIterator; + } + + public boolean isCompiled() { + return this.compiled; + } + + /** + * Get a random block out of the bucket + * + * @return Randomly picked block (cased on specified rates) + */ + public PlotBlock getBlock() { + if (!isCompiled()) { + this.compile(); + } + final int number = random.nextInt(101); + for (final Map.Entry entry : ranges.entrySet()) { + if (entry.getKey().isInRange(number)) { + return entry.getValue(); + } + } + // Didn't find a block? Try again + return getBlock(); + } + + private final class BucketIterator implements Iterator { + + @Override + public boolean hasNext() { + return true; + } + + @Override + public PlotBlock next() { + return getBlock(); + } + } + + @Getter + @EqualsAndHashCode + @RequiredArgsConstructor + private final static class Range { + + private final int min; + private final int max; + + public boolean isInRange(final int num) { + return num <= max && num >= min; + } + } +} diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockWrapper.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockWrapper.java deleted file mode 100644 index b63de816a..000000000 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockWrapper.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.github.intellectualsites.plotsquared.plot.object; - -import java.util.Collections; -import java.util.Map; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.ToString; - -@ToString -@EqualsAndHashCode -@RequiredArgsConstructor -public final class BlockWrapper { - - @Getter - private final PlotBlock type; - private final Map blockStates; - - public T getState(@NonNull final Class stateType, @NonNull final Class valueType, @NonNull final T defaultValue) { - if (!blockStates.containsKey(stateType)) { - return defaultValue; - } - final Object rawValue = blockStates.get(stateType); - if (!rawValue.getClass().equals(valueType)) { - throw new ClassCastException(String.format("State type %s has a value of type %s but %s was requested", - stateType.getSimpleName(), rawValue.getClass().getSimpleName(), valueType.getSimpleName())); - } - return valueType.cast(rawValue); - } - - public void setState(@NonNull final Class stateType, @NonNull final T value) { - this.blockStates.put(stateType, value); - } - - public Map getAllStates() { - return Collections.unmodifiableMap(this.blockStates); - } - -} diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockWrapperFactory.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockWrapperFactory.java deleted file mode 100644 index 9e8ce873c..000000000 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockWrapperFactory.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.github.intellectualsites.plotsquared.plot.object; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -public final class BlockWrapperFactory { - - @RequiredArgsConstructor - public static final class StateEntry { - - @Getter - private final StateType stateType; - @Getter - private final ObjectType value; - - } - - public interface BlockStateDeserializer { - StateEntry deserialize(PlotBlock type, String serialized); - boolean isOfType(String serializedString); - } - - @FunctionalInterface - public interface BlockStateSerializer { - String serialize(PlotBlock type, StateEntry entry); - } - - @RequiredArgsConstructor(access = AccessLevel.PRIVATE) - public static final class StateSerializationMapping { - @Getter - private final StateType type; - @Getter - private final ObjectType objectType; - @Getter - private final BlockStateSerializer serializer; - @Getter - private final BlockStateDeserializer deserializer; - } - - private final Map stateSerializationMappings = new HashMap<>(); - - public StateSerializationMapping - addStateMapping(@NonNull final StateType type, @NonNull final ObjectType objectType, @NonNull final BlockStateSerializer serializer, @NonNull final BlockStateDeserializer deserializer) { - final StateSerializationMapping stateSerializationMapping = new StateSerializationMapping<>(type, objectType, serializer, deserializer); - this.stateSerializationMappings.put(type, stateSerializationMapping); - return stateSerializationMapping; - } - - public StateSerializationMapping - getStateMapping(@NonNull final StateType type) { - return this.stateSerializationMappings.get(type); - } - - public Collection serializeStates(@NonNull final BlockWrapper blockWrapper) { - final List serializedStates = new ArrayList<>(); - blockWrapper.getAllStates().entrySet().stream().map(entry -> new StateEntry(entry.getKey(), entry.getValue())) - .forEach(entry -> { - final StateSerializationMapping stateSerializationMapping = getStateMapping(entry.getStateType()); - final BlockStateSerializer blockStateSerializer = stateSerializationMapping.getSerializer(); - final String serialized = blockStateSerializer.serialize(blockWrapper.getType(), entry); - serializedStates.add(serialized); - }); - return serializedStates; - } - - public BlockStateDeserializer getDeserializerRaw(@NonNull final String serializedString) { - for (final StateSerializationMapping stateSerializationMapping : this.stateSerializationMappings.values()) { - if (stateSerializationMapping.getDeserializer().isOfType(serializedString)) { - return stateSerializationMapping.getDeserializer(); - } - } - return null; - } - - public Collection deserializeStates(@NonNull final PlotBlock plotBlock, @NonNull final Collection serializedStates) { - final Collection stateEntries = new ArrayList<>(serializedStates.size()); - for (final String serializedState : serializedStates) { - if (serializedState == null || serializedState.isEmpty()) { - continue; - } - final BlockStateDeserializer blockStateDeserializer = getDeserializerRaw(serializedState); - if (blockStateDeserializer == null) { - throw new IllegalStateException(String.format("No deserializer available for %s", serializedState)); - } - stateEntries.add(blockStateDeserializer.deserialize(plotBlock, serializedState)); - } - return stateEntries; - } - -}