//Hello! Cosmic Emerald here, you guys can re-use the code and what not, just give credits. know that I guess

local SKIN = "laflower" //the skin we're applying the ability to

local stasisdelay = 8
local crouchstyle = 0 //the way crouching works; 0 is based on the classic games (crouch at low speeds, roll at high speeds), 1 is being able to crouch at any speed
local stopspinspam = true //whether it stop us from spindashing from pressing spin a second time or not
local allowjspindash = false //change tis to false if you don't want your character to be able to Spindash while crouching using jump

freeslot("S_PLAY_CROUCH", "S_PLAY_UNCROUCH")

states[S_PLAY_CROUCH] = {
    sprite = SPR_PLAY,
    frame = A|SPR2_LAND,
    var1 = 0,
    var2 = 0,
    tics = 2, //keep this at 2 for the transition to work
    nextstate = S_PLAY_UNCROUCH
}

states[S_PLAY_UNCROUCH] = {
    sprite = SPR_PLAY,
    frame = FF_SPR2ENDSTATE|SPR2_LAND,
    var1 = S_PLAY_STND,
    var2 = 0,
    tics = 2,         //the duration should end up to be at least 10 tics, you want to make it a division of it though so the amount of uncrouching frames can make it to that point without being too long
    nextstate = S_PLAY_UNCROUCH
}



addHook("PlayerThink", function(player)
 if (player.mo and player.mo.valid) //do we exist?
 and player.mo.skin == SKIN //and are we using that specified skin?
  //convenience stuff
  local mo = player.mo
  local cmd = player.cmd

  if player.thecrouch == nil
   player.thecrouch = {} //new table cause it's cool!
  end


  local ya_crouch = player.thecrouch //shorten the code writing

  if ya_crouch.state == nil //we should make this valid
   ya_crouch.state = 0 //it exists now :D
   ya_crouch.stasisdelay = 0

  elseif ya_crouch.state == 1 //Crouching
   if mo.state ~= S_PLAY_CROUCH //stop it,,,
    ya_crouch.state = 3
    //mo.height = P_GetPlayerHeight(player)
    player.height = P_GetPlayerHeight(player)

    return
   else

    if not (player.cmd.buttons&BT_SPIN) //spin let go?
     ya_crouch.state = 2 //we are Uncrouching now
     mo.state = S_PLAY_UNCROUCH
     ya_crouch.stasisdelay = stasisdelay
     //mo.height = P_GetPlayerHeight(player)
     player.height = P_GetPlayerHeight(player)

    else

     //mo.height = P_GetPlayerSpinHeight(player)
     player.height = P_GetPlayerSpinHeight(player)
     mo.state = S_PLAY_CROUCH //constantly set it to the state
     mo.frame = $|0 & ~FF_FRAMEMASK //make sure it's the first frame
     player.pflags = $|PF_STASIS //no movement
    end
   end

  elseif ya_crouch.state == 2 //Uncrouching
   player.pflags = $ & ~PF_STASIS //we wanna detect our inputs
   mo.height = P_GetPlayerHeight(player)
   player.height = skins[mo.skin].height

   if (cmd.forwardmove ~= 0 or cmd.sidemove ~= 0) and ya_crouch.stasisdelay < stasisdelay - 4 or mo.state ~= S_PLAY_UNCROUCH
    ya_crouch.state = 0
    
    if mo.state == S_PLAY_UNCROUCH
     P_Thrust(mo, cmd.angleturn<<16 + R_PointToAngle2(0,0,cmd.forwardmove*FRACUNIT,cmd.sidemove*FRACUNIT), FixedMul(player.accelstart, player.thrustfactor))  //do a start thrust based off the acceleration equation & control angle :p
     mo.state = S_PLAY_WALK
     player.pflags = $ & ~PF_STASIS
    end

   elseif mo.state == S_PLAY_UNCROUCH and ya_crouch.stasisdelay
    //player.pflags = $|PF_STASIS
   end
  elseif ya_crouch.state == 3 //Reset
   mo.height = P_GetPlayerHeight(player)
   player.height = skins[mo.skin].height
   ya_crouch.state = 0
  end

  ya_crouch.stasisdelay = max($-1, 0)
 else
  return //stop the code from running
 end
end)

//Initiating Crouch
addHook("SpinSpecial", function(player)
 if (player.mo and player.mo.valid) //do we exist?
 and player.mo.skin == SKIN //and are we using that specified skin?
  //convenience stuff
  local mo = player.mo
  local cmd = player.cmd

  if player.thecrouch ~= nil and player.thecrouch.state //and not player.thecrouch.stasisdelay
   /*if player.speed >= 5*FRACUNIT and not stopspinspam //spamdash stuff
    P_Thrust(mo, R_PointToAngle2(mo.x,mo.y,mo.momx,mo.momy), 5*FRACUNIT - player.speed)
   end*/

   return stopspinspam
  end

  if P_IsObjectOnGround(mo) and not (player.pflags&PF_SPINNING) and not (player.powers[pw_carry]) //main checks
  and (player.speed <= 5*FRACUNIT and crouchstyle == 0 or crouchstyle == 1) and player.charability2 == skins[SKIN].ability2 //speed stuff
  and player.thecrouch ~= nil and (player.thecrouch.state == 0 //we aren't crouching 
  or player.thecrouch.state == 2 and not player.thecrouch.stasisdelay) //or we are Uncrouching and 0 stasis delay
   mo.state = S_PLAY_CROUCH //change the state
   player.thecrouch.state = 1 //set this to on
   player.pflags = $|PF_STASIS //stasis

   return true
  end
 else
  return //stop the code from running
 end
end)

//Spindashing
addHook("JumpSpecial", function(player)
 if (player.mo and player.mo.valid) //do we exist?
 and player.mo.skin == SKIN //and are we using that specified skin?
  //convenience stuff
  local mo = player.mo
  local cmd = player.cmd

  if (player.pflags&PF_JUMPDOWN)
   if (player.pflags&PF_SPINNING|PF_STARTDASH)
    return true
   else
    return
   end
  end

  if P_IsObjectOnGround(mo) and player.thecrouch ~= nil and player.thecrouch.state == 1
   if allowjspindash //make us spindash
    player.thecrouch.state = 0
    player.pflags = $|PF_SPINNING|PF_STARTDASH|PF_JUMPDOWN & ~PF_STASIS
    mo.state = S_PLAY_SPINDASH
    S_StartSound(mo, sfx_spndsh)
    mo.height = P_GetPlayerHeight(player)
    player.height = skins[mo.skin].height

    if player.speed >= 5*FRACUNIT //speed stuff
     P_Thrust(mo, R_PointToAngle2(0, 0, mo.momx, mo.momy), 5*FRACUNIT - player.speed - 1)
    end

    return true
   end
   return
  end
 else
  return //stop the code from running
 end
end)

//Making the height be set correctly for spin gaps
local MakeTheHeightWork = function(player)
 if player.mo and player.mo.valid and player.mo.skin == SKIN

  if player.thecrouch ~= nil and player.thecrouch.state >= 2
  or player.mo.state == S_PLAY_UNCROUCH

   return P_GetPlayerHeight(player)
  elseif player.thecrouch ~= nil and player.thecrouch.state == 1
  or player.mo.state == S_PLAY_CROUCH

   return P_GetPlayerSpinHeight(player)
  end
 else
  return
 end
end

addHook("PlayerHeight", MakeTheHeightWork)