[1.7.10] Создание аддона для IC2

[1.7.10] Создание аддона для IC2

Версия(и) Minecraft
1.7.10
В разработке!
Achtung Kotlin!
В этом гайде мы будем делать простые и сложные машины, хранилища... Я постарался сделать как можно больше комментариев, поэтому в тексте их нет, если что-то не понятно, спрашивайте в теме.
I часть. Главный файл и зависимости.
Для начала нам нужно установить dev версию IC2. Заполним build.gradle.
Gradle (Groovy):
apply plugin: 'forge'
apply plugin: 'kotlin'
apply plugin: 'idea'

buildscript {
    repositories {
        mavenCentral()

        maven {
            name = "forge"
            url = "https://maven.minecraftforge.net/"
        }
        maven {
            name = "sonatype"
            url = "https://oss.sonatype.org/content/repositories/snapshots/"
        }
    }

    dependencies {
        classpath ('com.anatawa12.forge:ForgeGradle:1.2-1.0.+') {
            changing = true
        }
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10"
    }
}

repositories {
    mavenCentral()
    maven {
        name = "ic2"
        url = "https://maven.ic2.player.to/"
        metadataSources {
            artifact()
        }
    }
}

version = "1.0"
archivesBaseName = "modid"

minecraft {
    version = "1.7.10-10.13.4.1614-1.7.10"
    runDir = "run"
}

dependencies {
    implementation 'net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev'
    implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.10'
}

jar {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    exclude '**/*.kotlin_metadata'
    exclude '**/*.kotlin_module'
    exclude '**/*.kotlin_builtins'
}

idea {
    module {
        inheritOutputDirs = false
        outputDir = file('build/classes/main/')
    }
}

compileJava {
    options.encoding = "UTF-8"
    sourceCompatibility = targetCompatibility = '1.8'
}
Далее - меняем версию Gradle. Открываем gradle/wrapper/gradle-wrapper.properties и меняем в distributionUrl версию на 7.1.1
После этого синхронизируем проект. Дальше нам нужен главный файл нашего аддона. Создадим и заполним его.

Kotlin:
import cpw.mods.fml.common.Mod
import cpw.mods.fml.common.event.FMLPreInitializationEvent

@Mod(modid = "ic2addon", name = "IC2 Addon", version = "0.0.1", dependencies = "required-after:IC2;")
class IC2Addon {
    @Mod.EventHandler
    fun preInit(e: FMLPreInitializationEvent) {
        e.modLog.info("IC2 Addon PreInit")
    }
}
Запускаем игру и любуемся нашим аддоном.


II часть. Простые машины.
Создавать аддон без контента как-то не очень, поэтому мы добавим простых машин. Создадим файл для них. В IC2 используются блоки с метадатой. 0 - простой механизм, 1 - улучшенный механизм, 2-15 - машины(в случаем с первым уровнем). Наш файл должен наследоваться от BlockMultiID.
Kotlin:
import advancedmachines.TileEntityMy2SlotsMacerator
import cpw.mods.fml.common.registry.GameRegistry
import ic2.core.IC2
import ic2.core.block.BlockMultiID
import ic2.core.init.InternalName
import net.minecraft.block.Block
import net.minecraft.block.material.Material
import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
import net.minecraft.world.World
import org.apache.commons.lang3.mutable.MutableObject
import java.util.*

class MyMachine(internalName1: InternalName): BlockMultiID(internalName1, Material.iron, ItemMyMachine::class.java) {
    init {
        this.setHardness(2.0f) // Прочность
        this.setStepSound(Block.soundTypeMetal) // Звук ходьбы
        MyIC2Items.myMachine = ItemStack(this, 1, 0) // Инициализируем переменную. 1 параметр - класс, в нашем случае this, 2 параметр - количестно, ставим 1, 3 параметр - метадата, мин. 0, макс. 15
        MyIC2Items.myMacerator = ItemStack(this, 1, 1) // Инициализируем переменную. 1 параметр - класс, в нашем случае this, 2 параметр - количестно, ставим 1, 3 параметр - метадата, мин. 0, макс. 15
        MyIC2Items.myMacerator = ItemStack(this, 1, 2) // Инициализируем переменную. 1 параметр - класс, в нашем случае this, 2 параметр - количестно, ставим 1, 3 параметр - метадата, мин. 0, макс. 15
        GameRegistry.registerTileEntity(TileEntityMyMacerator::class.java, "My Macerator") // Регестрируем тайл
        GameRegistry.registerTileEntity(TileEntityMy2SlotsMacerator::class.java, "My 2 Slots Macerator") // Регестрируем тайл
    }

    public override fun getTextureFolder(id: Int): String = "myMachine" // Имя папки с текстурами. Путь - assets/ic2/textures/blocks/myMachine

    override fun damageDropped(meta: Int): Int = when(meta) { // Мета предмета который сломан.
        0 -> meta
        1, 2 -> 0
        else -> 0
    }

    override fun getTeClass(meta: Int, ctorArgTypes: MutableObject<Array<Class<*>>>?, ctorArgs: MutableObject<Array<Any>>?): Class<out TileEntity>? {
        return when(meta) {
            1 -> TileEntityMyMacerator::class.java // Возвращаем тайл энтити дробителя
            2 -> TileEntityMy2SlotsMacerator::class.java // Возвращаем тайл энтити дробителя с двумя слотами
            else -> null
        }
    }

    override fun randomDisplayTick(world: World?, x: Int, y: Int, z: Int, random: Random?) { // Создаем партиклы
        if(IC2.platform.isRendering) {
            val meta = world!!.getBlockMetadata(x, y, z)
            val f2: Float
            var fmod: Float
            var f1mod: Float
            var f2mod: Float
            if(meta == 1 && meta == 2 && this.isActive(world, x, y, z)) { // Для дробителя
                val f = x.toFloat() + 1.0f
                val f1 = y.toFloat() + 1.0f
                f2 = z.toFloat() + 1.0f

                for(i in 0..3) {
                    fmod = -0.2f - random!!.nextFloat() * 0.6f
                    f1mod = -0.1f + random.nextFloat() * 0.2f
                    f2mod = -0.2f - random.nextFloat() * 0.6f
                    world.spawnParticle("smoke", (f + fmod).toDouble(), (f1 + f1mod).toDouble(), (f2 + f2mod).toDouble(), 0.0, 0.0, 0.0)
                }
            }
        }
    }

    // Дальше копипаст из класса индастриала
    override fun hasComparatorInputOverride(): Boolean = false
}
Дальше нам нужен тайл. Создадим.
Kotlin:
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import ic2.api.recipe.Recipes
import ic2.core.block.invslot.InvSlotProcessableGeneric
import ic2.core.block.machine.container.ContainerStandardMachine
import ic2.core.block.machine.tileentity.TileEntityStandardMachine
import ic2.core.upgrade.UpgradableProperty
import net.minecraft.client.gui.GuiScreen
import net.minecraft.entity.player.EntityPlayer
import java.util.*

// Ускоряем работу в 3 раза и увеличиваем потребление энергии за тик в 6 раз
class TileEntityMyMacerator: TileEntityStandardMachine(12, 100, 1) {
    init {
        this.inputSlot = InvSlotProcessableGeneric(this, "input", 0, 1, Recipes.macerator) // Указывай входной слот. Менять ничего не нужно, только Recipes.macerator на другой тип если надо
    }

    override fun getInventoryName(): String = "Macerator"


    @SideOnly(Side.CLIENT)
    override fun getGui(entityPlayer: EntityPlayer, isAdmin: Boolean): GuiScreen = GuiMyMacerator(ContainerStandardMachine(entityPlayer, this)) // GUI машины
    override fun getStartSoundFile(): String = "Machines/MaceratorOp.ogg" // Звук начала работы
    override fun getInterruptSoundFile(): String = "Machines/InterruptOne.ogg" // Звук работы
    override fun getWrenchDropRate(): Float = 0.8f // Шанс успешного выпадения машины. Иначе падает механизм. От 0.0 до 1.0. 0.0 - 0%, 1.0 - 100%. За наводку спасибо Merisen
    // Улучшения которые можно установить
    override fun getUpgradableProperties(): Set<UpgradableProperty> = EnumSet.of(UpgradableProperty.Processing, UpgradableProperty.Transformer, UpgradableProperty.EnergyStorage, UpgradableProperty.ItemConsuming, UpgradableProperty.ItemProducing)
}
Дальше, нам нужен GUI. Так же создадим.
Kotlin:
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import ic2.core.GuiIC2
import ic2.core.IC2
import ic2.core.block.machine.container.ContainerStandardMachine
import net.minecraft.util.ResourceLocation
import net.minecraft.util.StatCollector

@SideOnly(Side.CLIENT)
class GuiMyMacerator(private val container: ContainerStandardMachine<out TileEntityMyMacerator>): GuiIC2(container) {
    override fun drawGuiContainerBackgroundLayer(f: Float, x: Int, y: Int) { // Отрисовка прогресса и зарядки
        super.drawGuiContainerBackgroundLayer(f, x, y)
        val chargeLevel = (14.0f * (this.container.base as TileEntityMyMacerator).chargeLevel).toInt()
        val progress = (24.0f * (this.container.base as TileEntityMyMacerator).progress).toInt()
        if(chargeLevel > 0)
            this.drawTexturedModalRect(this.xoffset + 56, this.yoffset + 36 + 14 - chargeLevel, 176, 14 - chargeLevel, 14, chargeLevel)

        if(progress > 0)
            this.drawTexturedModalRect(this.xoffset + 79, this.yoffset + 34, 176, 14, progress + 1, 16)
    }

    override fun getName(): String = StatCollector.translateToLocal("ic2.Macerator.gui.name") // Текст в гуи(название)
    override fun getResourceLocation(): ResourceLocation = ResourceLocation(IC2.textureDomain, "textures/gui/GUIMacerator.png") // Путь до текстуры. Использую стандартну.
}

В классе MyMachine есть какой-то ItemMyMachine. Это - предмет для блоков. Создадим его.
Kotlin:
import ic2.core.item.block.ItemBlockIC2
import net.minecraft.block.Block
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.util.StatCollector

class ItemMyMachine(block: Block): ItemBlockIC2(block) {
    init {
        this.maxDamage = 0
        this.setHasSubtypes(true)
    }

    override fun getMetadata(i: Int): Int = i

    override fun getUnlocalizedName(itemstack: ItemStack): String? { //Нелокализованное имя. ic2 советую оставить
        val meta = itemstack.itemDamage
        return when(meta) {
            0 -> "ic2.blockMyMachine"
            1 -> "ic2.blockMyMacerator"
            else -> null
        }
    }

    override fun addInformation(itemStack: ItemStack, player: EntityPlayer, info: MutableList<Any?>, b: Boolean) {
        val meta = itemStack.itemDamage
        when(meta) {
            // Добавляем описание.
            1 -> info.add(StatCollector.translateToLocal("ic2.item.tooltip.power") + " 3 EU/t, 32 EU/t " + StatCollector.translateToLocal("ic2.item.tooltip.max"))
            else -> {
            }
        }
    }
}
Но все же осталась одна ошибка у MyIC2Items. Это - файл в котором прописываются предметы для блоков. Создадим и заполним его.
Kotlin:
import net.minecraft.item.ItemStack

object MyIC2Items {
    lateinit var myMachine: ItemStack //Механизм
    lateinit var myMacerator: ItemStack //Дробитель
}
И дописываем в preInit MyMachine(InternalName.blockMachine)
Теперь он выглядит так
Kotlin:
import cpw.mods.fml.common.Mod
import cpw.mods.fml.common.event.FMLPreInitializationEvent
import ic2.core.init.InternalName

@Mod(modid = "ic2addon", name = "IC2 Addon", version = "0.0.1", dependencies = "required-after:IC2;")
class IC2Addon {
    @Mod.EventHandler
    fun preInit(e: FMLPreInitializationEvent) {
        e.modLog.info("IC2 Addon PreInit")
        MyMachine(InternalName.blockMachine)
    }
}
Теперь мы можем запускать игру и выдавать предмет. Не забудьте указать метадату.
Результат:
2018-06-10_15.55.24.png


III часть. Сложные машины.
Настанет время, когда TileEntityStandardMachine уже не будет хватать. Например, вы захотите добавить слотов. Для этого нам потребуется создать немного другой механизм. Создадим для него пакет, а в нем тайл.
Kotlin:
package advancedmachines

import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import ic2.api.recipe.Recipes
import ic2.core.ContainerBase
import ic2.core.IC2
import ic2.core.IHasGui
import ic2.core.block.invslot.*
import ic2.core.block.machine.tileentity.TileEntityElectricMachine
import ic2.core.upgrade.IUpgradableBlock
import ic2.core.upgrade.IUpgradeItem
import ic2.core.upgrade.UpgradableProperty
import net.minecraft.client.gui.GuiScreen
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.nbt.NBTTagCompound
import java.util.*

// Аргументы для конструктора TileEntityElectricMachine. 1 - максимальное количество энергии, 2 - уровень машины
class TileEntityMy2SlotsMacerator: TileEntityElectricMachine(256000, 2, 2), IHasGui, IUpgradableBlock {
    private var progress: Short = 0
    val inputSlotA: InvSlotProcessable
    val inputSlotB: InvSlotProcessable
    val outputSlotA: InvSlotOutput
    val outputSlotB: InvSlotOutput
    val upgradeSlot: InvSlotUpgrade

    init {
        // Если хотите добавить еще слотов, создаете еще переменные, но при инициализации третий аргумент должен быть на один больше предыдущего, это - индекс.
        this.inputSlotA = InvSlotProcessableGeneric(this, "inputA", 0, 1, Recipes.macerator) // Первый входной слот
        this.inputSlotB = InvSlotProcessableGeneric(this, "inputB", 1, 1, Recipes.macerator) // Второй входной слот
        this.outputSlotA = InvSlotOutput(this, "outputA", 3, 1) // Первый выходной слот
        this.outputSlotB = InvSlotOutput(this, "outputB", 4, 1) // Второй выходной слот
        this.upgradeSlot = InvSlotUpgrade(this, "upgrade", 5, 2) // Слот для улучшения. Обратите внимание на последний аргумент. Это - количество слотов.
    }

    override fun getInventoryName(): String = if(IC2.platform.isRendering) "My 2 Slots Macerator" else "My2SlotsMacerator" // Имя инвентаря
    override fun getWrenchDropRate(): Float = 0.80f // Шанс успешного выбадения машины. Иначе падает механизм. От 0.0 до 1.0. 0.0 - 0%, 1.0 - 100%. За наводку спасибо Merisen
    override fun getGuiContainer(entityPlayer: EntityPlayer): ContainerBase<TileEntityMy2SlotsMacerator> = ContainerMy2SlotsMacerator(entityPlayer, this) // Указываем контейнер
    @SideOnly(Side.CLIENT)override fun getGui(entityPlayer: EntityPlayer, isAdmin: Boolean): GuiScreen = GuiMy2SlotsMacerator(ContainerMy2SlotsMacerator(entityPlayer, this)) // Указываем GUI

    // Чтение прогресса из NBT
    override fun readFromNBT(nbttagcompound: NBTTagCompound) {
        super.readFromNBT(nbttagcompound)
        this.progress = nbttagcompound.getShort("progress")
    }

    // Запись прогресса в NBT
    override fun writeToNBT(nbttagcompound: NBTTagCompound) {
        super.writeToNBT(nbttagcompound)
        nbttagcompound.setShort("progress", this.progress)
    }

    fun gaugeProgressScaled(i: Int): Int = i * this.progress / 4000 // Прогресс

    // Цикл для работы.
    override fun updateEntityServer() {
        super.updateEntityServer()
        var needsInvUpdate = false
        var newActive = this.active

        if(this.progress >= 4000) { // 4000 - максимальный прогресс, если хотите сдеалть работу быстрее или медленнее уменьшаете или увеличиваете соответственно.
            this.operate()
            needsInvUpdate = true
            this.progress = 0
            newActive = false
        }

        val canOperate = this.canOperate()
        if(this.energy > 0.0 && canOperate) {
            --this.energy
            newActive = true
        }

        if(newActive && this.progress.toInt() != 0) {
            if(!canOperate || this.energy < 15.0) {
                if(!canOperate) {
                    this.progress = 0
                }

                newActive = false
            }
        } else if(canOperate) {
            if(this.energy >= 15.0) {
                newActive = true
            }
        } else {
            this.progress = 0
        }

        if(newActive && canOperate) {
            this.progress = (this.progress + 300).toShort() // Работа. Если хотите сдеалть работу быстрее или медленнее увеличиваете или уменьшаете соответственно
            this.energy -= 256.0 // Потребление энергии за тик
        }

        if(needsInvUpdate) {
            this.markDirty()
        }

        if(newActive != this.active) {
            this.active = newActive
        }

        for(i in 0 until this.upgradeSlot.size()) {
            val stack = this.upgradeSlot.get(i)
            if(stack != null && stack.item is IUpgradeItem && (stack.item as IUpgradeItem).onTick(stack, this)) {
                super.markDirty()
            }
        }

    }

    // Работа со слотами. Забираем из входного, помещаем в выходной
    private fun operate() {
        this.operate(this.inputSlotA, this.outputSlotA)
        this.operate(this.inputSlotB, this.outputSlotB)
    }
    private fun operate(inputSlot: InvSlotProcessable, outputSlot: InvSlotOutput) {
        if(this.canOperate(inputSlot, outputSlot)) {
            outputSlot.add(inputSlot.process().items)
            inputSlot.consume()
        }
    }

    // Можно ли положить предмет в выходной слот или забрать из входного.
    private fun canOperate(): Boolean = this.canOperate(this.inputSlotA, this.outputSlotA) || this.canOperate(this.inputSlotB, this.outputSlotB)
    private fun canOperate(inputSlot: InvSlotProcessable, outputSlot: InvSlotOutput): Boolean = if(inputSlot.isEmpty) { false } else { val output = inputSlot.process(); if(output == null) false else outputSlot.canAdd(output.items) }

    override fun onGuiClosed(entityPlayer: EntityPlayer) {} // Действие при закрытии GUI. Можно оставить пустым
    override fun getEnergy(): Double = this.energy // Получаем энергию в машине
    override fun useEnergy(amount: Double): Boolean = if(this.energy >= amount) { this.energy -= amount; true } else { false } // Используем энергию. Если true, то энергии хватает, иначе не хватает
    override fun getUpgradableProperties(): Set<UpgradableProperty> = EnumSet.of(UpgradableProperty.RedstoneSensitive, UpgradableProperty.ItemConsuming, UpgradableProperty.ItemProducing) // Улучшения которые можно установить
}
Дальше создадим GUI.
Kotlin:
package advancedmachines

import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import ic2.core.GuiIC2
import ic2.core.IC2
import net.minecraft.util.ResourceLocation
import net.minecraft.util.StatCollector

@SideOnly(Side.CLIENT)
class GuiMy2SlotsMacerator(container1: ContainerMy2SlotsMacerator): GuiIC2(container1) {
    override fun drawGuiContainerBackgroundLayer(f: Float, x: Int, y: Int) { // Рисуем зарядку и прогресс
        super.drawGuiContainerBackgroundLayer(f, x, y)
        val chargeLevel = Math.round((this.container.base as TileEntityMy2SlotsMacerator).chargeLevel * 14.0f)
        this.drawTexturedModalRect(this.xoffset + 56, this.yoffset + 36 + 14 - chargeLevel, 176, 14 - chargeLevel, 14, chargeLevel)
        val i1 = (this.container.base as TileEntityMy2SlotsMacerator).gaugeProgressScaled(24)
        this.drawTexturedModalRect(this.xoffset + 79, this.yoffset + 34, 176, 14, i1 + 1, 16)
    }

    override fun getName(): String = StatCollector.translateToLocal("myic2addon.My2SlotsMachine.gui.name") // Имя в GUI
    override fun getResourceLocation(): ResourceLocation = ResourceLocation(IC2.textureDomain, "textures/gui/GUIInduction.png") // Текстура. Используем индукционку для двух слотов.
}
В простой машине мы использовали стандартный контейнер, а сейчас будем делать свой.
Kotlin:
package advancedmachines

import ic2.core.block.machine.container.ContainerElectricMachine
import ic2.core.slot.SlotInvSlot
import net.minecraft.entity.player.EntityPlayer

class ContainerMy2SlotsMacerator(entityPlayer: EntityPlayer, tileEntity: TileEntityMy2SlotsMacerator): ContainerElectricMachine<TileEntityMy2SlotsMacerator>(entityPlayer, tileEntity, 166, 56, 53) {
    init {
        // Добавляем слоты в контейнер
        this.addSlotToContainer(SlotInvSlot(tileEntity.inputSlotA, 0, 47, 17))
        this.addSlotToContainer(SlotInvSlot(tileEntity.inputSlotB, 0, 63, 17))
        this.addSlotToContainer(SlotInvSlot(tileEntity.outputSlotA, 0, 113, 35))
        this.addSlotToContainer(SlotInvSlot(tileEntity.outputSlotB, 0, 131, 35))

        for(i in 0..1) {
            this.addSlotToContainer(SlotInvSlot(tileEntity.upgradeSlot, i, 153, 26 + i * 18))
        }
    }

    // Сетевые переменные. Что бы другие игроки видели прогресс
    override fun getNetworkedFields(): List<String> {
        val ret = super.getNetworkedFields()
        ret.add("progress")
        return ret
    }
}

Дальше нам осталось добавить его в класс с машинами. Теперь MyMachine выглядит так:
Kotlin:
import advancedmachines.TileEntityMy2SlotsMacerator
import cpw.mods.fml.common.registry.GameRegistry
import ic2.api.item.ITerraformingBP
import ic2.core.IC2
import ic2.core.block.BlockMultiID
import ic2.core.block.TileEntityBlock
import ic2.core.block.machine.tileentity.*
import ic2.core.init.InternalName
import net.minecraft.block.Block
import net.minecraft.block.material.Material
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
import net.minecraft.world.World
import org.apache.commons.lang3.mutable.MutableObject
import java.util.*

class MyMachine(internalName1: InternalName): BlockMultiID(internalName1, Material.iron, ItemMyMachine::class.java) {
    init {
        this.setHardness(2.0f) // Прочность
        this.setStepSound(Block.soundTypeMetal) // Звук ходьбы
        MyIC2Items.myMachine = ItemStack(this, 1, 0) // Инициализируем переменную. 1 параметр - класс, в нашем случае this, 2 параметр - количестно, ставим 1, 3 параметр - метадата, мин. 0, макс. 15
        MyIC2Items.myMacerator = ItemStack(this, 1, 1) // Инициализируем переменную. 1 параметр - класс, в нашем случае this, 2 параметр - количестно, ставим 1, 3 параметр - метадата, мин. 0, макс. 15
        MyIC2Items.myMacerator = ItemStack(this, 1, 2) // Инициализируем переменную. 1 параметр - класс, в нашем случае this, 2 параметр - количестно, ставим 1, 3 параметр - метадата, мин. 0, макс. 15
        GameRegistry.registerTileEntity(TileEntityMyMacerator::class.java, "My Macerator") // Регестрируем тайл
        GameRegistry.registerTileEntity(TileEntityMy2SlotsMacerator::class.java, "My 2 Slots Macerator") // Регестрируем тайл
    }

    public override fun getTextureFolder(id: Int): String = "myMachine" // Имя папки с текстурами. Путь - assets/ic2/textures/blocks/myMachine
    override fun damageDropped(meta: Int): Int = meta // Мета предмета
    override fun getTeClass(meta: Int, ctorArgTypes: MutableObject<Array<Class<*>>>?, ctorArgs: MutableObject<Array<Any>>?): Class<out TileEntity>? {
        return when(meta) {
            1 -> TileEntityMyMacerator::class.java // Возвращаем тайл энтити
            2 -> TileEntityMy2SlotsMacerator::class.java // Возвращаем тайл энтити
            else -> null
        }
    }

    override fun randomDisplayTick(world: World?, x: Int, y: Int, z: Int, random: Random?) { // Создаем партиклы
        if(IC2.platform.isRendering) {
            val meta = world!!.getBlockMetadata(x, y, z)
            val f2: Float
            var fmod: Float
            var f1mod: Float
            var f2mod: Float
            if(meta == 1 && meta == 2 && this.isActive(world, x, y, z)) { // Для дробителя
                val f = x.toFloat() + 1.0f
                val f1 = y.toFloat() + 1.0f
                f2 = z.toFloat() + 1.0f

                for(i in 0..3) {
                    fmod = -0.2f - random!!.nextFloat() * 0.6f
                    f1mod = -0.1f + random.nextFloat() * 0.2f
                    f2mod = -0.2f - random.nextFloat() * 0.6f
                    world.spawnParticle("smoke", (f + fmod).toDouble(), (f1 + f1mod).toDouble(), (f2 + f2mod).toDouble(), 0.0, 0.0, 0.0)
                }
            }
        }
    }

    // Дальше копипаст из ванильного класса
    override fun hasComparatorInputOverride(): Boolean {
        return true
    }

    override fun getComparatorInputOverride(world: World?, x: Int, y: Int, z: Int, side: Int): Int {
        val te = this.getOwnTe(world!!, x, y, z) as TileEntityBlock
        return when(te) {
            is TileEntityInduction -> Math.floor((te.heat.toFloat() / TileEntityInduction.maxHeat.toFloat() * 15.0f).toDouble()).toInt()
            is TileEntityMatter -> Math.floor(te.energy / 1000000.0 * 15.0).toInt()
            is TileEntityElectrolyzer -> Math.floor((te.energy.toFloat() / 20000.0f * 15.0f).toDouble()).toInt()
            is TileEntityStandardMachine -> Math.floor((te.progress * 15.0f).toDouble()).toInt()
            else -> 0
        }
    }

    override fun onBlockActivated(world: World?, x: Int, y: Int, z: Int, player: EntityPlayer, side: Int, hitX: Float, hitY: Float, hitZ: Float): Boolean {
        if(world!!.getBlockMetadata(x, y, z) == 15) {
            when {
                player.isSneaking -> return false
                world.isRemote -> return true
                else -> {
                    val te = world.getTileEntity(x, y, z) as TileEntityTerra
                    return when {
                        player.inventory.getCurrentItem() == null -> {
                            te.ejectBlueprint()
                            true
                        }
                        player.inventory.getCurrentItem().item is ITerraformingBP -> {
                            te.insertBlueprint(player.inventory.getCurrentItem().copy())
                            --player.inventory.getCurrentItem().stackSize
                            true
                        }
                        else -> false
                    }
                }
            }
        } else {
            return super.onBlockActivated(world, x, y, z, player, side, hitX, hitY, hitZ)
        }
    }
}
А ItemMyMachine так:
Kotlin:
import ic2.core.item.block.ItemBlockIC2
import net.minecraft.block.Block
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.util.StatCollector

class ItemMyMachine(block: Block): ItemBlockIC2(block) {
    init {
        this.maxDamage = 0
        this.setHasSubtypes(true)
    }

    override fun getMetadata(i: Int): Int = i

    override fun getUnlocalizedName(itemstack: ItemStack): String? { //Нелокализованное имя. ic2 советую оставить
        val meta = itemstack.itemDamage
        return when(meta) {
            0 -> "ic2.blockMyMachine"
            1 -> "ic2.blockMyMacerator"
            2 -> "ic2.blockMy2SlotsMacerator"
            else -> null
        }
    }

    override fun addInformation(itemStack: ItemStack, player: EntityPlayer, info: MutableList<Any?>, b: Boolean) {
        val meta = itemStack.itemDamage
        when(meta) {
            // Добавляем описание.
            1 -> info.add(StatCollector.translateToLocal("ic2.item.tooltip.power") + " 12 EU/t, 32 EU/t " + StatCollector.translateToLocal("ic2.item.tooltip.max"))
            2 -> info.add(StatCollector.translateToLocal("ic2.item.tooltip.power") + " 256 EU/t, 32 EU/t " + StatCollector.translateToLocal("ic2.item.tooltip.max"))
            else -> {
            }
        }
    }
}
Результат:
2018-06-10_15.48.41.png

Но что это? У нас стрелка вместо дробителя. Для этого надо немного изменить текстуру и поменять путь в GUI. Сделаем это!
GUIMy2SlotsMacerator.png

Кладем текстуру по пути assets/ic2/textures/gui
И теперь все хорошо.
2018-06-10_17.03.47.png

Так же установим текстуру и локализуем.
blockMy2SlotsMacerator.png

Вот такая текстура. Кладем текстуру по пути assets/ic2/textures/blocks/myMachine
Осталось локализовать.
Код:
myic2addon.My2SlotsMachine.gui.name=My 2 slots Macerator
ic2.blockMy2SlotsMacerator=My 2 slots Macerator
Путь assets/modid/lang
Результат:
2018-06-10_17.06.59.png

2018-06-10_17.07.11.png

Вы можете добавить больше 2 слотов. Я максимум я добавлял 4.
IV часть. Свое хранилище энергии.
Для начала создадим файл с хранилищами.
Kotlin:
package wiring

import MyIC2Items
import cpw.mods.fml.common.registry.GameRegistry
import ic2.core.IC2
import ic2.core.Ic2Items
import ic2.core.block.BlockMultiID
import ic2.core.block.TileEntityBlock
import ic2.core.block.wiring.TileEntityElectricBlock
import ic2.core.init.InternalName
import ic2.core.init.MainConfig
import ic2.core.util.ConfigUtil
import ic2.core.util.StackUtil
import ic2.core.util.Util
import net.minecraft.block.Block
import net.minecraft.block.material.Material
import net.minecraft.entity.EntityLivingBase
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
import net.minecraft.util.MathHelper
import net.minecraft.world.IBlockAccess
import net.minecraft.world.World
import org.apache.commons.lang3.mutable.MutableObject
import java.util.*

class MyBlockElectric(internalName1: InternalName): BlockMultiID(internalName1, Material.iron, MyItemElectricBlock::class.java) {
    init {
        // Уже знакомая конструкция
        this.setHardness(1.5f)
        this.setStepSound(Block.soundTypeMetal)
        MyIC2Items.smallEnergyStorage = ItemStack(this, 1, 0)
        MyIC2Items.bigEnergyStorage = ItemStack(this, 1, 1)
        GameRegistry.registerTileEntity(TilEntityElectricSmallEnergyStorage::class.java, "Small Energy Storage")
        GameRegistry.registerTileEntity(TilEntityElectricBigEnergyStorage::class.java, "Big Energy Storage")
    }

    override fun getTextureFolder(id: Int): String = "wiring" // Имя папки с текстурой

    override fun getTeClass(meta: Int, ctorArgTypes: MutableObject<Array<Class<*>>>?, ctorArgs: MutableObject<Array<Any>>?): Class<out TileEntity>? {
        return when(meta) {
            0 -> TilEntityElectricSmallEnergyStorage::class.java
            1 -> TilEntityElectricBigEnergyStorage::class.java
            else -> null
        }
    }

    // Дальше копипаст из класса индастриала
    override fun getItemDropped(meta: Int, random: Random?, fortune: Int): Item {
        return if(ConfigUtil.getBool(MainConfig.get(), "balance/ignoreWrenchRequirement")) {
            Item.getItemFromBlock(this)
        } else {
            when(meta) {
                0, 3 -> Item.getItemFromBlock(this)
                else -> Ic2Items.machine.item
            }
        }
    }
    override fun damageDropped(meta: Int): Int {
        return if(ConfigUtil.getBool(MainConfig.get(), "balance/ignoreWrenchRequirement")) {
            meta
        } else {
            when(meta) {
                0, 3 -> meta
                else -> Ic2Items.machine.itemDamage
            }
        }
    }
    override fun quantityDropped(random: Random?): Int = 1
    override fun isProvidingWeakPower(blockAccess: IBlockAccess?, x: Int, y: Int, z: Int, side: Int): Int {
        val te = this.getOwnTe(blockAccess!!, x, y, z) as TileEntityBlock
        return if(te !is TileEntityElectricBlock) {
            0
        } else {
            if(te.isEmittingRedstone) 15 else 0
        }
    }
    override fun canProvidePower(): Boolean = true
    override fun isNormalCube(world: IBlockAccess?, i: Int, j: Int, k: Int): Boolean = true
    override fun isBlockSolid(world: IBlockAccess, x: Int, y: Int, z: Int, side: Int): Boolean = true
    override fun onBlockPlacedBy(world: World?, x: Int, y: Int, z: Int, entityliving: EntityLivingBase?, itemStack: ItemStack?) {
        if(IC2.platform.isSimulating) {
            val te = this.getOwnTe(world!!, x, y, z) as TileEntityBlock
            if(te is TileEntityElectricBlock) {
                val nbttagcompound = StackUtil.getOrCreateNbtData(itemStack!!)
                te.energy = nbttagcompound.getDouble("energy")
            }

            if(entityliving == null) {
                te.facing = 1.toShort()
            } else {
                val yaw = MathHelper.floor_double((entityliving.rotationYaw * 4.0f / 360.0f).toDouble() + 0.5) and 3
                val pitch = Math.round(entityliving.rotationPitch)
                when {
                    pitch >= 65 -> te.facing = 1.toShort()
                    pitch <= -65 -> te.facing = 0.toShort()
                    else -> when(yaw) {
                        0 -> te.facing = 2.toShort()
                        1 -> te.facing = 5.toShort()
                        2 -> te.facing = 3.toShort()
                        3 -> te.facing = 4.toShort()
                    }
                }
            }
        }
    }
    override fun hasComparatorInputOverride(): Boolean = true
    override fun getComparatorInputOverride(world: World?, x: Int, y: Int, z: Int, side: Int): Int {
        val te = this.getOwnTe(world!!, x, y, z) as TileEntityBlock
        val teb = te as TileEntityElectricBlock
        return Math.round(Util.map(teb.energy, teb.maxStorage.toDouble(), 15.0)).toInt()
    }
}
Обратите внимая, что комментариев стало меньше. Создадим тайлы.
Первый:
Kotlin:
package wiring

import ic2.core.block.wiring.ContainerElectricBlock
import ic2.core.block.wiring.TileEntityElectricBlock
import net.minecraft.client.gui.GuiScreen
import net.minecraft.entity.player.EntityPlayer

// Аргументы TileEntityElectricBlock. 1 - уровень, 2 - выходное напряжение(EU/t), 3 - максимальное количество энергии
class TilEntityElectricSmallEnergyStorage: TileEntityElectricBlock(5, 4096, 1000000000) {
    override fun getInventoryName(): String = "SES"
    override fun getGui(entityPlayer: EntityPlayer?, isAdmin: Boolean): GuiScreen = GuiMyElectricBlock(ContainerElectricBlock(entityPlayer, this))
}
Второй:
Kotlin:
package wiring

import ic2.core.block.wiring.ContainerElectricBlock
import ic2.core.block.wiring.TileEntityElectricBlock
import net.minecraft.client.gui.GuiScreen
import net.minecraft.entity.player.EntityPlayer

// Аргументы TileEntityElectricBlock. 1 - уровень, 2 - выходное напряжение(EU/t), 3 - максимальное количество энергии
class TilEntityElectricBigEnergyStorage: TileEntityElectricBlock(6, 8192, 2000000000) {
    override fun getInventoryName(): String = "BES"
    override fun getGui(entityPlayer: EntityPlayer?, isAdmin: Boolean): GuiScreen = GuiMyElectricBlock(ContainerElectricBlock(entityPlayer, this))
}
Создадим GUI
Kotlin:
package wiring

import ic2.core.GuiIconButton
import ic2.core.IC2
import ic2.core.block.wiring.ContainerElectricBlock
import ic2.core.block.wiring.TileEntityChargepadBlock
import ic2.core.block.wiring.TileEntityElectricBlock
import ic2.core.network.NetworkManager
import ic2.core.util.GuiTooltipHelper
import net.minecraft.client.gui.GuiButton
import net.minecraft.client.gui.inventory.GuiContainer
import net.minecraft.init.Items
import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
import net.minecraft.util.ResourceLocation
import net.minecraft.util.StatCollector
import org.lwjgl.opengl.GL11

class GuiMyElectricBlock(container1: ContainerElectricBlock): GuiContainer(container1) {
    private val container: ContainerElectricBlock
    private val armorInv: String
    private val level: String
    private val name: String?
    private var background = ResourceLocation(IC2.textureDomain, "textures/gui/GUIElectricBlock.png") // Путь до текстуры

    init {
        this.ySize = 196
        this.container = container1
        this.armorInv = StatCollector.translateToLocal("ic2.EUStorage.gui.info.armor")
        this.level = StatCollector.translateToLocal("ic2.EUStorage.gui.info.level")
        when((container1.base as TileEntityElectricBlock).tier) {
            5 -> this.name = StatCollector.translateToLocal("myic2addon.ses.gui.name") // Имя GUI(название)
            6 -> this.name = StatCollector.translateToLocal("myic2addon.bes.gui.name") // Имя GUI(название)
            else -> this.name = "Untitled"
        }
    }

    // Все что дальше нам не нужно
    override fun initGui() {
        super.initGui()
        this.buttonList.add(GuiIconButton(0, (this.width - this.xSize) / 2 + 152, (this.height - this.ySize) / 2 + 4, 20, 20, ItemStack(Items.redstone), true))
    }
    override fun drawGuiContainerForegroundLayer(par1: Int, par2: Int) {
        this.fontRendererObj.drawString(this.name, (this.xSize - this.fontRendererObj.getStringWidth(this.name)) / 2, 6, 4210752)
        this.fontRendererObj.drawString(this.armorInv, 8, this.ySize - 126 + 3, 4210752)
        this.fontRendererObj.drawString(this.level, 79, 25, 4210752)
        val e = Math.min((this.container.base as TileEntityElectricBlock).energy, (this.container.base as TileEntityElectricBlock).maxStorage.toDouble()).toInt()
        this.fontRendererObj.drawString(" $e", 110, 35, 4210752)
        this.fontRendererObj.drawString("/" + (this.container.base as TileEntityElectricBlock).maxStorage, 110, 45, 4210752)
        val output = StatCollector.translateToLocalFormatted("ic2.EUStorage.gui.info.output", (this.container.base as TileEntityElectricBlock).output)
        this.fontRendererObj.drawString(output, 85, 60, 4210752)
        GuiTooltipHelper.drawAreaTooltip(par1 - this.guiLeft, par2 - this.guiTop, (this.container.base as TileEntityElectricBlock).getredstoneMode(), 153, 3, 172, 22)
    }
    override fun drawGuiContainerBackgroundLayer(f: Float, x: Int, y: Int) {
        GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f)
        this.mc.textureManager.bindTexture(background)
        val j = (this.width - this.xSize) / 2
        val k = (this.height - this.ySize) / 2
        this.drawTexturedModalRect(j, k, 0, 0, this.xSize, this.ySize)
        if((this.container.base as TileEntityElectricBlock).energy > 0.0) {
            val i1 = (24.0f * (this.container.base as TileEntityElectricBlock).chargeLevel).toInt()
            this.drawTexturedModalRect(j + 79, k + 34, 176, 14, i1 + 1, 16)
        }
    }
    override fun actionPerformed(guibutton: GuiButton?) {
        super.actionPerformed(guibutton)
        if(guibutton!!.id == 0) {
            (IC2.network.get() as NetworkManager).initiateClientTileEntityEvent(this.container.base as TileEntity, 0)
        }
    }
}
И последнее. MyItemElectricBlock.
Kotlin:
package wiring

import MyIC2Items
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import ic2.core.item.block.ItemBlockIC2
import ic2.core.util.StackUtil
import net.minecraft.block.Block
import net.minecraft.creativetab.CreativeTabs
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.util.StatCollector

class MyItemElectricBlock(block: Block): ItemBlockIC2(block) {
    init {
        this.maxDamage = 0
        this.setHasSubtypes(true)
        this.setMaxStackSize(1)
    }

    override fun getMetadata(i: Int): Int = i

    override fun getUnlocalizedName(itemstack: ItemStack?): String? {
        val meta = itemstack!!.itemDamage
        return when(meta) {
            0 -> "ic2.blockSES"
            1 -> "ic2.blockBES"
            else -> null
        }
    }

    // Добавляем описание
    override fun addInformation(itemStack: ItemStack?, player: EntityPlayer?, info: MutableList<Any?>, b: Boolean) {
        val meta = itemStack!!.itemDamage
        when(meta) {
            0 -> info.add(StatCollector.translateToLocal("ic2.item.tooltip.Output") + " 4096EU/t " + StatCollector.translateToLocal("ic2.item.tooltip.Capacity") + " 1b EU ")
            1 -> info.add(StatCollector.translateToLocal("ic2.item.tooltip.Output") + " 8192EU/t " + StatCollector.translateToLocal("ic2.item.tooltip.Capacity") + " 2b EU")
        }

        when(meta) {
            0, 1 -> {
                val nbttagcompound = StackUtil.getOrCreateNbtData(itemStack)
                info.add(StatCollector.translateToLocal("ic2.item.tooltip.Store") + " " + nbttagcompound.getInteger("energy") + " EU")
            }
        }
    }

    @SideOnly(Side.CLIENT)
    override fun getSubItems(item: Item, par2CreativeTabs: CreativeTabs?, itemList: MutableList<Any?>) {
        itemList.add(MyIC2Items.smallEnergyStorage) // Добавляем просто хранилища
        itemList.add(MyIC2Items.bigEnergyStorage) // Добавляем просто хранилища

        // Добавляем заполненые хранилища
        var itemStack = ItemStack(MyIC2Items.smallEnergyStorage.item, 1) // Инициализируем айтем стак
        itemStack.itemDamage = 0 // Ставим мету
        var nbttagcompound = StackUtil.getOrCreateNbtData(itemStack) // Инициализируем НБТ
        nbttagcompound.setDouble("energy", 1000000000.0) // Устанавливем энергию
        itemList.add(itemStack) // Добавляем

        itemStack = ItemStack(MyIC2Items.bigEnergyStorage.item, 1)
        itemStack.itemDamage = 1
        nbttagcompound = StackUtil.getOrCreateNbtData(itemStack)
        nbttagcompound.setDouble("energy", 2000000000.0)
        itemList.add(itemStack)
    }
}
Дальше нужно добавить в preInit и в MyIC2Items пару строк.
MyIC2Items:
Kotlin:
lateinit var smallEnergyStorage: ItemStack // Маленькое энергохранилище
lateinit var bigEnergyStorage: ItemStack // Большое энерго хранилище
preInit: MyBlockElectric(InternalName.blockElectric)
Устанавливаем текстуру и локализуем. Результат:
2018-06-10_19.31.31.png

2018-06-10_19.31.54.png

На скриншотах только маленькое хранилище на 1ККК энергии. Но большое на 2ККК такое же.

Как насчет добавить хранилище с зарядной пластиной? Давайте сделаем это.
Так же создаем класс с хранилищами.
Kotlin:
package wiring

import MyIC2Items
import cpw.mods.fml.common.registry.GameRegistry
import ic2.core.IC2
import ic2.core.block.BlockMultiID
import ic2.core.block.wiring.TileEntityChargepadBlock
import ic2.core.init.InternalName
import ic2.core.util.StackUtil
import net.minecraft.block.Block
import net.minecraft.block.material.Material
import net.minecraft.entity.Entity
import net.minecraft.entity.EntityLivingBase
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
import net.minecraft.util.MathHelper
import net.minecraft.world.IBlockAccess
import net.minecraft.world.World
import org.apache.commons.lang3.mutable.MutableObject
import java.util.*

class MyBlockChargepad(internalName: InternalName): BlockMultiID(internalName, Material.iron, MyItemChargepadBlock::class.java) {
    init {
        this.setHardness(1.5f)
        this.setStepSound(Block.soundTypeMetal)
        this.setBlockBounds(0.0f, 0.0f, 0.0f, 1.0f, 0.95f, 1.0f)
        MyIC2Items.chargepadSmallEnergyStorage = ItemStack(this, 1, 0)
        MyIC2Items.chargepadBigEnergyStorage = ItemStack(this, 1, 1)
        GameRegistry.registerTileEntity(TilEntityChargepadSmallEnergyStorage::class.java, "Chargepad Small Energy Storage")
        GameRegistry.registerTileEntity(TilEntityChargepadBigEnergyStorage::class.java, "Chargepad Big Energy Storage")
    }

    override fun getTextureFolder(id: Int): String = "wiring"

    override fun getTeClass(meta: Int, ctorArgTypes: MutableObject<Array<Class<*>>>?, ctorArgs: MutableObject<Array<Any>>?): Class<out TileEntity>? {
        return when(meta) {
            0 -> TilEntityChargepadSmallEnergyStorage::class.java
            1 -> TilEntityChargepadBigEnergyStorage::class.java
            else -> null
        }
    }

    // Спавн партиклов при зарядке
    override fun randomDisplayTick(world: World?, x: Int, y: Int, z: Int, random: Random?) {
        if(IC2.platform.isRendering) {
            val te = this.getOwnTe(world!!, x, y, z) as TileEntityChargepadBlock
            te.spawnParticles(world, x, y, z, random)
        }
    }

    // Зарядка брони на игроке
    override fun onEntityCollidedWithBlock(world: World?, x: Int, y: Int, z: Int, entity: Entity?) {
        if(IC2.platform.isSimulating) {
            if(entity is EntityPlayer) {
                val te = this.getOwnTe(world!!, x, y, z) as TileEntityChargepadBlock
                te.playerstandsat(entity)
            }
        }
    }

    // Поворот блока при установке
    override fun onBlockPlacedBy(world: World?, x: Int, y: Int, z: Int, entity: EntityLivingBase?, stack: ItemStack?) {
        if(IC2.platform.isSimulating) {
            val te = this.getOwnTe(world!!, x, y, z) as TileEntityChargepadBlock
            val nbttagcompound = StackUtil.getOrCreateNbtData(stack!!)
            te.energy = nbttagcompound.getDouble("energy")
            if(entity == null) {
                te.facing = 0.toShort()
            } else {
                val yaw = MathHelper.floor_double((entity.rotationYaw * 4.0f / 360.0f).toDouble() + 0.5) and 3
                val pitch = Math.round(entity.rotationPitch)
                if(pitch <= -65) {
                    te.facing = 0.toShort()
                } else {
                    when(yaw) {
                        0 -> te.facing = 2.toShort()
                        1 -> te.facing = 5.toShort()
                        2 -> te.facing = 3.toShort()
                        3 -> te.facing = 4.toShort()
                    }
                }
            }

        }
    }

    override fun isProvidingWeakPower(blockAccess: IBlockAccess?, x: Int, y: Int, z: Int, side: Int): Int {
        val te = this.getOwnTe(blockAccess!!, x, y, z) as TileEntityChargepadBlock
        return if(te.isEmittingRedstone) 15 else 0
    }
    override fun canProvidePower(): Boolean = true
    override fun isOpaqueCube(): Boolean = false
    override fun isNormalCube(world: IBlockAccess?, i: Int, j: Int, k: Int): Boolean = false
}
Дальше тайлы.
Маленькое хранилище:
Kotlin:
package wiring

import ic2.core.block.wiring.ContainerChargepadBlock
import ic2.core.block.wiring.TileEntityChargepadBlock
import net.minecraft.client.gui.GuiScreen
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack

class TilEntityChargepadSmallEnergyStorage: TileEntityChargepadBlock(5, 32, 1000000000) {
    override fun getInventoryName(): String = "Chargepad (SES)"
    override fun getGui(entityPlayer: EntityPlayer?, isAdmin: Boolean): GuiScreen = GuiMyChargepadBlock(ContainerChargepadBlock(entityPlayer, this))

    override fun getItems(player: EntityPlayer?) {
        if(player != null) {
            // Зарядка брони
            var var2 = player.inventory.armorInventory
            var var3 = var2.size
            var var4 = 0
            var current: ItemStack?
            while(var4 < var3) {
                current = var2[var4]
                if(current != null) {
                    this.chargeitems(current, 8192) // Второй аргумент - фактор зарядки. Умножаем на тикрейт и получаем EU/t зарядки
                }
                ++var4
            }
    
            // Зарядка вещей в инвентаре
            var2 = player.inventory.mainInventory
            var3 = var2.size
            var4 = 0
            while(var4 < var3) {
                current = var2[var4]
                if(current != null) {
                    this.chargeitems(current, 8192)
                }
                ++var4
            }
        }

    }
}
Большое хранилище:
Kotlin:
package wiring

import ic2.core.block.wiring.ContainerChargepadBlock
import ic2.core.block.wiring.TileEntityChargepadBlock
import net.minecraft.client.gui.GuiScreen
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack

class TilEntityChargepadBigEnergyStorage: TileEntityChargepadBlock(6, 32, 2000000000) {
    override fun getInventoryName(): String = "Chargepad (BES)"
    override fun getGui(entityPlayer: EntityPlayer?, isAdmin: Boolean): GuiScreen = GuiMyChargepadBlock(ContainerChargepadBlock(entityPlayer, this))

    // Здесь происходит зарядка
    override fun getItems(player: EntityPlayer?) {
        if(player != null) {
            var var2 = player.inventory.armorInventory
            var var3 = var2.size
            var var4 = 0
            var current: ItemStack?
            while(var4 < var3) {
                current = var2[var4]
                if(current != null) {
                    this.chargeitems(current, 16384)
                }
                ++var4
            }

            var2 = player.inventory.mainInventory
            var3 = var2.size
            var4 = 0
            while(var4 < var3) {
                current = var2[var4]
                if(current != null) {
                    this.chargeitems(current, 16384)
                }
                ++var4
            }
        }
    }
}
Дальше создадим MyItemChargepadBlock.
Kotlin:
package wiring

import MyIC2Items
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import ic2.core.item.block.ItemBlockIC2
import ic2.core.util.StackUtil
import net.minecraft.block.Block
import net.minecraft.creativetab.CreativeTabs
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.util.StatCollector

class MyItemChargepadBlock(block: Block): ItemBlockIC2(block) {
    init {
        this.maxDamage = 0
        this.setHasSubtypes(true)
        this.setMaxStackSize(1)
    }

    override fun getMetadata(i: Int): Int {
        return i
    }

    override fun getUnlocalizedName(itemstack: ItemStack?): String? {
        val meta = itemstack!!.itemDamage
        return when(meta) {
            0 -> "ic2.blockChargepadSES"
            1 -> "ic2.blockChargepadBES"
            else -> null
        }
    }

    override fun addInformation(itemStack: ItemStack?, player: EntityPlayer?, info: MutableList<Any?>, b: Boolean) {
        val meta = itemStack!!.itemDamage
        when(meta) {
            0 -> info.add(StatCollector.translateToLocal("ic2.item.tooltip.Output") + " 4096EU/t " + StatCollector.translateToLocal("ic2.item.tooltip.Capacity") + " 1b EU ")
            1 -> info.add(StatCollector.translateToLocal("ic2.item.tooltip.Output") + " 8192EU/t " + StatCollector.translateToLocal("ic2.item.tooltip.Capacity") + " 2b EU")
        }

        when(meta) {
            0, 1 -> {
                val nbttagcompound = StackUtil.getOrCreateNbtData(itemStack)
                info.add(StatCollector.translateToLocal("ic2.item.tooltip.Store") + " " + nbttagcompound.getInteger("energy") + " EU")
            }
        }
    }

    @SideOnly(Side.CLIENT)
    override fun getSubItems(item: Item, par2CreativeTabs: CreativeTabs?, itemList: MutableList<Any?>) {
        itemList.add(MyIC2Items.chargepadBigEnergyStorage)
        itemList.add(MyIC2Items.chargepadSmallEnergyStorage)

        var itemStack = ItemStack(MyIC2Items.chargepadBigEnergyStorage.item, 1)
        itemStack.itemDamage = 0
        var nbttagcompound = StackUtil.getOrCreateNbtData(itemStack)
        nbttagcompound.setDouble("energy", 1000000000.0)
        itemList.add(itemStack)
        itemStack = ItemStack(MyIC2Items.chargepadSmallEnergyStorage.item, 1)
        itemStack.itemDamage = 1
        nbttagcompound = StackUtil.getOrCreateNbtData(itemStack)
        nbttagcompound.setDouble("energy", 2000000000.0)
        itemList.add(itemStack)
    }
}
Дальше GUI.
Kotlin:
package wiring

import ic2.core.GuiIconButton
import ic2.core.IC2
import ic2.core.block.wiring.ContainerChargepadBlock
import ic2.core.block.wiring.TileEntityChargepadBlock
import ic2.core.network.NetworkManager
import ic2.core.util.GuiTooltipHelper
import net.minecraft.client.gui.GuiButton
import net.minecraft.client.gui.inventory.GuiContainer
import net.minecraft.init.Items
import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
import net.minecraft.util.ResourceLocation
import net.minecraft.util.StatCollector
import org.lwjgl.opengl.GL11

class GuiMyChargepadBlock(container1: ContainerChargepadBlock): GuiContainer(container1) {
    private val container: ContainerChargepadBlock = container1
    private val level: String = StatCollector.translateToLocal("ic2.EUStorage.gui.info.level")
    private val name: String?
    private var background: ResourceLocation = ResourceLocation(IC2.textureDomain, "textures/gui/GUIChargepadBlock.png")

    init {
        when((container1.base as TileEntityChargepadBlock).tier) {
            5 -> this.name = StatCollector.translateToLocal("myic2addon.sesChargepad.gui.name")
            6 -> this.name = StatCollector.translateToLocal("myic2addon.besChargepad.gui.name")
            else -> this.name = null
        }

    }

    override fun drawGuiContainerForegroundLayer(par1: Int, par2: Int) {
        this.fontRendererObj.drawString(this.name, (this.xSize - this.fontRendererObj.getStringWidth(this.name)) / 2, 6, 4210752)
        this.fontRendererObj.drawString(this.level, 79, 25, 4210752)
        val e = Math.min((this.container.base as TileEntityChargepadBlock).energy, (this.container.base as TileEntityChargepadBlock).maxStorage.toDouble()).toInt()
        this.fontRendererObj.drawString(" $e", 110, 35, 4210752)
        this.fontRendererObj.drawString("/" + (this.container.base as TileEntityChargepadBlock).maxStorage, 110, 45, 4210752)
        GuiTooltipHelper.drawAreaTooltip(par1 - this.guiLeft, par2 - this.guiTop, (this.container.base as TileEntityChargepadBlock).getredstoneMode(), 153, 3, 172, 22)
    }

    override fun drawGuiContainerBackgroundLayer(f: Float, x: Int, y: Int) {
        GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f)
        this.mc.getTextureManager().bindTexture(background)
        val j = (this.width - this.xSize) / 2
        val k = (this.height - this.ySize) / 2
        this.drawTexturedModalRect(j, k, 0, 0, this.xSize, this.ySize)
        if((this.container.base as TileEntityChargepadBlock).energy > 0.0) {
            val i1 = (24.0f * (this.container.base as TileEntityChargepadBlock).chargeLevel).toInt()
            this.drawTexturedModalRect(j + 79, k + 34, 176, 14, i1 + 1, 16)
        }

    }

    override fun initGui() {
        super.initGui()
        this.buttonList.add(GuiIconButton(0, (this.width - this.xSize) / 2 + 152, (this.height - this.ySize) / 2 + 4, 20, 20, ItemStack(Items.redstone), true))
    }

    override fun actionPerformed(guibutton: GuiButton?) {
        super.actionPerformed(guibutton)
        if(guibutton!!.id == 0) {
            (IC2.network.get() as NetworkManager).initiateClientTileEntityEvent(this.container.base as TileEntity, 0)
        }
    }
}
И наконец регистрация.
MyIC2Items
Kotlin:
lateinit var chargepadSmallEnergyStorage: ItemStack // Маленькое энергохранилище
lateinit var chargepadBigEnergyStorage: ItemStack // Большое энерго хранилище
preInit
MyBlockChargepad(InternalName.blockChargepad)
Готово! Дальше локализуем и ставим текстуру.
2018-06-10_19.49.14.png

V часть. Предмет на энергии.
Свой меч.
В этом разделе к коду я добавил больше комментариев. И так. Пришло время сделать что-то другое. Давайте сделаем свой меч. Я буду делать электрокатану. Создадим класс и назовем его Katana и поместим его в пакет items.
Kotlin:
package items

import com.google.common.collect.HashMultimap
import com.google.common.collect.Multimap
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import ic2.api.item.ElectricItem
import ic2.core.IC2
import ic2.core.init.InternalName
import ic2.core.item.armor.ItemArmorNanoSuit
import ic2.core.item.armor.ItemArmorQuantumSuit
import ic2.core.item.tool.ItemElectricTool
import net.minecraft.client.renderer.texture.IIconRegister
import net.minecraft.entity.EntityLivingBase
import net.minecraft.entity.SharedMonsterAttributes
import net.minecraft.entity.ai.attributes.AttributeModifier
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.util.DamageSource
import net.minecraft.util.IIcon

class Katana(internalName: InternalName): ItemElectricTool(internalName, 10) {
    // Переменные для удобства. Можно и не использовать
    private val energyConsumeOnHit = 15000.0 // Сколько будет тратится энергии за удар
    private val damage = 40.0 // Сколько будет наносить меч

    init {
        maxCharge = 500000 // Сколько может хранить зарядки
        tier = 4 // Энергоуровень
        transferLimit = 10000 // Сколько будет перемещаться в предмет EU/t при зарядке
        unlocalizedName = "katana" // Нелокализованное имя. Используется для перевода и текстур
    }

    @SideOnly(Side.CLIENT) lateinit var texture: IIcon // Переменная для текстуры
    @SideOnly(Side.CLIENT) override fun registerIcons(iconRegister: IIconRegister) { texture = iconRegister.registerIcon("${IC2.textureDomain}:katana") } // Регестрируем текстуру
    @SideOnly(Side.CLIENT) override fun getIcon(itemStack: ItemStack, pass: Int): IIcon = texture // Устанавливем текстуру
    @SideOnly(Side.CLIENT) override fun requiresMultipleRenderPasses(): Boolean = true // Благодаря этому текстуры пременятся на предметы с метадатой. Это нам обязательно нужно
    override fun isFull3D(): Boolean = true

    override fun hitEntity(stack: ItemStack?, target: EntityLivingBase?, source: EntityLivingBase?): Boolean { // Что происходит при ударе
        if(IC2.platform.isSimulating) {
            if(target !is EntityPlayer) { // Если цель не игрок
                if(ElectricItem.manager.canUse(stack, energyConsumeOnHit)) { // Если мы можем использовать предмет
                    target?.attackEntityFrom(DamageSource.causePlayerDamage(source as EntityPlayer), damage.toFloat()) // Наносим 40 едениц урона(20 сердец)
                    ElectricItem.manager.discharge(stack, energyConsumeOnHit, this.tier, true, false, false) // Разряжаем предмет на 15К EU
                }
            }
            else {// Если цель игрок
                for(i in 0..3) {
                    val armor = target.getEquipmentInSlot(i + 1) // Получем броню
                    var amount = 0.0
                    if(armor != null) { // Если броня надета
                        if(armor.item is ItemArmorNanoSuit) amount = 200000.0 // Если броня - нано-костюм, то будем её разряжать на 200К EU. Что бы полностью разрядит броню, потребуется 5 ударов
                        else if(armor.item is ItemArmorQuantumSuit) amount = 2000000.0 // Если броня - квант, то разряжаем её на 2KK EU. Что бы полностью разрядит броню, потребуется 5 ударов
                        if(amount > 0.0) ElectricItem.manager.discharge(armor, amount, this.tier, true, false, false); ElectricItem.manager.discharge(stack, energyConsumeOnHit * 1.5, this.tier, true, false, false) // И наконец разряжаем катану и броню противника
                    }
                }
            }
        }
        return true
    }

    override fun getAttributeModifiers(stack: ItemStack?): Multimap<*, *> {
        var dmg = 1.0 // Стандартно урон 1 еденица (0.5 сердца)

        if(ElectricItem.manager.canUse(stack, energyConsumeOnHit)) { // Если хватает энергии ставим урон на 40 едениц(20 сердец)
            dmg = damage
        }

        // Дальше записываем атрибут
        val ret = HashMultimap.create<String, AttributeModifier>()
        ret.put(SharedMonsterAttributes.attackDamage.attributeUnlocalizedName, AttributeModifier(Item.field_111210_e, "Tool modifier", dmg, 0))
        return ret
    }
}
Регистрируем в preInit.
Katana(InternalName.itemNanoSaber).
Локализуем, ставим иконку и готово!
Результат:
2018-06-12_21.29.58.png

Свой бур.
Создадим класс MyDrill в пакете items
Kotlin:
package items

import ic2.core.IC2
import ic2.core.init.InternalName
import ic2.core.item.tool.ItemDrill
import net.minecraft.enchantment.Enchantment
import net.minecraft.enchantment.EnchantmentHelper
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.world.World
import java.util.*

class MyDrill(internalName: InternalName): ItemDrill(internalName, 1000, HarvestLevel.Iridium) {
    var mode = 0 // Переменная режима, позже будем её менять
    init {
        maxCharge = 500000 // Максимальный заряд
        transferLimit = 10000 // Сколько будет перемещаться в предмет EU/t при зарядке
        tier = 4 // Энергоуровень
        efficiencyOnProperMaterial = 50F // Эффективность. Чем выше, тем быстрее копает
        unlocalizedName = "myDrill" // Нелокализованное имя
    }

    override fun getItemStack(charge: Double): ItemStack {
        val ret = super.getItemStack(charge)
        val enchantmentMap = HashMap<Int, Int>()
        enchantmentMap[Enchantment.fortune.effectId] = 5 // Добавляем удачу 5 уровня
        EnchantmentHelper.setEnchantments(enchantmentMap, ret)
        return ret
    }

    override fun onItemRightClick(stack: ItemStack, world: World?, player: EntityPlayer?): ItemStack {
        if(!IC2.platform.isSimulating) {
            return super.onItemRightClick(stack, world, player)
        } else {
            if(IC2.keyboard.isModeSwitchKeyDown(player)) {
                val enchantmentMap = HashMap<Int, Int>()
                enchantmentMap[Enchantment.fortune.effectId] = 5 // Так же добавляем удачу
                // Дальше будут переключаться режимы.
                when(mode) {
                    0 -> {
                        enchantmentMap[Enchantment.silkTouch.effectId] = 1 // Добавляем шёлковое касание
                        IC2.platform.messagePlayer(player, "ic2.tooltip.mode", "ic2.tooltip.mode.silkTouch")
                        mode += 1
                    }
                    1 -> {
                        efficiencyOnProperMaterial = 25F // Сделаем скорость ломания в 2 раза меньше
                        IC2.platform.messagePlayer(player, "ic2.tooltip.mode", "ic2.tooltip.mode.accurate")
                        mode += 1
                    }
                    2 -> {
                        efficiencyOnProperMaterial = 50F // Тут возвращаем скорость обратно
                        IC2.platform.messagePlayer(player, "ic2.tooltip.mode", "ic2.tooltip.mode.normal")
                        mode = 0
                    }
                }
                EnchantmentHelper.setEnchantments(enchantmentMap, stack) // Запись энчантов в предмет
            }

            return super.onItemRightClick(stack, world, player)
        }
    }

    override fun onItemUse(stack: ItemStack?, player: EntityPlayer, world: World?, x: Int, y: Int, z: Int, side: Int, xOffset: Float, yOffset: Float, zOffset: Float): Boolean {
        return if(IC2.keyboard.isModeSwitchKeyDown(player)) false else super.onItemUse(stack, player, world, x, y, z, side, xOffset, yOffset, zOffset)
    }
}
Регистрируем в preInit.
MyDrill(InternalName.itemToolDrill).
Локализуем, ставим иконку и готово!
Результат:
2018-06-12_23.06.57.png

2018-06-12_23.07.46.png

VI часть. Кастомный ядерный реактор.
TODO
Автор
Nix13
Просмотры
10,981
Первый выпуск
Обновление
Оценка
5.00 звёзд 3 оценок

Другие ресурсы пользователя Nix13

Последние обновления

  1. Актуализация туториала

    Обновил версию котлина до 1.5 Немного обновил Часть II
  2. Предметы

    Добавил энергопредметы и немного изменил простой и кастомный механизм
  3. Предупреждение о котлине

    Для некоторых особ написал предупреждение о котлине

Последние рецензии

Найс тема, побольше бы такого
Спасибо, респект таким как ты)
Сверху