class SquarePlayer : PlayerPawn
{
	int jumpBuffer; // how many tics the player can press the jump button before hitting the ground to trigger a second jump.
	int jumpGrace;	// how many tics the player can press jump _after_ falling off a ledge and still successfully jump.

	int jumpSoundTics;
	int jumpButtonTics;
	int jumpGraceTics;

	int prevJumpSoundTics;
	int prevJumpButtonTics;
	int prevJumpGraceTics;

	Property JumpBuffer:jumpBuffer;
	Property JumpGrace:jumpGrace;

	Default
	{
		Height 48;
		Radius 10;
		Speed 0.8;
		BloodType "SquareBlood";
		BloodColor "6f 00 6b";
		DamageFactor "Goo", 0.3;
		DamageFactor "Falling", 1.0;

		Player.StartItem "PaintGun";
		Player.StartItem "GoonadeWeapon";
		Player.ViewHeight 34;
		Player.JumpZ 9.38083152; // 64 mapunits
		Player.ColorRange 32, 47;
		Player.CrouchSprite "PLYC";
		Player.DisplayName "Square";
		Player.SoundClass "Square";
		Player.ColorSet 0, "Purple", 32, 47, 36;
		Player.ColorSet 1, "Blue", 64, 79, 68;
		Player.ColorSet 2, "Yellow", 240, 255, 243;
		Player.ColorSet 3, "Red", 192, 207, 198;
		Player.ColorSet 4, "Green", 112, 127, 116;
		Player.ColorSet 5, "Pink", 176, 191, 180;
		Player.ColorSet 6, "Cyan", 96, 111, 99;
		Player.ColorSet 7, "Brown", 224, 239, 227;
		Player.ColorSet 8, "Tan", 208, 223, 212;
		Player.ColorSet 9, "Olive", 128, 143, 131;
		Player.ColorSet 10, "Gray", 16, 31, 20;
		Player.ColorSet 11, "Black", 48, 63, 48;
		Player.ColorSet 12, "Maroon", 160, 175, 164;
		Player.ColorSet 13, "Beige", 80, 95, 85;
		Player.ColorSet 14, "Orange", 144, 159, 150;
		MaxStepHeight 16;

		SquarePlayer.JumpBuffer 3;
		SquarePlayer.JumpGrace 3;

		Player.WeaponSlot 1, "PaintGun", "PaintGuns";
		Player.WeaponSlot 2, "Oozi", "Oozis";
		Player.WeaponSlot 3, "Shotbow", "QuadCannon";
		Player.WeaponSlot 4, "PaintCannon";
		Player.WeaponSlot 5, "HellshellLauncher";
		Player.WeaponSlot 6, "Sceptre";
		Player.WeaponSlot 7, "Defibrillator";
	}

	States
	{
	Spawn:
		PLAY X 1 A_SetGravity(1.0);
	Loop;
	See:
		PLAY ABCD 4;
		Loop;
	Missile:
		PLAY F 8;
		Goto Spawn;
	Melee:
		PLAY G 8;
		Goto Missile;
	Pain:
		PLAY E 4;
		PLAY E 4 A_Pain;
		Goto Spawn;
	Death:
	XDeath:
		PLAY E 2 A_PlayerScream;
		PLAY E 2 A_SpawnItemEx("GibSpawnerPlayer",0,0,0,0,0,0,0,SXF_TRANSFERTRANSLATION);
		PLAY EE 0 A_SpawnItemEx("GibEye", random[SquareGibRandom](-32,32), random[SquareGibRandom](-4,4), random[SquareGibRandom](4,12), random[SquareGibRandom](-4,4), random[SquareGibRandom](-4,4), random[SquareGibRandom](0,8), 0, 1);
		PLAY E 0 A_SpawnItemEx("Legs");
		PLAY E 0 A_SetGravity(0.25);
		PLAY E 0 ThrustThingZ(0, 20, 0, 0);
		PLAY E 0 ThrustThing(random(0,255), random(1,5), 0, 0);
		TNT1 A -1;
		Stop;
	}

	override void CheckJump()
	{
		// Custom jump code! Huzzah!
		let player = self.player;
		if (player) {

			// Put some guards up so we don't change any values during
			// multiplayer client prediction and bork up the sync.
			bool notPredicting = !(player.cheats & CF_PREDICTING);
			if(notPredicting) {

				// if we're on the ground, reset the grace period.
				if(player.onground) {
					jumpGraceTics = jumpGrace;
				} else if(jumpGraceTics >= 0) {
					jumpGraceTics = prevJumpGraceTics - 1;
				}

				// sound replay countdown
				if(jumpSoundTics > 0) {
					jumpSoundTics = prevJumpSoundTics - 1;
				}
			}

			// now actually check for the jump.
			if (player.cmd.buttons & BT_JUMP) {

				// handle non-jump special cases first.
				if (player.crouchoffset != 0) { // Jumping while crouching will force an un-crouch but not jump
					player.crouching = 1;
				}
				else if (waterlevel >= 2) { // Swimmin'.
					Vel.Z = 4 * Speed;
				}
				else if (bNoGravity) { // Flyin'.
					Vel.Z = 3.;
				}

				// now handle jumping proper.
				else if (level.IsJumpingAllowed() && jumpGraceTics >= 0 && jumpButtonTics <= jumpBuffer) {
					double jumpvelz = JumpZ * 35 / TICRATE;
					double jumpfac = 0;

					// high jump powerup; pick the best factor from all active items.
					for (let p = Inv; p != null; p = p.Inv) {
						let pp = PowerHighJump(p);
						if (pp) {
							double f = pp.Strength;
							if (f > jumpfac) jumpfac = f;
						}
					}
					if (jumpfac > 0) jumpvelz *= jumpfac;

					// do it!
					Vel.Z = jumpvelz; // [XA] set it, don't add, else we'll fly straight up stairs. ;)
					bOnMobj = false;
					jumpGraceTics = 0;

					// limit how often the sound can play, in case we're jumping up stairs.
					if (notPredicting && jumpSoundTics == 0) {
						A_PlaySound("*jump2", CHAN_BODY);
						jumpSoundTics = 10;
					}
				}

				// keep track of how many tics we've held down the jump button.
				if (notPredicting) {
					jumpButtonTics = prevJumpButtonTics + 1;
				}
			} else if (notPredicting) {
				jumpButtonTics = 0;
			}
		}
	}

	// Keep track of the previous jumptic values. Since CheckJump() can be called
	// multiple times in multiplayer due to player prediction, we need to ensure
	// that doing so won't change the value more than once -- no ++'s here.
	override void Tick()
	{
		prevJumpButtonTics = jumpButtonTics;
		prevJumpGraceTics = jumpGraceTics;
		prevJumpSoundTics = jumpSoundTics;

		super.Tick();
	}
}

class Legs : Actor
{
	States
	{
	Spawn:
		LEGS A -1;
	Stop;
	}
}
