Advertisement
drinfernoo

Untitled

Sep 12th, 2012
529
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.93 KB | None | 0 0
  1. Ok, so I have noticed that there aren't very many tutorials about Forge's "Infinite Sprite Index",
  2.  
  3. so I decided I'd try my hand. This tutorial is written with Minecraft 1.3.2, MCP 7.2, and Minecraft
  4.  
  5. Forge 4.1.2.259 in mind, so anything earlier than that, I can't really support, and anything later
  6.  
  7. should be fairly similar, if not exactly the same.
  8.  
  9. I am using eclipse, and will be going off the premise that you have set up an MCP workspace, with
  10.  
  11. Forge, and haven't added anything else yet. These steps should work for any workspace, I will
  12.  
  13. simply be starting one new just for this tutorial. If you need help setting up a workspace, these
  14.  
  15. two tutorials are the mainstay:
  16.  
  17. Xain Faith's MCP Tutorial
  18. Minecraft Forge Installation
  19.  
  20. For this tutorial, we'll be creating a mod that adds in a Block and an Item, that way you can see
  21.  
  22. how both work (it's almost the same :P). We'll also recompile, reobfuscate, and distribute the mod
  23.  
  24. in the end :)
  25.  
  26. I guess let's just jump into some code!
  27.  
  28. InfiniteSprite.java
  29. [spoiler]
  30. This is going to be our main mod class, and if you notice, it don't not start with "mod_". That is
  31.  
  32. an old standard from ModLoader days, and is not necessary for Forge mods. You can name your mod
  33.  
  34. class whatever you want, as long as you write it correctly.
  35.  
  36. The first thing we're going to do is define our class:
  37. [code]
  38. package tutorials;
  39.  
  40. public class InfiniteSprite {
  41.  
  42. }
  43. [/code]
  44. Easy :) The package declaration in line 1 can simply be whatever you want. The default for most of
  45.  
  46. the vanilla Minecraft classes is net.minecraft.src, and if you want to put your classes there,
  47.  
  48. that's fine. I prefer mine in their own package for better organization.
  49.  
  50. After that, we have the class declaration, which just says that our class is called InfiniteSprite.
  51.  
  52. Now we need to add in the annotations, which let Forge know that this is our base mod class.
  53. [code]
  54. package tutorials;
  55.  
  56. import cpw.mods.fml.common.Mod;
  57. import cpw.mods.fml.common.network.NetworkMod;
  58.  
  59. @Mod(modid = "infinite_sprite", name = "Infinite Sprite", version = "1.0")
  60. @NetworkMod(clientSideRequired = true, serverSideRequired = false)
  61. public class InfiniteSprite {
  62.  
  63. }
  64. [/code]
  65. This adds in a couple lines. The import lines simply import in the classes for the annotations
  66.  
  67. below. If you are ever in doubt about which classes need to be imported and which ones don't,
  68.  
  69. eclipse has a handy keyboard shortcut, Ctrl+Shift+O, which organizes your imports for you :) Bear
  70.  
  71. in mind that if your package is net.minecraft.src, some of the imports in this tutorial won't be
  72.  
  73. necessary, as you only have to import classes from different packages.
  74.  
  75. Annotations are new to Forge 4.x, and give Forge a way to communicate with classes in a more
  76.  
  77. precise manner, while allowing you to be a little more flexible with your code. There are a few
  78.  
  79. more places we'll use these in this class as well.
  80.  
  81. The @Mod line finally tells Forge that this is our main mod class! It also tells Forge that our
  82.  
  83. mod's unique id is "infinite_sprite", its name is "Infinite Sprite", and it is version "1.0".
  84.  
  85. The @NetworkMod line below that tells Forge that this mod can run on a server, but you don't need
  86.  
  87. to have it installed on the server for it to run, only in the client. This annotation can get much
  88.  
  89. more complicated, but for simple mods like this, this is really all you need.
  90.  
  91. Next we need the methods that this class usually uses:
  92. [code]
  93. package tutorials;
  94.  
  95. import cpw.mods.fml.common.Mod;
  96. import cpw.mods.fml.common.Mod.Init;
  97. import cpw.mods.fml.common.Mod.PostInit;
  98. import cpw.mods.fml.common.Mod.PreInit;
  99. import cpw.mods.fml.common.event.FMLInitializationEvent;
  100. import cpw.mods.fml.common.event.FMLPostInitializationEvent;
  101. import cpw.mods.fml.common.event.FMLPreInitializationEvent;
  102. import cpw.mods.fml.common.network.NetworkMod;
  103.  
  104. @Mod(modid = "infinite_sprite", name = "Infinite Sprite", version = "1.0")
  105. @NetworkMod(clientSideRequired = true, serverSideRequired = false)
  106. public class InfiniteSprite {
  107.  
  108. @PreInit
  109. public void preInit(FMLPreInitializationEvent event) {
  110. //loads before your mod
  111. }
  112.  
  113. @Init
  114. public void init(FMLInitializationEvent event) {
  115. //loads your mod
  116. }
  117.  
  118. @PostInit
  119. public void postInit(FMLPostInitializationEvent event) {
  120. //loads after your mod
  121. }
  122. }
  123. [/code]
  124. This adds in a couple more imports, all for annotations and FML events, but the meat of the issue
  125.  
  126. is inside.
  127.  
  128. This added in three methods, each prefaced with an annotation. The @PreInit method is run before
  129.  
  130. anything else in your mod, so people usually use it for things like loading configuration files and
  131.  
  132. the like. The @Init method is where most of the fun happens, in terms of registering blocks and
  133.  
  134. such. The @PostInit method is run after your mod is loaded completely, and I don't know of many
  135.  
  136. uses for it, except I have read that it is used especially in intregation with Equivalent Exchange.
  137.  
  138. These methods can be named whatever you want, as long as they are prefaced with those annotations,
  139.  
  140. and take the same parameters.
  141.  
  142. Now for the fun stuff:
  143. [code]
  144. package tutorials;
  145.  
  146. import net.minecraft.src.Block;
  147. import net.minecraft.src.Item;
  148. import net.minecraft.src.ItemStack;
  149. import cpw.mods.fml.common.Mod;
  150. import cpw.mods.fml.common.Mod.Init;
  151. import cpw.mods.fml.common.Mod.PostInit;
  152. import cpw.mods.fml.common.Mod.PreInit;
  153. import cpw.mods.fml.common.event.FMLInitializationEvent;
  154. import cpw.mods.fml.common.event.FMLPostInitializationEvent;
  155. import cpw.mods.fml.common.event.FMLPreInitializationEvent;
  156. import cpw.mods.fml.common.network.NetworkMod;
  157. import cpw.mods.fml.common.registry.GameRegistry;
  158. import cpw.mods.fml.common.registry.LanguageRegistry;
  159.  
  160. @Mod(modid = "infinite_sprite", name = "Infinite Sprite", version = "1.0")
  161. @NetworkMod(clientSideRequired = true, serverSideRequired = false)
  162. public class InfiniteSprite {
  163.  
  164. public static Block ourBlock = new SpriteBlock(200, 0);
  165. public static Item ourItem = new SpriteItem(450, 0);
  166.  
  167. @PreInit
  168. public void preInit(FMLPreInitializationEvent event) {
  169. //loads before your mod
  170. }
  171.  
  172. @Init
  173. public void init(FMLInitializationEvent event) {
  174. GameRegistry.registerBlock(ourBlock);
  175. GameRegistry.addRecipe(new ItemStack(ourBlock), new Object[] {
  176. "XXX", Character.valueOf('X'), Block.dirt
  177. });
  178. GameRegistry.addRecipe(new ItemStack(ourItem), new Object[] {
  179. "XXX", Character.valueOf('X'), Block.wood
  180. });
  181.  
  182. LanguageRegistry.addName(ourBlock, "Sprite Block");
  183. LanguageRegistry.addName(ourItem, "Sprite Item");
  184.  
  185. MinecraftForgeClient.preloadTexture("/tutorials/terrain.png");
  186. MinecraftForgeClient.preloadTexture("/tutorials/items.png");
  187. }
  188.  
  189. @PostInit
  190. public void postInit(FMLPostInitializationEvent event) {
  191. //loads after your mod
  192. }
  193. }
  194. [/code]
  195. That's it! That is all the editing in this class :)
  196.  
  197. We did add a couple imports (thank you Ctrl+Shift+O), but those are all the ones we need for this
  198.  
  199. tutorial.
  200.  
  201. The lines
  202. [code]
  203. public static Block ourBlock = new SpriteBlock(200, 0);
  204. public static Item ourItem = new SpriteItem(450, 0);
  205. [/code]
  206. actually declare our block and our item for use later on. The first parameter in each of these is
  207.  
  208. the ID for that block/item. As of snapshot 12w36a, used block IDs end at 144, and used item IDs end
  209.  
  210. at 399, except for music discs, which take up 2256-2266. Forge allows us to add in blocks/items for outside of these ranges, but for an up-to-date listing of vanilla IDs, check out this page.
  211.  
  212. The 0's in those calls refer to which sprite in our image file to use.
  213.  
  214. If this is all you've done so far, these will be giving you an error. No worries, we will fix them
  215.  
  216. soon, the block is in the next section :)
  217.  
  218. In the @Init method, we've added a few ***Registry calls too.
  219.  
  220. GameRegistry is usually responsible for registering new Blocks, TileEntities, Recipes, and the
  221.  
  222. like, and that's what we do here. The first call simply registers our SpriteBlock from earlier, so
  223.  
  224. the game can use it.
  225.  
  226. The GameRegistry.addRecipe() lines add in recipes for our new block and our new item, that way they
  227.  
  228. are accessible in Survival mode. They create a new ItemStack, of the respective block/item, and
  229.  
  230. then make a recipe. These are usable in the standard 2x2 crafting square, with two Dirt blocks
  231.  
  232. making our Sprite Block, and two Wood blocks making our Sprite Item.
  233.  
  234. LanguageRegistry takes care of displaying in-game names on blocks and items. These are pretty
  235.  
  236. simple calls, just making sure that in our inventory, we see "Sprite Block" and "Sprite Item" when
  237.  
  238. we hover over them.
  239.  
  240. Next we have MinecraftForgeClient.preloadTexture(). This loads the image we are going to use for
  241.  
  242. our custom textures into memory so Forge can use them to pull textures from. These should be
  243.  
  244. 256x256 pixels, and the ones I am going to use are here:
  245. [spoiler]
  246. [img=http://i.imgur.com/TInpu.png]
  247. [img=http://i.imgur.com/8Aow7.png]
  248. Feel free to use these if you want, or make your own.
  249. [/spoiler]
  250. Please excuse my bad spriting :P I have multiple textures for the block, because I'll be showing
  251.  
  252. you how to texture side-by-side, but you really only need one if you aren't doing something fancy.
  253.  
  254. These images should be placed in *yourMCPfolder*\src\common\tutorials if you are using the same
  255.  
  256. package as me, or *yourMCPfolder*\src\common\*yourpackagehere*.
  257.  
  258. Now on to the Block!
  259. [/spoiler]
  260.  
  261. SpriteBlock.java
  262. [spoiler]
  263. [code]
  264. package tutorials;
  265.  
  266. import net.minecraft.src.Block;
  267.  
  268. public class SpriteBlock extends Block {
  269.  
  270. }
  271. [/code]
  272. This is our standard package and class definition, with a twist. This class extends Block, which
  273.  
  274. means that, aside from having to import Block, it also inherits all the methods and fields (aka
  275.  
  276. variables) that Block has.
  277.  
  278. Lets add in a little more content on this block:
  279. [code]
  280. package tutorials;
  281.  
  282. import java.util.Random;
  283.  
  284. import net.minecraft.src.Block;
  285. import net.minecraft.src.CreativeTabs;
  286. import net.minecraft.src.Material;
  287.  
  288. public class SpriteBlock extends Block {
  289.  
  290. public SpriteBlock(int blockID, int spriteIndex) {
  291. super(blockID, spriteIndex, Material.rock);
  292. this.setResistance(3.0F);
  293. this.setHardness(3.0F);
  294. this.setCreativeTab(CreativeTabs.tabBlock);
  295. this.setBlockName("ourBlock");
  296. }
  297.  
  298. @Override
  299. public int idDropped(int i, Random r, int j) {
  300. return InfiniteSprite.ourBlock.blockID;
  301. }
  302.  
  303. @Override
  304. public int quantityDropped(Random r) {
  305. return 1;
  306. }
  307. }
  308. [/code]
  309. At this point, we are "done" with this block. It is usable in-game, but it will look like Stone,
  310.  
  311. because its spriteIndex is 0, from our InfiniteSprite class.
  312.  
  313. Aside from imports, we added a constructor and a couple method here.
  314.  
  315. The constructor calls the standard Block constructor, passing it our blockID, our spriteIndex, and
  316.  
  317. we are going to use Material.rock, which dictates what sound it makes when you walk on it, and what
  318.  
  319. tools are effective against it. It also sets the Resistance (how damaged it is by explosions), the
  320.  
  321. Hardness (how damaged it is by tools), which tab it's displayed on in the Creative inventory, and
  322.  
  323. the block "name", which is solely in the code, and has no bearing on the ingame name. We did that
  324.  
  325. with LanguageRegistry, remember? :)
  326.  
  327. CreativeTabs is the vanilla class which dictates which tabs there are in the Creative inventory,
  328.  
  329. and it contains:
  330. [code]tabBlock, tabDeco, tabRedstone, tabTransport, tabMisc, tabAllSearch, tabFood, tabTools,
  331.  
  332. tabCombat, tabBrewing, tabMaterials, tabInventory[/code]
  333. But I don't know how tabAllSearch or tabInventory actually work in practice.
  334.  
  335. All but the super() call can be, and frequently are, called in the main mod class, like so:
  336. [code]
  337. public static Block ourBlock = new SpriteBlock(200, 0).setResistance(3.0F).setHardness
  338.  
  339. (3.0F).setCreativeTab(CreativeTabs.tablBlock).setBlockName("ourBlock");
  340. [/code]
  341. but I prefer it this way, as it looks a litle cleaner to me.
  342.  
  343. The next thing to add is the texturing methods:
  344. [code]
  345. package tutorials;
  346.  
  347. import java.util.Random;
  348.  
  349. import net.minecraft.src.Block;
  350. import net.minecraft.src.CreativeTabs;
  351. import net.minecraft.src.Material;
  352.  
  353. public class SpriteBlock extends Block {
  354.  
  355. public SpriteBlock(int blockID, int spriteIndex) {
  356. super(blockID, spriteIndex, Material.rock);
  357. this.setResistance(3.0F);
  358. this.setHardness(3.0F);
  359. this.setCreativeTab(CreativeTabs.tabBlock);
  360. this.setBlockName("ourBlock");
  361. }
  362.  
  363. @Override
  364. public int idDropped(int i, Random r, int j) {
  365. return InfiniteSprite.ourBlock.blockID;
  366. }
  367.  
  368. @Override
  369. public int quantityDropped(Random r) {
  370. return 1;
  371. }
  372.  
  373. @Override
  374. public String getTextureFile() {
  375. return "/tutorials/terrain.png";
  376. }
  377.  
  378. @Override
  379. public int getBlockTextureFromSide(int side) {
  380. switch (side) {
  381. case 0:
  382. return 0;
  383. case 1:
  384. return 1;
  385. case 2:
  386. return 2;
  387. case 3:
  388. return 3;
  389. case 4:
  390. return 4;
  391. case 5:
  392. return 5;
  393. default:
  394. return 0;
  395. }
  396. }
  397. }
  398. [/code]
  399. All this added is the getTextureFile() and getBlockTextureFromSide() methods.
  400.  
  401. The getTextureFile() method should return the same path that we preloaded in our mod class. This
  402.  
  403. simply tells Forge which image to take the texture for this block from.
  404.  
  405. The getBlockTextureFromSide() method, in this form, simply returns the index in our texture file
  406.  
  407. for whichever side the game is trying to texture. This will change if your have more than one
  408.  
  409. block, or if you try to do metadata blocks, you'll use a different method as well.
  410.  
  411. That's it for this block! Let's do the Item now. It's almost exactly the same as the block :P
  412. [/spoiler]
  413.  
  414. SpriteItem.java
  415. [spoiler]
  416. [code]
  417. package tutorials;
  418.  
  419. import net.minecraft.src.Item;
  420.  
  421. public class SpriteItem extends Item {
  422.  
  423. }
  424.  
  425. [/code]
  426. Exactly the same as SpriteBlock, except here we extend Item, so we're going to inherit the methods
  427.  
  428. and fields that an Item has.
  429.  
  430. I'm just going to finish it here :)
  431. [code]
  432. package tutorials;
  433.  
  434. import net.minecraft.src.CreativeTabs;
  435. import net.minecraft.src.Item;
  436.  
  437. public class SpriteItem extends Item {
  438.  
  439. public SpriteItem(int id, int spriteIndex) {
  440. super(id);
  441. this.setIconIndex(spriteIndex);
  442. this.setItemName("ourItem");
  443. this.setTabToDisplayOn(CreativeTabs.tabTools);
  444. }
  445.  
  446. @Override
  447. public String getTextureFile() {
  448. return "/tutorials/items.png";
  449. }
  450. }
  451. [/code]
  452. This one's not quite as complicated, but here we set our item's spriteIndex, Creative tab, and
  453.  
  454. name. We also return its texture file through getTextureFile(). That's it! All the coding is done
  455.  
  456. :)
  457. [/spoiler]
  458.  
  459. Now to package this up. Go to your MCP workspace folder (for me it's H:\workspace\MakingTutorials),
  460.  
  461. and run recompile.bat (or recompile.sh for Unix systems). This does a final build of the classes we
  462.  
  463. just wrote. Now run reobfuscate.bat (again, or reobfuscate.sh for Unix users). This script simply
  464.  
  465. compiles the classes from their .java source into .class program files. MCP 7.2 gives a warning:
  466. [code]"!! Can not find server bins, try recompiling !!"[/code]
  467. but this is a false alarm, a remnant of how there used to be a client and server version of all
  468.  
  469. mods.
  470.  
  471. Now, in the reobf folder, you should have reobf\minecraft\*yourpackagehere*, which contains all of
  472.  
  473. the class files we just made, but there's one problem we need to deal with. There's no images in
  474.  
  475. there! Simply go back to *yourMCPfolder*\src\common\*yourpackagehere*, and copy the images you used
  476.  
  477. into *yourMCPfolder*\reobf\minecraft\*yourpackagehere*. Now zip up the whole *yourpackagehere*
  478.  
  479. folder, using 7zip, WinRAR, or whatever you like, and you'll have a working mod :) You can now just
  480.  
  481. drop this zip into your mods folder in Minecraft, or put it in the mods tab in MultiMC if you like.
  482.  
  483. If there are any questions or suggestions, let me know :)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement