com.yungnickyoung.minecraft.yungsapi.api.world.randomize.BlockStateRandomizer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of YungsApi-1.21-Fabric Show documentation
Show all versions of YungsApi-1.21-Fabric Show documentation
A common API for YUNG's Minecraft mods
The newest version!
package com.yungnickyoung.minecraft.yungsapi.api.world.randomize;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.yungnickyoung.minecraft.yungsapi.YungsApiCommon;
import com.yungnickyoung.minecraft.yungsapi.world.structure.context.StructureContext;
import com.yungnickyoung.minecraft.yungsapi.world.structure.condition.StructureCondition;
import com.yungnickyoung.minecraft.yungsapi.world.structure.condition.StructureConditionType;
import java.util.*;
import net.minecraft.class_156;
import net.minecraft.class_2246;
import net.minecraft.class_2248;
import net.minecraft.class_2487;
import net.minecraft.class_2499;
import net.minecraft.class_2680;
import net.minecraft.class_5819;
/**
* Describes a set of BlockStates and the probability of each BlockState in the set being chosen.
* Useful for easily adding random variation to worldgen, especially structures and features.
*/
public class BlockStateRandomizer {
public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance
.group(
Entry.CODEC.listOf().fieldOf("entries").forGetter((randomizer) -> randomizer.entries),
class_2680.field_24734.fieldOf("defaultBlockState").forGetter((randomizer) -> randomizer.defaultBlockState))
.apply(instance, BlockStateRandomizer::new));
/**
* Map of BlockState to its corresponding probability.
* The total sum of all the probabilities should not exceed 1.
*/
private List entries = new ArrayList<>();
/**
* The default BlockState is used for any leftover probability ranges.
* For example, if the total sum of all the probabilities of the entries is 0.6, then
* there is a 0.4 chance of the defaultBlockState being selected.
*/
private class_2680 defaultBlockState = class_2246.field_10124.method_9564();
/**
* Saves this BlockStateRandomizer to a new CompoundTag.
* @return The CompoundTag
*/
public class_2487 saveTag() {
class_2487 compoundTag = new class_2487();
// Save default blockstate
compoundTag.method_10569("defaultBlockStateId", class_2248.field_10651.method_10206(this.defaultBlockState));
// Save entries
class_2499 entriesTag = class_156.method_654(new class_2499(), (tag) -> {
this.entries.forEach((entry) -> {
class_2487 entryTag = new class_2487();
entryTag.method_10569("entryBlockStateId", class_2248.field_10651.method_10206(entry.blockState));
entryTag.method_10548("entryChance", entry.probability);
tag.add(entryTag);
});
});
compoundTag.method_10566("entries", entriesTag);
return compoundTag;
}
/**
* Constructs a new BlockStateRandomizer from a CompoundTag.
* @param compoundTag The CompoundTag
*/
public BlockStateRandomizer(class_2487 compoundTag) {
this.defaultBlockState = class_2248.field_10651.method_10200(compoundTag.method_10550("defaultBlockStateId"));
this.entries = new ArrayList<>();
class_2499 entriesTag = compoundTag.method_10554("entries", 10);
entriesTag.forEach(entryTag -> {
class_2487 entryCompoundTag = ((class_2487) entryTag);
class_2680 blockState = class_2248.field_10651.method_10200(entryCompoundTag.method_10550("entryBlockStateId"));
float chance = entryCompoundTag.method_10583("entryChance");
this.addBlock(blockState, chance);
});
}
/**
* Constructs a new BlockStateRandomizer from a Map of BlockStates to their corresponding probabilities.
* @param entries Map of BlockStates to their corresponding probabilities
* @param defaultBlockState The default BlockState
*/
public BlockStateRandomizer(Map entries, class_2680 defaultBlockState) {
this.entries = new ArrayList<>();
entries.forEach(this::addBlock);
this.defaultBlockState = defaultBlockState;
}
/**
* Constructs a new BlockStateRandomizer from a List of BlockStateRandomizer Entries.
* @param entries List of BlockStateRandomizer Entries
* @param defaultBlockState The default BlockState
*/
public BlockStateRandomizer(List entries, class_2680 defaultBlockState) {
this.entries = entries;
this.defaultBlockState = defaultBlockState;
}
/**
* Constructs a new BlockStateRandomizer with only a default BlockState.
* @param defaultBlockState The default BlockState
*/
public BlockStateRandomizer(class_2680 defaultBlockState) {
this.defaultBlockState = defaultBlockState;
}
/**
* Constructs a new BlockStateRandomizer with no default BlockState nor entries.
*/
public BlockStateRandomizer() {
}
/**
* Convenience factory function that constructs a BlockStateRandomizer from a list of BlockStates.
* Each BlockState will have equal probability of being chosen.
*/
public static BlockStateRandomizer from(class_2680... blockStates) {
BlockStateRandomizer randomizer = new BlockStateRandomizer();
float chance = 1f / blockStates.length;
for (class_2680 state : blockStates) {
randomizer.addBlock(state, chance);
}
return randomizer;
}
/**
* Adds a BlockState with given chance of being selected.
* @return The modified BlockStateRandomizer
*/
public BlockStateRandomizer addBlock(class_2680 blockState, float chance) {
// Abort if BlockState already a part of this randomizer
if (entries.stream().anyMatch(entry -> entry.blockState.equals(blockState))) {
YungsApiCommon.LOGGER.warn("WARNING: duplicate block {} added to BlockStateRandomizer!", blockState.toString());
return this;
}
// Attempt to add BlockState to entries
float currTotal = entries.stream().map(entry -> entry.probability).reduce(Float::sum).orElse(0f);
float newTotal = currTotal + chance;
if (newTotal > 1.0F) { // Total probability cannot exceed 1
YungsApiCommon.LOGGER.warn("WARNING: block {} added to BlockStateRandomizer exceeds max probabiltiy of 1!", blockState.toString());
return this;
}
entries.add(new Entry(blockState, chance));
return this;
}
/**
* Randomly selects a BlockState from this BlockStateRandomizer.
* The Random provided should be one used in generation of your structure or feature,
* to ensure reproducibility for the same world seed.
*/
public class_2680 get(Random random) {
float target = random.nextFloat();
float currBottom = 0;
for (Entry entry : entries) {
if (currBottom <= target && target < currBottom + entry.probability) {
return entry.blockState;
}
currBottom += entry.probability;
}
// No match found
return this.defaultBlockState;
}
/**
* Randomly selects a BlockState from this BlockStateRandomizer.
* The RandomSource provided should be one used in generation of your structure or feature,
* to ensure reproducibility for the same world seed.
*/
public class_2680 get(class_5819 randomSource) {
float target = randomSource.method_43057();
float currBottom = 0;
for (Entry entry : entries) {
if (currBottom <= target && target < currBottom + entry.probability) {
return entry.blockState;
}
currBottom += entry.probability;
}
// No match found
return this.defaultBlockState;
}
/**
* Randomly selects a BlockState from this BlockStateRandomizer.
* The RandomSource provided should be one used in generation of your structure or feature,
* to ensure reproducibility for the same world seed.
* Enforces conditions via the StructureContext passed in.
*/
public class_2680 get(class_5819 randomSource, StructureContext ctx) {
float target = randomSource.method_43057();
float currBottom = 0;
for (Entry entry : entries) {
if (currBottom <= target && target < currBottom + entry.probability && entry.passesCondition(ctx)) {
return entry.blockState;
}
currBottom += entry.probability;
}
// No match found
return this.defaultBlockState;
}
/**
* Sets the default BlockState.
* This default BlockState is used for any leftover probability ranges.
*/
public void setDefaultBlockState(class_2680 blockState) {
this.defaultBlockState = blockState;
}
/**
* Returns a Map of BlockStates to their corresponding probabilities.
* Does not include the defaultBlockState.
* @return The Map
*/
public Map getEntriesAsMap() {
Map map = new HashMap<>();
this.entries.forEach(entry -> map.put(entry.blockState, entry.probability));
return map;
}
/**
* Returns a List of BlockStateRandomizer Entries.
* @return The List
*/
public List getEntries() {
return entries;
}
/**
* Returns the default BlockState.
* @return The default BlockState
*/
public class_2680 getDefaultBlockState() {
return defaultBlockState;
}
/**
* Represents a BlockState and its corresponding probability of being chosen.
*/
public static class Entry {
public static Codec CODEC = RecordCodecBuilder.create(instance -> instance
.group(
class_2680.field_24734.fieldOf("blockState").forGetter(entry -> entry.blockState),
Codec.floatRange(0.0F, 1.0F).fieldOf("probability").forGetter(entry -> entry.probability),
StructureConditionType.CONDITION_CODEC.optionalFieldOf("condition").forGetter(entry -> entry.condition))
.apply(instance, Entry::new));
public class_2680 blockState;
public float probability;
public Optional condition; // Conditions are ONLY supported via JSON
public Entry(class_2680 blockState, float probability) {
this.blockState = blockState;
this.probability = probability;
}
public Entry(class_2680 blockState, float probability, Optional condition) {
this.blockState = blockState;
this.probability = probability;
this.condition = condition;
}
public boolean passesCondition(StructureContext ctx) {
return this.condition.isEmpty() || this.condition.get().passes(ctx);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Entry) {
return this.blockState.equals(((Entry) obj).blockState);
} else if (obj instanceof class_2680) {
return this.blockState.equals(obj);
}
return false;
}
}
}