"有些话说出来就舒服多了"

(标题不用太在意,又一发黑枪而已...)
因为昨天把脚崴了,所以今天在家休息一天,感觉这段时间造的孽太多,前几天把一个在图书馆看霓虹动♂作♀片的仁兄曝光了(请叫我正义的朋友face4),在blog上又搞了十几天的放置play.因此决定趁现在瘸着腿什么都不想干的时候写点啥.
首先是教程还更不更的问题,这个我想说的是,基础篇估计不会更新了...真要更新的话可能是实体部分会再详细一点. 至于TileEntity,冶炼和地图生成这三部分可能不会再有了,等着别人来写吧 23333
Extra篇可能会有个3D部分,此外肯定还会有个关于这三年间我遇到的各种MC问题的Q&A.

顺便再感慨一下,终于有人做到这一步了:

它的作者Arun Gupta自称是畅销书作者,你可以在他的blogO'Reilly的官网找到目录信息,能把这点内容写出200页确实有两下子,反正我是把新旧两个教程连同Extra篇加在一起也凑不出200页.我觉得那本书叫Minecraft modding for dummies或许更合适,事实上作者本人也说他的目标读者是初学编程的未成年人,因此就不要指望书中会有网络编程或Coremod之类的了...

另外,有些东西不一定非要问别人才能知道...我不是很喜欢知乎的原因除了山寨和半实名制外,还因为它扼杀了一些人探究问题的能力,当擅长哗众取宠的人能垄断一个问题的话语权时,一切理性都是苍白无力的.比如关于配置时JAVA_HOME不正确之类的...这些问题大部分我都没法在自己的机器上重现,最后结果还不全是去Google搜索...想知道MC的xx怎么做/怎么用,最简单的办法是:看官方是怎么弄的.Eclipse中的References(查找引用)功能非常强大,点上你想查找引用的类/字段/方法,然后右键-References-Project(其实更多情况下我是直接Ctrl-Shift-G...)就能找它的所有调用,然后看看官方是怎么使用它的.想追踪调用栈的话可以Ctrl+Alt+H,查看类的继承家谱还可以F4,能自己很快完成的事情为何还要去找别人问呢. (顺便一提,如果不知道eclipse怎么解除智能感知的封印的话...去网上搜一下吧.虽然它的智能感知即使解除封印了也确实不如VS和IDEA,甚至在高版本中有越来越蠢的迹象,Luna版的智能感知一度想让我砸键盘...就看4个月后的Mars版怎么样了)

举个栗子,之前有人问我成就系统怎么弄,我以前从来没研究过成就系统,那现在就从零开始,开始我甚至找不到成就类在哪,不过在客户端中有个成就的Gui类(net.minecraft.client.gui.achievement.GuiAchievement),从它的一个字段中我找到了成就类(net.minecraft.stats.Achievement),然而在我现在的版本(1.7.2)中它的构造函数的参数还没有被写上可读的解释,因此我只能看看官方是怎么用的,通过超找它的构造函数的引用,我看到AchievementList类包含了游戏默认成就的全部实例,发现OpenInventory(游戏中的打开物品栏成就)的初始化方式是

public static Achievement openInventory = (new Achievement("achievement.openInventory", "openInventory", 0, 0, Items.book, (Achievement)null)).initIndependentStat().registerStat();

第一个参数看上去十分像语言文件中的键,第二个参数或许是它的ID(其实这两个结论在之后都被证实为错了...但推翻的过程要到待会再说),第三四个参数暂时不得而知,第五个参数应该是它的图标,第六个参数是个null,因此无从得知它的用途,之后还有两个采用Builder模式的方法,initIndependentStat的内容待会再调查,registerStat看上去像是注册当前成就.
然后我回过头去看Achievement的构造函数,未反混淆的参数名看上去乱糟糟的,但因为已经看过它们的实际使用方式,因此心里更有些普,我发现对第一个和第二个参数作用的结论反了,第一个才是成就ID,而第二个是语言文件的键的后半段,真是个充满迷惑性的不可理喻的设计...第三个和第四个在这里发现是他们在成就页中的位置.第五个参数在这里被包装为了ItemStack,然后存入了theItemStack字段中,它的介绍是"Holds the ItemStack that will be used to draw the achievement into the GUI.",看起来之前的猜想是正确的.第六个参数这里被明确指出是前提成就.由于OpenInventory是游戏中的第一个成就,因此它的前提成就肯定是null.此外我还注意到成就的描述文本(achievementDescription字段)是通过直接从语言文件中获取键为"achievement.[参数2].desc"的文本来获得.
然后再调查那两个方法,initIndependentStat方法的描述很含糊,内容也只是将initIndependentStat设为true,但是通过查找initIndependentStat的引用,我发现在StatBase里的toString中,将isIndependent称为"awardLocallyOnly",我记得MC1.7.2增加了成就在不同服务器中区分计算的设定,因此这个大概是让成就只储存在客户端中.而registerStat就是注册成就,没有什么好说的...
然而,我们还知道Forge为MC增加了成就分类系统,然而我并不知道它在哪(就装作我不知道吧...实际情况下翻翻那几个package也能翻到),但我想肯定它和成就界面有关,至少在成就界面中应该能找到它的引用.果然在net.minecraft.client.gui.achievement.GuiAchievements中看到了个AchievementPage.AchievementPage位于net.minecraftforge.common包下,作为一个库中的类,它的使用方式很简洁明了:创建一个实例,然后通过静态方法registerAchievementPage注册成就分页.
最后还有个问题,就是如何触发成就,这个听起来有些棘手,虽然只要有官方的用法参照就不是问题,但关键在于如何寻找官方的用法.由于AchievementList包含了所有成就的实例,因此我猜想如果想触发一个成就,则势必会用到那个成就的引用(虽然也有可能是通过成就Id来触发的...),果然通过查找随意一个成就(但不建议是openInventory,跟它相关的东西太多了...)的引用可以看到,触发成就的方式是调用玩家实体的triggerAchievement方法.

于是这便是从零研究如何做成就的办法,全程用时应该不会超过一刻钟,而如果你要问别人的话,需要等天知道有多久的时间才能得到回复,而且对方的解释你还不一定能立刻明白,为什么就不花一点点时间来自己研究一下呢?