以插件的形式替换游戏中怪物列表,首先要了解怪物列表在哪个类 并且属性叫什么。
打开游戏的 KFMod 源码目录(前提安装了 killing floor SDK)
steam \ steamapps \ common \ KillingFloor \ KFMod \ Classes
可以找到 "游戏类型" 的源代码文件
KFGameType.uc
从面相对象的角度来看 一个文件就是一个类,在 KF1 中 KFGameType 就是默认的生存模式。
里面有5千行代码,根据一些官方文档线索 能确定怪物列表的属性
var array<MSquadsList> InitSquads;
var array<SpecialSquad> SpecialSquads;
var array<SpecialSquad> FinalSquads;
三个属性都是动态数组,分别表示 普通小队表、特殊小队表、最终小队表;
-
普通小队表:InitSquads 表示正常刷的小怪
-
特殊小队表:SpecialSquads 以大怪为准,一般出现在普通小队末尾
-
最终小队表:FinalSquads 当boss被打残时,出现的支援怪物
游戏每次是以小队为一个单位产怪,比如 有个小队是 4 clot 执行时就会一次产 4 个 clot;
游戏源码中对怪物小队的初始化比较麻烦,并且有更多的属性、逻辑设计;
当进入地图 准备界面时,这三张小队表已初始化完成,用插件可直接对其修改。
查看 SDK 提供的源码作为示例,可以研究其参与的属性 和 逻辑
路径:steam \ steamapps \ common \ KillingFloor \ KFMutators \ Classes
文件名:KFClotMut.uc
该插件表示将 普通小队 全替换成 clot,功能虽简单 但代码写的很健全。
KFClotMut.uc 源码如下:
class KFClotMut extends Mutator;
function PostBeginPlay()
{
SetTimer(0.1,False);
}
function Timer()
{
local KFGameType KF;
local byte i;
local class<KFMonster> MC;
local int MSquadLength;
KF = KFGameType(Level.Game);
MC = Class<KFMonster>(DynamicLoadObject(KF.GetEventClotClassName(),Class'Class'));
if ( KF!=None && MC!=None )
{
// 小队表长度 和 小队长度
KF.InitSquads.Length = 1;
MSquadLength = Min( 8, KF.MaxZombiesOnce );
// 上面已将 小队表长度设置为 1,接下来设置第一个小队 长度和元素
KF.InitSquads[0].MSquad.Length = MSquadLength;
for( i=0; i<MSquadLength; i++ )
KF.InitSquads[0].MSquad[i] = MC;
}
Destroy();
}
以上代码从 PostBeginPlay() 开始执行,里面写了一个计时器,表示地图加载后 0.1秒再执行;
计时器最后有一个 Destroy() 销毁对象自身,表示 插件执行完成后自己销毁 不要了。
计时器开头是局部变量声明,任何局部变量声明都是写在函数开头,如果写在逻辑代码之后 编译时会警告。
局部变量的声明 取决于逻辑如何写,我们先来看整体代码逻辑
-
获得关卡的游戏类型,转换成 生存模式(KFGameType)
-
根据类名 动态加载"类型",获得 clot 怪物类型(活动事件名,如 圣诞节、普通...)
-
验证都能获得 游戏类型和怪物类型
-
将普通小队列表 动态数组长度设置为 1,即 只有一种小队
-
设置第一种小队的长度
-
设置第一种小队中的每个元素为 clot 的类型
根据以上逻辑过程,可以分析出局部变量的作用
local KFGameType KF; // 接收关卡的游戏类型
local byte i; // 循环变量,遍历怪物小队
local class<KFMonster> MC; // 接收 clot 怪物类型(用父类变量接收子类)
local int MSquadLength; // 用于设置 怪物小队长度
小队设置分三层:怪物类,怪物小队,怪物小队表
即:KFMonster、MSquad、InitSquads
最底层的是 "怪物类",第二层是 "怪物数组" 叫小队,第三层是 "怪物数组的数组" 即小队表。
最底层的怪物类是一个具体的类文件,而游戏类型中 小队的写法很特别
// 小队结构体
struct MSquadsList
{
// 小队是个怪物数组
var array< class<KFMonster> > MSquad;
};
// 普通小队表
var array<MSquadsList> InitSquads;
游戏类中把 "怪物数组" 叫做 MSquad,然后再做成结构体 叫做 MSquadsList;
最终把 怪物数组 再做成数组,就成了 InitSquads
所谓 InitSquads 普通小队表就是:怪物数组的数组,即 怪物数组_结构体 的数组。
与 普通小队结构体 有一点区别,特殊小队表、最终小队表 的写法如下
// 特殊小队结构体
struct SpecialSquad
{
// 怪物名列表
var array<string> ZedClass;
var array<int> NumZeds;
};
// 特殊小队表
var array<SpecialSquad> SpecialSquads;
// 最终小队表
var array<SpecialSquad> FinalSquads;
修改 "最终小队" 的写法比 "普通小队" 少了一个动态加载类的步骤
// 最终小队,双重循环 遍历小队表和小队元素
for (i=0; i<KF.FinalSquads.Length; i++)
{
for (j=0; j<KF.FinalSquads[i].ZedClass.Length; j++)
{
if (KF.FinalSquads[i].ZedClass[j] == "KFChar.ZEDTOREPLACE")
KF.FinalSquads[i].ZedClass[j]= "PACKAGENAME.NEWZED";
}
}
之前的普通小队表 是被改成了一个小队,如果存在多个小队 还是要用到双循环