我的世界神秘時代4.2.3.5從源代碼詳解注魔的風險控制
玩過神秘(Thaumcraft)4的人都知道,奧術注魔如果沒有采取保護措施,往往會出現(xiàn)咒波污染、爆炸等事故!赌謨浴犯嬖V我們,頭骨、魔晶、蠟燭等物品能降低注魔的風險。于是很多人看了網(wǎng)上的攻略或者教程,直接在符文矩陣下方幾個格子的地方擺了13*13的蠟燭方陣,的確解決了這些問題。然而,為什么是13*13呢?為什么要擺在那個位置呢?一般玩家都沒有這么深入地想下去。今天,我就通過解析反編譯之后的Java源代碼的方式,介紹一下關于奧術注魔的風險的詳細計算方法。不過不會Java也沒關系,直接看帖子結尾的結論就好了。
代碼的位置
和注魔有關的代碼(不包括配方)在thaumcraft.common.tiles包里的TileInfusionMatrix這個類中。其中TileInfusionMatrix.class文件用各種工具反編譯之后可以得到代碼TileInfusionMatrix.txt。 TileInfusionMatrix.txt
不穩(wěn)定性(instability)
這是craftingCycle方法中,第436~513行的代碼:
if ((!valid) || ((this.instability > 0) &&
(this.worldObj.rand.nextInt(500) <= this.instability))) {
switch (this.worldObj.rand.nextInt(21)) {
case 0: case 2: case 10: case 13: inEvEjectItem(0); break;
case 6: case 17: inEvEjectItem(1); break;
case 1: case 11: inEvEjectItem(2); break;
case 3: case 8: case 14: inEvZap(false); break;
case 5: case 16: inEvHarm(false); break;
case 12: inEvZap(true); break;
case 19: inEvEjectItem(3); break;
case 7: inEvEjectItem(4); break;
case 4: case 15: inEvEjectItem(5); break;
case 18: inEvHarm(true); break;
case 9:
this.worldObj.createExplosion(null, this.xCoord + 0.5F,
this.yCoord + 0.5F, this.zCoord + 0.5F,
1.5F + this.worldObj.rand.nextFloat(), false);
break;
case 20: inEvWarp();
}
if (valid) return;
}
復制代碼
craftingCycle方法會在奧術注魔的過程中被不斷調用,每10個tick調用一次,直到注魔結束。
這個if語句內部是奧術注魔的各種副作用,從調用的函數(shù)的名稱就可以看出來,什么彈出物品啊、爆炸啊、扭曲啊之類的。這些事故是我們玩家不希望發(fā)生的,因此這段代碼的關鍵就是if語句的條件。首先是"!valid",觀察前面的代碼會發(fā)現(xiàn),當注魔中途有檢測到注魔材料不完整的時候valid才會等于false,此時,不論后面的條件滿不滿足,都會強制觸發(fā)一次注魔事故。因此一旦注魔開始,就要盡量保護好基座上的物品,否則必定發(fā)生事故。
"或"符號后面的條件是(this.instability > 0) && (this.worldObj.rand.nextInt(500) <= this.instability)。
翻譯成自然語言,大概就是:
不穩(wěn)定性>0 且 不穩(wěn)定性>=(一個0~499的隨機數(shù))
也就是說注魔的風險和不穩(wěn)定性有關,只要不穩(wěn)定性大于零,就有幾率發(fā)生風險。不穩(wěn)定性越大,產生風險的可能性也就越大。
那么不穩(wěn)定性又是如何計算出來的呢?這是craftingStart方法的307~310行:
this.recipeInstability = recipe.getInstability(this.recipeInput);
this.recipeEssentia = recipe.getAspects(this.recipeInput).copy();
this.recipePlayer = player.getCommandSenderName();
this.instability = (this.symmetry + this.recipeInstability);
復制代碼
翻譯成自然語言就是:
不穩(wěn)定性 = 對稱性 + 配方不穩(wěn)定性
不過不要高興地太早,代碼中還有這么幾行(craftingCycle中,有好幾處):
if (this.worldObj.rand.nextInt(50 -(this.recipeInstability * 2)) == 0) {
this.instability += 1;
}
if (this.instability > 25) {
this.instability = 25;
}
復制代碼
這里似乎會不斷增加注魔的不穩(wěn)定性,最多增加到25,也就是每0.5s有5%的概率發(fā)生事故。然而仔細觀察上下文會發(fā)現(xiàn),這兩行代碼在正常情況下是不會被執(zhí)行的,它們只會在注魔過程中源質缺失、物品缺失或者注魔附魔時經驗不足時執(zhí)行。
配方不穩(wěn)定性是一個整數(shù),對應著魔導手冊中不同的風險等級。具體數(shù)值可以直接在配方文件中查閱,addInfusionCraftingRecipe方法的第三個參數(shù)就是配方不穩(wěn)定性:
在原版TC中,絕大多數(shù)的注魔的配方不穩(wěn)定性都在1~8之間,只有高級節(jié)點穩(wěn)定器的配方不穩(wěn)定性是10。
對稱性(symmetry)
對稱性涉及到的東西就是注魔祭壇的擺放問題了。關于對稱性的全部代碼幾乎都能在getSurroundings方法(852~953行)找到。
首先,程序獲取了周圍方塊的信息:
for (int xx = -12; xx <= 12; xx++) {
for (int zz = -12; zz <= 12; zz++) {
boolean skip = false;
for (int yy = -5; yy <= 10; yy++) {
if ((xx != 0) || (zz != 0)) {
int x = this.xCoord + xx;
int y = this.yCoord - yy;
int z = this.zCoord + zz;
TileEntity te = this.worldObj.getTileEntity(x, y, z);
if ((!skip) && (yy > 0) && (Math.abs(xx) <= 8) && (Math.abs(zz) <= 8)
&& (te != null) && (te instanceof TilePedestal)) {
this.pedestals.add(new ChunkCoordinates(x, y, z));
skip = true;
} else {
Block bi = this.worldObj.getBlock(x, y, z);
if ((bi == Blocks.skull) || ((bi instanceof IInfusionStabiliser)
&& (((IInfusionStabiliser) bi).canStabaliseInfusion(getWorldObj(), x, y, z)))) {
stuff.add(new ChunkCoordinates(x, y, z));
}
}
}
}
}
}
復制代碼
搜索的范圍是橫縱坐標±12、豎直方向-10~+5的范圍。為什么不是-5~+10呢?因為"int y = this.yCoord - yy;",中間是個減號而不是加號,千萬不要上當了。"if ((xx != 0) || (zz != 0))",有了這句話,說明和符文矩陣在同一條鉛垂線的格子不會被搜索到。這里還有個"skip",在yy循環(huán)的外層初始化為false,當檢查到第一個物品基座時變?yōu)閠rue,當檢查到第二個物品基座時就直接忽略——它的作用是對于幾個在同一條鉛垂線上的物品基座,只考慮最頂上的,忽略掉下方其它的基座。雖然搜索范圍很大,但是第一個if語句"if ((!skip) && (yy > 0) && (Math.abs(xx) <= 8) && (Math.abs(zz) <= 8) && (te != null) && ((te instanceof TilePedestal)))"告訴我們,實際上只有x坐標和z坐標相差小于等于8,且嚴格處于符文矩陣所在格子下方的物品基座才會被考慮進去。
但是對于"IInfusionStabiliser",也就是魔鏡、頭骨、蠟燭等“法器”,搜索范圍就是原來的大小,也就是橫縱坐標±12、豎直方向-10~+5的范圍。
這整一段代碼把一定范圍內的基座和“法器”都記錄下來,用于接下來的判斷:
this.symmetry = 0;
for (ChunkCoordinates cc : this.pedestals) {
boolean items = false;
int x = this.xCoord - cc.posX;
int z = this.zCoord - cc.posZ;
TileEntity te = this.worldObj.getTileEntity(cc.posX, cc.posY, cc.posZ);
if ((te != null) && (te instanceof TilePedestal)) {
this.symmetry += 2;
if (((TilePedestal) te).getStackInSlot(0) != null) {
this.symmetry += 1;
items = true;
}
}
int xx = this.xCoord + x;
int zz = this.zCoord + z;
te = this.worldObj.getTileEntity(xx, cc.posY, zz);
if ((te != null) && (te instanceof TilePedestal)) {
this.symmetry -= 2;
if ((((TilePedestal) te).getStackInSlot(0) != null) && (items)) {
this.symmetry -= 1;
}
}
}
復制代碼
這一部分計算的是所有的基座。對于每個基座,都會提供2點symmetry,如果上面有物品還會額外提供一點symmetry;后面的xx,zz計算的是這個基座以符文矩陣所在鉛垂線為軸的對稱位置,如果對稱位置也有基座,就會減少兩點symmetry;同理,這個基座上有物品、對稱的基座上也有物品,symmetry還會再減少一點。也就是說,如果基座擺得不對稱或者基座上的物品擺得不對稱就會增大注魔的風險,如果完全對稱,這一部分的貢獻的風險可以降為0。
float sym = 0.0F;
for (ChunkCoordinates cc : stuff) {
boolean items = false;
int x = this.xCoord - cc.posX;
int z = this.zCoord - cc.posZ;
Block bi = this.worldObj.getBlock(cc.posX, cc.posY, cc.posZ);
if ((bi == Blocks.skull) || ((bi instanceof IInfusionStabiliser) &&
(((IInfusionStabiliser) bi).canStabaliseInfusion(getWorldObj(), cc.posX, cc.posY, cc.posZ)))) {
sym += 0.1F;
}
int xx = this.xCoord + x;
int zz = this.zCoord + z;
bi = this.worldObj.getBlock(xx, cc.posY, zz);
if ((bi == Blocks.skull) || ((bi instanceof IInfusionStabiliser) &&
(((IInfusionStabiliser) bi).canStabaliseInfusion(getWorldObj(), cc.posX, cc.posY, cc.posZ)))) {
sym -= 0.2F;
}
}
復制代碼
這部分計算的是所有的"法器"。對于每個"法器",都會提供0.1的symmetry;如果對稱位置也有"法器"則會再減少0.2的symmetry(無論兩者是否相同)。也就是說擺放對稱的"法器"可以減少注魔風險,但是如果沒有成對地擺放反而會增加注魔風險。注意,"法器"提供的symmetry單獨存在一個單精度浮點數(shù)變量"sym"里面。
this.symmetry = ((int) (this.symmetry + sym));
復制代碼
最后把基座和"法器"提供的symmetry加起來,轉換為整數(shù)。注意這個轉換過程是截斷取整,也就是向0方向取整。
結論
1.注魔祭壇相關的所有“對稱性”都是以符文矩陣所在鉛垂線軸對稱(從上往下看就像是中心對稱),而且只需考慮“有”和“沒有”,和物品的種類無關。比如A位置和B位置對稱的,那么如果A擺了物品,B也要擺物品,否則就會不穩(wěn)定;但是對于B位置擺的物品的種類,是不影響注魔風險的。對于蠟燭、頭骨、魔晶等"法器"也是一樣,甚至蠟燭的對稱位置是頭骨,也能配成一對來降低注魔風險;而且蠟燭、頭骨、魔晶降低注魔風險的效果完全相同。此外,除了基座和法器外的方塊和實體對注魔祭壇的穩(wěn)定性沒有任何影響。
2.假設符文矩陣的坐標是(x,y,z),那么基座的有效擺放范圍是(x-8 ~ x+8, y-10 ~ y-1, z-8 ~ z+8),還要滿足兩個條件:基座和符文矩陣不能擺在同一條鉛垂線上、任意兩個基座不能擺在同一條鉛垂線上(否則只有上面的有效)。
3.假設符文矩陣的坐標是(x,y,z),那么蠟燭、頭骨、魔晶等"法器"的有效擺放范圍是(x-12 ~ x+12, y-10 ~ y+5, z-12 ~ z+12),并且不能和符文矩陣在同一條鉛垂線上。理論上可以擺下大量的蠟燭,因為蠟燭不只可以擺一層、而是可以擺好多層。
4.原版中,最危險的注魔是高級節(jié)點穩(wěn)定器。它的配方不穩(wěn)定性是10;因為物品個數(shù)是偶數(shù),可以擺成完全對稱,所以基座和基座上的材料帶來的symmetry可以達到0;因此理論上只需要擺100個兩兩對稱的蠟燭即可做到0風險。實測需要104個,可能是浮點數(shù)誤差的問題。如果基座上所需材料個數(shù)是奇數(shù),不穩(wěn)定性要+1,但是原版神秘中需要奇數(shù)個材料的注魔配方不穩(wěn)定性最高也只有8,還不如高級節(jié)點穩(wěn)定器危險?傊衩4原版想要100%安全地注魔只需要104根兩兩對稱的蠟燭,而不是大部分教程聲稱的169個、168個或是152個。如果不信,可以用NBT edit實時查看符文矩陣的instability值(http://royaljewelryandwatches.com/buding/116137.html)(安裝后鼠標指著符文矩陣輸入命令/nbtedit查看)
5.前期注魔的時候為了節(jié)約材料可以不用造那么多蠟燭(那些風險"微乎其微"的只要十來根蠟燭就夠了),蠟燭的數(shù)目可以隨著所需注魔的風險越來越高而逐漸增加。有些附屬MOD里的注魔或者被整合包作者魔改的注魔,如果不知道需要多少蠟燭,可以裝個NBT edit,先到創(chuàng)造模式里去試一試,計算出需要多少蠟燭了再回到生存里面實踐。
這里用的代碼來自TC4.2.3.5的dev版,和玩家拿到的版本是在功能上是完全一樣的。這些代碼的分發(fā)并沒有得到Thaumcraft作者Azanor的授權,僅供學習研究,請勿用于商業(yè)用途,下載后請在24小時內刪除。(感謝 @u.s.knowledge )
最后引用被神秘時代的作者很喜歡的一句話:
"Thaumaturgy... which giveth certain order to make strange works, of the sense to be perceived and of men greatly to be wondered at."- Mathematicall Praeface to Euclid's Elements (1570), John Dee
意思是說,神秘也是有一定規(guī)律的,在MC里體現(xiàn)為——神秘時代MOD就是一堆代碼。從代碼內部剖析神秘時代,得到的是另一種趣味。
查看所有0條評論>>