#include "character_humancontroller.hpp"

#include "defines.hpp"
#include "main.hpp"
#include "playstate.hpp"
#include "stage.hpp"
#include "tiles/tile.hpp"
#include "character.hpp"
#include "character_modelview.hpp"
//#include "sequences.hpp"
#include "../kungflowutils.hpp"

CharacterHumanController::CharacterHumanController(const KungFlowGame *game, const PlayState *playState, Character *character, const HumanGamer *humanGamer) : CharacterController(game, playState), character(character), humanGamer(humanGamer) {
  //createdSequence.sequence = 0;
  character->GetModelView()->AddMoveOwnership( e_MoveType_Jab  , new CharacterJabMove  (playState, character->GetModelView()->GetCharacterSprite(), character->GetModelView()->GetStats().jabStats) ); // todo: probably should put those default stats elsewhere
  character->GetModelView()->AddMoveOwnership( e_MoveType_Dodge, new CharacterDodgeMove(playState, character->GetModelView()->GetCharacterSprite(), character->GetModelView()->GetStats().dodgeStats) );
  character->GetModelView()->AddMoveOwnership( e_MoveType_Jump , new CharacterJumpMove (playState, character->GetModelView()->GetCharacterSprite(), character->GetModelView()->GetStats().jumpStats) );

  CharacterThrowMove *throwMove = new CharacterThrowMove(playState, character->GetModelView()->GetCharacterSprite(), character->GetModelView()->GetStats().throwStats); // todo: probably should put those default stats elsewhere
  character->GetModelView()->AddMoveOwnership( e_MoveType_Throw, throwMove );
  throwConnection = throwMove->sig_Throw.connect(boost::bind(&CharacterHumanController::OnThrow, this, _1, _2));

  throwBuffer = 1.0f;
}

CharacterHumanController::~CharacterHumanController() {
  throwConnection.disconnect();
}

void CharacterHumanController::OnStepPhysicsTime(CharacterModelView *characterModelView) {

  float autoKickFactor = _AUTOKICKDIRECTIONFACTOR;
  int opponentTeamID = 1;

  float secondsToReloadThrowBuffer = 0.5f;//3.0f;
  throwBuffer = Clamp(throwBuffer + (playState->GetPhysicsTimeStep_ms() * 0.001f) / secondsToReloadThrowBuffer, 0.0f, 1.0f);

  unsigned long time_ms = game->GetPhysicsTime_ms();

  const KeyInput &keyInput = humanGamer->GetKeyInput();
  const JoyInput &joyInput = humanGamer->GetJoyInput();


  // input keeping

  Input input;
  input.timestamp_ms = time_ms;

  // attack
  input.buttonAction.buttonID = 0;
  if (keyInput.actionkeys[5] || joyInput.buttons[2]) {
    input.buttonAction.pressed = true;
    //input.quantizationType = keyInput.actionkeys[5] ? e_QuantizationType_Digital : e_QuantizationType_Analog;
  } else {
    input.buttonAction.pressed = false;
  }
  inputHistory.TryInsertEntry(input);

  // alt attack
  input.buttonAction.buttonID = 1;
  if (keyInput.actionkeys[1] || joyInput.buttons[3]) {
    input.buttonAction.pressed = true;
    //input.quantizationType = keyInput.actionkeys[4] ? e_QuantizationType_Digital : e_QuantizationType_Analog;
  } else {
    input.buttonAction.pressed = false;
  }
  inputHistory.TryInsertEntry(input);

  // defend
  input.buttonAction.buttonID = 2;
  if (keyInput.actionkeys[3] || joyInput.buttons[1]) {
    input.buttonAction.pressed = true;
    //input.quantizationType = keyInput.actionkeys[3] ? e_QuantizationType_Digital : e_QuantizationType_Analog;
  } else {
    input.buttonAction.pressed = false;
  }
  inputHistory.TryInsertEntry(input);

  // jump
  input.buttonAction.buttonID = 3;
  if (keyInput.actionkeys[4] || joyInput.buttons[0]) {
    input.buttonAction.pressed = true;
    //input.quantizationType = keyInput.actionkeys[1] ? e_QuantizationType_Digital : e_QuantizationType_Analog;
  } else {
    input.buttonAction.pressed = false;
  }
  inputHistory.TryInsertEntry(input);

  //inputHistory.PrintStatus();
  //inputHistory.PrintHistory();
  //printf("\n");

/*
  Vector2 stick2DirectionRaw = joyInput.stick2_processed;
  //float powerFactor = fabs(playState->GetCurrentMusic()->GetBeatOffset() * 2.0f - 1.0f);
  float powerFactor = cos(playState->GetCurrentMusic()->GetBeatOffset() * 2 * pi) * 0.5f + 0.5f;
  powerFactor = 0.3f * powerFactor + 0.7f * (1.0f - playState->GetCurrentMusic()->GetBeatOffset());
  //if (game->GetPhysicsTimeStepsDone() % 70 == 0 && stick2DirectionRaw.GetLength() > 0.5f) {
  if (powerFactor > 0.994f && stick2DirectionRaw.GetLength() > 0.5f) {
    //playState->GetStage()->SpawnEnemy(e_EnemyType_Grenade, physicsState.position, direction * 10.0f);
    radian spreadAngle = 0.125f * pi;
    float step = spreadAngle / 2;
    for (radian rot = -spreadAngle * 0.5f; rot <= spreadAngle * 0.5f; rot += step) {
      Vector2 shotDirection = stick2DirectionRaw.GetNormalized(0).GetRotated(rot);
      //StageEntity *entity =
      playState->GetStage()->SpawnEnemy(e_EnemyType_Grenade, character->GetPosition() + shotDirection * 0.5f, character->GetZPosition() + character->GetHeight(), shotDirection * 7.0f, -0.2f);
      //entity->SetPosition(physicsState.position + shotDirection * (GetRadius() + entity->GetRadius())); // make sure there's no collision with body
    }
  }
*/

//  move only on the beat
//  powerFactor = cos(playState->GetCurrentMusic()->GetBeatOffset() * 2 * pi) * 0.5f + 0.5f;
//  powerFactor = 0.3f * powerFactor + 0.7f * (1.0f - playState->GetCurrentMusic()->GetBeatOffset());
//  if (powerFactor < 0.95f) return;q

  Vector2 inputDirectionRaw = joyInput.stick1_processed;
  float inputVelocity = 0;
  if (keyInput.left)  inputDirectionRaw.coords[0] -= 1.0f;
  if (keyInput.right) inputDirectionRaw.coords[0] += 1.0f;
  if (keyInput.up)    inputDirectionRaw.coords[1] -= 1.0f;
  if (keyInput.down)  inputDirectionRaw.coords[1] += 1.0f;
  if (keyInput.left || keyInput.right || keyInput.up || keyInput.down) autoKickFactor = 1.0f; // using keyboard == unable to properly aim manually
  inputDirectionRaw.Rotate(playState->GetViewOrientation());

  Vector2 inputDirectionRaw_noFlow = inputDirectionRaw;
/*
  // flow

  float axisMovementBandwidth = _AXISMOVEMENT_BANDWIDTH;

  bool flow = true;
  if (flow) {
    int flowAxis = playState->GetStage()->GetStadium()->GetFlowAxis();

    // forced movement towards the flow line
    float positionOnAxis = playState->GetStage()->GetStadium()->GetFlowPosition().coords[flowAxis];
    float playerToPositionOnAxis = positionOnAxis - character->GetPosition().coords[flowAxis];
    signed int playerToPositionOnAxisSignSide = SignSide(playerToPositionOnAxis);
    float toFlowLineMagnet = Curve(NormalizedClamp(playerToPositionOnAxis, -axisMovementBandwidth * 0.5f, axisMovementBandwidth * 0.5f), 0.0f) * 2.0f - 1.0f; // -1 to 1;
    float toFlowLineIntensity = pow(fabs(toFlowLineMagnet), _AXISMOVEMENT_CENTERPULL_EXP); // 0 to 1

    // user desired movement
    float inputOverAxis = inputDirectionRaw.coords[flowAxis];

    float flowAxisInput = playerToPositionOnAxisSignSide * toFlowLineIntensity + inputOverAxis * (1.0f - toFlowLineIntensity);

    inputDirectionRaw.coords[playState->GetStage()->GetStadium()->GetFlowAxis()] = flowAxisInput;
  }
*/

  //inputDirection.Normalize(0);
  //QuantizeDirection(inputDirection, 1.0f);

  //inputVelocity = (inputDirection.GetLength() > 0.5f) ? 1.0f : 0.0f;
  inputVelocity = std::min(1.0f, inputDirectionRaw.GetLength());
  //float veloMultiplier = playState->GetMediumVelocity();
  //if (keyInput.actionkeys[2]) veloMultiplier = playState->GetFullVelocity();
  //if (keyInput.actionkeys[8]) veloMultiplier = playState->GetSlowVelocity();
  //inputVelocity *= veloMultiplier;
  Vector2 inputDirection = GetQuantizedDirection(inputDirectionRaw, playState->GetQuantizationFactor());
  Vector2 inputDirection_noFlow = GetQuantizedDirection(inputDirectionRaw_noFlow, playState->GetQuantizationFactor());

  inputDirection.Normalize(0);
  inputDirection_noFlow.Normalize(0);
  inputDirectionRaw.Normalize(0);
  //inputDirection.Print();
  //printf("%f\n", inputVelocity);

  characterModelView->SetDesiredDirection(inputDirection);//.EnforceMaximumDeviation(character->GetDirection(), 0.3f).GetNormalized());
  characterModelView->SetDesiredVelocity(inputVelocity * playState->GetMediumVelocity());

  /* sprinting
  bool isPressed = false;
  inputHistory.FindCurrentStatus(1, isPressed); // alt attack
  //printf("%i\n", isPressed);
  if (isPressed) {
    characterModelView->SetDesiredVelocity(inputVelocity * playState->GetFullVelocity()); // sprinting is not fully implemented yet on the modelview side
  } else {
    characterModelView->SetDesiredVelocity(inputVelocity * playState->GetMediumVelocity());
  }
  */

  Vector2 lookDirection;
  float aimDistance = 0.3f;
  Character *badGuy = _stage->GetClosestCharacter(opponentTeamID, character->GetPosition() /*+ characterModelView->GetMovement() * 0.1f*/ + inputDirection * aimDistance);
  std::vector<Character*> assignedOpponents;
  playState->GetStage()->GetAssignedOpponentCharacters(character, assignedOpponents);
  if (!assignedOpponents.empty()) {
    // sort assigned opponents, closest first
    std::vector<CharacterProximity> proximities;
    for (unsigned int i = 0; i < assignedOpponents.size(); i++) {
      //if (assignedOpponents.at(i)->GetTeamID() == teamID && assignedOpponents.at(i) != ignore) {
      CharacterProximity prox;
      prox.character = assignedOpponents.at(i);
      prox.distance = character->GetPosition().GetDistance(assignedOpponents.at(i)->GetPosition());
      proximities.push_back(prox);
    }
    std::sort(proximities.begin(), proximities.end());
    badGuy = proximities.at(0).character;
  }

  if (badGuy) {
    Vector2 badGuyPosition = badGuy->GetPosition();
    Vector2 toBadGuy = badGuyPosition - characterModelView->GetPosition();
    lookDirection = toBadGuy.GetNormalized(0);
  } else {
    lookDirection = inputDirection_noFlow;//(inputDirection + playState->GetStage()->GetStadium()->GetFlowDirection() * 0.8f).GetNormalized(inputDirection);
  }
  characterModelView->SetDesiredBodyAngle(lookDirection.GetNormalized(playState->GetStage()->GetStadium()->GetFlowDirection().GetNormalized(_DEFAULTVECTOR)).GetAngle());


  /*
  float futureFactor = 0.1f;
  Vector2 badGuyFuturePos = badGuy->GetPosition() + badGuy->GetMovement() * futureFactor;
  Vector2 toBadGuyFuture = badGuyFuturePos - characterModelView->GetPosition();
  */


  Vector2 autoKickDirection = inputDirection_noFlow;
  Vector2 autoKickTargetPosition = characterModelView->GetPosition() + inputDirection_noFlow * 1.0f;
  if (badGuy) {
    Vector2 badGuyPosition = badGuy->GetPosition() + badGuy->GetMovement() * 0.2f;
    Vector2 toBadGuy = badGuyPosition - characterModelView->GetPosition();
    autoKickDirection = toBadGuy.GetNormalized(0);
    autoKickTargetPosition = badGuy->GetPosition();
  }
  // too different, use user input
  if (fabs(inputDirection_noFlow.GetAngle(autoKickDirection)) > 0.25f * pi) { // 45 deg == the maximum a keyboard/d-pad user can denote
    autoKickDirection = inputDirection_noFlow;
    autoKickTargetPosition = characterModelView->GetPosition() + inputDirection_noFlow * 1.0f;
  }
  //Vector2 desiredKickDirection = autoKickDirection;//inputDirection * 0.5f + autoKickDirection * 0.5f;
  //desiredKickDirection = GetQuantizedDirection(desiredKickDirection, 0.5f);

  Vector2 desiredJabDirection = (inputDirectionRaw_noFlow * (1.0f - autoKickFactor) + autoKickDirection * autoKickFactor).GetNormalized(0);
  //Vector2 desiredDefendDirection = inputDirection_noFlow.GetNormalized(-_DEFAULTVECTOR.GetRotated(characterModelView->GetBodyAngle()));
  Vector2 desiredDefendDirection = (-playState->GetStage()->GetStadium()->GetFlowDirection() * 0.3f +
                                    inputDirection_noFlow * 0.7f).GetNormalized(); // inputDirection_noFlow.GetNormalized(-_DEFAULTVECTOR.GetRotated(characterModelView->GetBodyAngle()))
  Vector2 desiredJumpDirection = inputDirection_noFlow;

/*
  // jab attack

  //if (keyInput.actionkeys[4] || joyInput.buttons[0]) {
  std::vector<ButtonStatus> jabCombo;
  jabCombo.push_back(ButtonStatus(0, true));
  //jabCombo.push_back(ButtonStatus(0, false));
  // * test: jab power not by loading gauge, but by music timing. so disable the need for button release * jabCombo.push_back(ButtonStatus(0, false));
  unsigned int jabComboTime_ms = playState->GetCurrentMusic()->GetBeatDuration_ms() * 1.5f;// * 0.95f;//600;
  unsigned int elapsedTime_ms = 0;
  unsigned long lastEntryID = 0;
  bool ignoreFirstButtonTime = jabCombo.size() > 1;
  //printf("%f\n", float(game->GetPhysicsTime_ms() * 0.001f) * (125 / 60.0f));
  //printf("beat %i . %f\n", playState->GetCurrentMusic()->GetBeat(), playState->GetCurrentMusic()->GetBeatOffset());
  if (inputHistory.FindCombo(jabCombo, time_ms, jabComboTime_ms, ignoreFirstButtonTime, elapsedTime_ms, lastEntryID, false) == true) {
    //printf("%f\n", std::min(1.0f, (float)elapsedTime_ms / (float)jabComboTime_ms));

    //float powerFactor = cos(playState->GetCurrentMusic()->GetBeatOffset() * 2 * pi) * 0.5f + 0.5f;
    //powerFactor = pow(powerFactor, 1.5f);
    //printf("beat %i . %f (== %f power)\n", playState->GetCurrentMusic()->GetBeat(), playState->GetCurrentMusic()->GetBeatOffset(), powerFactor);
    //if (characterModelView->RequestJab(desiredJabDirection, powerFactor)) {

    //float distanceFactor = std::min(1.0f, (float)elapsedTime_ms / (float)jabComboTime_ms); // combo time power
    float distanceFactor = 1.0f;
    if (characterModelView->RequestJab(desiredJabDirection, distanceFactor)) {
      //inputHistory.MarkComboAsProcessed(jabCombo, time_ms, jabComboTime_ms, true);
      inputHistory.SetProcessedUpTo(lastEntryID);
      //printf("[[ EXECUTE ]]\n");
    }
  }
*/

  // throw / shoot

  if (throwBuffer > 0.125f) {

    bool isThrowPressed = false;
    inputHistory.FindCurrentStatus(0, isThrowPressed);

    std::vector<ButtonStatus> throwCombo;
    throwCombo.push_back(ButtonStatus(0, true));
    //throwCombo.push_back(ButtonStatus(0, false));
    bool ignoreFirstButtonTime = throwCombo.size() > 1;
    unsigned int throwComboTime_ms = playState->GetCurrentMusic()->GetBeatDuration_ms() * 1.0f;// * 0.95f;//600;
    unsigned int elapsedTime_ms = 0;
    unsigned long lastEntryID = 0;

    if (inputHistory.FindCombo(throwCombo, time_ms, throwComboTime_ms, ignoreFirstButtonTime, elapsedTime_ms, lastEntryID, false) == true || isThrowPressed) {
    //if (isThrowPressed) {

      bool success = false;

      if (badGuy && badGuy->GetPosition().GetDistance(character->GetPosition()) < 1.2f && fabs(badGuy->GetZPosition() - character->GetZPosition()) < 0.5f) {

        // close? use bare knuckles
        success = characterModelView->RequestJab(desiredJabDirection, 1.0f);

      } else {

        /*
        float flowDirectionBias = 0.4f;
        Vector2 convergedThrowDirection = ( inputDirectionRaw_noFlow * (1.0f - flowDirectionBias) + playState->GetStage()->GetStadium()->GetFlowDirection() * flowDirectionBias ).GetNormalized(inputDirectionRaw);
        */
        //radian maxAngle = 0.75f * pi;
        Vector2 throwDirection;

        radian maxAngle = 0.45f * pi;
        //throwDirection = playState->GetStage()->GetStadium()->GetFlowDirection().GetRotated(Clamp(inputDirectionRaw_noFlow.GetAngle(playState->GetStage()->GetStadium()->GetFlowDirection()), -maxAngle, maxAngle));
        // front/back differentation (bi-directional)
        unsigned int flowAxis = playState->GetStage()->GetStadium()->GetFlowAxis();
        if (inputDirectionRaw_noFlow.coords[flowAxis] >= -0.25f) {
          throwDirection = playState->GetStage()->GetStadium()->GetFlowDirection().GetRotated(Clamp(inputDirectionRaw_noFlow.GetAngle(playState->GetStage()->GetStadium()->GetFlowDirection()), -maxAngle, maxAngle));
        } else { // invert backwards direction, more convenient
          // mirrored version
          //throwDirection = playState->GetStage()->GetStadium()->GetFlowDirection().GetRotated(Clamp((-inputDirectionRaw_noFlow).GetAngle(playState->GetStage()->GetStadium()->GetFlowDirection()), -maxAngle, maxAngle));
          //throwDirection.coords[1 - flowAxis] *= -1.0f; // invert sides
          // aim straight forward version
          throwDirection = playState->GetStage()->GetStadium()->GetFlowDirection();
        }


    /*
        Vector2 flowDirection = playState->GetStage()->GetStadium()->GetFlowDirection();
        Vector2 throwDirection = flowDirection;
        unsigned int flowAxis = playState->GetStage()->GetStadium()->GetFlowAxis();
        throwDirection.coords[abs(1 - flowAxis)] = inputDirectionRaw_noFlow.coords[abs(1.0f - flowAxis)] * inputDirectionRaw_noFlow.coords[flowAxis];
        throwDirection.Normalize(flowDirection);
    */

        success = characterModelView->RequestThrow(character->GetPosition() + throwDirection); //autoKickTargetPosition
        if (success) throwBuffer -= 0.25f;//playState->GetPhysicsTimeStep_ms() * 0.001f;

      }

      if (success) {
        inputHistory.SetProcessedUpTo(lastEntryID);
        //throwBuffer -= 0.25f;//playState->GetPhysicsTimeStep_ms() * 0.001f;
      }

    }
  }


  // dodge / block

  std::vector<ButtonStatus> dodgeCombo;
  dodgeCombo.push_back(ButtonStatus(2, true));
  bool ignoreFirstButtonTime = dodgeCombo.size() > 1;
  unsigned int dodgeComboTime_ms = playState->GetCurrentMusic()->GetBeatDuration_ms() * 1.5f;// * 0.95f;//600;
  unsigned int elapsedTime_ms = 0;
  unsigned long lastEntryID = 0;
  if (inputHistory.FindCombo(dodgeCombo, time_ms, dodgeComboTime_ms, ignoreFirstButtonTime, elapsedTime_ms, lastEntryID, false) == true) {

    bool success = false;

/*
    // check direction: sideways == dodge, forward/backward == block
    float ndotl = lookDirection.GetDotProduct(desiredDefendDirection);
    //if (fabs(ndotl) < 0.7f) {
    if (ndotl < 0.9f) {//0.7f) {
*/
      // dodge mode
      success = characterModelView->RequestDodge(desiredDefendDirection);
/*
    } else {

      // block mode
      success = characterModelView->RequestBlock();
    }
*/

    //if (success) inputHistory.MarkComboAsProcessed(dodgeCombo, time_ms, dodgeComboTime_ms, false);
    if (success) inputHistory.SetProcessedUpTo(lastEntryID);

  }/* else {

    // released block button
    if (characterModelView->GetActiveMoveState() == e_MoveState_Block) {
      characterModelView->ReleaseBlock();
    }

  }*/


  // jump

  std::vector<ButtonStatus> jumpCombo;
  jumpCombo.push_back(ButtonStatus(3, true));
  //jumpCombo.push_back(ButtonStatus(3, false));
  ignoreFirstButtonTime = jumpCombo.size() > 1;
  unsigned int jumpComboTime_ms = playState->GetCurrentMusic()->GetBeatDuration_ms() * 1.5f;//* 0.95f;//600;
  elapsedTime_ms = 0;
  lastEntryID = 0;
  if (inputHistory.FindCombo(jumpCombo, time_ms, jumpComboTime_ms, ignoreFirstButtonTime, elapsedTime_ms, lastEntryID, false) == true) {
    if (characterModelView->RequestJump(desiredJumpDirection, 1.0f)) { // std::min(1.0f, (float)elapsedTime_ms / (float)jumpComboTime_ms))
      inputHistory.SetProcessedUpTo(lastEntryID);
    }
  }





  // LEGACY / TESTING

  /*
  // DASH!
  std::vector<ButtonStatus> dashCombo;
  dashCombo.push_back(ButtonStatus(1, true));
  unsigned int dashComboTime_ms = 600;
  elapsedTime_ms = 0;
  lastEntryID = 0;
  if (inputHistory.FindCombo(dashCombo, time_ms, dashComboTime_ms, true, elapsedTime_ms, lastEntryID, false) == true) {
    //printf("%f\n", std::min(1.0f, (float)elapsedTime_ms / (float)jabComboTime_ms));
    if (characterModelView->RequestDash(autoKickTargetPosition)) {
      //inputHistory.MarkComboAsProcessed(dashCombo, time_ms, jabComboTime_ms, true);
      inputHistory.SetProcessedUpTo(lastEntryID);
    }
  }
  */

  /*
  // POWER SMASH
  if (keyInput.actionkeys[5] || joyInput.buttons[2]) {
    // kick button
    if (characterModelView->GetActiveMoveState() == e_MoveState_Physics) {
      characterModelView->RequestKickBuffer(desiredKickDirection);
    }
    if (characterModelView->GetActiveMoveState() == e_MoveState_KickBuffer) {
      characterModelView->AddKickBufferPower(desiredKickDirection);
    }
  } else {
    // released kick button
    if (characterModelView->GetActiveMoveState() == e_MoveState_KickBuffer) {
      characterModelView->RequestKick();
    }
  }
  */

}

void CharacterHumanController::OnThrow(const Vector2 &desiredMovement, const Vector2 &toTargetTranslation) {

  // physics version
  Vector2 throwDirection = toTargetTranslation.GetNormalized(playState->GetStage()->GetStadium()->GetFlowDirection());
  float throwPower = 12.0f;
  float safeDistance = (character->GetRadius() + 0.2f);

  // single shot
  //playState->GetStage()->SpawnEnemy(e_EnemyType_Grenade, character->GetPosition() + throwDirection * safeDistance, character->GetZPosition() + character->GetHeight() * 0.7f, throwDirection * throwPower, -0.1f);

  // hellfire!
  //radian spreadAngle = 0.125f * pi;
  //float step = spreadAngle / 4;
  radian spreadAngle = 0.05f * pi;
  float step = spreadAngle / 2;

  // shooting while aiming backwards
  if (desiredMovement.coords[playState->GetStage()->GetStadium()->GetFlowAxis()] < -0.25f) {
    spreadAngle = 1.0f * pi;
    step = spreadAngle / 4;
  }

  //if (playState->GetCurrentMusic()->GetBeat() % 2 == 0) spreadAngle = 0;
  for (radian rot = -spreadAngle * 0.5f; rot <= spreadAngle * 0.5f; rot += step) {
    float zMovement = -1.0;

    const Tile *tile = playState->GetStage()->GetStadium()->GetTile(character->GetPosition());
    if (tile) {
      zMovement -= NormalizedClamp(character->GetZPosition() - tile->GetZPosition(), 0.0f, 2.0f) * 8.0f;
    }
    playState->GetStage()->SpawnEnemy(e_EnemyType_Grenade, character->GetPosition() + throwDirection * safeDistance, character->GetZPosition() + character->GetHeight() * 0.7f, throwDirection.GetRotated(rot) * throwPower, zMovement);
    //entity->SetPosition(physicsState.position + shotDirection * (GetRadius() + entity->GetRadius())); // make sure there's no collision with body
  }

  playState->SoundPlay(e_GlobalSound_SoundBlast, 0.4f);

}
