class PowerupGiver : Inventory native
{
	
	native Class<Actor> PowerupType;
	native int EffectTics;		// Non-0 to override the powerup's default tics
	native color BlendColor;	// Non-0 to override the powerup's default blend
	native Name Mode;			// Meaning depends on powerup - used for Invulnerability and Invisibility
	native double Strength;		// Meaning depends on powerup - currently used only by Invisibility
	
	Default
	{
		Inventory.DefMaxAmount;
		+INVENTORY.INVBAR
		+INVENTORY.FANCYPICKUPSOUND
		Inventory.PickupSound "misc/p_pkup";
	}
}

class Powerup : Inventory native 
{
	native int EffectTics;	
	native color BlendColor;
	native Name Mode;			// Meaning depends on powerup - used for Invulnerability and Invisibility
	native double Strength;		// Meaning depends on powerup - currently used only by Invisibility

	// Note, that while this is an inventory flag, it only has meaning on an active powerup.
	override bool GetNoTeleportFreeze() { return bNoTeleportFreeze; }

	native virtual void InitEffect();
	native virtual void EndEffect();
	
}

class PowerInvulnerable : Powerup native
{
	Default
	{
		Powerup.Duration -30;
		inventory.icon "SPSHLD0";
	}
}

class PowerStrength : Powerup native
{
	Default
	{
		Powerup.Duration 1;
		Powerup.Color "ff 00 00", 0.5;
		+INVENTORY.HUBPOWER
	}
}

class PowerInvisibility : Powerup native
{
	Default
	{
		+SHADOW;
		Powerup.Duration -60;
		Powerup.Strength 80;
		Powerup.Mode "Fuzzy";
	}
}

class PowerGhost : PowerInvisibility
{
	Default
	{
		+GHOST;
		Powerup.Duration -60;
		Powerup.Strength 60;
		Powerup.Mode "None";
	}
}

class PowerShadow : PowerInvisibility
{
	Default
	{
		+INVENTORY.HUBPOWER
		Powerup.Duration -55;
		Powerup.Strength 75;
		Powerup.Mode "Cumulative";
	}
}

class PowerIronFeet : Powerup native
{
	Default
	{
		Powerup.Duration -60;
		Powerup.Color "00 ff 00", 0.125;
	}
}

class PowerMask : PowerIronFeet native
{
	Default
	{
		Powerup.Duration -80;
		Powerup.Color "00 00 00", 0;
		+INVENTORY.HUBPOWER
		Inventory.Icon "I_MASK";
	}
}

class PowerLightAmp : Powerup native
{
	Default
	{
		Powerup.Duration -120;
	}
}

class PowerTorch : PowerLightAmp native {}

class PowerFlight : Powerup native
{
	Default
	{
		Powerup.Duration -60;
		+INVENTORY.HUBPOWER
	}
}

class PowerWeaponLevel2 : Powerup native
{
	Default
	{
		Powerup.Duration -40;
		Inventory.Icon "SPINBK0";
		+INVENTORY.NOTELEPORTFREEZE
	}
}

class PowerSpeed : Powerup native
{
	native int SpeedFlags;

	Default
	{
		Powerup.Duration -45;
		Speed 1.5;
		Inventory.Icon "SPBOOT0";
		+INVENTORY.NOTELEPORTFREEZE
	}
	
	override double GetSpeedFactor() { return Speed; }
}

// Player Speed Trail (used by the Speed Powerup) ----------------------------

class PlayerSpeedTrail : Actor
{
	Default
	{
		+NOBLOCKMAP
		+NOGRAVITY
		Alpha 0.6;
		RenderStyle "Translucent";
	}
	
	override void Tick()
	{
		Alpha -= .6 / 8;
		if (Alpha <= 0)
		{
			Destroy ();
		}
	}
}

class PowerMinotaur : Powerup
{
	Default
	{
		Powerup.Duration -25;
		Inventory.Icon "SPMINO0";
	}
}

class PowerTargeter : Powerup native
{
	Default
	{
		Powerup.Duration -160;
		+INVENTORY.HUBPOWER
	}
	States
	{
	Targeter:
		TRGT A -1;
		Stop;
		TRGT B -1;
		Stop;
		TRGT C -1;
		Stop;
	}
}

//===========================================================================
//
// Frightener
//
//===========================================================================

class PowerFrightener : Powerup
{
	Default
	{
		Powerup.Duration -60;
	}
	
	override void InitEffect ()
	{
		Super.InitEffect();

		if (Owner== null || Owner.player == null)
			return;

		Owner.player.cheats |= CF_FRIGHTENING;
	}

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

		if (Owner== null || Owner.player == null)
			return;

		Owner.player.cheats &= ~CF_FRIGHTENING;
	}
}

//===========================================================================
//
// Buddha
//
//===========================================================================

class PowerBuddha : Powerup
{
	Default
	{
		Powerup.Duration -60;
	}

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

		if (Owner== null || Owner.player == null)
			return;

		Owner.player.cheats |= CF_BUDDHA;
	}

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

		if (Owner== null || Owner.player == null)
			return;

		Owner.player.cheats &= ~CF_BUDDHA;
	}
}

//===========================================================================
//
// Scanner (this is active just by being present)
//
//===========================================================================

class PowerScanner : Powerup
{
	Default
	{
		Powerup.Duration -80;
		+INVENTORY.HUBPOWER
	}
}

//===========================================================================
//
// TimeFreezer
//
//===========================================================================

class PowerTimeFreezer : Powerup
{
	Default
	{
		Powerup.Duration -12;
	}
	
	//===========================================================================
	//
	// InitEffect
	//
	//===========================================================================

	override void InitEffect()
	{
		int freezemask;

		Super.InitEffect();

		if (Owner == null || Owner.player == null)
			return;

		// When this powerup is in effect, pause the music.
		S_PauseSound(false, false);

		// Give the player and his teammates the power to move when time is frozen.
		freezemask = 1 << Owner.PlayerNumber();
		Owner.player.timefreezer |= freezemask;
		for (int i = 0; i < MAXPLAYERS; i++)
		{
			if (playeringame[i] &&
				players[i].mo != null &&
				players[i].mo.IsTeammate(Owner)
			   )
			{
				players[i].timefreezer |= freezemask;
			}
		}

		// [RH] The effect ends one tic after the counter hits zero, so make
		// sure we start at an odd count.
		EffectTics += !(EffectTics & 1);
		if ((EffectTics & 1) == 0)
		{
			EffectTics++;
		}
		// Make sure the effect starts and ends on an even tic.
		if ((level.time & 1) == 0)
		{
			level.frozen = true;;
		}
		else
		{
			// Compensate for skipped tic, but beware of overflow.
			if(EffectTics < 0x7fffffff)
				EffectTics++;
		}
	}

	//===========================================================================
	//
	// APowerTimeFreezer :: DoEffect
	//
	//===========================================================================

	override void DoEffect()
	{
		Super.DoEffect();
		// [RH] Do not change LEVEL_FROZEN on odd tics, or the Revenant's tracer
		// will get thrown off.
		// [ED850] Don't change it if the player is predicted either.
		if (level.time & 1 || (Owner != null && Owner.player != null && Owner.player.cheats & CF_PREDICTING))
		{
			return;
		}
		// [RH] The "blinking" can't check against EffectTics exactly or it will
		// never happen, because InitEffect ensures that EffectTics will always
		// be odd when level.time is even.
		level.frozen = ( EffectTics > 4*32 
			|| (( EffectTics > 3*32 && EffectTics <= 4*32 ) && ((EffectTics + 1) & 15) != 0 )
			|| (( EffectTics > 2*32 && EffectTics <= 3*32 ) && ((EffectTics + 1) & 7) != 0 )
			|| (( EffectTics >   32 && EffectTics <= 2*32 ) && ((EffectTics + 1) & 3) != 0 )
			|| (( EffectTics >    0 && EffectTics <= 1*32 ) && ((EffectTics + 1) & 1) != 0 ));
	}

	//===========================================================================
	//
	// APowerTimeFreezer :: EndEffect
	//
	//===========================================================================

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

		// If there is an owner, remove the timefreeze flag corresponding to
		// her from all players.
		if (Owner != null && Owner.player != null)
		{
			int freezemask = ~(1 << Owner.PlayerNumber());
			for (int i = 0; i < MAXPLAYERS; ++i)
			{
				players[i].timefreezer &= freezemask;
			}
		}

		// Are there any players who still have timefreezer bits set?
		for (int i = 0; i < MAXPLAYERS; ++i)
		{
			if (playeringame[i] && players[i].timefreezer != 0)
			{
				return;
			}
		}

		// No, so allow other actors to move about freely once again.
		level.frozen = false;

		// Also, turn the music back on.
		S_ResumeSound(false);
	}
}

//===========================================================================
//
// Damage
//
//===========================================================================

class PowerDamage : Powerup
{
	Default
	{
		Powerup.Duration -25;
	}
	
	//===========================================================================
	//
	// InitEffect
	//
	//===========================================================================

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

		if (Owner != null)
		{
			Owner.A_PlaySound(SeeSound, CHAN_5, 1.0, false, ATTN_NONE);
		}
	}

	//===========================================================================
	//
	// EndEffect
	//
	//===========================================================================

	override void EndEffect()
	{
		Super.EndEffect();
		if (Owner != null)
		{
			Owner.A_PlaySound(DeathSound, CHAN_5, 1.0, false, ATTN_NONE);
		}
	}

	//===========================================================================
	//
	// ModifyDamage
	//
	//===========================================================================

	override void ModifyDamage(int damage, Name damageType, out int newdamage, bool passive)
	{
		if (!passive && damage > 0)
		{
			newdamage = max(1, ApplyDamageFactors(GetClass(), damageType, damage, damage * 4));
			if (Owner != null && newdamage > damage) Owner.A_PlaySound(ActiveSound, CHAN_AUTO, 1.0, false, ATTN_NONE);
		}
	}
}

//===========================================================================
//
// Protection
//
//===========================================================================

class PowerProtection : Powerup
{
	Default
	{
		Powerup.Duration -25;
	}
	
	//===========================================================================
	//
	// InitEffect
	//
	//===========================================================================

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

		let o = Owner;	// copy to a local variable for quicker access.
		if (o != null)
		{
			o.A_PlaySound(SeeSound, CHAN_AUTO, 1.0, false, ATTN_NONE);

			// Transfer various protection flags if owner does not already have them.
			// If the owner already has the flag, clear it from the powerup.
			// If the powerup still has a flag set, add it to the owner.
			bNoRadiusDmg &= !o.bNoRadiusDmg;
			o.bNoRadiusDmg |= bNoRadiusDmg;

			bDontMorph &= !o.bDontMorph;
			o.bDontMorph |= bDontMorph;
			
			bDontSquash &= !o.bDontSquash;
			o.bDontSquash |= bDontSquash;

			bDontBlast &= !o.bDontBlast;
			o.bDontBlast |= bDontBlast;
			
			bNoTeleOther &= !o.bNoTeleOther;
			o.bNoTeleOther |= bNoTeleOther;
			
			bNoPain &= !o.bNoPain;
			o.bNoPain |= bNoPain;

			bDontRip &= !o.bDontRip;
			o.bDontRip |= bDontRip;
		}
	}

	//===========================================================================
	//
	// EndEffect
	//
	//===========================================================================

	override void EndEffect()
	{
		Super.EndEffect();
		let o = Owner;	// copy to a local variable for quicker access.
		if (o != null)
		{
			o.A_PlaySound(DeathSound, CHAN_AUTO, 1.0, false, ATTN_NONE);
			
			o.bNoRadiusDmg &= !bNoRadiusDmg;
			o.bDontMorph &= !bDontMorph;
			o.bDontSquash &= !bDontSquash;
			o.bDontBlast &= !bDontBlast;
			o.bNoTeleOther &= !bNoTeleOther;
			o.bNoPain &= !bNoPain;
			o.bDontRip &= !bDontRip;
		}
	}

	//===========================================================================
	//
	// AbsorbDamage
	//
	//===========================================================================

	override void ModifyDamage(int damage, Name damageType, out int newdamage, bool passive)
	{
		if (passive && damage > 0)
		{
			newdamage = max(1, ApplyDamageFactors(GetClass(), damageType, damage, damage / 4));
			if (Owner != null && newdamage < damage) Owner.A_PlaySound(ActiveSound, CHAN_AUTO, 1.0, false, ATTN_NONE);
		}
	}
}

//===========================================================================
//
// Drain
//
//===========================================================================

class PowerDrain : Powerup
{
	Default
	{
		Powerup.Duration -60;
	}
	
	override void InitEffect()
	{
		Super.InitEffect();

		if (Owner!= null && Owner.player != null)
		{
			// Give the player the power to drain life from opponents when he damages them.
			Owner.player.cheats |= CF_DRAIN;
		}
	}

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

		// Nothing to do if there's no owner.
		if (Owner!= null && Owner.player != null)
		{
			// Take away the drain power.
			Owner.player.cheats &= ~CF_DRAIN;
		}
	}
	
}

//===========================================================================
//
// Regeneration
//
//===========================================================================

class PowerRegeneration : Powerup
{
	Default
	{
		Powerup.Duration -120;
		Powerup.Strength 5;
	}
	
	override void DoEffect()
	{
		Super.DoEffect();
		if (Owner != null && Owner.health > 0 && (level.time & 31) == 0)
		{
			if (Owner.GiveBody(int(Strength)))
			{
				Owner.A_PlaySound("*regenerate", CHAN_ITEM);
			}
		}
	}
}

//===========================================================================
//
// HighJump
//
//===========================================================================

class PowerHighJump : Powerup
{
	override void InitEffect()
	{
		Super.InitEffect();

		if (Owner!= null && Owner.player != null)
		{
			// Give the player the power to jump much higher.
			Owner.player.cheats |= CF_HIGHJUMP;
		}
	}

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

		// Nothing to do if there's no owner.
		if (Owner!= null && Owner.player != null)
		{
			// Take away the high jump power.
			Owner.player.cheats &= ~CF_HIGHJUMP;
		}
	}
}

//===========================================================================
//
// DoubleFiringSpeed
//
//===========================================================================

class PowerDoubleFiringSpeed : Powerup
{
	override void InitEffect()
	{
		Super.InitEffect();

		if (Owner!= null && Owner.player != null)
		{
			// Give the player the power to shoot twice as fast.
			Owner.player.cheats |= CF_DOUBLEFIRINGSPEED;
		}
	}

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

		// Nothing to do if there's no owner.
		if (Owner!= null && Owner.player != null)
		{
			// Take away the shooting twice as fast power.
			Owner.player.cheats &= ~CF_DOUBLEFIRINGSPEED;
		}
	}
}

//===========================================================================
//
// InfiniteAmmo
//
//===========================================================================

class PowerInfiniteAmmo : Powerup
{
	Default
	{
		Powerup.Duration -30;
	}
	
	override void InitEffect()
	{
		Super.InitEffect();

		if (Owner!= null && Owner.player != null)
		{
			// Give the player infinite ammo
			Owner.player.cheats |= CF_INFINITEAMMO;
		}
	}

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

		// Nothing to do if there's no owner.
		if (Owner!= null && Owner.player != null)
		{
			// Take away the limitless ammo
			Owner.player.cheats &= ~CF_INFINITEAMMO;
		}
	}
}

//===========================================================================
//
// PowerMorph
//
//===========================================================================

class PowerMorph : Powerup native
{
	native Class<PlayerPawn> PlayerClass;
	native Class<Actor> MorphFlash, UnMorphFlash;
	native int MorphStyle;
	native PlayerInfo MorphedPlayer;

	Default
	{
		Powerup.Duration -40;
	}
	
	//===========================================================================
	//
	// InitEffect
	//
	//===========================================================================

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

		if (Owner != null && Owner.player != null && PlayerClass != null)
		{
			let realplayer = Owner.player;	// Remember the identity of the player
			if (realplayer.MorphPlayer(realplayer, PlayerClass, 0x7fffffff/*INDEFINITELY*/, MorphStyle, MorphFlash, UnMorphFlash))
			{
				Owner = realplayer.mo;				// Replace the new owner in our owner; safe because we are not attached to anything yet
				bCreateCopyMoved = true;			// Let the caller know the "real" owner has changed (to the morphed actor)
				MorphedPlayer = realplayer;			// Store the player identity (morphing clears the unmorphed actor's "player" field)
			}
			else // morph failed - give the caller an opportunity to fail the pickup completely
			{
				bInitEffectFailed = true;			// Let the caller know that the activation failed (can fail the pickup if appropriate)
			}
		}
	}

	//===========================================================================
	//
	// EndEffect
	//
	//===========================================================================

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

		// Abort if owner already destroyed or unmorphed
		if (Owner == null || MorphedPlayer == null || Owner.alternative == null)
		{
			return;
		}
		
		// Abort if owner is dead; their Die() method will
		// take care of any required unmorphing on death.
		if (MorphedPlayer.health <= 0)
		{
			return;
		}

		int savedMorphTics = MorphedPlayer.morphTics;
		MorphedPlayer.UndoPlayerMorph (MorphedPlayer, 0, !!(MorphedPlayer.MorphStyle & MRF_UNDOALWAYS));

		// Abort if unmorph failed; in that case,
		// set the usual retry timer and return.
		if (MorphedPlayer != null && MorphedPlayer.morphTics)
		{
			// Transfer retry timeout
			// to the powerup's timer.
			EffectTics = MorphedPlayer.morphTics;
			// Reload negative morph tics;
			// use actual value; it may
			// be in use for animation.
			MorphedPlayer.morphTics = savedMorphTics;
			// Try again some time later
			return;
		}
		// Unmorph suceeded
		MorphedPlayer = null;
	}

	
}

