L4D2 Vscript Helpers

From Valve Developer Community
Revision as of 09:32, 30 March 2022 by Eyal282 (talk | contribs)
Jump to navigation Jump to search

Sometimes an idea for a function is not immediately available in the List of L4D2 Script Functions

This function library was built to help you get more functions for your code.

/*
Eyal282's helper scripts:
*/

/**
 * Helper function that prevents the game from giving a pistol to an incapped player.
 */
function OnGameEvent_item_pickup( params )
{
	local ent = null
	
	if("userid" in params && params.userid == 0){
		return;
	}
	
	ent = GetPlayerFromUserID(params["userid"])

	if(ent.IsIncapacitated())
	{
		local classname = "weapon_" + params["item"];

		RemovePlayerWeapon(ent, classname);
	}
}


/**
 * Helper function to prevent witches from instakilling survivors. This is done by blocking the damage via AllowTakeDamage and manually setting the health to 0.
 * It also caps the damage to 100 to prevent dealing 400 damage when incapped. Note that she still shreds through that 400 health in under a second.
 */
function AllowTakeDamage(damageTable)
{
	local attacker = damageTable.Attacker;
	local victim = damageTable.Victim;

	if (attacker != null && victim != null && attacker.GetClassname() == "witch")
	{
		if(victim != null && victim.GetClassname() == "player" && victim.IsSurvivor())
		{
			if(damageTable.DamageDone > victim.GetMaxHealth())
				damageTable.DamageDone = victim.GetMaxHealth(); // Caps incap damage at 100.
				
			if(damageTable.DamageDone >= victim.GetHealth())
			{
				DoEntFire( "!self", "SetHealth", "0", 0, victim, victim );
			}
			else
			{
				victim.SetHealth(victim.GetHealth() - damageTable.DamageDone);
			}

			damageTable.DamageDone = 0;
			return false;
		}
	}
}


/**
 * Helper function to equip adrenaline and pipe bomb found in safe room to avoid wasting time, happens on grabbing any item, or when pressing E on safe room door.
 * If someone has a tactic to make this run when the player spawns, please alter it.
 */
function OnGameEvent_player_use( params )
{

	local autoEquipItems =
	{
		weapon_adrenaline = "Adrenaline"
		weapon_pipe_bomb = "Pipe Bomb"
	}

	local player = GetPlayerFromUserID( params["userid"] );
	
	if ( player )
	{
		if(player.IsSurvivor() && IsEntityInStartSafeRoom(player))
		{
			foreach(key, val in autoEquipItems)
			{
				if(DoesPlayerHaveWeapon(player, key))
				{
					continue;
				}
				else
				{
					local weapon = null;
					while ( weapon = Entities.FindByClassname( weapon, key + "_spawn" ) )
					{
						if ( weapon.IsValid() )
						{
							if( IsEntityInStartSafeRoom(weapon) )
							{
								DoEntFire( "!self", "Use", "", 0, player, weapon );
								
								ClientPrint(player, 5, "Equipped " + val + " from safe room.");
								
								break;
							}
						}
					}
				
				}
			}
		}
	}
}

/**
 * Checks whether or not an entity is in the starting safe room
 * 
 * @param entity     Entity to check
 * @return           true if entity is in start safe room, false otherwise.
 * @notes			 This works for the safe area on the first chapter of a campaign.
 */
function IsEntityInStartSafeRoom(entity)
{
	local origin = entity.GetOrigin();
	
	local navArea = NavMesh.GetNearestNavArea(origin, 2048, true, true);

	if(navArea != null)
	{
		// Some stupid maps like Blood Harvest finale and The Passing finale have CHECKPOINT inside a FINALE marked area.
		if(navArea.HasSpawnAttributes(64))
		{
			return false;
		}
		// https://developer.valvesoftware.com/wiki/List_of_L4D_Series_Nav_Mesh_Attributes
		else if(navArea.HasSpawnAttributes(2048) && GetFlowPercentForPosition(origin, false) <= 50.0)
		{
			return true;
		}

	}

	return false;
}



/**
 * Checks whether or not an entity is in the ending safe room
 * 
 * @param entity     Entity to check
 * @return           true if entity is in end safe room, false otherwise.
 * @notes			 It is unknown whether or not this works for rescue vehicles.
 */
function IsEntityInEndSafeRoom(entity)
{
	local origin = entity.GetOrigin();
	
	local navArea = NavMesh.GetNearestNavArea(origin, 2048, true, true);

	if(navArea != null)
	{
		// Some stupid maps like Blood Harvest finale and The Passing finale have CHECKPOINT inside a FINALE marked area.
		if(navArea.HasSpawnAttributes(64))
		{
			return false;
		}
		// https://developer.valvesoftware.com/wiki/List_of_L4D_Series_Nav_Mesh_Attributes
		else if(navArea.HasSpawnAttributes(2048) && GetFlowPercentForPosition(origin, false) >= 50.0)
		{
			return true;
		}

	}

	return false;
}



/**
 * Checks whether or not an player owns a weapon by its classname
 * 
 * @param player     player to check
 * @param classname  classname string to check
 * @return           true if player has the weapon in inventory, false otherwise
 */
function DoesPlayerHaveWeapon(player, classname)
{
	local invTable = {};

	GetInvTable(player, invTable);
	
	foreach(slot, weapon in invTable)
	{
		if(weapon.GetClassname() == classname)
		{
			return true;
		}
	}

	return false;
}



/**
 * Removes a weapon from a client's inventory by classname
 * 
 * @param player     player to remove
 * @param classname  classname string to remove.
 * @noreturn
 */
function RemovePlayerWeapon(player, classname)
{
	local invTable = {};

	GetInvTable(player, invTable);
	
	foreach(slot, weapon in invTable)
	{
		if(weapon.GetClassname() == classname)
		{
			weapon.Kill();
		}
	}

	return false;
}



/**
 * Checks whether or an entity is on the ground.
 * 
 * @param entity    Entity to check
 * @return			true if entity is on the ground, false otherwise ( in the air )
 * @error           Entity cannot have m_hGroundEntity, likely only for entities without physics.
 * @notes 			It is unknown what happens for surfing entities on slopes.
 */
function IsEntityOnGround(entity)
{
	local groundEntity = NetProps.GetPropEntity(entity, "m_hGroundEntity");

	// Cannot find a ground entity = air.
	if(groundEntity == null)
		return false;
	
	return true;
}



/**
 * Checks whether or a player is throwing a tank rock. This can work on non-tanks by looping all rocks and finding their thrower
 * 
 * @param player		Player to check
 * @return			true if player has an active rock, false otherwise.
 * @notes 			Due to checking every rock's owner, this won't error on non-tanks.
 * @notes			This is not exclusive to the tank rock animation. This is true for as long as the tank has a rock active.
 */
function IsPlayerThrowingRock(player)
{
	local rock = null;
	
	while((rock = Entities.FindByClassname(rock, "tank_rock")))
	{
		if(NetProps.GetPropEntity(rock, "m_hThrower") == player)
			return true;
	}

	return false;
}



/**
 * Checks how many seconds a burning tank has to live from fire alone.
 * 
 * @param player		Player to check
 * @return			returns a float of the seconds left for the player.
 * @notes			This works for every player, even if not a tank, and works if the player is not burning. It's just a formula.
 */
function GetTankBurnSecondsLeft(player)
{
	local gamemode = Convars.GetStr("mp_gamemode")
	local difficulty = Convars.GetStr("z_difficulty")
	local burnDuration = 0.0;

	switch(gamemode)
	{
		case "versus" : case "survival" :
			burnDuration = Convars.GetFloat("tank_burn_duration");
		break;

		default:
			switch(difficulty)
			{
				case "Impossible":
					burnDuration = Convars.GetFloat("tank_burn_duration_expert");
				break;

				case "Hard":
					burnDuration = Convars.GetFloat("tank_burn_duration_hard");
				break;

				default :
					burnDuration = Convars.GetFloat("tank_burn_duration");
				break;
			}
		break;
	}

	return (player.GetHealth().tofloat() / player.GetMaxHealth().tofloat()) * burnDuration;
}




/*
Anonymous's helper scripts:
*/

function IsMissionFinale()
{
	if(IsMissionFinalMap())
	{
		return true;
	}

	return false;

}