当前位置: 华文问答 > 游戏

当年星际争霸 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 版权所有,禁止转载。