6. diel - Minecraft Modding - Teleport 2. časť
Pokračujeme v triede Ovládače Na začiatok si prekryjeme metódu onItemUse (), ktorá je volaná pri kliknutí pravým tlačidlom na nejaký block.
public boolean onItemUse(ItemStack itemstack, EntityPlayer player, World world, int x, int y, int z, int side, float fx, float fy, float fz)
Najprv si zistíme, či klikáme na BlockSaltPort:
if (world.getBlock(x, y, z) instanceof BlockSaltPort){ if(!itemstack.hasTagCompound()) itemstack.stackTagCompound = new NBTTagCompound();
Ďalej musíme zistiť, či má náš ovládač NBT, pretože síce každý ItemStack má atribút NBT, ale iba ten kto ho potrebuje, si do neho uloží inštancie NBT. Musíme teda do atribútu pridať novú inštanciu NBT, inak by sme pracovali s null. Teraz si musíme načítať všetky atribúty nášho stackTagCompound.
int xq = itemstack.stackTagCompound.getInteger("x"); int yq = itemstack.stackTagCompound.getInteger("y"); int zq = itemstack.stackTagCompound.getInteger("z"); boolean MamData = itemstack.stackTagCompound.getBoolean("MamData");
Síce máte pravdu, že čítame niečo, čo ešte nebolo vôbec vložené, ale NBT nám vráti 0,0,0 a false as tým sa dá pracovať. Tu je dôležitý tag MamData, ktorý vracia false v pripade kliknutí na prvý block a true na druhý. Ukladanie 1. blocku do NBT bude vyzerať takto:
if(!MamData){ MamData=true; this.writeToNBT(x,y, z, MamData, itemstack);//Save world.playSoundAtEntity(player, "minecraft:random.click", 1F, 1F);//Sounds return true; }
Najprv sa opýtame, či klikáme na 1. block. Ak áno, nastavíme MamData na true (aby sa nám pri kľučku na 2. block už tento blok kodu nespustil). Potom použijeme metódu deklarovanú v minulom diele writeToNBT () a uložíme súradnice 1. blocku. Nakoniec prehráme zvuk kliknutia, aby hráč vedel, že ten item niečo naozaj robí. Vraciame true, pretože náš item niečo robí. Teraz príde tá ťažšia časť, a to vytvorenie nového spojenia. Pre prehľadnosť vytvorenie NBT uzlov poslúži tento graf.
Vo WorldSavedData respektíve vo MyWorldData je atribút NBTTagCompound. Do neho pridáme tag typu NBTTagCompound a menom "Teleport Management". Do neho budeme pridávať ďalšie NBTTagCompound tagy s menom ID, čo je unikátne meno každého spojenia, cez ktoré sa k tomuto spojeniu bude pristupovať. A toto spojenie bude mať 3 ďalšie tagy: int [], int [], int.
if(MamData){ int[] l1={xq,yq,zq}; //souřadnice 1. blocku int[] l2 = {x,y,z}; //souřadnice 2. blocku if(l1!=l2){ //pokud neklikám na ten samý block podruhé NBTTagCompound worldnbt = MyWorldData.forWorld(world).tag;//získám MyWorldData pro tento svět a jeho NBT tag worldnbt.setTag(TELEMANAGE, worldnbt.getCompoundTag(TELEMANAGE));
Možno sa vám zdá predchádzajúci riadok úplne zbytočný. Avšak toto riešenie má svoju logiku: Ak chceme získať NBT tag z NBT tagu, ktorý neexistuje, vráti nám new NBTTagCompound (). Ak existuje, jednoducho tam nahráme už existujúce tag, ak nie, uložíme tam novú inštanciu NBT.
NBTTagCompound tm = worldnbt.getCompoundTag(TELEMANAGE); NBTTagCompound spojeni= new NBTTagCompound();//nový tag spojení spojeni.setIntArray(L1, l1);//uložení obou lokací blocků pomocí finálních Stringů L1, L2 spojeni.setIntArray(L2, l2); tm.setInteger(PORADI, tm.getInteger(PORADI)+1);//nový tag do telemanage nebo již existující zvětšený o jedna. Užit ke zjištění jaké ID bude mít další spojení tm.setTag("Spojeni"+tm.getInteger(PORADI), spojeni);//uložíme do telemanage naše spojení s ID=klíčem: "Spojeni"+poradi MyWorldData.forWorld(world).markDirty();
Metóda markDirty () znamená v preklade zašpiniť. Ak označíme objekt metódou markDirty (), Minecraft v najbližšej dobe zavolá metódu writeToNBT (), čím zaistí uloženie.
BlockSaltPort b=(BlockSaltPort) world.getBlock(x, y, z);//získáme nějaký blockSaltPort b.setTransportID(world,x,y,z,tm.getInteger(PORADI)); //nastavení metadat 2. blocku b.setTransportID(world,xq,yq,zq,tm.getInteger(PORADI)); //nastavení metadat 1. blocku this.writeToNBT(0, 0, 0, false, itemstack);//nakonec vynulujeme náš ovladač. world.playSoundAtEntity(player, "minecraft:random.click", 1F, 1F); } }
SaltPortBlock
Pre reakciu na kliknutie pravým tlačidlom na block slúži metóda:
public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float fx, float fy, float fz)
Najprv zistíme, či je hráč na blocku:
if(player.onGround && (int)player.posX == x|| (int)player.posX == x+1||(int)player.posX == x-1 && player.posY==y+1 && (int)player.posZ==z||(int)player.posZ==z+1||(int)player.posZ==z-1) { port(world, player,x,y,z,world.getBlockMetadata(x, y, z)); return true; } return false;
Tu použijeme zatiaľ neexistujúce metódu port, ktorá má za parametre World, Player, súradnice tohto blocku a metadáta tohto blocku, ktoré budú použité pre zistenie ID spojenia.
public void port(World world, EntityPlayer player,int x,int y,int z, int meta) { try{//vyhodí výjimku, pokud NBT bude new NBT() bez atributu ke kterému přistupujeme NBTTagCompound teleportation = MyWorldData.forWorld(world).tag.getCompoundTag(ItemPortRemote.TELEMANAGE).getCompoundTag("Spojeni"+meta);//získání spojení pomocí metadat int[] location = {x,y,z};//vytvoření pole ze souřadnic blocku
Pretože v spojení sú uložené 2 súradnice bločky a my nevieme, aké súradnice použiť ako lokáciu k teleportu, zistíme, či prvý súradnice zo spojenia sú zhodné so súradnice tohto blocku. Ak sú, použijeme k teleportu tieto 2. súradnice, inak použijeme prvé.
if(Arrays.equals(location, teleportation.getIntArray(ItemPortRemote.L1))) location = teleportation.getIntArray(ItemPortRemote.L2); else location = teleportation.getIntArray(ItemPortRemote.L1); EntityPlayerMP player1 = (EntityPlayerMP)player;//Specialní instance hráče player1.playerNetServerHandler.setPlayerLocation(location[0] +0.5D, location[1]+1, location[2] +0.5D, player1.rotationYaw, player1.rotationPitch);//vlastní port world.playSoundAtEntity(player, "minecraft:mob.endermen.portal", 0.3F, 0.3F); } catch(Exception e){ //Neexistuje telespojeni s this.id this.removeTeleInfo(world,x,y,z); } }
Asi ste si všimli metódy removeTeleInfo (). Ešte som nespomenul jeden problém, a to vymazanie spojenie. To bude žiaduce v dvoch prípadoch:
- pri rozbití blocku = aby hráč nebol portovaný tam, kde tele-block nie je.
- pri zmene metadát = keď pomocou ovládača prepojíme už spojený block s iným, 3. block by portoval na 1. block, ale 1. by portoval na 2. a 2. port na 1.
Pri týchto eventoch bude nutné vymazať spojenie, ak tomu bude slúžiť táto metóda:
public void removeTeleInfo(World w,int x,int y,int z){ int removedmeta=w.getBlockMetadata(x, y, z); if(w.getBlockMetadata(x, y, z)!=0){//pokud má tento block nějaké spojení MyWorldData.forWorld(w).tag.getCompoundTag(ItemPortRemote.TELEMANAGE).setTag("Spojeni"+removedmeta, null);//odstranění tagu spojení w.setBlockMetadataWithNotify(x, y, z, 0, 2);//nastavení metadat na 0 = block nemá spojení MyWorldData.forWorld(w).markDirty();//uložíme NBT } }
Túto metódu budeme volať z ďalších týchto metód:
public void onBlockDestroyedByExplosion(World w, int x, int y, int z, Explosion ex){ removeTeleInfo(w,x,y,z); } public boolean removedByPlayer(World w, EntityPlayer player, int x, int y, int z) { removeTeleInfo(w,x,y,z); return w.setBlockToAir(x, y, z);//postaráme se o správnou fce této metody (koukněte na removedByPlayer() ve třídě Block) }
A nakoniec posledná metóda, ktorú sme volali pri tvorení spojenie z ovládača:
public void setTransportID(World w,int x,int y,int z,int meta){ this.removeTeleInfo(w,x,y,z); w.setBlockMetadataWithNotify(x, y, z, meta, 2); }
Táto metóda uloží na súradnice blocku metadáta, ale ešte predtým zavolá metódu removeTeleInfo (), ktorá sa postará o zrušení NBT spojenie (ak nejaké existuje).
Finále
Týmto sme dokončili všetky tri triedy:
- MyWorldData, patrí Worldu a uchováva NBT tag pre náš teleportačný systém.
- ItemPortRemote ovládač, ktorý slúži na vytváranie spojenia medzi dvoma blocky a jeho uloženie do MyWorldData.
- BlockSaltPort, načíta svoje spojenie z teleportačného systému pomocou uloženého meta a portom hráča na 2. block v spojení. Vymaže svoje spojenie z Tele-systému, ak je odstránený alebo ak mu je pridelené nové spojenie.
Keďže mám z tohto módu naozaj radosť, dovolím si ešte popísať funkčnosť:
Najprv položím dva port-blocky do sveta (je jedno v akom sú zavalitý, vzdialenosť tu nehrá žiadnu rolu). Vezmem si ovládač a pravým tlačidlom kliknem na 1. block. Ovládač cvakne, dobehnem k 2. blocku a zopakujem postup. Teraz, keď kliknem pravým tlačidlom na prvý či druhý block, portne ma to na ten druhý. Ak teraz jeden block odstránim a pokúsim sa kliknúť na druhý, nič sa nestane (spojenie bolo vymazané). Uvediem všetko do pôvodnej podoby, postavím teda dva bloky a prepojím je pomocou ovládača. Teraz položím tretí block, ktorý hneď prepojím s prvým Blockom. Tieto dva blocky (1. a 3.) teraz fungujú a druhý block, ktorému bolo vymazané spojenie, nie.
A to je koniec tohto dielu s dvoma časťami. Naučili sme sa tu, ako uložiť dáta po prvé do Item (NBT), po druhé do blocku (meta) a po tretie do Worldu (WorldSavedData + NBT).
Ak niečo nechápete, či som to zle vysvetlil, určite napíšte komentár. Celý zdrojový kód napísaný za tieto dva diely je na stiahnutie pod článkom. (Obsahujúci MyWorldData, ItemPortRemote a BlockSaltPort)
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 21x (8.51 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Java