//checked includes changed to match cerberus output
#include maps/mp/zombies/_zm_perks;
#include maps/mp/zombies/_zm_chugabud;
#include maps/mp/zombies/_zm_equipment;
#include maps/mp/zombies/_zm_buildables;
#include maps/mp/gametypes_zm/_gameobjects;
#include maps/mp/zombies/_zm_pers_upgrades_functions;
#include maps/mp/zombies/_zm_stats;
#include maps/mp/_demo;
#include maps/mp/gametypes_zm/_hud_util;
#include maps/mp/zombies/_zm_utility;
#include maps/mp/_utility;
#include common_scripts/utility;

laststand_global_init() //checked matches cerberus output
{
	level.const_laststand_getup_count_start = 0;
	level.const_laststand_getup_bar_start = 0.5;
	level.const_laststand_getup_bar_regen = 0.0025;
	level.const_laststand_getup_bar_damage = 0.1;
}

init() //checked matches cerberus output //checked matches beta dump
{
	if ( level.script == "frontend" )
	{
		return;
	}
	laststand_global_init();
	level.revive_tool = "syrette_zm";
	precacheitem( level.revive_tool );
	precachestring( &"ZOMBIE_BUTTON_TO_REVIVE_PLAYER" );
	precachestring( &"ZOMBIE_PLAYER_NEEDS_TO_BE_REVIVED" );
	precachestring( &"ZOMBIE_PLAYER_IS_REVIVING_YOU" );
	precachestring( &"ZOMBIE_REVIVING" );
	if ( !isDefined( level.laststandpistol ) )
	{
		level.laststandpistol = "m1911";
		precacheitem( level.laststandpistol );
	}
	level thread revive_hud_think();
	level.primaryprogressbarx = 0;
	level.primaryprogressbary = 110;
	level.primaryprogressbarheight = 4;
	level.primaryprogressbarwidth = 120;
	level.primaryprogressbary_ss = 280;
	if ( getDvar( "revive_trigger_radius" ) == "" )
	{
		setdvar( "revive_trigger_radius", "40" );
	}
	level.laststandgetupallowed = 0;
}

player_is_in_laststand() //checked changed to match cerberus output //changed at own discretion
{
	if ( !is_true( self.no_revive_trigger ) && isDefined( self.revivetrigger ) )
	{
		return 1;
	}
	if ( is_true( self.laststand ) )
	{
		return 1;
	}
	return 0;
}

player_num_in_laststand() //checked changed to match cerberus output //checked matches beta dump
{
	num = 0;
	players = get_players();
	for ( i = 0; i < players.size; i++ )
	{
		if ( players[ i ] player_is_in_laststand() )
		{
			num++;
		}
	}
	return num;
}

player_all_players_in_laststand() //checked changed at own discretion //checked matches beta dump
{
	if ( player_num_in_laststand() == get_players().size )
	{
		return 1;
	}
	return 0;
}

player_any_player_in_laststand() //checked changed at own discretion //checked matches beta dump
{
	if ( player_num_in_laststand() > 0 )
	{
		return 1;
	}
	return 0;
}

player_last_stand_stats( einflictor, attacker, idamage, smeansofdeath, sweapon, vdir, shitloc, psoffsettime, deathanimduration ) //checked matches cerberus output
{
	if ( isDefined( attacker ) && isplayer( attacker ) && attacker != self )
	{
		if ( level.gametype == "zcleansed" )
		{
			maps/mp/_demo::bookmark( "kill", getTime(), self, attacker, 0, einflictor );
		}
		if ( level.gametype == "zcleansed" )
		{
			if ( isDefined( attacker.is_zombie ) && !attacker.is_zombie )
			{
				attacker.kills++;
			}
			else
			{
				attacker.downs++;
			}
		}
		else
		{
			attacker.kills++;
		}
		attacker maps/mp/zombies/_zm_stats::increment_client_stat( "kills" );
		attacker maps/mp/zombies/_zm_stats::increment_player_stat( "kills" );
		if ( isDefined( sweapon ) )
		{
			dmgweapon = sweapon;
			if ( is_alt_weapon( dmgweapon ) )
			{
				dmgweapon = weaponaltweaponname( dmgweapon );
			}
			attacker addweaponstat( dmgweapon, "kills", 1 );
		}
		if ( is_headshot( sweapon, shitloc, smeansofdeath ) )
		{
			attacker.headshots++;
			attacker maps/mp/zombies/_zm_stats::increment_client_stat( "headshots" );
			attacker addweaponstat( sweapon, "headshots", 1 );
			attacker maps/mp/zombies/_zm_stats::increment_player_stat( "headshots" );
		}
	}
	self increment_downed_stat();
	if ( flag( "solo_game" ) && !self.lives && getnumconnectedplayers() < 2 )
	{
		self maps/mp/zombies/_zm_stats::increment_client_stat( "deaths" );
		self maps/mp/zombies/_zm_stats::increment_player_stat( "deaths" );
		self maps/mp/zombies/_zm_pers_upgrades_functions::pers_upgrade_jugg_player_death_stat();
	}
}

increment_downed_stat() //checked matches cerberus output
{
	if ( level.gametype != "zcleansed" )
	{
		self.downs++;
	}
	self maps/mp/zombies/_zm_stats::increment_client_stat( "downs" );
	self add_weighted_down();
	self maps/mp/zombies/_zm_stats::increment_player_stat( "downs" );
	zonename = self get_current_zone();
	if ( !isDefined( zonename ) )
	{
		zonename = "";
	}
	self recordplayerdownzombies( zonename );
}

playerlaststand( einflictor, attacker, idamage, smeansofdeath, sweapon, vdir, shitloc, psoffsettime, deathanimduration ) //checked matches cerberus output //checked changed to match beta dump
{
	self notify( "entering_last_stand" );
	if ( isDefined( level._game_module_player_laststand_callback ) )
	{
		self [[ level._game_module_player_laststand_callback ]]( einflictor, attacker, idamage, smeansofdeath, sweapon, vdir, shitloc, psoffsettime, deathanimduration );
	}
	if ( self player_is_in_laststand() )
	{
		return;
	}
	if ( is_true( self.in_zombify_call ) )
	{
		return;
	}
	self thread player_last_stand_stats( einflictor, attacker, idamage, smeansofdeath, sweapon, vdir, shitloc, psoffsettime, deathanimduration );
	if ( isDefined( level.playerlaststand_func ) )
	{
		[[ level.playerlaststand_func ]]( einflictor, attacker, idamage, smeansofdeath, sweapon, vdir, shitloc, psoffsettime, deathanimduration );
	}
	self.health = 1;
	self.laststand = 1;
	self.ignoreme = 1;
	self thread maps/mp/gametypes_zm/_gameobjects::onplayerlaststand();
	self thread maps/mp/zombies/_zm_buildables::onplayerlaststand();
	if ( !is_true( self.no_revive_trigger ) )
	{
		self revive_trigger_spawn();
	}
	else
	{
		self undolaststand();
	}
	if ( is_true( self.is_zombie ) )
	{
		self takeallweapons();
		if ( isDefined( attacker ) && isplayer( attacker ) && attacker != self )
		{
			attacker notify( "killed_a_zombie_player", eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration );
		}
	}
	else
	{
		self laststand_disable_player_weapons();
		self laststand_give_pistol();
	}
	if ( is_true( level.playerSuicideAllowed ) && get_players().size > 1 )
	{
		if ( !isDefined( level.canplayersuicide ) || self [[ level.canplayersuicide ]]() )
		{
			self thread suicide_trigger_spawn();
		}
	}
	if ( isDefined( self.disabled_perks ) )
	{
		self.disabled_perks = [];
	}
	if ( level.laststandgetupallowed )
	{
		self thread laststand_getup();
	}
	else
	{
		bleedout_time = getDvarFloat( "player_lastStandBleedoutTime" );
		self thread laststand_bleedout( bleedout_time );
	}
	if ( level.gametype != "zcleansed" )
	{
		maps/mp/_demo::bookmark( "zm_player_downed", getTime(), self );
	}
	self notify( "player_downed" );
	self thread refire_player_downed();
	self thread cleanup_laststand_on_disconnect();
}

refire_player_downed() //checked matches cerberus output
{
	self endon( "player_revived" );
	self endon( "death" );
	self endon( "disconnect" );
	wait 1;
	if ( self.num_perks )
	{
		self notify( "player_downed" );
	}
}

laststand_allowed( sweapon, smeansofdeath, shitloc ) //checked matches cerberus output
{
	if ( level.laststandpistol == "none" )
	{
		return 0;
	}
	return 1;
}

laststand_disable_player_weapons() //checked partially changed to match cerberus output //did not change while loop to for loop to prevent infinite loop bug due to continue
{
	weaponinventory = self getweaponslist( 1 );
	self.lastactiveweapon = self getcurrentweapon();
	if ( self isthrowinggrenade() && is_offhand_weapon( self.lastactiveweapon ) )
	{
		primaryweapons = self getweaponslistprimaries();
		if ( isDefined( primaryweapons ) && primaryweapons.size > 0 )
		{
			self.lastactiveweapon = primaryweapons[ 0 ];
			self switchtoweaponimmediate( self.lastactiveweapon );
		}
	}
	self setlaststandprevweap( self.lastactiveweapon );
	self.laststandpistol = undefined;
	self.hadpistol = 0;
	if ( isDefined( self.weapon_taken_by_losing_specialty_additionalprimaryweapon ) && self.lastactiveweapon == self.weapon_taken_by_losing_specialty_additionalprimaryweapon )
	{
		self.lastactiveweapon = "none";
		self.weapon_taken_by_losing_specialty_additionalprimaryweapon = undefined;
	}
	i = 0;
	while ( i < weaponinventory.size )
	{
		weapon = weaponinventory[ i ];
		class = weaponclass( weapon );
		if ( issubstr( weapon, "knife_ballistic_" ) )
		{
			class = "knife";
		}
		if ( ( class == "pistol" || class == "pistol spread" ) && !isDefined( self.laststandpistol ) || class == "pistolspread" && !isDefined( self.laststandpistol )  )
		{
			self.laststandpistol = weapon;
			self.hadpistol = 1;
		}
		if ( weapon == "syrette_zm" )
		{
			self maps/mp/zombies/_zm_stats::increment_client_stat( "failed_sacrifices" );
			self maps/mp/zombies/_zm_stats::increment_player_stat( "failed_sacrifices" );
		}
		else if ( is_zombie_perk_bottle( weapon ) )
		{
			self takeweapon( weapon );
			self.lastactiveweapon = "none";
			i++;
			continue;
		}
		if ( isDefined( get_gamemode_var( "item_meat_name" ) ) )
		{
			if ( weapon == get_gamemode_var( "item_meat_name" ) )
			{
				self takeweapon( weapon );
				self.lastactiveweapon = "none";
				i++;
				continue;
			}
		}
		i++;
	}
	if ( isDefined( self.hadpistol ) && self.hadpistol == 1 && isDefined( level.zombie_last_stand_pistol_memory ) )
	{
		self [[ level.zombie_last_stand_pistol_memory ]]();
	}
	if ( !isDefined( self.laststandpistol ) )
	{
		self.laststandpistol = level.laststandpistol;
	}
	self disableweaponcycling();
	self notify( "weapons_taken_for_last_stand" );
}

laststand_enable_player_weapons() //checked matches cerberus output
{
	if ( !is_true( self.hadpistol ) && isDefined( self.laststandpistol ) )
	{
		self takeweapon( self.laststandpistol );
	}
	if ( isDefined( self.hadpistol ) && self.hadpistol == 1 && isDefined( level.zombie_last_stand_ammo_return ) )
	{
		[[ level.zombie_last_stand_ammo_return ]]();
	}
	self enableweaponcycling();
	self enableoffhandweapons();
	if ( isDefined( self.lastactiveweapon ) && self.lastactiveweapon != "none" && self hasweapon( self.lastactiveweapon ) && !is_placeable_mine( self.lastactiveweapon ) && !is_equipment( self.lastactiveweapon ) )
	{
		self switchtoweapon( self.lastactiveweapon );
	}
	else
	{
		primaryweapons = self getweaponslistprimaries();
		if ( isDefined( primaryweapons ) && primaryweapons.size > 0 )
		{
			self switchtoweapon( primaryweapons[ 0 ] );
		}
	}
}

laststand_clean_up_on_disconnect( playerbeingrevived, revivergun ) //checked matches cerberus output
{
	self endon( "do_revive_ended_normally" );
	revivetrigger = playerbeingrevived.revivetrigger;
	playerbeingrevived waittill( "disconnect" );
	if ( isDefined( revivetrigger ) )
	{
		revivetrigger delete();
	}
	self cleanup_suicide_hud();
	if ( isDefined( self.reviveprogressbar ) )
	{
		self.reviveprogressbar destroyelem();
	}
	if ( isDefined( self.revivetexthud ) )
	{
		self.revivetexthud destroy();
	}
	self revive_give_back_weapons( revivergun );
}

laststand_clean_up_reviving_any( playerbeingrevived ) //checked changed to match cerberus output
{
	self endon( "do_revive_ended_normally" );
	playerbeingrevived waittill_any( "disconnect", "zombified", "stop_revive_trigger" );
	self.is_reviving_any--;

	if ( self.is_reviving_any < 0 )
	{
		self.is_reviving_any = 0;
	}
}

laststand_give_pistol() //checked changed to match cerberus output
{
	/*
/#
	assert( isDefined( self.laststandpistol ) );
#/
/#
	assert( self.laststandpistol != "none" );
#/
	*/
	if ( isDefined( level.zombie_last_stand ) )
	{
		[[ level.zombie_last_stand ]]();
	}
	else
	{
		self giveweapon( self.laststandpistol );
		self givemaxammo( self.laststandpistol );
		self switchtoweapon( self.laststandpistol );
	}
}

laststand_bleedout( delay ) //checked changed to match cerberus output
{
	self endon( "player_revived" );
	self endon( "player_suicide" );
	self endon( "zombified" );
	self endon( "disconnect" );
	if ( is_true( self.is_zombie ) || is_true( self.no_revive_trigger ) )
	{
		self notify( "bled_out" );
		wait_network_frame();
		self bleed_out();
		return;
	}
	setclientsysstate( "lsm", "1", self );
	self.bleedout_time = delay;
	while ( self.bleedout_time > int( delay * 0.5 ) )
	{
		self.bleedout_time -= 1;
		wait 1;
	}
	visionsetlaststand( "zombie_death", delay * 0.5 );
	while ( self.bleedout_time > 0 )
	{
		self.bleedout_time -= 1;
		wait 1;
	}
	while ( isDefined( self.revivetrigger ) && isDefined( self.revivetrigger.beingrevived ) && self.revivetrigger.beingrevived == 1 )
	{
		wait 0.1;
	}
	self notify( "bled_out" );
	wait_network_frame();
	self bleed_out();
}

bleed_out() //checked changed to match cerberus output
{
	self cleanup_suicide_hud();
	if ( isDefined( self.revivetrigger ) )
	{
		self.revivetrigger delete();
	}
	self.revivetrigger = undefined;
	setclientsysstate( "lsm", "0", self );
	self maps/mp/zombies/_zm_stats::increment_client_stat( "deaths" );
	self maps/mp/zombies/_zm_stats::increment_player_stat( "deaths" );
	self maps/mp/zombies/_zm_pers_upgrades_functions::pers_upgrade_jugg_player_death_stat();
	self recordplayerdeathzombies();
	self maps/mp/zombies/_zm_equipment::equipment_take();
	if ( level.gametype != "zcleansed" )
	{
		maps/mp/_demo::bookmark( "zm_player_bledout", getTime(), self, undefined, 1 );
	}
	level notify( "bleed_out", self.characterindex );
	self undolaststand();
	if ( is_true( level.is_zombie_level ) )
	{
		self thread [[ level.player_becomes_zombie ]]();
	}
	if ( is_true( level.is_specops_level ) )
	{
		self thread [[ level.spawnspectator ]]();
	}
	else
	{
		self.ignoreme = 0;
	}
}

cleanup_suicide_hud() //checked matches cerberus output
{
	if ( isDefined( self.suicideprompt ) )
	{
		self.suicideprompt destroy();
	}
	self.suicideprompt = undefined;
}

clean_up_suicide_hud_on_end_game() //checked matches cerberus output
{
	self endon( "disconnect" );
	self endon( "zombified" );
	self endon( "stop_revive_trigger" );
	self endon( "player_revived" );
	self endon( "bled_out" );
	level waittill_any( "end_game", "stop_suicide_trigger" );
	self cleanup_suicide_hud();
	if ( isDefined( self.suicidetexthud ) )
	{
		self.suicidetexthud destroy();
	}
	if ( isDefined( self.suicideprogressbar ) )
	{
		self.suicideprogressbar destroyelem();
	}
}

clean_up_suicide_hud_on_bled_out() //checked matches cerberus output
{
	self endon( "disconnect" );
	self endon( "zombified" );
	self endon( "stop_revive_trigger" );
	self waittill_any( "bled_out", "player_revived", "fake_death" );
	self cleanup_suicide_hud();
	if ( isDefined( self.suicideprogressbar ) )
	{
		self.suicideprogressbar destroyelem();
	}
	if ( isDefined( self.suicidetexthud ) )
	{
		self.suicidetexthud destroy();
	}
}

suicide_trigger_spawn() //checked matches cerberus output
{
	radius = getDvarInt( "revive_trigger_radius" );
	self.suicideprompt = newclienthudelem( self );
	self.suicideprompt.alignx = "center";
	self.suicideprompt.aligny = "middle";
	self.suicideprompt.horzalign = "center";
	self.suicideprompt.vertalign = "bottom";
	self.suicideprompt.y = -170;
	if ( self issplitscreen() )
	{
		self.suicideprompt.y = -132;
	}
	self.suicideprompt.foreground = 1;
	self.suicideprompt.font = "default";
	self.suicideprompt.fontscale = 1.5;
	self.suicideprompt.alpha = 1;
	self.suicideprompt.color = ( 1, 1, 1 );
	self.suicideprompt.hidewheninmenu = 1;
	self thread suicide_trigger_think();
}

suicide_trigger_think() //checked changed to match cerberus output
{
	self endon( "disconnect" );
	self endon( "zombified" );
	self endon( "stop_revive_trigger" );
	self endon( "player_revived" );
	self endon( "bled_out" );
	self endon( "fake_death" );
	level endon( "end_game" );
	level endon( "stop_suicide_trigger" );
	self thread clean_up_suicide_hud_on_end_game();
	self thread clean_up_suicide_hud_on_bled_out();
	while ( self usebuttonpressed() )
	{
		wait 1;
	}
	if ( !isDefined( self.suicideprompt ) )
	{
		return;
	}
	while ( 1 )
	{
		wait 0.1;
		if ( !isDefined( self.suicideprompt ) )
		{
			continue;
		}
		self.suicideprompt settext( &"ZOMBIE_BUTTON_TO_SUICIDE" );
		if ( !self is_suiciding() )
		{
			continue;
		}
		self.pre_suicide_weapon = self getcurrentweapon();
		self giveweapon( level.suicide_weapon );
		self switchtoweapon( level.suicide_weapon );
		duration = self docowardswayanims();
		suicide_success = suicide_do_suicide( duration );
		self.laststand = undefined;
		self takeweapon( level.suicide_weapon );
		if ( suicide_success )
		{
			self notify( "player_suicide" );
			wait_network_frame();
			self maps/mp/zombies/_zm_stats::increment_client_stat( "suicides" );
			self bleed_out();
			return;
		}
		self switchtoweapon( self.pre_suicide_weapon );
		self.pre_suicide_weapon = undefined;
	}
}

suicide_do_suicide( duration ) //checked matches cerberus output
{
	level endon( "end_game" );
	level endon( "stop_suicide_trigger" );
	suicidetime = duration;
	timer = 0;
	suicided = 0;
	self.suicideprompt settext( "" );
	if ( !isDefined( self.suicideprogressbar ) )
	{
		self.suicideprogressbar = self createprimaryprogressbar();
	}
	if ( !isDefined( self.suicidetexthud ) )
	{
		self.suicidetexthud = newclienthudelem( self );
	}
	self.suicideprogressbar updatebar( 0.01, 1 / suicidetime );
	self.suicidetexthud.alignx = "center";
	self.suicidetexthud.aligny = "middle";
	self.suicidetexthud.horzalign = "center";
	self.suicidetexthud.vertalign = "bottom";
	self.suicidetexthud.y = -173;
	if ( self issplitscreen() )
	{
		self.suicidetexthud.y = -147;
	}
	self.suicidetexthud.foreground = 1;
	self.suicidetexthud.font = "default";
	self.suicidetexthud.fontscale = 1.8;
	self.suicidetexthud.alpha = 1;
	self.suicidetexthud.color = ( 1, 1, 1 );
	self.suicidetexthud.hidewheninmenu = 1;
	self.suicidetexthud settext( &"ZOMBIE_SUICIDING" );
	while ( self is_suiciding() )
	{
		wait 0.05;
		timer += 0.05;
		if ( timer >= suicidetime )
		{
			suicided = 1;
			break;
		}
	}
	if ( isDefined( self.suicideprogressbar ) )
	{
		self.suicideprogressbar destroyelem();
	}
	if ( isDefined( self.suicidetexthud ) )
	{
		self.suicidetexthud destroy();
	}
	if ( isDefined( self.suicideprompt ) )
	{
		self.suicideprompt settext( &"ZOMBIE_BUTTON_TO_SUICIDE" );
	}
	return suicided;
}

can_suicide() //checked matches cerberus output
{
	if ( !isalive( self ) )
	{
		return 0;
	}
	if ( !self player_is_in_laststand() )
	{
		return 0;
	}
	if ( !isDefined( self.suicideprompt ) )
	{
		return 0;
	}
	if ( is_true( self.is_zombie ) )
	{
		return 0;
	}
	if ( is_true( level.intermission ) )
	{
		return 0;
	}
	return 1;
}

is_suiciding( revivee ) //checked changed at own discretion
{
	if ( self usebuttonpressed() && can_suicide() )
	{
		return 1;
	}
	return 0;
}

revive_trigger_spawn() //checked changed to match cerberus output
{
	if ( isDefined( level.revive_trigger_spawn_override_link ) )
	{
		[[ level.revive_trigger_spawn_override_link ]]( self );
	}
	else
	{
		radius = getDvarInt( "revive_trigger_radius" );
		self.revivetrigger = spawn( "trigger_radius", ( 0, 0, 0 ), 0, radius, radius );
		self.revivetrigger sethintstring( "" );
		self.revivetrigger setcursorhint( "HINT_NOICON" );
		self.revivetrigger setmovingplatformenabled( 1 );
		self.revivetrigger enablelinkto();
		self.revivetrigger.origin = self.origin;
		self.revivetrigger linkto( self );
		self.revivetrigger.beingrevived = 0;
		self.revivetrigger.createtime = getTime();
	}
	self thread revive_trigger_think();
}

revive_trigger_think() //checked partially changed to match cerberus output //did not change while loop to for loop because of infinite loop continue bug
{
	self endon( "disconnect" );
	self endon( "zombified" );
	self endon( "stop_revive_trigger" );
	level endon( "end_game" );
	self endon( "death" );
	while ( 1 )
	{
		wait 0.1;
		self.revivetrigger sethintstring( "" );
		players = get_players();
		for ( i = 0; i < players.size; i++ )
		{
			d = 0;
			d = self depthinwater();
			if ( players[ i ] can_revive( self ) || d > 20 )
			{
				self.revivetrigger setrevivehintstring( &"ZOMBIE_BUTTON_TO_REVIVE_PLAYER", self.team );
				break;
			}
		}
		i = 0;
		while ( i < players.size )
		{
			reviver = players[ i ];
			if ( self == reviver || !reviver is_reviving( self ) )
			{
				i++;
				continue;
			}
			gun = reviver getcurrentweapon();
			/*
/#
			assert( isDefined( gun ) );
#/
			*/
			if ( gun == level.revive_tool )
			{
				i++;
				continue;
			}
			reviver giveweapon( level.revive_tool );
			reviver switchtoweapon( level.revive_tool );
			reviver setweaponammostock( level.revive_tool, 1 );
			revive_success = reviver revive_do_revive( self, gun );
			reviver revive_give_back_weapons( gun );
			if ( isplayer( self ) )
			{
				self allowjump( 1 );
			}
			self.laststand = undefined;
			if ( revive_success )
			{
				if ( isplayer( self ) )
				{
					maps/mp/zombies/_zm_chugabud::player_revived_cleanup_chugabud_corpse();
				}
				self thread revive_success( reviver );
				self cleanup_suicide_hud();
				return;
			}
			i++;
		}
	}
}

revive_give_back_weapons( gun ) //checked matches cerberus output
{
	self takeweapon( level.revive_tool );
	if ( self player_is_in_laststand() )
	{
		return;
	}
	if ( gun != "none" && !is_placeable_mine( gun ) && gun != "equip_gasmask_zm" && gun != "lower_equip_gasmask_zm" && self hasweapon( gun ) )
	{
		self switchtoweapon( gun );
	}
	else
	{
		primaryweapons = self getweaponslistprimaries();
		if ( isDefined( primaryweapons ) && primaryweapons.size > 0 )
		{
			self switchtoweapon( primaryweapons[ 0 ] );
		}
	}
}

can_revive( revivee ) //checked changed to match cerberus output
{
	if ( !isDefined( revivee.revivetrigger ) )
	{
		return 0;
	}
	if ( !isalive( self ) )
	{
		return 0;
	}
	if ( self player_is_in_laststand() )
	{
		return 0;
	}
	if ( self.team != revivee.team )
	{
		return 0;
	}
	if ( is_true( self.is_zombie ) )
	{
		return 0;
	}
	if ( self has_powerup_weapon() )
	{
		return 0;
	}
	if ( is_true( level.can_revive_use_depthinwater_test ) && revivee depthinwater() > 10 )
	{
		return 1;
	}
	if ( isDefined( level.can_revive ) && ![[ level.can_revive ]]( revivee ) )
	{
		return 0;
	}
	if ( isDefined( level.can_revive_game_module ) && ![[ level.can_revive_game_module ]]( revivee ) )
	{
		return 0;
	}
	ignore_sight_checks = 0;
	ignore_touch_checks = 0;
	if ( isDefined( level.revive_trigger_should_ignore_sight_checks ) )
	{
		ignore_sight_checks = [[ level.revive_trigger_should_ignore_sight_checks ]]( self );
		if ( ignore_sight_checks && isDefined( revivee.revivetrigger.beingrevived ) && revivee.revivetrigger.beingrevived == 1 )
		{
			ignore_touch_checks = 1;
		}
	}
	if ( !ignore_touch_checks )
	{
		if ( !self istouching( revivee.revivetrigger ) )
		{
			return 0;
		}
	}
	if ( !ignore_sight_checks )
	{
		if ( !self is_facing( revivee ) )
		{
			return 0;
		}
		if ( !sighttracepassed( self.origin + vectorScale( ( 0, 0, 1 ), 50 ), revivee.origin + vectorScale( ( 0, 0, 1 ), 30 ), 0, undefined ) )
		{
			return 0;
		}
		if ( !bullettracepassed( self.origin + vectorScale( ( 0, 0, 1 ), 50 ), revivee.origin + vectorScale( ( 0, 0, 1 ), 30 ), 0, undefined ) )
		{
			return 0;
		}
	}
	return 1;
}

is_reviving( revivee ) //checked changed at own discretion
{
	if ( self usebuttonpressed() && can_revive( revivee ) ) 
	{
		return 1;
	}
	return 0;
}

is_reviving_any() //checked changed at own discretion
{
	if ( is_true( self.is_reviving_any ) )
	{
		return 1;
	}
	return 0;
}

is_facing( facee ) //checked matches cerberus output
{
	orientation = self getplayerangles();
	forwardvec = anglesToForward( orientation );
	forwardvec2d = ( forwardvec[ 0 ], forwardvec[ 1 ], 0 );
	unitforwardvec2d = vectornormalize( forwardvec2d );
	tofaceevec = facee.origin - self.origin;
	tofaceevec2d = ( tofaceevec[ 0 ], tofaceevec[ 1 ], 0 );
	unittofaceevec2d = vectornormalize( tofaceevec2d );
	dotproduct = vectordot( unitforwardvec2d, unittofaceevec2d );
	return dotproduct > 0.9;
}

revive_do_revive( playerbeingrevived, revivergun ) //checked changed to match cerberus output
{
	/*
/#
	assert( self is_reviving( playerbeingrevived ) );
#/
	*/
	revivetime = 3;
	if ( self hasperk( "specialty_quickrevive" ) )
	{
		revivetime /= 2;
	}
	if ( self maps/mp/zombies/_zm_pers_upgrades_functions::pers_revive_active() )
	{
		revivetime *= 0.5;
	}
	timer = 0;
	revived = 0;
	playerbeingrevived.revivetrigger.beingrevived = 1;
	playerbeingrevived.revive_hud settext( &"ZOMBIE_PLAYER_IS_REVIVING_YOU", self );
	playerbeingrevived revive_hud_show_n_fade( 3 );
	playerbeingrevived.revivetrigger sethintstring( "" );
	if ( isplayer( playerbeingrevived ) )
	{
		playerbeingrevived startrevive( self );
	}
	if ( !isDefined( self.reviveprogressbar ) )
	{
		self.reviveprogressbar = self createprimaryprogressbar();
	}
	if ( !isDefined( self.revivetexthud ) )
	{
		self.revivetexthud = newclienthudelem( self );
	}
	self thread laststand_clean_up_on_disconnect( playerbeingrevived, revivergun );
	if ( !isDefined( self.is_reviving_any ) )
	{
		self.is_reviving_any = 0;
	}
	self.is_reviving_any++;
	self thread laststand_clean_up_reviving_any( playerbeingrevived );
	self.reviveprogressbar updatebar( 0.01, 1 / revivetime );
	self.revivetexthud.alignx = "center";
	self.revivetexthud.aligny = "middle";
	self.revivetexthud.horzalign = "center";
	self.revivetexthud.vertalign = "bottom";
	self.revivetexthud.y = -113;
	if ( self issplitscreen() )
	{
		self.revivetexthud.y = -347;
	}
	self.revivetexthud.foreground = 1;
	self.revivetexthud.font = "default";
	self.revivetexthud.fontscale = 1.8;
	self.revivetexthud.alpha = 1;
	self.revivetexthud.color = ( 1, 1, 1 );
	self.revivetexthud.hidewheninmenu = 1;
	if ( self maps/mp/zombies/_zm_pers_upgrades_functions::pers_revive_active() )
	{
		self.revivetexthud.color = ( 0.5, 0.5, 1 );
	}
	self.revivetexthud settext( &"ZOMBIE_REVIVING" );
	self thread check_for_failed_revive( playerbeingrevived );
	while ( self is_reviving( playerbeingrevived ) )
	{
		wait 0.05;
		timer += 0.05;
		if ( self player_is_in_laststand() )
		{
			break;
		}
		else if ( isDefined( playerbeingrevived.revivetrigger.auto_revive ) && playerbeingrevived.revivetrigger.auto_revive == 1 )
		{
			break;
		}
		if ( timer >= revivetime )
		{
			revived = 1;
			break;
		}
	}
	if ( isDefined( self.reviveprogressbar ) )
	{
		self.reviveprogressbar destroyelem();
	}
	if ( isDefined( self.revivetexthud ) )
	{
		self.revivetexthud destroy();
	}
	if ( isDefined( playerbeingrevived.revivetrigger.auto_revive ) && playerbeingrevived.revivetrigger.auto_revive == 1 )
	{
	}
	else if ( !revived )
	{
		if ( isplayer( playerbeingrevived ) )
		{
			playerbeingrevived stoprevive( self );
		}
	}
	playerbeingrevived.revivetrigger sethintstring( &"ZOMBIE_BUTTON_TO_REVIVE_PLAYER" );
	playerbeingrevived.revivetrigger.beingrevived = 0;
	self notify( "do_revive_ended_normally" );
	self.is_reviving_any--;

	if ( !revived )
	{
		playerbeingrevived thread checkforbleedout( self );
	}
	return revived;
}

checkforbleedout( player ) //checked matches cerberus output
{
	self endon( "player_revived" );
	self endon( "player_suicide" );
	self endon( "disconnect" );
	player endon( "disconnect" );
	if ( is_classic() )
	{
		player.failed_revives++;
		player notify( "player_failed_revive" );
	}
}

auto_revive( reviver, dont_enable_weapons ) //checked changed to match cerberus output
{
	if ( isDefined( self.revivetrigger ) )
	{
		self.revivetrigger.auto_revive = 1;
		while ( self.revivetrigger.beingrevived == 1 )
		{
			while ( 1 )
			{
				if ( self.revivetrigger.beingrevived == 0 )
				{
					break;
				}
				wait_network_frame();
			}
		}
		self.revivetrigger.auto_trigger = 0;
	}
	self reviveplayer();
	self maps/mp/zombies/_zm_perks::perk_set_max_health_if_jugg( "health_reboot", 1, 0 );
	setclientsysstate( "lsm", "0", self );
	self notify( "stop_revive_trigger" );
	if ( isDefined( self.revivetrigger ) )
	{
		self.revivetrigger delete();
		self.revivetrigger = undefined;
	}
	self cleanup_suicide_hud();
	if ( !isDefined( dont_enable_weapons ) || dont_enable_weapons == 0 )
	{
		self laststand_enable_player_weapons();
	}
	self allowjump( 1 );
	self.ignoreme = 0;
	self.laststand = undefined;
	if ( !is_true( level.isresetting_grief ) )
	{
		reviver.revives++;
		reviver maps/mp/zombies/_zm_stats::increment_client_stat( "revives" );
		reviver maps/mp/zombies/_zm_stats::increment_player_stat( "revives" );
		self recordplayerrevivezombies( reviver );
		maps/mp/_demo::bookmark( "zm_player_revived", getTime(), self, reviver );
	}
	self notify( "player_revived" );
}

remote_revive( reviver ) //checked matches cerberus output
{
	if ( !self player_is_in_laststand() )
	{
		return;
	}
	self auto_revive( reviver );
}

revive_success( reviver, b_track_stats ) //checked changed to match cerberus output
{
	if ( !isDefined( b_track_stats ) )
	{
		b_track_stats = 1;
	}
	if ( !isplayer( self ) )
	{
		self notify( "player_revived", reviver );
		return;
	}
	if ( is_true( b_track_stats ) )
	{
		maps/mp/_demo::bookmark( "zm_player_revived", getTime(), self, reviver );
	}
	self notify( "player_revived", reviver );
	self reviveplayer();
	self maps/mp/zombies/_zm_perks::perk_set_max_health_if_jugg( "health_reboot", 1, 0 );
	if ( is_true( self.pers_upgrades_awarded[ "perk_lose" ] ) )
	{
		self thread maps/mp/zombies/_zm_pers_upgrades_functions::pers_upgrade_perk_lose_restore();
	}
	if ( !is_true( level.isresetting_grief ) && is_true( b_track_stats ) )
	{
		reviver.revives++;
		reviver maps/mp/zombies/_zm_stats::increment_client_stat( "revives" );
		reviver maps/mp/zombies/_zm_stats::increment_player_stat( "revives" );
		self recordplayerrevivezombies( reviver );
		reviver.upgrade_fx_origin = self.origin;
	}
	if ( is_classic() && is_true( b_track_stats ) )
	{
		maps/mp/zombies/_zm_pers_upgrades_functions::pers_increment_revive_stat( reviver );
	}
	if ( is_true( b_track_stats ) )
	{
		reviver thread check_for_sacrifice();
	}
	setclientsysstate( "lsm", "0", self );
	self.revivetrigger delete();
	self.revivetrigger = undefined;
	self cleanup_suicide_hud();
	self laststand_enable_player_weapons();
	self.ignoreme = 0;
}

revive_force_revive( reviver ) //checked matches cerberus output
{
	/*
/#
	assert( isDefined( self ) );
#/
/#
	assert( isplayer( self ) );
#/
/#
	assert( self player_is_in_laststand() );
#/
	*/
	self thread revive_success( reviver );
}

revive_hud_create() //checked matches cerberus output
{
	self.revive_hud = newclienthudelem( self );
	self.revive_hud.alignx = "center";
	self.revive_hud.aligny = "middle";
	self.revive_hud.horzalign = "center";
	self.revive_hud.vertalign = "bottom";
	self.revive_hud.foreground = 1;
	self.revive_hud.font = "default";
	self.revive_hud.fontscale = 1.5;
	self.revive_hud.alpha = 0;
	self.revive_hud.color = ( 1, 1, 1 );
	self.revive_hud.hidewheninmenu = 1;
	self.revive_hud settext( "" );
	self.revive_hud.y = -160;
}

revive_hud_think() //checked partially changed to match cerberus output //did not change while loops to for loops to prevent infinite loop bug due to continue
{
	self endon( "disconnect" );
	while ( 1 )
	{
		wait 0.1;
		if ( !player_any_player_in_laststand() )
		{
			continue;
		}
		players = get_players();
		playertorevive = undefined;
		i = 0;
		while ( i < players.size )
		{
			if ( !isDefined( players[ i ].revivetrigger ) || !isDefined( players[ i ].revivetrigger.createtime ) )
			{
				i++;
				continue;
			}
			if ( !isDefined( playertorevive ) || playertorevive.revivetrigger.createtime > players[ i ].revivetrigger.createtime )
			{
				playertorevive = players[ i ];
			}
			i++;
		}
		if ( isDefined( playertorevive ) )
		{
			i = 0;
			while ( i < players.size )
			{
				if ( players[ i ] player_is_in_laststand() )
				{
					i++;
					continue;
				}
				if ( getDvar( "g_gametype" ) == "vs" )
				{
					if ( players[ i ].team != playertorevive.team )
					{
						i++;
						continue;
					}
				}
				if ( is_encounter() )
				{
					if ( players[ i ].sessionteam != playertorevive.sessionteam )
					{
						i++;
						continue;
					}
					if ( is_true( level.hide_revive_message ) )
					{
						i++;
						continue;
					}
				}
				players[ i ] thread faderevivemessageover( playertorevive, 3 );
				i++;
			}
			playertorevive.revivetrigger.createtime = undefined;
			wait 3.5;
		}
	}
}

faderevivemessageover( playertorevive, time ) //checked matches cerberus output
{
	revive_hud_show();
	self.revive_hud settext( &"ZOMBIE_PLAYER_NEEDS_TO_BE_REVIVED", playertorevive );
	self.revive_hud fadeovertime( time );
	self.revive_hud.alpha = 0;
}

revive_hud_show() //checked matches cerberus output
{
	/*
/#
	assert( isDefined( self ) );
#/
/#
	assert( isDefined( self.revive_hud ) );
#/
	*/
	self.revive_hud.alpha = 1;
}

revive_hud_show_n_fade( time ) //checked matches cerberus output
{
	revive_hud_show();
	self.revive_hud fadeovertime( time );
	self.revive_hud.alpha = 0;
}

drawcylinder( pos, rad, height ) //checked matches cerberus output //may need to review order of operations
{
	/*
/#
	currad = rad;
	curheight = height;
	r = 0;
	while ( r < 20 )
	{
		theta = ( r / 20 ) * 360;
		theta2 = ( ( r + 1 ) / 20 ) * 360;
		line( pos + ( cos( theta ) * currad, sin( theta ) * currad, 0 ), pos + ( cos( theta2 ) * currad, sin( theta2 ) * currad, 0 ) );
		line( pos + ( cos( theta ) * currad, sin( theta ) * currad, curheight ), pos + ( cos( theta2 ) * currad, sin( theta2 ) * currad, curheight ) );
		line( pos + ( cos( theta ) * currad, sin( theta ) * currad, 0 ), pos + ( cos( theta ) * currad, sin( theta ) * currad, curheight ) );
		r++;
#/
	}
	*/
}

get_lives_remaining() //checked matches cerberus output
{
	/*
/#
	assert( level.laststandgetupallowed, "Lives only exist in the Laststand type GETUP." );
#/
	*/
	if ( level.laststandgetupallowed && isDefined( self.laststand_info ) && isDefined( self.laststand_info.type_getup_lives ) )
	{
		return max( 0, self.laststand_info.type_getup_lives );
	}
	return 0;
}

update_lives_remaining( increment ) //checked changed to match cerberus output //probably causes a crash since ternary operators may not be supported by the current compiler
{
	/*
/#
	assert( level.laststandgetupallowed, "Lives only exist in the Laststand type GETUP." );
#/
/#
	assert( isDefined( increment ), "Must specify increment true or false" );
#/
	*/
	increment = (isdefined( increment )?increment:false );
	self.laststand_info.type_getup_lives = max( 0, ( increment?self.laststand_info.type_getup_lives + 1:self.laststand_info.type_getup_lives - 1 ) );
	self notify( "laststand_lives_updated", self.laststand_info.type_getup_lives + 1, increment );
}

player_getup_setup() //checked matches cerberus output
{
	/*
/#
	println( "ZM >> player_getup_setup called" );
#/
	*/
	self.laststand_info = spawnstruct();
	self.laststand_info.type_getup_lives = level.const_laststand_getup_count_start;
}

laststand_getup() //checked matches cerberus output
{
	self endon( "player_revived" );
	self endon( "disconnect" );
	/*
/#
	println( "ZM >> laststand_getup called" );
#/
	*/
	self update_lives_remaining( 0 );
	setclientsysstate( "lsm", "1", self );
	self.laststand_info.getup_bar_value = level.const_laststand_getup_bar_start;
	self thread laststand_getup_hud();
	self thread laststand_getup_damage_watcher();
	while ( self.laststand_info.getup_bar_value < 1 )
	{
		self.laststand_info.getup_bar_value += level.const_laststand_getup_bar_regen;
		wait 0.05;
	}
	self auto_revive( self );
	setclientsysstate( "lsm", "0", self );
}

laststand_getup_damage_watcher() //checked matches cerberus output
{
	self endon( "player_revived" );
	self endon( "disconnect" );
	while ( 1 )
	{
		self waittill( "damage" );
		self.laststand_info.getup_bar_value -= level.const_laststand_getup_bar_damage;
		if ( self.laststand_info.getup_bar_value < 0 )
		{
			self.laststand_info.getup_bar_value = 0;
		}
	}
}

laststand_getup_hud() //checked matches cerberus output
{
	self endon( "player_revived" );
	self endon( "disconnect" );
	hudelem = newclienthudelem( self );
	hudelem.alignx = "left";
	hudelem.aligny = "middle";
	hudelem.horzalign = "left";
	hudelem.vertalign = "middle";
	hudelem.x = 5;
	hudelem.y = 170;
	hudelem.font = "big";
	hudelem.fontscale = 1.5;
	hudelem.foreground = 1;
	hudelem.hidewheninmenu = 1;
	hudelem.hidewhendead = 1;
	hudelem.sort = 2;
	hudelem.label = &"SO_WAR_LASTSTAND_GETUP_BAR";
	self thread laststand_getup_hud_destroy( hudelem );
	while ( 1 )
	{
		hudelem setvalue( self.laststand_info.getup_bar_value );
		wait 0.05;
	}
}

laststand_getup_hud_destroy( hudelem ) //checked matches cerberus output
{
	self waittill_either( "player_revived", "disconnect" );
	hudelem destroy();
}

check_for_sacrifice() //checked matches cerberus output
{
	self delay_notify( "sacrifice_denied", 1 );
	self endon( "sacrifice_denied" );
	self waittill( "player_downed" );
	self maps/mp/zombies/_zm_stats::increment_client_stat( "sacrifices" );
	self maps/mp/zombies/_zm_stats::increment_player_stat( "sacrifices" );
}

check_for_failed_revive( playerbeingrevived ) //checked matches cerberus output
{
	self endon( "disconnect" );
	playerbeingrevived endon( "disconnect" );
	playerbeingrevived endon( "player_suicide" );
	self notify( "checking_for_failed_revive" );
	self endon( "checking_for_failed_revive" );
	playerbeingrevived endon( "player_revived" );
	playerbeingrevived waittill( "bled_out" );
	self maps/mp/zombies/_zm_stats::increment_client_stat( "failed_revives" );
	self maps/mp/zombies/_zm_stats::increment_player_stat( "failed_revives" );
}

add_weighted_down() //checked matches cerberus output
{
	if ( !level.curr_gametype_affects_rank )
	{
		return;
	}
	weighted_down = 1000;
	if ( level.round_number > 0 )
	{
		weighted_down = int( 1000 / ceil( level.round_number / 5 ) );
	}
	self addplayerstat( "weighted_downs", weighted_down );
}

cleanup_laststand_on_disconnect() //checked matches cerberus output
{
	self endon( "player_revived" );
	self endon( "player_suicide" );
	self endon( "bled_out" );
	trig = self.revivetrigger;
	self waittill( "disconnect" );
	if ( isDefined( trig ) )
	{
		trig delete();
	}
}




