- 3,603
- 99
- 664
Предисловие:
В связи с тем, что на форуме стали активно появляется темы данного рода: "[1.10.2] Как зарегать текстуру для предмета", "[1.11] Памагите!!11", "[1.10.2] Как создать предмет?!?", я был вынужден создать данную тему, где по полочкам разложу, как написать свой мод для mc версий 1.8+. Базовые статьи будут выходить часто, чтобы как можно быстрее наполнить тему, а вот статьи продвинутые будут выходить по 2-3 в неделю, так же они будут создаваться в случайном порядке. Так же скорость создания тех или иных статей зависит только от вас.
Статьи:
Доска в Trello, там вы можете выбрать(проголосовать) за статью, что ускорит её выход в учебник.
В связи с тем, что на форуме стали активно появляется темы данного рода: "[1.10.2] Как зарегать текстуру для предмета", "[1.11] Памагите!!11", "[1.10.2] Как создать предмет?!?", я был вынужден создать данную тему, где по полочкам разложу, как написать свой мод для mc версий 1.8+. Базовые статьи будут выходить часто, чтобы как можно быстрее наполнить тему, а вот статьи продвинутые будут выходить по 2-3 в неделю, так же они будут создаваться в случайном порядке. Так же скорость создания тех или иных статей зависит только от вас.
Статьи:
Мечтали ли вы сделать себе новый эффект для зелья? Конечно же мечтали Сейчас я расскажу как создать свой эффект для зелья.
Для начала создадим класс PotionSnowman.
Теперь создадим класс PotionsRegister.
И добавим в ServerProxy в метод preInit() такой код:
Теперь добавим в EventsHandler вот такое событие:
Заходим в игру и прописываем команду
и начинаем ходить по земле ставя под ногами снег. Чтобы использовать наш эффект в предмете или ещё где нибудь, пропишем такой код:
Для начала создадим класс PotionSnowman.
Код:
public class PotionSnowman extends Potion
{
//Путь к нашей текстуре с иконками
private static final ResourceLocation ICON = new ResourceLocation("textures/gui/container/inventory.png");
/**
* Конструктор который передаст наши значения родителю.
* @param isBadEffectIn
* @param liquidColorIn
*/
public PotionSnowman(boolean isBadEffectIn, int liquidColorIn)
{
super(isBadEffectIn, liquidColorIn);
setRegistryName("potionSnowman");
//Это что-то типа UnlocalizedName для предмета
setPotionName("effect.snowman");
/*
* Полезный ли это эффект. Так же влияет на позицию иконки на экране.
*/
setBeneficial();
}
//Данный метод отвечает за рендер иконки в GuiInventory.
@Override
@SideOnly(Side.CLIENT)
public void renderInventoryEffect(int x, int y, PotionEffect effect, Minecraft mc)
{
//Вызываем нашу текстуру.
mc.renderEngine.bindTexture(ICON);
/**
* Отрисовываем нашу иконку на текстуре
* 1 - Позиция по оси X
* 2 - Позиция по оси Y
* 3 - Позиция иконки на самой текстуре по X
* 4 - Позиция иконки на самой текстуре по Y
* 5 - Ширина иконки
* 6 - Высота иконки
*/
mc.currentScreen.drawTexturedModalRect(x + 10, y + 11, 76, 238, 10, 10);
}
//Данный метод отвечает за рендер иконки на экране.
@Override
@SideOnly(Side.CLIENT)
public void renderHUDEffect(int x, int y, PotionEffect effect, Minecraft mc, float alpha)
{
mc.renderEngine.bindTexture(ICON);
mc.ingameGUI.drawTexturedModalRect(x + 7, y + 7, 76, 238, 10, 10);
}
}
Теперь создадим класс PotionsRegister.
Код:
public class PotionsRegister
{
public static PotionSnowman POTIONSNOWMAN = new PotionSnowman(false, 16777215);
public static void register()
{
GameRegistry.register(POTIONSNOWMAN);
}
}
И добавим в ServerProxy в метод preInit() такой код:
Код:
PotionsRegister.register();
Теперь добавим в EventsHandler вот такое событие:
Код:
@SubscribeEvent
public void onGround(LivingEvent.LivingUpdateEvent e)
{
//Проверяем сущность на игрока
if(e.getEntity() instanceof EntityPlayer)
{
//Получение нашего игрока
EntityPlayer player = (EntityPlayer) e.getEntity();
//Условие, если наше зелье активно и игрок на земле, то ставить снег на месте игрока.
if (player.isPotionActive(PotionsRegister.potionSnowman) && player.onGround)
{
int i, j, k;
for (int l = 0; l < 4; ++l)
{
i = MathHelper.floor_double(player.posX + (double) ((float) (l % 2 * 2 - 1) * 0.25F));
j = MathHelper.floor_double(player.posY);
k = MathHelper.floor_double(player.posZ + (double) ((float) (l / 2 % 2 * 2 - 1) * 0.25F));
BlockPos blockpos = new BlockPos(i, j, k);//получение позиции блока, на котором стоит игрок.
//Если под ногами игрока ничего нет и снег можно поставить, то мы соответственно ставим слой снега.
if (player.worldObj.getBlockState(blockpos).getMaterial() == Material.AIR && Blocks.SNOW_LAYER.canPlaceBlockAt(player.worldObj, blockpos))
{
player.worldObj.setBlockState(blockpos, Blocks.SNOW_LAYER.getDefaultState());
}
}
}
}
}
Заходим в игру и прописываем команду
Код:
/effect @p modexample:potionSnowman 1000
Код:
player.addPotionEffect(new PotionEffect(PotionSnowman.POTIONSNOWMAN, this.duration, 1));
Создание растения такое же простое, как и создание блока.
Создадим класс BlockDelphinium:
зарегистрируем. Теперь добавим модель и текстуру.
Заходим в игру и видим:
Создадим класс BlockDelphinium:
Код:
/**
* Как вы могли заметить, что вместо Block мы прописали BlockBush.
* Это некая заготовка/форма для простого создания своего растения.
*/
public class BlockDelphinium extends BlockBush
{
public BlockDelphinium(String name)
{
setRegistryName(name);
setUnlocalizedName(name);
//Прочность блока
setHardness(0);
setCreativeTab(TabsHandler.TAB_BLOCKS);
}
/**
* Квадрат столкновения. В данном случаем это заполненая зона 16x16.
*/
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos) {
return FULL_BLOCK_AABB;
}
}
зарегистрируем. Теперь добавим модель и текстуру.
Заходим в игру и видим:
С растениями в один блок всё понятно, а вот как сделать в два? Ведь в два блока круче чем какой то куст в один
Создадим класс BlockReed:
На будущие рекомендую сделать отдельный класс, допустим DoubleBlock, и засунуть туда весь код, кроме конструктора, чтобы потом не создавать одно и тоже для каждого растения в два блока.
Теперь зарегистрируем наш блок. Добавим модели, которые я сделал для вас
Запускаем игру и видим, что у нас получилось.
Создадим класс BlockReed:
Код:
public class BlockReed extends BlockBush
{
/**
* Переменная которая задаёт для состояния блока переменную half.
*/
public static final PropertyEnum<BlockDoublePlant.EnumBlockHalf> HALF = PropertyEnum.create("half", BlockDoublePlant.EnumBlockHalf.class);
public BlockReed(String name)
{
setRegistryName(name);
setUnlocalizedName(name);
setCreativeTab(TabsHandler.TAB_BLOCKS);
//Прочность блока
setHardness(0);
/**
* Стандартные состояния, которые должны быть. В данном случае это half=lower и half=upper.
* Про состояния блоков будет отдельная статья!
*/
setDefaultState(this.blockState.getBaseState().withProperty(HALF, BlockDoublePlant.EnumBlockHalf.LOWER).withProperty(HALF, BlockDoublePlant.EnumBlockHalf.UPPER));
}
/**
* Квадрат столкновения. В данном случаем это заполненая зона 16x16.
*/
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos)
{
return FULL_BLOCK_AABB;
}
/**
* Если под нашим растением нету блока, то уничтожать нижний и верхний блок растения, и дропать блок.
*/
@Override
protected void checkAndDropBlock(World worldIn, BlockPos pos, IBlockState state)
{
if (!this.canBlockStay(worldIn, pos, state))
{
boolean flag = state.getValue(HALF) == BlockDoublePlant.EnumBlockHalf.UPPER;
BlockPos blockpos = flag ? pos : pos.up();
BlockPos blockpos1 = flag ? pos.down() : pos;
Block block = flag ? this : worldIn.getBlockState(blockpos).getBlock();
Block block1 = flag ? worldIn.getBlockState(blockpos1).getBlock() : this;
if (!flag) this.dropBlockAsItem(worldIn, pos, state, 0);
if (block == this)
{
worldIn.setBlockState(blockpos, Blocks.AIR.getDefaultState(), 2);
}
if (block1 == this)
{
worldIn.setBlockState(blockpos1, Blocks.AIR.getDefaultState(), 3);
}
}
}
/**
* Делаем проверку на то, что есть ли под верхним блоком нижний.
*/
@Override
public boolean canBlockStay(World worldIn, BlockPos pos, IBlockState state)
{
if (state.getBlock() != this) return super.canBlockStay(worldIn, pos, state);
if (state.getValue(HALF) == BlockDoublePlant.EnumBlockHalf.UPPER)
{
return worldIn.getBlockState(pos.down()).getBlock() == this;
}
else
{
IBlockState iblockstate = worldIn.getBlockState(pos.up());
return iblockstate.getBlock() == this && super.canBlockStay(worldIn, pos, iblockstate);
}
}
/**
* Если блок был установлен, то ставим над ним верхний блок.
*/
@Override
public void onBlockPlacedBy(World worldIn, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack)
{
worldIn.setBlockState(pos.up(), this.getDefaultState().withProperty(HALF, BlockDoublePlant.EnumBlockHalf.UPPER), 2);
}
/**
* Проверка на сломаный блок
*/
@Override
public void onBlockHarvested(World worldIn, BlockPos pos, IBlockState state, EntityPlayer player)
{
//Если это верхний блок
if (state.getValue(HALF) == BlockDoublePlant.EnumBlockHalf.UPPER)
{
if (worldIn.getBlockState(pos.down()).getBlock() == this)
{
if (player.capabilities.isCreativeMode)
{
worldIn.setBlockToAir(pos.down());
}
else
{
if (worldIn.isRemote)
{
worldIn.setBlockToAir(pos.down());
}
else if (player.getHeldItemMainhand() != null && player.getHeldItemMainhand().getItem() == Items.SHEARS)
{
worldIn.setBlockToAir(pos.down());
}
else
{
worldIn.destroyBlock(pos.down(), true);
}
}
}
}
else if (worldIn.getBlockState(pos.up()).getBlock() == this)
{
worldIn.setBlockState(pos.up(), Blocks.AIR.getDefaultState(), 2);
}
super.onBlockHarvested(worldIn, pos, state, player);
}
/**
* Получение метатега.
*/
@Override
public IBlockState getStateFromMeta(int meta)
{
return (meta & 8) > 0 ? this.getDefaultState().withProperty(HALF, BlockDoublePlant.EnumBlockHalf.UPPER) : this.getDefaultState().withProperty(HALF, BlockDoublePlant.EnumBlockHalf.LOWER);
}
/**
* Получение метатега.
*/
@Override
public int getMetaFromState(IBlockState state)
{
return state.getValue(HALF) == BlockDoublePlant.EnumBlockHalf.UPPER ? 8 : 0;
}
/**
* Можно ли разместить блок сверху
*/
@Override
public boolean canPlaceBlockAt(World worldIn, BlockPos pos)
{
return super.canPlaceBlockAt(worldIn, pos) && worldIn.isAirBlock(pos.up());
}
/**
* Регистрация состояния блока.
*/
@Override
protected BlockStateContainer createBlockState()
{
return new BlockStateContainer(this, new IProperty[] {HALF});
}
}
Теперь зарегистрируем наш блок. Добавим модели, которые я сделал для вас
Запускаем игру и видим, что у нас получилось.
Метадата это некий "тип" для предмета, она нужна для создания большого количество предметов одним классом, прочности(как в ic2, заряженный предмет 133:1 разряженный 133:27) и т.п. вещам. В данном уроке мы рассмотрим прочность и добавление цвета к каждому предмету с различной метадатой.
Пропишем данный метод в конструктор нашего предмета:
Создадим в классе с предметом getSubItems:
И такой метод:
Теперь перейдём в класс ItemsRegister и добавим в метод registerRender такой код:
Как вы могли заметить, после SPHERE значение с 0 поменялось на 1, 2, 3 - это метадата нашего предмета, мы можем подгружать для каждой меты свою модель, но в данном случае мы подгружаем стандартную модель сферы.
Пропишем данный метод в конструктор нашего предмета:
Код:
//Данный конструктор объявляет о том, что наш предмет имеет подтипы
setHasSubtypes(true);
Создадим в классе с предметом getSubItems:
Код:
/**
* Данный метод возвращает предметы с метадатой
* @param itemIn - это наш предмет
* @param tab - получение вкладки (?)
* @param subItems - лист в котором хранятся наши предметы с метадатой.
*/
@Override
@SideOnly(Side.CLIENT)
public void getSubItems(Item itemIn, CreativeTabs tab, NonNullList<ItemStack>subItems)
{
/**
* 1 - наш предмет
* 2 - количество
* 3 - метадата, начинается с 0
*/
subItems.add(new ItemStack(itemIn, 1, 0));
subItems.add(new ItemStack(itemIn, 1, 1));
subItems.add(new ItemStack(itemIn, 1, 2));
subItems.add(new ItemStack(itemIn, 1, 3));
}
И такой метод:
Код:
/**
* Данный метод возвращает отображаемое название предмета
* @param stack - наш предмет
* @return
*/
@SideOnly(Side.CLIENT)
public String getItemStackDisplayName(final ItemStack stack)
{
final TextFormatting s;
//Получаем метадату и проверяем
switch(stack.getMetadata())
{
case 0: s = TextFormatting.DARK_GRAY; break;//Для 0 метадаты цвет тёмно-серый
case 1: s = TextFormatting.BLUE; break;//Для 1 метадаты цвет синий
case 2: s = TextFormatting.YELLOW; break;//Для 2 метадаты цвет жёлтый
case 3: s = TextFormatting.DARK_RED; break;//Для 3 метадаты цвет тёмно-красный
default: s = TextFormatting.GRAY; break;//Для всех остальный метадат серый цвет
}
//Возвращаем название предмета
return s + I18n.translateToLocal(getUnlocalizedName() + ".name");
}
Теперь перейдём в класс ItemsRegister и добавим в метод registerRender такой код:
Код:
Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(SPHERE, 1, new ModelResourceLocation(SPHERE.getRegistryName(), "inventory"));
Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(SPHERE, 2, new ModelResourceLocation(SPHERE.getRegistryName(), "inventory"));
Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(SPHERE, 3, new ModelResourceLocation(SPHERE.getRegistryName(), "inventory"));
В этой статье вы научитесь делать обновления к своему моду. Перейдём на GitHubGist и создадим файл update.json.
Нажимаем "Create public gist", нас перебросит к нашему файлу.
1 - С помощью этой кнопки мы будем получать полный путь до нашего файла, а затем копировать его в наш мод.
2 - С помощью этой кнопки мы будем вносить изменения в наш файл(Добавлять новые версии, описание фиксов и т.п.)
Нажимаем на кнопку "Raw" и копируем ссылку из адресной строки! Теперь переходим в среду разработки с нашим модом. Зайдём в главный класс и в аннотации @mod пропишем такую переменную:
затем вставим туда нашу ссылку:
изменим для проверки нашу переменную version дав ей значение "1.0.0.7" и зайдём в игру. И вот, что у нас получилось.
[video=youtube]
Нажимаем "Create public gist", нас перебросит к нашему файлу.
1 - С помощью этой кнопки мы будем получать полный путь до нашего файла, а затем копировать его в наш мод.
2 - С помощью этой кнопки мы будем вносить изменения в наш файл(Добавлять новые версии, описание фиксов и т.п.)
Нажимаем на кнопку "Raw" и копируем ссылку из адресной строки! Теперь переходим в среду разработки с нашим модом. Зайдём в главный класс и в аннотации @mod пропишем такую переменную:
Код:
updateJSON = ""
Код:
updateJSON = "https://gist.githubusercontent.com/WildsHearts/c71a54eb8491e43eae1547fc91bf7364/raw/cb295aa31051b3ad42706b2616ac0bd128bb8a7a/update.json"
[video=youtube]
В данной статье Вы научитесь создавать блок-контейнер или по простому, хранилище.
Создадим блок-хранилище:
И зарегистрируем его.
Создадим TileEntity:
Создадим GuiHandler, чтобы регистрировать наши контейнеры, gui и прочее:
И в ServerProxy в метод init() добавим такую строку:
Она нужна будет только для регистрации GuiHandler, прописывать её повторно не нужно. Теперь все манипуляции с gui, контейнерами будут происходить именно в GuiHandler.
Теперь создадим GuiWatcher:
И теперь создадим для него ContainerWatcher:
Теперь создадим класс EntityRegister:
Далее добавим в ServerProxy в метод preInit такой код:
Создадим блок-хранилище:
Код:
public class BlockWatcher extends BlockContainer
{
public BlockWatcher(String name)
{
super(Material.WOOD);
setRegistryName(name);
setUnlocalizedName(name);
setSoundType(SoundType.WOOD);
setHardness(2.0F);
setCreativeTab(TabsHandler.TAB_BLOCKS);
}
/**
* Данный метод срабатывает когда с блоком произошло взаимодействие. В нашем случаи если игрок кликнул, то открывает
* окно с ивентарём.
*/
@Override
public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ)
{
if(!worldIn.isRemote)
{
TileEntity entity = worldIn.getTileEntity(pos);
if(entity instanceof TileWatcher)
{
playerIn.openGui(Core.INSTANCE, GuiHandler.GUI_WATCHER, worldIn, pos.getX(), pos.getY(), pos.getZ());
}
}
return true;
}
/**
* Данный метод создаёт для нашего блок сущность.
*/
@Override
public TileEntity createNewTileEntity(World worldIn, int meta)
{
return new TileWatcher();
}
@Override
public void breakBlock(World worldIn, BlockPos pos, IBlockState state)
{
TileEntity tileEntity = worldIn.getTileEntity(pos);
if(tileEntity instanceof TileWatcher)
{
TileWatcher watcher = (TileWatcher) tileEntity;
InventoryHelper.dropInventoryItems(worldIn, pos, watcher.basic);
worldIn.updateComparatorOutputLevel(pos, this);
}
super.breakBlock(worldIn, pos, state);
}
}
Создадим TileEntity:
Код:
public class TileWatcher extends TileEntity
{
public InventoryBasic basic;
public TileWatcher()
{
/**
* Создадим новый инвентарь для TileEntity.
* @param tile - это название нашего инвентаря.
* @param customName - задаёт кастомное имя.
* @param slotCount - количество слотов
*/
basic = new InventoryBasic("invWatcher", false, 15);
}
/**
* Дабы наши вещи сохранялись, мы будем записывать их в NBT самого TileEntity.
*/
@Override
public NBTTagCompound writeToNBT(NBTTagCompound compound)
{
super.writeToNBT(compound);
NBTTagList list = new NBTTagList();
for(int i = 0; i < this.basic.getSizeInventory(); ++i)
{
if(this.basic.getStackInSlot(i) != null)
{
NBTTagCompound tag = new NBTTagCompound();
tag.setByte("Slot", (byte) i);
this.basic.getStackInSlot(i).writeToNBT(tag);
list.appendTag(tag);
}
}
compound.setTag("Items", list);
return compound;
}
/**
* Данный метод будет читать NBT и выводить значения из него.
*/
@Override
public void readFromNBT(NBTTagCompound compound)
{
super.readFromNBT(compound);
NBTTagList list = compound.getTagList("Items", 10);
for(int i = 0; i < list.tagCount(); ++i)
{
NBTTagCompound tag = list.getCompoundTagAt(i);
int j = tag.getByte("Slot") & 255;
if(j >= 0 && j < this.basic.getSizeInventory())
{
this.basic.setInventorySlotContents(j, new ItemStack(tag));
}
}
}
}
Создадим GuiHandler, чтобы регистрировать наши контейнеры, gui и прочее:
Код:
public class GuiHandler implements IGuiHandler
{
/**
* Это параметр хранящий в себе id нашего gui.
*/
public static final int GUI_WATCHER = 0;
/**
* Серверный метод открытия gui. Здесь мы будем открывать контейнер, так как серверу графика не нужна.
* @param ID - уникальный идентификатор нашего gui
* @param player - игрок, который открывает
* @param world - мир в котором открывается
* @param x - позиция по оси X (В основном позиции нужны для tileEntity, но о нём позже)
* @param y - позиция по оси Y
* @param z - позиция по оси Z
* @return
*/
@Override
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z)
{
switch (ID)
{
case GUI_WATCHER: return new ContainerWatcher(player.inventory, (TileWatcher) world.getTileEntity(new BlockPos(x, y, z)));
default: return null;
}
}
/**
* Серверный метод открытия gui. Здесь мы будем открывать gui и контейнер.
* @param ID - уникальный идентификатор нашего gui
* @param player - игрок, который открывает
* @param world - мир в котором открывается
* @param x - позиция по оси X (В основном позиции нужны для tileEntity, но о нём позже)
* @param y - позиция по оси Y
* @param z - позиция по оси Z
* @return
*/
@Override
@SideOnly(Side.CLIENT)
public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z)
{
switch (ID)
{
case GUI_WATCHER: return new GuiWatcher(new ContainerWatcher(player.inventory, (TileWatcher) world.getTileEntity(new BlockPos(x, y, z))));
default: return null;
}
}
}
И в ServerProxy в метод init() добавим такую строку:
Код:
NetworkRegistry.INSTANCE.registerGuiHandler(Core.INSTANCE, new GuiHandler());
Теперь создадим GuiWatcher:
Код:
public class GuiWatcher extends GuiContainer
{
/**
* Это текстура нашего gui. В данном случаи используется текстура выбрасывателя.
*/
private static final ResourceLocation TEXTURE = new ResourceLocation("textures/gui/container/dispenser.png");
public GuiWatcher(Container inventorySlotsIn)
{
super(inventorySlotsIn);
}
@Override
protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY)
{
/**
* Располагаем gui по центру.
*/
mc.renderEngine.bindTexture(TEXTURE);
int x = (this.width - this.xSize) / 2;
int y = (this.height - this.xSize) / 2;
drawTexturedModalRect(x, y, 0, 0, xSize, ySize);
}
}
И теперь создадим для него ContainerWatcher:
Код:
public class ContainerWatcher extends Container
{
public ContainerWatcher(IInventory playerInv, TileWatcher watcher)
{
int i = -18;
int j;
int k;
int index = 0;
/**
* Этот цикл отвечает за вывод всех 15 слотов, которые мы прописали.
*/
for (j = 0; j < 3; ++j)
{
for (k = 0; k < 5; ++k)
{
addSlotToContainer(new Slot(watcher.basic, index++, 44 + k * 18, 17 + j * 18));
}
}
/**
* Этот цикл отвечает за вывод инвентаря игрока.
*/
for (j = 0; j < 3; ++j)
{
for (k = 0; k < 9; ++k)
{
this.addSlotToContainer(new Slot(playerInv, k + j * 9 + 9, 8 + k * 18, 102 + j * 18 + i));
}
}
/**
* Этот цикл отвечает за вывод hud бара игрока.
*/
for (j = 0; j < 9; ++j)
{
this.addSlotToContainer(new Slot(playerInv, j, 8 + j * 18, 160 + i));
}
}
/**
* Можно ли взаимодействовать с контейнером.
*/
@Override
public boolean canInteractWith(EntityPlayer playerIn)
{
return true;
}
@Override
public ItemStack transferStackInSlot(EntityPlayer playerIn, int index)
{
ItemStack itemstack = ItemStack.EMPTY;
Slot slot = this.inventorySlots.get(index);
if (slot != null && slot.getHasStack())
{
ItemStack itemstack1 = slot.getStack();
itemstack = itemstack1.copy();
if (index < 15)
{
if (!this.mergeItemStack(itemstack1, 15, this.inventorySlots.size(), true))
{
return ItemStack.EMPTY;
}
}
else if(!this.mergeItemStack(itemstack1, 0, 15, false))
{
return ItemStack.EMPTY;
}
if (itemstack1.isEmpty())
{
slot.putStack(ItemStack.EMPTY);
}
else
{
slot.onSlotChanged();
}
}
return itemstack;
}
}
Теперь создадим класс EntityRegister:
Код:
public class EntityRegister
{
public static void register()
{
/**
* @param 1 - это класс нашего TileEntity
* @param 2 - это id нашего TileEntity
*/
GameRegistry.registerTileEntity(TileWatcher.class, "TileWatcher");
}
}
Далее добавим в ServerProxy в метод preInit такой код:
Код:
EntityRegister.register();
Теперь пора научится делать растения, которые будут расти.
Создадим класс BlockDogyCrop:
Теперь создадим зёрна:
И зарегистрируем их. Теперь добавим состояние блока для этого перейдём в assets/modexample/blockstates и добавим файл dogy_crop с таким содержимым:
В нашем случае используются модели пшеницы, но вы можете настроить свои модели.
Создадим класс BlockDogyCrop:
Код:
public class BlockDogyCrop extends BlockCrops
{
private Item crop, seed;
private static final AxisAlignedBB[] CROPS_AABB = new AxisAlignedBB[] {new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 0.125D, 1.0D), new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 0.25D, 1.0D), new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 0.375D, 1.0D), new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 0.5D, 1.0D), new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 0.625D, 1.0D), new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 0.75D, 1.0D), new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 0.875D, 1.0D), new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D)};
public BlockDogyCrop(String name, Item crop, Item seed)
{
this.setRegistryName(name);
this.setUnlocalizedName(name);
this.crop = crop;
this.seed = seed;
}
/**
* Данный метод позволит нам активировать блок, чтобы мы смогли не ломая блока взять с него урожай.
*/
@Override
public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
if(!worldIn.isRemote)
{
//Проверка на зрелость
if(this.isMaxAge(state))
{
//Выбрасываем предмет с блока.
EntityItem item = new EntityItem(worldIn, pos.getX(), pos.getY(), pos.getZ(), new ItemStack(getCrop()));
worldIn.spawnEntity(item);
//Устанавливаем для растения возраст 5
worldIn.setBlockState(pos, this.withAge(5));
return true;
}
}
return false;
}
/**
* Задаёт сетка столкновения. В нашем случае сетка будет увеличиваться в зависимости от роста.
*/
@Override
public AxisAlignedBB getBoundingBox(final IBlockState state, final IBlockAccess source, final BlockPos pos)
{
return CROPS_AABB[(state.getValue(this.getAgeProperty())).intValue()];
}
/**
* Задаёт блок на который можно поставить наше растение.
*/
@Override
protected boolean canSustainBush(final IBlockState state)
{
return state.getBlock() == Blocks.GRASS;
}
/**
* Задаёт зерна для нашего растения.
*/
@Override
protected Item getSeed()
{
return this.seed;
}
/**
* Задаёт урожай для нашего растения.
*/
@Override
protected Item getCrop()
{
return this.crop;
}
/**
* Задаёт возможность использовать костную муку на нашем растении.
*/
@Override
public boolean canUseBonemeal(final World worldIn, final Random rand, final BlockPos pos, final IBlockState state)
{
return true;
}
}
Теперь создадим зёрна:
Код:
public class ItemDogySeed extends Item implements IPlantable
{
public ItemDogySeed(String name)
{
this.setRegistryName(name);
this.setUnlocalizedName(name);
this.setCreativeTab(TabsHandler.TAB_BLOCKS);
}
/**
* Данный метод позволит нам установить наш блок урожая.
*/
@Override
public EnumActionResult onItemUse(EntityPlayer playerIn, World worldIn, BlockPos pos, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
final IBlockState state = worldIn.getBlockState(pos);
final ItemStack stack = playerIn.inventory.getItemStack();
if(facing == EnumFacing.UP && playerIn.canPlayerEdit(pos.offset(facing), facing, stack) && state.getBlock().canSustainPlant(state, worldIn, pos, EnumFacing.UP, this) && worldIn.isAirBlock(pos.up()))
{
worldIn.setBlockState(pos.up(), BlocksRegister.DOGY_CROP.getDefaultState());
stack.shrink(1);
return EnumActionResult.SUCCESS;
}
return EnumActionResult.FAIL;
}
/**
* Задаёт тип урожая.
*/
@Override
public EnumPlantType getPlantType(IBlockAccess world, BlockPos pos)
{
return EnumPlantType.Plains;
}
/**
* Задаёт состояние урожая.
*/
@Override
public IBlockState getPlant(IBlockAccess world, BlockPos pos)
{
return BlocksRegister.DOGY_CROP.getDefaultState();
}
}
И зарегистрируем их. Теперь добавим состояние блока для этого перейдём в assets/modexample/blockstates и добавим файл dogy_crop с таким содержимым:
Код:
{
"variants": {
"age=0": { "model": "wheat_stage0" },
"age=1": { "model": "wheat_stage1" },
"age=2": { "model": "wheat_stage2" },
"age=3": { "model": "wheat_stage3" },
"age=4": { "model": "wheat_stage4" },
"age=5": { "model": "wheat_stage5" },
"age=6": { "model": "wheat_stage6" },
"age=7": { "model": "wheat_stage7" }
}
}
Доска в Trello, там вы можете выбрать(проголосовать) за статью, что ускорит её выход в учебник.
Последнее редактирование: