Left 4 Dead 2

Left 4 Dead 2

评价数不足
[Stripper:Source]如何修改地图
由 您🛰 制作
一份与如何用 Stripper:Source 修改地图有关的指南,包含将 Stripper 地图移植为 VScript 格式的说明。
   
奖励
收藏
已收藏
取消收藏
添加实体(add)
首先用控制台命令 "map" 进入地图,例如 map c1m1_hotel。

如果你不知道你的地图的 "c1m1_hotel" 是什么,那么就先进入一下这张地图,再用 status 控制台命令来获取地图代码。



在控制台中使用 sv_cheats 1;director_stop;god 1 以确保在地图编辑过程中不会受到干扰。



用 /admin 打开管理员菜单,Object Spawner 会在管理员菜单中添加用于生成实体的选项。



你需要先用(3)生成物件来生成实体,再用(4)位移或旋转生成的实体,最后(1)保存实体。


其中:
  • 动态物件是指可以被坦克打飞、被 Boomer 炸飞、被 Charger 撞开、甚至被玩家推开(取决于模型的 mass),且具有物理特效(布娃娃之类的东西)的实体类型。虽然可以玩一些花活[forums.alliedmods.net],但该类实体最好只用来生成汽车。
  • 使用【固定物件】来生成道路或障碍物,例如浮空的栅栏或者堵路的路障之类的东西;
    固定物件(prop_dynamic)和动态物件(prop_physics)之间的主要区别。
  • 【穿透物件】与【固定物件】相似,但是没有碰撞体积;
  • 【物品物件】则是武器、投掷物、补给品之类的东西,包括弹药升级包、电击器和弹药堆。

生成实体并保存实体后,保存的实体(配置文件)位于 \Left 4 Dead 2\left4dead2\addons\stripper\maps\ 文件夹内,其名称与地图代码相同。实体信息以增量的形式保存,所以你的第二次保存并不会覆盖第一次保存时所生成的代码。

保存后的实体信息类似于这样:


solid 是该实体的碰撞属性:在为 0 时实体将没有碰撞体积(穿透物件),在为 6 时则具有碰撞体积(固定物件);

classname 为该实体的类型:固定物件和穿透物件为 prop_dynamic,而动态物件则是 prop_physics

origin 是该实体的位置;
angles 是该实体的角度:该参数的各个值最好为 45 的倍数,如 45、90、135。
model 则是该实体所使用的模型:实体的 mass(重量)、碰撞体积和它的外观由该参数所指向的模型决定。
  • 它也可以指定自定义模型,但是如果其他玩家的电脑上找不到该模型,那么红色的 error 将会作为这些玩家所看到的模型
我们可以通过手动添加属性来控制实体的某些特性:


例如,添加 "rendermode" "10" 可以将实体转换成隐形墙[forums.alliedmods.net]
添加 "disableshadows" "1" 则禁用与其相关的阴影效果。这会产生视觉瑕疵,但会提高客户端的FPS。

  • 上图:用 "disableshadows" "1" 禁用实体阴影后的效果;
  • 下图:没有使用 "disableshadows" "1" 禁用阴影的实体,会根据附近的光源渲染阴影。这在某些情况下会急剧降低客户端的 FPS,特别是在实体数量较多的情况下。
可以在 Valve Developer Community 上找到 classname(实体类型)所支持的属性,如prop_dynamicprop_physics
输入(Input)
现在,瞄准你刚刚用 Stripper 生成的实体,并在控制台输入:
ent_fire !picker kill
prop_dynamic 和 prop_physics 类实体接受被称作输入(Inputs)的各种命令,而这些命令的功能各不相同(详见 Valve Developer Community 上的有关页面);例如,kill 的功能是移除实体:

  • 用 ent_fire !picker kill 移除 c2m4 的栅栏围墙。
然而,地图上占多数的静态实体(prop_static)并不能用 kill 移除;此外,此类静态实体不接受任何包括 kill 在内的输入(input)。

你可以在瞄准对象后用 /ent 来获取包括实体类型(classname)在内的实体信息(需要 Dev Cmds);不过,/ent 并不能获取所有实体的信息。
通过 Stripper 来移除实体
与控制台命令有关的事实表明,实体可接受各种各样的输入,如 kill(移除实体)。现在接下来我们要将控制台命令“移植”到 Stripper 的 cfg 文件中。

以移除黑暗狂欢节第四关的栅栏围墙为例,首先我们需要进入这张图,然后用 /admin 菜单来生成一个空的 .cfg 配置文件,然后在这个文件里创建一个 logic_auto 实体:
  • add:
  • {
  • "classname" "logic_auto"
  • "OnMapSpawn" "props_coop,kill,,0,-1"
  • }
OnMapSpawn 在地图加载完成后执行,而 props_coop 则是目标实体的 targetname。
* props_coop,kill 的功能与 ent_fire props_coop kill 相同,均意味着【向名称为 props_coop 的所有实体提供 kill 输入】。

当地图重新加载时,OnMapSpawn 会向名为 props_coop 的所有实体提供 kill 的输入,从而移除具有 props_coop 的 targetname 的栅栏围墙。

移除栅栏围墙后,仍有一堵隐形墙挡路。虽然 /ent 并不能获得该实体的信息,不过可以用 ent_fire !picker addoutput "targetname test01" 和 ent_dump test01,或是 ent_dump !picker 来获取该实体的信息。

移除隐形墙

普通的隐形墙分为五种类型:
  1. 第一种是 env_physics_blocker;
  2. 第二种是 env_player_blocker;
  3. 第三种是 func_simpleladder,该类实体也被用作梯子;
  4. 第四种是加了 "rendermode" "10" 的 prop_dynamic;
  5. 第五种则是 func_brush 笔刷实体,该类实体无法通过 /ent 获取信息 。
首次使用 ent_dump 时,你可能会看到一个 classname 为 info_remarkable 的实体,该实体并非阻挡我们的隐形墙,因此需要用 ent_fire info_remarkable kill 删除它和其他类似的干扰因素。

反复获取实体信息并通过 ent_fire 删除这些实体,直到我们找到并删除了所有阻挡道路的实体。


控制台在这个过程中所打印的实体信息表明,有一个 func_simpleladder 和一个 func_brush 阻挡了道路。
* 如果你无法通过这种方法来获得挡路的实体信息,那么可以用 /find func_brush 查找附近的 func_brush 类实体,然后根据此命令给出的 origin 在 stripper_dump 导出的实体列表中搜索相关实体。
* 注意:大多数隐形墙都不是 func_brush 类实体。请考虑使用这个mod来移除隐形墙。

重新加载地图并在控制台输入 stripper_dump 以导出地图上可交互的实体的信息;我们需要在导出的实体信息中找到它们的 targetname,以通过 OnMapSpawn 的 kill 来移除这堵墙。


搜索 hammerid(实体的惟一ID)可以找到这两个实体。对于那个 func_brush,我们可以用 kill 来将其从地图上抹去。

然而,由于 func_simpleladder 没有 targetname,所以我们没办法用 OnMapSpawn 的 kill 来删除。

在这种情况下,我们需要使用 stripper 的另一个功能来删除实体。
通过 filter 来删除实体
Stripper 的 filter 可以用来删除具有 hammerid 的实体;虽然它不能移除由 TLS 更新所添加的各种实体。[forums.alliedmods.net]
在 c2m4_barns.cfg 的底部添加以下代码:
  • filter:
    {
    "hammerid" "2018662"
    }
    {
    "hammerid" "1864501"
    }

保存并重新载入地图。符合条件(由 hammerid 指定)的实体将会被 Stripper 的 filter 移除。
* 如果你想要继续在 filter 下面添加实体,还需要再加一行 add:

现在那个位置上应该没有恼人的空气墙堵路了,不过这还不够。

在控制台输入 nav_edit 1,你会看到那个位置上有个内嵌天蓝色方框的方格,而这个蓝框意味着机器人不会尝试穿过该区域(在“心理”上被阻挡)

func_nav_blocker 类实体通过向 navmesh area(s) 添加天蓝色方格来阻止常见感染者、特殊感染者和幸存者机器人进入该区域。

瞄准内嵌了天蓝色方框的方格,使用 ent_dump !picker 找到引起这个问题的 func_nav_blocker,再用 filter 将其从地图上移除:

制作你的第一个事件(event):按钮、发光道具与视觉和听觉特效
filter 可以完成一些相对比较简单的事件修改,例如,你可以用它来删除 c5m2(教区第二关)警报站的 CEDA 车门和警报站的按钮,从而禁用与其相关的事件(event)。

我们也可以用 add 来向地图添加类似于警报站站点的新事件。

首先进入死亡中心的第一关(c1m1_hotel),并用 /admin 生成一个空的 .cfg 文件,然后向该文件添加以下代码:
  • add:
    {
    "solid" "6"
    "origin" "390 5635 2818"
    "angles" "0 180 0"
    "model" "models/props_fortifications/concrete_barrier01.mdl"
    "classname" "prop_dynamic"
    }
    {
    "solid" "6"
    "origin" "402 5579 2909"
    "angles" "0 0 0"
    "model" "models/props_unique/generator_switch_01.mdl"
    "classname" "prop_dynamic"
    }

我们的目标是在左边的按钮被按下后移除右边的那堵墙。

prop_dynamic 并不具有按钮的功能;相反,按钮的功能实际上是由另外两种抽象实体来实现的,它们是:
首先,用 noclip 控制台命令启用穿墙模式,再移动到左边那个按钮的中心点外侧,并使用 /pos 获取脚下的坐标点。

再创建一个 func_button 实体,其 origin(坐标点)使用通过 /pos 获取的坐标点:
  • add:
    {
    "classname" "func_button"
    "origin" "..."
    "spawnflags" "1024"
    }
如果坐标点位于实体内部,那么玩家将没办法摸到这个按钮;同理,另一个常见的初学者错误是把按钮拍到地里,因为通过 /pos 获得的坐标点的高度位于脚下,而不是位于眼部。
* 我刚学的时候花了三个小时才意识到为什么按钮没用,因为我实际上把它拍地里了。
* 安装这个插件以在第一人称模式下显示腿部:https://forums.alliedmods.net/showthread.php?p=2536139
* 安装这个插件以使用 /3rd 切换到第三人称视角:https://forums.alliedmods.net/showthread.php?p=1712767

其中,“spawnflags“ ”1024“的意思是按E可以激活这个按钮。

然后我们需要给右边那堵墙一个 targetname 才能用 kill 将其移除。例如 "targetname" "c1m1_barricade_test01"。

为了让这个按钮在按下时移除右边那堵墙,还需要设计这个按钮在被按下时触发什么样的输出(outputs)
添加输出的过程与 logic_auto 的 OnMapSpawn 类似:首先添加一个 "OnPressed",之后再向其添加能够删除实体的代码:"c1m1_barricade_test01,kill,,0,-1"



玩家们也需要知道左边这个按钮是可以按的,为此我们需要给左边那个按钮(prop_dynamic)加一点特效:
glowcolor 是发光时的颜色(rgb);glowrange 指玩家离该实体多近时发光;glowstate 则是物体发光的状态(3 为开始发光,0 为不发光)。


玩家按下按钮后 prop_dynamic 的视觉特效应该被禁用。为了禁用这个实体的视觉特效,为其添加 targetname 是必须的;func_button 按钮需要 stopglowing 的 OnPressed 输出,才能在被按下时禁用相关实体的 glowing。
* 可以用 startglowing 输入来启用 glowstate 为 0 的实体的发光效果;stopglowing 则是用来关闭发光效果的输入。

为了让这个按钮在被按下时播放音效(音效也是另一种抽象实体产生的;它的 origin 与 prop_dynamic 按钮的位置相同),添加:
  • add:
    {
    "classname" "ambient_generic"
    "message" "ambient/atmosphere/arena_lights_off_01.wav"
    "pitch" "100"
    "health" "10"
    "pitchstart" "100"
    "spawnflags" "48"
    "radius" "5555"
    "targetname" "c1m1_barricade_test03"
    "origin" "402.00 5579.00 2909.00"
    }
* 可以在 Valve Developer Community 上找到与 classname(实体类型)有关的各种细节。

在 OnPressed 上添加与 PlaySound 有关的代码,以使按钮在被按下时命令 ambient_generic 类实体播放指定音效。

添加路障被摧毁时的视觉特效:
  • add:
    {
    "classname" "info_particle_system"
    "effect_name" "gas_explosion_main"
    "targetname" "sky_c1m3_CME_tank_particles"
    "origin" "390 5635 2818"
    }
* 用 Start 输入来触发该视觉特效;还需要在 0.8 秒后用 stop 禁用视觉特效。那个0是延迟N秒执行的意思。

和路障爆炸时的声音:
  • add:
    {
    "classname" "ambient_generic"
    "health" "10"
    "message" "weapons/hegrenade/explode5.wav"
    "pitch" "100"
    "pitchstart" "100"
    "radius" "7000"
    "spawnflags" "49"
    "targetname" "sky_c1m3_CME_tank_sound"
    "origin" "390 5635 2818"
    }

按下按钮后路障会立即爆炸,这在感觉上可能有点违和;为此,我们还需要推迟路障爆炸的时机:
  • add:
    {
    "classname" "func_button"
    "origin" "411.181060 5580.661132 2898.300292"
    "spawnflags" "1024"
    "OnPressed" "c1m1_barricade_test01,kill,,2,-1"
    "OnPressed" "c1m1_barricade_test02,stopglowing,,0,-1"
    "OnPressed" "c1m1_barricade_test03,PlaySound,,0,-1"
    "OnPressed" "sky_c1m3_CME_tank_particles,start,,2,-1"
    "OnPressed" "sky_c1m3_CME_tank_particles,stop,,2.8,-1"
    "OnPressed" "sky_c1m3_CME_tank_sound,PlaySound,,2,-1"
    }
其中,【2】和【2.8】是指推迟 N 秒后执行,而【0】则为立即执行。例如,【kill,,0】会立即删除实体,而【kill,,2】则意味着两秒后删除实体。

整个事件在做完之后大概是这个样子的:
怎么样,学会了吗?
  • 如果你想要在路障爆炸后召唤一波尸潮,可以用 ForcePanicEvent 来刺激名为 director 或 @director 的实体;该实体应该可以在 stripper_dump 导出的实体信息中找到。
trigger_once 和 modify: 移除恼人的 TLS 实体
trigger_once 是个类似于边界框的玩意,走进里面的玩家(也可以是别的东西,由 filtername 指定)会触发 OnTrigger 的输出。

例如,如果你想要删掉某个实体,那么在使用 trigger_once 会在符合条件的对象进入其边界框内时触发 OnTrigger,从而删除目标实体。这类似于 OnPressed。

这里有一些示例代码:
https://forums.alliedmods.net/showpost.php?p=2799367&postcount=181

trigger_once 的触发范围由 logic_auto 的 OnMapSpawn 的 AddOutput 添加,其位置则由 origin 指定。

TLS 往地图里添加了一些恼人的隐形墙,而且这些隐形墙没办法用 Stripper 的 filter 删除,因为 TLS 所添加的实体与 Stripper 之间存在加载顺序方面的问题。

TLS 实体在 Stripper 执行完毕后才会添加实体,而 Stripper 不能删除“不存在的”、还没生成的实体,因此无法移除 TLS 所添加的实体;相反,trigger_once 在实体生成后执行,因此可以避免与此有关的加载顺序问题。

或者,也可以简单地通过修改安全屋的门来推迟 kill 的执行顺序;在示例代码中:
  • match: 指定将要修改的实体,由 hammerid 或类似的属性指定;
  • insert: 将属性及其键值以插入的形式添加到该实体内,例如添加输出(OnFullyOpen)及其作用(如kill某个实体);
  • replace: 替换目标实体的某个属性,例如 “targetname“ "new_targetname" 会将目标实体的targetname 替换为指定的键值;
  • delete: 将目标实体的某个属性删除。
可以用 modify 修改安全门,然后将 kill 某物的代码以 insert 的形式嵌入到安全门的 OnFullyOpen 中;这样一来当安全门被完全打开时,相关代码就会被执行。

虽然这种方法挺方便的,但是这在大多数地图的第一关上是不可能实现的,因为出生点不是安全屋。

详见:https://www.bailopan.net/stripper/#newconf
向地图内添加可供幸存者攀爬的梯子
修改导航(navmesh)以使机器人能够识别并绕过添加的实体,或是在添加的实体上行走
效果预览:
输入 nav_edit 1 以启用导航网络的编辑模式,再用 nav_save 保存导航网络,这会让我们在 TLS 导航网络的基础上进行编辑。

进入 \Left 4 Dead 2\update\maps\,重命名当前地图的 .nav 文件以使 TLS 的 .nav 文件失效,因为该文件会覆盖我们所修改并保存的导航网络[forums.alliedmods.net]
* 例如,将 c1m1_hotel.nav 重命名为 c1m1_hotel#.nav

重新载入地图,再用 director_stop 禁用导演生成特感和小僵尸、ent_fire infected kill 移除所有的小僵尸、kick ... 踢掉所有的机器人,以防此类对象在导航网络的编辑过程中引起游戏崩溃。
* kick 可能是大小写敏感的,虽然我对此不太确定:如果 kick nick 不会起作用,那就试试 kick Nick。

通过 nav_edit 1 进入导航网络编辑模式。

绑定 nav_split 到小键盘的 0 键
  • bind "kp_ins" "nav_split";
绑定 nav_delete 到 del 键
  • bind "del" "nav_delete";
绑定 nav_save 到 F5
  • bind "f5" "nav_save";
绑定 nav_mark 到 '
  • bind "'" "nav_mark";
绑定 nav_connect 到 /
  • bind "/" "nav_connect";

根据 navmesh 上的白线,按小键盘 0 分割导航网络区域,再按 del 键以删除实体下方的导航网络,最后按 F5 保存更改。这个过程大概是这个样子的:
如果实体的角度(angles)不是 45 的倍数,那么实体与导航网络之间会有不对齐的问题。

为了让小僵尸(还有躺氪)能够攀爬实体,还需要:
  1. bind "HOME" "nav_create_area_at_feet 1";
  2. bind "END" "nav_create_area_at_feet 0";
  3. bind "[" "nav_begin_area";
  4. bind "]" "nav_end_area";

按 HOME 键将 nav_edit 的光标定位点(高度)改成脚下,因为 nav_edit 的光标无法识别 prop_dynamic。

移动到实体上方,瞄准实体的左上角按 { 键,再瞄准实体顶部的右下角按 } 键,以创建导航网络区域。
  • 由于格式问题,我打不出来方括号,所以用花括号替代一下
然后,按 END 键以将光标定位点改为准星。

瞄准我们刚刚创建的导航区域,按 ' 键选中它,再瞄准下方周边的导航区域,按 / 键以创建单向连接,这样一来位于顶部导航区域的小僵尸会根据这条线跳到下面;反之亦然,我们还需要创建从下到上的导航连接。
*如果你不小心创建了错误的连接,则需要使用 nav_disconnect 移除连接。

最后,用 z_spawn 生成几个小僵尸来测试一下导航网络能不能被机器人所识别。在继续编辑导航网络之前别忘了用 ent_fire infected kill 移除小僵尸,以免它们引起游戏崩溃。

例如:
梯子需要用 nav_build_ladder 来创建导航网络,然后用 ' 和 / 连接其他的导航网络。此外,prop_physics 类实体(和其他类似的实体)的编辑过程与 prop_dynamic 有所不同,详见:https://forums.alliedmods.net/showpost.php?p=2813452&postcount=225

修改完成后,还需要使用 nav_analyze 来让游戏引擎分析整个导航网络。

接下来还需要手动确定导航网络是否存在问题;如果导航网络存在问题,那么 director 将不会生成特感和小僵尸。
首先,在控制台输入:
z_debug 1;z_show_flow_delta 2;z_show_flow_distance 1

如果安全屋内的导航网络显示 Flow: -9999 / -9999 则表明导航网络存在问题:

移动到关底的安全屋,你会发现 Flow 中的数字有所不同:

往回走,直到找到离“正常的导航网络”最近的 -9999/-9999 的导航网络区域。

如果导航网络之间的连接存在问题,则用 ‘(nav_mark)和 /(nav_connect)建立彼此之间的连接;
如果导航网络之间缺少导航网络,则先用 '(nav_mark)标记一个导航区域,再瞄准另一个导航区域,用 nav_splice 填充它们之间的空白;
如果导航网络的问题是由 PLAYER_CLIP 的导航网络属性引起的,则需要瞄准导航区域并输入 clear_attribute PLAYERCLIP 以移除该属性。

* PLAYER_CLIP & BLOCKED_SURVIVOR 的一个例子。

修补导航网络的连接/空缺/属性问题之后,再用 nav_analyze 刷新导航网络;如果起始安全屋内的导航区域还是 -9999 / -9999 的话,则需要重复这个过程,直到起始安全屋内的导航区域没有此类问题。

* 注:Flow: -9999 / -9999 或类似的、不生成小僵尸的导航问题,也可能是由 func_nav_blocker 被移除时所引起的罕见问题[forums.alliedmods.net]

延伸阅读:
https://psteamcommunity.yuanyoumao.com/sharedfiles/filedetails/?id=1981189248
https://developer.valvesoftware.com/wiki/Nav_Mesh/Console_commands#Editing_commands
https://developer.valvesoftware.com/wiki/Sorting_out_navigation_flow
(高级用户)将 Stripper 的配置文件移植为 VScript
注:最好先用 Stripper 改出一张包含事件和梯子的新图出来,再考虑将 Stripper 移植到 VScript。我不会在这节解释先前已经解释过的 Stripper 基础知识。

从这里下载用于参考的示例代码:https://forums.alliedmods.net/showpost.php?p=2816742&postcount=230

在 \Left 4 Dead 2\left4dead2\scripts\vscripts\ 创建一个名为 director_base_addon.nut 的脚本文件,该脚本会在地图加载时运行(无论加载的地图是什么)。

if(whatisthemapname == "c11m1_greenhouse") 用于检测地图名称;如果(根据地图名称)检测到玩家在玩我们将要修改的地图,则运行生成实体的代码。

由于 director_base_addon.nut 及其所运行的脚本文件会执行两次(参见控制台打印的消息),因此还需要做重复检测(通过生成实体并检测实体是否存在,来判断脚本之前有没有执行过)。


其中:
  • if ( Entities.FindByName(null,"c11m1_script") == null) 用于检测名为 c11m1_script 的实体是否存在;
  • 如果该实体不存在(== null),则创建一个名为 c11m1_script 的实体(该实体用于执行生成其他实体的脚本)
  • 如果该实体已存在,则不进行任何操作:else {}。

IncludeScript("modified_dead_air_c11m1_ladder_controller.nut", this); 则用于生成梯子或修改梯子的属性。
必须将与梯子有关的 .nut 脚本放在 director_base_addon.nut 内执行,要不然会有导航网络问题。

VScript: add
现在,打开 modified_dead_air_c11m1.nut 这一用于生成实体的脚本:



Stripper 的 add 在 VScript 是用 SpawnEntityFromTable 来实现的;它看起来跟 Stripper 的实体格式很像,但却有所不同:
origin 和 angles 使用 Vector(123,456,789);
一般的参数的键值没有双引号(“”),但 model 和 targetname 之类的参数的键值需要使用双引号。


看起来 string 类的键值需要用到双引号。

用这个插件来生成 VScript 实体:https://forums.alliedmods.net/showpost.php?p=2814521&postcount=228

SpawnEntityFromTable 似乎不支持向实体添加 OnTrigger 之类的输出。下载 modified the parish vscript(experimental)[forums.alliedmods.net],打开 modified_the_parish_c5m1.nut 并跳到底部。

用 EntityOutputs.AddOutput 向 logic_relay / logic_auto 之类的实体添加 OnTrigger / OnMapSpawn 输出(需要实体具有 targetname,如 relay_intro_survivor_cameras)。



VScript: modify
用 EntityOutputs.AddOutput 或 EntFire 来 modify 实体的属性:



如果实体类型不支持 EntFire,或没有 targetname,则需要删除该实体,并通过 SpawnEntityFromTable 重建具有相关属性的实体。
VScript: filter
有三种方法来删除实体:1)EntFire;2)CBaseEntity.Kill();3)trigger_remove

1)EntFire:
EntFire("targetname","kill");
适用于有 targetname 的实体。

2)CBaseEntity.Kill():
适用于没有 targetname 的实体,但前提是实体类型为 CBaseEntity。门之类的实体可能没法用这种方法来删除。



用 Entities.FindByClassnameNearest 获取离 Vector 最近的 prop_physics(或其他类型的实体);“3” 指定了搜索范围的大小。

== null 是实体检测。如果没有找到实体,则执行 {} 内的代码。没有代码意味着不执行任何操作。

else
{
...
}

如果实体不为空(null),则创建一个名为 prop_physics_01 的变量,并指定该变量为通过 Entities.FindByClassnameNearest 获取的实体。

通过变量名称向实体发送 .Kill() 的命令,从而删除与变量有关的实体。

3)trigger_remove:
自己看:https://forums.alliedmods.net/showpost.php?p=2806050&postcount=193



直接复制然后拿来用就行(别忘了改 origin)。
梯子
用这个插件生成梯子:https://forums.alliedmods.net/showpost.php?p=2814535&postcount=229



由于 SpawnEntityFromTable 不支持向梯子添加属性,所以需要用 EntFire 的 addoutput 来添加 normal.x / normal.y / normal.z



VScript 没有与 hammerid 有关的函数(至少我没找到),且梯子没有 targetname,同时 FindByClassnameNearest 对其不起作用,因此无法通过 addoutput 来修改其 team 参数的键值以允许 survivor team 攀爬梯子,所以只能先删除梯子,再原地重建具有 team 0 的梯子,来允许 survivors 攀爬梯子。
由于删除梯子的代码根据梯子的 model 查找并删除梯子(通过 .Kill),所以我们需要把删除梯子的代码放在 .nut 文件的顶部。

生成梯子的代码必须在 director_base_addon.nut 里执行,要不然会有导航网络方面上的问题。
IncludeScript("modified_dead_air_c11m1_ladder_controller.nut", this);

用 VScript 的好处是,专用服务器和本地服务器不依赖 Stripper 及其前置,也不需要使用 -insecure;换言之,它可以在启用 VAC 的服务器上使用。

在本地服务器上使用时,由于不使用 -insecure,因此主机(host)可以加入启用 VAC 的服务器,无需移除不存在的 -insecure 并为此重启游戏。

此外,VScript 的 .nut 脚本文件可以与 .nav 和 .lmp 一起打包成单独的 .vpk 使用;也可以将打包的 .vpk 发到 Workshop 上,例如:
https://psteamcommunity.yuanyoumao.com/sharedfiles/filedetails/?id=3113799031

使用 VScript 的好处是可以推迟实体生成的时机,从而缓解与 ED_Alloc: no free edicts 有关的问题:先删除不必要的 vanilla 实体,再通过 SpawnEntityFromTable 生成实体,从而避免或缓解生成大量实体时所引起的游戏崩溃。

使用 Source 引擎的某些游戏也支持 VScript,如 Gmod,因此 L4D2 的 VScript 脚本理论上也可以在其他游戏里使用。

例如,也许可以利用 VScript 来在 Gmod 上的 L4D2 地图中生成敌人(及其他各类实体);虽然我没这么试过,不过理论上是可行的。

看完了吗