이 문서는 http://www.minecraftforge.net/wiki/Potion_Tutorial의 문서를 번역한 것입니다. 중간에 번역이 잘못된 곳이 있을 수 있으나 강의를 전달함에는 문제가 없게 번역하고 있습니다.

 

 

제목: 포션 강의

이 문서는 Spartan322가 Lolcroc의 강의를 각색한 것입니다. 난이도는 중급입니다.

 

1. 목표

새로운 포션을 만든다.

포션을 조작한다.

새로운 포션을 사용한다.

// 코멘트: 여기서 포션이란 마시는 아이템이 아닌 플레이어나 동물에게 걸리는 효과를 의미합니다.

 

2. 사전 요구사항

포지 설치

기초 모드 제작 환경 설정

 

3. 포션의 기초

포션은 무기나 갑옷에 추가적인 기능을 부여하거나 새로운 효과, 외관을 만들어내며 심지어는 특수한 방식으로 몬스터를 소환할 때도 사용됩니다. 이 강의에서는 사용시 몬스터를 사용자 근처에 소환하는 포션을 만들어 볼 것입니다. (그런 의미에서 이 강의는 엔티티를 소환하고 다루는 유용한 방법 또한 소개해준다고 할 수 있습니다) 저는 이번 강의에서 보고 배우기 기법으로 가르쳐드릴 것이므로 하나하나 해야 할 것을 가르쳐주리라 기대하지 마세요. 전 어디가 어떻게 작동하는 지만 설명할겁니다.

자 시작해봅시다.

 

4. 포션 추가 도입부

포션 설정

자, 가장 기본적이면서 아무 효과도 없는 포션을 만들어봅시다. 조금 어려운 부분이므로 먼저 보여주고 설명하겠습니다.

일단은 당신의 모드 파일이 다음과 같이 설정되어 있다고 생각하겠습니다.

 

package mods.tutorial.common;

import java.util.Objects;

import mods.zmass.client.ClientProxyMass;
import net.minecraft.block.Block;
import net.minecraft.block.StepSound;
import net.minecraft.block.material.Material;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.EnumToolMaterial;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.EnumAction
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraftforge.common.EnumHelper;
import net.minecraftforge.common.MinecraftForge;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandle
import cpw.mods.fml.common.Mod.Init;
import cpw.mods.fml.common.Mod.PreInit;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.network.NetworkMod;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;
import net.minecraftforge.common.Configuration;
import cpw.mods.fml.common.network.NetworkMod;
import cpw.mods.fml.common.registry.EntityRegistry;



@Mod(modid = "TutorialMod", name = "Tutorial Mod", version = "Version Tutorial Version")

@NetworkMod(clientSideRequired = true, serverSideRequired = false)
public class TutorialMod
{
    @SidedProxy(clientSide = "mods.Tutorial.client.ClientProxy",
                serverSide = "mods.tutorial.common.CommonProxy")

    @EventHandler
    public void preInit(FMLPreInitializationEvent event)
    {
        //PreInit tasks
    }
        
    @EventHandler
    public void load(FMLInitializationEvent event)
    {
        //Load tasks
    }
}

 

위가 이해가 되지 않는다면 더 기초적인 강의를 보고 오셔야 합니다.

 

이제는 특별한 포션 객체를 만들어야 할 시간입니다. 다음의 내용을 새로운 파일에 작성해도 좋고, 기본 모드 파일에 작성해도 좋으니 알아서 하세요.

 

public static Potion PotionModName;


@EventHandler
public void preInit(FMLPreInitializationEvent event) {
    Potion[] potionTypes = null;

    for (Field f : Potion.class.getDeclaredFields()) {
        f.setAccessible(true);
        try {
            if (f.getName().equals("potionTypes") || f.getName().equals("field_76425_a")) {
                Field modfield = Field.class.getDeclaredField("modifiers");
                modfield.setAccessible(true);
                modfield.setInt(f, f.getModifiers() & ~Modifier.FINAL);

                potionTypes = (Potion[])f.get(null);
                final Potion[] newPotionTypes = new Potion[256];
                System.arraycopy(potionTypes, 0, newPotionTypes, 0, potionTypes.length);
                f.set(null, newPotionTypes);
            }
        } catch (Exception e) {
            System.err.println("Severe error, please report this to the mod author:");
            System.err.println(e);
        }
    }

    MinecraftForge.EVENT_BUS.register(new ModNameEventHooks());
}

 

위의 내용을 쉽게 설명하자면 포지가 새로운 포션을 추가하는 기능을 제공하기 이전까진 위의 코드가 여러분이 새 포션을 추가할 수 있게 해 줄 것이란 거고 어렵게 말하자면 새로운 포션 배열을 만들어서 기존의 마인크래프트 메소드를 통해서 전달할 필요 없이 포션을 만들 수 있게 해 준다는 겁니다. 그 이외에도 이름을 바꾸고 효과를 부여하고 하는 등 많은 것들을 할 수 있습니다. http://www.minecraftforum.net/forums/archive/tutorials/931752-forge-creating-custom-potion-effects에서 코드를 참조했습니다.

 

자 이제 포션을 추가해봅시다.

 

포션을 추가함에 있어 먼저 해야 할 것은 우리가 아는 거랑 비슷한지 확인하기 위해 Potion.java를 한번 보는 겁니다.

.

package net.minecraft.potion;

import com.google.common.collect.Maps;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import java.util.Map.Entry;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.attributes.Attribute;
import net.minecraft.entity.ai.attributes.AttributeInstance;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.ai.attributes.BaseAttributeMap;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.DamageSource;
import net.minecraft.util.StringUtils;

public class Potion
{
    /** The array of potion types. */
    public static final Potion[] potionTypes = new Potion[32];
    public static final Potion field_76423_b = null;
    public static final Potion moveSpeed = (new Potion(1false8171462)).setPotionName("potion.moveSpeed").setIconIndex(00).func_111184_a(SharedMonsterAttributes.field_111263_d"91AEAA56-376B-4498-935B-2F7F68070635", 0.20000000298023224D, 2);
    public static final Potion moveSlowdown = (new Potion(2true5926017)).setPotionName("potion.moveSlowdown").setIconIndex(10).func_111184_a(SharedMonsterAttributes.field_111263_d"7107DE5E-7CE8-4030-940E-514C1F160890"-0.15000000596046448D, 2);
    public static final Potion digSpeed = (new Potion(3false14270531)).setPotionName("potion.digSpeed").setIconIndex(20).setEffectiveness(1.5D);
    public static final Potion digSlowdown = (new Potion(4true4866583)).setPotionName("potion.digSlowDown").setIconIndex(30);
    public static final Potion damageBoost = (new PotionAttackDamage(5false9643043)).setPotionName("potion.damageBoost").setIconIndex(40).func_111184_a(SharedMonsterAttributes.field_111264_e"648D7064-6A60-4F59-8ABE-C2C23A6DD7A9", 3.0D, 2);
    public static final Potion heal = (new PotionHealth(6false16262179)).setPotionName("potion.heal");
    public static final Potion harm = (new PotionHealth(7true4393481)).setPotionName("potion.harm");
    public static final Potion jump = (new Potion(8false7889559)).setPotionName("potion.jump").setIconIndex(21);
    public static final Potion confusion = (new Potion(9true5578058)).setPotionName("potion.confusion").setIconIndex(31).setEffectiveness(0.25D);

    /** The regeneration Potion object. */
    public static final Potion regeneration = (new Potion(10false13458603)).setPotionName("potion.regeneration").setIconIndex(70).setEffectiveness(0.25D);
    public static final Potion resistance = (new Potion(11false10044730)).setPotionName("potion.resistance").setIconIndex(61);

    /** The fire resistance Potion object. */
    public static final Potion fireResistance = (new Potion(12false14981690)).setPotionName("potion.fireResistance").setIconIndex(71);

    /** The water breathing Potion object. */
    public static final Potion waterBreathing = (new Potion(13false3035801)).setPotionName("potion.waterBreathing").setIconIndex(02);

    /** The invisibility Potion object. */
    public static final Potion invisibility = (new Potion(14false8356754)).setPotionName("potion.invisibility").setIconIndex(01);

    /** The blindness Potion object. */
    public static final Potion blindness = (new Potion(15true2039587)).setPotionName("potion.blindness").setIconIndex(51).setEffectiveness(0.25D);

    /** The night vision Potion object. */
    public static final Potion nightVision = (new Potion(16false2039713)).setPotionName("potion.nightVision").setIconIndex(41);

    /** The hunger Potion object. */
    public static final Potion hunger = (new Potion(17true5797459)).setPotionName("potion.hunger").setIconIndex(11);
    
    /** Rest of class omitted */
}

 

여기가 코드의 윗부분인데, 블록이나 아이템 등록 과정과 상당히 유사하단 걸 알 수 있습니다. 저 위에서 하나를 복사해서 우리에게 맞게 바꿀 수 있겠네요.

 

 /** The hunger Potion object. */
    tutorialPotion = (new PotionModName(40false0)).setPotionName("potion.potionName").setIconIndex(00);

 

일단 이걸 복사하면 몇 가지 오류가 생길 겁니다. 당장은 오류를 무시하도록 하고.

포션 클래스 생성자의 첫 번째 인자는 포션 ID입니다. 포션 ID는 프로그램이 포션을 구분하는 이름표와 같은 겁니다. 그렇기 때문에 절대 겹쳐서는 안됩니다. 포지가 알아서 겹친 ID를 재 설정 해 주지는 않기 때문이죠. 두 번째 인자는 포션이 완전한 효과를 가지는지 아닌지에 대한 설명입니다ㅣ. 'True'라면 효과가 절반만 적용 됩니다. 마지막 인자는 포션 병의 색을 나타냅니다. 지금 당장은 신경 쓰지 않겠습니다.(사전식, html 색 지정을 잘 아는 사람이라면 쉬운 내용입니다)

포션의 이름(Potion Name)은 코드가 포션을 알아내는 이름표입니다. 사실 여러분의 포션은 모드 파일로부터 만들어지기 때문에 딱히 이름이 쓸모는 없으나 그냥 붙입니다. 아이콘 주소(Icon Index)는 포션이 적용되었을 때 인벤토리에 보여지는 아이콘을 설정하는 기능입니다. 마인크래프트의 포션 아이콘은 하나의 거대한 텍스쳐 파일로 저장되는데, 거기서 어떤 좌표에 원하는 아이콘이 있는 지를 설정하는 겁니다. (x, y)인 형식인데, 각각 0~7의 값이 들어갑니다.(원본 설명이 부족하여 역자인 제가 덧붙입니다)

 

이제는 굉장히 간단한 부분입니다. Potion 클래스를 상속하는 클래스를 만들 건데, 사실 내용물은 하나도 없습니다.

 

package mod.tutorial.common;

import net.minecraft.potion.Potion;

public class PotionModName extends Potion {

  public PotionModName(int par1, boolean par2, int par3) 
  {
     super(par1, par2, par3);
  }

   public Potion setIconIndex(int par1, int par2) 
   {
     super.setIconIndex(par1, par2);
     return this;
   }
}

 

이게 여러분이 처음 만들어야 할 기본 Potion 클래스입니다. 이 클래스가 다른 포션 효과를 만들 때도 계속 기본 틀로 사용될 것이므로 이름을 PotionMod.java 처럼 범용적으로 짓는 것이 좋습니다. 이건 정말 기본에 불과하지만 여러분이 훨씬 멋있는 효과를 넣고자 한다면 결국엔 여러 내용이 추가 되야 만 할 것입니다.

 

5. 효과 부여하기

자 이제 제일 재미있는 부분입니다. 이제 사람, 엔티티, 심지어는 월드에 적용될 엄청난 효과들을 만들어낼 차례입니다. 그러기 위해서 이벤트 후킹을 할 파일을 하나 만들어야 합니다. 일단 코드를 보고 이야기 합시다.

 

package mods.tutorial.common;

public class ModNameEventHooks {



@ForgeSubscribe
public void onEntityUpdate(LivingUpdateEvent event) 
{
     //entityLiving in fact refers to EntityLivingBase so to understand everything about this part go to EntityLivingBase instead
     if (event.entityLiving.isPotionActive(ModName.potionName)) 
     {
         if (event.entityLiving.worldObj.rand.nextInt(20) == 0) 
         {
                  
         }
     }
}

 

onEntityUpdate라는 메소드는 이벤트 후킹에 의해서 살아 움직이는 모든 EntityLiving에 대해서 호출됩니다. 이제 우리는 그 중 우리가 만든 포션 효과가 적용된 엔티티에 대해서만 살펴본다는 의미입니다. 이 메소드는 매 틱 실행되기 때문에 중간에 nextInt를 이용해서 1/20의 확률로 발동되게 했습니다.

자 다음의 코드는 Zombie.java에 있는 NPC를 좀비로 바꾸는 코드입니다.

 

EntityZombie entityzombie = new EntityZombie(event.entityLiving.worldObj);
entityzombie.copyLocationAndAnglesFrom(event.entityLiving);
entityzombie.func_110161_a((EntityLivingData)null);
entityzombie.func_82187_q();

event.entityLiving.worldObj.removeEntity(event.entityLiving);
event.entityLiving.worldObj.spawnEntityInWorld(entityzombie);

 

자 이제 우리가 할 것은 이 포션에 중독된 엔티티를 서서히 죽이고 좀비를 소한하는 것입니다.

일단 좀비가 소환되는 내용을 적어보면 다음과 같이 될 겁니다.

 

package mods.tutorial.common;

public class ModNameEventHooks {



@ForgeSubscribe
public void onEntityUpdate(LivingUpdateEvent event) 
{
     //entityLiving in fact refers to EntityLivingBase so to understand everything about this part go to EntityLivingBase instead
     if (event.entityLiving.isPotionActive(ModName.potionName)) 
     {
         if (event.entityLiving.worldObj.rand.nextInt(20) == 0) 
         {
                  
                    EntityZombie entityzombie = new EntityZombie(event.entityLiving.worldObj);
                    entityzombie.copyLocationAndAnglesFrom(event.entityLiving);
                    entityzombie.func_110161_a((EntityLivingData)null);
                    entityzombie.func_82187_q();

            

                    event.entityLiving.worldObj.removeEntity(event.entityLiving);
                    event.entityLiving.worldObj.spawnEntityInWorld(entityzombie);

         }
     }
}

 

자 다음에 추가된 것은 포션이 데미지를 가하고, 죽음을 인식하는 내용입니다. 이 정도면 좀비 감염 바이러스가 충분히 구현된 것 같습니다.

 

package mods.tutorial.common;

public class ModNameEventHooks {



@ForgeSubscribe
public void onEntityUpdate(LivingUpdateEvent event) 
{
     //entityLiving in fact refers to EntityLivingBase so to understand everything about this part go to EntityLivingBase instead
     if (event.entityLiving.isPotionActive(ModName.potionName)) 
     {
         if (event.entityLiving.worldObj.rand.nextInt(20) == 0) 
         {

            event.entityLiving.attackEntityFrom(DamageSource.generic2);

            if( event.entityLiving.isDead() == true )
                  
                    EntityZombie entityzombie = new EntityZombie(event.entityLiving.worldObj);
                    entityzombie.copyLocationAndAnglesFrom(event.entityLiving);
                    entityzombie.func_110161_a((EntityLivingData)null);
                    entityzombie.func_82187_q();

            

                    event.entityLiving.worldObj.removeEntity(event.entityLiving);
                    event.entityLiving.worldObj.spawnEntityInWorld(entityzombie);

         }
     }
}

 

자 마지막으로 알아볼 내용은 특정한 엔티티에게 포션 효과를 적용시키는 것입니다.

 

entityliving.addPotionEffect(new PotionEffect(ModName.potionName.id2001false));

 

첫 번째 인자는 포션의 ID, 두 번째 인자는 지속 시간, 세 번째 인자는 효과 레벨입니다. 1이 기본이고 두 개 이상의 레벨을 가질 수는 없습니다. 마지막은 신호기에서 발생된 효과인지를 나타내는 인자인데 false로 하고 싶으면 그냥 안 적어도 됩니다.

지속시간을 적용시킬 거라면 다음의 코드가 이벤트 후크에 적으세요.

 

if (event.entityLiving.getActivePotionEffect(Yourmod.customPotion).getDuration()==0) 
{
            event.entityLiving.removePotionEffect(Yourmod.customPotion.id);
            return;
}

 

+ Recent posts