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

mmb.engine.inv.storage.SetInventory Maven / Gradle / Ivy

Go to download

Dependency for the MultiMachineBuilder, a voxel game about building an industrial empire in a finite world. THIS RELEASE IS NOT PLAYABLE. To play the game, donwload from >ITCH.IO LINK HERE< or >GH releases link here<

There is a newer version: 0.6
Show newest version
/**
 * 
 */
package mmb.engine.inv.storage;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.function.Supplier;

import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.common.collect.Iterators;

import mmb.NN;
import mmb.Nil;
import mmb.engine.MMBUtils;
import mmb.engine.craft.RecipeOutput;
import mmb.engine.debug.Debugger;
import mmb.engine.inv.Inventory;
import mmb.engine.inv.ItemRecord;
import mmb.engine.inv.ItemStack;
import mmb.engine.inv.SaveInventory;
import mmb.engine.item.ItemEntry;
import mmb.engine.json.JsonTool;

/**
 * @author oskar
 * A skeletal implementation of inventory using sets.
 * A SetInventory is faster and self-contained implementation of the inventory
 * @implSpec Undefined behavior:
 * 
    *
  • Creation with a populated set
  • *
  • Modification of a set outside of the inventory
  • *
  • Bulk insertion, if the set imposes restrictions on items beyond the type
  • *
  • Using an unmodifiable set (if inventory will be modified)
  • *
* @implNote The bulk insertion is implemented as follows: *
    *
  1. Test all items using the {@link #test} method
  2. *
  3. If any item does not pass, reject the insertion
  4. *
  5. Bulk insert as normal
  6. *
* @param type of items */ public class SetInventory implements SaveInventory{ //Debug @NN private static final Debugger debug = new Debugger("INVENTORIES"); //Inventory definition /** The underlying set*/ @NN public final Set set; /** The class of the items (null if unrestricted)*/ @Nil public final Class type; private double capacity = 2; private double volume = 0; //Constructors /** * Creates an inventory using a set * @param set set to base on (should be empty) * @param type type of items (null if unrestricted) */ public SetInventory(Set<@NN T> set, @Nil Class type) { this.set = set; this.type = type; for(ItemEntry item: set) { volume += item.volume(); } } /** * Creates an inventory using a set factory * @param supplier set factory * @param type type of items (null if unrestricted) */ public SetInventory(Supplier<@NN Set> supplier, @Nil Class type) { this(supplier.get(), type); } /** * Creates an unrestricted set inventory (simplest) * @return a new set inventory */ @NN public static SetInventory create() { return new SetInventory<>(new HashSet<>(), null); } /** * Creates an unrestricted set inventory with a custom set * @param set set to use * @return a new set inventory */ @NN public static SetInventory create(Set set) { return new SetInventory<>(set, null); } /** * Creates an unrestricted set inventory using a set factory * @param supplier set factory * @return a new set inventory */ @NN public static SetInventory create(Supplier<@NN Set> supplier) { return new SetInventory<>(supplier, null); } //Item calculation @Override public int insertibleRemain(int amount, ItemEntry item) { if(amount <= 0) return 0; if(set.contains(item)) return 0; if(!test(item)) return 0; return 1; } @Override public int insertibleRemainBulk(int amount, RecipeOutput ent) { if(amount <= 0) return 0; //Test the volume if(remainVolume() < ent.outVolume()) return 0; //Test the recipe output for(ItemStack entry: ent) { if(entry.amount > 1) return 0; if(!test(entry.item)) return 0; } return 1; } @Override public boolean isEmpty() { return set.isEmpty(); } @Override public int size() { return set.size(); } @Override @EnsuresNonNullIf(result = true, expression = {"e"}) public boolean test(@Nil ItemEntry e) { Class cls = type; return cls == null || cls.isInstance(e); } @Override public double volume() { return volume; } //Item manipulation @Override public int insert(ItemEntry ent, int amount) { if(amount <= 0) return 0; if(!test(ent)) return 0; @SuppressWarnings("unchecked") T casted = (T) ent; //item is already of correct type if(remainVolume() < ent.volume()) return 0; boolean insert = set.add(casted); if(insert) volume += ent.volume(); return MMBUtils.bool2int(insert); } @Override public int extract(ItemEntry ent, int amount) { if(amount <= 0) return 0; boolean remove = set.remove(ent); if(remove) volume -= ent.volume(); return MMBUtils.bool2int(remove); } @Override public int bulkInsert(RecipeOutput ent, int amount) { int canInsert = insertibleRemainBulk(amount, ent); if(canInsert == 0) return 0; for(ItemStack entry: ent) { if(entry.amount == 0) continue; volume += entry.outVolume(); @SuppressWarnings("unchecked") T casted = (T) entry.item; //item is already of correct type set.add(casted); } return 1; } //Item records class SIRecord implements ItemRecord{ @NN private final ItemEntry entry; public SIRecord(ItemEntry entry) { this.entry = entry; } @Override public int amount() { return MMBUtils.bool2int(set.contains(entry)); } @Override public Inventory inventory() { return SetInventory.this; } @Override public ItemEntry item() { return entry; } @Override public int insert(int amount) { return SetInventory.this.insert(entry, amount); } @Override public int extract(int amount) { return SetInventory.this.extract(entry, amount); } } @Override public @NN Iterator<@NN ItemRecord> iterator() { return Iterators.transform(set.iterator(), this::get); } @Override public ItemRecord get(ItemEntry entry) { return new SIRecord(entry); } @Override public ItemRecord nget(ItemEntry entry) { if(!set.contains(entry)) return null; return get(entry); } //Direct modification @Override public double capacity() { return capacity; } /** * Changes capacity of the inventory * @param capacity capacity of the inventory * @return this */ @Override @NN public SetInventory setCapacity(double capacity) { this.capacity = capacity; return this; } /** * Directly sets the contents of the inventory (no capacity checks) * @param items contents to set */ public void setContents(Set items) { set.clear(); addAllUnvolumed(items); } /** * Directly adds items (no capacity checks) * @param c items to add * @return were any items added? */ public boolean addAllUnvolumed(Collection<@NN ItemEntry> c) { boolean result = false; for(ItemEntry item: c) if(addUnvolumed(item)) result = true; return result; } /** * Directly adds an item (no capacity checks) * @param ent item to add * @return was the item added? */ public boolean addUnvolumed(ItemEntry ent) { if(!test(ent)) return false; @SuppressWarnings("unchecked") T casted = (T) ent; //item is already of correct type boolean insert = set.add(casted); if(insert) volume += ent.volume(); return insert; } /** * Replaces the contents of this inventory with the one of the other inventory * @param inv source inventory */ public void setContents(SetInventory inv) { set.clear(); capacity = inv.capacity(); volume = 0; for(T item: inv.set) { boolean added = set.add(item); if(added) volume += item.volume(); } } //Serialization @Override public JsonNode save() { ArrayNode array = JsonTool.newArrayNode(); array.add(capacity); for(ItemEntry item: set) array.add(ItemEntry.saveItem(item)); return array; } @Override public void load(@Nil JsonNode data) { if(data == null) return; for(JsonNode node: data) { if(node.isNumber()) { //number nodes - capacity capacity = data.asDouble(2); } else { //array nodes - items ItemEntry item = ItemEntry.loadFromJson(node); if(!test(item)) { if(item == null) debug.printl("Unsupported null item"); else debug.printl("Unsupported item class: "+item.getClass()+" for: "+item); continue; } @SuppressWarnings("unchecked") T casted = (T) item; //item is already of the correct type set.add(casted); if(item != null) volume += item.volume(); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy