Class UPlayer : UTPlayer
{
	Default
	{
		Player.StartItem "Automag";
		Player.StartItem "DispersionPistol";
		Player.StartItem "UMiniAmmo", 30;
		Player.StartItem "DefaultAmmo", 50;
	}

	override void GiveDefaultInventory()
	{
		if ( !player ) return;
		// HexenArmor must always be the first item in the inventory because
		// it provides player class based protection that should not affect
		// any other protection item.
		let myclass = GetClass();
		GiveInventoryType('HexenArmor');
		let harmor = HexenArmor(FindInventory('HexenArmor'));
		harmor.Slots[4] = self.HexenArmor[0];
		for ( int i=0; i<4; ++i ) harmor.SlotsIncrement[i] = self.HexenArmor[i+1];
		// BasicArmor must come right after that. It should not affect any
		// other protection item as well but needs to process the damage
		// before the HexenArmor does.
		GiveInventoryType('BasicArmor');
		// Now add the items from the DECORATE definition
		let di = GetDropItems();
		for ( DropItem di=GetDropItems(); di; di=di.Next )
		{
			Class<Actor> ti = di.Name;
			if ( !ti ) continue;
			// no pistol start
			if ( sting_nopstart && ((ti is 'Automag') || (ti is 'UMiniAmmo')) ) continue;
			let tinv = (class<Inventory>)(ti);
			if ( !tinv )
			{
				Console.Printf(TEXTCOLOR_ORANGE.."%s is not an inventory item and cannot be given to a player as start item.\n",di.Name);
				continue;
			}
			let item = FindInventory(tinv);
			if ( item ) item.Amount = clamp(item.Amount+(di.Amount?di.Amount:item.default.Amount),0,item.MaxAmount);
			else
			{
				item = Inventory(Spawn(ti));
				item.bIgnoreSkill = true;	// no skill multipliers here
				item.Amount = di.Amount;
				let weap = Weapon(item);
				if ( weap )
				{
					// To allow better control any weapon is emptied of
					// ammo before being given to the player.
					weap.AmmoGive1 = weap.AmmoGive2 = 0;
				}
				bool res;
				Actor check;
				[res,check] = item.CallTryPickup(self);
				if ( !res )
				{
					item.Destroy();
					item = null;
				}
				else if ( check != self )
				{
					// Player was morphed. This is illegal at game start.
					// This problem is only detectable when it's too late to do something about it...
					ThrowAbortException("Cannot give morph item '%s' when starting a game!",di.Name);
				}
			}
			let weap = Weapon(item);
			if ( weap && weap.CheckAmmo(Weapon.EitherFire,false) )
				player.ReadyWeapon = player.PendingWeapon = weap;
		}
	}

	// Have to modify the give cheat to handle UT armor
	override void CheatGive( String name, int amount )
	{
		if ( PlayerNumber() != consoleplayer )
			A_Log(String.Format("%s is a cheater: give %s\n",player.GetUserName(),name));
		if ( !player.mo || (player.health <= 0) ) return;
		int giveall = ALL_NO;
		if ( name ~== "all" ) giveall = ALL_YES;
		else if (name ~== "everything") giveall = ALL_YESYES;
		if ( name ~== "health" )
		{
			if ( amount > 0 )
			{
				health += amount;
				player.health = health;
			}
			else player.health = health = GetMaxHealth(true);
		}
		if ( giveall || (name ~== "backpack") )
		{
			// Select the correct type of backpack based on the game
			let type = (class<Inventory>)(gameinfo.backpacktype);
			if ( type ) GiveInventory(type,1,true);
			if ( !giveall ) return;
		}
		if ( giveall || (name ~== "ammo") )
		{
			// Find every unique type of ammo. Give it to the player if
			// he doesn't have it already, and set each to its maximum.
			for ( int i=0; i<AllActorClasses.Size(); i++ )
			{
				let type = (class<Ammo>)(AllActorClasses[i]);
				if ( !type || (type.GetParentClass() != "Ammo") )
					continue;
				// Only give if it's for a valid weapon, unless using "give everything"
				bool isvalid = false;
				for ( int j=0; j<AllActorClasses.Size(); j++ )
				{
					let type2 = (class<Weapon>)(AllActorClasses[j]);
					if ( !type2 ) continue;
					let rep = GetReplacement(type2);
					if ( (rep != type2) && !(rep is "DehackedPickup") ) continue;
					readonly<Weapon> weap = GetDefaultByType(type2);
					if ( !player.weapons.LocateWeapon(type2) || (weap.bCheatNotWeapon && (giveall != ALL_YESYES)) ) continue;
					if ( (weap.AmmoType1 == type) || (weap.AmmoType2 == type) )
					{
						isvalid = true;
						break;
					}
				}
				if ( !isvalid ) continue;
				let ammoitem = FindInventory(type);
				if ( !ammoitem )
				{
					ammoitem = Inventory(Spawn(type));
					ammoitem.AttachToOwner(self);
					ammoitem.Amount = ammoitem.MaxAmount;
				}
				else if ( ammoitem.Amount < ammoitem.MaxAmount )
					ammoitem.Amount = ammoitem.MaxAmount;
			}
			if ( !giveall ) return;
		}
		if ( giveall || (name ~== "armor") )
		{
			// Doomreal gives the player all subclasses of UnrealArmor
			for ( int i=0; i<AllActorClasses.Size(); i++ )
			{
				if ( !(AllActorClasses[i].GetParentClass() is "UnrealArmor") ) continue;
				let item = Inventory(Spawn(AllActorClasses[i]));
				item.ClearCounters();	// don't increase item counts
				item.Amount = item.MaxAmount;
				if ( !item.CallTryPickup(self) ) item.Destroy();
			}
			if ( !giveall ) return;
		}
		if ( giveall || (name ~== "keys") )
		{
			for ( int i=0; i<AllActorClasses.Size(); i++ )
			{
				if ( !(AllActorClasses[i] is "Key") ) continue;
				let keyitem = GetDefaultByType(AllActorClasses[i]);
				if ( keyitem.special1 )
				{
					let item = Inventory(Spawn(AllActorClasses[i]));
					if ( !item.CallTryPickup(self) ) item.Destroy();
				}
			}
			if ( !giveall ) return;
		}
		if ( giveall || (name ~== "weapons") )
		{
			let savedpending = player.PendingWeapon;
			for ( int i=0; i<AllActorClasses.Size(); i++ )
			{
				let type = (class<Weapon>)(AllActorClasses[i]);
				if ( !type || (type == "Weapon") ) continue;
				// Don't give replaced weapons unless the replacement was done by Dehacked.
				let rep = GetReplacement(type);
				if ( (rep == type) || (rep is "DehackedPickup") )
				{
					// Give the weapon only if it is set in a weapon slot.
					if ( !player.weapons.LocateWeapon(type) ) continue;
					readonly<Weapon> def = GetDefaultByType(type);
					if ( (giveall == ALL_YESYES) || !def.bCheatNotWeapon )
					{
						GiveInventory(type,1,true);
						if ( (type is 'Automag') && sting_automags )
						{
							// force akimbo
							let t = FindInventory(type);
							if ( t )
							{
								t.Amount = 2;
								t.SetTag(StringTable.Localize("$T_AUTOMAG2"));
							}
						}
						else if ( (type is 'Betamag') && sting_protomags )
						{
							// force akimbo
							let t = FindInventory(type);
							if ( t )
							{
								t.Amount = 2;
								t.SetTag(StringTable.Localize("$T_PROTOMAG2"));
							}
						}
						else if ( type is 'DispersionPistol' )
						{
							// force upgrade
							let dsp = DispersionPistol(FindInventory('DispersionPistol'));
							if ( dsp )
							{
								dsp.Ammo1.Amount = dsp.Ammo1.MaxAmount = 90;
								if ( (player.ReadyWeapon == dsp) && (player.PendingWeapon == WP_NOCHANGE) )
									dsp.pendingupgrade = 4;
								else
								{
									dsp.pendingupgrade = dsp.upgradelevel = 4;
									dsp.MainUse = 6;
									dsp.UpdateSelectionOrder();
								}
							}
						}
					}
				}
			}
			player.PendingWeapon = savedpending;
			if ( !giveall ) return;
		}
		if ( giveall || (name ~== "artifacts") )
		{
			for ( int i=0; i<AllActorClasses.Size(); i++ )
			{
				let type = (class<Inventory>)(AllActorClasses[i]);
				if ( !type ) continue;
				let def = GetDefaultByType (type);
				if ( def.Icon.isValid() && ((def.MaxAmount > 1) || (type is 'UnrealInventory')) &&
					!(type is "PuzzleItem") && !(type is "Powerup") && !(type is "Ammo") &&	!(type is "Armor") )
				{
					// Do not give replaced items unless using "give everything"
					if ( (giveall == ALL_YESYES) || (GetReplacement(type) == type) )
						GiveInventory(type,(amount<=0)?def.MaxAmount:amount,true);
				}
			}
			if ( !giveall ) return;
		}
		if ( giveall || (name ~== "puzzlepieces") )
		{
			for ( int i=0; i<AllActorClasses.Size(); i++ )
			{
				let type = (class<PuzzleItem>)(AllActorClasses[i]);
				if ( !type ) continue;
				let def = GetDefaultByType(type);
				if ( !def.Icon.isValid() ) continue;
				// Do not give replaced items unless using "give everything"
				if ( (giveall == ALL_YESYES) || (GetReplacement(type) == type) )
					GiveInventory(type,(amount<=0)?def.MaxAmount:amount,true);
			}
			if ( !giveall ) return;
		}
		if ( giveall ) return;
		let type = name;
		if ( !type )
		{
			if ( PlayerNumber() == consoleplayer )
				A_Log(String.Format("Unknown item \"%s\"\n",name));
		}
		else GiveInventory(type,amount,true);
	}

	override void PlayAttacking()
	{
		if ( player.Health <= 0 ) return;
		// no animation if crouched
		if ( (player && (player.mo == self)) && (player.crouchdir == -1) ) return;
		// check weapon type
		if ( ((player.ReadyWeapon is 'URifle') && !sting_rifle && (player.buttons&BT_ALTATTACK)) || ((player.ReadyWeapon is 'Peacemaker') && player.buttons&BT_ALTATTACK) )
			return;
		let psp = player.FindPSprite(PSP_WEAPON);
		if ( (player.ReadyWeapon is 'Eightball')
			|| ((player.ReadyWeapon is 'DispersionPistol') && DispersionPistol(player.ReadyWeapon).bCharging)
			|| ((player.ReadyWeapon is 'UBioRifle') && UBioRifle(player.ReadyWeapon).bCharging)
			|| ((player.ReadyWeapon is 'FlameGun') && FlameGun(player.ReadyWeapon).bCharging)
			|| (player.ReadyWeapon is 'Razorjack') || (player.ReadyWeapon is 'Stunner')
			|| ((player.ReadyWeapon is 'UFlamethrower') && UFlamethrower(player.ReadyWeapon).bCharging) )
		{
			if ( !InStateSequence(CurState,FindState("MissileRepStill")) )
				SetStateLabel("MissileRepStill");
		}
		else if ( ((player.ReadyWeapon is 'Stinger') && psp
			&& psp.CurState.InStateSequence(player.ReadyWeapon.FindState("Hold")))
			|| (player.ReadyWeapon is 'UFlamethrower') || (player.ReadyWeapon is 'UMinigun')
			|| ((player.ReadyWeapon is 'Bonesaw') && player.buttons&BT_ATTACK)
			|| ((player.ReadyWeapon is 'Impaler') && player.buttons&BT_ALTATTACK) )
		{
			if ( !InStateSequence(CurState,FindState("MissileRep")) )
				SetStateLabel("MissileRep");
		}
		else SetStateLabel("Missile");
	}

	override void PlayFootstep( double vol )
	{
		let boot = UJumpBoots(FindInventory("UJumpBoots"));
		if ( boot ) A_PlaySound("u1/bootfootstep",CHAN_5,min(1.,vol*2));
		else A_PlaySound("ut/playerfootstep",CHAN_5,vol);
	}
}

Class UFemaleArmGibber : UTGibber
{
	bool firstgib;

	override void BurstGibs()
	{
		Actor a;
		double ang, pt;
		Vector3 dir;
		if ( !firstgib )
		{
			firstgib = true;
			Actor a = Spawn("UTFemaleArm",Vec3Offset(-14*sin(angle),14*cos(angle),36));
			ang = FRandom[Blod](0,360);
			pt = FRandom[Blod](-90,90);
			dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
			a.vel = rvel*0.6+dir*FRandom[Blod](3.0,6.0);
		}
		for ( int i=0; i<gibsize; i++ )
		{
			Vector3 box = (FRandom[Blod](-4,4),FRandom[Blod](12,18),FRandom[Blod](32,40));
			let a = Spawn("UTBloodPuff",Vec3Offset(box.x*cos(angle)-box.y*sin(angle),box.x*sin(angle)+box.y*cos(angle),box.z));
			ang = FRandom[Blod](0,360);
			pt = FRandom[Blod](-90,90);
			dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
			a.vel = rvel*0.2+dir*FRandom[Blod](1.5,3.0);
		}
		A_CountDown();
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		gibsize = 3;
		reactiontime = 5;
	}
	States
	{
	Spawn:
		TNT1 A 0 NoDelay;	// no sound, done by player
		TNT1 A 1 BurstGibs();
		Wait;
	}
}

Class UPlayerFemale : UPlayer
{
	Default
	{
		Player.SoundClass "ufemale";
		UTPlayer.VoiceType VOICE_FemaleOne;
		UTPlayer.DollType DOLL_Female;
	}

	void A_ArmPop()
	{
		armlessL = true;
		let a = Actor.Spawn("UFemaleArmGibber",pos);
		a.vel = vel;
		a.Scale = Scale;
		a.A_SetSize(radius,height);
		UTGibber(a).Gibbed = self;
	}

	States
	{
	Reload:
		#### # 3;
		PLYR ABCDEF 4;
		Goto Spawn;
	Death.Zapped:
		#### # 0;
		Goto Death;
	Death.Decapitated:
		#### # 0 A_HeadPop();
		PLD6 A 3 A_PlayerScream();
		PLD6 B 3 A_NoBlocking();
		PLD6 CDEFGHIJ 3;
		PLD6 K 1 A_DMFade();
		Wait;
	Death:
		#### # 0 A_JumpIf(Health<(GetGibHealth()/3),"Death7");
		#### # 0 A_JumpIf(vel.length()>20,"Death2");
		#### # 0 A_JumpIf(vel.length()>10,"Death5");
		#### # 0 A_Jump(256,"Death1","Death3","Death4");
	Death1:
		#### # 3;
		PLD1 A 3 A_PlayerScream();
		PLD1 B 3 A_NoBlocking();
		PLD1 CDEFGHIJKLMNOPQRSTU 3;
		PLD1 V 1 A_DMFade();
		Wait;
	Death2:
		#### # 3;
		PLD2 A 3 A_PlayerScream();
		PLD2 B 3 A_NoBlocking();
		PLD2 CDEFGHIJKLMNOPQ 3;
		PLD2 R 1 A_DMFade();
		Wait;
	Death3:
		#### # 3;
		PLD3 A 3 A_PlayerScream();
		PLD3 B 3 A_NoBlocking();
		PLD3 CDEFGHIJKLMNO 3;
		PLD3 P 1 A_DMFade();
		Wait;
	Death4:
		#### # 3;
		PLD4 A 3 A_PlayerScream();
		PLD4 B 3 A_NoBlocking();
		PLD4 CDEFGHIJKL 3;
		PLD4 M 1 A_DMFade();
		Wait;
	Death5:
		#### # 3;
		PLD5 A 3 A_PlayerScream();
		PLD5 B 3 A_NoBlocking();
		PLD5 CDEFGHIJKLMNO 3;
		PLD5 P 1 A_DMFade();
		Wait;
	Death7:
		#### # 0 A_ArmPop();
		PLD7 A 3 A_PlayerScream();
		PLD7 B 3 A_NoBlocking();
		PLD7 CDEFGHIJKLMNOPQRSTUV 3;
		PLD7 W 1 A_DMFade();
		Wait;
	Taunt1:
		#### # 5;
		PLT1 ABCDEFGHIJKLMNO 3;
		Goto Spawn+1;
	Taunt2:
		#### # 5;
		PLT2 ABCDEFGHIJKLMNOPQR 3;
		Goto Spawn+1;
	Taunt3:
		#### # 5;
		PLT3 ABCDEFGHIJKLMNO 3;
		Goto Spawn+1;
	Taunt4:
		#### # 0;
		Goto Spawn;
	}
}

Class UPlayerFemale1 : UPlayerFemale
{
	Default
	{
		Player.DisplayName "$N_FEMALE1";
		UTPlayer.VoiceType VOICE_FemaleTwo;
		-NOMENU;
	}
	States
	{
	See:
		GINA A -1;
		Stop;
	}
}
Class UPlayerFemale2 : UPlayerFemale
{
	Default
	{
		Player.SoundClass "ufemale";
		Player.DisplayName "$N_FEMALE2";
		-NOMENU;
	}
	States
	{
	See:
		SONY A -1;
		Stop;
	}
}

Class UMaleTorsoGibber : UTGibber
{
	bool firstgib;

	override void BurstGibs()
	{
		static const class<Actor> parts[] = {"UTMaleArm","UTMaleArm","UTMaleTorso","UTHeadMale","UTHeart","UTLiver","UTStomach"};
		static const double partofsy[] = {14,-14,0,0,-2,5,-3};
		static const double partofsz[] = {36,36,32,48,40,32,35};
		Actor a;
		double ang, pt;
		Vector3 dir;
		if ( !firstgib )
		{
			firstgib = true;
			for ( int i=0; i<7; i++ )
			{
				Actor a = Spawn(parts[i],Vec3Offset(-partofsy[i]*sin(angle),partofsy[i]*cos(angle),partofsz[i]));
				ang = FRandom[Blod](0,360);
				pt = FRandom[Blod](-90,90);
				dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
				a.vel = rvel*0.6+dir*FRandom[Blod](3.0,6.0);
			}
		}
		for ( int i=0; i<gibsize; i++ )
		{
			Vector3 box = (FRandom[Blod](-15,15),FRandom[Blod](15,15),FRandom[Blod](30,50));
			let a = Spawn("UTBloodPuff",Vec3Offset(box.x*cos(angle)-box.y*sin(angle),box.x*sin(angle)+box.y*cos(angle),box.z));
			ang = FRandom[Blod](0,360);
			pt = FRandom[Blod](-90,90);
			dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
			a.vel = rvel*0.2+dir*FRandom[Blod](1.5,3.0);
		}
		A_CountDown();
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		gibsize = 3;
		reactiontime = 5;
	}
	States
	{
	Spawn:
		TNT1 A 0 NoDelay;	// no sound, done by player
		TNT1 A 1 BurstGibs();
		Wait;
	}
}

Class UPlayerMale : UPlayer
{
	Default
	{
		Player.SoundClass "umale1";
		UTPlayer.VoiceType VOICE_MaleOne;
	}

	void A_TorsoPop()
	{
		headless = true;
		armlessL = true;
		armlessR = true;
		torsoless = true;
		let a = Actor.Spawn("UMaleTorsoGibber",pos);
		a.vel = vel;
		a.Scale = Scale;
		a.A_SetSize(radius,height);
		UTGibber(a).Gibbed = self;
	}

	States
	{
	Death:
		#### # 0 A_JumpIf(Health<(GetGibHealth()/3),"Death5");
		#### # 0 A_JumpIf(vel.length()>20,"Death6");
		#### # 0 A_JumpIf(vel.length()>10,"Death1");
		#### # 0 A_Jump(256,"Death2","Death3","Death7");
	Death5:
		#### # 0 A_TorsoPop();
		PLD5 A 3 A_PlayerScream();
		PLD5 B 3 A_NoBlocking();
		PLD5 CDEFGHIJKLMNOPQRSTUV 3;
		PLD5 W 1 A_DMFade();
		Wait;
	Death6:
		#### # 3;
		PLD6 A 3 A_PlayerScream();
		PLD6 B 3 A_NoBlocking();
		PLD6 CDEFGHIJKLMNOPQRSTUVWXYZ[ 3;
		PLD6 \ 1 A_DMFade();
		Wait;
	Taunt1:
		#### # 5;
		PLT1 ABCDEFG 6;
		Goto Spawn+1;
	Taunt2:
		#### # 5;
		PLT2 ABCDEFGHIJKLMNOPQRSTUVWXY 3;
		Goto Spawn+1;
	Taunt3:
		#### # 5;
		PLT3 ABCDEFGHIJKLMNO 3;
		Goto Spawn+1;
	Taunt4:
		#### # 0;
		Goto Spawn;
	}
}
Class UPlayerMale1 : UPlayerMale
{
	override void PlayFootstep( double vol )
	{
		let boot = UJumpBoots(FindInventory("UJumpBoots"));
		if ( boot ) A_PlaySound("u1/bootfootstep",CHAN_5,min(1.,vol*2));
		else
		{
			double ang = level.time/(20*TICRATE/35.)*360.;
			if ( sin(ang) > 0 ) A_PlaySound("u1/metalfootstep",CHAN_5,min(1.,vol*2));
			else A_PlaySound("ut/playerfootstep",CHAN_5,vol);
		}
	}
	Default
	{
		Player.DisplayName "$N_MALE1";
		-NOMENU;
	}
	States
	{
	See:
		KURG A -1;
		Stop;
	}
}
Class UPlayerMale2 : UPlayerMale
{
	Default
	{
		Player.SoundClass "umale2";
		Player.DisplayName "$N_MALE2";
		-NOMENU;
	}
	States
	{
	See:
		ASH_ A -1;
		Stop;
	}
}
Class UPlayerMale3 : UPlayerMale
{
	Default
	{
		Player.SoundClass "umale3";
		Player.DisplayName "$N_MALE3";
		UTPlayer.VoiceType VOICE_MaleTwo;
		-NOMENU;
	}
	States
	{
	See:
		DANT A -1;
		Stop;
	}
}

Class UnrealInventory : Inventory
{
	bool bActive;	// is currently activated
	int Charge;	// for timed items
	int DefaultCharge;
	private int UItemFlags;

	Property Charge : DefaultCharge;

	FlagDef DrawSpecial : UItemFlags, 0;	// hud draws special1 as amount
	FlagDef UnlimitedCopies : UItemFlags, 1;	// can pick up unlimited copies

	// Drawstuffs under HUD
	virtual ui void PreRender( double lbottom ) {}
	// Drawstuffs over HUD
	virtual ui void PostRender( double lbottom ) {}

	virtual bool DrainCharge( int val )
	{
		Charge -= val;
		if ( Charge > 0 ) return false;
		if ( Amount >= 1 )
		{
			Amount--;
			Charge = DefaultCharge;
			bActive = false;
			return true;
		}
		return true;
	}
	override void AttachToOwner( Actor other )
	{
		Super.AttachToOwner(other);
		if ( !Charge ) Charge = DefaultCharge;
		// it's annoying to set this per-subclass
		InterHubAmount = bUNLIMITEDCOPIES?int.max:MaxAmount;
	}
	override bool HandlePickup( Inventory item )
	{
		if ( (item.GetClass() == GetClass()) && ((MaxAmount > 1) || (DefaultCharge > 0)) )
		{
			if ( MaxAmount > 1 )
			{
				if ( UnrealInventory(item).Charge ) // redistribute charge among copies
				{
					int addcharge = Charge+UnrealInventory(item).Charge;
					charge = min(DefaultCharge,addcharge);
					// if there's charge to spare, increase amount
					if ( addcharge > charge )
					{
						if ( (Amount > 0) && (Amount+item.Amount < 0) ) Amount = int.max;
						Amount = min(bUNLIMITEDCOPIES?int.max:MaxAmount,Amount+item.Amount);
						charge = addcharge-charge;
					}
				}
				else
				{
					if ( (Amount > 0) && (Amount+item.Amount < 0) ) Amount = int.max;
					Amount = min(bUNLIMITEDCOPIES?int.max:MaxAmount,Amount+item.Amount);	// fully charged new copy, just increase
				}
			}
			else Charge = DefaultCharge;	// reset charge
			item.bPickupGood = true;
			return true;
		}
		if ( item.GetClass() == GetClass() )
		{
			if ( (Amount < MaxAmount) || bUNLIMITEDCOPIES || (sv_unlimited_pickup && !item.ShouldStay()) )
			{
				if ( (Amount > 0) && (Amount+item.Amount < 0) )
					Amount = int.max;
				else Amount += item.Amount;
				if ( Amount > MaxAmount && !sv_unlimited_pickup && !bUNLIMITEDCOPIES )
					Amount = MaxAmount;
				item.bPickupGood = true;
			}
			return true;
		}
		return false;
	}
	override void Tick()
	{
		Super.Tick();
		// don't slide on floor when dropped
		if ( bDROPPED && (pos.z <= floorz) )
			vel.xy *= 0;
	}
	override void DetachFromOwner()
	{
		// deactivate
		bActive = false;
		Super.DetachFromOwner();
	}
	override void OwnerDied()
	{
		// deactivate
		bActive = false;
		Super.OwnerDied();
	}
	override void OnDrop( Actor dropper )
	{
		Super.OnDrop(dropper);
		// drop like weapons
		Vector2 hofs = RotateVector((dropper.radius,0),dropper.angle);
		SetOrigin(dropper.Vec3Offset(hofs.x,hofs.y,dropper.height*0.5),false);
		Vector3 x, y, z;
		[x, y, z] = dt_CoordUtil.GetAxes(dropper.pitch,dropper.angle,dropper.roll);
		vel = x*12.0;
		vel.z += 4.0;
		angle = dropper.angle;
		pitch = 0;
		roll = 0;
	}

	Default
	{
		+INVENTORY.INVBAR;
		UnrealInventory.Charge 0;
		Inventory.PickupSound "misc/p_pkup";
	}
}

Class UTeleportFog : Actor
{
	Default
	{
		+NOBLOCKMAP;
		+NOTELEPORT;
		+NOGRAVITY;
		RenderStyle "Add";
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		Spawn("UTTeleportLight",Vec3Offset(0,0,16));
		A_PlaySound("misc/teleport",volume:.5);
		Spawn("UTeleportParticles",Vec3Offset(0,0,16));
	}
	States
	{
	Spawn:
		TNT1 A 1;
		Stop;
	}
}

Class UTeleportParticles : UTParticleMesh
{
	Default
	{
		Tag "telepo;UTeleParticle";
		Args 21;
		ReactionTime 35;
		XScale 0.06;
		YScale 0.16;
	}
}
Class UTeleParticle : UTMeshParticle
{
	Default
	{
		Scale 0.2;
	}
	States
	{
	Spawn:
		TPNT A -1 Bright;
		Stop;
	}
}

Class UnrealWeapon : UTWeapon
{
	private int UWeaponFlags;

	FlagDef NoFirstGive : UWeaponFlags, 0;	// don't give ammo on first pickup (for weapons with a clip count)

	override void AttachToOwner (Actor other)
	{
		Inventory.AttachToOwner(other);
		Ammo1 = AddAmmo(Owner,AmmoType1,bNoFirstGive?0:AmmoGive1);
		Ammo2 = AddAmmo(Owner,AmmoType2,bNoFirstGive?0:AmmoGive2);
		SisterWeapon = AddWeapon(SisterWeaponType);
		if ( Owner.player )
		{
			if ( !Owner.player.GetNeverSwitch() && !bNo_Auto_Switch )
				Owner.player.PendingWeapon = self;
			if ( Owner.player.mo == players[consoleplayer].camera )
				StatusBar.ReceivedWeapon(self);
		}
		GivenAsMorphWeapon = false;
	}
	override void FireEffect()
	{
		Super.FireEffect();
		let invis = UInvisibility(Owner.FindInventory("UInvisibility"));
		if ( invis && invis.bActive ) invis.special1 = -1;
	}
	override void PlayUpSound( Actor origin )
	{
		origin.A_PlaySound(upsound,CHAN_WEAPON,Dampener.Active(origin)?.1:1.);
	}
	// For clips
	virtual clearscope int, int, bool, bool GetClipAmount() const
	{
		return -1, -1, false, false;
	}
}

Class UnrealStaticHandler : StaticEventHandler
{
	ui TextureID tex[8];
	ui int mtics, cur;
	ui String lastmusic;

	ui void StartMenu()
	{
		CVar protomenu = CVar.GetCVar('stinger_introtype',players[consoleplayer]);
		if ( !protomenu ) return;	// this can happen
		int proto = protomenu.GetInt();
		tex[0] = TexMan.CheckForTexture("graphics/UnLogo0.png",TexMan.Type_Any);
		tex[1] = TexMan.CheckForTexture("graphics/UnLogo1.png",TexMan.Type_Any);
		tex[2] = TexMan.CheckForTexture("graphics/UnLogo3.png",TexMan.Type_Any);
		tex[3] = TexMan.CheckForTexture("graphics/UnLogo2.png",TexMan.Type_Any);
		tex[4] = TexMan.CheckForTexture("graphics/UnBg.png",TexMan.Type_Any);
		tex[5] = TexMan.CheckForTexture("graphics/97Bg.png",TexMan.Type_Any);
		tex[6] = TexMan.CheckForTexture("graphics/96Bg.png",TexMan.Type_Any);
		tex[7] = TexMan.CheckForTexture("graphics/95Bg.png",TexMan.Type_Any);
		if ( proto > 2 ) S_ChangeMusic("Unreal");
		else if ( proto == 2 ) S_ChangeMusic("Isotox96");
		else if ( proto == 1 ) S_ChangeMusic("Unreal2");
		else S_ChangeMusic("FlyBy");
		cur = proto;
	}

	override void OnRegister()
	{
		// remove the UT static handler
		let hnd = UTStaticHandler(StaticEventHandler.Find("UTStaticHandler"));
		if ( hnd ) hnd.Destroy();
	}

	override void ConsoleProcess( ConsoleEvent e )
	{
		if ( gamestate != GS_TITLELEVEL ) return;
		if ( e.Name ~== "refreshmenu" ) StartMenu();
	}

	override void PostUiTick()
	{
		if ( gamestate != GS_TITLELEVEL ) return;
		if ( gametic <= 0 ) StartMenu();
		if ( musplaying.Name != lastmusic )
		{
			mtics = 0;
			lastmusic = musplaying.Name;
		}
		else mtics++;
	}

	override void RenderUnderlay( RenderEvent e )
	{
		if ( gamestate != GS_TITLELEVEL ) return;
		double ar = Screen.GetAspectRatio();
		Vector2 tsize = TexMan.GetScaledSize(tex[cur+4]);
		double sar = tsize.x/tsize.y;
		Vector2 vsize;
		if ( sar > ar ) vsize = (tsize.y*ar,tsize.y);
		else if ( sar < ar ) vsize = (tsize.x,tsize.x/ar);
		else vsize = tsize;
		Screen.DrawTexture(tex[cur+4],false,(vsize.x-tsize.x)/2,(vsize.y-tsize.y)/2,DTA_VirtualWidthF,vsize.x,DTA_VirtualHeightF,vsize.y,DTA_KeepRatio,true);
		Screen.Dim("Black",clamp(1.-((mtics+e.FracTic)/Thinker.TICRATE)*.2,0.,1.),0,0,Screen.GetWidth(),Screen.GetHeight());
		if ( menuactive ) return;
		tsize = TexMan.GetScaledSize(tex[cur]);
		sar = tsize.x/tsize.y;
		if ( sar > ar ) vsize = (tsize.x,tsize.x/ar);
		else if ( sar < ar ) vsize = (tsize.y*ar,tsize.y);
		else vsize = tsize;
		double alf = clamp(((mtics+e.FracTic)/Thinker.TICRATE)-8,0.,1.);
		Screen.DrawTexture(tex[cur],false,(vsize.x-tsize.x)/2,(vsize.y-tsize.y)/2,DTA_VirtualWidthF,vsize.x,DTA_VirtualHeightF,vsize.y,DTA_KeepRatio,true,DTA_Alpha,alf);
	}

	override void RenderOverlay( RenderEvent e )
	{
		// we have to stand in for the UT handler on this function
		// although it doesn't make much sense yet
		if ( players[consoleplayer].camera.player && players[consoleplayer].camera.player.ReadyWeapon && (players[consoleplayer].camera.player.ReadyWeapon is 'UTWeapon') )
			UTWeapon(players[consoleplayer].camera.player.ReadyWeapon).RenderOverlay(e);
	}
}

Class AmmoUsedInSlot
{
	Class<Ammo> AmmoType;
	bool UsedInSlot[10];
}

Class UnrealMainHandler : EventHandler
{
	Array<AmmoUsedInSlot> AmmoSlots;
	transient int slotflash[10];	// used by the 0.83 hud

	override void WorldThingDamaged( WorldEvent e )
	{
		// blow up stinger pumped actors when taking damage from other sources
		if ( e.DamageType == 'Stinger' ) return;
		let ti = ThinkerIterator.Create("TarydiumDebuff",Thinker.STAT_USER);
		TarydiumDebuff t;
		while ( t = TarydiumDebuff(ti.Next()) )
		{
			if ( (t.victim != e.Thing) || t.exploding ) continue;	// make sure to skip any debuffs that already blew up to prevent infinite recursion on chain reactions
			t.Amount += e.Damage;
			// stunner/asmd and others deal extra explosive charge
			if ( e.DamageType == 'jolted' ) t.Amount += 50+e.Damage;
			t.exploding = true;
			break;
		}
	}
	override void CheckReplacement( ReplaceEvent e )
	{
		if ( (e.Replacee == 'Chainsaw') || (e.Replacee == 'Gauntlets') )
		{
			if ( ((sting_proto && sting_dubious) || sting_olsmp) && !Random[Replacements](0,sting_proto?3:1) ) e.Replacement = 'WeaponPowerUp';
			else if ( sting_proto && Random[Replacements](0,2) )
			{
				if ( sting_dubious && Random[Replacements](0,1) ) e.Replacement = 'Bonesaw';
				else e.Replacement = 'Stunner';
			}
			else if ( sting_proto && !Random[Replacements](0,2) ) e.Replacement = 'Betamag';
			else e.Replacement = 'Automag';
		}
		else if ( (e.Replacee == 'Fist') || (e.Replacee == 'Staff') ) e.Replacement = 'DispersionPistol';
		else if ( (e.Replacee == 'Pistol') || (e.Replacee == 'GoldWand') )
		{
			if ( sting_proto && !Random[Replacements](0,2) ) e.Replacement = 'Betamag';
			else e.Replacement = 'Automag';
		}
		else if ( (e.Replacee == 'Shotgun') || (e.Replacee == 'SuperShotgun') || (e.Replacee == 'Crossbow') )
		{
			if ( !Random[Replacements](0,3) && (e.Replacee != 'SuperShotgun') )
			{
				if ( sting_proto && !Random[Replacements](0,2) ) e.Replacement = 'Betamag';
				else e.Replacement = 'Automag';
			}
			else switch( Random[Replacements](0,sting_proto?2:1) )
			{
			case 0:
				e.Replacement = 'Stinger';
				break;
			case 1:
				e.Replacement = 'ASMD';
				break;
			case 2:
				e.Replacement = 'QuadShot';
				break;
			}
		}
		else if ( (e.Replacee == 'Chaingun') || (e.Replacee == 'Blaster') )
		{
			if ( Random[Replacements](0,1) ) e.Replacement = 'UMinigun';
			else e.Replacement = 'Razorjack';
		}
		else if ( (e.Replacee == 'RocketLauncher') || (e.Replacee == 'PhoenixRod') )
		{
			if ( sting_proto && !Random[Replacements](0,3) )
			{
				if ( Random[Replacements](0,1) ) e.Replacement = 'UFlamethrower';
				else e.Replacement = 'FlameGun';
			}
			else if ( Random[Replacements](0,1) ) e.Replacement = 'UFlakCannon';
			else e.Replacement = 'Eightball';
		}
		else if ( (e.Replacee == 'PlasmaRifle') || (e.Replacee == 'SkullRod') )
		{
			if ( sting_proto && !Random[Replacements](0,2) ) e.Replacement = 'Impaler';
			else if ( Random[Replacements](0,1) ) e.Replacement = 'URifle';
			else e.Replacement = 'UBioRifle';
		}
		else if ( (e.Replacee == 'BFG9000') || (e.Replacee == 'Mace') )
		{
			if ( sting_olsmp && (!sting_proto || !sting_dubious || Random[Replacements](0,1)) ) e.Replacement = 'OLSMP';
			else if ( sting_proto && sting_dubious )
			{
				if ( Random[Replacements](0,1) ) e.Replacement = 'SMiniGun';
				else e.Replacement = 'BigGun';
			}
			else e.Replacement = 'WeaponPowerup';	// lousy, I know
		}
		else if ( (e.Replacee == 'Clip') || (e.Replacee == 'GoldWandAmmo') || (e.Replacee == 'GoldWandHefty') ) e.Replacement = 'UClip';
		else if ( (e.Replacee == 'ClipBox') )
		{
			if ( !Random[Replacements](0,2) ) e.Replacement = 'UClip';
			else if ( Random[Replacements](0,1) ) e.Replacement = 'UMiniAmmo';
			else e.Replacement = 'RazorAmmo';
		}
		else if ( (e.Replacee == 'BlasterAmmo') || (e.Replacee == 'BlasterHefty') )
		{
			if ( Random[Replacements](0,1) ) e.Replacement = 'UMiniAmmo';
			else e.Replacement = 'RazorAmmo';
		}
		else if ( (e.Replacee == 'Shell') || (e.Replacee == 'CrossbowAmmo') )
		{
			if ( !Random[Replacements](0,2) ) e.Replacement = 'UClip';
			else switch( Random[Replacements](0,sting_proto?2:1) )
			{
			case 0:
				e.Replacement = 'StingerAmmo';
				break;
			case 1:
				e.Replacement = 'ASMDAmmo2';
				break;
			case 2:
				e.Replacement = 'UShells2';
				break;
			}
		}
		else if ( (e.Replacee == 'ShellBox') || (e.Replacee == 'CrossbowHefty') )
		{
			switch( Random[Replacements](0,sting_proto?2:1) )
			{
			case 0:
				e.Replacement = 'StingerAmmo2';
				break;
			case 1:
				e.Replacement = 'ASMDAmmo';
				break;
			case 2:
				e.Replacement = 'UShells';
				break;
			}
		}
		else if ( (e.Replacee == 'RocketAmmo') || (e.Replacee == 'PhoenixRodAmmo') || (e.Replacee == 'MaceAmmo') )
		{
			if ( sting_proto && !Random[Replacements](0,4) ) e.Replacement = 'FlameAmmo';
			else if ( Random[Replacements](0,1) )
			{
				if ( !Random[Replacements](0,3) ) e.Replacement = 'UFlakBox';
				else e.Replacement = 'UFlakAmmo';
			}
			else
			{
				if ( !Random[Replacements](0,3) ) e.Replacement = 'URocketAmmo';
				else e.Replacement = 'URocketAmmo2';
			}
		}
		else if ( (e.Replacee == 'RocketBox') || (e.Replacee == 'PhoenixRodHefty') || (e.Replacee == 'MaceHefty') )
		{
			if ( sting_proto && !Random[Replacements](0,3) ) e.Replacement = 'FlameAmmo';
			else if ( Random[Replacements](0,1) ) e.Replacement = 'UFlakBox';
			else e.Replacement = 'URocketAmmo';
		}
		else if ( (e.Replacee == 'Cell') || (e.Replacee == 'SkullRodAmmo') )
		{
			if ( sting_proto && sting_dubious && !Random[Replacements](0,4) )
			{
				if ( !Random[Replacements](0,2) ) e.Replacement = 'BigAmmo2';
				else e.Replacement = 'BigAmmo3';
			}
			else if ( sting_proto && !Random[Replacements](0,3) )
			{
				if ( !Random[Replacements](0,3) ) e.Replacement = 'ImpalerAmmo';
				else e.Replacement = 'ImpalerAmmo2';
			}
			else if ( Random[Replacements](0,1) )
			{
				if ( !Random[Replacements](0,3) ) e.Replacement = 'UBioAmmo';
				else e.Replacement = 'UBioAmmo2';
			}
			else
			{
				if ( !Random[Replacements](0,3) ) e.Replacement = 'URifleAmmo';
				else e.Replacement = 'URifleAmmo2';
			}
		}
		else if ( (e.Replacee == 'CellPack') || (e.Replacee == 'SkullRodHefty') )
		{
			if ( sting_proto && sting_dubious && !Random[Replacements](0,4) )
			{
				if ( !Random[Replacements](0,2) ) e.Replacement = 'BigAmmo';
				else e.Replacement = 'BigAmmo2';
			}
			else if ( sting_proto && !Random[Replacements](0,3) ) e.Replacement = 'ImpalerAmmo';
			else if ( sting_olsmp && !Random[Replacements](0,2) ) e.Replacement = 'OLSMPAmmo';
			else if ( Random[Replacements](0,1) ) e.Replacement = 'UBioAmmo';
			else e.Replacement = 'URifleAmmo';
		}
		else if ( (e.Replacee == 'InvulnerabilitySphere') || (e.Replacee == 'ArtiInvulnerability') ) e.Replacement = 'PowerShield';
		else if ( (e.Replacee == 'Berserk') || (e.Replacee == 'ArtiTomeOfPower') )
		{
			if ( sting_msentry && !Random[Replacements](0,9) ) e.Replacement = 'SentryItem';
			else if ( ((sting_proto && sting_dubious) || sting_olsmp) && !Random[Replacements](0,2) ) e.Replacement = 'WeaponPowerUp';
			else e.Replacement = 'Amplifier';
		}
		else if ( e.Replacee == 'ArtiEgg' ) e.Replacement = 'VoiceBox';
		else if ( (e.Replacee == 'Soulsphere') || (e.Replacee == 'ArtiSuperHealth') ) e.Replacement = 'SuperHealth';
		else if ( e.Replacee == 'Megasphere' ) e.Replacement = 'ShieldBelt';
		else if ( (e.Replacee == 'Allmap') || (e.Replacee == 'SuperMap') )
		{
			if ( sting_proto && Random[Replacements](0,2) ) e.Replacement = 'MotionDetector';
			else e.Replacement = 'UAllMap';
		}
		else if ( (e.Replacee == 'BlurSphere') || (e.Replacee == 'ArtiInvisibility') ) e.Replacement = 'UInvisibility';
		else if ( (e.Replacee == 'Infrared') || (e.Replacee == 'ArtiTorch') ) e.Replacement = 'UFlashlight';
		else if ( e.Replacee == 'RadSuit' ) e.Replacement = 'UJumpBoots';
		else if ( e.Replacee == 'ArtiFly' ) e.Replacement = 'UJumpBoots';
		else if ( (e.Replacee == 'Backpack') || (e.Replacee == 'BagOfHolding') ) e.Replacement = 'UnrealBackpack';
		else if ( (e.Replacee == 'ArmorBonus') || (e.Replacee == 'ArtiTimeBomb') )
		{
			if ( sting_abonus && Random[Replacements](0,3) ) e.Replacement = 'UArmorBonus';
			else e.Replacement = 'Flare';
		}
		else if ( (e.Replacee == 'HealthBonus') || (e.Replacee == 'CrystalVial') ) e.Replacement = 'Bandages';
		else if ( (e.Replacee == 'GreenArmor') || (e.Replacee == 'Silvershield') ) e.Replacement = 'KevlarSuit';
		else if ( (e.Replacee == 'BlueArmor') || (e.Replacee == 'EnchantedShield') ) e.Replacement = 'UArmor';
		else if ( (e.Replacee == 'Stimpack') || (e.Replacee == 'ArtiHealth') )
		{
			if ( Random[Replacements](0,1) ) e.Replacement = 'UHealth';
			else if ( Random[Replacements](0,2) ) e.Replacement = 'NaliFruit';
			else e.Replacement = 'Seeds';
		}
		else if ( e.Replacee == 'Medikit' ) e.Replacement = 'UHealth';
		else if ( e.Replacee == 'ArtiTeleport' )
		{
			// I have no idea what to replace this with, so just have some random stuff
			switch( Random[Replacements](0,sting_proto?7:5) )
			{
			case 0:
				e.Replacement = 'UnrealBackpack';
				break;
			case 1:
				e.Replacement = 'ShieldBelt';
				break;
			case 2:
				e.Replacement = 'SuperHealth';
				break;
			case 3:
				e.Replacement = 'Amplifier';
				break;
			case 4:
				e.Replacement = 'WeaponPowerUp';
				break;
			case 5:
				e.Replacement = 'VoiceBox';
				break;
			case 6:
				e.Replacement = 'SentryGunItem';
				break;
			case 7:
				e.Replacement = 'Peacemaker';
				break;
			}
		}
		else if ( e.Replacee == 'TeleportFog' ) e.Replacement = 'UTeleportFog';
		// replace UT items (while this is mainly for the DT map pack, it also has the added effect of preventing the guns from being added by "give all")
		else if ( e.Replacee is 'ImpactHammer' ) e.Replacement = 'DispersionPistol';
		else if ( e.Replacee is 'Translocator' ) e.Replacement = 'UTranslocator';
		else if ( e.Replacee is 'UTChainsaw' ) e.Replacement = 'Slot1Weapons';
		else if ( e.Replacee is 'Enforcer' ) e.Replacement = 'Slot2Weapons';
		else if ( e.Replacee is 'BioRifle' ) e.Replacement = 'Slot3Weapons';
		else if ( e.Replacee is 'ShockRifle' ) e.Replacement = 'Slot4Weapons';
		else if ( e.Replacee is 'PulseGun' ) e.Replacement = 'Slot5Weapons';
		else if ( e.Replacee is 'Ripper2' ) e.Replacement = 'Slot6Weapons';
		else if ( e.Replacee is 'Minigun' ) e.Replacement = 'Slot7Weapons';
		else if ( e.Replacee is 'FlakCannon' ) e.Replacement = 'Slot8Ammo';
		else if ( e.Replacee is 'UTRocketLauncher' ) e.Replacement = 'Slot0Weapons';
		else if ( e.Replacee is 'SniperRifle' ) e.Replacement = 'Slot0Weapons';
		else if ( e.Replacee is 'WarheadLauncher' ) e.Replacement = 'Slot0SWeapons';
		else if ( e.Replacee is 'EnhancedShockRifle' ) e.Replacement = 'Amplifier';
		else if ( e.Replacee is 'ChainsawAmmo' ) e.Replacement = 'UNothing';
		else if ( e.Replacee is 'EClip' ) e.Replacement = 'Slot2Ammo';
		else if ( e.Replacee is 'BioAmmo' ) e.Replacement = 'Slot3Ammo';
		else if ( e.Replacee is 'ShockAmmo' ) e.Replacement = 'Slot4Ammo';
		else if ( e.Replacee is 'PulseAmmo' ) e.Replacement = 'Slot5Ammo';
		else if ( e.Replacee is 'RipperAmmo' ) e.Replacement = 'Slot6Ammo';
		else if ( e.Replacee is 'MiniAmmo' ) e.Replacement = 'Slot7Ammo';
		else if ( e.Replacee is 'FlakAmmo' ) e.Replacement = 'Slot8Ammo';
		else if ( e.Replacee is 'UTRocketAmmo' ) e.Replacement = 'Slot9Ammo';
		else if ( e.Replacee is 'RifleAmmo' ) e.Replacement = 'Slot0Ammo';
		else if ( e.Replacee is 'WarheadAmmo' ) e.Replacement = 'Slot0SAmmo';
		else if ( e.Replacee is 'EnhancedShockAmmo' ) e.Replacement = 'UNothing';
		else if ( e.Replacee is 'UTBackpack' ) e.Replacement = 'UnrealBackpack';
		else if ( e.Replacee is 'UDamage' ) e.Replacement = 'Amplifier';
		else if ( e.Replacee is 'UTInvisibility' ) e.Replacement = 'UInvisibility';
		else if ( e.Replacee is 'UTShieldBelt' ) e.Replacement = 'ShieldBelt';
		else if ( e.Replacee is 'UTBodyArmor' ) e.Replacement = 'UArmor';
		else if ( e.Replacee is 'UTThighPads' ) e.Replacement = 'KevlarSuit';
		else if ( e.Replacee is 'UTArmorBonus' )
		{
			if ( sting_abonus ) e.Replacement = 'UArmorBonus';
			else e.Replacement = 'Flare';
		}
		else if ( (e.Replacee is 'UTMedBox') || (e.Replacee is 'UTHealthBox') ) e.Replacement = 'UHealth';
		else if ( e.Replacee is 'UTHealthPack' ) e.Replacement = 'SuperHealth';
		else if ( e.Replacee is 'UTHealthBonus' ) e.Replacement = 'Bandages';
		else if ( e.Replacee is 'UTJumpBoots' ) e.Replacement = 'UJumpBoots';
		// we don't need these
		else if ( e.Replacee is 'UTActivatable' ) e.Replacement = 'UNothing';
		else if ( e.Replacee is 'UTActivatableHealth' ) e.Replacement = 'UNothing';
	}

	// translocator stuff
	override void PlayerEntered( PlayerEvent e )
	{
		if ( flak_translocator )
			players[e.playernumber].mo.TakeInventory("Translocator",1);
		if ( sting_proto && sting_telegun )
			players[e.playernumber].mo.GiveInventory("UTranslocator",1);
		if ( sting_proto && sting_flares )
		{
			players[e.playernumber].mo.GiveInventory("LightFlare",1);
			players[e.playernumber].mo.GiveInventory("DarkFlare",1);
		}
	}
	override void PlayerRespawned( PlayerEvent e )
	{
		PlayerEntered(e);
	}
	override void NetworkProcess( ConsoleEvent e )
	{
		if ( e.Name ~== "refreshtrans" )
		{
			if ( flak_translocator )
			{
				for ( int i=0; i<MAXPLAYERS; i++ ) if ( playeringame[i] )
				{
					players[i].mo.TakeInventory("Translocator",1);
				}
			}
			if ( sting_proto && sting_telegun )
			{
				for ( int i=0; i<MAXPLAYERS; i++ ) if ( playeringame[i] )
				{
					players[i].mo.GiveInventory("UTranslocator",1);
				}
			}
			if ( sting_proto && sting_flares )
			{
				for ( int i=0; i<MAXPLAYERS; i++ ) if ( playeringame[i] )
				{
					players[i].mo.GiveInventory("LightFlare",1);
					players[i].mo.GiveInventory("DarkFlare",1);
				}
			}
		}
		else if ( (e.Name ~== "usetranslator") && (e.player != -1) && playeringame[e.player] && !menuactive )
		{
			let t = players[e.player].mo.FindInventory("UTranslator");
			if ( t ) t.Use(false);
		}
		else if ( e.Name ~== "Bar083SlotFlash" )
			slotflash[e.Args[0]] = gametic+20;
	}
	private static bool CmpWeapon( Class<Weapon> a, Class <Weapon> b )
	{
		let defa = GetDefaultByType(a);
		let defb = GetDefaultByType(b);
		if ( defa.SlotPriority <= defb.SlotPriority ) return true;
		if ( defa.SlotNumber > defb.SlotNumber ) return true;
		return false;
	}
	override void WorldLoaded( WorldEvent e )
	{
		// More "authentic" Unreal flavor of these edits
		if ( (level.GetChecksum() ~== "959A613006CC3AA912C4A22908B7566A") || (level.GetChecksum() ~== "0EADB2F82732A968B8513E4DC6138439") )
		{
			S_ChangeMusic("Cyrene");
			TextureID sky95 = TexMan.CheckForTexture("Sky95",TexMan.Type_Any);
			level.ChangeSky(sky95,sky95);
			level.ReplaceTextures("rClfFlr0","C_flr19",0);
			level.ReplaceTextures("rClfBas0","C_wal19k",0);
			level.ReplaceTextures("uAlnWl2b","C_WAL19A",0);
			level.ReplaceTextures("xAlnWl2b","C_WAL19F",0);
		}
		// populate weapons array
		Array<Class<Weapon> > SortedWeapons;
		SortedWeapons.Clear();
		for ( int i=0; i<AllActorClasses.Size(); i++ )
		{
			let type = (class<Weapon>)(AllActorClasses[i]);
			if ( !type || (type == "Weapon") ) continue;
			let rep = Actor.GetReplacement(type);
			if ( (rep != type) && !(rep is "DehackedPickup") ) continue;
			readonly<Weapon> def = GetDefaultByType(type);
			int wslot = def.SlotNumber;
			if ( wslot == -1 ) continue;
			if ( !def.AmmoType1 ) continue;
			SortedWeapons.Push(type);
		}
		// sort weapons array
		for ( int i=0; i<SortedWeapons.Size(); i++ )
		{
			int j = 1;
			while ( j < SortedWeapons.Size() )
			{
				int k = j;
				while ( (k > 0) && CmpWeapon(SortedWeapons[k-1],SortedWeapons[k]) )
				{
					Class<Weapon> tmp = SortedWeapons[k];
					SortedWeapons[k] = SortedWeapons[k-1];
					SortedWeapons[k-1] = tmp;
					k--;
				}
				j++;
			}
		}
		// populate ammo-by-slot array
		AmmoSlots.Clear();
		for ( int i=0; i<SortedWeapons.Size(); i++ )
		{
			readonly<Weapon> def = GetDefaultByType(SortedWeapons[i]);
			int wslot = def.SlotNumber;
			int found = -1;
			for ( int j=0; j<AmmoSlots.Size(); j++ )
			{
				if ( AmmoSlots[j].AmmoType != def.AmmoType1 ) continue;
				found = j;
			}
			if ( found == -1 )
			{
				let asl = new("AmmoUsedInSlot");
				asl.AmmoType = def.AmmoType1;
				asl.UsedInSlot[wslot] = true;
				AmmoSlots.Push(asl);
			}
			else AmmoSlots[found].UsedInSlot[wslot] = true;
		}
	}
}
