Skip to content

Instantly share code, notes, and snippets.

@Tsuey
Created July 25, 2021 15:02
Show Gist options
  • Save Tsuey/6745de1ec3949305ab74ac15b7417c76 to your computer and use it in GitHub Desktop.
Save Tsuey/6745de1ec3949305ab74ac15b7417c76 to your computer and use it in GitHub Desktop.
Cut Code from L4D2-Community-Update
L4D2_TLS_Community_Update_Cut_Fixes.txt
=======================================
WORK_IN_PROGRESS_-_GENERATE_A_FUNCTIONAL_SCRIPT_WITH_ALL_THIS_SOMETIME_LATER
THIS DOCUMENT IS MISSING A LOT (BELIEVE IT OR NOT)
Sections (in no orderly fashion):
MISCELLANEOUS
PROPPER MODELS
VERSUS :: EMPTY SPAWN AREA SET FINALE BUG
CHOPPER SURVIVOR/INFECTED FILTERS
SACRIFICE 1 HITTABLE TANK DOOR
COMPREHENSIVE SCAVENGE MODE FIXES
COMPREHENSIVE TAAANNNK! MUTATION FIXES -- ELEVATOR "ALLOWINCAP" STUFF
SACRIFICE 1 BOXCAR TANK FIX
PASSING 2 EVENT GATES EXPLOIT FIX
DEAD CENTER 1 TANK WARP
DEATH TOLL 5 ROCKSLIDE PHYSICS SIMULATION -- IT'S COOL, BUT THE LANDING ARRANGEMENT UNRELIABLE
VERSUS :: DEAD CENTER 1 LOBBY SKYLIGHT ATTACK -- IT'S COOL, BUT OFF-STANDARD
VERSUS :: MISCELLANEOUS CUT CONTENT
This is a broad collection of map-specific fixes and Versus content that was cut from
the update, due to being incomplete or not up to standards. It's the "first look" place
for what we can immediately start improving on -- or keep it cut.
Note that some stuff, like the Scavenge and elevator "allowincap" fixes, may have been
implemented, but with *.LMP files or other methods instead, and no longer need fixing.
There is no warranty with this code and some of it will be shit.
Comments in terms of "how the code works / why this was done" were cut... comments that
describe content and/or implementations kept.
####################################################################################################
####################################################################################################
MISCELLANEOUS
Loud Dead Center 1 Chopper intro -- this was likely more ideally fixed with audio edits, but
this would've also been workable... it just wouldn't have been ideal for sound mods since
those with mods that already soften it wouldn't have been able to hear it at all anymore:
EntFire( "relay_intro_start", "AddOutput", "OnTrigger sound_chopperleave:Volume:5:2.01:-1" );
Forgotten Dead Air 3 prop_static railing fix that Salad asked for long ago.
Forgotten Passing 2 _commentary.txt blocker QoL at event Niels_L asked for long ago, which
should be delayed until _commentary.txt are fully ported to *.NUT and *.LMP files.
Tank warp fix for Dead Center 3:
Teleported into the bathroom with the door automatically open.
Turning the breakwall into a working one with fake func_illusionary has been trivialized
thanks to breakwall Propper models -- so "infodecal hacks" aren't necessary.
Husky provided a fence/barricade and ladder for the hallway for SI to climb over, and
Commons can even climb over it. It was all pretty good to go, but was cut since more
input/say into the situation was valued.
Tank warp fix for Dead Air 4 so Tank doesn't spawn behind fence, is teleported up to rubble instead.
####################################################################################################
####################################################################################################
PROPPER MODELS
Full list of Propper models added with the update, some went un-used and are a part of below cuts.
In active use (in *.LMP files only -- but Blood Harvest 2 has a breakwall that still needs this):
models/props/effects/tankwall_128_2_128.mdl
models/props/effects/tankwall_48_1_48.mdl
models/props_update/c11m1_fakewater.mdl
In active use (in VScript only):
models/props_update/plywood_128.mdl
models/props_update/c1m2_wrongway_rooftop1.mdl
models/props_update/c1m2_wrongway_rooftop2.mdl
models/props_update/c1m2_wrongway_wall.mdl
models/props_update/c2m2_fairgroundwall.mdl
models/props_update/c2m4_barn_overhang.mdl
models/props_update/c2m5_infectedroom.mdl
models/props_update/c2m5_infectedroom_doorway.mdl
models/props_update/c3m3_nodrawfence.mdl
models/props_update/c5m2_billboard_nodraw.mdl
models/props_update/c8m1_rooftop_1.mdl
models/props_update/c8m1_rooftop_2.mdl
models/props_update/c8m1_rooftop_3.mdl
models/props_update/c8m1_rooftop_4.mdl
models/props_update/c8m2_generatorroom.mdl
models/props_update/c8m4_skylight_rooftop.mdl
models/props_update/c9m1_nodraw_window.mdl
models/props_update/c10m4_hellcade_nodraw.mdl
models/props_update/c11m1_greenhouse_nodraw.mdl
models/props_update/c11m1_greenhouse_plywood.mdl
models/props_update/c11m1_plywood.mdl
models/props_update/c11m3_nodraw_cinderwall.mdl
models/props_update/c11m3_wrongway_curb.mdl
models/props_update/c11m3_wrongway_fence.mdl
Not used, in any capacity:
models/props_update/brick_128.mdl
models/props_update/brick_256.mdl
models/props_update/concrete_128.mdl
models/props_update/concrete_256.mdl
models/props_update/plywood_256.mdl
models/props_update/whitebrick_128.mdl
models/props_update/whitebrick_256.mdl
models/props_update/wood_128.mdl
models/props_update/wood_256.mdl
models/props_update/c1m3_skylight_nodraw.mdl
models/props_update/c5m2_nodraw_eventroof.mdl
So 38 total, 36 of them in /props_update/ folder.
####################################################################################################
####################################################################################################
VERSUS :: EMPTY SPAWN AREA SET FINALE BUG
Since we're splitting "anv_maptrigs.nut" into map-specific files:
https://github.com/Tsuey/L4D2-Community-Update/blob/be0cf3b251627fe018916c3670cc60eeee81db31/scripts/vscripts/anv_maptrigs.nut
There's several placeholders that are now obsolete and have been deleted:
case "c3m4_plantation":
{
// FIX: Finale "empty spawn area set" dynamic "z_finale_spawn_mob_safety_range".
break;
}
case "c10m5_houseboat":
{
// FIX: Finale "empty spawn area set" dynamic "z_finale_spawn_mob_safety_range".
break;
}
case "c11m5_runway":
{
// FIX: Finale "empty spawn area set" dynamic "z_finale_spawn_mob_safety_range".
break;
}
case "c12m5_cornfield":
{
// FIX: Finale "empty spawn area set" dynamic "z_finale_spawn_mob_safety_range".
break;
}
These are still problems. And exclusively in Versus mode. SI Players used to automatically spawn
during finales in L4D1 -- this problem is very old, and dates back to those times, and a brief
bug where Ghost SI would spawn unreasonably far away. Very old roots to this problem.
Get ProdigySim or others on the problem with actual debuggers to possibly find the optimal path
for Kerry to take. CVAR "z_finale_spawn_safety_range" is default 300 and has been for years, but
I did get Kerry to add "z_finale_spawn_mob_safety_range" (default 600) to fix the Death Toll 5
docks-camping issue, and the rockslide was added to deter running back which also causes this bug.
z_finale_spawn_safety_range Affects Ghost SI materialize range and Common (Mob) spawns
z_finale_spawn_mob_safety_range Affects exclusively Common (Mob) spawns (NEW)
z_finale_spawn_safety_range_override Affects exclusively Ghost SI spawns
NOTE: With "z_finale_spawn_safety_range" it was undesirable that it affects Commons,
but setting it to a negative value like -600 results in Commons taking the absolute
value of 600 (which fixes the problem), then use "z_finale_spawn_safety_range_override"
to override Ghost SI spawns. More nonsense about that in below chat with ProdigySim:
Dev Thread: Map fixes for Valve
https://steamcommunity.com/app/550/discussions/1/1651043320659915818/?ctp=3#c1643170269583340912
While triggers and CVAR toggles (which thanks to TLS reset on round transition, so doing that as
a fix is at least viable now) is possible on 10/11/12, it's Swamp Fever 4 that's a huge problem
given that setting "z_finale_spawn_mob_safety_range" higher fixes dead-zones outside, but then
creates dead-zones if all Survivors camp directly underneath the attic... so this map would require
multiple triggers. Custom/competitive servers have long overridden spawn behaviors to avoid this.
Kerry helped with "z_finale_spawn_mob_safety_range", but fact is the negative value hack and the
systemic Convars.SetValue() round reset changes would've allowed for a Death Toll 5 fix by themselves.
####################################################################################################
####################################################################################################
CHOPPER SURVIVOR/INFECTED FILTERS
This function was officially added to the game but fell to non-use:
function modify_trigfilter( strOrigin,
strFiltername = "anv_globalfixes_filter_survivor",
strClassname = "trigger_once" )
{
local hndTrigger = Entities.FindByClassnameNearest( strClassname, StringToVector_Valve( strOrigin, " " ), 1 );
local hndFiltername = Entities.FindByName( null, strFiltername );
if ( SafelyExists( hndTrigger ) && SafelyExists( hndFiltername ) )
{
NetProps.SetPropString( hndTrigger, "m_iFilterName", strFiltername );
NetProps.SetPropEntity( hndTrigger, "m_hFilter", hndFiltername );
}
if ( developer() > 0 )
{
printl( strClassname + " filter modified to " + strFiltername + " @ setpos_exact " + strOrigin + "\n" );
}
}
Some cases where it'd have been used might've been fixed in *.LMP files, but definitely
not all of them -- here's some to keep in mind:
1. Dead Center 1 skylight damage trigger shouldn't kill Hunters. Due to cutting
the tree env_beam ladders, the trigger modification to change the damage was
removed. Resolve the lobby attack another way and add back this trigger edit.
2. No Mercy 2 end area Chopper can be triggered by Infected.
3. Dark Carnival 3 Coaster area Chopper was deliberately removed from Versus mode
but not entirely, so remains buggy.
4. Dark Carnival 4 bumper car exit Chopper may still be broken -- to be consistent
with Valve's deliberate change above, maybe even remove it from just Versus.
####################################################################################################
####################################################################################################
SACRIFICE 1 HITTABLE TANK DOOR
Make the Tank door fly off and then become a hittable:
EntFire( "tankdoorin_model", "AddOutput", "OnTakeDamage !self:Kill::0:-1" );
EntFire( "tankdoorin_model", "AddOutput", "OnTakeDamage tank_door_clip:Kill::0:-1" );
EntFire( "tankdoorin_model", "AddOutput", "OnTakeDamage tankdoorin_button:Kill::0:-1" );
EntFire( "tankdoorin_model", "AddOutput", "OnTakeDamage tankdoorin:Kill::0:-1" );
EntFire( "tankdoorin_model", "AddOutput", "OnTakeDamage worldspawn:CallScriptFunction:c7m1_tankdoor:0:-1" );
function c7m1_tankdoor()
{
make_prop( "physics_ovr", "_hittable_tankdoorin", "models/props_vehicles/boxcar_tanktrap_door.mdl", "6968 672 120", "0 330 0", "shadow_yes", "solid_yes", "255 255 255", -1, 0, 20 );
Entities.FindByName( null, g_UpdateName + "_hittable_tankdoorin" ).ApplyAbsVelocityImpulse( Vector( 80, 0, 0 ) );
}
####################################################################################################
####################################################################################################
COMPREHENSIVE SCAVENGE MODE FIXES
NOTE: Shared these with Rayman1103 once and some may have been fixed in *.LMP files and even
left undocumented despite the various changelogs sent around with TLS.
This function was going to be used to fix gascans on c8m5 (and c14m2) at one point, but I think
we solved it better/other ways. Not sure if they're perfect fixes, though -- this approach was,
but is less optimized, and c1m4 has the "pipebomb problem" where we don't want to respawn cans
that Boomers have launched upwards to be inaccessible since Survivors can still rescue those.
Losing gascans in c7m2 water is another as-yet-unresolved issue, and maybe Kerry could update the
C++ code so gascans in deep water can "drown" -- at least those cans can usually be shot, though.
function monitor_gascans( intLostZ )
{
if ( developer() > 0 )
{
printl( "SCAVENGE: Map is being monitored for lost gascans.\n" );
}
function resolve_gascans()
{
local hndGascan = null;
while( ( hndGascan = Entities.FindByClassname( hndGascan, "weapon_gascan" ) ) != null )
{
if ( hndGascan.GetOrigin().z < intLostZ )
{
DoEntFire( "!self", "Ignite", "", 7.0, null, hndGascan );
if ( developer() > 0 )
{
printl( "SCAVENGE: Lost gascan igniting in 7 seconds.\n" );
}
}
}
}
SpawnEntityFromTable( "logic_timer",
{
targetname = g_updateName + "_resolve_gascans_timer",
RefireTime = 5,
connections =
{
OnTimer =
{
cmd1 = "!selfCallScriptFunctionresolve_gascans0-1"
}
}
} );
}
c1m4_atrium
// Upper "monitor_gascans()" has the pipebomb problem. No fixes.
c2m1_highway
// End safe room is accessible and transitions to the next map, which
// is not a Scavenge map. It is env_physics_blocker'd since it's vulnerable
// to the same Survival-boost, but the end entities also deleted to be safe.
EntFire( "prop_door_rotating_checkpoint", "Kill" ); // Nameless
EntFire( "info_changelevel", "Kill" ); // "c2m1_c2m2_changelevel" (IT'S ALWAYS JUST 1)
maker_blocker( to_do_also_make_it_apply_to_survival_but_safe_for_all_gamemodes );
MULTI-PRONGED FIX:
Spawn these blockers in ALL_MODES, because they block both Scavenge and Survival
access to starting safe room (via pipe/launcher boosts)... they infere with nothing:
maker_blocker( "_clipextend_poolwalla", "Survivors", 1, "-517 -10 0", "517 10 360", "2561 3843 -640" );
maker_blocker( "_clipextend_poolwallb", "Survivors", 1, "-6 -350 0", "6 350 360", "3067 4200 -640" );
The final issue is a discrepancy REGARDING THE CHAIN-LINK FENCE:
SURVIVAL Spawns func_brush above fence (deletable with "ent_fire survival_brush kill")
that blocks it only the height of the PINK clip already extended.
SCAVENGE Has no brush at all above fence, completely exploitable... I used to do this
by shooting a wall-painting over to extend the length of entire team's jump.
COOP/VERSUS Cannot blocker above this fence because it's a quick way back... and making it
dynamic would require a trigger_once which is over-kill for something so trivial.
... it's a shortcut not really worth patching, in TAAANK Tanks should
be aware to not hit players over, in COOP/VERSUS it's a difficult
shortcut with negligible return
Note that doing this on Scavenge could produce the fence-blocker from Survival, but Survival's blocker
is useless anyway since it can easily be boosted onto so IGNORE this crap:
ent_fire ptemplate_survival forcespawn
SOLUTION: Make a slightly-unique clip for the fence for BOTH Survival (extend it) + Scavenge (has nothing).
SURVIVAL-ONLY CLIP:
maker_blocker( "_survival_fenceextend", "Survivors", 1, "0 -234 0", "77 0 360", "2042 3837 -640" );
SCAVENGE-ONLY CLIP:
maker_blocker( "_scavenge_fence", "Survivors", 1, "0 -234 -145", "77 0 360", "2042 3837 -640" );
c3m1_plankcountry
// End safe room inaccessible because of wood board. No fixes.
c4m1_milltown_a
// End safe room inaccessible because of wood board. No fixes.
c4m2_sugarmill_a
// End safe room is accessible and transitions to the next map, which
// is identical but has harder rain. Map 4 doesn't have Map 1's Scavenge.
// Start safe room also accessible for camping.
EntFire( "prop_door_rotating_checkpoint", "Kill" ); // Deleting BOTH checkpoint_exit (start) AND checkpoint_entrance (end)
EntFire( "info_changelevel", "Kill" ); // Nameless
c5m2_park
// Start safe room is accessible and can simply be closed and camped.
EntFire( "prop_door_rotating_checkpoint", "Kill" ); // Only NEED to delete checkpoint_exit (start) but harmlessly deletes door_checkpoint (end)
c6m1_riverbank
// End safe room is accessible and transitions to the next map, which
// is also a Scavenge map.
EntFire( "prop_door_rotating_checkpoint", "Kill" ); // "checkpoint_entrance"
EntFire( "info_changelevel", "Kill" ); // Nameless
c6m2_bedlam
// Requires start safe door removal to prevent camping, end safe door
// removal to prevent changelevel transition. Button deletion makes
// end safe doro Kill unnecessary, but harmlessly done just in case.
EntFire( "prop_door_rotating_checkpoint", "Kill" ); // Only NEED to delete checkpoint_exit (start) unnecessarily doing checkpoint_entrance (end)
EntFire( "info_changelevel", "Kill" ); // "c6m2_c6m3_changelevel" (Valve really inconsistently names these)
// Also remove hint text "Keep moving! Run to the saferoom!" for event,
// invisible glowing button, and sprite so the button appears powerless.
EntFire( "washer_button_game_event", "Kill" ); // Named exactly these and only 1 of each, 2nd gate has all different names
EntFire( "button_minifinale", "Kill" );
EntFire( "sprite_on", "Kill" );
c6m3_port
// Start safe room inaccessible because of elevator and great clips. No fixes.
c7m1_docks
// End safe room is blocked by func_brush "kiln_door", but can still be
// accessed with a boost through the zombie breakwall or up into ventilation.
EntFire( "prop_door_rotating_checkpoint", "Kill" ); // Nameless (and only 1 b/c 1st map of campaign)
EntFire( "info_changelevel", "Kill" ); // Nameless
c7m2_barge
// Start safe room inaccessible because of fences but could be boosted over.
// End safe room accessible by simply jumping over the wooden blocker, so
// it has been env_physics_blocker'd above it. No need to Kill "info_changelevel"
// here because the new blocker makes accessing end safe room impossible...
// so really this only (somewhat but not fully unnecessarily) Kills start safe door.
EntFire( "prop_door_rotating_checkpoint", "Kill" ); // Nameless BOTH start AND end, no choice but to delete BOTH
maker_blocker( one_we_already_have_there_above_wood );
c8m1_apartment
// End safe room inaccessible because of func_brush "kiln_door". No fixes.
c8m5_rooftop
// Ignite gascans lost to the deep abyss with longest possible delay of
// 90 seconds, reducing as there's less cans left to pour. Note that after
// it's Ignited it needs an additional 20 seconds to naturally respawn.
monitor_gascans( "lower", 121, 90 );
c10m3_ranchhouse --- ***IGNORE B/C SEE RE-DONE***
// Start safe room is freely accessible to camp, but the end safe room
// already has no door and no transition. Even though Valve already deleted
// the end safe room entry door, it still has 3 "prop_door_rotating_checkpoint"
// entities: (1) the way-in door from previous map, (2) the start safe door
// named "checkpoint_exit" that needs deletion because it can be camped, and
// (3) the way-out safe door into the next map -- in most maps, cosmetic safe
// doors are static but not all of them. Specifies exact door to delete to
// preserve cosmetics. It's pure luck that only the keepers are Nameless.
EntFire( "checkpoint_exit", "Kill" );
// End safe room is blocked unlike all other Scavenge safe rooms, but
// it's by the func_button which is the size of the removed door and also
// blocks gascans from being thrown in. Invisible func_button can still
// be Pressed to start the minifinale and once it concludes the button
// allows access in. To keep the func_button as a pseudo-blocker it's
// simply Locked, and the func_orator is also Killed so the churchguy
// stops talking when the door isn't even there.
EntFire( "button_safedoor_PANIC", "Lock" );
EntFire( "churchguy", "Kill" );
// Ghost Infected can move through the end safe room func_button to spawn
// and get stuck in there. Survivors can't. This does remove an Infected
// attack from the end safe room, but they have the rooftop and it's better
// than having been stuck if they weren't shot to death.
*** INSTEAD OF REMOVING A GHOST INFECTED SPAWN OPPORTUNITY, APPROACH WAS RE-DONE ***
c10m3_ranchhouse --- ***RE-DONE BECAUSE IT'S BETTER THAN BLOCKING GHOST INFECTED FROM ENTERING SAFE ROOM***
// Start safe room is freely accessible to camp, but the end safe room
// already has no door and no transition. Even though Valve already deleted
// the end safe room entry door, it still has 3 "prop_door_rotating_checkpoint"
// entities: (1) the way-in door from previous map; (2) the start safe door
// named "checkpoint_exit" that needs deletion because it can be camped; and
// (3) the way-out safe door into the next map -- in most maps, cosmetic safe
// doors are static but not all of them. Specifies exact door to delete to
// preserve cosmetics. It's pure luck that only keepers are Nameless.
EntFire( "checkpoint_exit", "Kill" );
// This is the only Scavenge map where the end safe room is invisibly blocked,
// in this case it's by the func_button that's the same size of the door
// Valve already removed. While it blocks gascans from being thrown in, it
// doesn't block Ghost Infected so they can spawn while inside and get stuck.
// The invisible func_button can also still be Pressed to start the minifinale
// and once it concludes it allows everyone access. Delete the func_button
// and also delete the func_orator so he stops talking without a door even there.
EntFire( "button_safedoor_PANIC", "Kill" );
EntFire( "churchguy", "Kill" );
// Everyone now has end safe room access, good for Ghost Infected spawns but
// bad because top of ladder would be an over-powered Survivor camp -- thus
// access up top is blocked with visible plywood and 2 blockers (the blockers
// required because the func_simpleladder sticks out far).
// USED THIS == prop_dynamic_create props_highway/plywood_03.mdl
maker_prop( "dynamic", "_scavenge_safeplywood", mdl.plywood_03, "-2666 34 159", "90 -90 0", "shadow_no" );
maker_blocker( "_scavenge_safeladdera", "Everyone", 1, "-24 -5 -64", "24 5 64", "-2642 34 216" );
maker_blocker( "_scavenge_safeladderb", "Everyone", 1, "-17 -17 -2", "17 17 2", "-2643 15 327" );
c11m4_terminal
// Start safe room inaccessible because of event fence and end safe room
// already has no door and no transition. No fixes.
c12m5_cornfield
// Start safe room can be accessed and camped with a boost up one-way.
EntFire( "prop_door_rotating_checkpoint", "Kill" ); // "checkpoint_exit" (only 1 b/c finale)
####################################################################################################
####################################################################################################
COMPREHENSIVE TAAANNNK! MUTATION FIXES -- ELEVATOR "ALLOWINCAP" STUFF
Elevators :: A Full Assessment
NOTE: Changing 0 to 1 requires that Survivors haven't entered the trigger yet.
Keep trigger states (haven't entered / are inside / entireteam / one out) in
mind, since it's very simple but testing all possibilities can get confusing.
c1m1_hotel
// Change only Taaannnkk! Mutation to allow Survivors to start the
// elevator if all non-incapped Survivors are inside (ditching incaps).
// Elevator not named so the trigger is found by coordinates.
if ( g_mutationmode == "mutation19" )
{
Entities.FindByClassnameNearest( "trigger_multiple", Vector( 2164, 5847.35, 2888.24 ), 1 ).__KeyValueFromInt( "allowincap", 0 );
}
c1m4_atrium
// Change all gamemodes except Taaannnkk! Mutation to require all
// Survivors to be inside the elevator in order to start it. This
// prevents players in Coop/Versus from griefing and/or pre-gathering
// all gascans to effectively skip the finale. Elevator not named.
if ( g_mutationmode != "mutation19" )
{
Entities.FindByClassnameNearest( "trigger_multiple", Vector( -4008, -3408, 596 ), 1 ).__KeyValueFromInt( "allowincap", 1 );
}
c3m1_plankcountry
// Already allows Survivors on the ferry to leave incaps behind, so
// don't change anything as players are used to it -- besides just
// humorously leaving a teammate behind, it's not gamebreaking.
// The trigger_multiple's name is "ferry_tram_button_trigger" with
// "allowincap 0" (for future reference).
c4m2_sugarmill_a
c4m3_sugarmill_b
// Both elevators are 100% identical except a 0.25 difference on
// Z axis origin ("-1469.82 -9546.5 196" / 196.25). Both are named
// "trigger_elevator" with "allowincap 0". Survivors can leave all
// incaps behind in both elevators, where in map 2 it's just to
// their disadvantage, and map 3 isn't exploitive since a trigger
// kills them (unless they out-run it by slipping out of the doors,
// but that's a different exploit, and again on them, and again if
// that were patched it'd interfere with Smoker Death Pulls, so no).
// Taaannnkk! Mutation is as good as it can be on both maps, short
// of skipping the elevators entirely, which goes against design.
c6m3_port
// Change only Taaannnkk! Mutation to allow Survivors to start the
// elevator if all non-incapped Survivors are inside (ditching incaps).
// Of all elevators changed, this is the only one that has a name.
if ( g_mutationmode == "mutation19" )
{
Entities.FindByName( null, "generator_elevator_trigger" ).__KeyValueFromInt( "allowincap", 0 );
}
c8m4_interior
// Change only Taaannnkk! Mutation to allow Survivors to start the
// elevator if all non-incapped Survivors are inside (ditching incaps).
if ( g_mutationmode == "mutation19" )
{
Entities.FindByClassnameNearest( "trigger_multiple", Vector( 13432, 15273.8, 484.54 ), 1 ).__KeyValueFromInt( "allowincap", 0 );
}
####################################################################################################
####################################################################################################
SACRIFICE 1 BOXCAR TANK FIX
Survivors can throw a gascan on top of the boxcar or lean one against it to ignite the Tank before
it can get out. Versus spawns the Tank immediately and is too familiar to change, and Coop doesn't
spawn the Tank until the doors have opened.
Give the Tank fire immunity until the doors have opened -- the Tank could still be standing inside
visible fire, this does nothing to get rid of the actual "inferno" ground entities:
if ( g_ranOnce == false )
{
function c7m1_fireImmunity()
{
local hndPlayer = null;
while( ( hndPlayer = Entities.FindByClassname( hndPlayer, "player") ) != null )
{
if ( ! hndPlayer.IsSurvivor()
&& hndPlayer.GetZombieType() == 8
&& hndPlayer.IsOnFire() )
{
hndPlayer.Extinguish();
}
}
}
function c7m1_thinkOn() { AddThinkToEnt( Entities.First(), "c7m1_fireImmunity" ); }
function c7m1_thinkOff() { AddThinkToEnt( Entities.First(), null ); }
}
Entities.FindByName( null, "versus_tank" ).ConnectOutput( "OnTrigger", "c7m1_thinkOn" );
Entities.FindByName( null, "tankdoorin_button" ).ConnectOutput( "OnTimeUp", "c7m1_thinkOff" );
The ideal, superior fix would be getting "func_extinguisher" to work with VScript mins/maxs... but,
this may not be that useful anyway, especially if it only kills "inferno" entities -- the perfect
fix needs to extinguish the fire on the ground AND the player/client.
The "Spitter with HP of a Tank" bug (if a player leaves the server during the spawn) is another
pressing matter in this area, and while Kerry would need to fix that on a global C++ scale, if
we're running VScript code here already we could check for and correct those cases as well.
####################################################################################################
####################################################################################################
PASSING 2 EVENT GATES EXPLOIT FIX
Clips were added on the gates to prevent Ghost SI from standing on them -- but it's still possible
to stand behind them and prevent the doors from swinging open, and possibly even on top of one.
This fix:
if ( g_ranOnce == false )
{
g_gate1Teleport <- Vector( 2528.64, 5702.63, -1064 );
g_gate2Teleport <- Vector( 5168.64, 5446.61, -1064 );
}
EntFire( "minifinale_gate1", "AddOutput", "OnBlockedOpening !activator:RunScriptCode:activator.SetOrigin(g_gate1Teleport):0:-1" );
EntFire( "minifinale_gate2", "AddOutput", "OnBlockedOpening !activator:RunScriptCode:activator.SetOrigin(g_gate2Teleport):0:-1" );
Was going to teleport the offender to the front of either gate if "OnBlockedOpening". Bit overkill,
but still a completely valid fix for something that's still a niche exploitable.
####################################################################################################
####################################################################################################
DEAD CENTER 1 TANK WARP
There's a lot of context to set here. This was "the plan":
immediately on map spawn in Versus mode:
reduce elevator auto-open time from 60 to 25 seconds
after Survivors have ascended the stairwell (inject into existing trigger):
enable all lower-floor fire particles
give Tank continuous immunity to fire
if Survivors haven't started elevator:
insta-teleport any lower-floor Tank spawns into closet
and remove immunity to fire
if Survivors have started elevator:
DisableTankFrustration for duration of descent
one elevator doors open:
EnableTankFrustration
remove immunity to fire
Based on 500 point total, 125 points per player, with 10% spawn variation, which we
fixed with TLS but there's still some remnant / randomized flow variation left:
VERSUS POINT DISTANCE
0 start spawn duh
49 doorway at base of last stairwell before long hall
60 2/3rds down long hall, freakishly-early top-level natural Tank spawn
65 end of the long hallway before elevator
68 inside of STORAGE closet Tank is teleported to
71 inside elevator
71 entire elevator ride
81 OnReachedBottom, 10 point jump
83 first step outside elevator
102 kitchen door before ballroom, latest i've ever seen Tank spawn
Code for dumping coordinates of trigger_hurt/trigger_push which are NUMEROUS and NAMELESS and
best identified by being under Z = 1400 -- this was just test / proof of concept stuff:
function dump_hotel()
{
printl( "TRIGGER_HURT" );
local hurt = null;
while( ( hurt = Entities.FindByClassname( hurt, "trigger_hurt") ) != null )
{
printl( hurt.GetOrigin().z );
}
printl( "TRIGGER_PUSH" );
local push = null;
while( ( push = Entities.FindByClassname( push, "trigger_push") ) != null )
{
printl( push.GetOrigin().z );
}
}
Useful functions to offer the bottom-floor Tank spawn to test the teleport -- note that, due
to random L4D2 code mysteries, AI bot Tanks are not "activated" (they exist, but not in a state
where they can be teleported) and will (or used to, could've been silently/accidentally fixed
with TLS) not be affected by "trigger_teleport" entities, hence why those were not used:
SpawnEntityFromTable( "filter_activator_infected_class",
{
targetname = "anv_globalfixes_filter_tank",
Negated = "Allow entities that match criteria",
filterinfectedclass = 8
} );
function offer_bullshit_tank_spawn()
{
local tank = SpawnEntityFromTable( "info_zombie_spawn",
{
origin = Vector( 1517, 5598, 1300 ),
population = "river_docks_trap",
offer_tank = 1
} );
DoEntFire( "!self", "SpawnZombie", "", 0.0, null, tank );
DoEntFire( "!self", "Kill", "", 1.0, null, tank );
}
Abandoned "trigger_multiple" fix approach:
g_elevatorStarted <- false;
if ( g_ranOnce == false )
{
function c1m1_lowest_trigger_set( strType, strState )
{
local strTrigger = null;
switch( strType )
{
case "push": strTrigger = "trigger_push"; break;
case "hurt": strTrigger = "trigger_hurt"; break;
}
local hndTrigger = null;
while( ( hndTrigger = Entities.FindByClassname( hndTrigger, strTrigger ) ) != null )
{
if ( hndTrigger.GetOrigin().z < 1400 )
{
switch( strState )
{
case "off": DoEntFire( "!self", "Disable", "", 0.0, null, hndTrigger ); break;
case "on": DoEntFire( "!self", "Enable", "", 0.0, null, hndTrigger ); break;
}
}
}
}
function c1m1_lowest_trigger_pushs( strState ) { c1m1_lowest_trigger_set( "push", strState ); }
function c1m1_lowest_trigger_hurts( strState ) { c1m1_lowest_trigger_set( "hurt", strState ); }
function c1m1_tank_teleport( activator )
{
if ( g_elevatorStarted == false )
{
activator.SetOrigin( Vector( 2494, 5342, 2464 ) );
activator.SetForwardVector( Vector( -0.392, 0.919, 0 ) );
}
if ( g_elevatorStarted == true )
{
activator.SetOrigin( Vector( 1932, 5819, 1184 ) );
activator.SetForwardVector( Vector( 0.311, -0.951, 0 ) );
}
}
}
c1m1_lowest_trigger_pushs( "off" );
c1m1_lowest_trigger_hurts( "off" );
local strTriggerName = g_updateName + "_tank_trigmult";
EntFire( "relay_elevator_reached_bottom", "AddOutput", "OnTrigger elevator_door_button1:Press::30:-1" );
EntFire( "elevator_button", "AddOutput", "OnPressed director:DisableTankFrustration::0:-1" );
EntFire( "elevator_button", "AddOutput", "OnPressed !self:RunScriptCode:g_elevatorStarted = true:0:-1" );
EntFire( "elevator_button", "AddOutput", "OnPressed !self:RunScriptCode:c1m1_lowest_trigger_pushs(\"on\"):18:-1" );
EntFire( "elevator_button", "AddOutput", "OnPressed !self:RunScriptCode:c1m1_lowest_trigger_hurts(\"on\"):22:-1" );
EntFire( "elevator_door_button1", "AddOutput", "OnPressed director:EnableTankFrustration::0:-1" );
EntFire( "elevator_1", "AddOutput", "OnReachedBottom " + strTriggerName + ":Kill::5:-1" );
SpawnEntityFromTable( "trigger_multiple",
{
targetname = strTriggerName,
origin = Vector( 2680, 6530, 1184 ),
spawnflags = 3,
filtername = "anv_globalfixes_filter_tank",
connections =
{
OnStartTouch =
{
cmd1 = "!activatorRunScriptCodec1m1_tank_teleport(activator)0-1"
}
}
} );
EntFire( strTriggerName, "AddOutput", "mins -3400 -2500 0" );
EntFire( strTriggerName, "AddOutput", "maxs 0 0 270" );
EntFire( strTriggerName, "AddOutput", "solid 2" );
The above triggered error "AN ERROR HAS OCCURED [the index 'activator' does not exist]"
every 1st time the trigger was touched.
This "Think" implementation:
g_elevatorStarted <- false;
if ( g_ranOnce == false )
{
function c1m1_lowest_trigger_set( strType, strState )
{
local strTrigger = null;
switch( strType )
{
case "push": strTrigger = "trigger_push"; break;
case "hurt": strTrigger = "trigger_hurt"; break;
}
local hndTrigger = null;
while( ( hndTrigger = Entities.FindByClassname( hndTrigger, strTrigger ) ) != null )
{
if ( hndTrigger.GetOrigin().z < 1400 )
{
switch( strState )
{
case "off": DoEntFire( "!self", "Disable", "", 0.0, null, hndTrigger ); break;
case "on": DoEntFire( "!self", "Enable", "", 0.0, null, hndTrigger ); break;
}
}
}
}
function c1m1_lowest_trigger_pushs( strState ) { c1m1_lowest_trigger_set( "push", strState ); }
function c1m1_lowest_trigger_hurts( strState ) { c1m1_lowest_trigger_set( "hurt", strState ); }
function c1m1_tank_telegraph( vecTeleportUpper )
{
printl( "c1m1_tank_telegraph RAN" );
local tblKeyvalues =
{
targetname = g_updateName + "_tank_yellexplode",
origin = vecTeleportUpper,
spawnflags = 48,
health = 10,
pitch = 100,
pitchstart = 100
};
local randNum = ( rand() % 4.5 ).tointeger();
if ( randNum == 0 ) { tblKeyvalues.message <- "HulkZombie.Yell"; }
if ( randNum == 1 ) { tblKeyvalues.message <- "HulkZombie.Throw"; }
if ( randNum == 2 ) { tblKeyvalues.message <- "HulkZombie.Attack"; }
if ( randNum == 3 ) { tblKeyvalues.message <- "HulkZombie.Throw.Fail"; }
if ( randNum == 4 ) { tblKeyvalues.message <- "HulkZombie.StartLedgeClimb"; }
printl( tblKeyvalues.message );
SpawnEntityFromTable( "ambient_generic", tblKeyvalues );
tblKeyvalues.message = "explode_1";
printl( tblKeyvalues.message );
SpawnEntityFromTable( "ambient_generic", tblKeyvalues );
SpawnEntityFromTable( "env_shake",
{
targetname = g_updateName + "_tank_shake",
origin = vecTeleportUpper,
radius = 1500,
frequency = 1.7,
duration = 2.1,
amplitude = 10
} );
SpawnEntityFromTable( "info_particle_system",
{
targetname = g_updateName + "_tank_burst",
origin = vecTeleportUpper + Vector( 0, 24, 10 ),
start_active = 1,
effect_name = "aircraft_destroy_smokepufflong"
} );
EntFire( g_updateName + "_tank_yellexplode", "PlaySound" );
EntFire( g_updateName + "_tank_shake", "StartShake" );
EntFire( g_updateName + "_tank_burst", "Stop", "", 0.5 );
EntFire( g_updateName + "_tank_*", "Kill", "", 2.0 );
}
function c1m1_thinkOn() { AddThinkToEnt( Entities.First(), "c1m1_tank_teleport_think" ); }
function c1m1_thinkOff() { AddThinkToEnt( Entities.First(), null ); }
function c1m1_tank_teleport_think()
{
if ( Director.IsTankInPlay() == true )
{
local hndPlayer = null;
while( ( hndPlayer = Entities.FindByClassname( hndPlayer, "player" ) ) != null )
{
if ( ! hndPlayer.IsSurvivor()
&& hndPlayer.GetZombieType() == 8 )
{
if ( hndPlayer.GetOrigin().z < 1400 )
{
local vecTeleportUpper = Vector( 2494, 5342, 2464 );
local vecTeleportLower = Vector( 1932, 5819, 1184 );
if ( g_elevatorStarted == false )
{
hndPlayer.SetOrigin( vecTeleportUpper );
hndPlayer.SetForwardVector( Vector( -0.392, 0.919, 0 ) );
c1m1_tank_telegraph( vecTeleportUpper );
}
if ( g_elevatorStarted == true )
{
hndPlayer.SetOrigin( vecTeleportLower );
hndPlayer.SetForwardVector( Vector( 0.311, -0.951, 0 ) );
EntFire( "worldspawn", "CallScriptFunction", "c1m1_thinkOff" );
}
}
}
}
}
}
}
c1m1_thinkOn();
c1m1_lowest_trigger_pushs( "off" );
c1m1_lowest_trigger_hurts( "off" );
local strTriggerName = g_updateName + "_tank_trigmult";
EntFire( "relay_elevator_reached_bottom", "AddOutput", "OnTrigger elevator_door_button1:Press::30:-1" );
EntFire( "elevator_button", "AddOutput", "OnPressed director:DisableTankFrustration::0:-1" );
EntFire( "elevator_button", "AddOutput", "OnPressed !self:RunScriptCode:g_elevatorStarted = true:0:-1" );
EntFire( "relay_top_button", "AddOutput", "OnTrigger !self:RunScriptCode:c1m1_lowest_trigger_pushs(\"on\"):14:-1" );
EntFire( "relay_top_button", "AddOutput", "OnTrigger !self:RunScriptCode:c1m1_lowest_trigger_hurts(\"on\"):18:-1" );
EntFire( "elevator_1", "AddOutput", "OnReachedBottom director:EnableTankFrustration::17:-1" );
EntFire( "elevator_1", "AddOutput", "OnReachedBottom worldspawn:CallScriptFunction:c1m1_thinkOff:4:-1" );
break;
Was, at the time, seemingly indestructible... it just came down to a warp maybe being contentious.
This Steam Forums thread:
CAN YOU PLEASE ADD A FREAKING INFECTED LADDER IN DEAD CENTER 1 FOR TANK?
https://steamcommunity.com/app/550/discussions/0/3166568651733182047/
Elaborates on the bad options:
1. Remove Tank altogether... Valve deliberately removed it from Coop and possibly didn't
test the spawn in Versus enough to even realize the broken spawn existed.
2. Over-zealous, "high production" approaches of the Tank "breaking into" the elevator,
that could never be 100% visually "sold" anyway. But the elevator lights are dynamic,
and can be shut off... but they do that when it's fully descended and needs prying open.
And the best options:
1. *.LMP edits to remove elevator doors (and LUMP 4 VISIBILITY also needed, or have Kerry
add a VScript func_areaportal/window that can work dynamically ANYWHERE), then modify
the shaft brushes to be a ladder. It's balanced because Survivors can trade-off risk
by looking down the shaft to shoot Tank pre-emptively while risking Death Charges down
it... there's not as much concern over Survivor preparedness/weaponry as with a warp.
2. FireWolfBoy loves top-floor Tanks as much as I do -- but they require stuck-warps and
fairplay from both teams. If he's happy with pushing the Tank spawn further on the
bottom to prevent spawn before Survivors get in elevator, I'll be happy with it too...
but option #1 is still valid for server-side modders if we can get LUMP 4 edits working.
####################################################################################################
####################################################################################################
DEATH TOLL 5 ROCKSLIDE PHYSICS SIMULATION -- IT'S COOL, BUT THE LANDING ARRANGEMENT UNRELIABLE
/*****************************************************************************
** THE BOATHOUSE SEISMIC ROCKSLIDE
**
** Creates a fake animation using prop_physics_override rocks so they slide
** down from the mountain which are then timely deleted and replaced with
** permanent prop_dynamics. Survivors must be beyond fire barrel to be inside
** the FINALE area, and can only view this animation from afar, so accuracy
** is strived for but not absolutely paramount.
**
** Every finale has a distinct "point of no return" except for Death Toll 5.
** This map's geometry doesn't allow for a one-way drop like others, but has
** always been craving treatment with a rockslide. Survivors going backwards
** on Coop isn't a huge deal because Commons still spawn, just they'll always
** spawn in "FINALE"-marked NAV then run the long way -- but in Versus, there's
** an exclusive "empty spawn area set" bug that fails to spawn any Commons and
** therefore fails to advance the finale if Survivors run back this far.
**
** The "empty spawn area set" bug starts roughly at the fire barrel which isn't
** ideal for a dynamic blockade such as this, so until that's fixed Survivors
** in Versus mode can still camp back-to-the-rocks. In theory, Infected ladders
** could be cloned behind the new rockslide nerfing the spot a bit, but reality
** is Survivor players would still use this to exploit and it still will never
** advance the finale if they run back up near fire barrel to summon a wave.
** The only good news is that since Commons spawn in FINALE area, we don't need
** any func_nav_blockers for these rocks since there's only spawns on one side.
**
** Modifies Coop and Versus basemodes only -- Survival and Holdout Mutation both
** use other ways to prevent backtracking.
*****************************************************************************/
printl( "Logic modified for rockslide point-of-no-return" );
// Permanent new tree to visually justify not slipping past blockade.
make_prop( "dynamic", "_rockslide_permtree", "models/props_foliage/trees_cluster01.mdl", "4220 -314 -221.9", "-11.9 255.7 -2.05", "shadow_no" );
// Seismic event functions.
function c10m5_rockslide_layer1()
{
// Thin blocker out in front of the funnel. Survivor-only and requires non-0
// "angles", so it'll always unavoidably block the rocks it's just moved away.
make_clip( "_rockslide_survivorclipa", "Survivors", 1, "-572 -120 0", "399 -100 1100", "4455 -54 -224", "0 35 0" );
make_clip( "_rockslide_survivorclipb", "Survivors", 1, "-17 -17 -17", "17 17 17", "4187 -415 -153" );
make_clip( "_rockslide_funneler_L1", "All and Physics", 1, "-572 -110 0", "500 -100 450", "4455 -54 -224", "0 35 0" );
make_clip( "_rockslide_funneler_L2", "All and Physics", 1, "500 100 0", "1000 110 700", "4687 -180 -224", "0 65 0" );
make_clip( "_rockslide_funneler_R1", "All and Physics", 1, "-572 100 0", "500 110 450", "4455 -54 -224", "0 35 0" );
make_clip( "_rockslide_funneler_R2", "All and Physics", 1, "500 -110 0", "1000 -100 700", "4422 251 -224", "0 0 0" );
SpawnEntityFromTable( "ambient_generic",
{
targetname = g_UpdateName + "_rockslide_fx_sound",
origin = Vector( 5093, 202, 123 ),
spawnflags = 48,
health = 10,
pitch = 50,
pitchstart = 50,
radius = 20000,
message = "physics/destruction/smash_rockcollapse1.wav"
} );
SpawnEntityFromTable( "env_shake",
{
targetname = g_UpdateName + "_rockslide_fx_shake",
origin = Vector( 5093, 202, 123 ),
radius = 2300,
frequency = 2,
duration = 10,
amplitude = 7
} );
SpawnEntityFromTable( "info_particle_system",
{
targetname = g_UpdateName + "_rockslide_fx_prtcl",
origin = Vector( 5093, 202, 103 ),
start_active = 1,
effect_name = "burning_General"
} );
EntFire( g_UpdateName + "_rockslide_fx_sound", "PlaySound" );
EntFire( g_UpdateName + "_rockslide_fx_shake", "StartShake" );
EntFire( g_UpdateName + "_rockslide_fx_prtcl", "Stop", "", 0.5 );
EntFire( g_UpdateName + "_rockslide_fx_*", "Kill", "", 2.0 );
// Where "spawnflags 1" is Clients and "damagetype 1" is CRUSH; trigger_hurt_ghost not needed
// since it won't allow SI Players to spawn inside the rock and even if they managed they'd
// die. No Mercy 4 elevator also uses CRUSH. Fail-safe is substituting prop_physics for prop_dynamic.
// Note that "spawnflags 3" was needed to include NPC's (Commons).
SpawnEntityFromTable( "trigger_hurt",
{
targetname = g_UpdateName + "_rockslide_trighurt_1a",
spawnflags = 3,
damagetype = 1,
damage = 10000,
damagecap = 10000,
filtername = "anv_globalfixes_filter_infected",
origin = Vector( 5294, 250, 254 )
} );
SpawnEntityFromTable( "trigger_hurt",
{
targetname = g_UpdateName + "_rockslide_trighurt_1b",
spawnflags = 3,
damagetype = 1,
damage = 10000,
damagecap = 10000,
filtername = "anv_globalfixes_filter_infected",
origin = Vector( 5294, 250, 164 )
} );
SpawnEntityFromTable( "trigger_hurt",
{
targetname = g_UpdateName + "_rockslide_trighurt_1c",
spawnflags = 3,
damagetype = 1,
damage = 10000,
damagecap = 10000,
filtername = "anv_globalfixes_filter_infected",
origin = Vector( 5221, 429, 258 )
} );
EntFire( g_UpdateName + "_rockslide_trighurt_1*", "AddOutput", "mins -120 -120 -120" );
EntFire( g_UpdateName + "_rockslide_trighurt_1*", "AddOutput", "maxs 120 120 120" );
EntFire( g_UpdateName + "_rockslide_trighurt_1*", "AddOutput", "solid 2" );
ZSpawn( { type = 9, pos = Vector( 4416, -38, -202 ), ang = Vector( 0, 0, 0 ) } );
ZSpawn( { type = 9, pos = Vector( 4416, -38, -202 ), ang = Vector( 0, 0, 0 ) } );
ZSpawn( { type = 9, pos = Vector( 4416, -38, -202 ), ang = Vector( 0, 0, 0 ) } );
ZSpawn( { type = 9, pos = Vector( 4416, -38, -202 ), ang = Vector( 0, 0, 0 ) } );
ZSpawn( { type = 9, pos = Vector( 4528, -32, -202 ), ang = Vector( 0, 0, 0 ) } );
ZSpawn( { type = 9, pos = Vector( 4528, -32, -202 ), ang = Vector( 0, 0, 0 ) } );
ZSpawn( { type = 9, pos = Vector( 4528, -32, -202 ), ang = Vector( 0, 0, 0 ) } );
ZSpawn( { type = 9, pos = Vector( 4528, -32, -202 ), ang = Vector( 0, 0, 0 ) } );
make_prop( "physics_ovr", "_rockslide_physlayer_1a", "models/props/cs_militia/militiarock01.mdl", "5294 250 254", "0 75 0", "shadow_yes", "solid_yes", "255 255 255", -1.0, 0.0, 0.02 );
make_prop( "physics_ovr", "_rockslide_physlayer_1b", "models/props/cs_militia/militiarock01.mdl", "5294 250 164", "0 -45 25", "shadow_yes", "solid_yes", "222 222 222", -1.0, 0.0, 0.01 );
make_prop( "physics_ovr", "_rockslide_physlayer_1c", "models/props/cs_militia/militiarock01.mdl", "5221 429 258", "45 75 70", "shadow_yes", "solid_yes", "177 177 177", -1.0, 0.0, 0.02 );
EntFire( g_UpdateName + "_rockslide_trighurt_1a", "SetParent", g_UpdateName + "_rockslide_physlayer_1a" );
EntFire( g_UpdateName + "_rockslide_trighurt_1b", "SetParent", g_UpdateName + "_rockslide_physlayer_1b" );
EntFire( g_UpdateName + "_rockslide_trighurt_1c", "SetParent", g_UpdateName + "_rockslide_physlayer_1c" );
}
function c10m5_rockslide_layer2()
{
make_prop( "physics_ovr", "_rockslide_physlayer_2a", "models/props/cs_militia/militiarock02.mdl", "5284 248 212", "0 75 0", "shadow_yes", "solid_yes", "255 255 255", -1.0, 0.0, 0.02 );
make_prop( "physics_ovr", "_rockslide_physlayer_2b", "models/props/cs_militia/militiarock02.mdl", "5274 248 154", "0 -45 25", "shadow_yes", "solid_yes", "222 222 222", -1.0, 0.0, 0.015 );
make_prop( "physics_ovr", "_rockslide_physlayer_2c", "models/props/cs_militia/militiarock02.mdl", "5211 229 255", "45 75 70", "shadow_yes", "solid_yes", "177 177 177", -1.0, 0.0, 0.02 );
}
function c10m5_rockslide_finalize()
{
// Fake-keyframe a slightly smoother transition by briefly having both sets
// of rocks active for occasions where the prop_physics land way off.
EntFire( g_UpdateName + "_rockslide_physlayer_*", "Kill", null, 0.17 );
EntFire( g_UpdateName + "_rockslide_static_*", "EnableShadow", null, 0.17 );
make_prop( "dynamic", "_rockslide_static_1a", "models/props/cs_militia/militiarock01.mdl", "4759 169 -96.8125", "18.3691 139.3506 313.0225", "shadow_no", "solid_yes", "255 255 255" );
make_prop( "dynamic", "_rockslide_static_1b", "models/props/cs_militia/militiarock01.mdl", "4378.5313 -104.9063 -86.4063", "348.2227 79.4092 234.6680", "shadow_no", "solid_yes", "222 222 222" );
make_prop( "dynamic", "_rockslide_static_1c", "models/props/cs_militia/militiarock01.mdl", "4480.125 -2.7188 -149.9375", "2.6807 26.7188 148.2275", "shadow_no", "solid_yes", "177 177 177" );
make_prop( "dynamic", "_rockslide_static_2a", "models/props/cs_militia/militiarock02.mdl", "4606.125 98.625 -81.7813", "321.9873 261.167 46.4941", "shadow_no", "solid_yes", "255 255 255" );
make_prop( "dynamic", "_rockslide_static_2b", "models/props/cs_militia/militiarock02.mdl", "4494.4688 -23.4688 -136.5", "10.415 39.7266 329.0625", "shadow_no", "solid_yes", "222 222 222" );
make_prop( "dynamic", "_rockslide_static_2c", "models/props/cs_militia/militiarock02.mdl", "4450.1875 -119.8125 -83.0938", "342.1143 278.4375 110.7861", "shadow_no", "solid_yes", "177 177 177" );
// Fail-safe if these weren't deleted with their Parents.
EntFire( g_UpdateName + "_rockslide_trighurt_1*", "Kill" );
}
SpawnEntityFromTable( "trigger_push",
{
targetname = g_UpdateName + "_rockslide_trigpush",
StartDisabled = 0,
spawnflags = 8,
pushdir = Vector( 0, 210, 90 ),
speed = 300,
origin = Vector( 4455, -54, -224 ),
angles = Vector( 0, 35, 0 )
} );
EntFire( g_UpdateName + "_rockslide_trigpush", "AddOutput", "mins -572 -216 0" );
EntFire( g_UpdateName + "_rockslide_trigpush", "AddOutput", "maxs 1000 216 800" );
EntFire( g_UpdateName + "_rockslide_trigpush", "AddOutput", "solid 2" );
SpawnEntityFromTable( "logic_timer",
{
targetname = g_UpdateName + "_rockslide_entireteam_timer",
StartDisabled = 1,
UseRandomTime = 1,
LowerRandomBound = 1,
UpperRandomBound = 4,
connections =
{
OnTimer =
{
cmd1 = "worldspawnCallScriptFunctionc10m5_rockslide_layer101",
cmd2 = "worldspawnCallScriptFunctionc10m5_rockslide_layer241",
cmd3 = "anv_mapfixes_rockslide_physlayer_1*DisableMotion121",
cmd4 = "anv_mapfixes_rockslide_trigpushSetPushSpeed100121",
cmd5 = "anv_mapfixes_rockslide_physlayer_2*DisableMotion141",
cmd6 = "worldspawnCallScriptFunctionc10m5_rockslide_finalize141",
cmd7 = "anv_mapfixes_rockslide_funneler_*Kill151",
cmd8 = "anv_mapfixes_rockslide_trig*Kill151",
cmd9 = "!selfKill151"
}
}
} );
SpawnEntityFromTable( "trigger_multiple",
{
targetname = g_UpdateName + "_rockslide_entireteam_trigmult",
StartDisabled = 0,
spawnflags = 1,
allowincap = 1,
entireteam = 2,
filtername = "anv_globalfixes_filter_survivor",
origin = Vector( 4692, -377, -125 )
} );
EntFire( g_UpdateName + "_rockslide_entireteam_trigmult", "AddOutput", "mins -100 -100 -100" );
EntFire( g_UpdateName + "_rockslide_entireteam_trigmult", "AddOutput", "maxs 100 100 100" );
EntFire( g_UpdateName + "_rockslide_entireteam_trigmult", "AddOutput", "solid 2" );
EntFire( g_UpdateName + "_rockslide_entireteam_trigmult", "AddOutput", "OnStartTouch anv_mapfixes_rockslide_entireteam_timer:Enable::0:-1" );
EntFire( g_UpdateName + "_rockslide_entireteam_trigmult", "AddOutput", "OnEndTouch anv_mapfixes_rockslide_entireteam_timer:Disable::0:-1" );
EntFire( g_UpdateName + "_rockslide_entireteam_trigmult", "AddOutput", "OnEndTouch anv_mapfixes_rockslide_entireteam_timer:ResetTimer::0:-1" );
####################################################################################################
####################################################################################################
VERSUS :: DEAD CENTER 1 LOBBY SKYLIGHT ATTACK -- IT'S COOL, BUT OFF-STANDARD
/*****************************************************************************
** THE VANNAH HOTEL LOBBY SKYLIGHT
**
** Transforms the end lobby into Versus-only SI Player attackable space by
** making a trigger_hurt only harm Survivors and adds 360-degree Beam ladders.
**
** The Beam ladders are spawned in immediately because they'll need to be seen
** from a distance. The trigger_once that causes the Commons to fall through
** the skylight is injected to modify the trigger_hurt (which needs to always
** kill Infected players except for the very end) and transform the rooftop
** above the skylight to have new clips and wrongway signs to assist players.
** The trigger_hurt_ghost can simply be deleted.
**
** Modifies Human-Controlled SI modes only.
*****************************************************************************/
con_comment( "FILE:\tRunScriptFile'd \"c1m1_versus_lobby.nut\" for SI Player attack." );
// Inject call into trigger_once and make it triggerable by Ghost SI.
DoEntFire( "!self", "AddOutput", "OnStartTouch worldspawn:CallScriptFunction:c1m1_lobby_skylight:0:-1", 0, null, Entities.FindByClassnameNearest( "trigger_once", Vector( 424, 4520, 1224 ), 1 ) );
DoEntFire( "!self", "AddOutput", "allowghost 1", 0, null, Entities.FindByClassnameNearest( "trigger_once", Vector( 424, 4520, 1224 ), 1 ) );
// Transform the rooftop once any Infected steps through that trigger. Due to
// the mechanics of the elevator and kill triggers, this is impossible until now.
function c1m1_lobby_skylight()
{
modify_trigfilter( "1600 4608 1648", "filter_survivor", "trigger_hurt" );
kill_entity( Entities.FindByClassnameNearest( "trigger_hurt_ghost", Vector( 1600, 4616, 1648 ), 1 ) );
make_clip( "_lobby_clipa", "SI Players", 1, "-608 -460 0", "-598 460 512", "1232 4636 1536" );
make_clip( "_lobby_clipb", "SI Players", 1, "598 -460 0", "608 460 512", "1232 4636 1536" );
make_clip( "_lobby_clipc", "SI Players", 1, "-608 -460 0", "608 -450 512", "1232 4636 1536" );
make_prop( "dynamic", "_wrongway_propa", "models/props_placeable/wrong_way.mdl", "632 4722 1631", "0 0 90", "shadow_no", "solid_no", "255 255 255", "17", "217" );
make_prop( "dynamic", "_wrongway_propb", "models/props_placeable/wrong_way.mdl", "1832 4598 1631", "0 180 90", "shadow_no", "solid_no", "255 255 255", "17", "217" );
}
// DO ALL THE FOLLOWING IMMEDIATELY
// Precache ladder material. Necessary because map has 0 func_simpleladder.
PrecacheEntityFromTable( { classname = "env_beam", texture = "tools/climb_versus.vmt" } );
// Delete left and right glass panels that would block SI Player climb.
kill_entity( Entities.FindByClassnameNearest( "func_breakable", Vector( 1040, 4632, 1564 ), 1 ) );
kill_entity( Entities.FindByClassnameNearest( "func_breakable", Vector( 1424, 4632, 1564 ), 1 ) );
// Left 360-degree Beam ladder.
SpawnEntityFromTable( "info_target",
{
targetname = g_UpdateName + "_beamladder_left_top",
origin = Vector( 1025, 4802, 1600 )
} );
SpawnEntityFromTable( "info_target",
{
targetname = g_UpdateName + "_beamladder_left_bottom",
origin = Vector( 1025, 4802, 1213 )
} );
SpawnEntityFromTable( "env_beam",
{
texture = "tools/climb_versus.vmt",
targetname = g_UpdateName + "_beamladder_left",
damage = 0,
LightningStart = g_UpdateName + "_beamladder_left_top",
LightningEnd = g_UpdateName + "_beamladder_left_bottom",
spawnflags = 1,
TextureScroll = 0,
BoltWidth = 16,
origin = Vector( 1025, 4802, 1213 )
} );
// Right 360-degree Beam ladder.
SpawnEntityFromTable( "info_target",
{
targetname = g_UpdateName + "_beamladder_right_top",
origin = Vector( 1408, 4813, 1600 )
} );
SpawnEntityFromTable( "info_target",
{
targetname = g_UpdateName + "_beamladder_right_bottom",
origin = Vector( 1408, 4813, 1213 )
} );
SpawnEntityFromTable( "env_beam",
{
texture = "tools/climb_versus.vmt",
targetname = g_UpdateName + "_beamladder_right",
damage = 0,
LightningStart = g_UpdateName + "_beamladder_right_top",
LightningEnd = g_UpdateName + "_beamladder_right_bottom",
spawnflags = 1,
TextureScroll = 0,
BoltWidth = 16,
origin = Vector( 1408, 4813, 1213 )
} );
// Left tree step-blockers.
make_clip( "_beamladder_left_step01", "SI Players", 1, "-11.30 -6.60 0", "2.60 5.30 18", "1030.3 4802.1 1213.97" );
make_clip( "_beamladder_left_step02", "SI Players", 1, "-11.20 -6.00 0", "2.50 5.20 18", "1030.3 4802.1 1231.97" );
make_clip( "_beamladder_left_step03", "SI Players", 1, "-11.10 -5.40 0", "2.40 5.10 18", "1030.3 4802.1 1249.97" );
make_clip( "_beamladder_left_step04", "SI Players", 1, "-11.00 -4.80 0", "2.30 5.00 18", "1030.3 4802.1 1267.97" );
make_clip( "_beamladder_left_step05", "SI Players", 1, "-10.90 -4.20 0", "2.20 4.90 18", "1030.3 4802.1 1285.97" );
make_clip( "_beamladder_left_step06", "SI Players", 1, "-10.80 -4.10 0", "2.10 4.80 18", "1030.3 4802.1 1303.97" );
make_clip( "_beamladder_left_step07", "SI Players", 1, "-10.70 -4.00 0", "2.00 4.70 18", "1030.3 4802.1 1321.97" );
make_clip( "_beamladder_left_step08", "SI Players", 1, "-10.60 -3.90 0", "1.90 4.60 18", "1030.3 4802.1 1339.97" );
make_clip( "_beamladder_left_step09", "SI Players", 1, "-10.56 -3.86 0", "1.86 4.56 18", "1030.3 4802.1 1357.97" );
make_clip( "_beamladder_left_step10", "SI Players", 1, "-10.52 -3.82 0", "1.82 4.52 18", "1030.3 4802.1 1375.97" );
make_clip( "_beamladder_left_step11", "SI Players", 1, "-10.48 -3.78 0", "1.78 4.48 18", "1030.3 4802.1 1393.97" );
make_clip( "_beamladder_left_step12", "SI Players", 1, "-10.44 -3.74 0", "1.74 4.44 18", "1030.3 4802.1 1411.97" );
make_clip( "_beamladder_left_step13", "SI Players", 1, "-10.40 -3.70 0", "1.70 4.40 18", "1030.3 4802.1 1429.97" );
make_clip( "_beamladder_left_step14", "SI Players", 1, "-10.36 -3.66 0", "1.66 4.36 18", "1030.3 4802.1 1447.97" );
make_clip( "_beamladder_left_step15", "SI Players", 1, "-10.32 -3.62 0", "1.62 4.32 18", "1030.3 4802.1 1465.97" );
make_clip( "_beamladder_left_step16", "SI Players", 1, "-10.28 -3.58 0", "1.58 4.28 18", "1030.3 4802.1 1483.97" );
make_clip( "_beamladder_left_step17", "SI Players", 1, "-10.24 -3.54 0", "1.54 4.24 18", "1030.3 4802.1 1501.97" );
make_clip( "_beamladder_left_step18", "SI Players", 1, "-10.20 -3.50 0", "1.50 4.20 18", "1030.3 4802.1 1519.97" );
make_clip( "_beamladder_left_step19", "SI Players", 1, "-10.16 -3.46 0", "1.46 4.16 18", "1030.3 4802.1 1537.97" );
make_clip( "_beamladder_left_step20", "SI Players", 1, "-10.12 -3.42 0", "1.42 4.12 18", "1030.3 4802.1 1555.97" );
make_clip( "_beamladder_left_step21", "SI Players", 1, "-10.08 -3.36 0", "1.36 4.08 18", "1030.3 4802.1 1573.97" );
// Right tree step-blockers.
make_clip( "_beamladder_right_step01", "SI Players", 1, "-12.30 -8.60 0", "2.60 7.30 18", "1415.3 4810.6 1213.97" );
make_clip( "_beamladder_right_step02", "SI Players", 1, "-12.20 -8.00 0", "2.50 7.20 18", "1415.3 4810.6 1231.97" );
make_clip( "_beamladder_right_step03", "SI Players", 1, "-12.10 -7.40 0", "2.40 7.10 18", "1415.3 4810.6 1249.97" );
make_clip( "_beamladder_right_step04", "SI Players", 1, "-12.00 -6.80 0", "2.30 7.00 18", "1415.3 4810.6 1267.97" );
make_clip( "_beamladder_right_step05", "SI Players", 1, "-11.90 -6.20 0", "2.20 6.90 18", "1415.3 4810.6 1285.97" );
make_clip( "_beamladder_right_step06", "SI Players", 1, "-11.80 -6.10 0", "2.10 6.80 18", "1415.3 4810.6 1303.97" );
make_clip( "_beamladder_right_step07", "SI Players", 1, "-11.70 -6.00 0", "2.00 6.70 18", "1415.3 4810.6 1321.97" );
make_clip( "_beamladder_right_step08", "SI Players", 1, "-11.60 -5.90 0", "1.90 6.60 18", "1415.3 4810.6 1339.97" );
make_clip( "_beamladder_right_step09", "SI Players", 1, "-11.56 -5.86 0", "1.86 6.56 18", "1415.3 4810.6 1357.97" );
make_clip( "_beamladder_right_step10", "SI Players", 1, "-11.52 -5.82 0", "1.82 6.52 18", "1415.3 4810.6 1375.97" );
make_clip( "_beamladder_right_step11", "SI Players", 1, "-11.48 -5.78 0", "1.78 6.48 18", "1415.3 4810.6 1393.97" );
make_clip( "_beamladder_right_step12", "SI Players", 1, "-11.44 -5.74 0", "1.74 6.44 18", "1415.3 4810.6 1411.97" );
make_clip( "_beamladder_right_step13", "SI Players", 1, "-11.40 -5.70 0", "1.70 6.40 18", "1415.3 4810.6 1429.97" );
make_clip( "_beamladder_right_step14", "SI Players", 1, "-11.36 -5.66 0", "1.66 6.36 18", "1415.3 4810.6 1447.97" );
make_clip( "_beamladder_right_step15", "SI Players", 1, "-11.32 -5.62 0", "1.62 6.32 18", "1415.3 4810.6 1465.97" );
make_clip( "_beamladder_right_step16", "SI Players", 1, "-11.28 -5.58 0", "1.58 6.28 18", "1415.3 4810.6 1483.97" );
make_clip( "_beamladder_right_step17", "SI Players", 1, "-11.24 -5.54 0", "1.54 6.24 18", "1415.3 4810.6 1501.97" );
make_clip( "_beamladder_right_step18", "SI Players", 1, "-11.20 -5.50 0", "1.50 6.20 18", "1415.3 4810.6 1519.97" );
make_clip( "_beamladder_right_step19", "SI Players", 1, "-11.16 -5.46 0", "1.46 6.16 18", "1415.3 4810.6 1537.97" );
make_clip( "_beamladder_right_step20", "SI Players", 1, "-11.12 -5.42 0", "1.42 6.12 18", "1415.3 4810.6 1555.97" );
make_clip( "_beamladder_right_step21", "SI Players", 1, "-11.08 -5.36 0", "1.36 6.08 18", "1415.3 4810.6 1573.97" );
// Backboards to reduce time wasted climbing backs of trees.
make_clip( "_beamladder_left_guide", "SI Players", 1, "-17 -2 -80", "17 24 10", "1026 4840 1430" );
make_clip( "_beamladder_right_guide", "SI Players", 1, "-17 -2 -80", "17 24 10", "1411 4840 1430" );
// Pushers to avoid hitting head. Speed 55 is just good enough to not push off.
make_trigpush( "_beamladder_left_push", "Infected", 55, "0 -90 0", "-17 -27 -90", "17 -4 10", "1026 4840 1430" );
make_trigpush( "_beamladder_right_push", "Infected", 55, "0 -90 0", "-17 -27 -90", "17 -4 10", "1409 4840 1430" );
// Force-feedback force crouch as fake animation and discourage non-climbing use.
make_trigmove( "_beamladder_left_forceduck", "Duck", "-12.30 -7.60 0", "3.60 6.30 298", "1030.3 4802.1 1293.97" );
make_trigmove( "_beamladder_left_forcejump", "Jump", "-12.30 -7.60 0", "3.60 6.30 298", "1030.3 4802.1 1293.97" );
make_trigmove( "_beamladder_right_forceduck", "Duck", "-13.30 -9.60 0", "3.60 8.30 298", "1415.3 4810.6 1293.97" );
make_trigmove( "_beamladder_right_forcejump", "Jump", "-13.30 -9.60 0", "3.60 8.30 298", "1415.3 4810.6 1293.97" );
####################################################################################################
####################################################################################################
VERSUS :: MISCELLANEOUS CUT CONTENT
Dead Center 3 Skylight Attack:
make_prop( "dynamic", "_placeholder_01", "models/props_misc/ceda_banner.mdl", "6649 -2671 456", "-2 178 -95", "shadow_no", "solid_no", "100 100 100" );
make_prop( "dynamic", "_placeholder_02", "models/props_highway/plywood_03.mdl", "6657.6 -2592 619.5", "27 0 0", "shadow_no" );
make_prop( "dynamic", "_placeholder_03", "models/props_highway/plywood_02.mdl", "6657.6 -2640.9 619.5", "27 0 0", "shadow_no" );
make_prop( "dynamic", "_placeholder_04", "models/props_highway/plywood_03.mdl", "6657.6 -2495.1 620.4", "-207 180 0", "shadow_no" );
make_prop( "dynamic", "_placeholder_05", "models/props_highway/plywood_02.mdl", "6658.1 -2688 619.5", "-180 0 0", "shadow_no" );
make_prop( "dynamic", "_placeholder_06", "models/props_highway/plywood_01.mdl", "6630 -2729.6 634.7", "-180 -32 12", "shadow_no" );
make_prop( "dynamic", "_placeholder_07", "models/c1_chargerexit/plywood_cent.mdl", "6510 -2505.5 322.25", "180 0 63", "shadow_no" );
make_prop( "dynamic", "_placeholder_08", "models/c1_chargerexit/plywood_cent.mdl", "6420 -2614 269", "180 180 116", "shadow_no" );
make_prop( "dynamic", "_placeholder_09", "models/c1_chargerexit/plywood_cent.mdl", "6414 -2570 269.1", "180 0 116", "shadow_no" );
make_prop( "dynamic", "_placeholder_10", "models/c1_chargerexit/plywood_cent.mdl", "6550 -2575 275.7", "180 -10 115", "shadow_no" );
make_prop( "dynamic", "_placeholder_11", "models/props_highway/plywood_03.mdl", "6550 -2440 654", "8 24 -24", "shadow_no" );
make_prop( "dynamic", "_placeholder_12", "models/props_highway/plywood_01.mdl", "6680 -2500 630.2", "40 6.8 -11", "shadow_no" );
make_prop( "dynamic", "_placeholder_13", "models/props_highway/plywood_03.mdl", "6257.3 -2640 613", "27 180 0", "shadow_no" );
make_prop( "dynamic", "_placeholder_14", "models/props_highway/plywood_03.mdl", "6257.3 -2544 613", "27 180 0", "shadow_no" );
make_prop( "dynamic", "_placeholder_15", "models/props_highway/plywood_03.mdl", "6270.4 -2496 625", "-80 180 0", "shadow_no" );
make_prop( "dynamic", "_placeholder_16", "models/props_highway/plywood_03.mdl", "6257 -2640 615.5", "-207 0 0", "shadow_no" );
make_prop( "dynamic", "_placeholder_17", "models/props_highway/plywood_03.mdl", "6346 -2440 87.5", "-30 -10 177", "shadow_no" );
make_prop( "dynamic", "_placeholder_18", "models/c1_chargerexit/plywood_cent.mdl", "6450 -2780 273.5", "200 160 100", "shadow_no" );
make_prop( "dynamic", "_placeholder_19", "models/c1_chargerexit/mall_rampbundle.mdl", "6438 -3004 280", "0 120 0", "shadow_no" );
make_prop( "dynamic", "_the_main_event", "models/props_update/c1m3_skylight_nodraw.mdl", "6460 -2575 584", "0 0 0", "shadow_no" );
make_ladder( "ladder_bannerlower_cloned_sodavent", "754 -1300 351", "7398 -3964 64", "0 180 0", "-1 0 0" );
make_ladder( "ladder_bannerupper_cloned_sodavent", "754 -1300 351", "7398 -3964 192", "0 180 0", "-1 0 0" );
TO_DO:
See pre-QA anv_mapfixes.nut (later to be split into anv_versus.nut)
A lot of open attacks (specifically Death Toll 4 rooftops) that would've opened
players up to NODRAW / culling / Skybox boundaries... all *.LMP / BSP hexedit candidates.
####################################################################################################
####################################################################################################
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment