diff --git a/src/main/kotlin/org/kotobank/kuarry/KuarryModItems.kt b/src/main/kotlin/org/kotobank/kuarry/KuarryModItems.kt index 86c6110..d6ddf34 100644 --- a/src/main/kotlin/org/kotobank/kuarry/KuarryModItems.kt +++ b/src/main/kotlin/org/kotobank/kuarry/KuarryModItems.kt @@ -26,25 +26,38 @@ object KuarryModItems { ) } + private val levelUpgrades by lazy { + listOf( + Pair("level_2_upgrade", HardenedUpgrade()), + Pair("level_3_upgrade", ReinforcedUpgrade()) + ) + } + private val items by lazy { listOf( - Pair("kuarry", ItemBlock(KuarryModBlocks.kuarry)), Pair("denatured_stone", ItemBlock(KuarryModBlocks.denatured_stone)), Pair("kuarry_casing", Item()) ) } - internal val allItems by lazy { items + upgrades } + /** Items that can easily be registered with the same code */ + internal val commonlyRegisteredItems by lazy { items + upgrades + levelUpgrades } + + // region Items that need special code for registration internal val luckUpgrade by lazy { KuarryLuckUpgrade() } + internal val kuarry by lazy { KuarryItemBlock(KuarryModBlocks.kuarry) } + + // endregion + @SubscribeEvent fun registerItems(event: Register) { // Set each upgrade's max stack size upgrades.forEach { (_, item) -> item.setMaxStackSize(item.stackSize) } - allItems.forEach { (name, item) -> + commonlyRegisteredItems.forEach { (name, item) -> item.apply { setRegistryName(KuarryMod.MODID, name) setUnlocalizedName(name) @@ -61,6 +74,20 @@ object KuarryModItems { setUnlocalizedName("luck_upgrade") event.registry.register(this) } + + with(kuarry) { + setRegistryName(KuarryMod.MODID, "kuarry") + setUnlocalizedName("kuarry") + + ModelLoader.setCustomMeshDefinition(this) { stack -> + val level = stack.tagCompound?.getInteger("upgrade_level") ?: 0 + + // Return a custom model based on the level from the ItemStack + ModelResourceLocation(this.registryName!!, "facing=north,level=${level}") + } + + event.registry.register(this) + } } // The fields are named by their IDs @@ -83,7 +110,7 @@ object ItemModelLoader { @SubscribeEvent(priority = EventPriority.LOW) @Suppress("UNUSED_PARAMETER") fun loadModels(event: Register) { - KuarryModItems.allItems.forEach { (_, item) -> + KuarryModItems.commonlyRegisteredItems.forEach { (_, item) -> item.apply { ModelLoader.setCustomModelResourceLocation(this, 0, ModelResourceLocation(this.registryName!!, "inventory")) } diff --git a/src/main/kotlin/org/kotobank/kuarry/block/KuarryBlock.kt b/src/main/kotlin/org/kotobank/kuarry/block/KuarryBlock.kt index 06f4bb3..cf2d973 100644 --- a/src/main/kotlin/org/kotobank/kuarry/block/KuarryBlock.kt +++ b/src/main/kotlin/org/kotobank/kuarry/block/KuarryBlock.kt @@ -1,8 +1,10 @@ package org.kotobank.kuarry.block +import java.util.ArrayList import net.minecraft.block.Block import net.minecraft.block.material.Material import net.minecraft.block.properties.PropertyDirection +import net.minecraft.block.properties.PropertyInteger import net.minecraft.block.state.BlockStateContainer import net.minecraft.block.state.IBlockState import net.minecraft.client.gui.GuiScreen @@ -25,6 +27,7 @@ import net.minecraftforge.fml.relauncher.Side import net.minecraftforge.fml.relauncher.SideOnly import net.minecraftforge.items.ItemStackHandler import org.kotobank.kuarry.helper.TranslationHelper +import org.kotobank.kuarry.item.LevelUpgrade import org.kotobank.kuarry.KuarryMod import org.kotobank.kuarry.KuarryModGUIHandler import org.kotobank.kuarry.tile_entity.KuarryTileEntity @@ -36,6 +39,8 @@ class KuarryBlock(material: Material, registryName: String) : Block(material), I val FACING: PropertyDirection = PropertyDirection.create("facing", EnumFacing.Plane.HORIZONTAL) + val LEVEL: PropertyInteger = PropertyInteger.create("level", 0, 2) + val tileEntityClass = KuarryTileEntity::class.java } @@ -58,13 +63,21 @@ class KuarryBlock(material: Material, registryName: String) : Block(material), I playerIn: EntityPlayer, hand: EnumHand, facing: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean { if (!worldIn.isRemote) { + val item = playerIn.getHeldItem(hand).item + val te = worldIn.getTileEntity(pos) + // If the item used is an upgrade and the tile entity exists, DON'T open the GUI, + // because the block will be upgraded + if (item is LevelUpgrade && te is KuarryTileEntity && item.shouldUpgrade(te)) { + return false + } + playerIn.openGui(KuarryMod, KuarryModGUIHandler.KUARRY, worldIn, pos.x, pos.y, pos.z) } - return true; + return true } - override fun createBlockState() = BlockStateContainer(this, FACING) + override fun createBlockState() = BlockStateContainer(this, FACING, LEVEL) override fun getMetaFromState(state: IBlockState) = state.getValue(FACING).index @@ -156,6 +169,16 @@ class KuarryBlock(material: Material, registryName: String) : Block(material), I } } + override fun getActualState(state: IBlockState, worldIn: IBlockAccess, pos: BlockPos): IBlockState { + val te = worldIn.getTileEntity(pos) + return if (te is KuarryTileEntity) { + // If there tile entity already exists, return the upgrade level from it + state.withProperty(LEVEL, te.upgradeLevel) + } else { + state + } + } + // region IDismantleable implementation @Optional.Method(modid = "cofhcore") diff --git a/src/main/kotlin/org/kotobank/kuarry/container/kuarry/KuarryContainer.kt b/src/main/kotlin/org/kotobank/kuarry/container/kuarry/KuarryContainer.kt index f8480bf..17054a0 100644 --- a/src/main/kotlin/org/kotobank/kuarry/container/kuarry/KuarryContainer.kt +++ b/src/main/kotlin/org/kotobank/kuarry/container/kuarry/KuarryContainer.kt @@ -50,7 +50,7 @@ class KuarryContainer(inventoryPlayer: InventoryPlayer, val tileEntity: KuarryTi // Add the upgrade inventory val upgradeInventory = tileEntity.upgradeInventory - InventoryHelper.forEachPositionInInventory(KuarryTileEntity.upgradeInventoryWidth, KuarryTileEntity.upgradeInventoryHeight) { + InventoryHelper.forEachPositionInInventory(tileEntity.upgradeInventoryWidth, tileEntity.upgradeInventoryHeight) { positionInInventory: Int, widthPos: Int, heightPos: Int -> addSlotToContainer( diff --git a/src/main/kotlin/org/kotobank/kuarry/container/kuarry/KuarryGUIContainer.kt b/src/main/kotlin/org/kotobank/kuarry/container/kuarry/KuarryGUIContainer.kt index eec9662..2a58db7 100644 --- a/src/main/kotlin/org/kotobank/kuarry/container/kuarry/KuarryGUIContainer.kt +++ b/src/main/kotlin/org/kotobank/kuarry/container/kuarry/KuarryGUIContainer.kt @@ -11,16 +11,29 @@ import org.kotobank.kuarry.packet.SwitchKuarrySetting import org.kotobank.kuarry.tile_entity.KuarryTileEntity.ActivationMode import org.kotobank.kuarry.container.BaseGUIContainer import org.kotobank.kuarry.integration.autopushing.Autopusher +import org.kotobank.kuarry.item.LevelValues class KuarryGUIContainer(override val container: KuarryContainer) : BaseGUIContainer(container) { companion object { - private const val energyBarTopY = 76 + private const val energyBarTopY = 184 private const val energyBarPlaceX = 194 private const val energyBarTextureX = 240 private const val energyBarHeight = 38 private const val energyBarWidth = 14 + + private const val upgradeInvX = 182 + private const val upgradeInvTopY = 0 + private const val upgradeInvWidth = 50 + private const val upgradeInvSurroundersHeight = 7 + private const val upgradeInvSlotLineHeight = 18 + + private const val upgradeInvSlotLineTextureX = 182 + private const val upgradeInvSlotLineTextureY = 230 + private const val upgradeInvSurroundersTextureX = 131 + private const val upgradeInvHeaderTextureY = 230 + private const val upgradeInvBottomTextureY = 241 } override val actualXSize = 231 @@ -58,6 +71,32 @@ class KuarryGUIContainer(override val container: KuarryContainer) : BaseGUIConta energyBarTextureX, energyBarTopY + energyBarHeight - scaledHeight, energyBarWidth, scaledHeight ) + + // Upgrade inventory header + drawTexturedModalRect( + guiLeft + upgradeInvX, guiTop + upgradeInvTopY, + upgradeInvSurroundersTextureX, upgradeInvHeaderTextureY, + upgradeInvWidth, upgradeInvSurroundersHeight + ) + val upgradeSlotLines = LevelValues[container.tileEntity.upgradeLevel].upgradeSlotLines + // Draw all the lines + for (i in 0 until upgradeSlotLines) { + drawTexturedModalRect( + guiLeft + upgradeInvX, + // Add the header + all the lines' heights that were before this line + guiTop + upgradeInvTopY + upgradeInvSurroundersHeight + (i * upgradeInvSlotLineHeight), + upgradeInvSlotLineTextureX, upgradeInvSlotLineTextureY, + upgradeInvWidth, upgradeInvSlotLineHeight + ) + } + // Draw the inventory bottom after all the lines + drawTexturedModalRect( + guiLeft + upgradeInvX, + guiTop + upgradeInvTopY + upgradeInvSurroundersHeight + + (upgradeSlotLines * upgradeInvSlotLineHeight), + upgradeInvSurroundersTextureX, upgradeInvBottomTextureY, + upgradeInvWidth, upgradeInvSurroundersHeight + ) } } diff --git a/src/main/kotlin/org/kotobank/kuarry/item/KuarryItemBlock.kt b/src/main/kotlin/org/kotobank/kuarry/item/KuarryItemBlock.kt new file mode 100644 index 0000000..2822e3a --- /dev/null +++ b/src/main/kotlin/org/kotobank/kuarry/item/KuarryItemBlock.kt @@ -0,0 +1,18 @@ +package org.kotobank.kuarry.item + +import net.minecraft.block.Block +import net.minecraft.item.ItemBlock +import net.minecraft.item.ItemStack + +class KuarryItemBlock(block: Block) : ItemBlock(block) { + override fun getUnlocalizedName(stack: ItemStack): String { + val blockName = super.getUnlocalizedName(stack) + + // Get the name based on the level + return when (val level = stack.tagCompound?.getInteger("upgrade_level") ?: 0) { + 1, 2 -> + "${blockName}.level_${level + 1}" + else -> super.getUnlocalizedName(stack) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/kotobank/kuarry/item/LevelUpgrade.kt b/src/main/kotlin/org/kotobank/kuarry/item/LevelUpgrade.kt new file mode 100644 index 0000000..d4bd959 --- /dev/null +++ b/src/main/kotlin/org/kotobank/kuarry/item/LevelUpgrade.kt @@ -0,0 +1,62 @@ +package org.kotobank.kuarry.item + +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.init.SoundEvents +import net.minecraft.item.Item +import net.minecraft.util.* +import net.minecraft.util.math.BlockPos +import net.minecraft.world.World +import org.kotobank.kuarry.tile_entity.KuarryTileEntity + +/** Data storage for various level-dependant things of the [KuarryTileEntity]. */ +data class LevelProperties(val energy: Int, val upgradeSlotLines: Int) + +/** Data for all levels. */ +val LevelValues = arrayOf( + // Base + LevelProperties(100_000, 3), + // Hardened + LevelProperties(200_000, 4), + // Reinforced + LevelProperties(300_000, 5) +) + +sealed class LevelUpgrade : Item() { + abstract val level: Int + + /** Whether the upgrade should apply. + * + * Currently the only condition is that the machine is of the level directly below the upgrade level. + */ + fun shouldUpgrade(te: KuarryTileEntity) = level - 1 == te.upgradeLevel + + override fun onItemUse(player: EntityPlayer, worldIn: World, pos: BlockPos, hand: EnumHand, facing: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): EnumActionResult { + val itemstack = player.getHeldItem(hand) + + if (itemstack.item == this) { + val te = worldIn.getTileEntity(pos) + if (te is KuarryTileEntity) { + if (shouldUpgrade(te)) { + // Set the level and shrink the amount of upgrades + te.upgradeLevel = level + itemstack.shrink(1) + + // Play the upgrade sound, pass null as a player so it plays for everyone + worldIn.playSound(null, pos, SoundEvents.BLOCK_ANVIL_USE, SoundCategory.PLAYERS, 1f, 1f) + + return EnumActionResult.SUCCESS + } + } + } + + return EnumActionResult.PASS + } +} + +class HardenedUpgrade : LevelUpgrade() { + override val level = 1 +} + +class ReinforcedUpgrade : LevelUpgrade() { + override val level = 2 +} \ No newline at end of file diff --git a/src/main/kotlin/org/kotobank/kuarry/tile_entity/KuarryTileEntity.kt b/src/main/kotlin/org/kotobank/kuarry/tile_entity/KuarryTileEntity.kt index 606f608..a7cc2c4 100644 --- a/src/main/kotlin/org/kotobank/kuarry/tile_entity/KuarryTileEntity.kt +++ b/src/main/kotlin/org/kotobank/kuarry/tile_entity/KuarryTileEntity.kt @@ -13,6 +13,7 @@ import net.minecraft.network.play.server.SPacketUpdateTileEntity import net.minecraft.tileentity.TileEntity import net.minecraft.util.* import net.minecraft.util.math.* +import net.minecraft.world.World import net.minecraft.world.chunk.Chunk import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.energy.* @@ -20,6 +21,7 @@ import net.minecraftforge.items.* import net.minecraftforge.fluids.IFluidBlock import net.minecraftforge.fml.common.Loader import org.kotobank.kuarry.* +import org.kotobank.kuarry.helper.InventoryHelper import org.kotobank.kuarry.integration.autopushing.Autopusher import org.kotobank.kuarry.item.* import org.kotobank.kuarry.integration.MjReceiverImpl @@ -27,10 +29,6 @@ import kotlin.reflect.KClass class KuarryTileEntity : TileEntity(), ITickable { companion object { - internal const val upgradeInventoryWidth = 2 - internal const val upgradeInventoryHeight = 3 - internal const val upgradeInventorySize = upgradeInventoryWidth * upgradeInventoryHeight - internal const val packetEntityID = 0 /** The blocks that cannot be un-blacklisted by an external filter */ @@ -67,7 +65,14 @@ class KuarryTileEntity : TileEntity(), ITickable { } private var lastEnergyStored = 0 - private val energyStorage = EnergyStorage(100000, 2000, 5000) + private val energyStorageCapacity + get() = LevelValues[upgradeLevel].energy + private val energyStorage = object : EnergyStorage(energyStorageCapacity, 2000, 5000) { + /** The capacity of the storage that can also be changed. */ + var capacity + get() = super.capacity + set(value) { super.capacity = value } + } /** IMjReceiver implementation for BuildCraft compatibility */ private val mjEnergyStorage: MjReceiverImpl? by lazy { @@ -85,14 +90,62 @@ class KuarryTileEntity : TileEntity(), ITickable { } } + internal val upgradeInventoryWidth = 2 + internal val upgradeInventoryHeight + get() = + LevelValues[upgradeLevel].upgradeSlotLines + internal val upgradeInventorySize + get() = upgradeInventoryWidth * upgradeInventoryHeight + + /** A small inventory for upgrades not exposed via getCapability */ - internal val upgradeInventory = object : ItemStackHandler(upgradeInventoryWidth * upgradeInventoryHeight) { + internal val upgradeInventory = object : ItemStackHandler(upgradeInventorySize) { override fun onContentsChanged(slot: Int) { super.onContentsChanged(slot) markDirty() } } + /** The level of the kuarry. The number is used to access the data from the [LevelValues] at that index. */ + var upgradeLevel = 0 + set(value) { + // Only do this if the level actually changed + if (field != value) { + // Update the field, this will change upgradeInventorySize + field = value + + val oldSize = upgradeInventory.slots + // If the new size is different, the inventory needs to be expanded + if (oldSize != upgradeInventorySize) { + val oldHeight = oldSize / upgradeInventoryWidth + // Copy all the items in the same order the player sees them by saving their width/height positions + val oldItems = HashMap, ItemStack>(oldSize) + InventoryHelper.forEachPositionInInventory(upgradeInventoryWidth, oldHeight) { + posInInventory: Int, widthPos: Int, heightPos: Int -> + + oldItems[Pair(widthPos, heightPos)] = upgradeInventory.getStackInSlot(posInInventory) + + false + } + + // Set the new upgrade inventory size, after the field has been updated + upgradeInventory.setSize(upgradeInventorySize) + + // Restore all the items to their orignal positions + oldItems.forEach { (widthPos, heightPos), stack -> + val pos = (widthPos * upgradeInventoryHeight) + heightPos + + upgradeInventory.setStackInSlot(pos, stack) + } + } + + // Update the energy storage capacity with the new level's value + energyStorage.capacity = energyStorageCapacity + + notifyClientAndMarkDirty() + } + } + private val autopusher by lazy { if (Autopusher.isEnabled) Autopusher(this, inventory, inventoryWidth, inventoryHeight) @@ -116,9 +169,11 @@ class KuarryTileEntity : TileEntity(), ITickable { */ internal var activationMode = ActivationMode.AlwaysOn set(value) { - field = value + if (field != value) { + field = value - notifyClientAndMarkDirty() + notifyClientAndMarkDirty() + } } /** Switches the [activationMode] circularly and notifies the client about the change. @@ -153,9 +208,11 @@ class KuarryTileEntity : TileEntity(), ITickable { get() = Autopusher.isEnabled && field set(value) { - field = value + if (field != value) { + field = value - notifyClientAndMarkDirty() + notifyClientAndMarkDirty() + } } /** Toggles [autopush] on or off. */ @@ -212,6 +269,8 @@ class KuarryTileEntity : TileEntity(), ITickable { setString("activation_mode", activationMode.name) setBoolean("autopush", autopush) + + setInteger("upgrade_level", upgradeLevel) } /** Read mod's NBT data, but not game's NBT data. */ @@ -223,6 +282,8 @@ class KuarryTileEntity : TileEntity(), ITickable { activationMode = ActivationMode.valueOf(getString("activation_mode")) autopush = getBoolean("autopush") + + upgradeLevel = getInteger("upgrade_level") } } diff --git a/src/main/resources/assets/kuarry/blockstates/kuarry.json b/src/main/resources/assets/kuarry/blockstates/kuarry.json index 668fc8b..d4ed37f 100644 --- a/src/main/resources/assets/kuarry/blockstates/kuarry.json +++ b/src/main/resources/assets/kuarry/blockstates/kuarry.json @@ -8,11 +8,25 @@ } }, "variants": { - "facing=north": { "model": "kuarry:kuarry" }, - "facing=south": { "model": "kuarry:kuarry", "y": 180, "uvlock": true }, - "facing=east": { "model": "kuarry:kuarry", "y": 90, "uvlock": true }, - "facing=west": { "model": "kuarry:kuarry", "y": 270, "uvlock": true }, + "level": { + "0": { + "textures": { "front": "kuarry:block/kuarry_front", "side": "kuarry:block/kuarry_side" } + }, + "1": { + "textures": { "front": "kuarry:block/kuarry_front_level_2", "side": "kuarry:block/kuarry_side_level_2" } + }, + "2": { + "textures": { "front": "kuarry:block/kuarry_front_level_3", "side": "kuarry:block/kuarry_side_level_3" } + } + }, - "inventory": {"model": "kuarry:kuarry"} + "facing": { + "north": { "model": "kuarry:kuarry" }, + "south": { "model": "kuarry:kuarry", "y": 180, "uvlock": true }, + "east": { "model": "kuarry:kuarry", "y": 90, "uvlock": true }, + "west": { "model": "kuarry:kuarry", "y": 270, "uvlock": true } + }, + + "inventory": { "model": "kuarry:kuarry" } } } \ No newline at end of file diff --git a/src/main/resources/assets/kuarry/lang/en_us.lang b/src/main/resources/assets/kuarry/lang/en_us.lang index a2a5a9f..cfe91e5 100644 --- a/src/main/resources/assets/kuarry/lang/en_us.lang +++ b/src/main/resources/assets/kuarry/lang/en_us.lang @@ -1,6 +1,8 @@ #PARSE_ESCAPES tile.kuarry.name=Kuarry +tile.kuarry.level_2.name=Kuarry (Hardened) +tile.kuarry.level_3.name=Kuarry (Reinforced) tile.kuarry.tooltips.description=It sucks resources out from the bowels of earth, and leaves just a frothy stone-like sponge in place of matter. \ Who knows, what will crawl out tomorrow, woken up by the cleeky intruders from the surface? tile.kuarry.tooltips.energy_stored=%sEnergy stored @@ -52,6 +54,9 @@ item.custom_filter.gui.default_blacklist=Default blacklist item.speed_upgrade.name=Kuarry speed upgrade item.speed_upgrade.description=§c§lThe red one goes faster +item.level_2_upgrade.name=Kuarry hardened upgrade +item.level_3_upgrade.name=Kuarry reinforced upgrade + tooltips.about_energy_multiplication=Multiplies energy usage by %s tooltips.hold_shift_for_ext_desc=[Hold %1$sShift%2$s for a detailed description] \ No newline at end of file diff --git a/src/main/resources/assets/kuarry/models/item/level_2_upgrade.json b/src/main/resources/assets/kuarry/models/item/level_2_upgrade.json new file mode 100644 index 0000000..0980b4b --- /dev/null +++ b/src/main/resources/assets/kuarry/models/item/level_2_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "kuarry:item/level_2_upgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/kuarry/models/item/level_3_upgrade.json b/src/main/resources/assets/kuarry/models/item/level_3_upgrade.json new file mode 100644 index 0000000..8dd4ca3 --- /dev/null +++ b/src/main/resources/assets/kuarry/models/item/level_3_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "kuarry:item/level_3_upgrade" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/kuarry/textures/block/kuarry_front_level_2.png b/src/main/resources/assets/kuarry/textures/block/kuarry_front_level_2.png new file mode 100644 index 0000000..accb3fd Binary files /dev/null and b/src/main/resources/assets/kuarry/textures/block/kuarry_front_level_2.png differ diff --git a/src/main/resources/assets/kuarry/textures/block/kuarry_front_level_3.png b/src/main/resources/assets/kuarry/textures/block/kuarry_front_level_3.png new file mode 100644 index 0000000..5418afc Binary files /dev/null and b/src/main/resources/assets/kuarry/textures/block/kuarry_front_level_3.png differ diff --git a/src/main/resources/assets/kuarry/textures/block/kuarry_side_level_2.png b/src/main/resources/assets/kuarry/textures/block/kuarry_side_level_2.png new file mode 100644 index 0000000..0a7a933 Binary files /dev/null and b/src/main/resources/assets/kuarry/textures/block/kuarry_side_level_2.png differ diff --git a/src/main/resources/assets/kuarry/textures/block/kuarry_side_level_3.png b/src/main/resources/assets/kuarry/textures/block/kuarry_side_level_3.png new file mode 100644 index 0000000..f5f6e47 Binary files /dev/null and b/src/main/resources/assets/kuarry/textures/block/kuarry_side_level_3.png differ diff --git a/src/main/resources/assets/kuarry/textures/gui/kuarry.png b/src/main/resources/assets/kuarry/textures/gui/kuarry.png index 7bc7571..99d14de 100644 Binary files a/src/main/resources/assets/kuarry/textures/gui/kuarry.png and b/src/main/resources/assets/kuarry/textures/gui/kuarry.png differ diff --git a/src/main/resources/assets/kuarry/textures/item/level_2_upgrade.png b/src/main/resources/assets/kuarry/textures/item/level_2_upgrade.png new file mode 100644 index 0000000..6d8f7cd Binary files /dev/null and b/src/main/resources/assets/kuarry/textures/item/level_2_upgrade.png differ diff --git a/src/main/resources/assets/kuarry/textures/item/level_3_upgrade.png b/src/main/resources/assets/kuarry/textures/item/level_3_upgrade.png new file mode 100644 index 0000000..d709dd8 Binary files /dev/null and b/src/main/resources/assets/kuarry/textures/item/level_3_upgrade.png differ