原版AI中两个造成NPC不动作的BUG
BUG 1:文件:x0_i0_talent
位置:
// Try a given talent.
// This will only cast spells and feats if the targets do not already
// have the effects of those feats, and will funnel all talents
// through bkTalentFilter for a final check.
int TryTalent(talent tUse, object oTarget=OBJECT_SELF, object oCaster=OBJECT_SELF)
{
int nType = GetTypeFromTalent(tUse);
int nIndex = GetIdFromTalent(tUse);
if(nType == TALENT_TYPE_SPELL&& GetHasSpellEffect(nIndex, oTarget))
{
return FALSE;
}
else if(nType == TALENT_TYPE_FEAT && GetHasFeatEffect(nIndex, oTarget))
{
return FALSE;
}
// MODIFIED February 7 2003. Implicit else, implies success.
bkTalentFilter(tUse, OBJECT_SELF);
//MyPrintString("TryTalent Successful Exit");
return TRUE;
return FALSE;
}
虽然有注释(Implicit else),但这个"Implicit else"怎么看也明显不对。它在bkTalentFilter完成后无条件返回,后面的return FALSE永远不可能执行。并且,如果bkTalentFilter如果返回FALSE,即调用失败的话,那么在当前tryTalent返回TRUE将导致其在DeterminCombatRound中的后续备选talent全部被忽略,角色在当前round和之后的所有round中将一直发呆,直到下一次onPercepition或onAttacked被触发;而一旦之后每一次触发onPerception或onAttacked都陷在此处,角色将在战斗中作壁上观,看着你的PC人物被一群敌人围攻而丝毫不行动。
修正的方法也很简单,if语句把bkTalentFilter括起来即可:
// MODIFIED February 7 2003. Implicit else, implies success.
if(bkTalentFilter(tUse, OBJECT_SELF))
{
//MyPrintString("TryTalent Successful Exit");
return TRUE;
}
return FALSE;
BUG2:
文件:x0_inc_generic
位置:
if (GetTypeFromTalent(tUse) == TALENT_TYPE_FEAT)
{
//MyPrintString("Using feat: " + IntToString(iId));
nNotValid = TRUE;
if (VerifyCombatMeleeTalent(tUse, oTarget)
&& VerifyDisarm(tUse, oTarget))
{
//MyPrintString("combat melee & disarm OK");
nNotValid = FALSE;
}
}
这段的意思,是确保角色如果打算使用卸除武器时,卸除武器能被正确地使用。VerifyDisarm的作用是判断卸除武器在当前情况下是否合法:要么tUse和oTarget都正确(即可以正确使用),要么根本没有用卸除武器这个技能(即tUse不等于卸除武器)。单看这一句看不出什么问题,我们先往下看二十行左右:
// * BK: My talent was not appropriate to use
// * will attack this round instead
if (nNotValid)
{
//MyPrintString("Invalid talent, id: " + IntToString(iId)
// + ", type: " + IntToString(GetTypeFromTalent(tUse)));
if (bJustTest == FALSE)
WrapperActionAttack(oTarget);
}
else
{
if (bJustTest == FALSE)
ActionUseTalentOnObject(tFinal, oTarget);
return TRUE;
}
这下问题来了:如果前面一段中tUse不是disarm的话,bNotValid也会等于FALSE,那么在后面这段中,如果使用tUse的动机检查失败之后,直接进行物理攻击的逻辑将会错误地执行,将一个不是disarm的talent用到oTarget身上。当然如果tUse是一个可指定目标的talent,我们还可以装聋作哑,因为实际执行时什么也看不出来;但是假如tUse是一个selftarget的talent,ActionUseTalentOnObject(tFinal, oTarget)将卡死这个NPC,其后果同BUG1。
因此,我们必须在前面一段的检测中对检测条件作进一步的限制:
if (GetTypeFromTalent(tUse) == TALENT_TYPE_FEAT)
{
//MyPrintString("Using feat: " + IntToString(iId));
nNotValid = TRUE;
if (VerifyCombatMeleeTalent(tUse, oTarget)
&& FEAT_DISARM == GetIdFromTalent(tUse)
&& VerifyDisarm(tUse, oTarget))
{
//MyPrintString("combat melee & disarm OK");
nNotValid = FALSE;
}
}
以上。我表达能力有限,可能不太容易看懂。没关系,你只要知道它们在官方战役中不会导致任何问题,但如果你打算使用自己制作一些“牛×”或者“滥强”的佣兵时,这两个BUG可能会让你的这些勇猛的跟班全部罢工。根据我自己测试的结果,它们在魔武双修或者主动专长比较多的NPC上发生的几率相当大,尤其是圣武士这类角色(恶意猜测:为什么SOU不让我用paladin MM,大概官方早就知道这个BUG了)。另外说一句,第三方的AI补丁中,如果重用了官方的DeterminCombatRound接口,那么这两个BUG在补丁中也存在。
我把修改后的文件贴在下面。请注意这两个文件是中间脚本,没有main函数,是不能直接编译的。要使用它们,你需要找出所有调用了它们的脚本文件,并进行编译,然后把编译后的ncs文件覆盖原来的。强烈建议,最好是解出全部脚本文件,自己维护它们。这样做的好处是,游戏中所有与脚本相关的控制都在自己的掌握之下,并且可以很方便地把自己的一些新主意加进去(比如hook一下GetCasterLevel什么的)。当然,那是另一个故事了^_^。 请允许在百度无冬吧转帖。 没问题。转载请注明出处。如果有bioware账户的话最好能贴到官方论坛。我没有账户。 看来该直接试试SOLO一把 bioware服务器前不久遭受攻击挂掉了。不知道好了没,我的帐号已然忘了。。。有机会看能让朋友帖那里不。。。
页:
[1]