All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yungnickyoung.minecraft.yungsapi.api.world.randomize.ItemRandomizer Maven / Gradle / Ivy

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 java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import net.minecraft.class_156;
import net.minecraft.class_1792;
import net.minecraft.class_1802;
import net.minecraft.class_2487;
import net.minecraft.class_2499;
import net.minecraft.class_5819;
import net.minecraft.class_7923;

/**
 * Describes a set of Items and the probability of each Item in the set being chosen.
 * This is very useful for easily adding random variation to things like armor stands during world generation.
 */
public class ItemRandomizer {
    public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance
            .group(
                    Entry.CODEC.listOf().fieldOf("entries").forGetter((randomizer) -> randomizer.entries),
                    class_7923.field_41178.method_39673().fieldOf("defaultItem").forGetter((randomizer) -> randomizer.defaultItem))
            .apply(instance, ItemRandomizer::new));

    /**
     * Map of Items to their corresponding probabilities.
     * The total sum of all the probabilities should not exceed 1.
     */
    private List entries = new ArrayList<>();

    /**
     * The default Item 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 defaultItem being selected.
     */
    private class_1792 defaultItem = class_1802.field_8162;

    /**
     * Saves this ItemRandomizer to a new CompoundTag.
     * @return The CompoundTag
     */
    public class_2487 saveTag() {
        class_2487 compoundTag = new class_2487();

        // Save default blockstate
        compoundTag.method_10569("defaultItemId", class_7923.field_41178.method_10206(this.defaultItem));

        // 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("entryItemId", class_7923.field_41178.method_10206(entry.item));
                entryTag.method_10548("entryChance", entry.probability);
                tag.add(entryTag);
            });
        });
        compoundTag.method_10566("entries", entriesTag);

        return compoundTag;
    }

    /**
     * Constructs a new ItemRandomizer from a CompoundTag.
     * @param compoundTag The CompoundTag
     */
    public ItemRandomizer(class_2487 compoundTag) {
        this.defaultItem = class_7923.field_41178.method_10200(compoundTag.method_10550("defaultItemId"));
        this.entries = new ArrayList<>();

        class_2499 entriesTag = compoundTag.method_10554("entries", 10);
        entriesTag.forEach(entryTag -> {
            class_2487 entryCompoundTag = ((class_2487) entryTag);
            class_1792 item = class_7923.field_41178.method_10200(entryCompoundTag.method_10550("entryItemId"));
            float chance = entryCompoundTag.method_10583("entryChance");
            this.addItem(item, chance);
        });
    }

    /**
     * Constructs a new ItemRandomizer from a list of Entries and a default Item.
     * @param entries List of Entries
     * @param defaultItem The default Item
     */
    public ItemRandomizer(List entries, class_1792 defaultItem) {
        this.entries = entries;
        this.defaultItem = defaultItem;
    }

    /**
     * Constructs a new ItemRandomizer with only a default Item.
     * @param defaultItem The default Item
     */
    public ItemRandomizer(class_1792 defaultItem) {
        this.defaultItem = defaultItem;
    }

    /**
     * Constructs a new ItemRandomizer with no default Item nor entries.
     */
    public ItemRandomizer() {
    }

    /**
     * Convenience factory function to construct an ItemRandomizer from a list of Items.
     * Each Item will have equal probability of being chosen.
     */
    public static ItemRandomizer from(class_1792... items) {
        ItemRandomizer randomizer = new ItemRandomizer();
        float chance = 1f / items.length;

        for (class_1792 item : items) {
            randomizer.addItem(item, chance);
        }

        return randomizer;
    }

    /**
     * Adds an Item with given chance of being selected.
     * @return The modified ItemRandomizer
     */
    public ItemRandomizer addItem(class_1792 item, float chance) {
        // Abort if Item already a part of this randomizer
        if (entries.stream().anyMatch(entry -> entry.item.equals(item))) {
            YungsApiCommon.LOGGER.warn("WARNING: duplicate item {} added to ItemRandomizer!", item.toString());
            return this;
        }

        // Attempt to add Item to entries
        float currTotal = entries.stream().map(entry -> entry.probability).reduce(Float::sum).orElse(0f);
        float newTotal = currTotal + chance;
        if (newTotal > 1) { // Total probability cannot exceed 1
            YungsApiCommon.LOGGER.warn("WARNING: item {} added to ItemRandomizer exceeds max probabiltiy of 1!", item.toString());
            return this;
        }
        entries.add(new Entry(item, chance));
        return this;
    }

    /**
     * Randomly selects an Item from this ItemRandomizer.
     * The random provided should be one used in generation of your structure or feature,
     * to ensure reproducibility for the same world seed.
     */
    public class_1792 get(Random random) {
        float target = random.nextFloat();
        float currBottom = 0;

        for (Entry entry : entries) {
            if (currBottom <= target && target < currBottom + entry.probability) {
                return entry.item;
            }

            currBottom += entry.probability;
        }

        // No match found
        return this.defaultItem;
    }

    /**
     * Randomly selects an Item from this ItemRandomizer.
     * The RandomSource provided should be one used in generation of your structure or feature,
     * to ensure reproducibility for the same world seed.
     */
    public class_1792 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.item;
            }

            currBottom += entry.probability;
        }

        // No match found
        return this.defaultItem;
    }

    /**
     * Sets the default Item.
     * The default Item is used for any leftover probability ranges.
     */
    public void setDefaultItem(class_1792 item) {
        this.defaultItem = item;
    }

    /**
     * Returns a Map of Items to their corresponding probabilities.
     * Does not include the default Item.
     * @return The Map
     */
    public Map getEntriesAsMap() {
        Map map = new HashMap<>();
        this.entries.forEach(entry -> map.put(entry.item, entry.probability));
        return map;
    }

    /**
     * Returns a List of Entries.
     * @return The List
     */
    public List getEntries() {
        return entries;
    }

    /**
     * Returns the default Item.
     * @return The default Item
     */
    public class_1792 getDefaultItem() {
        return defaultItem;
    }

    /**
     * Represents an Item and its corresponding probability of being chosen.
     */
    public static class Entry {
        public static Codec CODEC = RecordCodecBuilder.create(instance -> instance
                .group(
                        class_7923.field_41178.method_39673().fieldOf("item").forGetter((entry) -> entry.item),
                        Codec.floatRange(0.0F, 1.0F).fieldOf("probability").forGetter(entry -> entry.probability))
                .apply(instance, Entry::new));

        public class_1792 item;
        public float probability;

        public Entry(class_1792 item, float probability) {
            this.item = item;
            this.probability = probability;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Entry) {
                return this.item.equals(((Entry) obj).item);
            } else if (obj instanceof class_1792) {
                return this.item.equals(obj);
            }
            return false;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy