Туториал [1.5.1][Forge][Modding]Создание модов для Minecraft [All][Guide]

Nicolayka

Владелец
Команда форума
Регистрация
4 Апр 2025
Сообщения
24
Реакции
0
Баллы
1
Город
Санкт-Петербург
Веб-сайт
paxgame.ru
Discord
nicolayka
В этом учебнике вы научитесь моддить в Forge, тут написаны мои, и собраны разные туториалы, с разных источников. Удачи в моддинге!
Что требуется для моддинга с Forge?
1. JDK 7
2. MCP (Minecraft Coder Pack)
3. Forge-Src, находим Recommended:, и нажимаем Source. Другие версии тут.
4. Eclipse
5. Чистый клиент и обязательно сервер
Как скачать клиент (Пиратский)
Качаем minecraftSP.zip, распаковываем, нажимаем, и видем окно:
f355f2e7feae42618168bd9294e4164e.png

Ждём пока загрузка кончиться, и всё, у вас есть чистый клиент!

Осталось обновить:
Создание Metadata блоку. - уже скоро
Создание Metadata итему.


История:
30.04.2013 - Добавлен урок про Прокси.
24.04.2013 - Изменены 2 гайда.
22.04.2013 - Добавлены уроки по созданию брони, и инструментов.
16.04.2013 - Добавлено объяснение как убрать баг по открытию печки/сундука.
10.04.2013 - Обновлен Туториал по созданию сундука. Обновлен Туториал по созданию печки. Обновлены все гайды кроме 2-х.
07.04.2013 - Убрана надпись внизу темы по "Добавлению туториалов от других пользователей". Обновлен FAQ.
06.04.2013 - Изменен урок №1. Обновлен урок №0.2-8-9-10.
05.04.2013 - Обновлен урок по созданию Блока. Добавлен урок по текстурах. Обновлен урок по созданию предмета.

Внимание!
1.Если вы моддите в первый раз лучше всего идти постепенно, не пропуская уроков.
2.Если будут проблемы, или ошибки, не спешите в теме просить помощь, сначала посмотрите маленький FAQ.

Мини-FAQ
Всё свои ошибки пишем в спойлерах:

[ spoiler ] [ /spoiler ]
Вопрос: Я написал код, как мне теперь его проверить в игре?
Ответ: Теперь вам надо его рекомпилировать, нажмите recompile.bat (Пакетный файл MS-DOS), подождите пока пройдёт время, и теперь нажмите startclient.bat.

Вопрос: Я хотел рекомпилировать мод, нажал recompile.bat, появилось окно и пропало, что мне делать?
Ответ: Запускать надо recompile.bat (Пакетный файл MS-DOS), это относится и другим bat'никам.

Вопрос: Нажал recompile.bat, всё было хорошо, но потом мне консоль написал это:
(Пример) src\minecraft\net\testmod\src\TestModBase.java:25: unmappable character for enco
ding UTF-8
//????
Ответ: Forge не принимает UTF-8, удаляйте все русские комментарии, а то рекомпилятор покарает вас ошибками.

Вопрос: У меня в игре не работают текстуры, вместо текстуры написано missing textures, что делать?
Ответ: Посмотрите урок №0.3.

0.1. Подготовка.
Не надо удалять META-INF, в minecraft.jar, и minecraft_server.jar
1. Создаем папку (Например: MCP Forge) и распаковываем туда содержание архива с MCP.
2. Копируем в папку ../MCP Forge/jars/ , из клиента папку bin и resources, из сервера нам потребуется только minecraft_server.jar.
3. Распаковываем архив с Forge-src в папку MCP Forge
4. Открываем папку ../MCP Forge/forge/ и запускаем файл install.cmd. Ждем окончания декомпиляции..
(P.S Если хотите что бы у вас уже был готовый MCP с Forge, например если вы случайно удалили его или ещё что-то, то: Когда декомпиляция пройдёт запустите игру пусть скачает lib, и потом добавьте в архив этот MCP Forge например так (mcp Forge 1.5.1)
5. "Устанавливаем" и запускаем Eclipse, во время запуска у вас "спросят" папку с проектом, указываем путь: ../MCP Forge/eclipse/
6. Итак, мы открыли проект, теперь (Обычно слева, есть Package explorer) смотрим, какую библиотеку он "ест"(В окне Package explorer, "открываем проект Minecraft" и если будет строка "JRE System Library [JavaSE-1.7]", то все нормально, но если строка "JRE System Library [JavaSE-1.6]", то нажимаем ПКМ(Правой кнопкой мыши), выбираем пункт Properties, далее открылось окно в нем пункт Execution environment, выбираем в нем JavaSE-1.7 и нажимаем Ok.
7. Итак, приготовления готовы.

Как же все таки начать писать мод?
Для начала, нам нужно создать "основу", то есть основную "папку" в которой будет храниться наша "основа" и все остальное.
  • 1.Заходим в Eclipse видим в Package Explorer папку Minecraft, открываем, и нажимаем ПКМ на папку src, выбираем New->Package. Открытом окне в строке Name, мы указываем название папки в которой будет все храниться (Для примера возьмем: mods.testmod.src ) и нажимаем Ok.
  • 2. Нажимаем ПКМ по нашей папке ( mods.testmod.src ), далее New -> Class. В строке Name указываем название нашего файла (Например: TestModBase ) и нажимаем Finish.
  • 3. Меняйте путь, но не уберайте mods, вот так например mods.NewItemMod.src.
  • 4. В новых версиях надо импортировать "Очень много" всего, чтобы сделать это быстрее в Эклипсе нажимайте "ctrl + shift + o", и вам быстро он всё импортирует, а если появиться окно тогда выбирайте нужный вам импорт.
  • 5. Если вы работаете не в эклипсе, тогда вам будет намного труднее, так что лучше перейти на него, он укажет где ошибка, и какие импорты надо, и вы не будете задавать глупых вопросов в комментариях.
Код нашего TestModBase
package mods.testmod.src;

import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.Init;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.Mod.PostInit;
import cpw.mods.fml.common.Mod.PreInit;
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.NetworkMod;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;
import cpw.mods.fml.common.network.NetworkMod.SidedPacketHandler;
import cpw.mods.fml.common.SidedProxy;
@Mod (modid = "TestMod", name = "Test Mod", version = "0.0.1")
@NetworkMod (clientSideRequired = true, serverSideRequired = false, versionBounds = "1.0.0")

public class TestModBase {

@Instance("TestModID")
public static TestModBase instance;

@Init
public void load(FMLInitializationEvent event)
{
}

@PreInit
public void preLoad(FMLPreInitializationEvent event)
{
}

@PostInit
public void postLoad(FMLPostInitializationEvent event)
{
}
}
0.2 Как сделать сам мод (Как запаковать в архив).
Когда вы уже сделаете мод, надо: Нажимайте recompile.bat, потом reobfuscate.bat, когда всё закончиться зайдите в папку reobf/minecraft/mods, и положите в папку testmod нашу папку с текстурами, textures, теперь нажимаем на папку mods правой кнопкой, Добавить в архив... выбираем формат .zip, пишем название мода, кидаем наш архив в клиент, в папку mods, играем!
0.3 Как добавить текстуру в 1.5 (Объяснение)
В 1.5 теперь стало немного по другому добавлять текстуры, не надо волноваться, все можно понять, и научиться делать)
Путь текстур менять можно, кроме:
/textures/blocks/
/textures/items/
/textures/gui/

Как добавить текстуру блоку:
Добавляем метод в блок:

@Override
public void registerIcons(IconRegister par1IconRegister)
{
this.blockIcon = par1IconRegister.registerIcon("testmod:testBlock");
}
("testmod:testBlock"), testmod - это "имя папки", в которой будет "папка с текстурой", testBlock - название нашей текстуры. Вот такое у нас размещение текстуры:


\Tutorial Forge 1.5.1\mcp744\src\minecraft\mods\testmod\textures\blocks\testBlock.png
Как добавить текстуру итему:
Добавляем метод в итем:

@Override
public void registerIcons(IconRegister par1IconRegister)
{
this.itemIcon = par1IconRegister.registerIcon("testmod:testItem");
}
("testmod:testItem"), testmod - это "имя папки", в которой будет "папка с текстурой", testItem - название нашей текстуры. Вот такое у нас размещение текстуры:


\Tutorial Forge 1.5.1\mcp744\src\minecraft\mods\testmod\textures\items\testItem.png
Как добавить текстуру GUI:
Еще не готово!
Вот такое у нас размещение текстуры:

\Tutorial Forge 1.5.1\mcp744\src\minecraft\mods\testmod\textures\gui\testGui.png
1. Создание блока.
TestModBase

package mods.testmod.src;

import net.minecraft.block.Block;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.Init;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.Mod.PostInit;
import cpw.mods.fml.common.Mod.PreInit;
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.NetworkMod;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;
import cpw.mods.fml.common.network.NetworkMod.SidedPacketHandler;
import cpw.mods.fml.common.SidedProxy;

@Mod (modid = "TestMod", name = "Test Mod", version = "0.0.1")
@NetworkMod (clientSideRequired = true, serverSideRequired = false, versionBounds = "1.0.0")

public class TestModBase {

/**
* Сам блок, .setUnlocalizedName("testBlock"); - это будет имя блока и текстуры,
* теперь все текстуры будут отдельно.
*/
public static final Block testBlock = new TestBlock(2020).setUnlocalizedName("testBlock");

@Instance("TestModID")
public static TestModBase instance;

@Init
public void load(FMLInitializationEvent event)
{

GameRegistry.registerBlock(testBlock);//Регистрация блока
LanguageRegistry.addName(testBlock, "Test Block");//Имя блока

}

@PreInit
public void preLoad(FMLPreInitializationEvent event)
{
}

@PostInit
public void postLoad(FMLPostInitializationEvent event)
{
}
}
TestBlock


package mods.testmod.src;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.texture.IconRegister;
import net.minecraft.creativetab.CreativeTabs;

public class TestBlock extends Block {

public TestBlock(int par1)
{
super(par1, Material.ground);//Материал
this.setCreativeTab(CreativeTabs.tabTools);//Добавляем в креатив
}
//Регистрируем текстуру
@Override
public void registerIcons(IconRegister par1IconRegister)
{
this.blockIcon = par1IconRegister.registerIcon("testmod:testBlock");
}
}
2. Создание предмета.
TestModBase

package mods.testmod.src;

import net.minecraft.item.Item;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.Init;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.Mod.PostInit;
import cpw.mods.fml.common.Mod.PreInit;
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.NetworkMod;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;
import cpw.mods.fml.common.network.NetworkMod.SidedPacketHandler;
import cpw.mods.fml.common.SidedProxy;

@Mod (modid = "TestMod", name = "Test Mod", version = "0.0.1")
@NetworkMod (clientSideRequired = true, serverSideRequired = false, versionBounds = "1.0.0")

public class TestModBase {

public static Item testItem = new TestItem(2020).setUnlocalizedName("testItem");

@Instance("TestModID")
public static TestModBase instance;

@Init
public void load(FMLInitializationEvent event)
{
LanguageRegistry.addName(testItem, "Test Item");
}

@PreInit
public void preLoad(FMLPreInitializationEvent event)
{
}

@PostInit
public void postLoad(FMLPostInitializationEvent event)
{
}
}
TestItem


package mods.testmod.src;

import net.minecraft.item.Item;
import net.minecraft.client.renderer.texture.IconRegister;
import net.minecraft.creativetab.CreativeTabs;

public class TestItem extends Item
{
public TestItem(int par1)
{
super(par1);
setCreativeTab(CreativeTabs.tabTools);
}

//Регистрация текстуры для итема немного отличается от блока
@Override
public void registerIcons(IconRegister par1IconRegister)
{
this.itemIcon = par1IconRegister.registerIcon("testmod:testItem");
}
}
3. Рецепты (Крафт, Печка)
package mods.testmod.src;

import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.Init;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.Mod.PostInit;
import cpw.mods.fml.common.Mod.PreInit;
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.NetworkMod;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;
import cpw.mods.fml.common.network.NetworkMod.SidedPacketHandler;
import cpw.mods.fml.common.SidedProxy;

@Mod (modid = "TestMod", name = "Test Mod", version = "0.0.1")
@NetworkMod (clientSideRequired = true, serverSideRequired = false, versionBounds = "1.0.0")

public class TestModBase {

@Instance("TestModID")
public static TestModBase instance;

@Init
public void load(FMLInitializationEvent event)
{

/**
* "Обычный крафт"
* При крафте получаем алмаз, 1 - сколько получим при крафте, для своего крафта пишем
* так: TestModBase.свой блок/итем. Если вам будет понятней то можете крафт записать и в другой форме.
*/
GameRegistry.addRecipe(new ItemStack(Block.oreDiamond, 1), new Object[]{ "XXX", "X#X", "XXX", Character.valueOf('X'), Block.cobblestone, ('#'), Block.dirt});
GameRegistry.addRecipe(new ItemStack(Block.oreDiamond, 1), new Object[]{
"XXX",
"X#X",
"XXX", Character.valueOf('X'), Block.cobblestone, ('#'), Block.dirt});

/**
* "Бесформенный крафт"
* С этим крафтом мы сможем класть наши вещи как попало. Кладём палку и грязь, получаем алмазную руду.
*/
GameRegistry.addShapelessRecipe(new ItemStack(Block.oreDiamond, 4), new Object[] {Item.stick, Block.dirt});

/**
* "Переплавка в печке"
* С земли делаем уголь, 1 - сколько получим после переплавки, 1.0F - опыт при переплавки
*/
GameRegistry.addSmelting(Block.dirt.blockID, new ItemStack(Item.coal, 1), 1.0F);
}

@PreInit
public void preLoad(FMLPreInitializationEvent event)
{
}

@PostInit
public void postLoad(FMLPostInitializationEvent event)
{
}
}
4. Как сделать еду.
Пишем в TestModBase это:

package mods.testmod.src;

import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.Init;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.Mod.PostInit;
import cpw.mods.fml.common.Mod.PreInit;
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.NetworkMod;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;
import cpw.mods.fml.common.network.NetworkMod.SidedPacketHandler;
import cpw.mods.fml.common.SidedProxy;

@Mod (modid = "TestMod", name = "Test Mod", version = "0.0.1")
@NetworkMod (clientSideRequired = true, serverSideRequired = false, versionBounds = "1.0.0")

public class TestModBase {
/**
* 1-2 это количество заполняемых сердец, 0.4F, на сколько утоляет голод,
* false - можно ли прирученных волков кормить.
**/
//Обычная еда
public static Item testFood = new TestFood(5003, 1, 0.4F, false).setUnlocalizedName("testFood");
//Суп
public static Item testSoup = new TestSoup(5004, 2).setUnlocalizedName("testSoup");

@Instance("TestModID")
public static TestModBase instance;

@Init
public void load(FMLInitializationEvent event)
{
LanguageRegistry.addName(testFood, "Test Food");
LanguageRegistry.addName(testSoup, "Test Soup");

GameRegistry.addRecipe(new ItemStack(TestModBase.testFood, 1), new Object[]{ "#X", Character.valueOf('X'), Item.appleRed, Character.valueOf('#'), Block.dirt});
GameRegistry.addRecipe(new ItemStack(TestModBase.testSoup, 1), new Object[]{ "ZC", Character.valueOf('C'), Item.bowlEmpty, Character.valueOf('Z'), Item.appleRed});

}

@PreInit
public void preLoad(FMLPreInitializationEvent event)
{
}

@PostInit
public void postLoad(FMLPostInitializationEvent event)
{
}
}
Создаём TestFood:

package mods.testmod.src;

import net.minecraft.client.renderer.texture.IconRegister;
import net.minecraft.item.ItemFood;

/**
* Мы поставили родитель ItemFood, и теперь наша еда автоматически будет в категории tabFood.
**/
public class TestFood extends ItemFood
{
public TestFood(int par1, int par2, float par3, boolean par4)
{
super(par1, par2, par4);
maxStackSize = 64;
}
@Override
public void registerIcons(IconRegister par1IconRegister)
{
this.itemIcon = par1IconRegister.registerIcon("testmod:testFood");
}
}
И TestSoup:

package mods.testmod.src;

import net.minecraft.client.renderer.texture.IconRegister;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemFood;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;

public class TestSoup extends ItemFood
{
public TestSoup(int par1, int par2)
{
super(par1, par2, false);
this.setMaxStackSize(1);
}

public ItemStack onEaten(ItemStack par1ItemStack, World par2World, EntityPlayer par3EntityPlayer)
{
super.onEaten(par1ItemStack, par2World, par3EntityPlayer);
return new ItemStack(Item.bowlEmpty);//Когда мы покушаем в руке останется деревянная миска.
}

@Override
public void registerIcons(IconRegister par1IconRegister)
{
this.itemIcon = par1IconRegister.registerIcon("testmod:testFood");
}
}
5. Создание конфиг файла.
Немного изменим TestModBase:

package mods.testmod.src;

import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.common.Configuration;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.Init;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.Mod.PostInit;
import cpw.mods.fml.common.Mod.PreInit;
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.NetworkMod;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;
import cpw.mods.fml.common.network.NetworkMod.SidedPacketHandler;
import cpw.mods.fml.common.SidedProxy;

@Mod (modid = "TestMod", name = "Test Mod", version = "0.0.1")
@NetworkMod (clientSideRequired = true, serverSideRequired = false, versionBounds = "1.0.0")

public class TestModBase {
//Немного изменим наш итем
public static Item testItem;
public static int testItemitemID; //обязательно добавляем itemID
//И изменим наш блок
public static Block testBlock ;
public static int testBlockblockID; //добавляем blockID

@Instance("TestModID")
public static TestModBase instance;

@Init
public void load(FMLInitializationEvent event)
{
//Дописываем наш итем, и вместо айди теперь у нас TestItemitemID
testItem = new TestItem(testItemitemID).setUnlocalizedName("testItem");
//Дописываем блок
testBlock = new TestBlock(testBlockblockID,0).setUnlocalizedName("testBlock");

LanguageRegistry.addName(testItem, "Test Item");

GameRegistry.registerBlock(testBlock);
LanguageRegistry.addName(testBlock, "Test Block");
}

@PostInit
public void load(FMLPreInitializationEvent event)
{

}

@PreInit
public void preLoad(FMLPreInitializationEvent event)
{
Configuration config = new Configuration(event.getSuggestedConfigurationFile());
config.load();//Писать надо только между load и save:
testItemitemID = config.getItem("testItem", 2020).getInt();
testBlockblockID = config.getBlock("testBlock", 2021).getInt();
config.save();
}

}
6. Создание Metadata блоку. Не обновлено!
7. Создание Metadata итему. Не обновлено!
8. Создание своей вкладки в CreativeTabs.
[1.4.7]Внимание: Оригинальный туториал тут.
TestModBase:

package mods.testmod.src;

import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.common.Configuration;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.Init;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.Mod.PostInit;
import cpw.mods.fml.common.Mod.PreInit;
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.NetworkMod;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;
import cpw.mods.fml.common.network.NetworkMod.SidedPacketHandler;
import cpw.mods.fml.common.SidedProxy;

@Mod (modid = "TestMod", name = "Test Mod", version = "0.0.1")
@NetworkMod (clientSideRequired = true, serverSideRequired = false, versionBounds = "1.0.0")

public class TestModBase {
//Добавляем наш CreativeTabs
public static CreativeTabs tabTest = new TabTest(CreativeTabs.getNextID(),"TestCreativeTab");

@Instance("TestModID")
public static TestModBase instance;

@Init
public void load(FMLInitializationEvent event)
{
}

@PreInit
public void preLoad(FMLPreInitializationEvent event)
{
}

@PostInit
public void load(FMLPreInitializationEvent event)
{
}
}
TabTest:


package mods.testmod.src;

import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import cpw.mods.fml.relauncher.*;

public class TabTest extends CreativeTabs {

public TabTest(int position, String tabID)
{
super(position, tabID); //Конструктор вкладки
}
/**
* Какая картинка будет в категории, алмазный топор. Для своего
* итема пишем так: TestModBase.TestItem.itemID
* для блока: TestModBase.TestBlock.blockID
*/
@SideOnly(Side.CLIENT)
public int getTabIconItemIndex()
{
return Item.diamond.itemID;
}

public String getTranslatedTabLabel()
{
return "More Diamond"; //Имя вкладки в игре
}
}
9. Прокси.
Прокси:
Будем делать прокси, если мы например захотели сделать моба, или блок с моделью, и поставим этот мод на сервер, он закидает вас ошибками, или рендер просто не будет роботать, а прокси избавит нас от этой проблемы.

Добавляем в наш главный класс (базу):

@SidedProxy(clientSide = "mods.testmod.src.ClientProxy", serverSide = "mods.testmod.src.CommonProxyTutorial")
public static ClientProxy proxy;
После этого:

public class TestModBase {
Теперь регистрируем рендер в прокси:

proxy.registerRenderThings();
После:

@Init
public void load(FMLInitializationEvent event)
{
Создайем классы: CommonProxyTutorial, и ClientProxy.
Немного изменяем CommonProxyTutorial:

package mods.testmod.src;

public class CommonProxyTutorial
{
public void registerRenderThings()
{

}
}
И изменяем ClientProxy:

package mods.testmod.src;

public class ClientProxy extends CommonProxyTutorial
{
@Override
public void registerRenderThings()
{

}
}
Тут:

public void registerRenderThings()
{

}
будем писать наши рендеры:
Например вы дальше будете смотреть туториалы и делать моба.

public void registerRenderThings()
{
RenderingRegistry.registerEntityRenderingHandler(EntityCreeperMan.class, new RenderCreeperMan(new ModelBiped(), 0.3F));
}
Вот и все.
10. Создание моба.
Туториал по созданию анимации для Модели.
Создание враждебного моба.
[1.4.7]Туториал взят отсюда.
Будем делать человека-крипера, начнём!
TestModBase

package mods.testmod.src;

import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityEggInfo;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EnumCreatureType;
import net.minecraft.world.biome.BiomeGenBase;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.Init;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.network.NetworkMod;
import cpw.mods.fml.common.registry.EntityRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;

@Mod(modid = "TestMod", name = "Test Mod", version = "1.0")
@NetworkMod(clientSideRequired = true, serverSideRequired = false)
public class TestModBase
{
//путь к прокси
@SidedProxy(clientSide = "mods.testmod.src.ClientProxyMob", serverSide = "mods.testmod.src.CommonProxyTutorial")
public static ClientProxyMob proxy;

static int startEntityId = 300;//Айди яйца


@Init
public void load(FMLInitializationEvent event)
{
proxy.registerRenderThings();
/**
* 10 - как часто появляется, 2 - это минимальное появление мобов в мире, 4 - максимальное,
* EnumCreatureType.monster - будет спавнится ночью.
*/
EntityRegistry.registerModEntity(EntityCreeperMan.class, "Creeper Man", 1, this, 80, 3, true);
EntityRegistry.addSpawn(EntityCreeperMan.class, 10, 2, 4, EnumCreatureType.monster, BiomeGenBase.desert, BiomeGenBase.desertHills, BiomeGenBase.forest);
LanguageRegistry.instance().addStringLocalization("entity.TestMod.Creeper Man.name", "Creeper Man");

/**
* Цвет яйца, чтобы поменять цвет смотрите внизу туториала.
*/
registerEntityEgg(EntityCreeperMan.class, 0xffffff, 0x000000);
}

public static int getUniqueEntityId()
{
do
{
startEntityId++;
}
while (EntityList.getStringFromID(startEntityId) != null);

return startEntityId;
}

public static void registerEntityEgg(Class entity, int primaryColor, int secondaryColor)
{
int id = getUniqueEntityId();
EntityList.IDtoClassMapping.put(id, entity);
EntityList.entityEggs.put(id, new EntityEggInfo(id, primaryColor, secondaryColor));
}
}
Теперь EntityCreeperMan:
Вот текстура:
a0c806f51a2d.png


package mods.testmod.src;

import net.minecraft.entity.Entity;
import net.minecraft.entity.EnumCreatureAttribute;
import net.minecraft.entity.ai.EntityAIAttackOnCollide;
import net.minecraft.entity.ai.EntityAIBreakDoor;
import net.minecraft.entity.ai.EntityAIHurtByTarget;
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
import net.minecraft.entity.ai.EntityAISwimming;
import net.minecraft.entity.ai.EntityAIWander;
import net.minecraft.entity.ai.EntityAIWatchClosest;
import net.minecraft.entity.monster.EntityMob;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;

public class EntityCreeperMan extends EntityMob
{

public EntityCreeperMan(World par1World)
{
super(par1World);
this.texture = "/mods/testmod/textures/creeper_man.png";
this.moveSpeed = 0.25F;//Скорость
this.tasks.addTask(0, new EntityAISwimming(this));
this.tasks.addTask(1, new EntityAIBreakDoor(this));
this.tasks.addTask(2, new EntityAIAttackOnCollide(this, EntityPlayer.class, this.moveSpeed, false));
this.tasks.addTask(3, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F));
this.tasks.addTask(4, new EntityAIWander(this, this.moveSpeed));
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, 16.0F, 0, true));
}
//Сила атаки.
public int getAttackStrength(Entity par1Entity) { return 4; }

protected boolean isAIEnabled()
{
return true;
}
//Здоровье
public int getMaxHealth()
{
return 20;
}
//Броня
public int getTotalArmorValue()
{
return 2;
}
//Будет гореть днём, как и зомби
public void onLivingUpdate()
{
if (this.worldObj.isDaytime() && !this.worldObj.isRemote && !this.isChild())
{
float var1 = this.getBrightness(1.0F);

if (var1 > 0.5F && this.rand.nextFloat() * 30.0F < (var1 - 0.4F) * 2.0F && this.worldObj.canBlockSeeTheSky(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ)))
{
boolean var2 = true;
ItemStack var3 = this.getCurrentItemOrArmor(4);

if (var3 != null)
{
if (var3.isItemStackDamageable())
{
var3.setItemDamage(var3.getItemDamageForDisplay() + this.rand.nextInt(2));

if (var3.getItemDamageForDisplay() >= var3.getMaxDamage())
{
this.renderBrokenItemStack(var3);
this.setCurrentItemOrArmor(4, (ItemStack)null);
}
}

var2 = false;
}

if (var2)
{
this.setFire(8);
}
}
}

super.onLivingUpdate();
}
//Звуки моба
protected String getLivingSound()
{
return "mob.zombie.say";
}

protected String getHurtSound()
{
return "mob.zombie.hurt";
}

protected String getDeathSound()
{
return "mob.zombie.death";
}

protected void playStepSound(int par1, int par2, int par3, int par4)
{
this.worldObj.playSoundAtEntity(this, "mob.zombie.step", 0.15F, 1.0F);
}

protected void dropRareDrop(int par1)
{
switch (this.rand.nextInt(2))
{
case 0:
this.dropItem(Item.ingotIron.itemID, 1);
break;
case 1:
this.dropItem(Item.helmetSteel.itemID, 1);
break;
}
}

protected int getDropItemId()
{
return Item.diamond.itemID;
}

public EnumCreatureAttribute getCreatureAttribute()
{
return EnumCreatureAttribute.UNDEAD;
}
}
Теперь делаем прокси, CommonProxyTutorial:

package mods.testmod.src;

public class CommonProxyTutorial
{
public void registerRenderThings()
{

}
}
И ClientProxyMob:

package mods.testmod.src;

import cpw.mods.fml.client.registry.RenderingRegistry;
import net.minecraft.client.model.ModelBiped;
import net.minecraftforge.client.MinecraftForgeClient;

public class ClientProxyMob extends CommonProxyTutorial
{
@Override
public void registerRenderThings()
{
RenderingRegistry.registerEntityRenderingHandler(EntityCreeperMan.class, new RenderCreeperMan(new ModelBiped(), 0.3F));
}
}
И RenderCreeperMan:

package mods.testmod.src;

import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.renderer.entity.RenderLiving;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving;

public class RenderCreeperMan extends RenderLiving
{
protected ModelBiped model;

public RenderCreeperMan (ModelBiped modelBiped, float f)
{
super(modelBiped, f);
model = ((ModelBiped)mainModel);
}

public void renderCreeperMan(EntityCreeperMan entity, double par2, double par4, double par6, float par8, float par9)
{
super.doRenderLiving(entity, par2, par4, par6, par8, par9);
}

public void doRenderLiving(EntityLiving par1EntityLiving, double par2, double par4, double par6, float par8, float par9)
{
renderCreeperMan((EntityCreeperMan)par1EntityLiving, par2, par4, par6, par8, par9);
}

public void doRender(Entity par1Entity, double par2, double par4, double par6, float par8, float par9)
{
renderCreeperMan((EntityCreeperMan)par1Entity, par2, par4, par6, par8, par9);
}
}
Вот тут можете взять другой цвет для яйца.
Что надо сделать для своей модели:
Для своей модели нам надо немного изменить методы:


//Legleft, Legright, Body, Head - это название для примера, у вас может быть другое название, и другое количество.
public void render(Entity par1Entity, float par2, float par3, float par4, float par5, float par6, float par7)
{
super.render(par1Entity, par2, par3, par4, par5, par6, par7);
setRotationAngles(par2, par3, par4, par5, par6, par7, par1Entity);
Legleft.render(par7);
Legright.render(par7);
Body.render(par7);
Head.render(par7);
}

private void setRotation(ModelRenderer model, float x, float y, float z)
{
model.rotateAngleX = x;
model.rotateAngleY = y;
model.rotateAngleZ = z;
}


public void setRotationAngles(float par1, float par2, float par3, float par4, float par5, float par6, Entity par7Entity)
{
super.setRotationAngles(par1, par2, par3, par4, par5, par6, par7Entity);
}
11. Как сделать блок с моделью.
[1.4.7]Внимание: Автор этого туториала не я.
Модели делаем в Techne.
TestModBase:

package mods.testmod.src;

import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemReed;
import net.minecraftforge.common.Configuration;
import cpw.mods.fml.client.registry.ClientRegistry;
import cpw.mods.fml.client.registry.RenderingRegistry;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.Init;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.Mod.PostInit;
import cpw.mods.fml.common.Mod.PreInit;
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.NetworkMod;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;
import cpw.mods.fml.common.network.NetworkMod.SidedPacketHandler;
import cpw.mods.fml.common.SidedProxy;

@Mod (modid = "TestModBase", name = "Test Mod", version = "0.0.1")
@NetworkMod (clientSideRequired = true, serverSideRequired = false, versionBounds = "1.0.0")

public class TestModBase {

public static int renderTestModelID;//это будет наш RenderType
public static Block testModel = new BlockTest(2020).setCreativeTab(CreativeTabs.tabDecorations).setHardness(0.0F).setUnlocalizedName("testModel");

@Instance("TestModBaseID")
public static TestModBase instance;

@Init
public void load(FMLInitializationEvent event)
{
GameRegistry.registerBlock(testModel);
GameRegistry.registerTileEntity( mods.testmod.src.TileEntityTestModel.class, "TileEntityTestModel" );
LanguageRegistry.addName(testModel, "Head");
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityTestModel.class, new RendererTestModel());//регистрируем рендер, и энтити
//это поможет нам отрендерить модель в руке, то-есть как сундук
renderTestModelID = RenderingRegistry.getNextAvailableRenderId();
RenderingRegistry.registerBlockHandler(renderTestModelID, new TestBlockHandler());
}

@PostInit
public void load(FMLPreInitializationEvent event)
{
}

@PreInit
public void preLoad(FMLPreInitializationEvent event)
{
}

}
Самое главное не забывайте менять родитель на BlockContainer, чтобы в комментариях не писать глупых вопросов!
Теперь BlockTest:


package mods.testmod.src;

import net.minecraft.block.BlockContainer;
import net.minecraft.block.material.Material;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.*;

public class BlockTest extends BlockContainer
{
public BlockTest(int id)
{
super(id, Material.ground);
}

public int getRenderType()
{
return TestModBase.renderTestModelID;//выбираем наш рендер
}

public boolean isOpaqueCube()
{
return false;
}

public boolean renderAsNormalBlock()
{
return false;
}

@Override
public TileEntity createNewTileEntity(World world)
{
return new TileEntityTestModel();
}
}
TileEntityTestModel:


package mods.testmod.src;

import net.minecraft.tileentity.TileEntity;

public class TileEntityTestModel extends TileEntity
{
public TileEntityTestModel()
{

}
}
И теперь TestBlockHandler, он нужен для регистрации нашего RenderType:

package mods.testmod.src;

import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
import net.minecraft.world.IBlockAccess;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;

public class TestBlockHandler implements ISimpleBlockRenderingHandler
{

@Override
public void renderInventoryBlock(Block block, int metadata, int modelID, RenderBlocks renderer)
{
if(block == TestModBase.testModel)//выбираем тот блок у которого своя модель
TileEntityRenderer.instance.renderTileEntityAt(new TileEntityTestModel(), -0.5D, -0.5D, -0.5D, 0F);//Меняет расположения модели в руке

}

@Override
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer)
{
return false;
}

@Override
public boolean shouldRender3DInInventory()
{
return true;
}

@Override
public int getRenderId()
{
return 0;
}

}
RendererTestModel:


package mods.testmod.src;

import org.lwjgl.opengl.GL11;
import net.minecraft.client.renderer.tileentity.*;
import net.minecraft.tileentity.TileEntity;

public class RendererTestModel extends TileEntitySpecialRenderer
{
private static ModelHead model;//наша модель

public RendererTestModel()
{
model = new ModelHead();
}

public void renderTileEntityAt(TileEntity tileEntity, double d1, double d2, double d3, float f)
{
this.renderTileEntity((TileEntityTestModel)tileEntity, d1, d2, d3, f);
}

public void renderTileEntity(TileEntityTestModel tileEntity, double d1, double d2, double d3, float f)
{
GL11.glPushMatrix();
GL11.glTranslatef((float)d1 + 0.5F, (float)d2 + 1.5F, (float)d3 + 0.5F);
GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F);
this.bindTextureByName("/mods/testmod/textures/head.png");//текстура
this.model.render();
GL11.glPopMatrix();
}
}
И теперь модель, ModelHead (Голова Стива):

package mods.testmod.src;

import net.minecraft.client.model.*;//Добавлем это
import net.minecraft.entity.Entity;//И это

public class ModelHead extends ModelBase
{
//fields
ModelRenderer head;

public ModelHead()
{
textureWidth = 64;
textureHeight = 32;

head = new ModelRenderer(this, 0, 0);
head.addBox(-4F, -8F, -4F, 8, 8, 8);
head.setRotationPoint(0F, 24F, 0F);
head.setTextureSize(64, 32);
head.mirror = true;
setRotation(head, 0F, 0F, 0F);
}

public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5)
{
super.render(entity, f, f1, f2, f3, f4, f5);
setRotationAngles(f, f1, f2, f3, f4, f5, entity);//Изменяем
head.render(f5);
}

private void setRotation(ModelRenderer model, float x, float y, float z)
{
model.rotateAngleX = x;
model.rotateAngleY = y;
model.rotateAngleZ = z;
}

public void setRotationAngles(float f, float f1, float f2, float f3, float f4, float f5)
{
setRotationAngles(f, f1, f2, f3, f4, f5);//Изменяем
}
//Добавляем метод
public void render()
{
head.render(0.0625F);
}

}
Вот для сравнение оригинальная модель головы, (еще до изменения) ModelHead:

// Date: 06.02.2013 17:41:58
// Template version 1.1
// Java generated by Techne
// Keep in mind that you still need to fill in some blanks
// - ZeuX






package net.minecraft.src;

public class ModelNew extends ModelBase
{
//fields
ModelRenderer head;

public ModelNew()
{
textureWidth = 64;
textureHeight = 32;

head = new ModelRenderer(this, 0, 0);
head.addBox(-4F, -8F, -4F, 8, 8, 8);
head.setRotationPoint(0F, 24F, 0F);
head.setTextureSize(64, 32);
head.mirror = true;
setRotation(head, 0F, 0F, 0F);
}

public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5)
{
super.render(entity, f, f1, f2, f3, f4, f5);
setRotationAngles(f, f1, f2, f3, f4, f5);
head.render(f5);
}

private void setRotation(ModelRenderer model, float x, float y, float z)
{
model.rotateAngleX = x;
model.rotateAngleY = y;
model.rotateAngleZ = z;
}

public void setRotationAngles(float f, float f1, float f2, float f3, float f4, float f5)
{
super.setRotationAngles(f, f1, f2, f3, f4, f5);
}

}
12. Что делать если не открывается сундук/печка
Когда вы сделали печку/сундук, и не можете её открыть в игре, а в консоль пишет это:

A mod tried to open a gui on the server without being a NetworkMod
, или в консоль ничего не пишет, надо чтобы modid, и Instance совпадали.
Вот смотрите, у нас в туториале:

@Mod (modid = "TestModBase", name = "Test", version = "0.0.1")
, и

@Instance("TestModBase")
, если TestModBase не будут совпадать, то вы не сможете открыть сундук.
13. Как сделать свой сундук.
Я взял стандартный сундук, и немного изменил, приступим...
(В сундуке возможно есть баги, о багах пишите в тему)
TestModBase:

package mods.testmod.src;

import net.minecraft.block.Block;
import net.minecraft.item.ItemBlock;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.*;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.network.NetworkMod;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;

@Mod(modid="TestModBase", name="TestModBase", version="1.0")
@NetworkMod (clientSideRequired = true, serverSideRequired = false, versionBounds = "1.0.0")
public class TestModBase {

@Instance("TestModBase")
public static TestModBase instance;

public static Block newChest = new BlockNewChest(2020, 0).setUnlocalizedName("newChest");

@Init
public void init(FMLInitializationEvent event)
{
GameRegistry.registerBlock(newChest);
LanguageRegistry.addName(newChest, "New Chest");
//Регистрируем Энтити
GameRegistry.registerTileEntity(TileEntityNewChest.class, "containerNew Chest");
//Нужен для регистрации GUI и Container
NetworkRegistry.instance().registerGuiHandler(this, new GuiHandler());
}
}
Теперь блок BlockNewChest:

package mods.testmod.src;

import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import java.util.Iterator;
import java.util.Random;

import net.minecraft.block.Block;
import net.minecraft.block.BlockContainer;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.texture.IconRegister;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.passive.EntityOcelot;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.InventoryLargeChest;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityChest;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.Icon;
import net.minecraft.util.MathHelper;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;

import static net.minecraftforge.common.ForgeDirection.*;

public class BlockNewChest extends BlockContainer
{
private final Random random = new Random();
public final int field_94443_a;
private Icon bottomIcon;
private Icon topIcon;
private Icon frontIcon;
private Icon sideIcon;


protected BlockNewChest(int par1, int par2)
{
super(par1, Material.wood);
this.field_94443_a = par2;
this.setCreativeTab(CreativeTabs.tabDecorations);
}

//Указываем путь к текстурам
@Override
public void registerIcons(IconRegister par1IconRegister) {
bottomIcon = par1IconRegister.registerIcon("testmod:bottom");
topIcon = par1IconRegister.registerIcon("testmod:top");
sideIcon = par1IconRegister.registerIcon("testmod:side");
frontIcon = par1IconRegister.registerIcon("testmod:front");
}

public Icon getBlockTextureFromSideAndMetadata(int side, int metadata)
{
if(side == 0)
return bottomIcon;
else if(side == 1)
return topIcon;
else if((side == 2 && metadata == 2) || (side == 5 && metadata == 3) || (side == 3 && metadata == 0) || (side == 4 && metadata == 1))
return frontIcon;
else
return sideIcon;
}

/**
* Checks the neighbor blocks to see if there is a chest there. Args: world, x, y, z
*/
private boolean isThereANeighborChest(World par1World, int par2, int par3, int par4)
{
return par1World.getBlockId(par2, par3, par4) != this.blockID ? false : (par1World.getBlockId(par2 - 1, par3, par4) == this.blockID ? true : (par1World.getBlockId(par2 + 1, par3, par4) == this.blockID ? true : (par1World.getBlockId(par2, par3, par4 - 1) == this.blockID ? true : par1World.getBlockId(par2, par3, par4 + 1) == this.blockID)));
}

/**
* Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are
* their own) Args: x, y, z, neighbor blockID
*/
public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, int par5)
{
super.onNeighborBlockChange(par1World, par2, par3, par4, par5);
TileEntityNewChest tileentitychest = (TileEntityNewChest)par1World.getBlockTileEntity(par2, par3, par4);

if (tileentitychest != null)
{
tileentitychest.updateContainingBlockInfo();
}
}

@Override
public void breakBlock(World world, int x, int y, int z, int par5, int par6) {
dropItems(world, x, y, z);
super.breakBlock(world, x, y, z, par5, par6);
}

private void dropItems(World world, int x, int y, int z){
Random rand = new Random();

TileEntity tileEntity = world.getBlockTileEntity(x, y, z);
if (!(tileEntity instanceof IInventory)) {
return;
}
IInventory inventory = (IInventory) tileEntity;

for (int i = 0; i < inventory.getSizeInventory(); i++) {
ItemStack item = inventory.getStackInSlot(i);

if (item != null && item.stackSize > 0) {
float rx = rand.nextFloat() * 0.8F + 0.1F;
float ry = rand.nextFloat() * 0.8F + 0.1F;
float rz = rand.nextFloat() * 0.8F + 0.1F;

EntityItem entityItem = new EntityItem(world,
x + rx, y + ry, z + rz,
new ItemStack(item.itemID, item.stackSize, item.getItemDamage()));

if (item.hasTagCompound()) {
entityItem.getEntityItem().setTagCompound((NBTTagCompound) item.getTagCompound().copy());
}

float factor = 0.05F;
entityItem.motionX = rand.nextGaussian() * factor;
entityItem.motionY = rand.nextGaussian() * factor + 0.2F;
entityItem.motionZ = rand.nextGaussian() * factor;
world.spawnEntityInWorld(entityItem);
item.stackSize = 0;
}
}
}

@Override
public boolean onBlockActivated(World world, int x, int y, int z,
EntityPlayer player, int idk, float what, float these, float are) {
TileEntity tileEntity = world.getBlockTileEntity(x, y, z);
if (tileEntity == null || player.isSneaking()) {
return false;
}
//Открывает GUI
player.openGui(TestModBase.instance, 0, world, x, y, z);
return true;
}


public IInventory func_94442_h_(World par1World, int par2, int par3, int par4)
{
Object object = (TileEntityNewChest)par1World.getBlockTileEntity(par2, par3, par4);

if (object == null)
{
return null;
}
else if (par1World.isBlockSolidOnSide(par2, par3 + 1, par4, DOWN))
{
return null;
}
else if (isOcelotBlockingChest(par1World, par2, par3, par4))
{
return null;
}
else if (par1World.getBlockId(par2 - 1, par3, par4) == this.blockID && (par1World.isBlockSolidOnSide(par2 - 1, par3 + 1, par4, DOWN) || isOcelotBlockingChest(par1World, par2 - 1, par3, par4)))
{
return null;
}
else if (par1World.getBlockId(par2 + 1, par3, par4) == this.blockID && (par1World.isBlockSolidOnSide(par2 + 1, par3 + 1, par4, DOWN) || isOcelotBlockingChest(par1World, par2 + 1, par3, par4)))
{
return null;
}
else if (par1World.getBlockId(par2, par3, par4 - 1) == this.blockID && (par1World.isBlockSolidOnSide(par2, par3 + 1, par4 - 1, DOWN) || isOcelotBlockingChest(par1World, par2, par3, par4 - 1)))
{
return null;
}
else if (par1World.getBlockId(par2, par3, par4 + 1) == this.blockID && (par1World.isBlockSolidOnSide(par2, par3 + 1, par4 + 1, DOWN) || isOcelotBlockingChest(par1World, par2, par3, par4 + 1)))
{
return null;
}
else
{
if (par1World.getBlockId(par2 - 1, par3, par4) == this.blockID)
{
object = new InventoryLargeChest("container.chestDouble", (TileEntityNewChest)par1World.getBlockTileEntity(par2 - 1, par3, par4), (IInventory)object);
}

if (par1World.getBlockId(par2 + 1, par3, par4) == this.blockID)
{
object = new InventoryLargeChest("container.chestDouble", (IInventory)object, (TileEntityNewChest)par1World.getBlockTileEntity(par2 + 1, par3, par4));
}

if (par1World.getBlockId(par2, par3, par4 - 1) == this.blockID)
{
object = new InventoryLargeChest("container.chestDouble", (TileEntityNewChest)par1World.getBlockTileEntity(par2, par3, par4 - 1), (IInventory)object);
}

if (par1World.getBlockId(par2, par3, par4 + 1) == this.blockID)
{
object = new InventoryLargeChest("container.chestDouble", (IInventory)object, (TileEntityNewChest)par1World.getBlockTileEntity(par2, par3, par4 + 1));
}

return (IInventory)object;
}
}

/**
* Returns a new instance of a block's tile entity class. Called on placing the block.
*/
public TileEntity createNewTileEntity(World par1World)
{
TileEntityNewChest tileentitychest = new TileEntityNewChest();
return tileentitychest;
}

/**
* Can this block provide power. Only wire currently seems to have this change based on its state.
*/
public boolean canProvidePower()
{
return this.field_94443_a == 1;
}

/**
* Returns true if the block is emitting indirect/weak redstone power on the specified side. If isBlockNormalCube
* returns true, standard redstone propagation rules will apply instead and this will not be called. Args: World, X,
* Y, Z, side. Note that the side is reversed - eg it is 1 (up) when checking the bottom of the block.
*/
public int isProvidingWeakPower(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
{
if (!this.canProvidePower())
{
return 0;
}
else
{
int i1 = ((TileEntityNewChest)par1IBlockAccess.getBlockTileEntity(par2, par3, par4)).numUsingPlayers;
return MathHelper.clamp_int(i1, 0, 15);
}
}

/**
* Returns true if the block is emitting direct/strong redstone power on the specified side. Args: World, X, Y, Z,
* side. Note that the side is reversed - eg it is 1 (up) when checking the bottom of the block.
*/
public int isProvidingStrongPower(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
{
return par5 == 1 ? this.isProvidingWeakPower(par1IBlockAccess, par2, par3, par4, par5) : 0;
}

/**
* Looks for a sitting ocelot within certain bounds. Such an ocelot is considered to be blocking access to the
* chest.
*/
public static boolean isOcelotBlockingChest(World par0World, int par1, int par2, int par3)
{
Iterator iterator = par0World.getEntitiesWithinAABB(EntityOcelot.class, AxisAlignedBB.getAABBPool().getAABB((double)par1, (double)(par2 + 1), (double)par3, (double)(par1 + 1), (double)(par2 + 2), (double)(par3 + 1))).iterator();
EntityOcelot entityocelot;

do
{
if (!iterator.hasNext())
{
return false;
}

EntityOcelot entityocelot1 = (EntityOcelot)iterator.next();
entityocelot = (EntityOcelot)entityocelot1;
}
while (!entityocelot.isSitting());

return true;
}

/**
* If this returns true, then comparators facing away from this block will use the value from
* getComparatorInputOverride instead of the actual redstone signal strength.
*/
public boolean hasComparatorInputOverride()
{
return true;
}

/**
* If hasComparatorInputOverride returns true, the return value from this is used instead of the redstone signal
* strength when this block inputs to a comparator.
*/
public int getComparatorInputOverride(World par1World, int par2, int par3, int par4, int par5)
{
return Container.func_94526_b(this.func_94442_h_(par1World, par2, par3, par4));
}
}
TileEntityNewChest:


package mods.testmod.src;

import java.util.Iterator;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.block.BlockChest;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.ContainerChest;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.InventoryLargeChest;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;

public class TileEntityNewChest extends TileEntity implements IInventory
{
private ItemStack[] chestContents = new ItemStack[36];

/** Determines if the check for adjacent chests has taken place. */
public boolean adjacentChestChecked = false;

/** Contains the chest tile located adjacent to this one (if any) */
public TileEntityNewChest adjacentChestZNeg;

/** Contains the chest tile located adjacent to this one (if any) */
public TileEntityNewChest adjacentChestXPos;

/** Contains the chest tile located adjacent to this one (if any) */
public TileEntityNewChest adjacentChestXNeg;

/** Contains the chest tile located adjacent to this one (if any) */
public TileEntityNewChest adjacentChestZPosition;

/** The current angle of the lid (between 0 and 1) */
public float lidAngle;

/** The angle of the lid last tick */
public float prevLidAngle;

/** The number of players currently using this chest */
public int numUsingPlayers;

/** Server sync counter (once per 20 ticks) */
private int ticksSinceSync;
private int field_94046_i = -1;
private String field_94045_s;

/**
* Returns the number of slots in the inventory.
*/
public int getSizeInventory()
{
return 27;
}

/**
* Returns the stack in slot i
*/
public ItemStack getStackInSlot(int par1)
{
return this.chestContents[par1];
}

/**
* Removes from an inventory slot (first arg) up to a specified number (second arg) of items and returns them in a
* new stack.
*/
public ItemStack decrStackSize(int par1, int par2)
{
if (this.chestContents[par1] != null)
{
ItemStack itemstack;

if (this.chestContents[par1].stackSize this.getInventoryStackLimit())
{
par2ItemStack.stackSize = this.getInventoryStackLimit();
}

this.onInventoryChanged();
}

/**
* Имя сундука в в инвентаре
*/
public String getInvName()
{
return "New Chest";
}

@Override
public boolean isInvNameLocalized() {
// TODO Auto-generated method stub
return false;
}

/**
* Reads a tile entity from NBT.
*/
public void readFromNBT(NBTTagCompound par1NBTTagCompound)
{
super.readFromNBT(par1NBTTagCompound);
NBTTagList nbttaglist = par1NBTTagCompound.getTagList("Items");
this.chestContents = new ItemStack[this.getSizeInventory()];

if (par1NBTTagCompound.hasKey("CustomName"))
{
this.field_94045_s = par1NBTTagCompound.getString("CustomName");
}

for (int i = 0; i < nbttaglist.tagCount(); ++i)
{
NBTTagCompound nbttagcompound1 = (NBTTagCompound)nbttaglist.tagAt(i);
int j = nbttagcompound1.getByte("Slot") & 255;

if (j >= 0 && j < this.chestContents.length)
{
this.chestContents[j] = ItemStack.loadItemStackFromNBT(nbttagcompound1);
}
}
}

/**
* Writes a tile entity to NBT.
*/
public void writeToNBT(NBTTagCompound par1NBTTagCompound)
{
super.writeToNBT(par1NBTTagCompound);
NBTTagList nbttaglist = new NBTTagList();

for (int i = 0; i < this.chestContents.length; ++i)
{
if (this.chestContents != null)
{
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
nbttagcompound1.setByte("Slot", (byte)i);
this.chestContents.writeToNBT(nbttagcompound1);
nbttaglist.appendTag(nbttagcompound1);
}
}

par1NBTTagCompound.setTag("Items", nbttaglist);

if (this.isInvNameLocalized())
{
par1NBTTagCompound.setString("CustomName", this.field_94045_s);
}
}

/**
* Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't
* this more of a set than a get?*
*/
public int getInventoryStackLimit()
{
return 64;
}

/**
* Do not make give this method the name canInteractWith because it clashes with Container
*/
public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer)
{
return this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord) != this ? false : par1EntityPlayer.getDistanceSq((double)this.xCoord + 0.5D, (double)this.yCoord + 0.5D, (double)this.zCoord + 0.5D) 0 && this.lidAngle == 0.0F && this.adjacentChestZNeg == null && this.adjacentChestXNeg == null)
{
double d1 = (double)this.xCoord + 0.5D;
d0 = (double)this.zCoord + 0.5D;

if (this.adjacentChestZPosition != null)
{
d0 += 0.5D;
}

if (this.adjacentChestXPos != null)
{
d1 += 0.5D;
}

this.worldObj.playSoundEffect(d1, (double)this.yCoord + 0.5D, d0, "random.chestopen", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F);
}

if (this.numUsingPlayers == 0 && this.lidAngle > 0.0F || this.numUsingPlayers > 0 && this.lidAngle < 1.0F)
{
float f1 = this.lidAngle;

if (this.numUsingPlayers > 0)
{
this.lidAngle += f;
}
else
{
this.lidAngle -= f;
}

if (this.lidAngle > 1.0F)
{
this.lidAngle = 1.0F;
}

float f2 = 0.5F;

if (this.lidAngle < f2 && f1 >= f2 && this.adjacentChestZNeg == null && this.adjacentChestXNeg == null)
{
d0 = (double)this.xCoord + 0.5D;
double d2 = (double)this.zCoord + 0.5D;

if (this.adjacentChestZPosition != null)
{
d2 += 0.5D;
}

if (this.adjacentChestXPos != null)
{
d0 += 0.5D;
}

this.worldObj.playSoundEffect(d0, (double)this.yCoord + 0.5D, d2, "random.chestclosed", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F);
}

if (this.lidAngle < 0.0F)
{
this.lidAngle = 0.0F;
}
}
}

/**
* Called when a client event is received with the event number and argument, see World.sendClientEvent
*/
public boolean receiveClientEvent(int par1, int par2)
{
if (par1 == 1)
{
this.numUsingPlayers = par2;
return true;
}
else
{
return super.receiveClientEvent(par1, par2);
}
}

public void openChest()
{
if (this.numUsingPlayers < 0)
{
this.numUsingPlayers = 0;
}

++this.numUsingPlayers;
this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, this.getBlockType().blockID, 1, this.numUsingPlayers);
this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord, this.zCoord, this.getBlockType().blockID);
this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord - 1, this.zCoord, this.getBlockType().blockID);
}

public void closeChest()
{
if (this.getBlockType() != null && this.getBlockType() instanceof BlockChest)
{
--this.numUsingPlayers;
this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, this.getBlockType().blockID, 1, this.numUsingPlayers);
this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord, this.zCoord, this.getBlockType().blockID);
this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord - 1, this.zCoord, this.getBlockType().blockID);
}
}

/**
* Returns true if automation is allowed to insert the given stack (ignoring stack size) into the given slot.
*/
public boolean isStackValidForSlot(int par1, ItemStack par2ItemStack)
{
return true;
}

/**
* invalidates a tile entity
*/
public void invalidate()
{
super.invalidate();
this.updateContainingBlockInfo();
}
}
Теперь самое главное, GuiHandler:

package mods.testmod.src;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import cpw.mods.fml.common.network.IGuiHandler;

public class GuiHandler implements IGuiHandler {

@Override
public Object getServerGuiElement(int id, EntityPlayer player, World world,
int x, int y, int z) {
TileEntity tileEntity = world.getBlockTileEntity(x, y, z);
//Открывает наш контейнер
if(tileEntity instanceof TileEntityNewChest)
{
return new ContainerNewChest(player.inventory, (TileEntityNewChest) tileEntity);
}
return null;
}

@Override
public Object getClientGuiElement(int id, EntityPlayer player, World world,
int x, int y, int z) {
TileEntity tileEntity = world.getBlockTileEntity(x, y, z);
//Открывает наше GUI
if(tileEntity instanceof TileEntityNewChest)
{
return new GuiNewChest(player.inventory, (TileEntityNewChest) tileEntity);
}
return null;

}
}
ContainerNewChest:


package mods.testmod.src;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;

public class ContainerNewChest extends Container
{
private IInventory lowerChestInventory;
private int numRows;

public ContainerNewChest(IInventory par1IInventory, IInventory par2IInventory)
{
this.lowerChestInventory = par2IInventory;
this.numRows = par2IInventory.getSizeInventory() / 9;
par2IInventory.openChest();
int i = (this.numRows - 4) * 18;
int j;
int k;

for (j = 0; j < this.numRows; ++j)
{
for (k = 0; k < 9; ++k)
{
this.addSlotToContainer(new Slot(par2IInventory, k + j * 9, 8 + k * 18, 18 + j * 18));
}
}

for (j = 0; j < 3; ++j)
{
for (k = 0; k < 9; ++k)
{
this.addSlotToContainer(new Slot(par1IInventory, k + j * 9 + 9, 8 + k * 18, 103 + j * 18 + i));
}
}

for (j = 0; j < 9; ++j)
{
this.addSlotToContainer(new Slot(par1IInventory, j, 8 + j * 18, 161 + i));
}
}

public boolean canInteractWith(EntityPlayer par1EntityPlayer)
{
return this.lowerChestInventory.isUseableByPlayer(par1EntityPlayer);
}

/**
* Called when a player shift-clicks on a slot. You must override this or you will crash when someone does that.
*/
public ItemStack transferStackInSlot(EntityPlayer par1EntityPlayer, int par2)
{
ItemStack itemstack = null;
Slot slot = (Slot)this.inventorySlots.get(par2);

if (slot != null && slot.getHasStack())
{
ItemStack itemstack1 = slot.getStack();
itemstack = itemstack1.copy();

if (par2 < this.numRows * 9)
{
if (!this.mergeItemStack(itemstack1, this.numRows * 9, this.inventorySlots.size(), true))
{
return null;
}
}
else if (!this.mergeItemStack(itemstack1, 0, this.numRows * 9, false))
{
return null;
}

if (itemstack1.stackSize == 0)
{
slot.putStack((ItemStack)null);
}
else
{
slot.onSlotChanged();
}
}

return itemstack;
}

/**
* Callback for when the crafting gui is closed.
*/
public void onCraftGuiClosed(EntityPlayer par1EntityPlayer)
{
super.onCraftGuiClosed(par1EntityPlayer);
this.lowerChestInventory.closeChest();
}

/**
* Return this chest container's lower chest inventory.
*/
public IInventory getLowerChestInventory()
{
return this.lowerChestInventory;
}
}
И последнее GuiNewChest:

package mods.testmod.src;

import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.inventory.ContainerChest;
import net.minecraft.inventory.IInventory;
import net.minecraft.util.StatCollector;
import org.lwjgl.opengl.GL11;

@SideOnly(Side.CLIENT)
public class GuiNewChest extends GuiContainer
{
private IInventory upperChestInventory;
private IInventory lowerChestInventory;

/**
* window height is calculated with this values, the more rows, the heigher
*/
private int inventoryRows = 0;

public GuiNewChest(IInventory par1IInventory, IInventory par2IInventory)
{
super(new ContainerChest(par1IInventory, par2IInventory));
this.upperChestInventory = par1IInventory;
this.lowerChestInventory = par2IInventory;
this.allowUserInput = false;
short short1 = 222;
int i = short1 - 108;
this.inventoryRows = par2IInventory.getSizeInventory() / 9;
this.ySize = i + this.inventoryRows * 18;
}

/**
* Draw the foreground layer for the GuiContainer (everything in front of the items)
*/
protected void drawGuiContainerForegroundLayer(int par1, int par2)
{
this.fontRenderer.drawString(this.lowerChestInventory.isInvNameLocalized() ? this.lowerChestInventory.getInvName() : StatCollector.translateToLocal(this.lowerChestInventory.getInvName()), 8, 6, 4210752);
this.fontRenderer.drawString(this.upperChestInventory.isInvNameLocalized() ? this.upperChestInventory.getInvName() : StatCollector.translateToLocal(this.upperChestInventory.getInvName()), 8, this.ySize - 96 + 2, 4210752);
}

/**
* Draw the background layer for the GuiContainer (everything behind the items)
*/
protected void drawGuiContainerBackgroundLayer(float par1, int par2, int par3)
{
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
this.mc.renderEngine.bindTexture("/gui/container.png");//Путь к GUI, мы сделаем с стандартным
int k = (this.width - this.xSize) / 2;
int l = (this.height - this.ySize) / 2;
this.drawTexturedModalRect(k, l, 0, 0, this.xSize, this.inventoryRows * 18 + 17);
this.drawTexturedModalRect(k, l + this.inventoryRows * 18 + 17, 0, 126, this.xSize, 96);
}
}
14. Как сделать свою печку.
Как и сундук, я взял стандартную печку, урезал, и немного изменил. Ну, начинаем...
(В печке возможно есть баги, о багах пишите в тему)
TestModBase:

package mods.testmod.src;

import net.minecraft.block.Block;
import net.minecraft.block.BlockFurnace;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.ItemBlock;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.*;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.network.NetworkMod;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;

@Mod (modid = "TestModBase", name = "Test Mod", version = "0.0.1")
@NetworkMod (clientSideRequired = true, serverSideRequired = false, versionBounds = "1.0.0")

public class TestModBase {

@Instance("TestModBase")
public static TestModBase instance;

public static final Block newFurnaceIdle = (new BlockNewFurnace(2020, false)).setHardness(3.5F).setUnlocalizedName("newfurnace").setCreativeTab(CreativeTabs.tabDecorations);
public static final Block newFurnaceActive = (new BlockNewFurnace(2021, true)).setHardness(3.5F).setLightValue(0.875F).setUnlocalizedName("newfurnace");

@Init
public void init(FMLInitializationEvent event)
{
GameRegistry.registerBlock(newFurnaceIdle);
GameRegistry.registerBlock(newFurnaceActive);
LanguageRegistry.addName(newFurnaceIdle, "New Furnace");
GameRegistry.registerTileEntity(TileEntityNewFurnace.class, "containerNew Furnace");

NetworkRegistry.instance().registerGuiHandler(this, new GuiHandler());
}
}
BlockNewFurnace:


package mods.testmod.src;

import java.util.Random;

import net.minecraft.block.Block;
import net.minecraft.block.BlockContainer;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.texture.IconRegister;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityFurnace;
import net.minecraft.util.Icon;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;

public class BlockNewFurnace extends BlockContainer
{
/**
* Is the random generator used by furnace to drop the inventory contents in random directions.
*/
private final Random furnaceRand = new Random();

/** True if this is an active furnace, false if idle */
private final boolean isActive;

/**
* This flag is used to prevent the furnace inventory to be dropped upon block removal, is used internally when the
* furnace block changes from idle to active and vice-versa.
*/
private static boolean keepFurnaceInventory = false;
@SideOnly(Side.CLIENT)
private Icon field_94458_cO;
@SideOnly(Side.CLIENT)
private Icon field_94459_cP;

private Icon bottomIcon;
private Icon topIcon;
private Icon sideIcon;
private Icon frontIcon;

protected BlockNewFurnace(int par1, boolean par2)
{
super(par1, Material.rock);
this.isActive = par2;
}

/**
* Returns the ID of the items to drop on destruction.
*/
public int idDropped(int par1, Random par2Random, int par3)
{
return TestModBase.newFurnaceIdle.blockID;
}

@Override
public void registerIcons(IconRegister par1IconRegister) {
bottomIcon = par1IconRegister.registerIcon("testmod:bottom");
topIcon = par1IconRegister.registerIcon("testmod:top");
frontIcon = par1IconRegister.registerIcon(this.isActive ? "testmod:furnace_front_active" : "testmod:front");//Если активно текстура одна, если не работает другая текстура. Не доработан поворот front
sideIcon = par1IconRegister.registerIcon("testmod:side");
}

public Icon getBlockTextureFromSideAndMetadata(int side, int metadata)
{
if(side == 0)
return bottomIcon;
else if(side == 1)
return topIcon;
else if((side == 2 && metadata == 2) || (side == 5 && metadata == 3) || (side == 3 && metadata == 0) || (side == 4 && metadata == 1))
return frontIcon;
else
return sideIcon;
}


/**
* Called whenever the block is added into the world. Args: world, x, y, z
*/
public void onBlockAdded(World par1World, int par2, int par3, int par4)
{
super.onBlockAdded(par1World, par2, par3, par4);
this.setDefaultDirection(par1World, par2, par3, par4);
}

/**
* set a blocks direction
*/
private void setDefaultDirection(World par1World, int par2, int par3, int par4)
{
if (!par1World.isRemote)
{
int l = par1World.getBlockId(par2, par3, par4 - 1);
int i1 = par1World.getBlockId(par2, par3, par4 + 1);
int j1 = par1World.getBlockId(par2 - 1, par3, par4);
int k1 = par1World.getBlockId(par2 + 1, par3, par4);
byte b0 = 3;

if (Block.opaqueCubeLookup[l] && !Block.opaqueCubeLookup[i1])
{
b0 = 3;
}

if (Block.opaqueCubeLookup[i1] && !Block.opaqueCubeLookup[l])
{
b0 = 2;
}

if (Block.opaqueCubeLookup[j1] && !Block.opaqueCubeLookup[k1])
{
b0 = 5;
}

if (Block.opaqueCubeLookup[k1] && !Block.opaqueCubeLookup[j1])
{
b0 = 4;
}

par1World.setBlockMetadataWithNotify(par2, par3, par4, b0, 2);
}
}

@Override
public boolean onBlockActivated(World world, int x, int y, int z,
EntityPlayer player, int idk, float what, float these, float are) {
TileEntity tileEntity = world.getBlockTileEntity(x, y, z);
if (tileEntity == null || player.isSneaking()) {
return false;
}

player.openGui(TestModBase.instance, 0, world, x, y, z);
return true;
}


/**
* Update which block ID the furnace is using depending on whether or not it is burning
*/
public static void updateFurnaceBlockState(boolean par0, World par1World, int par2, int par3, int par4)
{
int l = par1World.getBlockMetadata(par2, par3, par4);
TileEntity tileentity = par1World.getBlockTileEntity(par2, par3, par4);
keepFurnaceInventory = true;

if (par0)
{
par1World.setBlock(par2, par3, par4, TestModBase.newFurnaceActive.blockID);
}
else
{
par1World.setBlock(par2, par3, par4, TestModBase.newFurnaceIdle.blockID);
}

keepFurnaceInventory = false;
par1World.setBlockMetadataWithNotify(par2, par3, par4, l, 2);

if (tileentity != null)
{
tileentity.validate();
par1World.setBlockTileEntity(par2, par3, par4, tileentity);
}
}

@SideOnly(Side.CLIENT)

/**
* A randomly called display update to be able to add particles or other items for display
*/
public void randomDisplayTick(World par1World, int par2, int par3, int par4, Random par5Random)
{
if (this.isActive)
{
int l = par1World.getBlockMetadata(par2, par3, par4);
float f = (float)par2 + 0.5F;
float f1 = (float)par3 + 0.0F + par5Random.nextFloat() * 6.0F / 16.0F;
float f2 = (float)par4 + 0.5F;
float f3 = 0.52F;
float f4 = par5Random.nextFloat() * 0.6F - 0.3F;

if (l == 4)
{
par1World.spawnParticle("smoke", (double)(f - f3), (double)f1, (double)(f2 + f4), 0.0D, 0.0D, 0.0D);
par1World.spawnParticle("flame", (double)(f - f3), (double)f1, (double)(f2 + f4), 0.0D, 0.0D, 0.0D);
}
else if (l == 5)
{
par1World.spawnParticle("smoke", (double)(f + f3), (double)f1, (double)(f2 + f4), 0.0D, 0.0D, 0.0D);
par1World.spawnParticle("flame", (double)(f + f3), (double)f1, (double)(f2 + f4), 0.0D, 0.0D, 0.0D);
}
else if (l == 2)
{
par1World.spawnParticle("smoke", (double)(f + f4), (double)f1, (double)(f2 - f3), 0.0D, 0.0D, 0.0D);
par1World.spawnParticle("flame", (double)(f + f4), (double)f1, (double)(f2 - f3), 0.0D, 0.0D, 0.0D);
}
else if (l == 3)
{
par1World.spawnParticle("smoke", (double)(f + f4), (double)f1, (double)(f2 + f3), 0.0D, 0.0D, 0.0D);
par1World.spawnParticle("flame", (double)(f + f4), (double)f1, (double)(f2 + f3), 0.0D, 0.0D, 0.0D);
}
}
}

/**
* Returns a new instance of a block's tile entity class. Called on placing the block.
*/
public TileEntity createNewTileEntity(World par1World)
{
return new TileEntityNewFurnace();
}

/**
* ejects contained items into the world, and notifies neighbours of an update, as appropriate
*/
public void breakBlock(World par1World, int par2, int par3, int par4, int par5, int par6)
{
if (!keepFurnaceInventory)
{
TileEntityNewFurnace tileentityfurnace = (TileEntityNewFurnace)par1World.getBlockTileEntity(par2, par3, par4);

if (tileentityfurnace != null)
{
for (int j1 = 0; j1 < tileentityfurnace.getSizeInventory(); ++j1)
{
ItemStack itemstack = tileentityfurnace.getStackInSlot(j1);

if (itemstack != null)
{
float f = this.furnaceRand.nextFloat() * 0.8F + 0.1F;
float f1 = this.furnaceRand.nextFloat() * 0.8F + 0.1F;
float f2 = this.furnaceRand.nextFloat() * 0.8F + 0.1F;

while (itemstack.stackSize > 0)
{
int k1 = this.furnaceRand.nextInt(21) + 10;

if (k1 > itemstack.stackSize)
{
k1 = itemstack.stackSize;
}

itemstack.stackSize -= k1;
EntityItem entityitem = new EntityItem(par1World, (double)((float)par2 + f), (double)((float)par3 + f1), (double)((float)par4 + f2), new ItemStack(itemstack.itemID, k1, itemstack.getItemDamage()));

if (itemstack.hasTagCompound())
{
entityitem.getEntityItem().setTagCompound((NBTTagCompound)itemstack.getTagCompound().copy());
}

float f3 = 0.05F;
entityitem.motionX = (double)((float)this.furnaceRand.nextGaussian() * f3);
entityitem.motionY = (double)((float)this.furnaceRand.nextGaussian() * f3 + 0.2F);
entityitem.motionZ = (double)((float)this.furnaceRand.nextGaussian() * f3);
par1World.spawnEntityInWorld(entityitem);
}
}
}

par1World.func_96440_m(par2, par3, par4, par5);
}
}

super.breakBlock(par1World, par2, par3, par4, par5, par6);
}

/**
* If this returns true, then comparators facing away from this block will use the value from
* getComparatorInputOverride instead of the actual redstone signal strength.
*/
public boolean hasComparatorInputOverride()
{
return true;
}

/**
* If hasComparatorInputOverride returns true, the return value from this is used instead of the redstone signal
* strength when this block inputs to a comparator.
*/
public int getComparatorInputOverride(World par1World, int par2, int par3, int par4, int par5)
{
return Container.func_94526_b((IInventory)par1World.getBlockTileEntity(par2, par3, par4));
}
}
TileEntityNewFurnace:


package mods.testmod.src;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemHoe;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemSword;
import net.minecraft.item.ItemTool;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.common.ForgeDirection;
import net.minecraftforge.common.ForgeDummyContainer;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;

public class TileEntityNewFurnace extends TileEntity implements ISidedInventory, net.minecraftforge.common.ISidedInventory
{
/**
* The ItemStacks that hold the items currently being used in the furnace
*/
private ItemStack[] furnaceItemStacks = new ItemStack[3];

/** The number of ticks that the furnace will keep burning */
public int furnaceBurnTime = 0;

/**
* The number of ticks that a fresh copy of the currently-burning item would keep the furnace burning for
*/
public int currentItemBurnTime = 0;

/** The number of ticks that the current item has been cooking for */
public int furnaceCookTime = 0;

/**
* Returns the number of slots in the inventory.
*/
public int getSizeInventory()
{
return this.furnaceItemStacks.length;
}

/**
* Returns the stack in slot i
*/
public ItemStack getStackInSlot(int par1)
{
return this.furnaceItemStacks[par1];
}

/**
* Removes from an inventory slot (first arg) up to a specified number (second arg) of items and returns them in a
* new stack.
*/
public ItemStack decrStackSize(int par1, int par2)
{
if (this.furnaceItemStacks[par1] != null)
{
ItemStack itemstack;

if (this.furnaceItemStacks[par1].stackSize this.getInventoryStackLimit())
{
par2ItemStack.stackSize = this.getInventoryStackLimit();
}
}

/**
* Имя в инвентаре
*/
public String getInvName()
{
return "New Furnace";
}

/**
* Reads a tile entity from NBT.
*/
public void readFromNBT(NBTTagCompound par1NBTTagCompound)
{
super.readFromNBT(par1NBTTagCompound);
NBTTagList nbttaglist = par1NBTTagCompound.getTagList("Items");
this.furnaceItemStacks = new ItemStack[this.getSizeInventory()];

for (int i = 0; i < nbttaglist.tagCount(); ++i)
{
NBTTagCompound nbttagcompound1 = (NBTTagCompound)nbttaglist.tagAt(i);
byte b0 = nbttagcompound1.getByte("Slot");

if (b0 >= 0 && b0 < this.furnaceItemStacks.length)
{
this.furnaceItemStacks[b0] = ItemStack.loadItemStackFromNBT(nbttagcompound1);
}
}

this.furnaceBurnTime = par1NBTTagCompound.getShort("BurnTime");
this.furnaceCookTime = par1NBTTagCompound.getShort("CookTime");
this.currentItemBurnTime = getItemBurnTime(this.furnaceItemStacks[1]);

}

/**
* Writes a tile entity to NBT.
*/
public void writeToNBT(NBTTagCompound par1NBTTagCompound)
{
super.writeToNBT(par1NBTTagCompound);
par1NBTTagCompound.setShort("BurnTime", (short)this.furnaceBurnTime);
par1NBTTagCompound.setShort("CookTime", (short)this.furnaceCookTime);
NBTTagList nbttaglist = new NBTTagList();

for (int i = 0; i < this.furnaceItemStacks.length; ++i)
{
if (this.furnaceItemStacks != null)
{
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
nbttagcompound1.setByte("Slot", (byte)i);
this.furnaceItemStacks.writeToNBT(nbttagcompound1);
nbttaglist.appendTag(nbttagcompound1);
}
}

par1NBTTagCompound.setTag("Items", nbttaglist);
}

/**
* Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't
* this more of a set than a get?*
*/
public int getInventoryStackLimit()
{
return 64;
}

@SideOnly(Side.CLIENT)

/**
* Returns an integer between 0 and the passed value representing how close the current item is to being completely
* cooked
*/
public int getCookProgressScaled(int par1)
{
return this.furnaceCookTime * par1 / 200;
}

@SideOnly(Side.CLIENT)

/**
* Returns an integer between 0 and the passed value representing how much burn time is left on the current fuel
* item, where 0 means that the item is exhausted and the passed value means that the item is fresh
*/
public int getBurnTimeRemainingScaled(int par1)
{
if (this.currentItemBurnTime == 0)
{
this.currentItemBurnTime = 200;
}

return this.furnaceBurnTime * par1 / this.currentItemBurnTime;
}

/**
* Returns true if the furnace is currently burning
*/
public boolean isBurning()
{
return this.furnaceBurnTime > 0;
}

/**
* Allows the entity to update its state. Overridden in most subclasses, e.g. the mob spawner uses this to count
* ticks and creates a new spawn inside its implementation.
*/
public void updateEntity()
{
boolean flag = this.furnaceBurnTime > 0;
boolean flag1 = false;

if (this.furnaceBurnTime > 0)
{
--this.furnaceBurnTime;
}

if (!this.worldObj.isRemote)
{
if (this.furnaceBurnTime == 0 && this.canSmelt())
{
this.currentItemBurnTime = this.furnaceBurnTime = getItemBurnTime(this.furnaceItemStacks[1]);

if (this.furnaceBurnTime > 0)
{
flag1 = true;

if (this.furnaceItemStacks[1] != null)
{
--this.furnaceItemStacks[1].stackSize;

if (this.furnaceItemStacks[1].stackSize == 0)
{
this.furnaceItemStacks[1] = this.furnaceItemStacks[1].getItem().getContainerItemStack(furnaceItemStacks[1]);
}
}
}
}

if (this.isBurning() && this.canSmelt())
{
++this.furnaceCookTime;

if (this.furnaceCookTime == 200)
{
this.furnaceCookTime = 0;
this.smeltItem();
flag1 = true;
}
}
else
{
this.furnaceCookTime = 0;
}

if (flag != this.furnaceBurnTime > 0)
{
flag1 = true;
BlockNewFurnace.updateFurnaceBlockState(this.furnaceBurnTime > 0, this.worldObj, this.xCoord, this.yCoord, this.zCoord);
}
}

if (flag1)
{
this.onInventoryChanged();
}
}

/**
* Returns true if the furnace can smelt an item, i.e. has a source item, destination stack isn't full, etc.
*/
private boolean canSmelt()
{
if (this.furnaceItemStacks[0] == null)
{
return false;
}
else
{
ItemStack itemstack = NewFurnaceRecipes.smelting().getSmeltingResult(this.furnaceItemStacks[0]);
if (itemstack == null) return false;
if (this.furnaceItemStacks[2] == null) return true;
if (!this.furnaceItemStacks[2].isItemEqual(itemstack)) return false;
int result = furnaceItemStacks[2].stackSize + itemstack.stackSize;
return (result
 
Главная Регистрация
Назад
Сверху