L4D2 Level Design/Scavenge Finale/zh

From Valve Developer Community
Jump to: navigation, search

本页面由大康翻译于2021年9月25日,相当于原文有小改动(例如输出)并修复了一些内容,但没有删减内容。部分内容由机器翻译。


求生之路2 清道夫结局需要一个结局区域,感染者会在此区域试图阻止生还者捡起散落在地图上的汽油桶,以及用它们给逃生载具加油。对抗模式的得分取决于在一个回合中给逃生载具加了油的汽油桶数量。与标准结局相比,清道夫结局需要一些额外的实体和脚本,才可以正常进行。

组成内容

c1m4_atrium 的反编译地图将作为案例。另一张地图是 c6m3_port,其中还有求生之路1的生还者。这是与清道夫结局相关的组成内容的粗略列表:

Note.png注意:c1m4_atrium_finale.nut 在结局开始时会自动加载。它遵循命名规则 scripts/vscripts/<地图名>_finale.nut

地图

一般来说,你需要在生还者和汽油桶的空间之间设置障碍物。障碍物通常是加油口(point_prop_use_target)和汽油桶之间的距离/地形。

武器

考虑到生还者在开始终局时需要武器,他们仍然需要能够为面前的挑战做好准备,所以路上还应该有武器和物品,以便生还者可以坚持一段时间。玩你的地图的人会有很多不同的方式,例如,喜欢花时间游玩的人与快速通关的人相比。

环境

这些只是建议:

  • 制作多条路径,让生还者可以进出建筑物以获取补给品,甚至可以在终局中使用汽油桶(译者注:应该不是灌油用的汽油)。
  • 请记住,玩家并不总是想要走狭窄的走廊,而是大型开放区域,例如生还者可以走在街道上,但会被附近建筑物中生成的感染者或从屋顶攻击他们的特殊感染者攻击。
  • 汽油桶和灌油口之间的距离是两队优势的平衡。

清道夫结局实体

  • 你需要一个 trigger_finale 以便生还者可以开始结局。在该实体中,你将找到各种选项(即键值)。其中之一是结局类型。它的默认设置为“Standard”(标准)。你要把它设置为“Scavenge”(清道夫)。现在保持 Use Delay 键值不变,但将 First Use Delay 设置为 5 秒。这可以让生还者通过播放音频知道他们在做什么。(这不是必需的,但确实有帮助)
  • 你要放置一个 game_scavenge_progress_display 实体。在它的属性界面上,你可以将(加油的)最大值更改为你想要的任何值。出于本教程的目的,我们将其设为 8。然后将其目标名(Name, 不是 Classname)设置为唯一的名称。在本教程里,我们设置为 scav_progshower。接下来,你需要一个 math_counter 实体。将其目标名设置为 scav_counter,然后将其 Initial Value (初始值)保留为 0,最大值与你在 game_scavenge_progress_display 中设置的最大值相同。在本教程中,我们将其设置为 8。将其最小值保留为 0。
  • 你需要确定要灌油的位置。模型可以是你选择的任何模型。在本教程中,我们将使用灌油口模型 radio_generator_fillup.mdl。你需要放置一个 prop_dynamic 实体,然后在模型浏览器中查找 radio_generator_fillup。将其放置在距离玩家可以操作的范围内(玩家高度约为 64 个单位,因此最好的高度为 45 到 50 个单位)。将其轮廓颜色设置为你选择的任何颜色。
  • 你需要在加油口上放置一个 point_prop_use_target 实体。

Hammer scavtut usetarget.jpg

转到你的加油口模型实体并打开其属性并将其目标名设置为 scav_nozzle。然后返回你的 point_prop_use_target 并打开属性界面切换到输出菜单。添加具有以下设置的新输出:

  My Output Target Entity Target Input Parameter Delay Only Once
Io11.png OnUseFinished scav_counter Add +1 (或者是1) 0.00 No

这意味着当玩家用汽油桶在加油口加了油后,point_prop_use_target 将向 scav_counter 添加一个值。

Hammer scavtut output.jpeg

汽油桶

在实体列表中查找 weapon_scavenge_item_spawn 实体。将它放置于地图并打开其属性界面。将其目标名更改为 scav_gascans。 然后回到你的 trigger_finale 实体,打开它的属性界面,转到输出选项卡并单击添加(Add)

输入以下内容:

  My Output Target Entity Target Input Parameter Delay Only Once
Io11.png UseStart scav_gascans TurnGlowsOn <none> 0.00 No

(以上意味着当结局开始时,所有名为 scav_gascans 的实体的轮廓会开始发光。)


添加另一个输出,如下所示:

  My Output Target Entity Target Input Parameter Delay Only Once
Io11.png UseStart scav_nozzle StartGlowing <none> 0.00 No

(与上面类似,让加油口的模型的轮廓发光。)

接下来放置实体 logic_auto 并转到其属性的输出选项卡,然后单击 Add。

添加以下输出:

  My Output Target Entity Target Input Parameter Delay Only Once
Io11.png OnMapSpawn scav_gascans TurnGlowsOff <none> 0.00 No
Io11.png OnMapSpawn scav_nozzle StopGlowing <none> 0.00 No
Io11.png OnMapSpawn scav_progshower TurnOff <none> 0.00 No

(以上两个输出确保它们在我们开始结局之前不会发光,最后的输出可确保加油进度的 HUD 在我们启用之前不会处于活动状态。)

Hammer scavtut autooutput.jpeg

救援载具

这通常是一个带有动画的 prop_dynamic,但实际上它可以是任何东西。


出于本教程的目的,我们仅将 C130 用于 prop_dynamic 实体作为救援载具。 转到纹理浏览器并查找"trigger"。它看起来像这样:

Hammer scavtut triggertex.jpeg

制作一个和你的逃离载具一样大的固体,空间大小是 C130 的内部空间。按 Enter 键创建它,然后按 Ctrl+T 转换为实体。在 Classname 下输入 trigger_multiple 并将其目标名改为 escape_trigger。然后将 Entire Team Number 设置为 Survivor,并将 Start Disabled 设置为 Yes。然后关闭它的属性界面。


返回你的 math_counter 并转到输出选项卡。添加如下输出:

  My Output Target Entity Target Input Parameter Delay Only Once
Io11.png OnHitMax Scav_finale_starter FinaleEscapeVehicleReadyForSurvivors <none> 0.00 No
Io11.png OnHitMax escape_trigger Enable <none> 0.00 Yes
Io11.png OnHitMax scav_gascans TurnGlowsOff <none> 0.00 Yes
Io11.png OnHitMax scav_usenozzle Kill <none> 0.00 Yes
Io11.png OnHitMax scav_nozzle StopGlowing <none> 0.00 Yes

Hammer scavtut counteroutputs.jpeg

然后返回到你的 trigger_finale 并将其目标名更改为 Scav_finale_starter。

安全地带

你需要在地图范围之外制作一个带有 4 个info_survivor_position实体的区域。在实体属性界面中,将第一个 info_survivor_position 的 Order 更改为 1,将第二个更改为 2,依此类推,直到第四个。然后将它们的目标名更改为你放置它们的任何顺序。(例如:survivor_pos1、survivor_pos2 等)

Warning.png警告:有时这块区域在地图外生成时会出现问题,建议不要这样做。

转到你的 trigger_escape(你在救援载具中制作的 trigger 笔刷)并添加具有以下内容的输出:

  My Output Target Entity Target Input Parameter Delay Only Once
Io11.png OnEntireTeamStartTouch Scav_finale_starter FinaleEscapeForceSurvivorPositions <none> 0.00 No
Io11.png OnEntireTeamStartTouch Scav_finale_starter FinaleEscapeFinished <none> 0.00 No

Hammer scavtut escapetrgoutputs.jpeg

然后返回到纹理浏览器。寻找 Clip (空气墙);它看起来像这样:

Hammer scavtut cliptex.jpeg

使用应用了此纹理的固体将 info_survivor_position 的位置包围在地图范围之外,然后按 Enter 键以创建它。然后单击 Ctrl+T 转换为实体。将 Classname 更改为 func_brush。将 Solid BSP 键值设置为 Yes,然后把 Solidity 键值设置为 Always Solid。最后,将目标名设置为 clip_scavend,点击应用并关闭其属性界面。

返回你的 trigger_multiple 添加输出:

  My Output Target Entity Target Input Parameter Delay Only Once
Io11.png OnEntireTeamStartTouch clip_scavend Kill <none> 0.00 No

用 nodraw 纹理的固体覆盖逃离载具的地板,这样你就可以在上面行走并在上面创建导航网格。

然后放置 point_veiwcontrol_multiplayer 实体,作为救援载具逃离动画的相机。然后打开其属性并将其命名为 outro_cam。然后放置 2 个 env_fade 实体,将第一个命名为 fade1,并将其 Hold Time (保持时间)键值设置为1。然后将其 Fade In/Out 键值设置为 0.15。

(这样做不是看到玩家和快速切换到相机,而是允许淡入淡出。在此期间,生还者将传送到他们的位置,然后视图将返回到相机。)

在第一个淡入淡出的实体的输出选项卡中添加输出:

  My Output Target Entity Target Input Parameter Delay Only Once
Io11.png OnFade fade2 Fade <none> 6.00 No

然后返回到逃离载具内的 trigger_escape 并添加输出:

  My Output Target Entity Target Input Parameter Delay Only Once
Io11.png OnEntireTeamStartTouch outro_cam Enable <none> 0.00 No

转到 fade2 实体并添加如下输出:

  My Output Target Entity Target Input Parameter Delay Only Once
Io11.png OnFade outro_stats RollStatsCrawl <none> 0.00 No

然后在实体选项卡中查找 env_outtro_stats。 在其属性中将其目标名设置为 outro_stats。

导航网格

Hammer scavtut devconsole.jpeg

运行你的地图,进入关卡后按波浪号键 ~ 打开开发者控制台,然后输入

  • sv_cheats 1——这允许在游戏中启用作弊
  • noclip——允许在地图上自由移动,你不会被任何东西挡住
  • nav_edit 1——启用导航网格编辑
  • z_debug 1——允许你在地图上查看导航属性和所有丧尸
  • director_stop——阻止导演生成丧尸
  • nb_delete_all——在地图上删除所有的 NPC

然后去你的结局区域并打开开发者控制台并输入

  • nav_mark_walkable——在地图上标记一个可步行的点,由紫色金字塔标记,可以生成导航网格
  • nav_generate_incremental——生成距 nav_mark_walkable 一定距离的导航网格

然后确保你在结局触发器周围选择了你想要的区域,以及丧尸从中产生的距离区域。然后在开发者控制台中输入以下内容:

  • mark Finale——这是在战役的最终关卡中使用的导航网格的属性,并与 trigger_finale 结合使用。
Note.png注意:为了使导航工作,必须有一个标记有 checkpoint 属性的导航网格。
Hammer scavtut navfinale.jpeg

Hammer scavtut navrescue.jpeg

然后去你的玩家起始区域并用 CHECKPOINT 标记这些区域。 最后,你需要去你的逃离载具并用 RESCUE_VEHICLE 标记里面的导航区域。(如右图所示)

完成后,在开发者控制台中输入以下内容:

  • nav_analyze——这会分析所有导航网格并在 left4dead2 文件夹的地图文件夹中写入一个名为“<你的地图名称>.nav”的文件。

这是你制作清道夫结局所需的全部内容,希望对你有所帮助

脚本(VScript)

此次清道夫结局需要三个脚本。请注意,有些代码是多余的或被注释掉的,可能是开发过程的标记。包括补充意见:

c1m4_atrium.nut

该脚本需要作为导演脚本使用 info_director 实体的 BeginScript 输入手动在 OnMapSpawn 事件加载。它设置汽油桶的数量、默认的 CommonLimit、解锁救援车辆导航区域,并且(出于某种原因)声明函数 GasCanPoured()。

  • 如果是单人游戏,汽油桶的数量会减少
  • 由于人类玩家将在救援车辆上活动,因此救援车辆导航区域是不能被阻挡的。
  • GasCanPoured() 将由导演 (OnTeamScored) 或 point_prop_use_target (OnUsedFinished) 调用,只要成功倒入汽油桶。
Msg(" atrium map script "+"\n")

// number of cans needed to escape.

if ( Director.IsSinglePlayerGame() )
{
	NumCansNeeded <- 8
}
else
{
	NumCansNeeded <- 13
}

// This script is called on MapSpawn, so the CommonLimit is for play before the finale start.
DirectorOptions <-
{
	
CommonLimit = 15

}

NavMesh.UnblockRescueVehicleNav() // Unblock so humans can be rescued when incapped near nozzle

EntFire( "progress_display", "SetTotalItems", NumCansNeeded ) //Set number of cans with game_scavenge_progress_display


function GasCanPoured(){} // Declaration of function, but was moved to main finale script

c1m4_atrium_finale.nut

一旦结局强制开始(当电梯门在中庭的最低层打开时),就会加载此脚本。它包含大多数结局设置和逻辑。

  • 有多个结局阶段,ONSLAUGHT (猛攻)、PANIC (尸潮)或 TANK。第一阶段是 ONSLAGUT,本阶段的 DirectorOptions 将使用 InitialOnslaughtOptions。一旦汽油桶被捡起四次或一个汽油桶成功倒出,本阶段就会结束,导致 PANIC 阶段
  • 其余的猛攻从 DirectorOptions 的 c1m4_delay.nut 运行。这种猛攻与时间有关,并且在进入下一阶段之前对汽油桶接触次数的容忍度较低。
Msg("----------------------FINALE SCRIPT------------------\n")
//-----------------------------------------------------
// Stage type enumerations
PANIC <- 0
TANK <- 1
DELAY <- 2
ONSLAUGHT <- 3
//-----------------------------------------------------
// Initialized tables along with stage settings

SharedOptions <-
// Base DirectorOptions
{
 	A_CustomFinale1 = ONSLAUGHT //Will be stopped with input to director, EndCustomScriptedStage
	A_CustomFinaleValue1 = "" //InitialOnslaughtOptions is slightly different from c1m4_delay

	A_CustomFinale2 = PANIC
	A_CustomFinaleValue2 = 1 //1 PANIC wave

	A_CustomFinale3 = ONSLAUGHT
	A_CustomFinaleValue3 = "c1m4_delay" //This onslaught also depends on timer
        
	A_CustomFinale4 = PANIC
	A_CustomFinaleValue4 = 1

	A_CustomFinale5 = ONSLAUGHT
	A_CustomFinaleValue5 = "c1m4_delay"

	A_CustomFinale6 = TANK
	A_CustomFinaleValue6 = 1

	A_CustomFinale7 = ONSLAUGHT
	A_CustomFinaleValue7 = "c1m4_delay"
 
 	A_CustomFinale8 = PANIC
	A_CustomFinaleValue8 = 1

	A_CustomFinale9 = ONSLAUGHT
	A_CustomFinaleValue9 = "c1m4_delay"
 
 	A_CustomFinale10 = PANIC
	A_CustomFinaleValue10 = 1

	A_CustomFinale11 = ONSLAUGHT
	A_CustomFinaleValue11 = "c1m4_delay"

	A_CustomFinale12 = PANIC
	A_CustomFinaleValue12 = 1
        
 	A_CustomFinale13 = ONSLAUGHT
	A_CustomFinaleValue13 = "c1m4_delay"
        
	A_CustomFinale14 = TANK
	A_CustomFinaleValue14 = 1 //1 TANK
        
 	A_CustomFinale15 = ONSLAUGHT
	A_CustomFinaleValue15 = "c1m4_delay"
        
	A_CustomFinale16 = PANIC
	A_CustomFinaleValue16 = 1  
                  
 	A_CustomFinale17 = ONSLAUGHT
	A_CustomFinaleValue17 = "c1m4_delay"    
                       
 	A_CustomFinale18 = PANIC
	A_CustomFinaleValue18 = 1  
	
 	A_CustomFinale19 = ONSLAUGHT
	A_CustomFinaleValue19 = "c1m4_delay"
        
	A_CustomFinale20 = PANIC
	A_CustomFinaleValue20 = 1   
        
 	A_CustomFinale21 = ONSLAUGHT
	A_CustomFinaleValue21 = "c1m4_delay"
        
	A_CustomFinale22 = TANK
	A_CustomFinaleValue22 = 1  
                  
 	A_CustomFinale23 = ONSLAUGHT
	A_CustomFinaleValue23 = "c1m4_delay"    
                       
 	A_CustomFinale24 = PANIC
	A_CustomFinaleValue24 = 1
															
 	A_CustomFinale25 = ONSLAUGHT
	A_CustomFinaleValue25 = "c1m4_delay"
        
	A_CustomFinale26 = PANIC
	A_CustomFinaleValue26 = 1   
        
 	A_CustomFinale27 = ONSLAUGHT
	A_CustomFinaleValue27 = "c1m4_delay"
        
	A_CustomFinale28 = PANIC
	A_CustomFinaleValue28 = 1  
                  
 	A_CustomFinale29 = ONSLAUGHT
	A_CustomFinaleValue29 = "c1m4_delay"    
                       
 	A_CustomFinale30 = PANIC
	A_CustomFinaleValue30 = 1

 	A_CustomFinale31 = ONSLAUGHT
	A_CustomFinaleValue31 = "c1m4_delay"   
        // End of finale, regardless of gas cans filled
                      
	//-----------------------------------------------------
        // More Default DirectorOptions

	PreferredMobDirection = SPAWN_LARGE_VOLUME
	PreferredSpecialDirection = SPAWN_LARGE_VOLUME
        
//	BoomerLimit = 0
//	SmokerLimit = 2
//	HunterLimit = 1
//	SpitterLimit = 1
//	JockeyLimit = 0
//	ChargerLimit = 1

	ProhibitBosses = true
	ZombieSpawnRange = 3000
	MobRechargeRate = 0.5
	HordeEscapeCommonLimit = 15
	BileMobSize = 15
	
	MusicDynamicMobSpawnSize = 8
	MusicDynamicMobStopSize = 2
	MusicDynamicMobScanStopSize = 1
} 

InitialOnslaughtOptions <-
// DirectorOptions for first onslaught
{
    LockTempo = 0
	IntensityRelaxThreshold = 1.1
	RelaxMinInterval = 2
	RelaxMaxInterval = 4
	SustainPeakMinTime = 25
	SustainPeakMaxTime = 30
	
	MobSpawnMinTime = 4
	MobSpawnMaxTime = 8
	MobMinSize = 2
	MobMaxSize = 6
	CommonLimit = 5
	
	SpecialRespawnInterval = 100
}

PanicOptions <-
// DirectorOptions when in a PANIC stage
{

	MegaMobSize = 0 // randomized in OnBeginCustomFinaleStage
	MegaMobMinSize = 20
	MegaMobMaxSize = 40
	
	CommonLimit = 15
	
	SpecialRespawnInterval = 40
}

TankOptions <-
// DirectorOptions when in a TANK stage
{
	ShouldAllowMobsWithTank = true
	ShouldAllowSpecialsWithTank = true

	MobSpawnMinTime = 10
	MobSpawnMaxTime = 20
	MobMinSize = 3
	MobMaxSize = 5

	CommonLimit = 7
	
	SpecialRespawnInterval = 60
}


DirectorOptions <- clone SharedOptions
// Start with SharedOptions
{
}


//-----------------------------------------------------

// number of cans needed to escape. again. (Later moved to c1m4_atrium.nut)
NumCansNeeded <- 13

// fewer cans in single player since bots do not help much
if ( Director.IsSinglePlayerGame() )
{
	NumCansNeeded <- 8
}

// duration of delay stage.
DelayMin <- 10
DelayMax <- 20

// Number of touches and/or pours allowed before a delay is aborted.
DelayPourThreshold <- 1
DelayTouchedOrPouredThreshold <- 2


// Once the delay is aborted, amount of time before it progresses to next stage.
AbortDelayMin <- 1
AbortDelayMax <- 3

// Number of touches and pours it takes to transition out of c1m4_finale_wave_1
GimmeThreshold <- 4


// console overrides
if ( Director.IsPlayingOnConsole() )
{
	DelayMin <- 20
	DelayMax <- 30
	
	// Number of touches and/or pours allowed before a delay is aborted.
	DelayPourThreshold <- 2
	DelayTouchedOrPouredThreshold <- 4
	
	TankOptions.ShouldAllowSpecialsWithTank = false
	
}
//-----------------------------------------------------
//      INIT
//-----------------------------------------------------

GasCansTouched          <- 0
GasCansPoured           <- 0
DelayTouchedOrPoured    <- 0
DelayPoured             <- 0

EntFire( "timer_delay_end", "LowerRandomBound", DelayMin )
EntFire( "timer_delay_end", "UpperRandomBound", DelayMax )
EntFire( "timer_delay_abort", "LowerRandomBound", AbortDelayMin )
EntFire( "timer_delay_abort", "UpperRandomBound", AbortDelayMax )

// this is occurs too late. moved to c1m4_atrium.nut
//EntFire( "progress_display", "SetTotalItems", NumCansNeeded )

function AbortDelay(){}  	// only defined during a delay, in c1m4_delay.nut
function EndDelay(){}		// only defined during a delay, in c1m4_delay.nut

NavMesh.UnblockRescueVehicleNav() // This is redundant since it was already done once

//-----------------------------------------------------

function GasCanTouched()
// This is called by weapon_scavenge_item_spawn OnItemPickedUp
{
    GasCansTouched++
    Msg(" Touched: " + GasCansTouched + "\n")   
     
    EvalGasCansPouredOrTouched()    
}
    
function GasCanPoured()
// In this map, it is called by the director OnTeamScored
{
    GasCansPoured++
    DelayPoured++
    Msg(" Poured: " + GasCansPoured + "\n")   

    if ( GasCansPoured == NumCansNeeded )
    {
        Msg(" needed: " + NumCansNeeded + "\n") 
        EntFire( "relay_car_ready", "trigger" )
    }

    EvalGasCansPouredOrTouched()
}

function EvalGasCansPouredOrTouched()
// Evaluate the number of times gas cans poured or touched
{
    TouchedOrPoured <- GasCansPoured + GasCansTouched
    Msg(" Poured or touched: " + TouchedOrPoured + "\n")

    DelayTouchedOrPoured++
    Msg(" DelayTouchedOrPoured: " + DelayTouchedOrPoured + "\n")
    Msg(" DelayPoured: " + DelayPoured + "\n")
    
    if (( DelayTouchedOrPoured >= DelayTouchedOrPouredThreshold ) || ( DelayPoured >= DelayPourThreshold ))
    // This is for c1m4_delay.nut (c1m4_delay.nut also resets the counter for Poured and TouchOrPoured)
    {
        AbortDelay()
    }
    
    switch( TouchedOrPoured ) //For stopping the InitialOnslaught (first stage)
    {
        case GimmeThreshold:
            EntFire( "@director", "EndCustomScriptedStage" )
            break
    }
}
//-----------------------------------------------------

function AddTableToTable( dest, src )
// This function is used to move table keys and values to other tables
{
	foreach( key, val in src )
	{
		dest[key] <- val
	}
}

function OnBeginCustomFinaleStage( num, type )
// Special Function every time a finale stage starts.
// Instructions in this function set DirectorOptions for PANIC and TANK
{
	printl( "Beginning custom finale stage " + num + " of type " + type );
	
	local waveOptions = null
	if ( num == 1 )
	{
		waveOptions = InitialOnslaughtOptions
	}
	else if ( type == PANIC )
	{
		waveOptions = PanicOptions
		waveOptions.MegaMobSize = PanicOptions.MegaMobMinSize + rand()%( PanicOptions.MegaMobMaxSize - PanicOptions.MegaMobMinSize )
		
		Msg("*************************" + waveOptions.MegaMobSize + "\n")
		
	}
	else if ( type == TANK )
	{
		waveOptions = TankOptions
	}
	
	//---------------------------------


	MapScript.DirectorOptions.clear()
	

	AddTableToTable( MapScript.DirectorOptions, SharedOptions );

	if ( waveOptions != null )
	{
		AddTableToTable( MapScript.DirectorOptions, waveOptions );
	}
	
	
	Director.ResetMobTimer()
	
	if ( developer() > 0 )
	{
		Msg( "\n*****\nMapScript.DirectorOptions:\n" );
		foreach( key, value in MapScript.DirectorOptions )
		{
			Msg( "    " + key + " = " + value + "\n" );
		}

		if ( LocalScript.rawin( "DirectorOptions" ) )
		{
			Msg( "\n*****\nLocalScript.DirectorOptions:\n" );
			foreach( key, value in LocalScript.DirectorOptions )
			{
				Msg( "    " + key + " = " + value + "\n" );
			}
		}
	}
}

//-----------------------------------------------------


if ( Director.GetGameMode() == "coop" )
{
}
else if ( Director.GetGameMode() == "versus" ) // Allow tanks and witches in VS
{
	SharedOptions.ProhibitBosses = false
}

c1m4_delay.nut

每当开始猛攻阶段时都会加载此脚本,但第一个阶段除外。

  • 调用 logic_timer 实体启动计时器以进入下一阶段
  • 阶段在结束时间(timer_delay_end)或汽油桶可以灌油和拾取(timer_delay_abort)时结束
Msg("**Delay started**\n")

DirectorOptions <-
{
	MobMinSize = 2
	MobMaxSize = 3
        
	BoomerLimit = 0
	SmokerLimit = 0
	HunterLimit = 0
	SpitterLimit = 0
	JockeyLimit = 0
	ChargerLimit = 0
        
	MinimumStageTime = 15
       
	CommonLimit = 5
}

Director.ResetMobTimer() //Start the above onslaught settings immediately


// start the delay timer
EntFire( "timer_delay_end", "enable" )

//reset for this stage (PANIC and TANK depend on other factors)
DelayTouchedOrPoured   <- 0
DelayPoured            <- 0
//-------------------------------------------------


// abort the delay if a survivor picks up or pours a gas can
// Function called by c1m4_atrium_finale.nut
function AbortDelay()
{
    Msg("**Delay aborted early**\n")    
    EntFire( "timer_delay_abort", "enable" )
}

// called by the timers themselves
// These EntFired timers MUST be the same targetname in the map!
function EndDelay()
{
        Msg("**Delay ended**\n") 
        EntFire( "timer_delay_end", "Disable" )
        EntFire( "timer_delay_end", "ResetTimer" )
        EntFire( "timer_delay_abort", "Disable" )
        EntFire( "timer_delay_abort", "ResetTimer" )
        EntFire( "@director", "EndCustomScriptedStage" )
}