
class ZArtiTomeOfPower : ArtiTomeOfPower
{

	override void SetGiveAmount(Actor receiver, int amount, bool givecheat)
	{
		if (givecheat) return;

		Super.SetGiveAmount(receiver, amount, givecheat);
	}

}

class ZDukePlayerBase : PlayerPawn
{

	override void CheatGive(String name, int amount)
	{
		let player = self.player;

		if (player.mo == NULL || player.health <= 0)
		{
			return;
		}

		let invs = self.FindInventory("DukeShrinkCounter");
		if (invs!=null && invs.Amount>0) return;

		int duke_bot_amount = 0;
		let invdb = self.FindInventory("Duke_Bot");
		if (invdb!=null) duke_bot_amount = invdb.Amount;

		Super.CheatGive(name, amount);

		if (name ~== "all" || name ~== "everything")
		{
			int dukebots = 0;
			let inv1 = self.FindInventory("Dukebot1Inventory");
			let inv2 = self.FindInventory("Dukebot2Inventory");
			let inv3 = self.FindInventory("Dukebot3Inventory");
			let inv4 = self.FindInventory("Dukebot4Inventory");
			if (inv1!=null)	dukebots = dukebots + inv1.Amount;
			if (inv2!=null)	dukebots = dukebots + inv2.Amount;
			if (inv3!=null)	dukebots = dukebots + inv3.Amount;
			if (inv4!=null)	dukebots = dukebots + inv4.Amount;

			self.A_GiveInventory("Dukebot1Inventory",1);
			self.A_GiveInventory("Dukebot2Inventory",1);
			self.A_GiveInventory("Dukebot3Inventory",1);
			self.A_GiveInventory("Dukebot4Inventory",1);
			self.A_SetInventory("Dukebot1Type",random[RIDPLAYER](1,4));
			self.A_SetInventory("Dukebot2Type",random[RIDPLAYER](1,4));
			self.A_SetInventory("Dukebot3Type",random[RIDPLAYER](1,4));
			self.A_SetInventory("Dukebot4Type",random[RIDPLAYER](1,4));
			self.A_SetInventory("Duke_Bot",duke_bot_amount+(4-dukebots));
		}

	}

	void A_D3DPlayDukeSound(String sound)
	{
		let inv = self.FindInventory("DukeTaunting");
		if (inv==null || inv.Amount==0) {
			self.A_PlaySound(sound,CHAN_VOICE); 
			self.A_GiveInventory("DukeTaunting",1);
		}
	}

	private void clearDukebots()
	{
		let inv = self.FindInventory("Dukebot1Active");
		if (inv!=null && inv.Amount>0) {
			self.A_TakeInventory("Dukebot1Active", 1);
			self.A_SetInventory("Dukebot1Retrieve", 0);
			self.A_GiveInventory("Duke_Bot", 1);
			self.A_TakeInventory("Retrieve_Duke_Bots", 1);
		}

		inv = self.FindInventory("Dukebot2Active");
		if (inv!=null && inv.Amount>0) {
			self.A_TakeInventory("Dukebot2Active", 1);
			self.A_SetInventory("Dukebot2Retrieve", 0);
			self.A_GiveInventory("Duke_Bot", 1);
			self.A_TakeInventory("Retrieve_Duke_Bots", 1);
		}

		inv = self.FindInventory("Dukebot3Active");
		if (inv!=null && inv.Amount>0) {
			self.A_TakeInventory("Dukebot3Active", 1);
			self.A_SetInventory("Dukebot3Retrieve", 0);
			self.A_GiveInventory("Duke_Bot", 1);
			self.A_TakeInventory("Retrieve_Duke_Bots", 1);
		}

		inv = self.FindInventory("Dukebot4Active");
		if (inv!=null && inv.Amount>0) {
			self.A_TakeInventory("Dukebot4Active", 1);
			self.A_SetInventory("Dukebot4Retrieve", 0);
			self.A_GiveInventory("Duke_Bot", 1);
			self.A_TakeInventory("Retrieve_Duke_Bots", 1);
		}
	}

	override int DamageMobj(Actor inflictor, Actor source, int damage, Name mod, int flags, double angle)
    	{
		if( !source || (source && (source==self || source.species!="Dukes")) )
		{
	        	if(mod == 'Fire' || mod == 'Slime' || mod == 'DukeAcid' || mod == 'DukeFire')
        		{
				let inv = self.FindInventory("Protective Boots");
				if (inv!=null && inv.Amount>0) {
					damage=0;
					self.A_GiveInventory("BootsActive", 100);
					self.A_TakeInventory("Protective Boots",1);
					inv = self.FindInventory("Protective Boots");
					if (inv!=null) self.A_SetInventory("BootsDuration", inv.Amount); else self.A_SetInventory("BootsDuration", 0);
				} else if ( inflictor && (mod == 'Fire' || mod == 'DukeAcid') ) {
					if (inflictor.GetClassName()=="RadioactiveProjectile" || inflictor.GetClassName()=="BarrelExplosionAcidColumn") {
						damage = damage/2.0;
					} else	if (inflictor.GetClassName()=="DukeBurningFireDamage") {
						damage = damage/5.0;
					}
				}
			}
			else if(mod == 'BulletFlame')
        		{
				let inv = self.FindInventory("Protective Boots");
				if (inv!=null && inv.Amount>0) {
					damage=max(1,damage/2);
					self.A_GiveInventory("BootsActive", 100);
					self.A_TakeInventory("Protective Boots",damage);
					inv = self.FindInventory("Protective Boots");
					if (inv!=null) self.A_SetInventory("BootsDuration", inv.Amount); else self.A_SetInventory("BootsDuration", 0);

				} 
			}
		}

		if(source || inflictor)
		{
			if(!source && inflictor) source = inflictor;

        		else if(mod == 'LowPush')
        		{
				float knockbackangle = self.angle + deltaangle(self.angle,self.angleto(source));
				float knockback = 1.5;
				float knockbackz = 0.0;
				self.A_ChangeVelocity(-cos(knockbackangle)*knockback, -sin(knockbackangle)*knockback, knockbackz/5.0 );
        		}
        		else if(mod == 'MidPush')
        		{
				float knockbackangle = self.angle + deltaangle(self.angle,self.angleto(source));
				float knockback = 5.0;
				float knockbackz = 2.0;
				self.A_ChangeVelocity(-cos(knockbackangle)*knockback, -sin(knockbackangle)*knockback, knockbackz/5.0 );
        		}
        		else if(mod == 'HighPush')
        		{
				float knockbackangle = self.angle + deltaangle(self.angle,self.angleto(source));
				float knockback = 10.0;
				float knockbackz = 4.0;
				self.A_ChangeVelocity(-cos(knockbackangle)*knockback, -sin(knockbackangle)*knockback, knockbackz/5.0 );
        		}
		}

		if(inflictor && inflictor.GetClass() is 'DukePlasmaExplosion') damage = 0;

	        return super.DamageMobj(inflictor, source, damage, mod, flags, angle);
	}

	override void CheckCrouch(bool totallyfrozen)
    	{
		if (waterlevel || bNoGravity)
		{
			let player = self.player;
			UserCmd cmd = player.cmd;
			if (cmd.buttons & BT_CROUCH) {
				player.Uncrouch();
				self.A_ChangeVelocity(0, 0, -3/5.0 );
			}
		}
		else
		{
			Super.CheckCrouch(totallyfrozen);
		}
	}

	override void CheckJump()
	{
		if (bNoGravity)
		{
			let player = self.player;
			UserCmd cmd = player.cmd;
			if (cmd.buttons & BT_JUMP) {
				self.A_ChangeVelocity(0, 0, 3/5.0 );
			}
		}
		else
		{
			Super.CheckJump();
		}
	}

	override void CheckAirSupply()
	{
		if (waterlevel >= 3) {
			let inv = self.FindInventory("Scuba Gear");
			if (inv!=null && inv.Amount>0) {
				self.ACS_ScriptCall("HudScubaGear");
				ResetAirSupply();
				self.A_GiveInventory("ScubaActive", 100);
				if(Level.maptime%TICRATE==0) self.A_TakeInventory("Scuba Gear",1);
				inv = self.FindInventory("Scuba Gear");
				if (inv!=null) self.A_SetInventory("ScubaDuration", inv.Amount); else self.A_SetInventory("ScubaDuration", 0);
			} else {
				if (Level.airsupply > 0 && AirCapacity > 0) {
					float totaloxygen = Level.airsupply * AirCapacity;
					int oxygen = int( ((player.air_finished - Level.maptime) / totaloxygen) * 100.0 );
					if (oxygen<0) oxygen = 0;
					self.A_SetInventory("OxygenBarActive", oxygen);
					self.A_SetInventory("OxygenBar", oxygen);
				}
			}
		} else {
			self.A_SetInventory("OxygenBarActive", 0);
		}

		Super.CheckAirSupply();
	}

	override void Die(Actor source, Actor inflictor, int dmgflag)
    	{
		ActorIterator Finder = Level.CreateActorIterator(self.PlayerNumber()+2999,"Actor");
		Actor mo;

		while ( (mo = Actor(Finder.Next())) )
		{
			mo.ChangeTid(3500);
			mo.TakeInventory("EnemyBeingStomped",1);
		}

		self.A_SetInventory("PipebombThrowForce", 0);

	        super.Die(source, inflictor, dmgflag);
	}

	override void Tick()
	{
		if (!player || !player.mo || player.mo != self)
		{
			Super.Tick();
			return;
		}

		if (Level.maptime==0) clearDukebots();

		let inv = self.FindInventory("DukeDamagePickup");
		if (inv!=null && inv.Amount>0) {
			self.A_TakeInventory("DukeDamagePickup",1);
		}

		let invb = self.FindInventory("BootsActive");
		if (invb!=null && invb.Amount>0) {
			self.A_TakeInventory("BootsActive",1);
		}

		let invs = self.FindInventory("ScubaActive");
		if (invs!=null && invs.Amount>0) {
			self.A_TakeInventory("ScubaActive",1);
		}

		let invp = self.FindInventory("PipebombThrowForce");
		if (invp!=null && invp.Amount>0) {
			int pbbar = int( ((invp.Amount-1) / 19.0) * 100.0 );
			self.A_SetInventory("PipebombBarActive", pbbar);
			self.A_SetInventory("PipebombBar", pbbar);
		} else {
			self.A_SetInventory("PipebombBarActive", 0);
		}

        	Super.Tick();
	}

}

class ZDukePlayer : ZDukePlayerBase
{
	Weapon lastweapon;
	Weapon wpistol;
	Weapon wshotgun;
	Weapon wplasma;
	Weapon wexpander;
	Weapon wincinerator;

	double max_distance;
	float enemy_heightadd;
	float enemy_heightsub;
	float enemy_heightmult;

	double morphhealth;
	int morpharmoramount;

	override bool MorphPlayer(playerinfo activator, Class<PlayerPawn> spawntype, int duration, int style, Class<Actor> enter_flash, Class<Actor> exit_flash)
	{
		morphhealth = self.health;

		let inv = self.FindInventory("BasicArmor");
		if(inv) morpharmoramount = inv.Amount;

		self.A_SetInventory("PipebombThrowForce", 0);

		self.A_StopSound(5);

		return super.MorphPlayer(activator, spawntype, duration, style, enter_flash, exit_flash);
	}

	override void PostBeginPlay()
	{
		max_distance = 50;
		enemy_heightadd = 0;
		enemy_heightsub = 12;
		enemy_heightmult = 1;

		Super.PostBeginPlay();
	}

	override void OnRespawn()
	{
		self.A_TakeInventory("TossPipeBomb", 1);
		self.A_TakeInventory("TossTripBomb", 1);

		Super.OnRespawn();
	}

	float getAimHeight(float enemyheight){
		return enemy_heightadd + (enemyheight * enemy_heightmult) - enemy_heightsub;
	}

	Actor,double findClosestActor()
	{
		PlayerPawn a = self;
		float closest_distance=(max_distance+1)*(max_distance+1);
		Actor closest=null;

		ActorIterator Finder = Level.CreateActorIterator(3500,"Actor");
		Actor mo;

		while ( (mo = Actor(Finder.Next())) )
		{
			if (mo == a || mo.bDormant || !mo.bShootable) { continue; }

			if (mo.health <= 0 && !mo.bIceCorpse) { continue; }

			if ( (!mo.bISMONSTER && !mo.bIceCorpse) || (mo.bFriendly && !(mo is "DukeGenericBabe")) ) { continue; }

			if (mo.GetSpecies()!="shrunkenemy" && mo.GetSpecies()!="FrozenEnemy" && !mo.bIceCorpse) { continue; }

			if (mo.CheckInventory("EnemyBeingStomped",1) || a.CheckInventory("Attacking",1)) { continue; }

			if (mo.bInvisible) { continue; }

			double mo_distance = a.distance3DSquared(mo);

			if (mo.GetSpecies()=="shrunkenemy" && mo_distance > max_distance*max_distance) { continue; }

			if ( (mo.GetSpecies()=="FrozenEnemy" || mo.bIceCorpse) && mo_distance > (max_distance+14)*(max_distance+14)) { continue; }

			if (closest && mo_distance > closest_distance) { continue; }

			closest=mo;
			closest_distance=mo_distance;
		}

		return closest,closest_distance;
	}

	//get angle and delta from two positions
	static vector3,double,double lookAt(Vector3 pos1,Vector3 pos2){
			//calculate difference between pos1 and pos2 (level.Vec3Diff takes portals into account)
			
			Vector3 delta=level.Vec3Diff(pos1,pos2);
			
			//calculate angle and pitch to other actor
			double target_angle=atan2(delta.y,delta.x);
			double target_pitch=-asin(delta.z/delta.length());

			return delta,target_angle,target_pitch;
	}

	void A_FindShrunkenFrozenEnemy()
	{
		PlayerPawn a = self;
		float closest_distance=max_distance+1;
		Actor closest=null;

		[closest,closest_distance]=findClosestActor();

		//if there was an enemy found
		if(closest){
			if(closest.GetSpecies()=="shrunkenemy") {
				closest.ChangeTid(a.PlayerNumber()+2999);
				closest.GiveInventory("EnemyBeingStomped",1);
				if(!closest.bIceCorpse) closest.SetIdle();

				float pheight=a.viewheight*a.player.crouchfactor;
				Vector3 aimheight=(0,0,getAimHeight(closest.height));
				Vector3 delta;
				Vector3 view=a.pos+(0,0,pheight);
				//get target angle and pitch
				double target_angle,target_pitch;
				[delta,target_angle,target_pitch]=lookAt(view,closest.pos+aimheight);

				a.TakeInventory("StompingAngle",100);
				a.TakeInventory("StompingPitch",100);
				a.GiveInventory("StompingAngle",int((target_angle/360.0)*100.0));
				a.GiveInventory("StompingPitch",int((target_pitch/360.0)*100.0));

				a.SetStateLabel("Stomping");
			}
			else if(closest.GetSpecies()=="FrozenEnemy" || closest.bIceCorpse) {
				if (a.absangle(a.AngleTo(closest), a.angle) <= (120 / 2)) {
					a.SetStateLabel("KickFrozen");
				}
			}
		}
	}

	override void CheckWeaponChange()
	{
		Super.CheckWeaponChange();

		let ReadyWeapon = player.ReadyWeapon.GetClass();
		let PendingWeapon = player.PendingWeapon.GetClass();

		if(ReadyWeapon is "CrackKnuckles" && lastweapon!=null) {
			player.PendingWeapon = lastweapon;
		}
		else if(ReadyWeapon is "DukeMoneyTaunt" && lastweapon!=null) {
			player.PendingWeapon = lastweapon;
		}

		if (d3d_weaponslotpriority==0) {
			if(ReadyWeapon is "Pistol ") {
				if(PendingWeapon is "Dual Pistol"|| self.FindInventory("Dual Pistol")==null ) {wpistol = null;} else {wpistol = player.ReadyWeapon;}
			}
			else if(ReadyWeapon is "Shotgun ") {
				if(PendingWeapon is "SSG" || self.FindInventory("SSG")==null ) {wshotgun = null;} else {wshotgun = player.ReadyWeapon;}
			}
			else if(ReadyWeapon is "Plasma Cannon") {
				if(PendingWeapon is "Chaingun Cannon" || self.FindInventory("Chaingun Cannon")==null ) {wplasma = null;} else {wplasma = player.ReadyWeapon;}
			}
			else if(ReadyWeapon is "Expander") {
				if(PendingWeapon is "Shrinker"|| self.FindInventory("Shrinker")==null ) {wexpander = null;} else {wexpander = player.ReadyWeapon;}
			}
			else if(ReadyWeapon is "Incinerator Flamethrower") {
				if(PendingWeapon is "Freezethrower"|| self.FindInventory("Freezethrower")==null ) {wincinerator = null;} else {wincinerator = player.ReadyWeapon;}
			}
		}

		if(PendingWeapon is "CrackKnuckles") {
			if(ReadyWeapon is "MightyBoot") {lastweapon = null;} else {lastweapon = player.ReadyWeapon;}
		}
		else if(PendingWeapon is "DukeMoneyTaunt") {
			if(ReadyWeapon is "MightyBoot") {lastweapon = null;} else {lastweapon = player.ReadyWeapon;}
		}

		if (d3d_weaponslotpriority==0) {
			if(PendingWeapon is "Dual Pistol" && wpistol!=null) {player.PendingWeapon = wpistol;}
			else if(PendingWeapon is "SSG" && wshotgun!=null) {player.PendingWeapon = wshotgun;}
			else if(PendingWeapon is "Chaingun Cannon" && wplasma!=null) {player.PendingWeapon = wplasma;}
			else if(PendingWeapon is "Shrinker" && wexpander!=null) {player.PendingWeapon = wexpander;}
			else if(PendingWeapon is "Freezethrower" && wincinerator!=null) {player.PendingWeapon = wincinerator;}
		}
	}

	override void Die(Actor source, Actor inflictor, int dmgflag)
    	{
		morphhealth = 0;
		morpharmoramount = 0;
		self.A_TakeInventory("TossPipeBomb", 1);
		self.A_TakeInventory("TossTripBomb", 1);

	        super.Die(source, inflictor, dmgflag);
	}

	override void Tick()
	{
		if (!player || !player.mo || player.mo != self)
		{
			Super.Tick();
			return;
		}

		if (morphhealth>0) {
			self.A_SetHealth(morphhealth);
			morphhealth = 0;

			if(morpharmoramount>0) {
				for(int i=1;i<=morpharmoramount;i++)
				{
					self.A_GiveInventory("DukeMorphArmor",1);
				}
			}
			morpharmoramount = 0;

			self.A_TakeInventory("TossPipeBomb", 1);
			self.A_TakeInventory("TossTripBomb", 1);
		}

		if (d3d_altmovement==0 && curSector && !(curSector.flags & 16))
		{
			level.airControl = 0.55;
			A_SetSpeed(2.2);
			vel.xy *= 0.9;
			viewBob = 0.9;
		}
		else
		{
			level.airControl = 0.00390625;
			A_SetSpeed(1);
		}

        	Super.Tick();
	}

}