/* Emacs style mode select   -*- C++ -*-
 *-----------------------------------------------------------------------------
 *
 * $Id: p_user.c,v 1.4.2.2 2002/07/20 18:08:37 proff_fs Exp $
 *
 *  PrBoom a Doom port merged with LxDoom and LSDLDoom
 *  based on BOOM, a modified and improved DOOM engine
 *  Copyright (C) 1999 by
 *  id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
 *  Copyright (C) 1999-2000 by
 *  Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 *  02111-1307, USA.
 *
 * DESCRIPTION:
 *      Player related stuff.
 *      Bobbing POV/weapon, movement.
 *      Pending weapon.
 *
 *-----------------------------------------------------------------------------*/

static const char
rcsid[] = "$Id: p_user.c,v 1.4.2.2 2002/07/20 18:08:37 proff_fs Exp $";

#include "doomstat.h"
#include "d_event.h"
#include "r_main.h"
#include "p_map.h"
#include "p_spec.h"
#include "p_user.h"
#include "p_inter.h"
#include "st_stuff.h"
#include "d_main.h"
#include "g_game.h"
#include "m_misc.h"

// Index of the special effects (INVUL inverse) map.

#define INVERSECOLORMAP 32

//
// Movement.
//

// 16 pixels of bob

#define MAXBOB  0x100000

boolean onground; // whether player is on ground or in air

//
// P_Thrust
// Moves the given origin along a given angle.
//

void P_Thrust(player_t* player,angle_t angle,fixed_t move)
  {
  angle >>= ANGLETOFINESHIFT;
  player->mo->momx += FixedMul(move,finecosine[angle]);
  player->mo->momy += FixedMul(move,finesine[angle]);
  }


/*
 * P_Bob
 * Same as P_Thrust, but only affects bobbing.
 *
 * killough 10/98: We apply thrust separately between the real physical player
 * and the part which affects bobbing. This way, bobbing only comes from player
 * motion, nothing external, avoiding many problems, e.g. bobbing should not
 * occur on conveyors, unless the player walks on one, and bobbing should be
 * reduced at a regular rate, even on ice (where the player coasts).
 */

void P_Bob(player_t *player, angle_t angle, fixed_t move)
{
  player->momx += FixedMul(move,finecosine[angle >>= ANGLETOFINESHIFT]);
  player->momy += FixedMul(move,finesine[angle]);
}

//
// P_CalcHeight
// Calculate the walking / running height adjustment
//

void P_CalcHeight (player_t* player)
  {
  int     angle;
  fixed_t bob;

  // Regular movement bobbing
  // (needs to be calculated for gun swing
  // even if not on ground)
  // OPTIMIZE: tablify angle
  // Note: a LUT allows for effects
  //  like a ramp with low health.


  /* killough 10/98: Make bobbing depend only on player-applied motion.
   *
   * Note: don't reduce bobbing here if on ice: if you reduce bobbing here,
   * it causes bobbing jerkiness when the player moves from ice to non-ice,
   * and vice-versa.
   */
  player->bob = !mbf_features ?
    (FixedMul (player->mo->momx, player->mo->momx)
     + FixedMul (player->mo->momy,player->mo->momy))>>2 :
    player_bobbing ? (FixedMul(player->momx,player->momx) +
        FixedMul(player->momy,player->momy))>>2 : 0;

  if (player->bob > MAXBOB)
    player->bob = MAXBOB;

  if (!onground || player->cheats & CF_NOMOMENTUM)
    {
    player->viewz = player->mo->z + VIEWHEIGHT;

    if (player->viewz > player->mo->ceilingz-4*FRACUNIT)
      player->viewz = player->mo->ceilingz-4*FRACUNIT;

// The following line was in the Id source and appears      // phares 2/25/98
// to be a bug. player->viewz is checked in a similar
// manner at a different exit below.

//  player->viewz = player->mo->z + player->viewheight;
    return;
    }

  angle = (FINEANGLES/20*leveltime)&FINEMASK;
  bob = FixedMul(player->bob/2,finesine[angle]);

  // move viewheight

  if (player->playerstate == PST_LIVE)
    {
    player->viewheight += player->deltaviewheight;

    if (player->viewheight > VIEWHEIGHT)
      {
      player->viewheight = VIEWHEIGHT;
      player->deltaviewheight = 0;
      }

    if (player->viewheight < VIEWHEIGHT/2)
      {
      player->viewheight = VIEWHEIGHT/2;
      if (player->deltaviewheight <= 0)
        player->deltaviewheight = 1;
      }

    if (player->deltaviewheight)
      {
      player->deltaviewheight += FRACUNIT/4;
      if (!player->deltaviewheight)
        player->deltaviewheight = 1;
      }
    }

  //kaiser
  player->viewz = player->mo->z + player->viewheight + bob;
  if(player->mo->feetisclipped == true
		&& player->playerstate != PST_DEAD
		&& player->mo->z <= player->mo->floorz)
	{
		player->viewz -= 10*FRACUNIT;
	}


  if (player->viewz > player->mo->ceilingz-4*FRACUNIT)
    player->viewz = player->mo->ceilingz-4*FRACUNIT;
  }

//
// P_MovePlayer
//
// Adds momentum if the player is not in the air
//
// killough 10/98: simplified

void P_MovePlayer (player_t* player)
{
  ticcmd_t *cmd = &player->cmd;
  mobj_t *mo = player->mo;
  sector_t*	sector;
  int look;	//kaiser
	
    sector = player->mo->subsector->sector;

  mo->angle += cmd->angleturn << 16;
  onground = (mo->z <= mo->floorz || mo->onmobj == true);	//kaiser

  // killough 10/98:
  //
  // We must apply thrust to the player and bobbing separately, to avoid
  // anomalies. The thrust applied to bobbing is always the same strength on
  // ice, because the player still "works just as hard" to move, while the
  // thrust applied to the movement varies with 'movefactor'.

  if (cmd->forwardmove | cmd->sidemove) // killough 10/98
    {
      if (onground || mo->flags & MF_BOUNCES) // killough 8/9/98
      {
        int friction, movefactor = P_GetMoveFactor(mo, &friction);

        // killough 11/98:
        // On sludge, make bobbing depend on efficiency.
        // On ice, make it depend on effort.

        int bobfactor =
          friction < ORIG_FRICTION ? movefactor : ORIG_FRICTION_FACTOR;

        if (cmd->forwardmove)
        {
          P_Bob(player,mo->angle,cmd->forwardmove*bobfactor);
          P_Thrust(player,mo->angle,cmd->forwardmove*movefactor);
        }

        if (cmd->sidemove)
        {
          P_Bob(player,mo->angle-ANG90,cmd->sidemove*bobfactor);
          P_Thrust(player,mo->angle-ANG90,cmd->sidemove*movefactor);
        }
      }
      if (mo->state == states+S_PLAY)
        P_SetMobjState(mo,S_PLAY_RUN1);
    }

  look = cmd->lookfly&15;
	if(look > 7)
	{
		look -= 16;
	}
	if(look)
	{
		if(look == TOCENTER)
		{
			player->centering = true;
		}
	}

	/*1-26-06	fixed lookmove from 'jerking' if the player tried to look
	all the way down or up		-kaiser*/
	if(nrNum10(player->lookdir) < 190 && cmd->lookmove > 0)
	{
		if(player->lookdir + cmd->lookmove > 190)
			player->lookdir = 190;
		else
		player->lookdir += cmd->lookmove;
	}

	if(nrNum10(player->lookdir) > -190 && cmd->lookmove < 0)
	{
		if(player->lookdir + cmd->lookmove < -190)
			player->lookdir = -190;
		else
		player->lookdir += cmd->lookmove;
	}

	if(player->centering)
	{
		if(player->lookdir > 0)
		{
			player->lookdir -= 48;
		}
		else if(player->lookdir < 0)
		{
			player->lookdir += 48;
		}
		if(abs(player->lookdir) < 48)
		{
			player->lookdir = 0;
			player->centering = false;
		}
	}

  //kaiser
  if (cmd->buttons & BT_JUMP
	  && player->jumptics <= 0
	  && !(cmd->buttons & BT_SPECIAL)
	  && !(cmd->buttons & BT_CHANGE)
	  && player->mo->momz == 0 && (player->mo->z == player->mo->floorz ||
	  player->mo->onmobj == true))//updated 10-10-05
    {
            player->mo->momz = JUMPGRAVITY/2;
            player->mo->momz = JUMPGRAVITY;
                player->jumpdown |= 1;
				player->jumptics = 27;
				player->mo->onmobj = false;
    }

  if(cmd->arti)
  {
	  if(player->inventory.invTime == 0)
	  {
	  if(cmd->arti&AFLAG_INVLEFT)
	  {
		  P_InvLeft(player);
		  player->inventory.invTime = 4;
		  player->inventory.invDecide = 25;
	  }

	  if(cmd->arti&AFLAG_INVRIGHT)
	  {
		  P_InvRight(player);
		  player->inventory.invTime = 4;
		  player->inventory.invDecide = 25;
	  }

	  if(cmd->arti&AFLAG_DROP)
	  {
		  P_TossInventory(player, false);
		  player->inventory.invTime = 4;
	  }

	  if(cmd->arti&AFLAG_USEINV)
	  {
		  P_InvUse(player);
		  player->inventory.invTime = 8;
	  }
	  }
  }
}

#define ANG5 (ANG90/18)

//
// P_DeathThink
// Fall on your face when dying.
// Decrease POV height to floor height.
//

void P_DeathThink (player_t* player)
  {
  angle_t angle;
  angle_t delta;

  if(gamemap == 29)	//die at the last map and get the dark ending -kaiser
  {
	  finaletype = dark;
	  D_StartEndPic();
  }
  P_MovePsprites (player);

  // fall to the ground

  if (player->viewheight > 6*FRACUNIT)
    player->viewheight -= FRACUNIT;

  if (player->viewheight < 6*FRACUNIT)
    player->viewheight = 6*FRACUNIT;

  player->deltaviewheight = 0;
  onground = (player->mo->z <= player->mo->floorz);
  player->deltaviewheight = 0;
		if(player->viewheight > 6*FRACUNIT)
			player->viewheight -= FRACUNIT;
		if(player->viewheight < 6*FRACUNIT)
			player->viewheight = 6*FRACUNIT;
		if(player->lookdir > 0)
		{
			player->lookdir -= 6;
		}
		else if(player->lookdir < 0)
		{
			player->lookdir += 6;
		}
		if(abs(player->lookdir) < 6)
		{
			player->lookdir = 0;
		}
  P_CalcHeight (player);

  if (player->attacker && player->attacker != player->mo)
    {
    angle = R_PointToAngle2 (player->mo->x,
                             player->mo->y,
                             player->attacker->x,
                             player->attacker->y);

    delta = angle - player->mo->angle;

    if (delta < ANG5 || delta > (unsigned)-ANG5)
      {
      // Looking at killer,
      //  so fade damage flash down.

      player->mo->angle = angle;

      if (player->damagecount)
        player->damagecount--;
      }
    else if (delta < ANG180)
      player->mo->angle += ANG5;
    else
      player->mo->angle -= ANG5;
    }
  else if (player->damagecount)
    player->damagecount--;

  if (player->cmd.buttons & BT_USE)
    //player->playerstate = PST_REBORN;
	G_PlayerRebornLoad(false);
  }


//
// P_PlayerThink
//

void P_PlayerThink (player_t* player)
  {
  ticcmd_t*    cmd;
  weapontype_t newweapon;

  // killough 2/8/98, 3/21/98:
  if (player->cheats & CF_NOCLIP)
    player->mo->flags |= MF_NOCLIP;
  else
    player->mo->flags &= ~MF_NOCLIP;

  // chain saw run forward

  cmd = &player->cmd;
  if (player->mo->flags & MF_JUSTATTACKED)
    {
    cmd->angleturn = 0;
    cmd->forwardmove = 0xc800/512;
    cmd->sidemove = 0;
    player->mo->flags &= ~MF_JUSTATTACKED;
    }

  if (player->playerstate == PST_DEAD)
    {
		P_DeathThink (player);
		return;
    }

  if(player->weaponowned[wp_crossbow] && player->ammo[am_poisarrows] > 0)
  {
	  player->weaponowned[wp_crossbow2] = true;
  }

  if(player->weaponowned[wp_grenade] && player->ammo[am_grenades2] > 0)
  {
	  player->weaponowned[wp_grenade2] = true;
  }

  if(player->jumptics)	//kaiser
	{
		player->jumptics--;
	}
  if(player->misctime)
  {
	  player->misctime--;
  }
  if(player->inventory.invTime)
  {
	  player->inventory.invTime--;
  }
  if(player->inventory.invDecide)
  {
	  player->inventory.invDecide--;
  }
  if(player->burntics)
  {
	  player->burntics--;
	  if(player->burntics == 0)
	  {
		player->playerstate = PST_DEAD;
		P_DropWeapon (player);
		return;
	  }
  }
  if(player->lastNpcTic)
  {
	  P_UseLines(player);	//make sure the npc is activated after a slideshow
	  player->lastNpcTic--;
  }
  if(player->misctime2)
  {
	  player->misctime2--;
	  if (!(leveltime & 31) && player->misctime2 > 16*TICRATE)
	  {
		  P_DamageMobj (player->mo, NULL, NULL, 5);
	  }
  }
  player->worldTimer++;

  // Move around.
  // Reactiontime is used to prevent movement
  //  for a bit after a teleport.

  if (player->mo->reactiontime)
    player->mo->reactiontime--;
  else
    P_MovePlayer (player);

  P_CalcHeight (player); // Determines view height and bobbing

  // Determine if there's anything about the sector you're in that's
  // going to affect you, like painful floors.

  if (player->mo->subsector->sector->special)
    P_PlayerInSpecialSector (player);

  // Check for weapon change.

  if (cmd->buttons & BT_CHANGE)
    {
    // The actual changing of the weapon is done
    //  when the weapon psprite can do it
    //  (read: not in the middle of an attack).

    newweapon = (cmd->buttons & BT_WEAPONMASK)>>BT_WEAPONSHIFT;

    // killough 3/22/98: For demo compatibility we must perform the fist
    // and SSG weapons switches here, rather than in G_BuildTiccmd(). For
    // other games which rely on user preferences, we must use the latter.

    /*if (demo_compatibility)
      { // compatibility mode -- required for old demos -- killough
      if (newweapon == wp_fist && player->weaponowned[wp_chainsaw] &&
        (player->readyweapon != wp_chainsaw ||
         !player->powers[pw_strength]))
        newweapon = wp_chainsaw;
      if (gamemode == commercial &&
        newweapon == wp_shotgun &&
        player->weaponowned[wp_supershotgun] &&
        player->readyweapon != wp_supershotgun)
        newweapon = wp_supershotgun;
      }*/	//kaiser

    // killough 2/8/98, 3/22/98 -- end of weapon selection changes

    if (player->weaponowned[newweapon] && newweapon != player->readyweapon)

      // Do not go to plasma or BFG in shareware,
      //  even if cheated.

      /*if ((newweapon != wp_plasma && newweapon != wp_bfg)
          || (gamemode != shareware) )*/
        player->pendingweapon = newweapon;
    }

  // check for use

  if (cmd->buttons & BT_USE)
    {
    if (!player->usedown)
      {
      P_UseLines (player);
      player->usedown = true;
      }
    }
  else
    player->usedown = false;

  // cycle psprites

  P_MovePsprites (player);

  // Counters, time dependent power ups.

  // Strength counts up to diminish fade.

  if (player->powers[pw_strength])
    player->powers[pw_strength]++;

  // killough 1/98: Make idbeholdx toggle:

  if (player->powers[pw_invulnerability] > 0) // killough
    player->powers[pw_invulnerability]--;

  if (player->powers[pw_invisibility] > 0)    // killough
    if (! --player->powers[pw_invisibility] )
      player->mo->flags &= ~MF_SHADOW;

  if (player->powers[pw_infrared] > 0)        // killough
    player->powers[pw_infrared]--;

  if (player->powers[pw_ironfeet] > 0)        // killough
    player->powers[pw_ironfeet]--;

  if (player->powers[pw_targeter] > 0)
  player->powers[pw_targeter]--;

  if (player->damagecount)
    player->damagecount--;

  if (player->bonuscount)
    player->bonuscount--;

  // Handling colormaps.
  // killough 3/20/98: reformat to terse C syntax

  player->fixedcolormap = player->powers[pw_invulnerability] > 4*32 ||
    player->powers[pw_invulnerability] & 8 ? INVERSECOLORMAP :
    player->powers[pw_infrared] > 4*32 || player->powers[pw_infrared] & 8;

	if(player->misctime && player->readyweapon == wp_sigil)
	{
		player->fixedcolormap = INVERSECOLORMAP;
	}
  }

//===========================================================================
// P_SetMessage
//===========================================================================
void P_SetMessage(player_t *pl, char *msg)
{
	pl->message = msg;
}
