From 46f85b9011b2772cfb95a202722f4161d128b805 Mon Sep 17 00:00:00 2001 From: TwoThe Date: Tue, 21 Jul 2015 11:04:30 +0200 Subject: [PATCH] Initial commit - Version release 1.0 --- .gitignore | 21 ++ README.md | 24 ++ build.gradle | 63 ++++ src/main/java/baubles/api/BaubleType.java | 7 + src/main/java/baubles/api/BaublesApi.java | 41 ++ src/main/java/baubles/api/IBauble.java | 45 +++ src/main/java/baubles/api/package-info.java | 5 + .../api/IPlayerExtendedInventoryWrapper.java | 11 + .../java/tconstruct/api/TConstructAPI.java | 13 + .../two/graves/API/IInventoryHandler.java | 87 +++++ .../java/two/graves/API/InventoryContent.java | 89 +++++ .../two/graves/API/InventoryHandlerBase.java | 68 ++++ .../graves/API/InventoryHandlerRegistry.java | 80 ++++ .../graves/API/InventoryHandlerVanilla.java | 53 +++ .../java/two/graves/API/package-info.java | 5 + src/main/java/two/graves/Config.java | 62 +++ src/main/java/two/graves/Graves.java | 72 ++++ src/main/java/two/graves/GravesAssets.java | 22 ++ .../two/graves/InitializableModContent.java | 11 + src/main/java/two/graves/ProxyBase.java | 124 ++++++ src/main/java/two/graves/ProxyClient.java | 19 + src/main/java/two/graves/ProxyServer.java | 9 + .../java/two/graves/blocks/BlockBase.java | 44 +++ .../java/two/graves/blocks/BlockGrave.java | 139 +++++++ .../InventoryHandlerBaubles.java | 67 ++++ .../InventoryHandlerTConstruct.java | 92 +++++ .../graves/network/PacketForceEquipItems.java | 71 ++++ src/main/java/two/graves/tiles/TileGrave.java | 196 ++++++++++ src/main/java/two/graves/util/BlockSide.java | 352 ++++++++++++++++++ src/main/java/two/graves/util/BlockUtil.java | 39 ++ .../util/InvalidTileEntityException.java | 15 + src/main/java/two/graves/util/ItemUtil.java | 106 ++++++ src/main/java/two/graves/util/Logging.java | 33 ++ .../java/two/graves/util/TimeCounter.java | 38 ++ src/main/java/two/graves/util/TwoMath.java | 38 ++ src/main/java/two/graves/util/Vector3d.java | 112 ++++++ .../assets/twograves/lang/en_US.lang | 8 + .../twograves/textures/blocks/grave_front.png | Bin 0 -> 2143 bytes .../twograves/textures/blocks/grave_sides.png | Bin 0 -> 2070 bytes src/main/resources/mcmod.info | 16 + 40 files changed, 2297 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 build.gradle create mode 100644 src/main/java/baubles/api/BaubleType.java create mode 100644 src/main/java/baubles/api/BaublesApi.java create mode 100644 src/main/java/baubles/api/IBauble.java create mode 100644 src/main/java/baubles/api/package-info.java create mode 100644 src/main/java/tconstruct/api/IPlayerExtendedInventoryWrapper.java create mode 100644 src/main/java/tconstruct/api/TConstructAPI.java create mode 100644 src/main/java/two/graves/API/IInventoryHandler.java create mode 100644 src/main/java/two/graves/API/InventoryContent.java create mode 100644 src/main/java/two/graves/API/InventoryHandlerBase.java create mode 100644 src/main/java/two/graves/API/InventoryHandlerRegistry.java create mode 100644 src/main/java/two/graves/API/InventoryHandlerVanilla.java create mode 100644 src/main/java/two/graves/API/package-info.java create mode 100644 src/main/java/two/graves/Config.java create mode 100644 src/main/java/two/graves/Graves.java create mode 100644 src/main/java/two/graves/GravesAssets.java create mode 100644 src/main/java/two/graves/InitializableModContent.java create mode 100644 src/main/java/two/graves/ProxyBase.java create mode 100644 src/main/java/two/graves/ProxyClient.java create mode 100644 src/main/java/two/graves/ProxyServer.java create mode 100644 src/main/java/two/graves/blocks/BlockBase.java create mode 100644 src/main/java/two/graves/blocks/BlockGrave.java create mode 100644 src/main/java/two/graves/inventoryhandlers/InventoryHandlerBaubles.java create mode 100644 src/main/java/two/graves/inventoryhandlers/InventoryHandlerTConstruct.java create mode 100644 src/main/java/two/graves/network/PacketForceEquipItems.java create mode 100644 src/main/java/two/graves/tiles/TileGrave.java create mode 100644 src/main/java/two/graves/util/BlockSide.java create mode 100644 src/main/java/two/graves/util/BlockUtil.java create mode 100644 src/main/java/two/graves/util/InvalidTileEntityException.java create mode 100644 src/main/java/two/graves/util/ItemUtil.java create mode 100644 src/main/java/two/graves/util/Logging.java create mode 100644 src/main/java/two/graves/util/TimeCounter.java create mode 100644 src/main/java/two/graves/util/TwoMath.java create mode 100644 src/main/java/two/graves/util/Vector3d.java create mode 100644 src/main/resources/assets/twograves/lang/en_US.lang create mode 100644 src/main/resources/assets/twograves/textures/blocks/grave_front.png create mode 100644 src/main/resources/assets/twograves/textures/blocks/grave_sides.png create mode 100644 src/main/resources/mcmod.info diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b65d1b --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +/.git +/.gradle +/.nb-gradle +/.settings +/build +/eclipse +/gradle +/assets/forge +/cpw +/ibxm +/net +/paulscode +/*.lzma +/*.cfg +/*.properties +/*.png +/*.xcf +/*.java +.nb-gradle-properties +gradlew +gradlew.bat \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..93dc2b9 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +TwoGrave +======= + +TwoGrave - Keeps your items safe after death + +License +======= + +Copyright (c) 2013 Stefan Feldbinder + +Everyone is allowed to do whatever they want with a copy of this software, +with the following restrictions: + + 1. You must not claim to be the original author. + 2. You must give credit to the original author in an appropriate form. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..5944fc0 --- /dev/null +++ b/build.gradle @@ -0,0 +1,63 @@ +buildscript { + repositories { + mavenCentral() + maven { + name = "forge" + url = "http://files.minecraftforge.net/maven" + } + maven { + name = "sonatype" + url = "https://oss.sonatype.org/content/repositories/snapshots/" + } + } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT' + } +} + +apply plugin: 'forge' + +version = "1710.1.0" +group= "two.graves" // http://maven.apache.org/guides/mini/guide-naming-conventions.html +archivesBaseName = "TwoGraves" + +minecraft { + version = "1.7.10-10.13.4.1481-1.7.10" + runDir = "eclipse" +} + +dependencies { + // you may put jars on which you depend on in ./libs + // or you may define them like so.. + //compile "some.group:artifact:version:classifier" + //compile "some.group:artifact:version" + + // real examples + //compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env + //compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env + + // for more info... + // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html + // http://www.gradle.org/docs/current/userguide/dependency_management.html + +} + +processResources +{ + // this will ensure that this task is redone when the versions change. + inputs.property "version", project.version + inputs.property "mcversion", project.minecraft.version + + // replace stuff in mcmod.info, nothing else + from(sourceSets.main.resources.srcDirs) { + include 'mcmod.info' + + // replace version and mcversion + expand 'version':project.version, 'mcversion':project.minecraft.version + } + + // copy everything else, thats not the mcmod.info + from(sourceSets.main.resources.srcDirs) { + exclude 'mcmod.info' + } +} diff --git a/src/main/java/baubles/api/BaubleType.java b/src/main/java/baubles/api/BaubleType.java new file mode 100644 index 0000000..22349ff --- /dev/null +++ b/src/main/java/baubles/api/BaubleType.java @@ -0,0 +1,7 @@ +package baubles.api; + +public enum BaubleType { + RING, + AMULET, + BELT +} diff --git a/src/main/java/baubles/api/BaublesApi.java b/src/main/java/baubles/api/BaublesApi.java new file mode 100644 index 0000000..f122e90 --- /dev/null +++ b/src/main/java/baubles/api/BaublesApi.java @@ -0,0 +1,41 @@ +package baubles.api; + +import java.lang.reflect.Method; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import cpw.mods.fml.common.FMLLog; + +/** + * @author Azanor + */ +public class BaublesApi +{ + static Method getBaubles; + + /** + * Retrieves the baubles inventory for the supplied player + */ + public static IInventory getBaubles(EntityPlayer player) + { + IInventory ot = null; + + try + { + if(getBaubles == null) + { + Class fake = Class.forName("baubles.common.lib.PlayerHandler"); + getBaubles = fake.getMethod("getPlayerBaubles", EntityPlayer.class); + } + + ot = (IInventory) getBaubles.invoke(null, player); + } + catch(Exception ex) + { + FMLLog.warning("[Baubles API] Could not invoke baubles.common.lib.PlayerHandler method getPlayerBaubles"); + } + + return ot; + } + +} diff --git a/src/main/java/baubles/api/IBauble.java b/src/main/java/baubles/api/IBauble.java new file mode 100644 index 0000000..ccc3683 --- /dev/null +++ b/src/main/java/baubles/api/IBauble.java @@ -0,0 +1,45 @@ +package baubles.api; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.ItemStack; + +/** + * + * This interface should be extended by items that can be worn in bauble slots + * + * @author Azanor + */ + +public interface IBauble { + + /** + * This method return the type of bauble this is. + * Type is used to determine the slots it can go into. + */ + public BaubleType getBaubleType(ItemStack itemstack); + + /** + * This method is called once per tick if the bauble is being worn by a player + */ + public void onWornTick(ItemStack itemstack, EntityLivingBase player); + + /** + * This method is called when the bauble is equipped by a player + */ + public void onEquipped(ItemStack itemstack, EntityLivingBase player); + + /** + * This method is called when the bauble is unequipped by a player + */ + public void onUnequipped(ItemStack itemstack, EntityLivingBase player); + + /** + * can this bauble be placed in a bauble slot + */ + public boolean canEquip(ItemStack itemstack, EntityLivingBase player); + + /** + * Can this bauble be removed from a bauble slot + */ + public boolean canUnequip(ItemStack itemstack, EntityLivingBase player); +} diff --git a/src/main/java/baubles/api/package-info.java b/src/main/java/baubles/api/package-info.java new file mode 100644 index 0000000..497a7dd --- /dev/null +++ b/src/main/java/baubles/api/package-info.java @@ -0,0 +1,5 @@ +@API(owner = "Baubles", apiVersion = "1.0.1.10", provides = "Baubles|API") +package baubles.api; + +import cpw.mods.fml.common.API; + diff --git a/src/main/java/tconstruct/api/IPlayerExtendedInventoryWrapper.java b/src/main/java/tconstruct/api/IPlayerExtendedInventoryWrapper.java new file mode 100644 index 0000000..5ec5856 --- /dev/null +++ b/src/main/java/tconstruct/api/IPlayerExtendedInventoryWrapper.java @@ -0,0 +1,11 @@ +package tconstruct.api; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; + +public interface IPlayerExtendedInventoryWrapper +{ + public IInventory getKnapsackInventory (EntityPlayer player); + + public IInventory getAccessoryInventory (EntityPlayer player); +} diff --git a/src/main/java/tconstruct/api/TConstructAPI.java b/src/main/java/tconstruct/api/TConstructAPI.java new file mode 100644 index 0000000..98cfb46 --- /dev/null +++ b/src/main/java/tconstruct/api/TConstructAPI.java @@ -0,0 +1,13 @@ +package tconstruct.api; + +import net.minecraft.entity.player.EntityPlayer; + +public class TConstructAPI +{ + public static String PROP_NAME; + + public static IPlayerExtendedInventoryWrapper getInventoryWrapper (EntityPlayer player) + { + return (IPlayerExtendedInventoryWrapper) player.getExtendedProperties(PROP_NAME); + } +} diff --git a/src/main/java/two/graves/API/IInventoryHandler.java b/src/main/java/two/graves/API/IInventoryHandler.java new file mode 100644 index 0000000..ccfe9c0 --- /dev/null +++ b/src/main/java/two/graves/API/IInventoryHandler.java @@ -0,0 +1,87 @@ +/* + * (c) Two aka Stefan Feldbinder + */ +package two.graves.API; + +import java.util.Collection; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; + +/** + * + * @author Two + */ +public interface IInventoryHandler { + + /** + * Removes and returns all items from the given player that are handled by this handler. + * + * After execution all items that are in the result value must have been stripped + * from the player. All further items are subject to the usual death routine, + * which means they are most likely going to be spilled out into the world (if + * not handled by something else). + * + * Note that unless you implement your own synchronization functionality, this + * should be executed on both client and server side. + * + * @param player The player to take the items from. + * @param isRemote true if this is called on the server, false if it is called on the client. + * @return A collection containing all the items that have been removed from the player and should be placed into the grave. + */ + public Collection removeAllItems(final EntityPlayer player, final boolean isRemote); + + /** + * Tries to set the exact slot of an inventory related to the player to itemStack. + * + * If for that slot is occupied or cannot be set for some other reason, false + * must be returned. It is not allowed to try to add the item in any other way, + * that case is handled by addToInventory later. + * + * Note that unless you implement your own synchronization functionality, this + * should be executed on both client and server side. + * + * @param player The player to modify. + * @param slot The slot this must be placed in (if possible). + * @param itemStack The ItemStack to add. + * @param isRemote true if this is called on the server, false if it is called on the client. + * @return true if that exact slot could be set, false otherwise. + */ + public boolean set(final EntityPlayer player, final int slot, final ItemStack itemStack, final boolean isRemote); + + /** + * Tries to add the given itemStack an inventory related to player. + * + * This is called if the specific slot above failed, and is allowed to + * add the item to any suitable slot. If that is not possible (or reasonable) + * this method must return false, in which case the item is added to the + * generic (vanilla) player inventory (if possible). + * + * Note that unless you implement your own synchronization functionality, this + * should be executed on both client and server side. + * + * @param player The player to modify. + * @param itemStack The ItemStack to add. + * @param isRemote true if this is called on the server, false if it is called on the client. + * @return true if the items was added to any valid inventory slot, false otherwise. + */ + public boolean add(final EntityPlayer player, final ItemStack itemStack, final boolean isRemote); + + /** + * Called to notify the handler of inventory changes. + * Should call markDirty on the handled inventory to sync changes. + * + * Note that unless you implement your own synchronization functionality, this + * should be executed on both client and server side. + * + * @param player The player who's inventory has changed. + * @param isRemote true if this is called on the server, false if it is called on the client. + */ + public void markDirty(final EntityPlayer player, final boolean isRemote); + + /** + * Returns the ID of this InventoryHandler. + * + * @return The ID of this InventoryHandler. + */ + public String getID(); +} diff --git a/src/main/java/two/graves/API/InventoryContent.java b/src/main/java/two/graves/API/InventoryContent.java new file mode 100644 index 0000000..1dae161 --- /dev/null +++ b/src/main/java/two/graves/API/InventoryContent.java @@ -0,0 +1,89 @@ +/* + * (c) Two aka Stefan Feldbinder + */ +package two.graves.API; + +import cpw.mods.fml.common.network.ByteBufUtils; +import io.netty.buffer.ByteBuf; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +/** + * + * @author Two + */ +public class InventoryContent { + + protected final String handlerID; + protected final int slot; + protected final ItemStack itemStack; + + /** + * Creates a new InventoryContent data set. + * @param handlerID The ID that will handle this content. Must be a valid handlerID as registered with InventoryHandlerRegistry. + * @param slot The slot this was taken from/is supposed to be put in. + * @param itemStack The actual item that was removed. Must not be null. + */ + public InventoryContent(final String handlerID, final int slot, final ItemStack itemStack) { + if (handlerID == null) { + throw new IllegalArgumentException("HandlerID cannot be null"); + } + if (itemStack == null) { + throw new IllegalArgumentException("ItemStack cannot be null"); + } + this.handlerID = handlerID; + this.slot = slot; + this.itemStack = itemStack; + } + + public String getHandlerID() { + return handlerID; + } + + public int getSlot() { + return slot; + } + + public ItemStack getItemStack() { + return itemStack; + } + + @Override + public String toString() { + return (this.itemStack == null ? "null" : this.itemStack.toString()) + " for handler " + this.handlerID + " @Slot " + this.slot; + } + + /** *********************************************************************** */ + /** Streaming ************************************************************* */ + /** *********************************************************************** */ + public static String KEY_TARGET = "InventoryTarget"; + public static String KEY_SLOT = "InventorySlot"; + + public static InventoryContent readFromNBT(final NBTTagCompound tagCompound) { + return new InventoryContent( + tagCompound.getString(KEY_TARGET), + tagCompound.getInteger(KEY_SLOT), + ItemStack.loadItemStackFromNBT(tagCompound) + ); + } + + public void writeToNBT(final NBTTagCompound tagCompound) { + tagCompound.setString(KEY_TARGET, this.handlerID); + tagCompound.setInteger(KEY_SLOT, this.slot); + this.itemStack.writeToNBT(tagCompound); + } + + public void toBytes(final ByteBuf buffer) throws Exception { + ByteBufUtils.writeUTF8String(buffer, this.handlerID); + buffer.writeInt(this.slot); + ByteBufUtils.writeItemStack(buffer, this.itemStack); + } + + public static InventoryContent fromBytes(final ByteBuf buffer) throws Exception { + final String handlerID = ByteBufUtils.readUTF8String(buffer); + final int slot = buffer.readInt(); + final ItemStack itemStack = ByteBufUtils.readItemStack(buffer); + return new InventoryContent(handlerID, slot, itemStack); + } + +} diff --git a/src/main/java/two/graves/API/InventoryHandlerBase.java b/src/main/java/two/graves/API/InventoryHandlerBase.java new file mode 100644 index 0000000..79ef71d --- /dev/null +++ b/src/main/java/two/graves/API/InventoryHandlerBase.java @@ -0,0 +1,68 @@ +/* + * (c) Two aka Stefan Feldbinder + */ +package two.graves.API; + +import java.util.ArrayList; +import java.util.Collection; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; + +/** + * Base implementation of an IInventoryHandler. + * + * This provides convenience functions to handle standard Minecraft IInventory. + * See InventoryHandlerVanilla for an example of how to use this. + * + * @author Two + */ +public abstract class InventoryHandlerBase implements IInventoryHandler { + + /* This will strip all items from a standard Minecraft IInventory.*/ + protected Collection stripInventory(final EntityPlayer player, final IInventory inventory) { + final ArrayList result = new ArrayList(); + + final int size = inventory.getSizeInventory(); + for (int slotID = 0; slotID < size; ++slotID) { + final ItemStack itemStack = inventory.getStackInSlot(slotID); + if ((itemStack != null) && canRemove(player, inventory, slotID, itemStack)) { + final InventoryContent content = new InventoryContent(this.getID(), slotID, itemStack.copy()); + result.add(content); + inventory.setInventorySlotContents(slotID, null); + } + } + + return result; + } + + /* Sets the slot of a standard Minecraft IInventory. */ + protected boolean setInventorySlot(final EntityPlayer player, final IInventory inventory, final int slotID, final ItemStack itemStack) { + if (canAdd(player, inventory, slotID, itemStack)) { + inventory.setInventorySlotContents(slotID, itemStack); + return true; + } + return false; + } + + /* Adds the item to a standard Minecraft IInventory. */ + protected boolean addToInventory(final EntityPlayer player, final IInventory inventory, final ItemStack itemStack, final boolean isRemote) { + final int size = inventory.getSizeInventory(); + for (int slotID = 0; slotID < size; ++slotID) { + if (set(player, slotID, itemStack, isRemote)) { + return true; + } + } + return false; + } + + /* Intended to be overwritte by handlers that require a more complex handling. */ + protected boolean canAdd(final EntityPlayer player, final IInventory inventory, final int slotID, final ItemStack itemStack) { + return ((inventory.getStackInSlot(slotID) == null) && inventory.isItemValidForSlot(slotID, itemStack)); + } + + /* Intended to be overwritte by handlers that require a more complex handling. */ + protected boolean canRemove(final EntityPlayer player, final IInventory inventory, final int slotID, final ItemStack itemStack) { + return (inventory.getStackInSlot(slotID) != null); + } +} diff --git a/src/main/java/two/graves/API/InventoryHandlerRegistry.java b/src/main/java/two/graves/API/InventoryHandlerRegistry.java new file mode 100644 index 0000000..cadc781 --- /dev/null +++ b/src/main/java/two/graves/API/InventoryHandlerRegistry.java @@ -0,0 +1,80 @@ +/* + * (c) Two aka Stefan Feldbinder + */ +package two.graves.API; + +import cpw.mods.fml.common.FMLLog; +import java.util.Collection; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.logging.log4j.Level; + +/** + * + * @author Two + */ +public class InventoryHandlerRegistry { + + /* Internal collection of registered handlers. */ + protected static final ConcurrentHashMap inventoryHandlers = new ConcurrentHashMap(); + + /** + * Register a new IInventoryHandler. + * + * @param handler An initialized IInventoryHandler that will receive all requests for inventory insertion for its handlerID. + * @return true if the handler was added, false if that handler was already registered. + */ + public static boolean registerHandler(final IInventoryHandler handler) { + if ((handler.getID() == null) || handler.getID().isEmpty()) { + throw new IllegalArgumentException("HandlerID cannot be null or empty"); + } + if (handler == null) { + throw new IllegalArgumentException("Handler cannot be null"); + } + + if (inventoryHandlers.putIfAbsent(handler.getID(), handler) == null) { + FMLLog.log("TwoGraves", Level.INFO, "Added Grave handler for %s", handler.getID()); + return true; + } + return false; + } + + /** + * Returns a list of all registered handlers. + * + * @return A list of all registered handlers. + */ + public static Collection getAllHandlers() { + return inventoryHandlers.values(); + } + + /** + * Returns the handler registered for the given ID or null. + * + * @param handlerID the ID to query. + * @return The handler registered with the given ID or null. + */ + public static IInventoryHandler getHandlerForID(final String handlerID) { + return inventoryHandlers.get(handlerID); + } + + /** + * Returns true if a handler with a given ID is registered, false otherwise. + * + * @param handlerID the ID to query. + * @return True if a handler with a given ID is registered, false otherwise. + */ + public static boolean isHandlerRegistered(final String handlerID) { + return inventoryHandlers.containsKey(handlerID); + } + + /* For reference. This ID is intentionally blank to minimize network packet size. */ + public static final String INVENTORY_HANDLER_ID_VANILLA = ""; + + static { + /** + * Don't do this! Use InventoryHandlerRegistry.registerHandler instead. + * This is done here to bypass usual handlerID checks for the special vanilla handler. + */ + InventoryHandlerRegistry.inventoryHandlers.put(INVENTORY_HANDLER_ID_VANILLA, new InventoryHandlerVanilla()); + } +} diff --git a/src/main/java/two/graves/API/InventoryHandlerVanilla.java b/src/main/java/two/graves/API/InventoryHandlerVanilla.java new file mode 100644 index 0000000..52b873c --- /dev/null +++ b/src/main/java/two/graves/API/InventoryHandlerVanilla.java @@ -0,0 +1,53 @@ +/* + * (c) Two aka Stefan Feldbinder + */ +package two.graves.API; + +import java.util.Collection; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; + +/** + * Handler for vanilla inventory management. + * + * This handles the entire Vanilla player inventory (including the hot bar and + * armor) by using the base implementation functions. + * + * Also serves as an example. + * + * @author Two + */ +public class InventoryHandlerVanilla extends InventoryHandlerBase { + + /* Remove and return all items in the player's inventory. */ + @Override + public Collection removeAllItems(final EntityPlayer player, final boolean isRemote) { + return this.stripInventory(player, player.inventory); // using the base implementation to strip standard IInventory. + } + + /* Set item to a specific slot if possible. */ + @Override + public boolean set(final EntityPlayer player, final int slot, final ItemStack itemStack, final boolean isRemote) { + return this.setInventorySlot(player, player.inventory, slot, itemStack); // using the base implementation to set a IInventory slot. + } + + /* Add the item to any possible slot. */ + @Override + public boolean add(final EntityPlayer player, final ItemStack itemStack, final boolean isRemote) { + return this.addToInventory(player, player.inventory, itemStack, isRemote); // using the base implementation to merge with standard IInventory. + } + + /* Return the ID of this handler. */ + @Override + public String getID() { + return ""; // this is a special code to minimze network packet size. You must return a real ID here, like your mod-ID. + } + + @Override + public void markDirty(final EntityPlayer player, final boolean isRemote) { + if (isRemote) { + player.inventory.markDirty(); // notify that the inventory has changed + } + } + +} diff --git a/src/main/java/two/graves/API/package-info.java b/src/main/java/two/graves/API/package-info.java new file mode 100644 index 0000000..1345a41 --- /dev/null +++ b/src/main/java/two/graves/API/package-info.java @@ -0,0 +1,5 @@ +@API(owner = "TwoGraves", apiVersion = "1.0", provides = "TwoGraves|API") +package two.graves.API; + +import cpw.mods.fml.common.API; + diff --git a/src/main/java/two/graves/Config.java b/src/main/java/two/graves/Config.java new file mode 100644 index 0000000..318aaf5 --- /dev/null +++ b/src/main/java/two/graves/Config.java @@ -0,0 +1,62 @@ +/* + */ +package two.graves; + +import java.io.File; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.common.config.Property; + +/** + * @author Two + */ +public class Config { + + protected static final String CATEGORY_ALLOWED_RECIPES = "Allowed Recipes"; + protected static final String CATEGORY_VARIOUS_SETTINGS = "Settings"; + //--- Class ------------------------------------------------------------------ + protected Configuration configuration; + + protected Config() { + } + + protected void initialize(final File configFile) { + configuration = new Configuration(configFile); + } + + protected void load() { + configuration.load(); + } + + protected void save() { + configuration.save(); + } + + public boolean isCraftingEnabled(final String key) { + return isCraftingEnabled(key, true); + } + + public boolean isCraftingEnabled(final String key, final boolean defaultValue) { + final Property property = configuration.get(CATEGORY_ALLOWED_RECIPES, key, defaultValue); + return property.getBoolean(defaultValue); + } + + public int getMiscInteger(final String key, final int defaultValue) { + final Property property = configuration.get(CATEGORY_VARIOUS_SETTINGS, key, defaultValue); + return property.getInt(defaultValue); + } + + public long getMiscLong(final String key, final long defaultValue) { + final Property property = configuration.get(CATEGORY_VARIOUS_SETTINGS, key, (double) defaultValue); + return (long) property.getDouble(defaultValue); + } + + public double getMiscDouble(final String key, final double defaultValue) { + final Property property = configuration.get(CATEGORY_VARIOUS_SETTINGS, key, defaultValue); + return property.getDouble(defaultValue); + } + + public boolean getMiscBoolean(final String key, final boolean defaultValue) { + final Property property = configuration.get(CATEGORY_VARIOUS_SETTINGS, key, defaultValue); + return property.getBoolean(defaultValue); + } +} diff --git a/src/main/java/two/graves/Graves.java b/src/main/java/two/graves/Graves.java new file mode 100644 index 0000000..bf10a43 --- /dev/null +++ b/src/main/java/two/graves/Graves.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) by Stefan Feldbinder aka Two + */ +package two.graves; + +import cpw.mods.fml.common.Mod; +import cpw.mods.fml.common.SidedProxy; +import cpw.mods.fml.common.event.FMLInitializationEvent; +import cpw.mods.fml.common.event.FMLPostInitializationEvent; +import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import cpw.mods.fml.common.network.NetworkRegistry; +import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper; + +/** + * + * @author Two + */ +@Mod(modid = Graves.MOD_ID, name = Graves.MOD_NAME, version = Graves.MOD_VERSION) +public class Graves { + + public static final String MOD_NAME = "Graves"; + public static final String MOD_ID = "TwoGraves"; + public static final String MOD_VERSION = "1710.1.0"; + //---------------------------------------------------------------------------- + @Mod.Instance("TwoGraves") + public static Graves instance; + @SidedProxy(clientSide = "two.graves.ProxyClient", serverSide = "two.graves.ProxyServer") + public static ProxyBase proxy; + public static final Config config = new Config(); + public static final SimpleNetworkWrapper networkChannel = NetworkRegistry.INSTANCE.newSimpleChannel(MOD_ID); + + public static boolean baublesEnabled = false; + + public static String getTextureName(final String filePrefix) { + return Graves.MOD_ID + ":" + filePrefix; + } + + public static String getSoundName(final String soundName) { + return Graves.MOD_ID + ":" + soundName; + } + + public static String getTooltipName(final String itemName) { + return getTooltipName(itemName, null); + } + + public static String getTooltipName(final String itemName, final String suffix) { + return "item." + itemName + ".tooltip" + (suffix == null || suffix.length() == 0 ? "" : "." + suffix); + } + + public static String getEntityName(final String name) { + return "entity." + name; + } + + @Mod.EventHandler + public void preInit(final FMLPreInitializationEvent event) { + config.initialize(event.getSuggestedConfigurationFile()); + + proxy.onPreInit(); + } + + @Mod.EventHandler + public void load(final FMLInitializationEvent event) { + config.load(); + proxy.onInit(); + config.save(); + } + + @Mod.EventHandler + public void postInit(final FMLPostInitializationEvent event) { + proxy.onPostInit(); + } +} diff --git a/src/main/java/two/graves/GravesAssets.java b/src/main/java/two/graves/GravesAssets.java new file mode 100644 index 0000000..db1d5e4 --- /dev/null +++ b/src/main/java/two/graves/GravesAssets.java @@ -0,0 +1,22 @@ +/* + * (c) Two aka Stefan Feldbinder + */ +package two.graves; + +import two.graves.blocks.BlockGrave; + +/** + * + * @author Two + */ +public class GravesAssets { + + /* Blocks */ + public static BlockGrave blockGrave; + + /* Config values */ + public static int itemsPerTick = -1; + public static boolean playerCanDestroyGave = true; + public static boolean handleBaubles = true; + public static boolean handleTConstruct = true; +} diff --git a/src/main/java/two/graves/InitializableModContent.java b/src/main/java/two/graves/InitializableModContent.java new file mode 100644 index 0000000..a532987 --- /dev/null +++ b/src/main/java/two/graves/InitializableModContent.java @@ -0,0 +1,11 @@ +/* + */ +package two.graves; + +/** + * + * @author Two + */ +public interface InitializableModContent { + public void initialize(); +} diff --git a/src/main/java/two/graves/ProxyBase.java b/src/main/java/two/graves/ProxyBase.java new file mode 100644 index 0000000..97f3b8c --- /dev/null +++ b/src/main/java/two/graves/ProxyBase.java @@ -0,0 +1,124 @@ +/* + */ +package two.graves; + +import two.graves.inventoryhandlers.InventoryHandlerBaubles; +import cpw.mods.fml.common.FMLLog; +import cpw.mods.fml.common.Loader; +import cpw.mods.fml.common.eventhandler.EventPriority; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.relauncher.Side; +import two.graves.blocks.BlockGrave; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicInteger; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChunkCoordinates; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.living.LivingDeathEvent; +import two.graves.API.InventoryHandlerRegistry; +import two.graves.API.InventoryHandlerVanilla; +import two.graves.inventoryhandlers.InventoryHandlerTConstruct; +import two.graves.network.PacketForceEquipItems; +import two.graves.tiles.TileGrave; + +/** + * @author Two + */ +public class ProxyBase { + + /* Config */ + public final String KEY_ITEMS_PER_SECOND = "Items a player can take each tick"; + public final String KEY_PLAYER_CAN_DESTROY_GRAVE = "Players can destroy graves"; + public final String KEY_HANDLE_BAUBLES = "Handle Baubles items"; + public final String KEY_HANDLE_TCONSTRUCT = "Handle Tinker's Construct items"; + /* Initialization list for content that needs post-initialization. */ + protected ArrayList pendingInitialization = new ArrayList(); + protected final AtomicInteger networkID = new AtomicInteger(0); + + public ProxyBase() { + } + + protected void loadGlobalConfigValues() { + GravesAssets.itemsPerTick = Graves.config.getMiscInteger(KEY_ITEMS_PER_SECOND, -1); + GravesAssets.playerCanDestroyGave = Graves.config.getMiscBoolean(KEY_PLAYER_CAN_DESTROY_GRAVE, true); + GravesAssets.handleBaubles = Graves.config.getMiscBoolean(KEY_HANDLE_BAUBLES, true); + GravesAssets.handleTConstruct = Graves.config.getMiscBoolean(KEY_HANDLE_TCONSTRUCT, true); + } + + protected void registerBlocks() { + GravesAssets.blockGrave = new BlockGrave(); + pendingInitialization.add(GravesAssets.blockGrave); + } + + protected void registerItems() { + } + + protected void registerRenderers() { + } + + protected void registerNetwork() { + final int graveID = networkID.getAndIncrement(); + Graves.networkChannel.registerMessage(PacketForceEquipItems.class, PacketForceEquipItems.class, graveID, Side.CLIENT); + Graves.networkChannel.registerMessage(PacketForceEquipItems.class, PacketForceEquipItems.class, graveID, Side.SERVER); + } + + protected void registerInventoryHandlers() { + if (InventoryHandlerRegistry.isHandlerRegistered(InventoryHandlerRegistry.INVENTORY_HANDLER_ID_VANILLA) == false) { + throw new IllegalStateException("InventoryHandlerVanilla not registered"); // this is mostly to initialize the vanilla handler class + } + } + + public void onPreInit() { + MinecraftForge.EVENT_BUS.register(this); + } + + public void onInit() { + loadGlobalConfigValues(); + registerBlocks(); + registerItems(); + registerRenderers(); + registerNetwork(); + registerInventoryHandlers(); + + for (final InitializableModContent content : pendingInitialization) { + content.initialize(); + } + pendingInitialization.clear(); + } + + public void onPostInit() { + if (GravesAssets.handleBaubles && Loader.isModLoaded("Baubles")) { + InventoryHandlerRegistry.registerHandler(new InventoryHandlerBaubles()); + } + if (GravesAssets.handleTConstruct && Loader.isModLoaded("TConstruct")) { + InventoryHandlerRegistry.registerHandler(new InventoryHandlerTConstruct()); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH, receiveCanceled = false) + public void OnDeath(final LivingDeathEvent event) { + if (event.entityLiving instanceof EntityPlayer) { + final EntityPlayer player = (EntityPlayer) event.entityLiving; + if ((player != null) && (player.worldObj.isRemote == false)) { + final ChunkCoordinates deathChestPos = findNearestAirBlock(player); + if (deathChestPos != null) { + if (player.worldObj.setBlock(deathChestPos.posX, deathChestPos.posY, deathChestPos.posZ, GravesAssets.blockGrave, 0, 1 + 2)) { + final TileEntity tileEntity = player.worldObj.getTileEntity(deathChestPos.posX, deathChestPos.posY, deathChestPos.posZ); + if (tileEntity instanceof TileGrave) { + ((TileGrave) tileEntity).takePlayerInventory(player); + } else { + FMLLog.severe("Placed Grave, expected %s, but got: %s", TileGrave.class.getName(), tileEntity == null ? "null" : tileEntity.getClass().getName()); + } + } else { + FMLLog.severe("Unable to place Grave at %s", deathChestPos.toString()); + } + } + } + } + } + + protected static ChunkCoordinates findNearestAirBlock(final EntityPlayer player) { + return new ChunkCoordinates((int) player.posX, (int) player.posY, (int) player.posZ); + } +} diff --git a/src/main/java/two/graves/ProxyClient.java b/src/main/java/two/graves/ProxyClient.java new file mode 100644 index 0000000..0a49e8a --- /dev/null +++ b/src/main/java/two/graves/ProxyClient.java @@ -0,0 +1,19 @@ +/* + */ +package two.graves; + +import two.graves.util.ItemUtil; + +/** + * @author Two + */ +public class ProxyClient extends ProxyBase { + + @Override + public void onInit() { + super.onInit(); + + ItemUtil.clearCachedTooltips(); + } + +} diff --git a/src/main/java/two/graves/ProxyServer.java b/src/main/java/two/graves/ProxyServer.java new file mode 100644 index 0000000..43f2ac2 --- /dev/null +++ b/src/main/java/two/graves/ProxyServer.java @@ -0,0 +1,9 @@ +/* + */ +package two.graves; + +/** + * @author Two + */ +public class ProxyServer extends ProxyBase { +} diff --git a/src/main/java/two/graves/blocks/BlockBase.java b/src/main/java/two/graves/blocks/BlockBase.java new file mode 100644 index 0000000..430cfc9 --- /dev/null +++ b/src/main/java/two/graves/blocks/BlockBase.java @@ -0,0 +1,44 @@ +/* + */ +package two.graves.blocks; + +import cpw.mods.fml.common.registry.GameRegistry; +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.creativetab.CreativeTabs; +import two.graves.InitializableModContent; +import two.graves.Graves; + +/** + * @author Two + */ +public abstract class BlockBase extends Block implements InitializableModContent { + + public BlockBase(final Material material) { + super(material); + } + + /** + * Sets a blocks base values. + * + * Use this if the block needs to be in a different creative tab. + * + * @param name The internal name of the block. Equals texture name and language registry name. + * @param creativeTab The creative tab to put this block into. + * @param soundType The sound to make when walking over this block. + * @param hardness The hardness (artificial Minecraft value) that determines how long it takes to mine this block. + * @param harvestType The tool required to harvest this block. See BlockUtil for standard constants, but it can be any value. + * @param harvestLevel The material level of the tool required to harvest this block. See BlockUtil for standard constants, but it can be any value. + */ + protected void setBaseValues(final String name, final CreativeTabs creativeTab, final Block.SoundType soundType, final float hardness, final String harvestType, final int harvestLevel) { + GameRegistry.registerBlock(this, name); + setBlockName(name); + setCreativeTab(creativeTab); + setStepSound(soundType); + setHardness(hardness); + setHarvestLevel(harvestType, harvestLevel); + + setBlockTextureName(Graves.MOD_ID + ":" + name); + } + +} diff --git a/src/main/java/two/graves/blocks/BlockGrave.java b/src/main/java/two/graves/blocks/BlockGrave.java new file mode 100644 index 0000000..af77aff --- /dev/null +++ b/src/main/java/two/graves/blocks/BlockGrave.java @@ -0,0 +1,139 @@ +/* + */ +package two.graves.blocks; + +import cpw.mods.fml.common.FMLLog; +import cpw.mods.fml.common.registry.GameRegistry; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import java.util.List; +import net.minecraft.block.Block; +import net.minecraft.block.ITileEntityProvider; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.IIcon; +import net.minecraft.world.Explosion; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import org.apache.logging.log4j.Level; +import two.graves.Graves; +import two.graves.GravesAssets; +import two.graves.tiles.TileGrave; +import two.graves.util.BlockUtil; + +/** + * @author Two + */ +public class BlockGrave extends BlockBase implements ITileEntityProvider { + + protected static final float PIXEL_SIZE = 1.0f / 16.0f; + public static final String NAME = "grave"; + protected static final Class tileEntityClass = TileGrave.class; +//-- Class ------------------------------------------------------------------- + protected IIcon textureFront, textureSides; + + public BlockGrave() { + super(Blocks.stone.getMaterial()); + GameRegistry.registerTileEntity(tileEntityClass, tileEntityClass.getName()); + } + + @Override + public void initialize() { + setBaseValues(NAME, null, soundTypeStone, 3.0F, BlockUtil.HARVEST_TOOL_AXE, BlockUtil.HARVEST_LEVEL_STONE); + setResistance(6000.0F); // Explosion resistance as Obsidian + if (GravesAssets.playerCanDestroyGave == false) { + setBlockUnbreakable(); + } + } + + @Override + public boolean onBlockActivated(final World world, final int x, final int y, final int z, final EntityPlayer player, final int side, final float hitX, final float hitY, final float hitZ) { + if (world.isRemote == false) { + final TileEntity tileEntity = world.getTileEntity(x, y, z); + if (tileEntityClass.isInstance(tileEntity)) { + tileEntityClass.cast(tileEntity).giveItemsToPlayer(player); + } else { + FMLLog.log(Graves.MOD_ID, Level.ERROR, "Grave TileEntity expected %s, but found %s", tileEntityClass.getName(), tileEntity == null ? "null" : tileEntity.getClass().getName()); + } + } + return true; + } + + @Override + public void breakBlock(final World world, final int x, final int y, final int z, final Block block, final int metadata) { + final TileEntity tileEntity = world.getTileEntity(x, y, z); + if (tileEntityClass.isInstance(tileEntity)) { + tileEntityClass.cast(tileEntity).spillOutInventory(); + } else { + FMLLog.log(Graves.MOD_ID, Level.ERROR, "Grave TileEntity expected %s, but found %s", tileEntityClass.getName(), tileEntity == null ? "null" : tileEntity.getClass().getName()); + } + + super.breakBlock(world, x, y, z, block, metadata); + } + + @Override + public void onBlockDestroyedByExplosion(final World world, final int x, final int y, final int z, final Explosion explosion) { + final TileEntity tileEntity = world.getTileEntity(x, y, z); + if (tileEntityClass.isInstance(tileEntity)) { + tileEntityClass.cast(tileEntity).spillOutInventory(); + } else { + FMLLog.log(Graves.MOD_ID, Level.ERROR, "Grave TileEntity expected %s, but found %s", tileEntityClass.getName(), tileEntity == null ? "null" : tileEntity.getClass().getName()); + } + } + + @Override + public TileEntity createNewTileEntity(final World world, final int i) { + try { + return tileEntityClass.newInstance(); + } catch (Exception ex) { + FMLLog.log(Graves.MOD_ID, Level.ERROR, ex, "Creation of %s failed!", tileEntityClass.getName()); + return null; + } + } + + @SideOnly(Side.CLIENT) + @Override + public void registerBlockIcons(final IIconRegister iconRegister) { + textureFront = iconRegister.registerIcon(Graves.getTextureName(NAME + "_front")); + textureSides = iconRegister.registerIcon(Graves.getTextureName(NAME + "_sides")); + } + + @SideOnly(Side.CLIENT) + @Override + public IIcon getIcon(final int side, final int metadata) { + return (side == 2) ? textureFront : textureSides; + } + + @Override + public boolean renderAsNormalBlock() { + return false; + } + + @Override + public void addCollisionBoxesToList(final World world, final int x, final int y, final int z, final AxisAlignedBB boundingBox, final List list, final Entity entity) { + this.setBlockBoundsBasedOnState(world, x, y, z); + super.addCollisionBoxesToList(world, x, y, z, boundingBox, list, entity); + } + + @Override + public void setBlockBoundsBasedOnState(final IBlockAccess world, final int x, final int y, final int z) { + this.setBlockBounds( + 2 * PIXEL_SIZE, 0.0F, 1.0f - 3 * PIXEL_SIZE, + 1.0f - 2 * PIXEL_SIZE, 1.0f - 2 * PIXEL_SIZE, 1.0f - PIXEL_SIZE + ); + } + + @Override + public void setBlockBoundsForItemRender() { + this.setBlockBounds(0.375F, 0.0F, 0.0F, 0.625F, 1.0F, 1.0F); + } + + @Override + public boolean isOpaqueCube() { + return false; + } +} diff --git a/src/main/java/two/graves/inventoryhandlers/InventoryHandlerBaubles.java b/src/main/java/two/graves/inventoryhandlers/InventoryHandlerBaubles.java new file mode 100644 index 0000000..7d9bb0c --- /dev/null +++ b/src/main/java/two/graves/inventoryhandlers/InventoryHandlerBaubles.java @@ -0,0 +1,67 @@ +/* + * (c) Two aka Stefan Feldbinder + */ +package two.graves.inventoryhandlers; + +import baubles.api.BaublesApi; +import baubles.api.IBauble; +import java.util.Collection; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import two.graves.API.InventoryContent; +import two.graves.API.InventoryHandlerBase; +import two.graves.util.ItemUtil; + +/** + * + * @author Two + */ +public class InventoryHandlerBaubles extends InventoryHandlerBase { + + @Override + public Collection removeAllItems(final EntityPlayer player, final boolean isRemote) { + return this.stripInventory(player, BaublesApi.getBaubles(player)); + } + + @Override + protected boolean canAdd(final EntityPlayer player, final IInventory inventory, final int slotID, final ItemStack itemStack) { + return (itemStack.getItem() instanceof IBauble) + && ((IBauble) itemStack.getItem()).canEquip(itemStack, player) + && super.canAdd(player, inventory, slotID, itemStack); + } + + @Override + protected boolean canRemove(final EntityPlayer player, final IInventory inventory, final int slotID, final ItemStack itemStack) { + return ((itemStack.getItem() instanceof IBauble) + && ((IBauble) itemStack.getItem()).canUnequip(itemStack, player) + && super.canRemove(player, inventory, slotID, itemStack)); + } + + @Override + public boolean set(final EntityPlayer player, final int slot, final ItemStack itemStack, final boolean isRemote) { + if (isRemote) { + return this.setInventorySlot(player, BaublesApi.getBaubles(player), slot, itemStack); + } else { + return ItemUtil.isSameItem(itemStack, BaublesApi.getBaubles(player).getStackInSlot(slot)); // Baubles syncs on its own + } + } + + @Override + public boolean add(final EntityPlayer player, final ItemStack itemStack, final boolean isRemote) { + return this.addToInventory(player, BaublesApi.getBaubles(player), itemStack, isRemote); + } + + @Override + public String getID() { + return "Baubles"; + } + + @Override + public void markDirty(EntityPlayer player, final boolean isRemote) { + if (isRemote) { + BaublesApi.getBaubles(player).markDirty(); + } + } + +} diff --git a/src/main/java/two/graves/inventoryhandlers/InventoryHandlerTConstruct.java b/src/main/java/two/graves/inventoryhandlers/InventoryHandlerTConstruct.java new file mode 100644 index 0000000..e298c8a --- /dev/null +++ b/src/main/java/two/graves/inventoryhandlers/InventoryHandlerTConstruct.java @@ -0,0 +1,92 @@ +/* + * (c) Two aka Stefan Feldbinder + */ +package two.graves.inventoryhandlers; + +import java.util.Collection; +import java.util.LinkedList; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import tconstruct.api.IPlayerExtendedInventoryWrapper; +import tconstruct.api.TConstructAPI; +import two.graves.API.InventoryContent; +import two.graves.API.InventoryHandlerBase; + +/** + * + * @author Two + */ +public class InventoryHandlerTConstruct extends InventoryHandlerBase { + + protected static final int SLOT_KNAPSACK = 2; + protected static final int SLOT_HEART_RED = 4; + protected static final int SLOT_HEART_YELLOW = 5; + protected static final int SLOT_HEART_GREEN = 6; + + protected static final int OFFSET_KNAPSACK = 100; + + @Override + public Collection removeAllItems(final EntityPlayer player, final boolean isRemote) { + final IPlayerExtendedInventoryWrapper inventoryWrapper = TConstructAPI.getInventoryWrapper(player); + final IInventory knapsack = inventoryWrapper.getKnapsackInventory(player); + final IInventory accessories = inventoryWrapper.getAccessoryInventory(player); + final LinkedList result = new LinkedList(); + + final int sizeKnapsack = knapsack.getSizeInventory(); + for (int slotID = 0; slotID < sizeKnapsack; ++slotID) { + final ItemStack itemStack = knapsack.getStackInSlot(slotID); + if ((itemStack != null) && canRemove(player, knapsack, slotID, itemStack)) { + final InventoryContent content = new InventoryContent(this.getID(), slotID + OFFSET_KNAPSACK, itemStack.copy()); + result.add(content); + knapsack.setInventorySlotContents(slotID, null); + } + } + + final int sizeAccesories = accessories.getSizeInventory(); + for (int slotID = 0; slotID < sizeAccesories; ++slotID) { + if ((slotID != SLOT_HEART_RED) && (slotID != SLOT_HEART_YELLOW) && (slotID != SLOT_HEART_GREEN)) { + final ItemStack itemStack = accessories.getStackInSlot(slotID); + if ((itemStack != null) && canRemove(player, accessories, slotID, itemStack)) { + final InventoryContent content = new InventoryContent(this.getID(), slotID, itemStack.copy()); + result.addFirst(content); + accessories.setInventorySlotContents(slotID, null); + } + } + } + + return result; + } + + @Override + public boolean set(final EntityPlayer player, final int slot, final ItemStack itemStack, final boolean isRemote) { + if (slot >= 100) { + return this.setInventorySlot(player, TConstructAPI.getInventoryWrapper(player).getKnapsackInventory(player), slot - OFFSET_KNAPSACK, itemStack); + } else { + return this.setInventorySlot(player, TConstructAPI.getInventoryWrapper(player).getAccessoryInventory(player), slot, itemStack); + } + } + + @Override + protected boolean canAdd(final EntityPlayer player, final IInventory inventory, final int slotID, final ItemStack itemStack) { + return (inventory.getStackInSlot(slotID) == null); // TConstruct does not properly implement IsValidForSlot + } + + @Override + public boolean add(final EntityPlayer player, final ItemStack itemStack, final boolean isRemote) { + return false; // if it doesn't fit in the knapsack, then no other slot can take it. Add it to the vanilla inventory instead if possible. + } + + @Override + public String getID() { + return "TConstruct"; + } + + @Override + public void markDirty(EntityPlayer player, final boolean isRemote) { + if (isRemote) { + TConstructAPI.getInventoryWrapper(player).getKnapsackInventory(player).markDirty(); + TConstructAPI.getInventoryWrapper(player).getAccessoryInventory(player).markDirty(); + } + } +} diff --git a/src/main/java/two/graves/network/PacketForceEquipItems.java b/src/main/java/two/graves/network/PacketForceEquipItems.java new file mode 100644 index 0000000..630b96c --- /dev/null +++ b/src/main/java/two/graves/network/PacketForceEquipItems.java @@ -0,0 +1,71 @@ +/* + * (c) Two aka Stefan Feldbinder + */ +package two.graves.network; + +import cpw.mods.fml.common.FMLLog; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import cpw.mods.fml.relauncher.Side; +import io.netty.buffer.ByteBuf; +import java.util.Deque; +import java.util.LinkedList; +import net.minecraft.client.Minecraft; +import org.apache.logging.log4j.Level; +import two.graves.API.InventoryContent; +import two.graves.Graves; +import two.graves.tiles.TileGrave; + +/** + * + * @author Two + */ +public class PacketForceEquipItems implements IMessage, IMessageHandler { + + protected final Deque inventoryContent; + + public PacketForceEquipItems(final Deque inventoryContent) { + this.inventoryContent = inventoryContent == null ? new LinkedList() : new LinkedList(inventoryContent); + } + + /* Forge requires a default constructor for initialization from stream. */ + public PacketForceEquipItems() { + this(null); + } + + @Override + public void fromBytes(final ByteBuf buf) { + int count = buf.readInt(); + while (count-- > 0) { + try { + final InventoryContent content = InventoryContent.fromBytes(buf); + this.inventoryContent.add(content); + } catch (Exception e) { + FMLLog.log(Graves.MOD_ID, Level.ERROR, e, "Unable to read Grave content properly"); + } + } + } + + @Override + public void toBytes(final ByteBuf buf) { + buf.writeInt(this.inventoryContent.size()); + for (final InventoryContent content : this.inventoryContent) { + try { + content.toBytes(buf); + } catch (Exception e) { + FMLLog.log(Graves.MOD_ID, Level.ERROR, e, "Unable to package Grave content %s", content.toString()); + } + } + } + + @Override + public PacketForceEquipItems onMessage(final PacketForceEquipItems message, final MessageContext ctx) { + if (ctx.side == Side.CLIENT) { + TileGrave.giveItemsToPlayer(Minecraft.getMinecraft().thePlayer, message.inventoryContent, false); + return null; // no answer required + } else { + throw new IllegalStateException("Received " + PacketForceEquipItems.class.getSimpleName() + " for " + ctx.side.toString()); + } + } +} diff --git a/src/main/java/two/graves/tiles/TileGrave.java b/src/main/java/two/graves/tiles/TileGrave.java new file mode 100644 index 0000000..0a8fe02 --- /dev/null +++ b/src/main/java/two/graves/tiles/TileGrave.java @@ -0,0 +1,196 @@ +/* + */ +package two.graves.tiles; + +import cpw.mods.fml.common.FMLLog; +import cpw.mods.fml.common.registry.LanguageRegistry; +import java.util.Deque; +import java.util.LinkedList; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChatComponentText; +import net.minecraftforge.common.util.Constants; +import org.apache.logging.log4j.Level; +import two.graves.API.IInventoryHandler; +import two.graves.API.InventoryContent; +import two.graves.API.InventoryHandlerRegistry; +import two.graves.Graves; +import two.graves.GravesAssets; +import two.graves.network.PacketForceEquipItems; + +/** + * @author Two + */ +public class TileGrave extends TileEntity { + + protected static final String KEY_OWNER = "GraveOwner"; + protected static final String KEY_INVENTORY = "GraveInventory"; + protected static final String KEY_LOCALIZATION_NOTYOURGRAVE = "notyourgrave"; + + protected final LinkedList inventoryContent; + protected String ownerName; + + public TileGrave() { + super(); + this.inventoryContent = new LinkedList(); + } + + @Override + public void readFromNBT(final NBTTagCompound tagCompound) { + super.readFromNBT(tagCompound); + + this.ownerName = tagCompound.getString(KEY_OWNER); + this.inventoryContent.clear(); + final NBTTagList inventoryList = tagCompound.getTagList(KEY_INVENTORY, Constants.NBT.TAG_COMPOUND); // we have a list of compounds + for (int tagCount = inventoryList.tagCount() - 1; tagCount >= 0; --tagCount) { + final NBTTagCompound inventoryEntry = inventoryList.getCompoundTagAt(tagCount); + try { + final InventoryContent content = InventoryContent.readFromNBT(inventoryEntry); + this.inventoryContent.add(content); + } catch (Exception e) { + FMLLog.log(Graves.MOD_ID, Level.ERROR, "Unable to load inventory content entry: %s\n%s", e.toString(), inventoryEntry == null ? "null" : inventoryEntry.toString()); + } + } + } + + @Override + public void writeToNBT(final NBTTagCompound tagCompound) { + super.writeToNBT(tagCompound); + + tagCompound.setString(KEY_OWNER, this.ownerName); + final NBTTagList inventoryList = new NBTTagList(); + for (final InventoryContent content : this.inventoryContent) { + final NBTTagCompound inventoryEntry = new NBTTagCompound(); + content.writeToNBT(inventoryEntry); + inventoryList.appendTag(inventoryEntry); + } + tagCompound.setTag(KEY_INVENTORY, inventoryList); + } + + public void takePlayerInventory(final EntityPlayer player) { + this.ownerName = player.getDisplayName(); + for (final IInventoryHandler handler : InventoryHandlerRegistry.getAllHandlers()) { + this.inventoryContent.addAll(handler.removeAllItems(player, this.worldObj.isRemote)); + handler.markDirty(player, this.worldObj.isRemote); + } + + if (this.inventoryContent.isEmpty()) { + this.destroyThisGrave(); + } else { + this.markDirty(); + } + } + + /* Called from the server-side block */ + public void giveItemsToPlayer(final EntityPlayer player) { + if (this.ownerName.equals(player.getDisplayName())) { + if (player instanceof EntityPlayerMP) { + final PacketForceEquipItems packet = new PacketForceEquipItems(this.inventoryContent); + Graves.networkChannel.sendTo(packet, (EntityPlayerMP) player); // have the same happen on the client to synchronize inventories + } + + if (TileGrave.giveItemsToPlayer(player, this.inventoryContent, true)) { + this.destroyThisGrave(); + } else { + this.markDirty(); + } + + for (final IInventoryHandler handler : InventoryHandlerRegistry.getAllHandlers()) { + handler.markDirty(player, true); + } + + } else { + final String message = String.format(LanguageRegistry.instance().getStringLocalization(KEY_LOCALIZATION_NOTYOURGRAVE), this.ownerName); + player.addChatComponentMessage(new ChatComponentText(message)); + } + } + + /* Called from the server-side block and the client-side packet handler */ + public static boolean giveItemsToPlayer(final EntityPlayer player, final Deque inventoryList, final boolean remote) { + int count = GravesAssets.itemsPerTick >= 0 ? GravesAssets.itemsPerTick : Integer.MAX_VALUE; + if (count == 0) { + return true; + } + InventoryContent content; + final Deque notAdded = new LinkedList(); + + while ((count-- > 0) && ((content = inventoryList.poll()) != null)) { + try { + final IInventoryHandler handler = InventoryHandlerRegistry.getHandlerForID(content.getHandlerID()); + if (handler != null) { + if (handler.set(player, content.getSlot(), content.getItemStack(), remote) == false) { + notAdded.add(content); + } + } else { + FMLLog.log(Graves.MOD_ID, Level.WARN, "No handler registered for ID '%s'. The item '%s' is lost.", content.getHandlerID(), content.getItemStack().getDisplayName()); + } + } catch (Exception e) { + FMLLog.log(Graves.MOD_ID, Level.ERROR, "Failed to add item to inventory: %s\n%s", e.toString(), content.toString()); + } + } + + final IInventoryHandler vanillaHandler = InventoryHandlerRegistry.getHandlerForID(InventoryHandlerRegistry.INVENTORY_HANDLER_ID_VANILLA); + for (InventoryContent leftOverContent : notAdded) { + final IInventoryHandler handler = InventoryHandlerRegistry.getHandlerForID(leftOverContent.getHandlerID()); + if (handler != null) { + if (handler.add(player, leftOverContent.getItemStack(), remote) == false) { + if ((handler == vanillaHandler) || (vanillaHandler.add(player, leftOverContent.getItemStack(), remote) == false)) { + inventoryList.add(leftOverContent); // return the item to the grave for later + } + } + } else { + FMLLog.log(Graves.MOD_ID, Level.WARN, "No handler registered for ID '%s'. The item '%s' is lost.", leftOverContent.getHandlerID(), leftOverContent.getItemStack().getDisplayName()); + } + } + + return inventoryList.isEmpty(); + } + + public void spillOutInventory() { + for (final InventoryContent content : this.inventoryContent) { + spillOutItem(content.getItemStack()); + } + this.inventoryContent.clear(); + this.markDirty(); + } + + public void spillOutItem(final ItemStack itemstack) { + if (itemstack != null) { + final float modX = worldObj.rand.nextFloat() * 0.8F + 0.1F; + final float modY = worldObj.rand.nextFloat() * 0.8F + 0.1F; + final float modZ = worldObj.rand.nextFloat() * 0.8F + 0.1F; + + while (itemstack.stackSize > 0) { + int stackSplit = worldObj.rand.nextInt(21) + 10; + + if (stackSplit > itemstack.stackSize) { + stackSplit = itemstack.stackSize; + } + + itemstack.stackSize -= stackSplit; + final EntityItem entityitem = new EntityItem(worldObj, (double) ((float) xCoord + modX), (double) ((float) yCoord + modY), (double) ((float) zCoord + modZ), new ItemStack(itemstack.getItem(), stackSplit, itemstack.getItemDamage())); + + if (itemstack.hasTagCompound()) { + entityitem.getEntityItem().setTagCompound((NBTTagCompound) itemstack.getTagCompound().copy()); + } + + final float baseVelocity = 0.05F; + entityitem.motionX = (double) ((float) worldObj.rand.nextGaussian() * baseVelocity); + entityitem.motionY = (double) ((float) worldObj.rand.nextGaussian() * baseVelocity + 0.2F); + entityitem.motionZ = (double) ((float) worldObj.rand.nextGaussian() * baseVelocity); + worldObj.spawnEntityInWorld(entityitem); + } + } + } + + protected void destroyThisGrave() { + if (this.worldObj.isRemote == false) { + this.worldObj.setBlockToAir(this.xCoord, this.yCoord, this.zCoord); + } + } +} diff --git a/src/main/java/two/graves/util/BlockSide.java b/src/main/java/two/graves/util/BlockSide.java new file mode 100644 index 0000000..b2092d9 --- /dev/null +++ b/src/main/java/two/graves/util/BlockSide.java @@ -0,0 +1,352 @@ +package two.graves.util; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.MathHelper; +import net.minecraftforge.common.util.ForgeDirection; + +/** + * @author Two + */ +public enum BlockSide { + + BOTTOM, TOP, NORTH, SOUTH, WEST, EAST; // Blockside.ordinal() is the Minecraft side of a Block facing north + public static final int ROTATION_MASK = 0x0C; // this mask extracts the rotation information from the metadata + public static final int DATA_MASK = 0x03; // this mask extracts block specific data from the metadata + + public ForgeDirection behind() { + switch (this) { + case BOTTOM: + return ForgeDirection.UP; + case TOP: + return ForgeDirection.DOWN; + case NORTH: + return ForgeDirection.SOUTH; + case SOUTH: + return ForgeDirection.NORTH; + case WEST: + return ForgeDirection.EAST; + case EAST: + return ForgeDirection.WEST; + } + throw new IllegalStateException("Illegal side for behind: " + this.name()); + } + + public BlockSide backSide() { + switch (this) { + case BOTTOM: + return TOP; + case TOP: + return BOTTOM; + case NORTH: + return SOUTH; + case SOUTH: + return NORTH; + case WEST: + return EAST; + case EAST: + return WEST; + } + throw new IllegalStateException("Illegal side for backSide: " + this.name()); + } + + public ForgeDirection infront() { + switch (this) { + case BOTTOM: + return ForgeDirection.DOWN; + case TOP: + return ForgeDirection.UP; + case NORTH: + return ForgeDirection.NORTH; + case SOUTH: + return ForgeDirection.SOUTH; + case WEST: + return ForgeDirection.WEST; + case EAST: + return ForgeDirection.EAST; + } + throw new IllegalStateException("Illegal side for infront: " + this.name()); + } + + public ForgeDirection left() { + switch (this) { + case NORTH: + return ForgeDirection.WEST; + case SOUTH: + return ForgeDirection.EAST; + case WEST: + return ForgeDirection.NORTH; + case EAST: + return ForgeDirection.SOUTH; + } + throw new IllegalStateException("Illegal side for left: " + this.name()); + } + + public BlockSide leftSide() { + switch (this) { + case NORTH: + return WEST; + case SOUTH: + return EAST; + case WEST: + return NORTH; + case EAST: + return SOUTH; + } + throw new IllegalStateException("Illegal side for leftSide: " + this.name()); + } + + public ForgeDirection right() { + switch (this) { + case NORTH: + return ForgeDirection.EAST; + case SOUTH: + return ForgeDirection.WEST; + case WEST: + return ForgeDirection.SOUTH; + case EAST: + return ForgeDirection.NORTH; + } + throw new IllegalStateException("Illegal side for right: " + this.name()); + } + + public BlockSide rightSide() { + switch (this) { + case NORTH: + return EAST; + case SOUTH: + return WEST; + case WEST: + return SOUTH; + case EAST: + return NORTH; + } + throw new IllegalStateException("Illegal side for rightSide: " + this.name()); + } + + /** + * Returns the direction the entity is looking at. + * This is intended to be used for a block's metadata on placement. + * + * @param entity the entity in question + * @return the direction the entity is looking at as CW 0 (west) to 3 (south) + */ + public static int getLookDirection(final EntityLivingBase entity) { + return MathHelper.floor_double(((double) (entity.rotationYaw + 360.0F - 45.0F + 180.0F)) / 90.0) & 3; // Minecraft is -180° (north) to 180° CW, +360 to replace modulo with & + } + + /** + * Returns the side the entity is looking at. + * + * @param entity the entity in question + * @return the BlockSide the entity is looking at + */ + public static BlockSide getLookSide(final EntityLivingBase entity) { + return fromDirection(getLookDirection(entity)); + } + + /** + * Returns the direction that is facing the entity + * This is intended to be used for a block's metadata on placement. + * + * @param entity the entity in question + * @return the direction that is facing the entity as CW 0 (west) to 3 (south) + */ + public static int getDirectionFacing(final EntityLivingBase entity) { + return MathHelper.floor_double(((double) (entity.rotationYaw + 360.0F - 45.0F + 180.0F)) / 90.0) & 3; // Minecraft is -180° (north) to 180° CW, +360 to replace modulo with & + } + + /** + * Returns the side that is facing the entity + * + * @param entity the entity in question + * @return the BlockSide that is facing the entity. + */ + public static BlockSide getSideFacing(final EntityLivingBase entity) { + return fromDirection(getDirectionFacing(entity)); + } + + /** + * Creates rotation data based on the given BlockSide ordinal. + * + * @param direction a direction ordinal. + * @return rotation data based on the given BlockSide ordinal. + */ + public static int createRotationData(final int direction) { + return (direction & 3) << 2; + } + + public static int createRotationData(final BlockSide direction) { + return createRotationData(direction.direction()); + } + + public int direction() { + switch (this) { + case WEST: + return 0; + case NORTH: + return 1; + case EAST: + return 2; + case SOUTH: + return 3; + } + throw new IllegalArgumentException("No direction for " + this); + } + + /** + * Returns the raw rotation data as found in the given metadata. + * @param metadata the block's metadata. + * @return the raw rotation data as found in the given metadata. + */ + public static int getRotationData(final int metadata) { + return metadata & ROTATION_MASK; + } + + /** + * Returns the BlockSide according the given metadata. + * + * @param metadata metadata created by createRotationData. + * @return the BlockSide according the given metadata. + */ + public static int getDirectionFrom(final int metadata) { + return (metadata & ROTATION_MASK) >>> 2; + } + + /** + * Calculates which side is side given the block's orientation + * + * @param side the side that is searched for + * @param metadata the facing of the block. + * @return the side that corresponds to side according to the block's rotation. + */ + public static BlockSide getRotatedSide(final int side, final int metadata) { + switch (side) { + case 0: + return BOTTOM; + case 1: + return TOP; + case 2: // north side + switch (getDirectionFrom(metadata)) { + case 0: // facing west + return EAST; + case 1: // facing north + return NORTH; + case 2: // facing east + return WEST; + case 3: // facing south + return SOUTH; + } + case 3: // south side + switch (getDirectionFrom(metadata)) { + case 0: // facing west + return WEST; + case 1: // facing north + return SOUTH; + case 2: // facing east + return EAST; + case 3: // facing south + return NORTH; + } + case 4: // west side + switch (getDirectionFrom(metadata)) { + case 0: // facing west + return NORTH; + case 1: // facing north + return WEST; + case 2: // facing east + return SOUTH; + case 3: // facing south + return EAST; + } + case 5: // east side + switch (getDirectionFrom(metadata)) { + case 0: // facing west + return SOUTH; + case 1: // facing north + return EAST; + case 2: // facing east + return NORTH; + case 3: // facing south + return WEST; + } + } + throw new IllegalArgumentException("Illegal side " + side); + } + + /** + * Returns the "native" block orientation based on side. + * This is a convenient function for blocks that do not rotate (with a metadata of 0). + * + * @param side the side that is searched for. + * @return the "native" block orientation based on side. + */ + public static BlockSide getSide(final int side) { + return getRotatedSide(side, 0); + } + + /** + * Returns the side that corresponds to direction as encoded in the metadata. + * + * @param metadata the direction to look up. + * @return the side that corresponds to direction. + */ + public static BlockSide fromMetadata(final int metadata) { + return fromDirection(getDirectionFrom(metadata)); + } + + /** + * Returns the side that corresponds to direction. + * + * @param direction the direction to look up. + * @return the side that corresponds to direction. + */ + public static BlockSide fromDirection(final int direction) { + switch (direction & 3) { + case 0: + return WEST; + case 1: + return NORTH; + case 2: + return EAST; + case 3: + return SOUTH; + } + throw new IllegalArgumentException("Illegal direction " + direction); // impossible to reach + } + + /** + * Returns a block's state from its metadata by removing rotation data. + * + * @param metadata the block's metadata. + * @return the block's metadata state. + */ + public static int getBlockDataFromMetadata(final int metadata) { + return (metadata & BlockSide.DATA_MASK); + } + + /** + * Updates a block's state from its current metadata and block state. + * + * @param metaCurrent the block's current metadata. + * @param state the block's internal state. + * @return the block's metadata. + */ + public static int updateState(final int metaCurrent, final int state) { + return ((metaCurrent & BlockSide.ROTATION_MASK) | (state & BlockSide.DATA_MASK)); + } + + /** + * Creates a block's metadata using the given rotation data and state. + * + * @param metaCurrent the block's rotation data. + * @param state the block's internal state. + * @return the block's metadata. + */ + public static int createState(final int rotationData, final int state) { + return (createRotationData(rotationData) | (state & BlockSide.DATA_MASK)); + } + + public static int createState(final BlockSide blockFacing, final int state) { + return createState(blockFacing.direction(), state); + } +} diff --git a/src/main/java/two/graves/util/BlockUtil.java b/src/main/java/two/graves/util/BlockUtil.java new file mode 100644 index 0000000..169d9dc --- /dev/null +++ b/src/main/java/two/graves/util/BlockUtil.java @@ -0,0 +1,39 @@ +/* + */ +package two.graves.util; + +import cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry; +import cpw.mods.fml.common.registry.GameData; +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; + +/** + * @author Two + */ +public class BlockUtil { + + /* Constants for basic harvest tools. */ + public static final String HARVEST_TOOL_AXE = "axe"; + public static final String HARVEST_TOOL_PICKAXE = "pickaxe"; + public static final String HARVEST_TOOL_SHOVEL = "shovel"; + /* Constants for basic harvest material levels */ + public static final int HARVEST_LEVEL_WOOD = 0; + public static final int HARVEST_LEVEL_STONE = 1; + public static final int HARVEST_LEVEL_IRON = 2; + public static final int HARVEST_LEVEL_DIAMOND = 3; + public static final int HARVEST_LEVEL_GOLD = 0; + + public static final FMLControlledNamespacedRegistry registry = GameData.getBlockRegistry(); + + public static Block findByName(final String name) { + return registry.getObject(name); + } + + public static boolean isSameBlockType(final Block block, final Block other) { + return (block == other); + } + + public static boolean isAir(final Block block) { + return (block.getMaterial() == Material.air); + } +} diff --git a/src/main/java/two/graves/util/InvalidTileEntityException.java b/src/main/java/two/graves/util/InvalidTileEntityException.java new file mode 100644 index 0000000..1b4d058 --- /dev/null +++ b/src/main/java/two/graves/util/InvalidTileEntityException.java @@ -0,0 +1,15 @@ +/* + */ +package two.graves.util; + +import net.minecraft.tileentity.TileEntity; + +/** + * @author Two + */ +public class InvalidTileEntityException extends Exception { + + public InvalidTileEntityException(final Class expected, final TileEntity found, final int x, final int y, final int z) { + super(String.format("TileEntity at %d, %d, %d should have been %s, but was %s", x, y, z, expected.getName(), found == null ? "null" : found.getClass().getName())); + } +} diff --git a/src/main/java/two/graves/util/ItemUtil.java b/src/main/java/two/graves/util/ItemUtil.java new file mode 100644 index 0000000..502968a --- /dev/null +++ b/src/main/java/two/graves/util/ItemUtil.java @@ -0,0 +1,106 @@ +/* + */ +package two.graves.util; + +import cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry; +import cpw.mods.fml.common.registry.GameData; +import cpw.mods.fml.common.registry.LanguageRegistry; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import java.util.HashMap; +import java.util.Map; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.FurnaceRecipes; + +/** + * @author Two + */ +public class ItemUtil { + + public static final FMLControlledNamespacedRegistry registry = GameData.getItemRegistry(); + + public static boolean isSameItem(final ItemStack itemStack, final ItemStack other) { + if (itemStack == other) { + return true; + } + if (itemStack == null) { + return false; + } + return (itemStack.isItemEqual(other) && ItemStack.areItemStackTagsEqual(itemStack, other)); // intenionally no check for stack size + } + + public static boolean isSameItem(final Item item, final Item other) { + return (item == other); // is this enough? + } + + public static boolean isStackHolding(final ItemStack itemStack, final Item item) { + return ((itemStack != null) && isSameItem(itemStack.getItem(), item) && (itemStack.hasTagCompound() == false)); + } + + public static boolean isSameBaseType(final ItemStack itemStack, final Item item) { + return ((itemStack != null) && isSameItem(itemStack.getItem(), item)); + } + + public static Item findByName(final String name) { + return registry.getObject(name); + } + + /** + * Fetches the smelting result for a given ingredient. + * Convenience function that handles all kinds of null checks. + * + * @param ingredients the ingredient to smelt. + * @return the smelting result or null if the ingredient cannot be smelted into anything. + */ + public static ItemStack getSmeltingResult(final ItemStack ingredients) { + if ((ingredients == null) || (ingredients.getItem() == null)) { + return null; + } + final ItemStack smeltingResult = FurnaceRecipes.smelting().getSmeltingResult(ingredients); + if ((smeltingResult == null) || (smeltingResult.getItem() == null)) { + return null; + } + return smeltingResult.copy(); + } + + /** + * Convenience function to check if a given item can be smelted at all. + * + * @param ingredients the ingredient to smelt. + * @return true if the item can be smelted in any form of furnace, false otherwise. + */ + public static boolean canSmelt(final ItemStack ingredients) { + if ((ingredients == null) || (ingredients.getItem() == null)) { + return false; + } + final ItemStack smeltingResult = FurnaceRecipes.smelting().getSmeltingResult(ingredients); + if ((smeltingResult == null) || (smeltingResult.getItem() == null)) { + return false; + } + return true; + } + + protected static final Map tooltipCache = new HashMap(); + + @SideOnly(Side.CLIENT) + public static String getCachedTooltip(final String key) { + String result = tooltipCache.get(key); + if (result == null) { + result = LanguageRegistry.instance().getStringLocalization(key); + if (result == null) { + result = ""; // this prevents further lookups + } + tooltipCache.put(key, result); + } + if (result.length() == 0) { + return null; + } + return result; + } + + @SideOnly(Side.CLIENT) + public static void clearCachedTooltips() { + tooltipCache.clear(); + } +} diff --git a/src/main/java/two/graves/util/Logging.java b/src/main/java/two/graves/util/Logging.java new file mode 100644 index 0000000..9c39126 --- /dev/null +++ b/src/main/java/two/graves/util/Logging.java @@ -0,0 +1,33 @@ +/* + */ +package two.graves.util; + +import cpw.mods.fml.common.FMLLog; +import org.apache.logging.log4j.Level; +import two.graves.Graves; + +/** + * @author Two + */ +public class Logging { + + public static String methodParametersToString(final Object[] params) { + if ((params == null) || (params.length == 0)) { + return ""; + } + + final int iMax = params.length - 1; + final StringBuilder result = new StringBuilder(); + for (int i = 0;; i++) { + result.append(String.valueOf(params[i])); + if (i == iMax) { + return result.toString(); + } + result.append(", "); + } + } + + public static void logMethodEntry(final String className, final String methodName, final Object... params) { + FMLLog.log(Graves.MOD_ID, Level.INFO, "[Entered] %s.%s(%s)", className, methodName, methodParametersToString(params)); + } +} diff --git a/src/main/java/two/graves/util/TimeCounter.java b/src/main/java/two/graves/util/TimeCounter.java new file mode 100644 index 0000000..6e7fa87 --- /dev/null +++ b/src/main/java/two/graves/util/TimeCounter.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) by Stefan Feldbinder aka Two + */ +package two.graves.util; + +/** + * + * @author Two + */ +public class TimeCounter { + + final String what; + long timeStart; + double calls, timeTotal, timeMax, timeMin; + + public TimeCounter(final String what) { + this.what = what; + this.timeMax = -1; + this.timeMin = -1; + } + + public void start() { + timeStart = System.nanoTime(); + } + + public void stop() { + ++calls; + final double timeTaken = ((double) (System.nanoTime() - timeStart)) / 1000000.0; + timeTotal += timeTaken; + this.timeMax = this.timeMax == -1 ? timeTaken : Math.max(timeTaken, this.timeMax); + this.timeMin = this.timeMin == -1 ? timeTaken : Math.min(timeTaken, this.timeMin); + } + + @Override + public String toString() { + return String.format("[%s]: Time [avg]: %3.2f ms, [min]: %3.2f ms, [max]: %3.2f ms", what, timeTotal / calls, this.timeMin, this.timeMax); + } +} diff --git a/src/main/java/two/graves/util/TwoMath.java b/src/main/java/two/graves/util/TwoMath.java new file mode 100644 index 0000000..98fac56 --- /dev/null +++ b/src/main/java/two/graves/util/TwoMath.java @@ -0,0 +1,38 @@ +/* + */ +package two.graves.util; + +/** + * @author Two + */ +public class TwoMath { + + public static int withinBounds(final int value, final int lowerBound, final int upperBound) { + if (value < lowerBound) { + return lowerBound; + } else if (value > upperBound) { + return upperBound; + } else { + return value; + } + } + + public static double distance(final double x0, final double y0, final double z0, final double x1, final double y1, final double z1) { + final double xDist = Math.abs(x1 - x0); + final double yDist = Math.abs(y1 - y0); + final double zDist = Math.abs(z1 - z0); + return Math.sqrt(xDist * xDist + yDist * yDist + zDist * zDist); + } + + public static double noLessThanOne(final double value) { + if ((value > -1.0) && (value < 1.0)) { + if (value >= 0.0) { + return 1.0; + } else { + return -1.0; + } + } else { + return value; + } + } +} diff --git a/src/main/java/two/graves/util/Vector3d.java b/src/main/java/two/graves/util/Vector3d.java new file mode 100644 index 0000000..567ebde --- /dev/null +++ b/src/main/java/two/graves/util/Vector3d.java @@ -0,0 +1,112 @@ +/* + */ +package two.graves.util; + +import java.util.Objects; +import net.minecraft.util.Vec3; + +/** + * @author Two + */ +public class Vector3d extends Vec3 { + + public Vector3d() { + this(0.0, 0.0, 0.0); + } + + public Vector3d(final Vec3 other) { + this(other.xCoord, other.yCoord, other.zCoord); + } + + public Vector3d(final double x, final double y, final double z) { + super(x, y, z); + } + + public Vector3d set(final Vec3 other) { + return set(other.xCoord, other.yCoord, other.zCoord); + } + + public Vector3d set(final double x, final double y, final double z) { + return (Vector3d) setComponents(x, y, z); + } + + public Vec3 add(final Vec3 other) { + return add(other.xCoord, other.yCoord, other.zCoord); + } + + public Vec3 add(final double x, final double y, final double z) { + xCoord += x; + yCoord += y; + zCoord += z; + return this; + } + + public Vec3 sub(final Vec3 other) { + return sub(other.xCoord, other.yCoord, other.zCoord); + } + + public Vec3 sub(final double x, final double y, final double z) { + xCoord -= x; + yCoord -= y; + zCoord -= z; + return this; + } + + public Vec3 setSub(final Vec3 from, final Vec3 to) { + return setSub(from.xCoord, from.yCoord, from.zCoord, to.xCoord, to.yCoord, to.zCoord); + } + + public Vec3 setSub(final double fromX, final double fromY, final double fromZ, final double toX, final double toY, final double toZ) { + xCoord = toX - fromX; + yCoord = toY - fromY; + zCoord = toZ - fromZ; + return this; + } + + public Vec3 cross(final Vec3 other) { + this.xCoord = this.yCoord * other.zCoord - this.zCoord * other.yCoord; + this.yCoord = this.zCoord * other.xCoord - this.xCoord * other.zCoord; + this.zCoord = this.xCoord * other.yCoord - this.yCoord * other.xCoord; + return this; + } + + public double distanceToBlock(final double blockX, final double blockY, final double blockZ) { + return distanceTo(blockX + 0.5, blockY + 0.5, blockZ + 0.5); + } + + public double distanceTo(final double x, final double y, final double z) { + final double distX = x - this.xCoord; + final double distY = y - this.yCoord; + final double distZ = z - this.zCoord; + return Math.sqrt(distX * distX + distY * distY + distZ * distZ); + } + + public Vector3d copy() { + return new Vector3d(this); + } + + @Override + public String toString() { + return String.format("(%+8.3f, %+8.3f, %+8.3f)", this.xCoord, this.yCoord, this.zCoord); + } + + @Override + public int hashCode() { + return Objects.hash(xCoord, yCoord, zCoord); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (obj.getClass().isAssignableFrom(this.getClass())) { + final Vec3 other = (Vec3) obj; + return ((this.xCoord == other.xCoord) && (this.yCoord == other.yCoord) && (this.zCoord == other.zCoord)); + } + return false; + } +} diff --git a/src/main/resources/assets/twograves/lang/en_US.lang b/src/main/resources/assets/twograves/lang/en_US.lang new file mode 100644 index 0000000..ee249a8 --- /dev/null +++ b/src/main/resources/assets/twograves/lang/en_US.lang @@ -0,0 +1,8 @@ +itemGroup.Graves=Graves + +language.name=English +language.region=US +language.code=en_US + +tile.grave.name=Grave +notyourgrave=Rest in peace %s \ No newline at end of file diff --git a/src/main/resources/assets/twograves/textures/blocks/grave_front.png b/src/main/resources/assets/twograves/textures/blocks/grave_front.png new file mode 100644 index 0000000000000000000000000000000000000000..c3d62a3012f66441757cc51390b4715e41de290d GIT binary patch literal 2143 zcmV-l2%z_gP)WFU8GbZ8()Nlj2>E@cM*00+ZKL_t(Y$0gNUt|Zx2 zfZ=uDdq-sEIj5>z5)JPNXt&xFLKq1Tz@7mT!?lnA%Ql7vn*s2EsIGG|GGfQRtmTKE z!7qIeKmOtupU(Qql#)`(N~}5GT65$JVFkpTu16i`egIL2+8YWYrFz^&bIL1(ATbe_vRcZ_ zwC}qEx`Rp)64mUJ^71Fdx0umJFXfqpkK>Ekm2$o=>&vo!nEebihxe`d_4*Eo#;Dr-p9q$Q%~yrg_Qw)@Mwcl&m~KEHs^2uGk&F0Zc}i;9)G)#>=yJ}0}(sQ^&&Ww^5t zMZ|d?WqBDv(;K72fmJ>_y9yNEJzD$9CVBV09|XG>Nh=xnXF zL(|P+InS<|FV`Q52#y}J_p|n5Yae5OY(z{50f-POF9ETx*Lv)^6o+8d*Ds$Dfsm3J zfMQzCc9im!M+4xT-;<1(-p-xIh~wB)^SWM%SS8(V&#iZNKkGgx2+L?Ke2&o};OX}4 zGp0vMnZ)+TzAUQ;5U~hBL_Lnyd$L7T0S3)60Kk2oM^#Z4I*#Kw4ggB&%EV(1GXo?< zOl7HOO*#4WIeqj7;C+l_cDvn{ax+;0V<}g&!kpIiBLS*=5D_9y_c_DE5g@>)dpO)~ zH)a9=OA7+1DiI^Ve(YsgiDZoF5CB9(VkxB+5mfV>@#V|^(&!JOG$<+D-cJBL_D9MEfrME^5WrFjkAM5sua{*#TO%f77-KM!hqFj)ofv!X zb9QF>?Z5vQL?FxPBLG#inzgo*urle>%gY?SwTjGR^pwljfAtp;KqCM0ufKu_1SBL? zB~_vyNNg!95&rCF|Ik_k#HB1uJZ5*F+xF%CyN@|t2~Z1H03xYL-S>zfL;zq>Mmk$1LLw5?oXYT-%&5=FqO*^Z%eHNzhQy-w zo8SJ2uz>rw-+bMBM?w)}rkFF_Rg?&s35kFC%YTjt4>wbQV3s*&0Gwy_7__7dGh0f4 z6yZb!5$-cQRLvo_#{&?Lql)U7PRwUJ0WbnY1R_KzS^B%b{ToIW5oR8GE3;}L+M zW@d)OeR|3X2`H5(1kbrJQ7&0X=Im!ZYCXUC`Y%N_r?hRGs*N$jy&g3JN-4LeClMiF zgg`<|skJV|`{R*w8e_b^-H+qIfBMItt6ra;-VyPB9KE-7S;w4pe~2g&nI-o*-GzmO zl3G0~LhSYU)33g2ts^2a?T^RhdMl-zXM4NfTdyi|f4or}eOYc}^f3k#Uaz+~Cllpm zb@1t9jxH)C7eFFJH9OB**5!74>T?|RSl0_8m319+++SajfCzIcGr%m9Y1+0o)wDhC z9#M}&RHx5ctEpiCAOHX$5C*f%G2Vap0022z%s}MtzyE)PhzL_7;rrX`54m$Rf*>Lh zkvT@PoKmusM(+^-0KJc#3nBREIVEOBz&U5F)!olpBj%i=l!aMy&h8%JMEv~xqNdsM z~x4c{cXpWvsf`G?k z_n7Ol5J9rkM+dDJiONG zbDrnf+Ic+o(K-PRkGAj391fxk9>|PHOnmm%S|e2=WJFq*bw7^t>^bEzx`>`%m9>zV8p8-g{>e zL}Wr!-L~zqKj!Gn$fWMD-8U5p4`u-XRm~}pNJ^PSd+)t>04SCT0ab($#RLH$0EyIW zv^M5wt&KS$B0O3<=kx#va5KB#?}#|(4DjRFnYEv1Z&g^#w3KzQ6J^}y$03B&m zSad^gZEa<4bN~PV002XBWnpw>WFU8GbZ8()Nlj2>E@cM*00(+WL_t(Y$1RmO-xAj++U)l))hjK7>Qb2tu$uZk3AA2L1hsV)tVFQ^6w;%2pD6u z_Jf(v^ur`&IEW^d2u7gHjJ2|w;{eau?d$X@GO>ao3^?;jug>-+Kc zo-s26f!lKX`g*dcXq$1)!0q|Fl$%chfL3oaCJRw!Ue~cK@18URAW3aylKcJ5?Dl&7 zVWN-EU+2En(#~TSRc0=&t?QZ@s-<7OSj*(A_w(AdJOY-hchPpg|3gI3J$;Pp9IdRq z`@X%1m=F>GAWK~ma#`=^vDeljlf}ON{0k8XxtIYcmh~EYTmCJh18^;$q@Hv3YbWx}&K^*=Pm##DUt+7@P3)0nANG7*8zN+~8Pt*ur}n5~p*g;dwpj$fM{zQQ5JjQk2)=wJaLcn9+N^J-vOr=(YT-SR{KlW`|*Q-`{MCDyn1_wdpCiOkn|+#m15JrmUG zoB^2tNQkX2Rw@wzfEIiI_#mOTx3_&ekZ@TRX7)KE=Iiz6^W#@3Hv-fQzs_?%jvqhX z_t!>5ujhAWjL}84U;X|4Ls*FLx=w&ZBvn0+9cBpq?Y}C^b)AGrM505rnlQoO&Lj4n9)=qV~jaH zXy5)9fRAlw5w&{ti%IL!N^L&eea@JG2#^^80JXHpgv{sj`TTnB=cZOh?-2@!L!NFb&CK$x|*$TYKlU1J>kJ`qWTN^MN^dc8`iV+;ZK`uc**x-2FBHX7HTb5;vG3G3#)z&inZ&mOupF;#+c#DH z@%HA!iTK!Fs)|IlF2{Mg4@T%?Q1`K{clY7$M0mg7XG|ul>l&Fe=5!a))*2uoqM2U( zZ0quPy!rIwJlFMxgl$>p^w-xH5)ff6Edb0anU>e{TeWQ4GZL=j5YZX)I!;r?%)hDu zfH0ZGr+-~U8N$TT$+d47Mn51;O%6C@#th)j30T8dez?jsWbV7S-X z2qC=JQdpP(!{>RO5pi8-Ld5je7A7sV&WOxOB=0{yRJB^&j-6sUKn@=wLP#;aT7wK# zW0o1yN@;EBy(^1VOMv_Be(bxMsaPgxF;NbW>BFpMPWQpY>+SZqzmfZ4s>~QMx9vqp z$F;Aw``=}jQcQL94w<805VwQ8iRsj9x7-?dc)+|Ofwy@)6y zFynq(k8LLaoIb{!YIYpQb)N3N@0*Gy|?M9A`+pgzFx0w-{&06z^oDQdTuI` zk<0>ss#;4yk-r}`MjvAUU@espP(=w*Q~)3ofXK{zbf4aPpL6~NQNOM^BQpTPOrNhO zBF;Gz=6USGI<9N>+g@h2y}lD5 zGu=nAGRBy5T-T_}8aagp05j)wpL3X6F(V&tVx#wjK&12PBKrTXYhgwrI?hv=d+*D# z`WVcN05bv*h}cBF|NPDbWFGFsI_I2o0%9ioAM7L`k6RE2$^ZZW07*qoM6N<$f?gQ< AssI20 literal 0 HcmV?d00001 diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info new file mode 100644 index 0000000..56f0997 --- /dev/null +++ b/src/main/resources/mcmod.info @@ -0,0 +1,16 @@ +[ +{ + "modid" : "TwoGraves", + "name" : "Graves", + "description": "At your death items are put into a grave chest", + "version": "${version}", + "mcversion": "${mcversion}", + "url": "http://www.minecraftforum.net/topic/2219062-", + "updateUrl": "", + "authors": ["Two"], + "credits": "", + "logoFile": "", + "screenshots": [], + "dependencies": [] +} +]