7. diel - Minecraft Modding - ItemFood, Potion s EventHook
V tomto diele si vytvoríme šalát pomocou triedy ItemFood a pridáme mu vlastný PotionEffect.
ItemSalat
Najprv si vytvoríme nový itemSalat, ktorý bude dediť z ItemFood. Tu je konštruktor s parametrami:
public ItemFood(int foodLevel, float sytost/saturace, boolean isWolfsFavoriteMeal)
- IsWolfsFavoriteMeal je true v prípade jedla, ktorým môžeme nakŕmiť vlka. (Vo vanille je to napr: raw a cooked porkchop)
- FoodLevel udáva počet "pol-stehienok", ktorá sa majú pridať po zjedení tohto item. (Hráč má maximum 20)
- Saturácia (sýtosť) narozdiel od foodlevelu nie je znázornená vizuálne. Keď saturácia dosiahne nulu, hráči začnú ubúdať stehienka. Maximum možné saturácia sa rovná počtu stehienok: napr. Pokiaľ má hráč plný foodBar (všetkých 20 pol-stehienok) bude maximum saturácie 20F.
Ďalším faktorom týkajúcim sa jedla je napríklad vyčerpanie (exhaustion). Tento faktor ovplyvňuje ako rýchlo hráči bude ubúdať saturácie a foodLevel. Na MinecraftWiki je prehľadná tabuľka všetkých činností, ktoré zväčšujú vyčerpania. (Napr. Šprint)
V konstruktoru ItemFood môžeme nastaviť tiež:
this.setAlwaysEdible();
Ak nastavené, hráč bude môcť jedlo zjesť aj v prípade, ak by mal foodLevel všetkých 20/20 (plne na jeden). Normálne nastavené na false.
Po troche teórie si skúsime konštruktor:
public ItemSalat(){ super(5,8F,false)//foodLevel, saturace, isWolfsFavoriteMeal this.setUnlocalizedName("salat"); this.setTextureName("salat"); }
Odporúčam si písať komentáre, aby bolo presne vidieť, čo je čo.
Metódy určené na Override
public int getMaxItemUseDuration(ItemStack itemstack) { return 32; }
Určuje v tickách, ako dlho trvá zjedenie.
public EnumAction getItemUseAction(ItemStack itemstack) { return EnumAction.eat; }
Určuje animáciu, ktorá sa má vykonať pri udalosti itemInUse. (Keď používam item) Ďalej je možné použiť aj EnumAction.drink.
Udalosť jedenie v ItemFood
Metóda OnItemRightClick () zistí, či sa hráč môže najesť a ak áno nastaví player.setItemInUse (getMaxItemUseDuration ()). Teraz sa prehráva animácie zvolená v metóde getItemUseAction (). Keď hráč dojedol, zavolá sa metóda onEaten () (ktorá mimochodom prebíja metódu z Item). Tá sa postará o zmenšenie počtu itemstacku o jedna, prehrá zvuk, nakŕmi hráča pomocou rovnakej metódy, ktorú sme použili vo 4. diele Soľná Hůlka a crafting a nakoniec zavolá metódu onFoodEaten (), ktorá sa postará o pridanie nejakého potion efektu, ak je nastavený .
Forge 's event hooks
V preklade niečo ako udalostnej háčiky, je možnosť reagovať na eventy pomocou iných metód, než ktoré sú normálne volány pri objektoch, čo môže byť problém vo chvíli, keď chceme pracovať s nejakými vannila objektmi, pretože ich triedy a metódy upravovať nemôžeme. Tieto háčiky nám umožní "zaháknuÈ sa" k nejakému eventu, reagovať na neho alebo ho skonzumovať metódou setCanceled (boolean). Existuje mnoho eventov, na ktoré môžeme reagovať. Pre príklad uvediem LivingJumpEvent, ktorý sa zavolá keď entita skáče, EntityConstructing sa zavolá pri vytvorení entity a LivingUpdateEvent sa zavolá pri každom updatu entity. (Zvedavci nech hodí oko na LivingUpdateEvent a triedy z ktorej dedia atď.)
Vytvorenie custom EventHooku
Pre obsluhovanie eventov si vytvoríme v novom balíčku nazvanom event_hooks novú triedu, ktorá (čo znie možno čudne) nebude dediť z žadné inej triedy, o všetko sa postará Forge. Túto novú triedu si nazvime ChokingPotionEventHook, ktorá bude zabezpečovať funkciu zatiaľ neexistujúceho dávícího potion efektu, ktorý hráč dostane vo chvíli, keď zje šalát s pravdepodobnosťou 1/20. Zatiaľ si pripravíme metódu, ktorá bude obsluhovať LivingUpdateEvent.
- Dôležité je, aby sa volala public void onEntityUpdate (LivingUpdateEvent event).
- Musí mať anotáciu @SubscribeEvent
Tieto pravidlá sú dôležité z toho dôvodu, že Forge si túto triedu prečíta, zistí či je v nej metóda s anotáciou @SubscribeEvent a ak áno, zistí či sa volá správne a či má správny počet a typ parametrov, respektíve či má hlavičku, akú by mala mať.
public class ChokingPotionEventHook{ @SubscribeEvent public void onEntityUpdate(LivingUpdateEvent event){} }
Týmto sme si vytvorili nový háčik chytajúci udalosť, ktorý s ňou zatiaľ nič nerobí. Teraz sa presuňme do Main triedy do metódy init (FMLInitializationEvent).
Inštanciu ChokingPotionEventHook musíme registrovať, aby Forge vedel, že má spravovať nejaký eventHook. K tomu nám slúžia v triede MinecraftForge inštancie triedy EventBus nazvaná EVENT_BUS, na ktoré zavoláme metódu register (Object) a vložíme do nej inštanciu našej "háčikové" triedy.
MinecraftForge.EVENT_BUS.register(new ChokingPotionEventHook());
Teraz keď spustíme Minecraft a otvoríme nejaký svet, metóda onEntityUpdate () v našej "háčikové" triede sa bude volať pri každom ticku / updatu každej livingEntity. (To znamená, že sa volá u každej entity zvlášť.)
PotionEffects
Minecraft má mnoho spôsobu, ako vytvoriť nejaké Potion alebo efekty. Ja som pre tento diel naplánoval jednoduchšiu cestu, a tak si vytvoríme len potionEffect a nie potionItem (fľaštička s elixírom). Potion sú uchovávané v triede Potion v public poli nazvanom potionTypes alebo field_76425_a (záleží na verziu Forge). Problém však je, že toto pole je finálna a taky že je to pole -> má finálny dĺžku. Toto pole má dĺžku 32, znamená to teda že iba 32 Potion môže v Minecraftu existovať? Našťastie je tu reflexion.
Reflection
Reflection popisuje kód, ktorý je schopný skontrolovať / upravovať iný kód. V skratke sme schopní kód meniť za behu programu, ako napríklad JavaScript a práca s elementmi. Môžeme napríklad zistiť aké všetky metódy (i private) trieda má alebo upraviť veľkosť poľa čo je to, čo hľadáme. Metódu nižšie nebudem opisovať, pretože odbočujeme z Minecraft Modding a dostávame sa do úplne inej kapitoly. Stačí vedieť, že táto metóda prepíše polia v Potion poľom novým a väčším a prekopíruje do tohto nového poľa zo starého poľa všetky Potion, takže o nič neprídeme a zväčšíme si pole na koľko chceme.
private void incrementPotionFieldLength(int newLength){ 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[newLength]; System.arraycopy(potionTypes, 0, newPotionTypes, 0, potionTypes.length); f.set(null, newPotionTypes); } } catch (Exception e) { System.err.println("Error, cant increment length of potions field. Report this to a mod author."); System.err.println(e); } } }
Túto metódu si vytvoríme v našej Main triede a budeme ju volať z preInit () ako prvý z celej metódy. Ako novú veľkosť zvolím 50.
Potion druhýkrát
V novom balíčku nazvanom potions vytvoríme triedu ChokingPotion, ktorá bude dediť z Potion.
public class ChokingPotion extends Potion { public ChokingPotion(int id) { super(id, false, 999999);//id, isBadEffect, int color this.setPotionName("potion.choking"); } }
Ako vidíte, super konštruktor má v parametroch id (index v poli Potion.potionTypes), či je potion zlý, a farbu particles. Ďalej nastavujeme potionName, ktorý možno samozrejme neskôr upraviť v lang súbore ako všetko ostatné:
potion.choking=Choking/Dávení
Týmto sme dokončili ChokingPotion. Ešte by sme ale chceli k nášmu Potion pristupovať, ako je tomu v Potion (napr. Potion.confusion). Do rovnakého balíčka, ako je ChokingPotion si vytvoríme triedu pomenovanú SaltModPotions. Táto trieda bude slúžiť iba ako úschovňa všetkých našich Potion.
public static final Potion chokingPotion = new ChokingPotion(33);//id ==index v poli
Týmto máme celý Potion za sebou (okrem funkcionality) a môžeme ho aplikovať na hráča. Ako som už spomenul pred chvíľou, hráč sa môže zakuckat / dáviť sa, keď zje ItemSalat. Do konstruktoru ItemSalat teda pridáme riadok:
this.setPotionEffect(SaltModPotions.chokingPotion.getId(), 8, 1, 0.2F);// potion id, délka (v sekundách), level, pravděpodobnost
Tu snáď stojí za zmienku iba posledný parameter, ktorý určuje pravdepodobnosť, že efekt nastane. Parameter môže byť od 0F do 1F, kde platí, že 0 == nulová pravdepodobnosť a 1 == stopercentnú pravdepodobnosť. Ja zvolil 20% pravdepodobnosť, že efekt nastane.
Záverečná funkcionalita
Teraz ak zjeme šalát, možno dostaneme efekt. Budú okolo nás víriť particles, ale to je tak všetko. Pre funkcionalitu sme už skôr vyhradili ChokingPotionEventHook. Presuňme sa teda do tejto triedy do jedinej metódy.
@SubscribeEvent public void onEntityUpdate(LivingUpdateEvent event) { }
Možno si hovoríte, kde máme tú entitu. Ona je totiž trýzniť schovaná v evente v atribúte entityLiving. Teraz by sme chceli zistiť, či má táto entita náš ChokingPotionEffect.
if(event.entityLiving.getActivePotionEffect(SaltModPotions.chokingPotion)!=null){ if (event.entityLiving.getActivePotionEffect(SaltModPotions.chokingPotion).getDuration()==0) {//někdy to funguje i po vyprchání efektu event.entityLiving.removePotionEffect(SaltModPotions.chokingPotion.id); return; } //Výše jsme zjistili, zdali má entita aktivní náš potionEffect, ještě se musí otestovat, zdali potionEffect ještě trvá, pokud ne, tento efekt odstranit. event.entityLiving.attackEntityFrom(DamageSource.drown, 1F);// Zranit entitu o půl srdíčka, a typ zranění topení Random rand = event.entityLiving.worldObj.rand; for(int i =0;i<40;i++){//vytvoření malé fontánky se splash particles na hlavě entity boolean dir1 = rand.nextBoolean(); boolean dir2 = rand.nextBoolean(); double velZ=rand.nextInt(10)+1; double velX=rand.nextInt(10)+1; velZ=dir1?velZ:-velZ; velX=dir2?velX:-velX; event.entityLiving.worldObj.spawnParticle("splash", event.entityLiving.posX, event.entityLiving.posY, event.entityLiving.posZ,velX,rand.nextDouble()*3,velZ); } }
Záver
Triedy ItemSalat, ChokingPotionEventHook, Main, ChokingPotion, SaltModPotions sú ku stiahnutiu pod článkom + obrázok šalátu.
Teraz by všetko malo fungovať: pokiaľ hráč sníva šalát, pribudne mu 5 pol-stehienok, možno dostane ChokingPotionEffect, začne sa dusiť, ubúdajú mu srdiečka, a strieka mu z hlavy voda. Ehmm to nedáva moc zmysel čo? : D
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkamiStiahnuté 640x (3.23 kB)