/* Emacs style mode select   -*- C++ -*- */
/*-----------------------------------------------------------------------------*/

/* $Id:$*/

/* Copyright (C) 1993-1996 by id Software, Inc.*/

/* This source is available for distribution and/or modification*/
/* only under the terms of the DOOM Source Code License as*/
/* published by id Software. All rights reserved.*/

/* The source is distributed in the hope that it will be useful,*/
/* but WITHOUT ANY WARRANTY; without even the implied warranty of*/
/* FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License*/
/* for more details.*/

/* $Log:$*/

/* DESCRIPTION:*/
/*	Player related stuff.*/
/*	Bobbing POV/weapon, movement.*/
/*	Pending weapon.*/

/*-----------------------------------------------------------------------------*/


static const char
rcsid[] = "$Id: p_user.c,v 1.3 1997/01/28 22:08:29 b1 Exp $";


#include "doomdef.h"
#include "d_event.h"

#include "p_local.h"

#include "doomstat.h"



/* Index of the special effects (INVUL inverse) map.*/
#define INVERSECOLORMAP		NUMCOLORMAPS



/* Movement.*/


/* 16 pixels of bob*/
#define MAXBOB	0x100000

static boolean	onground;


/* Jump cheat on? */
boolean		jump_cheat = false;
boolean		float_cheat = false;
#ifndef FULL_NEW_FEATURES
/* In case this cheat is strictly local, i.e. only then consoleplayer gets it */
int		vertical_acceleration = 0;
#endif



/* 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_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.*/
    player->bob =
	FixedMul (player->mo->momx, player->mo->momx)
	+ FixedMul (player->mo->momy,player->mo->momy);

    player->bob >>= 2;

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

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

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

	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;
	}
    }
    player->viewz = player->mo->z + player->viewheight + bob;

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




/* P_MovePlayer*/

void P_MovePlayer (player_t* player)
{
    ticcmd_t*		cmd;
    signed char		move_by;
    boolean		player_float;
    int			movefactor;

    cmd = &player->cmd;

    player->mo->angle += (cmd->angleturn<<16);

    /* Do not let the player control movement*/
    /*  if not onground.*/
    onground = (player->mo->z <= player->mo->floorz);

    /* Floating mode cheat -- we still want to move then. */
    player_float = ((player->mo->flags & MF_NOGRAVITY) != 0);

#ifdef DIYBOOM
    movefactor = P_GetMoveFactor(player->mo);
#else
    movefactor = 2048;
#endif

    if ((cmd->forwardmove && onground) || player_float)
    {
        move_by = (signed char)(cmd->forwardmove);
	P_Thrust (player, player->mo->angle, move_by*movefactor);
    }

    if ((cmd->sidemove && onground) || player_float)
    {
        move_by = (signed char)(cmd->sidemove);
	P_Thrust (player, player->mo->angle-ANG90, move_by*movefactor);
    }

    if ( (cmd->forwardmove || cmd->sidemove)
	 && player->mo->state == &states[S_PLAY] )
    {
	P_SetMobjState (player->mo, S_PLAY_RUN1);
    }

    /* If floating reduce momentum to 15/16 each tic. */
    if (player_float)
    {
	player->mo->momx -= (player->mo->momx >> 4);
	player->mo->momy -= (player->mo->momy >> 4);
	player->mo->momz -= (player->mo->momz >> 4);
    }

    if (player == &players[consoleplayer])
	float_cheat = player_float;
}




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

#define ANG5   	(ANG90/18)

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

    /* In case of the floating cheat: end floating */
    player->mo->flags &= ~(MF_NOGRAVITY + MF_FLOAT);

    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);
    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;
}




/* P_PlayerThink*/

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

    /* fixme: do this in the cheat code*/
    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;
    }

    /* 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);

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

    /* Check for weapon change.*/

    /* A special event has no other buttons.*/
    if (cmd->buttons & BT_SPECIAL)
	cmd->buttons = 0;

    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;

        BOOMSTATEMENT(if (demo_compatibility) {)
	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;
	}
        BOOMSTATEMENT(})

	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;

#ifdef FULL_NEW_FEATURES
    /* Full support for the new stuff? */
    /* First update the mobj info according to the flags */
    if (player != &players[consoleplayer])
    {
	if ((cmd->newflags & NEW_FEATURE_FLOAT) == 0)
	{
	    player->mo->flags &= ~(MF_NOGRAVITY + MF_FLOAT);
	}
	else
	{
	    player->mo->flags |= (MF_NOGRAVITY + MF_FLOAT);
	}
    }
    /* Then actually move the guy */
    if (cmd->vaccel != 0)
    {
	if ((((cmd->newflags & NEW_FEATURE_JUMP) != 0) && onground) || ((cmd->newflags & NEW_FEATURE_FLOAT) != 0))
	{
	    player->mo->momz += ((cmd->vaccel << 24) >> (24 - FRACBITS));
	}
    }
#else
    /* Jumping/floating mode? Only allowed in single player mode, not in net games. */
    if ((vertical_acceleration != 0) && (player == &players[consoleplayer]))
    {
	if ((jump_cheat && onground) || ((player->mo->flags & MF_NOGRAVITY) != 0))
	{
	    player->mo->momz += (vertical_acceleration) << FRACBITS;
	}
	vertical_acceleration = 0;
    }
#endif

    /* cycle psprites*/
    P_MovePsprites (player);

    /* Counters, time dependend power ups.*/

    /* Strength counts up to diminish fade.*/
    if (player->powers[pw_strength])
	player->powers[pw_strength]++;

    if (player->powers[pw_invulnerability])
	player->powers[pw_invulnerability]--;

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

    if (player->powers[pw_infrared])
	player->powers[pw_infrared]--;

    if (player->powers[pw_ironfeet])
	player->powers[pw_ironfeet]--;

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

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


    /* Handling colormaps.*/
    if (player->powers[pw_invulnerability])
    {
	if (player->powers[pw_invulnerability] > 4*32
	    || (player->powers[pw_invulnerability]&8) )
	    player->fixedcolormap = INVERSECOLORMAP;
	else
	    player->fixedcolormap = 0;
    }
    else if (player->powers[pw_infrared])
    {
	if (player->powers[pw_infrared] > 4*32
	    || (player->powers[pw_infrared]&8) )
	{
	    /* almost full bright*/
	    player->fixedcolormap = 1;
	}
	else
	    player->fixedcolormap = 0;
    }
    else
	player->fixedcolormap = 0;
}


