當前位置: 華文問答 > 遊戲

當年星際爭霸 1 的「小狗變飛龍」屬於 bug 還是制作組有意為之?

2013-05-07遊戲

小狗變飛龍是bug嗎?

顯然是。而且相當嚴重。1.05版的patch.txt裏明確提到修復了這個bug:

- Fixed the bug that incorrectly allowed 'morph to lurker' commands
to be issued to units that weren't hydralisks.

這裏我要強調幾點:

  1. 「小狗變飛龍」是 BW1.04版獨有 的bug,只有這個版本有。1.03版的時候還沒BW,沒法變;1.05版以後已經修復。
  2. 「小狗變飛龍」bug的正確表述如patch.txt所言,是「刺蛇以外的東西能變地刺」。實際表現是單位可以透過bug操作變成 「孵地刺的飛龍蛋」 ,不取消就會變成地刺,取消就會變成飛龍並全額退款(相當於免費變飛龍)。 不只是小狗,遊戲中絕大多數單位都可以用相同操作bug變蛋、繼而變飛龍,包括其它種族的單位,甚至建築。
    這個bug以「小狗變飛龍」的形式出名也是有原因的。為什麽是小狗,是因為小狗作為消耗品最便宜(變成飛龍後原單位會永久消失),而且用狗變出的飛龍效能很強(只占1.5人口、繼承狗的跑速、攻速升級),價效比遠超用其它單位變。至於為什麽是飛龍,因為免費啊,要是不取消押金就沒了啊。
  3. 「小狗變飛龍」 觸發bug變蛋的充分必要操作是「選刺蛇命令它變身的瞬間選到其它單位」 ,不需要像網傳那樣按著Alt瘋狂121212。Alt鍵這裏的作用只是加快GUI的反應,能在變身圖示發白的時候更早一點切換編隊,但其即時機正確的話是 不需要按Alt 的,手法好的話一次可以成功。有興趣的朋友可以自己試試。

為什麽會出現這個bug?

因為1.04版的資料片升級,蟲族新增了地刺(Lurker)這個單位,遊戲給刺蛇增加了「變身成地刺」這個指令,卻沒有嚴格檢查「執行指令的單位是不是刺蛇」。 正常來講這確實沒啥問題——只要玩家選擇的單位不是刺蛇,哪怕是其它單位和刺蛇混選,命令選單裏也是沒有變身這個操作的。

但是,很快就有玩家在激烈對戰中開發出了一個飄逸操作: 先單獨選擇刺蛇,命令它執行變身的一瞬間選擇其它單位 ,就相當於 讓後來選中的單位執行了「變身成地刺」的命令

更詭異的是,這些冒名頂替的單位竟然不是像刺蛇那樣變成普通的蛋蛋,而是變成了漂在空中的 飛龍蛋 (飛龍變螃蟹/大蝦時的蛋,如下圖),而且孵的內容是地刺。

如果變身成功結束,它們就會變成地刺。但如果玩家這時取消變身,這些單位會變成飛龍。

註意螢幕頂端的錢和人口。錢全退到變之前的狀態了,一整隊飛龍只占了18人口。用人口1以上的單位變出的飛龍是正常的2人口,但狗變出來的只占1.5人口。

為什麽會變成飛龍?

我手上當然沒有星際爭霸的原始碼,說什麽都是瞎猜,死無對證。但是作為程式設計師,從程式角度上分析,最有可能的原因是這樣的:

*註意,以下分析純屬我的個人猜測。

讀過我專欄的朋友們,你們可能還記得這遊戲是工期一拖再拖、拼命加班趕工做出來的(見開發『星際爭霸』的艱辛之路,轉譯自Tough times on the road to Starcraft - Code Of Honor)。

然後 原版SC中,蟲族只有兩種單位可以變身 (Morph——註意農民變成建築不算,那個是Mutate)。一個是基地爬出來的那種 幼蟲(Larva) ,可以變成各種單位;另一個就是 飛龍 ,可以變成螃蟹。

於是,開發團隊在熬夜嚴重、頭腦不清醒的狀態下,程式碼大概會寫成這種邏輯:

void Morph(Unit selectedUnit, Type targetUnitType) { Spend(typeof(selectedUnit), targetUnitType); //消耗錢和人口 if (selectedUnit is Larva) //幼蟲 { selectedUnit.TransformToEgg(targetUnitType); //蛋蛋 } else //反正也沒別的單位能變身,飛龍就湊合寫在這吧 { selectedUnit.TransformToCocoon(targetUnitType); //飛龍蛋 } }

如果原單位是幼蟲,就變普通蛋;否則變飛龍蛋。

再然後遊戲發售,大受歡迎。

老板龍顏大悅:搞資料片,加新單位!

於是就有了醫療兵、瓦基裏、地刺、大蝦、隱刀、海盜船這些BW單位。

地刺是刺蛇變的對不對?這樣原來的程式碼就不能用了,怎麽辦,只好打個修補程式上去:

void Morph(Unit selectedUnit, Type targetUnitType) { Spend(typeof(selectedUnit), targetUnitType); //消耗錢和人口 if (selectedUnit is Larva || selectedUnit is Hydralisk) //幼蟲或者刺蛇 { selectedUnit.TransformToEgg(targetUnitType); //蛋蛋 } else //咦,下面的忘了改了。 { selectedUnit.TransformToCocoon(targetUnitType); //飛龍蛋 } }

如果原單位是幼蟲或刺蛇,就變普通蛋;否則變飛龍蛋。

本來這樣能行。問題是刺蛇變身那邊寫漏了,呼叫了Morph之前忘了檢測原單位是不是刺蛇。於是當小狗、農民、基地……反正除了刺蛇和幼蟲之外的一切東西,只要執行進去了,程式一看,哦哦不是幼蟲也不是刺蛇,那就變成飛龍蛋吧。

然後「取消變身」的程式碼,我估計是這種邏輯:

void CancelMorph(Unit selectedUnit, Type oldUnitType, Type targetUnitType) { Refund(typeof(selectedUnit), targetUnitType); //退錢、退人口 if (selectedUnit is Egg) //地面蛋蛋 { //KillUnit(selectedUnit); 舊版的邏輯,殺死幼蟲 if(oldUnitType is Hydralisk) //如果原單位是刺蛇,就變回刺蛇 { selectedUnit.Transform(Hydralisk); //退一條刺蛇 } else //蛋蛋不是刺蛇變的,就一定是幼蟲變的 { KillUnit(selectedUnit); //殺了它! } } else //剩下的情況只有飛龍蛋蛋吧? { selectedUnit.Transform(Mutalisk); //退一條飛龍 } }

飛龍蛋(或者說:普通蛋以外的蛋)取消,就給飛龍。

於是,你取消了這個蛋,它就變成飛龍了,哪怕它原本只是條狗。

這就是(我推測的)「孵地刺的飛龍蛋」的前因後果。

*註意,以上分析純屬我的個人猜測。程式碼也只是我為了示意邏輯瞎敲的,別當真。星際是C++寫的,語法都不一樣的。

最後附上一些論據。

雖然我前面說是瞎猜,但也不是空口無憑。如果你考慮到以下事實,應該會覺得我的猜測還是很靠譜的。

  1. 刺蛇不能變飛龍,即使用了bug變蛋操作也只能變成普通的「孵地刺的蛋」 ,然後和正常一樣,取消就變回刺蛇、不取消就變成地刺。
  2. 幼蟲也不能變飛龍,但用bug變蛋手法可以變成「直接孵地刺的蛋」 (跳過了造刺蛇的消耗),如果取消變身,會直接死掉。
  3. 其它各種單位用bug變蛋手法變「孵地刺的飛龍蛋」如出一轍 ,只是最後孵出的飛龍效能不同而已(會繼承原單位的某些升級和被動技能)。

(蟲族基地變的飛龍,之前留下的幼蟲會像鴨子一樣一直跟著媽媽走)

(神族仲裁變的飛龍可以使附近單位隱形,之所以自己也隱形了是因為仲裁的被動原理是使「半徑內除仲裁以外的單位」隱形)

而且,暴雪遊戲出這種卡操作的bug是常事。 且不說星際早期版本還有過「飛龍變螃蟹時狂按取消可以退多份錢」的bug(1.02修復),你們自己想想之前暗黑1的復制大法操作:把要復制的物品扔地上,撿起來的瞬間用滑鼠擡起一個身上不要的便宜貨(藥水或1G),那1G就消失了,取而代之的是滑鼠上和背包裏各有一份要復制的物品。是不是跟這個小狗變飛龍性質很類似?

——————————

本答案中的圖片為『StarCraft: Brood War』1.04版實際遊戲截圖。文字 @Thinkraft 版權所有,禁止轉載。