/*
	server/ai/zombie_core.qc

	zombie things

	Copyright (C) 2021-2024 NZ:P Team

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

	See the GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to:

		Free Software Foundation, Inc.
		59 Temple Place - Suite 330
		Boston, MA  02111-1307, USA

*/

#define ZOMBIE_DAMAGE_THROUGH_WINDOW 		60

void() Zombie_Walk_Setup;
void() crawler_rip_board1;
void() crawler_da1;
void() crawler_die1;
void() crawler_death_wunder1;
void() LinkZombiesHitbox;


float EMPTY_BBOX = 0;
float BASE_BBOX = 1;
float IDLE_BBOX = 2;
//Rising from ground bbox
float WALK1_BBOX = 4;
float WALK2_BBOX = 5;
float WALK3_BBOX = 6;
float JOG_BBOX = 7;
float RUN_BBOX = 8;
float CRAWLER_BBOX = 9;

void(float type) Zombie_PlaySoundType =
{
	// If we already have ambient groaning type sounds playing, don't allow another.
	// We DO want to allow attacks, deaths, etc. though.
	if (self.sound_time > time && type != ZOMBIE_SOUND_TYPE_MELEE && type != ZOMBIE_SOUND_TYPE_DEATH)
		return;

	if(self.classname == "ai_zombie")
	{
		float next_sound_time = 0;
		float curr_sound_len = 0;
		float rand = 0;

		switch(type) {
			// 8 options.
			case ZOMBIE_SOUND_TYPE_MELEE:
				rand = rint(random() * 7) + 1;

				switch(rand) {
					case 1:
						Sound_PlaySound(self.head, "sounds/zombie/a0.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						curr_sound_len = 1.1;
						break;
					case 2:
						Sound_PlaySound(self.head, "sounds/zombie/a1.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						curr_sound_len = 0.8;
						break;
					case 3:
						Sound_PlaySound(self.head, "sounds/zombie/a2.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						curr_sound_len = 0.9;
						break;
					case 4:
						Sound_PlaySound(self.head, "sounds/zombie/a3.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						curr_sound_len = 0.8;
						break;
					case 5:
						Sound_PlaySound(self.head, "sounds/zombie/a4.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						curr_sound_len = 1;
						break;
					case 6:
						Sound_PlaySound(self.head, "sounds/zombie/a5.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						curr_sound_len = 1;
						break;
					case 7:
						Sound_PlaySound(self.head, "sounds/zombie/a6.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						curr_sound_len = 1;
						break;
					case 8:
						Sound_PlaySound(self.head, "sounds/zombie/a7.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						curr_sound_len = 1;
						break;
					default:
						break;
				}
				break;

			// 15 options.
			case ZOMBIE_SOUND_TYPE_WALK:
				rand = rint(random() * 14) + 1;
				next_sound_time = rint(random() * 4) + 1;

				// Don't allow overriding current playing sounds.
				if (self.s_time > time)
					break;
				
				switch(rand) {
					case 1:
						Sound_PlaySound(self.head, "sounds/zombie/w0.wav", SOUND_TYPE_ZOMBIE_QUIET, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.3;
						break;
					case 2:
						Sound_PlaySound(self.head, "sounds/zombie/w1.wav", SOUND_TYPE_ZOMBIE_QUIET, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.75;
						break;
					case 3:
						Sound_PlaySound(self.head, "sounds/zombie/w2.wav", SOUND_TYPE_ZOMBIE_QUIET, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.95;
						break;
					case 4:
						Sound_PlaySound(self.head, "sounds/zombie/w3.wav", SOUND_TYPE_ZOMBIE_QUIET, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.4;
						break;
					case 5:
						Sound_PlaySound(self.head, "sounds/zombie/w4.wav", SOUND_TYPE_ZOMBIE_QUIET, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 2.7;
						break;
					case 6:
						Sound_PlaySound(self.head, "sounds/zombie/w5.wav", SOUND_TYPE_ZOMBIE_QUIET, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.9;
						break;
					case 7:
						Sound_PlaySound(self.head, "sounds/zombie/w6.wav", SOUND_TYPE_ZOMBIE_QUIET, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.6;
						break;
					case 8:
						Sound_PlaySound(self.head, "sounds/zombie/w7.wav", SOUND_TYPE_ZOMBIE_QUIET, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.9;
						break;
					case 9:
						Sound_PlaySound(self.head, "sounds/zombie/w8.wav", SOUND_TYPE_ZOMBIE_QUIET, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 2.4;
						break;
					case 10:
						Sound_PlaySound(self.head, "sounds/zombie/w9.wav", SOUND_TYPE_ZOMBIE_QUIET, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.5;
						break;
					case 11:
						Sound_PlaySound(self.head, "sounds/zombie/t0.wav", SOUND_TYPE_ZOMBIE_QUIET, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.5;
						break;
					case 12:
						Sound_PlaySound(self.head, "sounds/zombie/t1.wav", SOUND_TYPE_ZOMBIE_QUIET, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.6;
						break;
					case 13:
						Sound_PlaySound(self.head, "sounds/zombie/t2.wav", SOUND_TYPE_ZOMBIE_QUIET, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 2.3;
						break;
					case 14:
						Sound_PlaySound(self.head, "sounds/zombie/t3.wav", SOUND_TYPE_ZOMBIE_QUIET, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.75;
						break;
					case 15:
						Sound_PlaySound(self.head, "sounds/zombie/t4.wav", SOUND_TYPE_ZOMBIE_QUIET, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.7;
						break;
					default:
						break;
				}
				break;
			
			// 5 options.
			case ZOMBIE_SOUND_TYPE_CRAWL:
				rand = rint(random() * 4) + 1;
				next_sound_time = rint(random() * 4) + 1;

				// Don't allow overriding current playing sounds.
				if (self.s_time > time)
					break;

				switch(rand) {
					case 1:
						Sound_PlaySound(self.head, "sounds/zombie/t0.wav", SOUND_TYPE_ZOMBIE_CRAWLER, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.5;
						break;
					case 2:
						Sound_PlaySound(self.head, "sounds/zombie/t1.wav", SOUND_TYPE_ZOMBIE_CRAWLER, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.6;
						break;
					case 3:
						Sound_PlaySound(self.head, "sounds/zombie/t2.wav", SOUND_TYPE_ZOMBIE_CRAWLER, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 2.3;
						break;
					case 4:
						Sound_PlaySound(self.head, "sounds/zombie/t3.wav", SOUND_TYPE_ZOMBIE_CRAWLER, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.75;
						break;
					case 5:
						Sound_PlaySound(self.head, "sounds/zombie/t4.wav", SOUND_TYPE_ZOMBIE_CRAWLER, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 1.7;
						break;
					default:
						break;
				}
				break;

			// 10 options.
			case ZOMBIE_SOUND_TYPE_SPRINT:
				rand = rint(random() * 9) + 1;
				next_sound_time = rint(random() * 4) + 1;

				// Don't allow overriding current playing sounds.
				if (self.s_time > time)
					break;
				
				switch(rand) {
					case 1:
						Sound_PlaySound(self.head, "sounds/zombie/r0.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 3.1;
						break;
					case 2:
						Sound_PlaySound(self.head, "sounds/zombie/r1.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 4.6;
						break;
					case 3:
						Sound_PlaySound(self.head, "sounds/zombie/r2.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 3.4;
						break;
					case 4:
						Sound_PlaySound(self.head, "sounds/zombie/r3.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 5.1;
						break;
					case 5:
						Sound_PlaySound(self.head, "sounds/zombie/r4.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 3.5;
						break;
					case 6:
						Sound_PlaySound(self.head, "sounds/zombie/r5.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 3.2;
						break;
					case 7:
						Sound_PlaySound(self.head, "sounds/zombie/r6.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 5.1;
						break;
					case 8:
						Sound_PlaySound(self.head, "sounds/zombie/r7.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 5.1;
						break;
					case 9:
						Sound_PlaySound(self.head, "sounds/zombie/r8.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 3;
						break;
					case 10:
						Sound_PlaySound(self.head, "sounds/zombie/r9.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_ALLOWSKIP);
						curr_sound_len = 4.1;
						break;
					default:
						break;
				}
				break;

			// 8 options.
			case ZOMBIE_SOUND_TYPE_DEATH:
				rand = rint(random() * 7) + 1;
				next_sound_time = 0.1;
				curr_sound_len = 2;

				switch(rand) {
					case 1:
						Sound_PlaySound(self.head, "sounds/zombie/d0.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						break;
					case 2:
						Sound_PlaySound(self.head, "sounds/zombie/d1.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						break;
					case 3:
						Sound_PlaySound(self.head, "sounds/zombie/d2.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						break;
					case 4:
						Sound_PlaySound(self.head, "sounds/zombie/d3.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						break;
					case 5:
						Sound_PlaySound(self.head, "sounds/zombie/d4.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						break;
					case 6:
						Sound_PlaySound(self.head, "sounds/zombie/d5.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						break;
					case 7:
						Sound_PlaySound(self.head, "sounds/zombie/d6.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						break;
					case 8:
						Sound_PlaySound(self.head, "sounds/zombie/d7.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
						break;
					default:
						break;
				}
				break;
		}

		self.sound_time = time + next_sound_time;
		self.s_time = time + curr_sound_len;
	}
};

void(__inout vector what, float howmuch) vector_scale_hack =
{
	what_x *= howmuch;
	what_y *= howmuch;
	what_z *= howmuch;
}

void(float which) SetZombieHitBox =
{

	//Zombie's bbox is constant across all hitbox setups, so we don't update it.
	
	if(self.currentHitBoxSetup == which)
		return;

	//which = IDLE_BBOX;
	
	switch(which)
	{
		case EMPTY_BBOX:
			self.head.view_ofs = '0 0 0';
			self.rarm.view_ofs = '0 0 0';
			self.larm.view_ofs = '0 0 0';
			break;
		case IDLE_BBOX: 
			self.head.bbmins = '-5 -5 -5';
			self.head.bbmaxs = '5 5 5';
			self.head.view_ofs = '0 10 35';
		
			self.larm.bbmins = '-5 -5 -15';
			self.larm.bbmaxs  = '5 5 15';
			self.larm.view_ofs = '-10 10 10';
		
			self.rarm.bbmins = '-5 -5 -15';
			self.rarm.bbmaxs = '5 5 15';
			self.rarm.view_ofs = '10 10 10';
			break;
		case WALK1_BBOX:
			self.head.bbmins = '-5 -5 -5';
			self.head.bbmaxs = '5 5 5';
			self.head.view_ofs = '2 -3 35';

			self.larm.bbmins = '-5 -7 -15';
			self.larm.bbmaxs = '5 7 15';
			self.larm.view_ofs = '-10 -4 15';

			self.rarm.bbmins = '-5 -8 -11';
			self.rarm.bbmaxs = '5 8 11';
			self.rarm.view_ofs = '10 11 11';
			break;
		case WALK2_BBOX:
			self.head.bbmins = '-7 -5 -5';
			self.head.bbmaxs = '7 5 5';
			self.head.view_ofs = '-11 6 36';

			self.larm.bbmins = '-5 -6 -15';
			self.larm.bbmaxs = '5 6 15';
			self.larm.view_ofs = '-14 3 15';
	
			self.rarm.bbmins = '-5 -5 -11';
			self.rarm.bbmaxs = '5 5 11';
			self.rarm.view_ofs = '4 13 11';
			break;
		case WALK3_BBOX:
			self.head.bbmins = '-5 -5 -5';
			self.head.bbmaxs = '5 5 5';
			self.head.view_ofs = '-2 13 31';
	
			self.larm.bbmins = '-4 -6 -14';
			self.larm.bbmaxs = '4 6 14';
			self.larm.view_ofs = '-12 3 11';
	
			self.rarm.bbmins = '-5 -5 -11';
			self.rarm.bbmaxs = '5 5 11';
			self.rarm.view_ofs = '14 6 12';
			break;
		case JOG_BBOX:
			self.head.bbmins = '-5 -5 -5';
			self.head.bbmaxs = '5 5 5';
			self.head.view_ofs = '0 5 36';
			
			self.larm.bbmins = '-5 -13 -5';
			self.larm.bbmaxs = '5 13 5';
			self.larm.view_ofs = '-8 19 24';
			
			self.rarm.bbmins = '-5 -13 -5';
			self.rarm.bbmaxs = '5 13 5';
			self.rarm.view_ofs = '10 19 24';
			break;
		case RUN_BBOX:
			self.head.bbmins = '-5 -5 -5';
			self.head.bbmaxs = '5 5 5';
			self.head.view_ofs = '3 17 32';
	
			self.larm.bbmins = '-4 -10 -9';
			self.larm.bbmaxs = '4 10 9';
			self.larm.view_ofs = '-12 28 18';
			
			self.rarm.bbmins = '-4 -13 -10';
			self.rarm.bbmaxs = '4 13 10';
			self.rarm.view_ofs = '12 -2 19';
			break;
		case CRAWLER_BBOX:
			self.head.bbmins = '-5 -5 -5';
			self.head.bbmaxs = '5 5 5';
			self.head.view_ofs = '2 18 -14';
	
			self.larm.bbmins = '-4 -10 -9';
			self.larm.bbmaxs = '4 10 9';
			self.larm.view_ofs = '-9 24 -27';
			
			self.rarm.bbmins = '-4 -13 -10';
			self.rarm.bbmaxs = '4 13 10';
			self.rarm.view_ofs = '12 15 -25';
			break;
		default:  //also known as BASE_BBOX
			self.head.bbmins = '-5 -5 -5';
			self.head.bbmaxs = '5 5 5';
			self.head.view_ofs = '0 0 35';
			
			self.larm.bbmins = '-5 -5 -15';
			self.larm.bbmaxs = '5 5 15';
			self.larm.view_ofs = '-10 0 5';
			
			self.rarm.bbmins = '-5 -5 -15';
			self.rarm.bbmaxs = '5 5 15';
			self.rarm.view_ofs = '10 0 5';
			break;
	}

	self.currentHitBoxSetup = which;

	if (map_compatibility_mode == MAP_COMPAT_BETA) {
		vector_scale_hack(self.head.bbmins, self.head.scale);
		vector_scale_hack(self.head.bbmaxs, self.head.scale);
		vector_scale_hack(self.head.view_ofs, self.head.scale);

		vector_scale_hack(self.larm.bbmins, self.larm.scale);
		vector_scale_hack(self.larm.bbmaxs, self.larm.scale);
		vector_scale_hack(self.larm.view_ofs, self.larm.scale);

		vector_scale_hack(self.rarm.bbmins, self.rarm.scale);
		vector_scale_hack(self.rarm.bbmaxs, self.rarm.scale);
		vector_scale_hack(self.rarm.view_ofs, self.rarm.scale);
	}
};

//
// Zombie_ProcessRespawn()
// Logic called by Zombie_Think for checking
// the last origin every second, for determining
// if a zombie is "stuck" inside and needs to re-spawn.
//
void() Zombie_ProcessRespawn =
{
	// Check once every 3 seconds
	if (self.respawn_timer < time && !self.outside) {
		// Check if the Zombie has barely moved (16qu)
		if (vlen(self.new_ofs - self.origin) <= 16) {
			// Continue to iterate
			self.respawn_iterator++;

			// 30 seconds have passed -- assume properly stuck, respawn.
			if (self.respawn_iterator >= 10) {
				self.respawn_iterator = 0;

				// Set outside -- we do this to prevent Power-Ups from
				// dropping.
				self.outside = true;

				// Kill it.
				self.th_die();

				// This is a pseudo-hack that just tells the rounds core that we should
				// spawn another.
				Current_Zombies--;
				Remaining_Zombies++;
			}
		} else {
			// Set to zero -- Zombie is moving appropriately.
			self.respawn_iterator = 0;
		}

		// Iterate another 3 seconds.
		self.respawn_timer = time + 3;

		// Update our last origin
		self.new_ofs = self.origin;
	}
}

void() zombie_decide;
void() Zombie_Think = //called every frame for zombies
{
	if (self.onfire) {
		Effect_Fire(self.origin);

		if (self.ltime < time && self.onfire){
			DamageHandler(self, self.firer, 300, DMG_TYPE_FLAMETHROWER);
			self.ltime = time + 2;

			if (self.fire_timeout < time)
				self.onfire = false;
		}
	}

	Zombie_ProcessRespawn();

	// Electro-Shock Death
	if (self.death_timer < time && self.death_timer_activated == true) {
		self.th_die();
	}

	if (self.bleedingtime < time && !self.head.deadflag)
	{
		self.bleedingtime = time + 1;
		DamageHandler (self, self.usedent, z_health * 0.2, DMG_TYPE_HEADSHOT);
	}

	// If we're mid attack and our enemy has moved out of range
	if (self.is_attacking && vlen(self.enemy.origin - self.origin) > 64) {
		// Cancel the animation, start walking again to catch up.
		self.is_attacking = false;

		// zombie_decide() here used to call an idle loop if they were
		// outside, lesson learned.
		if (!self.outside)
			self.th_walk();
	}
}

void() zombie_footstep
{
	if(self.laststep == 1)
	{
		self.laststep = 0;
		Sound_PlaySound(self, "sounds/zombie/s0.wav", SOUND_TYPE_PLAYER_FOOTSTEP, SOUND_PRIORITY_PLAYALWAYS);
	}
	else
	{
		self.laststep = 1;
		Sound_PlaySound(self, "sounds/zombie/s1.wav", SOUND_TYPE_PLAYER_FOOTSTEP, SOUND_PRIORITY_PLAYALWAYS);
	}
}

//////////////////////////////////////////////////////////////////////
//37-52
$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10 walk11 walk12 walk13 walk14 walk15 walk16
//walk15
//walk 1!
void()	zombie_walk1		=[	$walk1,		zombie_walk2	] 	{self.nextthink = time + 0.12;Zombie_Walk(8);SetZombieHitBox(WALK1_BBOX);/*if(freeze_time < time)*/ self.frame = 37;Zombie_Think();};
void()	zombie_walk2		=[	$walk2,		zombie_walk3	] 	{self.nextthink = time + 0.12;Zombie_Walk(3.5); self.frame = 38;Zombie_Think();};
void()	zombie_walk3		=[	$walk3,		zombie_walk4	] 	{self.nextthink = time + 0.12;Zombie_Walk(3.5); self.frame = 39;Zombie_Think();};
void()	zombie_walk4		=[	$walk4,		zombie_walk5	] 	{self.nextthink = time + 0.12;Zombie_Walk(3.5); self.frame = 40;Zombie_Think();};
void()	zombie_walk5		=[	$walk5,		zombie_walk6	] 	{self.nextthink = time + 0.12;Zombie_Walk(3.5); self.frame = 41;Zombie_Think();zombie_footstep();};//footstep
void()	zombie_walk6		=[	$walk6,		zombie_walk7	] 	{self.nextthink = time + 0.12;Zombie_Walk(3.5); self.frame = 42;Zombie_Think();};
void()	zombie_walk7		=[	$walk7,		zombie_walk8	] 	{self.nextthink = time + 0.12;Zombie_Walk(8.8); self.frame = 43;Zombie_Think();};
void()	zombie_walk8		=[	$walk8,		zombie_walk9	] 	{self.nextthink = time + 0.12;Zombie_Walk(9); self.frame = 44;Zombie_Think();zombie_footstep();};//footstep
void()	zombie_walk9		=[	$walk9,		zombie_walk10	] 	{self.nextthink = time + 0.12;Zombie_Walk(4); self.frame = 45;Zombie_Think();};
void()	zombie_walk10		=[	$walk10,		zombie_walk11	] 	{self.nextthink = time + 0.12;Zombie_Walk(4); self.frame = 46;Zombie_Think();};
void()	zombie_walk11		=[	$walk11,		zombie_walk12	] 	{self.nextthink = time + 0.12;Zombie_Walk(7.8); self.frame = 47;Zombie_Think();};
void()	zombie_walk12		=[	$walk12,		zombie_walk13	] 	{self.nextthink = time + 0.12;Zombie_Walk(5.2); self.frame = 48;Zombie_Think();zombie_footstep();};//footstep
void()	zombie_walk13		=[	$walk13,		zombie_walk14	] 	{self.nextthink = time + 0.12;Zombie_Walk(2.4); self.frame = 49;Zombie_Think();};
void()	zombie_walk14		=[	$walk14,		zombie_walk15	] 	{self.nextthink = time + 0.12;Zombie_Walk(2.8); self.frame = 50;Zombie_Think();};
void()	zombie_walk15		=[	$walk15,		zombie_walk16	] 	{self.nextthink = time + 0.12;Zombie_Walk(6.5); self.frame = 51;Zombie_Think();};
void()	zombie_walk16		=[	$walk16,		Zombie_Walk_Setup	] 	{self.nextthink = time + 0.12;Zombie_Walk(7.7); self.frame = 52;Zombie_Think();zombie_footstep();};//footstep

/////////////////////////////////////////////////////////////////////////
//53-66
//Walk 2!
$frame bwalk1 bwalk2 bwalk3 bwalk4 bwalk5 bwalk6 bwalk7 bwalk8 bwalk9 bwalk10 bwalk11 bwalk12 bwalk13 bwalk14

void()	zombie_walkb1		=[	$bwalk1,		zombie_walkb2	] 	{self.nextthink = time + 0.13;Zombie_Walk(4.2);SetZombieHitBox(WALK2_BBOX);/*if(freeze_time < time)*/ self.frame = 53;Zombie_Think();};
void()	zombie_walkb2		=[	$bwalk2,		zombie_walkb3	] 	{self.nextthink = time + 0.13;Zombie_Walk(5.9); self.frame = 54;Zombie_Think();};
void()	zombie_walkb3		=[	$bwalk3,		zombie_walkb4	] 	{self.nextthink = time + 0.13;Zombie_Walk(3); self.frame = 55;Zombie_Think();};
void()	zombie_walkb4		=[	$bwalk4,		zombie_walkb5	] 	{self.nextthink = time + 0.13;Zombie_Walk(5.7); self.frame = 56;Zombie_Think();zombie_footstep();};//footstep
void()	zombie_walkb5		=[	$bwalk5,		zombie_walkb6	] 	{self.nextthink = time + 0.13;Zombie_Walk(4.2); self.frame = 57;Zombie_Think();};
void()	zombie_walkb6		=[	$bwalk6,		zombie_walkb7	] 	{self.nextthink = time + 0.13;Zombie_Walk(7.2); self.frame = 58;Zombie_Think();};
void()	zombie_walkb7		=[	$bwalk7,		zombie_walkb8	] 	{self.nextthink = time + 0.13;Zombie_Walk(4.5); self.frame = 59;Zombie_Think();zombie_footstep();};//footstep
void()	zombie_walkb8		=[	$bwalk8,		zombie_walkb9	] 	{self.nextthink = time + 0.13;Zombie_Walk(3.4); self.frame = 60;Zombie_Think();};
void()	zombie_walkb9		=[	$bwalk9,		zombie_walkb10	] 	{self.nextthink = time + 0.13;Zombie_Walk(1.2); self.frame = 61;Zombie_Think();};
void()	zombie_walkb10		=[	$bwalk10,		zombie_walkb11	] 	{self.nextthink = time + 0.13;Zombie_Walk(6.7); self.frame = 62;Zombie_Think();zombie_footstep();};//footstep
void()	zombie_walkb11		=[	$bwalk11,		zombie_walkb12	] 	{self.nextthink = time + 0.13;Zombie_Walk(8.4); self.frame = 63;Zombie_Think();};
void()	zombie_walkb12		=[	$bwalk12,		zombie_walkb13	] 	{self.nextthink = time + 0.13;Zombie_Walk(1.3); self.frame = 64;Zombie_Think();};
void()	zombie_walkb13		=[	$bwalk13,		zombie_walkb14	] 	{self.nextthink = time + 0.13;Zombie_Walk(6.3); self.frame = 65;Zombie_Think();};
void()	zombie_walkb14		=[	$bwalk14,		Zombie_Walk_Setup	] 	{self.nextthink = time + 0.13;Zombie_Walk(3.6); self.frame = 66;Zombie_Think();zombie_footstep();};//footstep

/////////////////////////////////////////////////////////////////////////
//67-82
//Walk 3!
$frame cwalk1 cwalk2 cwalk3 cwalk4 cwalk5 cwalk6 cwalk7 cwalk8 cwalk9 cwalk10 cwalk11 cwalk12 cwalk13 cwalk14 cwalk15 cwalk16

void()	zombie_walkc1		=[	$cwalk1,		zombie_walkc2	] 	{self.nextthink = time + 0.13;Zombie_Walk(3.4);SetZombieHitBox(WALK3_BBOX);/*if(freeze_time < time)*/ self.frame = 67;Zombie_Think();};
void()	zombie_walkc2		=[	$cwalk2,		zombie_walkc3	] 	{self.nextthink = time + 0.13;Zombie_Walk(2); self.frame = 68;Zombie_Think();};
void()	zombie_walkc3		=[	$cwalk3,		zombie_walkc4	] 	{self.nextthink = time + 0.13;Zombie_Walk(5.4); self.frame = 69;Zombie_Think();};
void()	zombie_walkc4		=[	$cwalk4,		zombie_walkc5	] 	{self.nextthink = time + 0.13;Zombie_Walk(5.6); self.frame = 70;Zombie_Think();zombie_footstep();};//footstep
void()	zombie_walkc5		=[	$cwalk5,		zombie_walkc6	] 	{self.nextthink = time + 0.13;Zombie_Walk(3); self.frame = 71;Zombie_Think();};
void()	zombie_walkc6		=[	$cwalk6,		zombie_walkc7	] 	{self.nextthink = time + 0.13;Zombie_Walk(3.1); self.frame = 72;Zombie_Think();};
void()	zombie_walkc7		=[	$cwalk7,		zombie_walkc8	] 	{self.nextthink = time + 0.13;Zombie_Walk(3); self.frame = 73;Zombie_Think();};
void()	zombie_walkc8		=[	$cwalk8,		zombie_walkc9	] 	{self.nextthink = time + 0.13;Zombie_Walk(2.9); self.frame = 74;Zombie_Think();zombie_footstep();};//footstep
void()	zombie_walkc9		=[	$cwalk9,		zombie_walkc10	] 	{self.nextthink = time + 0.13;Zombie_Walk(3.3); self.frame = 75;Zombie_Think();};
void()	zombie_walkc10		=[	$cwalk10,		zombie_walkc11	] 	{self.nextthink = time + 0.13;Zombie_Walk(2.2); self.frame = 76;Zombie_Think();};
void()	zombie_walkc11		=[	$cwalk11,		zombie_walkc12	] 	{self.nextthink = time + 0.13;Zombie_Walk(3.7); self.frame = 77;Zombie_Think();};
void()	zombie_walkc12		=[	$cwalk12,		zombie_walkc13	] 	{self.nextthink = time + 0.13;Zombie_Walk(4.2); self.frame = 78;Zombie_Think();zombie_footstep();};//footstep
void()	zombie_walkc13		=[	$cwalk13,		zombie_walkc14	] 	{self.nextthink = time + 0.13;Zombie_Walk(5.3); self.frame = 79;Zombie_Think();};
void()	zombie_walkc14		=[	$cwalk14,		zombie_walkc15	] 	{self.nextthink = time + 0.13;Zombie_Walk(5.2); self.frame = 80;Zombie_Think();};
void()	zombie_walkc15		=[	$cwalk15,		zombie_walkc16	] 	{self.nextthink = time + 0.13;Zombie_Walk(2.2); self.frame = 81;Zombie_Think();};
void()	zombie_walkc16		=[	$cwalk16,		Zombie_Walk_Setup	] 	{self.nextthink = time + 0.13;Zombie_Walk(2.4); self.frame = 82;Zombie_Think();zombie_footstep();};//footstep


/////////////////////////////////////////////////////////////////////
//116- 129
$frame jog1 jog2 jog3 jog4 jog5 jog6 jog7 jog8

void()	zombie_jog1		=[	$jog1,		zombie_jog2	] 	{self.nextthink = time + 0.08;Zombie_Walk(6);SetZombieHitBox(JOG_BBOX);/*if(freeze_time < time)*/ self.frame = 83;Zombie_Think();};//FOOTSTEP
void()	zombie_jog2		=[	$jog2,		zombie_jog3	] 		{self.nextthink = time + 0.08;Zombie_Walk(6); self.frame = 84;Zombie_Think();};
void()	zombie_jog3		=[	$jog3,		zombie_jog4	] 		{self.nextthink = time + 0.08;Zombie_Walk(6); self.frame = 85;Zombie_Think();};
void()	zombie_jog4		=[	$jog4,		zombie_jog5	] 		{self.nextthink = time + 0.08;Zombie_Walk(6); self.frame = 86;Zombie_Think();zombie_footstep();};//footstep
void()	zombie_jog5		=[	$jog5,		zombie_jog6	] 		{self.nextthink = time + 0.08;Zombie_Walk(6); self.frame = 87;Zombie_Think();};
void()	zombie_jog6		=[	$jog6,		zombie_jog7	] 		{self.nextthink = time + 0.08;Zombie_Walk(6); self.frame = 88;Zombie_Think();};
void()	zombie_jog7		=[	$jog7,		zombie_jog8	] 		{self.nextthink = time + 0.08;Zombie_Walk(6); self.frame = 89;Zombie_Think();};
void()	zombie_jog8		=[	$jog8,		Zombie_Walk_Setup	] 	{self.nextthink = time + 0.08;Zombie_Walk(6); self.frame = 90;Zombie_Think();zombie_footstep();};//footstep

/////////////////////////////////////////////////////////////////////
//78-85
$frame run1 run2 run3 run4 run5 run6 run7 run8 run9 run10 
//12 unless 14
void()	zombie_run1		=[	$run1,		zombie_run2	] 	{Zombie_Walk(13);/*if(freeze_time < time)*/ self.frame = 92;SetZombieHitBox(RUN_BBOX);Zombie_Think();zombie_footstep();};//footstep
void()	zombie_run2		=[	$run2,		zombie_run3	] 	{Zombie_Walk(13);self.frame = 93;Zombie_Think();};
void()	zombie_run3		=[	$run3,		zombie_run4	] 	{Zombie_Walk(13); self.frame = 94;Zombie_Think();zombie_footstep();};//footstep
void()	zombie_run4		=[	$run4,		zombie_run5	] 	{Zombie_Walk(13); self.frame = 95;Zombie_Think();};
void()	zombie_run5		=[	$run5,		zombie_run6	] 	{Zombie_Walk(13); self.frame = 96;Zombie_Think();};
void()	zombie_run6		=[	$run6,		zombie_run7	] 	{Zombie_Walk(13); self.frame = 97;Zombie_Think();zombie_footstep();};//footstep
void()	zombie_run7		=[	$run7,		zombie_run8	] 	{Zombie_Walk(13); self.frame = 98;Zombie_Think();};
void()	zombie_run8		=[	$run8,		zombie_run9	] 	{Zombie_Walk(13); self.frame = 99;Zombie_Think();zombie_footstep();};//footstep
void()	zombie_run9		=[	$run9,		zombie_run10	] 	{Zombie_Walk(13); self.frame = 100;Zombie_Think();};
void()	zombie_run10		=[	$run10,	Zombie_Walk_Setup	] 	{Zombie_Walk(13); self.frame = 101;Zombie_Think();};

void() Zombie_Walk_Setup = 
{
	if(Remaining_Zombies == 1 && rounds >= 4)
	{
		self.walktype = 5;
	}
	
	if(self.walktype == 1)
	{
		zombie_walk1();
	}
	
	else if(self.walktype == 2)
	{
		zombie_walkb1();
	}
	
	else if(self.walktype == 3)
	{
		zombie_walkc1();
	}
	
	else if(self.walktype == 4)
	{
		zombie_jog1();
	}
	else if(self.walktype == 5)
	{
		zombie_run1();
	}
};
///////////////////////////////////////////////////////////////////// ZOMBIE IDLE 1
//0-12
$frame idle1 idle2 idle3 idle4 idle5 idle6 idle7 idle8 idle9 idle10 idle11 idle12 idle13 idle14
void() zombie_idle		=[	$idle1, 		zombie_idle1	]		{Zombie_Walk(0);self.frame = 0;SetZombieHitBox(IDLE_BBOX);Zombie_Think();};
void() zombie_idle1		=[	$idle2, 		zombie_idle2	]		{Zombie_Walk(0);self.frame = 1;Zombie_Think();};
void() zombie_idle2		=[	$idle3, 		zombie_idle3	]		{Zombie_Walk(0);self.frame = 2;Zombie_Think();};
void() zombie_idle3		=[	$idle4, 		zombie_idle4	]		{Zombie_Walk(0);self.frame = 3;Zombie_Think();};
void() zombie_idle4		=[	$idle5, 		zombie_idle5	]		{Zombie_Walk(0);self.frame = 4;Zombie_Think();};
void() zombie_idle5		=[	$idle6, 		zombie_idle6	]		{Zombie_Walk(0);self.frame = 5;Zombie_Think();};
void() zombie_idle6		=[	$idle7, 		zombie_idle7	]		{Zombie_Walk(0);self.frame = 6;Zombie_Think();};
void() zombie_idle7		=[	$idle8, 		zombie_idle8	]		{Zombie_Walk(0);self.frame = 7;Zombie_Think();};
void() zombie_idle8		=[	$idle9, 		zombie_idle9	]		{Zombie_Walk(0);self.frame = 8;Zombie_Think();};
void() zombie_idle9		=[	$idle10, 		zombie_idle10	]		{Zombie_Walk(0);self.frame = 9;Zombie_Think();};
void() zombie_idle10		=[	$idle12, 		zombie_idle11	]		{Zombie_Walk(0);self.frame = 10;Zombie_Think();};
void() zombie_idle11		=[	$idle13, 		zombie_idle12	]		{Zombie_Walk(0);self.frame = 11;Zombie_Think();};
void() zombie_idle12		=[	$idle14, 		zombie_idle1	]		{Zombie_Walk(0);self.frame = 12;Zombie_Think();};

void() zombie_decide = 
{
	if(self.outside == TRUE)
	{
		self.th_idle();
	}
	else
	{
		self.th_walk();
	}
};
/////////////////////////////////////////////////////////

void() Window_Damage;
void() Window_Damage_Setup =
{
	local entity old_self;
	old_self = self;
	self = self.goalentity;
	Window_Damage();
	self = old_self;
};
void() zombie_attack2 =
{
	if(self.outside == TRUE)
	{
		if(self.goalentity.classname == "window")
		{
			if(self.enemy.classname != "window")
			{
				self.enemy = self.goalentity;
			}
		}
	}
	
	if(self.goalentity.classname == "window")
	{
		Window_Damage_Setup();
	}
	// motolegacy -- Prohibit damage to the Player if the Nuke Power-Up logic is currently
	// running.
	else if (self.enemy.classname == "player" && nuke_powerup_active == false)
	{
		if(vlen(self.enemy.origin - self.origin) < 64)
		{
			if (self.classname == "ai_dog")
				DamageHandler (self.enemy, self, 40, DMG_TYPE_ZOMBIESWIPE);
			else
				DamageHandler (self.enemy, self, 50, DMG_TYPE_ZOMBIESWIPE);

			// Getting hit by Zombies triggers a GamePad rumble.
			nzp_rumble(self.enemy, 1000, 1200, 75);
		}
	}
	else return;
};

//////////////////////////////WINDOW HOPPING
//113-122
void(float pushaway) moveforwardalittle =
{
	makevectors(self.angles);
	local float amount;
	amount = 4.35;//was 8.7, but running twice as much so...
	if(self.crawling == TRUE)
		amount = 2.33;
	
	self.origin = self.origin + (v_forward * amount);
	setorigin(self, self.origin);
	self.zoom = 1;
	
	if(pushaway == 1)
	{
		local entity ent;
		ent = findradius(self.origin,50);
		while(ent)
		{
			if(ent.classname == "player" && !ent.downed)
			{
				ent.velocity = ent.velocity + (v_forward * 250);
			}
			ent = ent.chain;
		}
	}
};

$frame hop1 hop2 hop3 hop4 hop5 hop6 hop7 hop8 hop9 hop10
void() zombie_hop1	=[	$hop1,	zombie_hop1_5	] {self.movetype = self.head.movetype = MOVETYPE_STEP;self.frame = self.head.frame = 113;self.nextthink = time + 0.05;moveforwardalittle(1); self.state = 1;Zombie_Think();};
void() zombie_hop1_5	=[	$hop1,	zombie_hop2	] {self.frame = self.head.frame = 113;self.nextthink = time + 0.05;moveforwardalittle(0); };
void() zombie_hop2	=[	$hop2,	zombie_hop2_5	] {self.frame = self.head.frame = 114;self.nextthink = time + 0.05;moveforwardalittle(1);Zombie_Think();};	
void() zombie_hop2_5	=[	$hop2,	zombie_hop3	] {self.frame = self.head.frame = 114;self.nextthink = time+0.05;moveforwardalittle(0);};	
void() zombie_hop3	=[	$hop3,	zombie_hop3_5	] {self.frame = self.head.frame = 115;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();};
void() zombie_hop3_5	=[	$hop3,	zombie_hop4	] {self.frame = self.head.frame = 115;self.nextthink = time+0.05;moveforwardalittle(0);};
void() zombie_hop4	=[	$hop4,	zombie_hop4_5	] {self.frame = self.head.frame = 116;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();};
void() zombie_hop4_5	=[	$hop4,	zombie_hop5	] {self.frame = self.head.frame = 116;self.nextthink = time+0.05;moveforwardalittle(1);};
void() zombie_hop5	=[	$hop5,	zombie_hop5_5	] {self.frame = self.head.frame = 117;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();};
void() zombie_hop5_5	=[	$hop5,	zombie_hop6	] {self.frame = self.head.frame = 117;self.nextthink = time+0.05;moveforwardalittle(1);};
void() zombie_hop6	=[	$hop6,	zombie_hop6_5	] {self.frame = self.head.frame = 118;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();};
void() zombie_hop6_5	=[	$hop6,	zombie_hop7	] {self.frame = self.head.frame = 118;self.nextthink = time+0.05;moveforwardalittle(1);};
void() zombie_hop7	=[	$hop7,	zombie_hop7_5	] {self.frame = self.head.frame = 119;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();};
void() zombie_hop7_5	=[	$hop7,	zombie_hop8	] {self.frame = self.head.frame = 119;self.nextthink = time+0.05;moveforwardalittle(1);};
void() zombie_hop8	=[	$hop8,	zombie_hop8_5	] {self.frame = self.head.frame = 120;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();zombie_footstep();};
void() zombie_hop8_5	=[	$hop8,	zombie_hop9	] {self.frame = self.head.frame = 120;self.nextthink = time+0.05;moveforwardalittle(1);};
void() zombie_hop9	=[	$hop9,	zombie_hop9_5	] {self.frame = self.head.frame = 121;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();};
void() zombie_hop9_5	=[	$hop9,	zombie_hop10	] {self.frame = self.head.frame = 121;self.nextthink = time+0.05;moveforwardalittle(0);};
void() zombie_hop10	=[	$hop10,	zombie_hop10_5	] {self.frame = self.head.frame = 122;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();};
void() zombie_hop10_5=[	$hop10,	SUB_Null		] {self.frame = self.head.frame = 122;	
															self.oldorigin = self.origin = self.goalentity.hop_spot;
															setorigin(self, self.origin); 
															self.state = 0;
															self.hop_step = 6;
															if(self.crawling == 2) 
															{
																makeCrawler(self); 
																//return;
															}
															self.movetype = MOVETYPE_WALK;
															self.th_walk();
														};

//ripboard1
//181-191
$frame zrip1 zrip2 zrip3 zrip4 zrip5 zrip6 zrip7 zrip8 zrip9 zrip10 zrip11
void() zombie_ripboardA1			=[	$zrip1,		zombie_ripboardA2		]	{self.frame = 181;SetZombieHitBox(IDLE_BBOX);Zombie_Think();};
void() zombie_ripboardA2			=[	$zrip2,		zombie_ripboardA3		]	{self.frame = 182;Zombie_Think();};
void() zombie_ripboardA3			=[	$zrip3,		zombie_ripboardA4		]	{self.frame = 183;Zombie_Think();};
void() zombie_ripboardA4			=[	$zrip4,		zombie_ripboardA5		]	{self.frame = 184;Zombie_Think();};
void() zombie_ripboardA5			=[	$zrip5,		zombie_ripboardA6		]	{self.frame = 185;Zombie_Think();};
void() zombie_ripboardA6			=[	$zrip6,		zombie_ripboardA7		]	{self.frame = 186;Zombie_Think();};
void() zombie_ripboardA7			=[	$zrip7,		zombie_ripboardA8		]	{self.frame = 187;zombie_attack2();Zombie_Think();};
void() zombie_ripboardA8			=[	$zrip8,		zombie_ripboardA9		]	{self.frame = 188;Zombie_Think();};
void() zombie_ripboardA9			=[	$zrip9,		zombie_ripboardA10		]	{self.frame = 189;Zombie_Think();};
void() zombie_ripboardA10			=[	$zrip10,		zombie_ripboardA11		]	{self.frame = 190;Zombie_Think();};
void() zombie_ripboardA11			=[	$zrip11,		zombie_decide		]	{self.frame = 191;Zombie_Think();};
/////////////////////////////////////////////////////////
//ripboard2
//192-201
$frame zripB1 zripB2 zripB3 zripB4 zripB5 zripB6 zripB7 zripB8 zripB9 zripB10 zripB11
void() zombie_ripboardB1			=[	$zripB1,		zombie_ripboardB2		]	{self.frame = 192;SetZombieHitBox(IDLE_BBOX);Zombie_Think();};
void() zombie_ripboardB2			=[	$zripB2,		zombie_ripboardB3		]	{self.frame = 193;Zombie_Think();};
void() zombie_ripboardB3			=[	$zripB3,		zombie_ripboardB4		]	{self.frame = 194;Zombie_Think();};
void() zombie_ripboardB4			=[	$zripB4,		zombie_ripboardB5		]	{self.frame = 195;Zombie_Think();};
void() zombie_ripboardB5			=[	$zripB5,		zombie_ripboardB6		]	{self.frame = 196;Zombie_Think();};
void() zombie_ripboardB6			=[	$zripB6,		zombie_ripboardB7		]	{self.frame = 197;zombie_attack2();Zombie_Think();};
void() zombie_ripboardB7			=[	$zripB7,		zombie_ripboardB8		]	{self.frame = 198;Zombie_Think();};
void() zombie_ripboardB8			=[	$zripB8,		zombie_ripboardB9		]	{self.frame = 199;Zombie_Think();};
void() zombie_ripboardB9			=[	$zripB9,		zombie_ripboardB10		]	{self.frame = 200;Zombie_Think();};
void() zombie_ripboardB10			=[	$zripB10,		zombie_decide		]	{self.frame = 201;Zombie_Think();};
/////////////////////////////////////////////////////////
//attack through window
//180-181
//181-185
//186-188
void() zombie_attack_through_window =
{
	Zombie_PlaySoundType(ZOMBIE_SOUND_TYPE_MELEE);
	local entity who;
	who = findradius(self.origin, 70);
	while(who)
	{
		if(who.classname == "player")
		{
			DamageHandler (who, self, ZOMBIE_DAMAGE_THROUGH_WINDOW, DMG_TYPE_ZOMBIESWIPE);
		}
		who = who.chain;
	}
};
//202-210

//
// Zombie Attacks
//
void() zombie_attack2;

$frame zatkw1 zatkw2 zatkw3 zatkw4 zatkw5 zatkw6 zatkw7 zatkw8 zatkw9
void() zombie_attack_through_w1		=[	$zatkw1,		zombie_attack_through_w2		]	{self.is_attacking = true; self.frame = 202;SetZombieHitBox(IDLE_BBOX);Zombie_Think();};//START

void() zombie_attack_through_w2		=[	$zatkw2,		zombie_attack_through_w3		]	{self.frame = 203;zombie_attack_through_window();Zombie_Think();};//SWINGING
void() zombie_attack_through_w3		=[	$zatkw3,		zombie_attack_through_w4		]	{self.frame = 204;Zombie_Think();};//SWINGING
void() zombie_attack_through_w4		=[	$zatkw4,		zombie_attack_through_w5		]	{self.frame = 205;Zombie_Think();};//SWINGING
void() zombie_attack_through_w5		=[	$zatkw5,		zombie_attack_through_w6		]	{self.frame = 206;Zombie_Think();};//SWINGING
void() zombie_attack_through_w6		=[	$zatkw6,		zombie_attack_through_w7		]	{self.frame = 207;Zombie_Think();};//SWINGING

void() zombie_attack_through_w7		=[	$zatkw2,		zombie_attack_through_w8		]	{self.frame = 203;Zombie_Think();};//SWINGING
void() zombie_attack_through_w8		=[	$zatkw3,		zombie_attack_through_w9		]	{self.frame = 204;Zombie_Think();};//SWINGING
void() zombie_attack_through_w9		=[	$zatkw4,		zombie_attack_through_w10		]	{self.frame = 205;Zombie_Think();};//SWINGING
void() zombie_attack_through_w10		=[	$zatkw5,		zombie_attack_through_w11		]	{self.frame = 206;Zombie_Think();};//SWINGING
void() zombie_attack_through_w11		=[	$zatkw6,		zombie_attack_through_w12		]	{self.frame = 207;Zombie_Think();};//SWINGING

void() zombie_attack_through_w12		=[	$zatkw2,		zombie_attack_through_w13		]	{self.frame = 203;Zombie_Think();};//SWINGING
void() zombie_attack_through_w13		=[	$zatkw3,		zombie_attack_through_w14		]	{self.frame = 204;Zombie_Think();};//SWINGING
void() zombie_attack_through_w14		=[	$zatkw4,		zombie_attack_through_w15		]	{self.frame = 205;Zombie_Think();};//SWINGING
void() zombie_attack_through_w15		=[	$zatkw5,		zombie_attack_through_w16		]	{self.frame = 206;zombie_attack_through_window();Zombie_Think();};//SWINGING
void() zombie_attack_through_w16		=[	$zatkw6,		zombie_attack_through_w17		]	{self.frame = 207;Zombie_Think();};//SWINGING

void() zombie_attack_through_w17		=[	$zatkw7,		zombie_attack_through_w18		]	{self.frame = 208;Zombie_Think();};//ENDING
void() zombie_attack_through_w18		=[	$zatkw8,		zombie_attack_through_w19		]	{self.frame = 209;Zombie_Think();};//ENDING
void() zombie_attack_through_w19		=[	$zatkw9,		zombie_decide		]	{self.is_attacking = false; self.frame = 210;Zombie_Think();};//ENDING

void() zm_swipe_right_slow	=[	1, 	zm_swipe_right_slow1	]	{self.is_attacking = true; self.frame = 102; SetZombieHitBox(BASE_BBOX); Zombie_Think(); };
void() zm_swipe_right_slow1	=[	2, 	zm_swipe_right_slow2	]	{self.frame = 103; Zombie_Think(); };
void() zm_swipe_right_slow2	=[	3, 	zm_swipe_right_slow3	]	{self.frame = 104; Zombie_Think(); Zombie_PlaySoundType(ZOMBIE_SOUND_TYPE_MELEE); };
void() zm_swipe_right_slow3	=[	4, 	zm_swipe_right_slow4	]	{zombie_attack2(); self.frame = 105; Zombie_Think(); };
void() zm_swipe_right_slow4	=[	5, 	zombie_decide			]	{self.is_attacking = false; self.frame = 106; Zombie_Think(); };

void() zm_swipe_right_fast	=[	1, 	zm_swipe_right_fast1	]	{self.is_attacking = true; self.frame = 104; Zombie_Think(); Zombie_PlaySoundType(ZOMBIE_SOUND_TYPE_MELEE); };
void() zm_swipe_right_fast1	=[	2, 	zm_swipe_right_fast2	]	{zombie_attack2(); self.frame = 105; Zombie_Think(); };
void() zm_swipe_right_fast2	=[	3, 	zombie_decide			]	{self.is_attacking = false; self.frame = 106; Zombie_Think(); };

void() zm_swipe_left_slow	=[	1, 	zm_swipe_left_slow1		]	{self.is_attacking = true; self.frame = 107; SetZombieHitBox(BASE_BBOX); Zombie_Think(); };
void() zm_swipe_left_slow1	=[	2, 	zm_swipe_left_slow2		]	{self.frame = 108; Zombie_Think(); };
void() zm_swipe_left_slow2	=[	3, 	zm_swipe_left_slow3		]	{self.frame = 109; Zombie_Think(); Zombie_PlaySoundType(ZOMBIE_SOUND_TYPE_MELEE); };
void() zm_swipe_left_slow3	=[	4, 	zm_swipe_left_slow4		]	{zombie_attack2(); self.frame = 110; Zombie_Think(); };
void() zm_swipe_left_slow4	=[	5, 	zm_swipe_left_slow5		]	{self.frame = 111; Zombie_Think(); };
void() zm_swipe_left_slow5	=[	6, 	zombie_decide			]	{self.is_attacking = false; self.frame = 112; Zombie_Think(); };

void() zm_swipe_left_fast	=[	1, 	zm_swipe_left_fast1		]	{self.is_attacking = true; self.frame = 109; SetZombieHitBox(BASE_BBOX); Zombie_Think(); Zombie_PlaySoundType(ZOMBIE_SOUND_TYPE_MELEE); };
void() zm_swipe_left_fast1	=[	2, 	zm_swipe_left_fast2		]	{zombie_attack2(); self.frame = 110; Zombie_Think(); };
void() zm_swipe_left_fast2	=[	3, 	zm_swipe_left_fast3		]	{self.frame = 111; Zombie_Think(); };
void() zm_swipe_left_fast3	=[	4, 	zombie_decide			]	{self.is_attacking = false; self.frame = 112; Zombie_Think(); };

void() zm_swipe_both_slow	=[	1, 	zm_swipe_both_slow1		]	{self.is_attacking = true; self.frame = 102; SetZombieHitBox(BASE_BBOX); Zombie_Think(); };
void() zm_swipe_both_slow1	=[	2, 	zm_swipe_both_slow2		]	{self.frame = 103; Zombie_Think(); };
void() zm_swipe_both_slow2	=[	3, 	zm_swipe_both_slow3		]	{self.frame = 104; Zombie_Think(); Zombie_PlaySoundType(ZOMBIE_SOUND_TYPE_MELEE); };
void() zm_swipe_both_slow3	=[	4, 	zm_swipe_both_slow4		]	{zombie_attack2(); self.frame = 105; Zombie_Think(); };
void() zm_swipe_both_slow4	=[	5, 	zm_swipe_both_slow5		]	{self.frame = 106; Zombie_Think(); };
void() zm_swipe_both_slow5	=[	6, 	zm_swipe_both_slow6		]	{self.frame = 107; Zombie_Think(); };
void() zm_swipe_both_slow6	=[	7, 	zm_swipe_both_slow7		]	{self.frame = 108; Zombie_Think(); };
void() zm_swipe_both_slow7	=[	8, 	zm_swipe_both_slow8		]	{self.frame = 109; Zombie_Think(); };
void() zm_swipe_both_slow8	=[	9, 	zm_swipe_both_slow9		]	{self.frame = 110; Zombie_Think(); Zombie_PlaySoundType(ZOMBIE_SOUND_TYPE_MELEE); };
void() zm_swipe_both_slow9	=[	10, zombie_decide			]	{zombie_attack2(); self.is_attacking = false; self.frame = 111; Zombie_Think(); };

void() zm_swipe_both_fast	=[	1, 	zm_swipe_both_fast1		]	{self.is_attacking = true; self.frame = 104; SetZombieHitBox(BASE_BBOX); Zombie_Think(); Zombie_PlaySoundType(ZOMBIE_SOUND_TYPE_MELEE); };
void() zm_swipe_both_fast1	=[	2, 	zm_swipe_both_fast2		]	{zombie_attack2(); self.frame = 105; Zombie_Think(); };
void() zm_swipe_both_fast2	=[	3, 	zm_swipe_both_fast3		]	{self.frame = 106; Zombie_Think(); };
void() zm_swipe_both_fast3	=[	4, 	zm_swipe_both_fast4		]	{self.frame = 109; Zombie_Think(); };
void() zm_swipe_both_fast4	=[	5, 	zm_swipe_both_fast5		]	{self.frame = 110; Zombie_Think(); };
void() zm_swipe_both_fast5	=[	6, 	zm_swipe_both_fast6		]	{self.frame = 111; Zombie_Think(); Zombie_PlaySoundType(ZOMBIE_SOUND_TYPE_MELEE); };
void() zm_swipe_both_fast6	=[	7, 	zombie_decide			]	{zombie_attack2(); self.is_attacking = false; self.frame = 112; Zombie_Think(); };

void(float which) zombie_attack_choose =
{
	
	self.angles_y = vectoyaw(self.enemy.origin - self.origin);
	
	if(which != 1)
	{
		if(random() > 0.4)
		{
			return;
		}
	}

	//
	// choose what attack to play
	//

	// cypress -- we used to do a really cool thing here where
	// zombies could only attack with an arm they had, but this
	// became problematic for repetitive animations + it really
	// made double-swiping stand out :( maybe one day we could
	// have this back!

	float attack_type = random();
	float attack_is_slow = (random() < 0.5); // :^)

	// Right Arm
	if (attack_type < 0.33) {
		// Slow
		if (attack_is_slow)
			zm_swipe_right_slow();
		// Fast
		else
			zm_swipe_right_fast();
	}
	// Left Arm
	else if (attack_type < 0.66) {
		// Slow
		if (attack_is_slow)
			zm_swipe_left_slow();
		// Fast
		else
			zm_swipe_left_fast();
	}
	// Both (right, followed by left arm)
	else {
		// Slow
		if (attack_is_slow)
			zm_swipe_both_slow();
		// Fast
		else
			zm_swipe_both_fast();
	}
};

$frame zda1 zda2 zda3 zda4 zda5 zda6 zda7 zda8 zda9 zda10 zda11 zda12 zda13 zda14
void() zombie_zda		=[	$zda1, 		zombie_zda1	]		{self.frame = 0;zombie_attack_choose(0);SetZombieHitBox(IDLE_BBOX);Zombie_Think();};
void() zombie_zda1		=[	$zda2, 		zombie_zda2	]		{self.frame = 1;zombie_attack_choose(0);Zombie_Think();};
void() zombie_zda2		=[	$zda3, 		zombie_zda3	]		{self.frame = 2;zombie_attack_choose(0);Zombie_Think();};
void() zombie_zda3		=[	$zda4, 		zombie_zda4	]		{self.frame = 3;zombie_attack_choose(0);Zombie_Think();};
void() zombie_zda4		=[	$zda5, 		zombie_zda5	]		{self.frame = 4;zombie_attack_choose(0);Zombie_Think();};
void() zombie_zda5		=[	$zda6, 		zombie_zda6	]		{self.frame = 5;zombie_attack_choose(0);Zombie_Think();};
void() zombie_zda6		=[	$zda7, 		zombie_zda7	]		{self.frame = 6;zombie_attack_choose(0);Zombie_Think();};
void() zombie_zda7		=[	$zda8, 		zombie_zda8	]		{self.frame = 7;zombie_attack_choose(0);Zombie_Think();};
void() zombie_zda8		=[	$zda9, 		zombie_zda9	]		{self.frame = 8;zombie_attack_choose(0);Zombie_Think();};
void() zombie_zda9		=[	$zda10, 		zombie_zda10	]		{self.frame = 9;zombie_attack_choose(0);Zombie_Think();};
void() zombie_zda10		=[	$zda12, 		zombie_zda11	]		{self.frame = 10;zombie_attack_choose(0);Zombie_Think();};
void() zombie_zda11		=[	$zda13, 		zombie_zda12	]		{self.frame = 11;zombie_attack_choose(0);Zombie_Think();};
void() zombie_zda12		=[	$zda14, 		zombie_zda1	]		{self.frame = 12;zombie_attack_choose(1);Zombie_Think();};

void() zombie_attack =
{
	self.velocity = '0 0 0';

    if(self.state == 1) // If the zombie is in this state (rising, or something else) they shouldn't be able to attack!
        return;

	if (self.electro_targeted == true)
		return;

	if(self.outside == TRUE)
	{
		self.angles_y = self.goalentity.angles_y;
	}
	else
	{
		self.angles_y = vectoyaw(self.enemy.origin - self.origin);
	}
		
	
	if(self.crawling == TRUE)
	{
		if(self.outside == TRUE)
			crawler_rip_board1();
		else
			crawler_da1();//crawler delay attack
	}
	else
	{
		//zombie_attack_through_w1();
		
		if(self.outside == TRUE)
		{
			if(random() < 0.30)
			{
				local entity who;
				local float found;
				found = 0;
				who = findradius(self.origin, 70);
				while (who != world && found == false)
				{
					if(who.classname == "player")
					{
						found = true;
					}
					else
						who = who.chain;
				}
				if (found == true)
				{
					zombie_attack_through_w1();
					return;
				}
			}
			
			if(random() < 0.5)
				zombie_ripboardA1();
			else
				zombie_ripboardB1();
		}
		else
		{
			zombie_zda();//Zombie Delay Attack
		}
	}
	
};

//==========================Falling Code==========================
void CheckLand() =
{
	if ((self.flags & FL_ONGROUND))
	{
		if (self.fall == TRUE)
		{
			self.fall = FALSE;
			self.th_land();
		}
	}
};

//Falling
//149-152
$frame fall1 fall2 fall3 fall4

void() zombie_fall1		=[	$fall1,		zombie_fall2	]	{self.frame = 149;SetZombieHitBox(BASE_BBOX);self.fall = TRUE;CheckLand();Zombie_Think();};
void() zombie_fall2		=[	$fall2,		zombie_fall3	]	{self.frame = 150;CheckLand();Zombie_Think();};
void() zombie_fall3		=[	$fall3,		zombie_fall4	]	{self.frame = 151;CheckLand();Zombie_Think();};
void() zombie_fall4		=[	$fall4,		zombie_fall3	]	{self.frame = 152;CheckLand();Zombie_Think();};

//Landing
//153-159
$frame land1 land2 land3 land4 land5 land6 land7
void() zombie_land1		=[	$land1,		zombie_land2	]	{self.frame = 153;SetZombieHitBox(BASE_BBOX);Zombie_Think();zombie_footstep();zombie_footstep();};
void() zombie_land2		=[	$land2,		zombie_land3	]	{self.frame = 154;Zombie_Think();};
void() zombie_land3		=[	$land3,		zombie_land4	]	{self.frame = 155;Zombie_Think();};
void() zombie_land4		=[	$land4,		zombie_land5	]	{self.frame = 156;Zombie_Think();};
void() zombie_land5		=[	$land5,		zombie_land6	]	{self.frame = 157;Zombie_Think();};
void() zombie_land6		=[	$land6,		zombie_land7	]	{self.frame = 158;Zombie_Think();};
void() zombie_land7		=[	$land7,		Zombie_Walk_Setup	]	{self.frame = 159;Zombie_Think();};

void() removeZombie =
{
	self.aistatus = "0";
	setmodel(self,"");

	// Hounds don't gib.
	if (self.classname != "ai_dog") {
		setmodel(self.larm,"");
		setmodel(self.rarm,"");
		setmodel(self.head,"");

#ifdef FTE

		remove(self.larm);
		remove(self.rarm);
		remove(self.head);

#else

		updateLimb (self, 0, world);
		updateLimb (self, 1, world);
		updateLimb (self, 2, world);

#endif // FTE

	}
	
	setorigin(self,'0 0 0');
	setorigin (self.goaldummy, '0 0 0');
	
	// cypress (20 dec 2023) -- added a nextthink() call here
	// so the next server frame we'll have always run SUB_Null().
	// there was some weird behavior here where a zombie's th_idle()
	// call would carry forward interrupting the procedure for walking
	// to a barricade.
	self.nextthink = time + 0.05;
	self.think = SUB_Null;
	self.classname = "freeZombieEntity";
	self.goalentity = world;
	self.enemy = world;
	self.solid = 0;
	self.movetype = 0;

	self.outside = TRUE;
	self.hop_step = 0;
	self.onfire = false;
	self.fire_timeout = 0;
	self.calc_time = 0;
	self.tries = 0;
	self.takedamage = DAMAGE_NO;
	self.flags = 0;
	self.crawling = 0;
	self.bleedingtime = 0;
	self.iszomb = 0; // 1 for alive AI, 0 for corpses / unallocated reserevd ents
	self.owner = world;

	self.spawnflags = 0;
	self.is_attacking = false;
	
	self.yaw_speed = 0;
	
	self.health = 0;
	self.th_die = SUB_Null;
	self.th_walk = SUB_Null;
	self.th_melee = SUB_Null;
	self.th_idle = SUB_Null;
	self.th_windowhop = SUB_Null;
	
	self.th_fall = SUB_Null;
	self.th_falling = SUB_Null;
	self.th_land = SUB_Null;
	
	self.th_jump = SUB_Null;
	self.th_grabledge = SUB_Null;
	self.th_diewunder = SUB_Null;
	//Disabling collision bboxes
	SetZombieHitBox(EMPTY_BBOX);
};

///////////////////////////////////////////////////////////////////// ZOMBIE DEATH 1
//123-133
$frame death1 death2 death3 death4 death5 death6 death7 death8 death9 death10 death11
void()	zombie_deathA1		=[	$death1,		zombie_death2	] 	{if (self.onfire) {Effect_Fire(self.origin);} self.frame = 123;};
void()	zombie_death2		=[	$death2,		zombie_death3	] 	{self.frame = 124;};
void()	zombie_death3		=[	$death3,		zombie_death4	] 	{self.frame = 125;};
void()	zombie_death4		=[	$death4,		zombie_death5	] 	{if (self.onfire) {Effect_Fire(self.origin);} self.frame = 126;};
void()	zombie_death5		=[	$death5,		zombie_death6	] 	{self.frame = 127;};
void()	zombie_death6		=[	$death6,		zombie_death7	] 	{self.frame = 128;};
void()	zombie_death7		=[	$death7,		zombie_death8	] 	{if (self.onfire) {Effect_Fire(self.origin);} self.frame = 129;};
void()	zombie_death8		=[	$death8,		zombie_death9	] 	{self.frame = 130;};
void()	zombie_death9		=[	$death9,		zombie_death10	] 	{self.frame = 131;};
void()	zombie_death10		=[	$death10,		zombie_death11	] 	{self.frame = 132;zombie_footstep();};
void()	zombie_death11		=[	$death11,		SUB_Null	] 	{if (self.onfire) {Effect_Fire(self.origin);} self.iszomb = 0; self.nextthink = time + 3; self.think = removeZombie;  self.frame = 133;};

//////////////////////////////////////////////////////////////////////////////////// ZOMBIE DEATH 2
//134-138
$frame deathB1 deathB2 deathB3 deathB4 deathB5

void()	zombie_deathB1		=[	$deathB1,		zombie_deathB2	] 	{if (self.onfire) {Effect_Fire(self.origin);} self.frame = 134;};
void()	zombie_deathB2		=[	$deathB2,		zombie_deathB3	] 	{self.frame = 135;};
void()	zombie_deathB3		=[	$deathB3,		zombie_deathB4	] 	{if (self.onfire) {Effect_Fire(self.origin);} self.frame = 136;};
void()	zombie_deathB4		=[	$deathB4,		zombie_deathB5	] 	{self.frame = 137;zombie_footstep();};
void()	zombie_deathB5		=[	$deathB5,		SUB_Null		] 	{if (self.onfire) {Effect_Fire(self.origin);} self.iszomb = 0; self.nextthink = time + 3; self.think = removeZombie; self.frame = 138;};


//////////////////////////////////////////////////////////////////////////////////// ZOMBIE DEATH 3
//139-148

$frame deathC1 deathC2 deathC3 deathC4 deathC5 deathC6 deathC7 deathC8 deathC9 deathC10

void()	zombie_deathC1		=[	$deathC1,		zombie_deathC2	] 	{if (self.onfire) {Effect_Fire(self.origin);} self.frame = 139;};
void()	zombie_deathC2		=[	$deathC2,		zombie_deathC3	] 	{self.frame = 140;};
void()	zombie_deathC3		=[	$deathC3,		zombie_deathC4	] 	{self.frame = 141;};
void()	zombie_deathC4		=[	$deathC4,		zombie_deathC5	] 	{if (self.onfire) {Effect_Fire(self.origin);} self.frame = 142;};
void()	zombie_deathC5		=[	$deathC5,		zombie_deathC6	] 	{self.frame = 143;};
void()	zombie_deathC6		=[	$deathC6,		zombie_deathC7	] 	{self.frame = 144;};
void()	zombie_deathC7		=[	$deathC7,		zombie_deathC8	] 	{if (self.onfire) {Effect_Fire(self.origin);} self.frame = 145;};
void()	zombie_deathC8		=[	$deathC8,		zombie_deathC9	] 	{self.frame = 146;zombie_footstep();};
void()	zombie_deathC9		=[	$deathC9,		zombie_deathC10	] 	{self.frame = 147;};
void()	zombie_deathC10		=[	$deathC10,		SUB_Null		] 	{if (self.onfire) {Effect_Fire(self.origin);} self.iszomb = 0; self.nextthink = time + 3; self.think = removeZombie; self.frame = 148;};



void(entity ent) Zombie_Death_Cleanup = {
	// Already dead
	if(ent.aistatus == "0") {
		return;
	}

	// Insta-Kill should always cause the head to gib on Zombie death.
	if (ent.head && instakill_finished > time) {
		makevectors(ent.head.owner.angles);
 		vector where = ent.origin + (ent.head.view_ofs_x * v_right) + (ent.head.view_ofs_y * v_forward) + (ent.head.view_ofs_z * v_up);
 		spawn_gibs(where);
 		ent.head.deadflag = false;
		ent.head.solid = SOLID_NOT;
		setmodel(ent.head, "");
		ent.head.frame = 0;

#ifndef FTE

		updateLimb (ent, 0, world);

#endif // FTE

	} 

	ent.aistatus = "0";
	ent.solid = SOLID_NOT;
	ent.movetype = MOVETYPE_NONE;
	ent.takedamage = DAMAGE_NO;
	
	ent.head.classname = "";
	ent.head.solid = SOLID_NOT;
	ent.head.movetype = MOVETYPE_NONE;
	ent.head.takedamage = DAMAGE_NO;
	ent.larm.solid = SOLID_NOT;
	ent.larm.movetype = MOVETYPE_NONE;
	ent.larm.takedamage = DAMAGE_NO;
	ent.rarm.solid = SOLID_NOT;
	ent.rarm.movetype = MOVETYPE_NONE;
	ent.rarm.takedamage = DAMAGE_NO;

	sound(ent, CHAN_WEAPON, "sounds/null.wav" ,1, ATTN_NORM);
	sound(ent, CHAN_VOICE, "sounds/null.wav" ,1, ATTN_NORM);
	sound(ent, CHAN_BODY, "sounds/null.wav" ,1, ATTN_NORM);
	sound(ent, CHAN_ITEM, "sounds/null.wav" ,1, ATTN_NORM);
	
	ent.usedent = world;
	
	if(ent.goalentity.classname == "window")
	{
		if(ent.goalentity.box1owner == ent)
			ent.goalentity.box1owner = world;
		if(ent.goalentity.box2owner == ent)
			ent.goalentity.box2owner = world;
		if(ent.goalentity.box3owner == ent)
			ent.goalentity.box3owner = world;
		if(ent.goalentity.usedent == ent)
			ent.goalentity.usedent = world;
	}
	
	ent.health = 0;
	ent.respawn_iterator = 0;
	ent.skin = 0;
	Remaining_Zombies = Remaining_Zombies - 1;
}


void() Zombie_Death =
{
	Zombie_Death_Cleanup(self);
	
	// Death Noises
	if (self.electro_targeted == false)
		Zombie_PlaySoundType(ZOMBIE_SOUND_TYPE_DEATH);
	else
		Sound_PlaySound(self.head, "sounds/machines/elec_shock.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
	
	if(self.outside == false && self.electro_targeted == false)
	{
		if (totalpowerups < POWERUPS_PER_ROUND)
		{
			if (total_powerup_points >= powerup_score_threshold)
			{
				Spawn_Powerup(self.origin, -1);
				powerup_activate *= 1.14;
				powerup_score_threshold = total_powerup_points + powerup_activate;
			}
			else if (random () <= 0.02)
				Spawn_Powerup(self.origin, -1);
		}
	}
	
	if(self.crawling == TRUE)
	{
		crawler_die1();
	}
	else
	{
		if(random() < 0.3)
		{
			zombie_deathA1();
		}
		else if(random() < 0.5)
		{
			zombie_deathB1();
		}
		else 
		{
			zombie_deathC1();
		}
	}
};

//
// --------------------
// Electric Damage
// --------------------
//

//
// Z_ElectroShock
// Mark Zombie to die by non-Waffe Electricity.
//
void() Z_ElectroShock =
{
	// Mark the Zombie as to-be-dead
	self.electro_targeted = true;

	// Determine a time for the Zombie to die. (0.1-1.25s)
	self.death_timer = (rint((random() * 11.5) + 1)/10) + time;
	self.death_timer_activated = true;

	// Spawn a Sparkle
	if(self.crawling == true) {
		tesla_spark(self.origin - '0 0 18');
	}
	else {
		tesla_spark(self.origin + '0 0 13');
	}
	// Zombie_Tesla_Light();

	// Decide what to Gib
	float r = random();

	// Head?
	if (r < 0.20 && self.head.deadflag) {
		self.head.frame = 0;
		self.head.health = 0;
		self.head.deadflag = false;
		self.head.solid = SOLID_NOT;
		setmodel(self.head, "");
		makevectors(self.angles);
		spawn_gibs(self.origin + (self.head.view_ofs_x * v_right) 
		+ (self.head.view_ofs_y * v_forward) + (self.head.view_ofs_z * v_up));
		SpawnBlood(self.origin, self.origin, 50);
	}
	// Right Arm?
	r = random();
	if (r < 0.20 && self.rarm.deadflag) {
		self.rarm.frame = 0;
		self.rarm.health = 0;
		self.rarm.deadflag = false;
		self.rarm.solid = SOLID_NOT;
		setmodel(self.rarm, "");
		makevectors(self.angles);
		spawn_gibs(self.origin + (self.rarm.view_ofs_x * v_right) 
		+ (self.rarm.view_ofs_y * v_forward) + (self.rarm.view_ofs_z * v_up));
		SpawnBlood(self.origin, self.origin, 50);
	}
	// Left Arm?
	r = random();
	if (r < 0.20 && self.larm.deadflag) {
		self.larm.frame = 0;
		self.larm.health = 0;
		self.larm.deadflag = false;
		self.larm.solid = SOLID_NOT;
		setmodel(self.larm, "");
		makevectors(self.angles);
		spawn_gibs(self.origin + (self.larm.view_ofs_x * v_right) 
		+ (self.larm.view_ofs_y * v_forward) + (self.larm.view_ofs_z * v_up));
		SpawnBlood(self.origin, self.origin, 50);
	}

	// Play the first Electro-Sound
	Sound_PlaySound(self.head, "sounds/machines/elec_shock.wav", SOUND_TYPE_ZOMBIE_LOUD, SOUND_PRIORITY_PLAYALWAYS);
}

/////////////////////ZOMBIE WAFFE STUFF/////////////////////

// Wunder'd
// 149-152 (FIXME - animate wunder'd zombies)
$frame wunder1 wunder2 wunder3 wunder4 wunder5 wunder6 wunder7 wunder8 wunder9 wunder10

void() zombie_wunder1		=[	$wunder1,		zombie_wunder2	]	{tesla_arc(); Zombie_PlaySoundType(ZOMBIE_SOUND_TYPE_CRAWL); self.frame = 149;};
void() zombie_wunder2		=[	$wunder2,		zombie_wunder3	]	{tesla_arc(); self.frame = 150;};
void() zombie_wunder3		=[	$wunder3,		zombie_wunder4	]	{tesla_arc();  self.frame = 151;};
void() zombie_wunder4		=[	$wunder4,		zombie_wunder5	]	{tesla_arc(); self.frame = 152;};
void() zombie_wunder5		=[	$wunder5,		zombie_wunder6	]	{tesla_arc(); self.frame = 151;};
void() zombie_wunder6		=[	$wunder6,		zombie_wunder7	]	{tesla_arc(); self.frame = 152;};
void() zombie_wunder7		=[	$wunder7,		zombie_wunder8	]	{tesla_arc(); self.frame = 151;};
void() zombie_wunder8		=[	$wunder8,		zombie_wunder9	]	{tesla_arc(); Zombie_PlaySoundType(ZOMBIE_SOUND_TYPE_DEATH); self.frame = 152;};
void() zombie_wunder9		=[	$wunder9,		zombie_wunder10	]	{tesla_arc(); self.frame = 151;};
void() zombie_wunder10		=[	$wunder10,		SUB_Null 		]	{
	tesla_arc(); self.iszomb = 0; self.nextthink = time + 3; self.think = removeZombie;self.frame = 152;
	// Play final death anim:
	if(random() < 0.3) {
		zombie_deathA1();
	}
	else if(random() < 0.5) {
		zombie_deathB1();
	}
	else {
		zombie_deathC1();
	}
};




void() Zombie_Tesla_Death = {
	Zombie_Death_Cleanup(self);
	if(self.crawling == true) {
		tesla_spark(self.origin - '0 0 18');
		crawler_death_wunder1();
	}
	else {
		tesla_spark(self.origin + '0 0 13');
		zombie_wunder1();
	}
};

//
// Zombie_HeadCanGib(weapon)
// Checks if a Zombie is in a state where
// it's head can gib.
//
float(entity who, float weapon) Zombie_HeadCanGib =
{
	// First, is this weapon even capable?
	if (!WepDef_WeaponCanGibEnemy(weapon))
		return false;

	// Has it's head already been ripped off?
	if (!who.head.deadflag)
		return false;

	// If they have more than 10%(?) of health,
	// don't gib.
	float health_ratio = who.head.health / z_health;
	if (health_ratio > 0.10)
		return false;

	// Seems to pass, let it gib!
	return true;
};

/////////////////////ZOMBIE SPAWNING/////////////////////
void() zombieRise =
{
	//better not move wile rising, would happen sometimes
	self.velocity_x = 0;
	self.velocity_y = 0;
	
	if(self.frame < 13)//begin
	{
		self.frame = 12;//since we're going to add one anyways
		self.state = 1;//don't make crawler, yo!
	}
	if(self.frame == 36)
	{
		self.state = 0;
		if(self.crawling == 2)
		{
			makeCrawler(self);
			return;
		}
		self.th_walk();
		return;
	}
	self.frame ++;
	self.think = zombieRise;
	self.nextthink = time + 0.1;
	//LinkZombiesHitbox();
	//bprint (PRINT_HIGH, "Linked hitboxes");
};
void() spawn_zombie =
{
	setsize(self, '0 0 0', '0 0 0');
	if (self.spawnflags & INACTIVE)
	{
		if (cvar("developer"))
			setmodel(self, "models/player.mdl");
		self.classname = "spawn_zombie_in";
	}
	else
	{
		if (cvar("developer"))
			setmodel(self, "models/ai/zfull.mdl");
		self.classname = "spawn_zombie";
		self.spawn_id = zombie_spawn_points;
		zombie_spawn_points++;
	}
	
	//self.think = zombie_spawn_think;
	//self.nextthink = time + 0.5;
	
	self.solid = SOLID_NOT;
};

void(entity where) spawn_a_zombieB;
void() set_z_health =
{
	if(rounds <= 10)
	{
		z_health = 50 + (rounds * 100);
	}
	else
	{	
		local float i;
		i = rounds - 10;
		z_health = 1040;
		while(i > 0)
		{
			z_health *= 1.1; 
			i--;
		}
	}
};

//
// Zombie_ReassignSpawnIDs()
// To be called whenever we mess with a spawn point,
// this makes sure that spawn_a_zombieA() can never
// try to spawn a zombie at a point which index is
// inactive.
//
void() Zombie_ReassignSpawnIDs =
{
	float num_spawns = zombie_spawn_points;

	// First, clear the list.
	zombie_spawn_points = 0;

	while(num_spawns > 0) {
		entity zombie_spawn = findfloat(world, spawn_id, num_spawns);
		if (zombie_spawn) {
			zombie_spawn.spawn_id = 0;
		}
		num_spawns--;

		if (num_spawns)
			zombie_spawn = findfloat(world, spawn_id, num_spawns);
	}

	// Now do the re-assignment
	entity zombie_spawns = find(world, classname, "spawn_zombie");
	while(zombie_spawns) {
		zombie_spawns.spawn_id = zombie_spawn_points;
		zombie_spawn_points++;

		zombie_spawns = find(zombie_spawns, classname, "spawn_zombie");
	}
}

inline entity() getFreeZombieEnt =
{
	entity who;
	who = find(world,classname,"freeZombieEntity");
	
	return who;
};

float() spawn_a_zombieA =
{
	// cypress (20 dec 2023) -- rewrote this routine to be a little less
	// needlessly bloaty/bogged down and be in greater parity.

	// don't bother doing anything if it's not even time yet (avoids needless
	// iteration over ent chain)
	if (zombie_spawn_timer > time)
		return false;

	// look for a free entity
	entity zombie_ent = getFreeZombieEnt();

	// no free entity found, maximum are in play.
	if (!zombie_ent)
		return false;

	// look for a random spawn point. note -- before there was a routine that
	// prevented spawns from repeating one after the other before. i liked the
	// evened out use of the playspace at the beginning of the game but this
	// isn't standard CoD functionality and it has negative consequences with 
	// larger areas.
	entity zombie_spawn;
	float zombie_spawn_id = rint((random() * (zombie_spawn_points - 1))); // starts at 0!

	zombie_spawn = find(world, classname, "spawn_zombie");
	while(zombie_spawn != world) {
		if (zombie_spawn.spawn_id == zombie_spawn_id)
			break;

		zombie_spawn = find(zombie_spawn, classname, "spawn_zombie");
	}

	if (zombie_spawn == world) {
		if (cvar("developer"))
			bprint(PRINT_HIGH, "spawn_a_zombieA: No active spawn point exists! Bailing!\n");
		return false;
	}

	// prompt stage 2: the actual spawning of the zombie.
	spawn_a_zombieB(zombie_spawn);
	spawn_delay = time + delay_at_round;
	return true;
};

void(float i) spawnSingleZombEnt =
{
	local entity zSpawn;
	zSpawn = spawn();
	zSpawn.classname = "freeZombieEntity";
	zSpawn.aistatus = "0";
	zSpawn.goaldummy = spawn();
	if (cvar("developer")) {
		setmodel(zSpawn.goaldummy, "models/way/normal_way.spr");
		zSpawn.goaldummy.effects = EF_FULLBRIGHT;
	}


	zSpawn.goaldummy.classname = "waypoint_target";
	
	zSpawn.head = spawn();
	zSpawn.head.classname = "in_ai_zombie_head";
	zSpawn.head.owner = zSpawn;
	zSpawn.head.solid = SOLID_NOT;
	zSpawn.head.movetype = MOVETYPE_STEP;
	zSpawn.head.takedamage = DAMAGE_NO;

#ifndef FTE

	updateLimb (zSpawn, 0, zSpawn.head);

#endif // FTE
	
	zSpawn.larm = spawn();
	zSpawn.larm.classname = "in_ai_zombie_larm";
	zSpawn.larm.owner = zSpawn;
	zSpawn.larm.solid = SOLID_NOT;
	zSpawn.larm.movetype = MOVETYPE_STEP;
	zSpawn.larm.takedamage = DAMAGE_NO;

#ifndef FTE

	updateLimb (zSpawn, 1, zSpawn.larm);

#endif // FTE
	
	zSpawn.rarm = spawn();
	zSpawn.rarm.classname = "in_ai_zombie_rarm";
	zSpawn.rarm.owner = zSpawn;
	zSpawn.rarm.solid = SOLID_NOT;
	zSpawn.rarm.movetype = MOVETYPE_STEP;
	zSpawn.rarm.takedamage = DAMAGE_NO;

#ifndef FTE
	updateLimb (zSpawn, 2, zSpawn.rarm);
#endif // FTE

};

void(entity szombie)  SetUpHitBoxes =
{
	szombie.head.solid = SOLID_CORPSE;
	szombie.head.movetype = MOVETYPE_STEP;
	szombie.head.classname = "ai_zombie_head";
	szombie.head.takedamage = DAMAGE_YES;

	szombie.larm.solid = SOLID_CORPSE;
	szombie.larm.movetype = MOVETYPE_STEP;
	szombie.larm.classname = "ai_zombie_larm";
	szombie.larm.takedamage = DAMAGE_YES;

	szombie.rarm.solid = SOLID_CORPSE;
	szombie.rarm.movetype = MOVETYPE_STEP;
	szombie.rarm.classname = "ai_zombie_rarm";
	szombie.rarm.takedamage = DAMAGE_YES;
	
#ifdef FTE

	szombie.head.dimension_solid = HITBOX_DIM_LIMBS;
	szombie.larm.dimension_solid = HITBOX_DIM_LIMBS;
	szombie.rarm.dimension_solid = HITBOX_DIM_LIMBS;

#endif	// FTE

	local entity oldself = self;
	self = szombie;
	SetZombieHitBox(BASE_BBOX);
	self = oldself;
};

void(entity szombie) SetZombieWalk =
{	
	float zombie_move_speed_threshold = (rounds - 1) * 8;
	float walktype_chance = rint((random() * 35)) + zombie_move_speed_threshold;

	// Walking Zombie
	if (walktype_chance <= 35) {
		float which_walk_anim = random();

		if (which_walk_anim < 0.33)
			szombie.walktype = 1;
		else if (which_walk_anim < 0.66)
			szombie.walktype = 2;
		else
			szombie.walktype = 3;
	}
	// Jogging Zombie
	else if (walktype_chance <= 70) {
		szombie.walktype = 4;
	} 
	// Sprinting Zombie
	else {
		szombie.walktype = 5;
	}
};

void() spawnAllZombEnts =
{
	local float i;
	i = 0;

	// cypress -- let host engine dictate
	// max ai at once.

#ifdef FTE

	while(i < 24)

#else

	while(i < nzp_maxai())

#endif // FTE
	{
		spawnSingleZombEnt(i);
		i++;
	}
};

void(entity where) spawn_a_zombieB =
{
	local entity szombie;//USED FOR WHAT TO SPAWN
	//szombie = spawn();
	
	szombie = getFreeZombieEnt();
	if(szombie == world || zombie_spawn_timer > time)
	{
		//bprint(PRINT_HIGH, "STOPPED AT B\n");
		//dprint("Out of zombie entities \n");
		return;
	}

#ifdef FTE
	szombie.head = spawn();
	szombie.rarm = spawn();
	szombie.larm = spawn();

	szombie.head.owner = szombie.rarm.owner = szombie.larm.owner = szombie;

#endif // FTE

	szombie.origin = where.origin;
	
	szombie.frame = 0;
	
	szombie.target = where.target;
	//blubsvel
	//szombie.movetype = MOVETYPE_STEP;

	szombie.head.deadflag = 1;
	szombie.larm.deadflag = 1;
	szombie.rarm.deadflag = 1;

	setmodel(szombie.head, "models/ai/zh^.mdl");
	setmodel(szombie.rarm, "models/ai/zar(.mdl");
	setmodel(szombie.larm, "models/ai/zal(.mdl");

#ifndef FTE

	updateLimb (szombie, 0, szombie.head);
	updateLimb (szombie, 1, szombie.larm);
	updateLimb (szombie, 2, szombie.rarm);

#endif // FTE
	
	
	szombie.solid = SOLID_CORPSE;

#ifdef FTE

	szombie.dimension_solid = HITBOX_DIM_ZOMBIES;

#endif // FTE

	szombie.movetype = MOVETYPE_WALK;
	setmodel(szombie, "models/ai/zb%.mdl");
	szombie.hop_step = 0;

	// cypress (20 dec 2023) -- see comment from removeZombie(),
	// this is just a sanity check just in case someone decides
	// to play around with a freeZombieEntity and breaks that check..
	szombie.nextthink = time + 0.05;
	szombie.think = SUB_Null;

	szombie.gravity = 1.0;
	
	if (map_compatibility_mode == MAP_COMPAT_BETA) {
		szombie.mins = '-6 -6 -24';
		szombie.maxs = '6 6 22';

#ifdef FTE

		// Need to force-set the hull for FTE or else Zombie setorigin()
		// calls will fail in tight spaces.
		szombie.hull = 1;

#endif // FTE

	} else {
		szombie.mins = '-8 -8 -32';
		szombie.maxs = '8 8 30';
	}
	
	setsize (szombie, szombie.mins, szombie.maxs);
		
	
	//====================================
	
	if(pointcontents(szombie.origin - '0 0 36') == -2)
	{
		while(pointcontents (szombie.origin - '0 0 36') == -2)
		{
			szombie.origin = szombie.origin + '0 0 1';
			setorigin(szombie,szombie.origin );
		}
	}

	szombie.classname = "ai_zombie";
	szombie.aistatus = "1";
	setorigin (szombie.goaldummy, '0 0 0');
	szombie.origin_z = szombie.origin_z + 1;
	szombie.outside = true;
	szombie.takedamage = DAMAGE_YES;
	setorigin(szombie, szombie.origin);
	szombie.flags = szombie.flags | FL_PARTIALGROUND | FL_MONSTER;

	szombie.spawnflags = where.spawnflags;
	//////////FIX FOR OUTSIDE
	if(szombie.spawnflags & 4)
	{
		szombie.outside = FALSE;
	}
	
	/////////
	
	szombie.ideal_yaw = szombie.angles_y;
	szombie.yaw_speed = 20;
	szombie.iszomb = 1;
	szombie.death_timer = 0;
	szombie.death_timer_activated = false;
	szombie.electro_targeted = false;
	
	// We used to set different limb health, too, but we now handle any limb damage differences in weapon stats.
	szombie.health = szombie.head.health = szombie.larm.health = szombie.rarm.health = z_health;

	szombie.th_die = Zombie_Death;
	szombie.th_walk = Zombie_Walk_Setup;
	szombie.th_melee = zombie_attack;
	szombie.th_idle = zombie_idle;
	szombie.th_windowhop = zombie_hop1;
	
	szombie.th_fall = zombie_fall1;
	szombie.th_land = zombie_land1;

	szombie.th_diewunder = Zombie_Tesla_Death;
	
	//szombie.th_jump = zombie_jump1;
	//szombie.th_grabledge = zombie_grabledge1;
	

	//==================================================================================================
	SetZombieWalk(szombie);
	//==================================================================================================
	
	SetUpHitBoxes(szombie); 
	//szombie.walktype = 5;
	////////////////////////////////////////////////////
	szombie.reload_delay = 30 + time;//save  floats, equals respawn time.
	local entity old_self;
	old_self = self;
	self = szombie;
	if (self.spawnflags & 2)//RISER
	{
		zombieRise();
	}
	else
		self.th_walk();
	self = old_self;
	
	//LinkZombiesHitbox();

	//
	// Zombie skin setting
	//
	float tex;
	tex = random();

	if (tex > 0.75)
		szombie.skin = 0;
	else if (tex > 0.50)
		szombie.skin = 1;
	else if (tex > 0.25)
		szombie.skin = 2;
	else
		szombie.skin = 3;

	szombie.head.skin = szombie.larm.skin = szombie.rarm.skin = szombie.skin;

	if (map_compatibility_mode == MAP_COMPAT_BETA)
		szombie.head.scale = szombie.larm.scale = szombie.rarm.scale = szombie.scale = 0.73;

	// set up timer for next spawn
	zombie_spawn_timer = zombie_spawn_delay + time;

	// Depending on how fast AI processes, sometimes Zombie_AI() will execute
	// while we are still at the world origin temporarily. This can cause us
	// to play a sound at the origin, so add a small delay to prevent this.
	// (and randomize it a bit).
	szombie.sound_time = time + (random() * 2) + 3;
};

void() zombie_dummy =
{
	self.frame = 0;
	
	self.solid = SOLID_SLIDEBOX;
	self.movetype = MOVETYPE_STEP;

	
	setmodel(self, "models/ai/zfull.mdl");
	
	setsize (self, '-8 -8 -32', '8 8 30');
	setorigin(self, self.origin);
	self.classname = "ai_zombie";
	self.takedamage = DAMAGE_YES;
	self.flags = self.flags | FL_PARTIALGROUND | FL_MONSTER;
	self.health = 999999;
	SetUpHitBoxes(self);
};
