/* 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:*/
/*	Handling interactions (i.e., collisions).*/

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


static const char
rcsid[] = "$Id: p_inter.c,v 1.4 1997/02/03 22:45:11 b1 Exp $";


/* Data.*/
#include "doomdef.h"
#include "dstrings.h"
#include "sounds.h"

#include "doomstat.h"

#include "m_random.h"
#include "i_system.h"

#include "am_map.h"

#include "p_local.h"

#include "s_sound.h"

#ifdef __GNUG__
#pragma implementation "p_inter.h"
#endif
#include "p_inter.h"


#define BONUSADD	6




/*
 *  For easy DeHackEd support: put all messages in an array
 */

#define MSG_GOTARMOR		0
#define MSG_GOTMEGA		1
#define MSG_GOTHTHBONUS		2
#define MSG_GOTARMBONUS		3
#define MSG_GOTSTIM		4
#define MSG_GOTMEDINEED		5
#define MSG_GOTMEDIKIT		6
#define MSG_GOTSUPER		7
#define MSG_GOTBLUECARD		8
#define MSG_GOTYELWCARD		9
#define MSG_GOTREDCARD		10
#define MSG_GOTBLUESKUL		11
#define MSG_GOTYELWSKUL		12
#define MSG_GOTREDSKULL		13
#define MSG_GOTINVUL		14
#define MSG_GOTBERSERK		15
#define MSG_GOTINVIS		16
#define MSG_GOTSUIT		17
#define MSG_GOTMAP		18
#define MSG_GOTVISOR		19
#define MSG_GOTMSPHERE		20
#define MSG_GOTCLIP		21
#define MSG_GOTCLIPBOX		22
#define MSG_GOTROCKET		23
#define MSG_GOTROCKBOX		24
#define MSG_GOTCELL		25
#define MSG_GOTCELLBOX		26
#define MSG_GOTSHELLS		27
#define MSG_GOTSHELLBOX		28
#define MSG_GOTBACKPACK		29
#define MSG_GOTBFG9000		30
#define MSG_GOTCHAINGUN		31
#define MSG_GOTCHAINSAW		32
#define MSG_GOTLAUNCHER		33
#define MSG_GOTPLASMA		34
#define MSG_GOTSHOTGUN		35
#define MSG_GOTSHOTGUN2		36

char *pinter_messages[PINTER_MESSAGES] = {
  GOTARMOR,
  GOTMEGA,
  GOTHTHBONUS,
  GOTARMBONUS,
  GOTSTIM,
  GOTMEDINEED,
  GOTMEDIKIT,
  GOTSUPER,

  GOTBLUECARD,
  GOTYELWCARD,
  GOTREDCARD,
  GOTBLUESKUL,
  GOTYELWSKUL,
  GOTREDSKULL,

  GOTINVUL,
  GOTBERSERK,
  GOTINVIS,
  GOTSUIT,
  GOTMAP,
  GOTVISOR,
  GOTMSPHERE,

  GOTCLIP,
  GOTCLIPBOX,
  GOTROCKET,
  GOTROCKBOX,
  GOTCELL,
  GOTCELLBOX,
  GOTSHELLS,
  GOTSHELLBOX,
  GOTBACKPACK,

  GOTBFG9000,
  GOTCHAINGUN,
  GOTCHAINSAW,
  GOTLAUNCHER,
  GOTPLASMA,
  GOTSHOTGUN,
  GOTSHOTGUN2
};

int MaxHealth = 100;
int MaxArmor = 200;
int GreenArmorClass = 1;
int BlueArmorClass = 2;
int MegasphereHealth = 200;
int SoulsphereHealth = 100;
int MaxSoulsphere = 200;




/* a weapon is found with two clip loads,*/
/* a big item has five clip loads*/
int	maxammo[NUMAMMO] = {200, 50, 300, 50};
int	clipammo[NUMAMMO] = {10, 4, 20, 1};



/* GET STUFF*/



/* P_GiveAmmo*/
/* Num is the number of clip loads,*/
/* not the individual count (0= 1/2 clip).*/
/* Returns false if the ammo can't be picked up at all*/


static boolean
P_GiveAmmo
( player_t*	player,
  ammotype_t	ammo,
  int		num )
{
    int		oldammo;

    if (ammo == am_noammo)
	return false;

    if (ammo < 0 || ammo > NUMAMMO)
	I_Error ("P_GiveAmmo: bad type %i", ammo);

    if ( player->ammo[ammo] == player->maxammo[ammo]  )
	return false;

    if (num)
	num *= clipammo[ammo];
    else
	num = clipammo[ammo]/2;

    if (gameskill == sk_baby
	|| gameskill == sk_nightmare)
    {
	/* give double ammo in trainer mode,*/
	/* you'll need in nightmare*/
	num <<= 1;
    }


    oldammo = player->ammo[ammo];
    player->ammo[ammo] += num;

    if (player->ammo[ammo] > player->maxammo[ammo])
	player->ammo[ammo] = player->maxammo[ammo];

    /* If non zero ammo, */
    /* don't change up weapons,*/
    /* player was lower on purpose.*/
    if (oldammo)
	return true;

    /* We were down to zero,*/
    /* so select a new weapon.*/
    /* Preferences are not user selectable.*/
    switch (ammo)
    {
      case am_clip:
	if (player->readyweapon == wp_fist)
	{
	    if (player->weaponowned[wp_chaingun])
		player->pendingweapon = wp_chaingun;
	    else
		player->pendingweapon = wp_pistol;
	}
	break;

      case am_shell:
	if (player->readyweapon == wp_fist
	    || player->readyweapon == wp_pistol)
	{
	    if (player->weaponowned[wp_shotgun])
		player->pendingweapon = wp_shotgun;
	}
	break;

      case am_cell:
	if (player->readyweapon == wp_fist
	    || player->readyweapon == wp_pistol)
	{
	    if (player->weaponowned[wp_plasma])
		player->pendingweapon = wp_plasma;
	}
	break;

      case am_misl:
	if (player->readyweapon == wp_fist)
	{
	    if (player->weaponowned[wp_missile])
		player->pendingweapon = wp_missile;
	}
      default:
	break;
    }

    return true;
}



/* P_GiveWeapon*/
/* The weapon name may have a MF_DROPPED flag ored in.*/

static boolean
P_GiveWeapon
( player_t*	player,
  weapontype_t	weapon,
  boolean	dropped )
{
    boolean	gaveammo;
    boolean	gaveweapon;

    if (netgame
	&& (deathmatch!=2)
	 && !dropped )
    {
	/* leave placed weapons forever on net games*/
	if (player->weaponowned[weapon])
	    return false;

	player->bonuscount += BONUSADD;
	player->weaponowned[weapon] = true;

	if (deathmatch)
	    P_GiveAmmo (player, weaponinfo[weapon].ammo, 5);
	else
	    P_GiveAmmo (player, weaponinfo[weapon].ammo, 2);
	player->pendingweapon = weapon;

	if (player == &players[consoleplayer])
	    S_StartSound (NULL, sfx_wpnup);
	return false;
    }

    if (weaponinfo[weapon].ammo != am_noammo)
    {
	/* give one clip with a dropped weapon,*/
	/* two clips with a found weapon*/
	if (dropped)
	    gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 1);
	else
	    gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 2);
    }
    else
	gaveammo = false;

    if (player->weaponowned[weapon])
	gaveweapon = false;
    else
    {
	gaveweapon = true;
	player->weaponowned[weapon] = true;
	player->pendingweapon = weapon;
    }

    return (gaveweapon || gaveammo);
}




/* P_GiveBody*/
/* Returns false if the body isn't needed at all*/

static boolean
P_GiveBody
( player_t*	player,
  int		num )
{
    if (player->health >= MaxHealth)
	return false;

    player->health += num;
    if (player->health > MaxHealth)
	player->health = MaxHealth;
    player->mo->health = player->health;

    return true;
}




/* P_GiveArmor*/
/* Returns false if the armor is worse*/
/* than the current armor.*/

static boolean
P_GiveArmor
( player_t*	player,
  int		armortype )
{
    int		hits;

    hits = armortype*100;
    if (player->armorpoints >= hits)
	return false;	/* don't pick up*/

    player->armortype = armortype;
    player->armorpoints = hits;

    return true;
}




/* P_GiveCard*/

static void
P_GiveCard
( player_t*	player,
  card_t	card )
{
    if (player->cards[card])
	return;

    player->bonuscount = BONUSADD;
    player->cards[card] = 1;
}



/* P_GivePower*/

boolean
P_GivePower
( player_t*	player,
  int /*powertype_t*/	power )
{
    if (power == pw_invulnerability)
    {
	player->powers[power] = INVULNTICS;
	return true;
    }

    if (power == pw_invisibility)
    {
	player->powers[power] = INVISTICS;
	player->mo->flags |= MF_SHADOW;
	return true;
    }

    if (power == pw_infrared)
    {
	player->powers[power] = INFRATICS;
	return true;
    }

    if (power == pw_ironfeet)
    {
	player->powers[power] = IRONTICS;
	return true;
    }

    if (power == pw_strength)
    {
	P_GiveBody (player, 100);
	player->powers[power] = 1;
	return true;
    }

    if (player->powers[power])
	return false;	/* already got it*/

    player->powers[power] = 1;
    return true;
}




/* P_TouchSpecialThing*/

void
P_TouchSpecialThing
( mobj_t*	special,
  mobj_t*	toucher )
{
    player_t*	player;
    int		i;
    fixed_t	delta;
    int		sound;

    delta = special->z - toucher->z;

    if (delta > toucher->height
	|| delta < -8*FRACUNIT)
    {
	/* out of reach*/
	return;
    }


    sound = sfx_itemup;
    player = toucher->player;
    /* Hmm. It seems that this can be called with toucher->player == 0... */
    if (!player)
      return;

    /* Dead thing touching.*/
    /* Can happen with a sliding player corpse.*/
    if (toucher->health <= 0)
	return;

    /* Identify by sprite.*/
    switch (special->sprite)
    {
	/* armor*/
      case SPR_ARM1:
	if (!P_GiveArmor (player, GreenArmorClass))
	    return;
	player->message = pinter_messages[MSG_GOTARMOR];
	break;

      case SPR_ARM2:
	if (!P_GiveArmor (player, BlueArmorClass))
	    return;
	player->message = pinter_messages[MSG_GOTMEGA];
	break;

	/* bonus items*/
      case SPR_BON1:
	player->health++;		/* can go over 100%*/
	if (player->health > MaxSoulsphere)
	    player->health = MaxSoulsphere;
	player->mo->health = player->health;
	player->message = pinter_messages[MSG_GOTHTHBONUS];
	break;

      case SPR_BON2:
	player->armorpoints++;		/* can go over 100%*/
	if (player->armorpoints > MaxArmor)
	    player->armorpoints = MaxArmor;
	if (!player->armortype)
	    player->armortype = GreenArmorClass;
	player->message = pinter_messages[MSG_GOTARMBONUS];
	break;

      case SPR_SOUL:
	player->health += SoulsphereHealth;
	if (player->health > MaxSoulsphere)
	    player->health = MaxSoulsphere;
	player->mo->health = player->health;
	player->message = pinter_messages[MSG_GOTSUPER];
	sound = sfx_getpow;
	break;

      case SPR_MEGA:
	if (gamemode != commercial)
	    return;
	player->health = MegasphereHealth;
	player->mo->health = player->health;
	P_GiveArmor (player, BlueArmorClass);
	player->message = pinter_messages[MSG_GOTMSPHERE];
	sound = sfx_getpow;
	break;

	/* cards*/
	/* leave cards for everyone*/
      case SPR_BKEY:
	if (!player->cards[it_bluecard])
	    player->message = pinter_messages[MSG_GOTBLUECARD];
	P_GiveCard (player, it_bluecard);
	if (!netgame)
	    break;
	return;

      case SPR_YKEY:
	if (!player->cards[it_yellowcard])
	    player->message = pinter_messages[MSG_GOTYELWCARD];
	P_GiveCard (player, it_yellowcard);
	if (!netgame)
	    break;
	return;

      case SPR_RKEY:
	if (!player->cards[it_redcard])
	    player->message = pinter_messages[MSG_GOTREDCARD];
	P_GiveCard (player, it_redcard);
	if (!netgame)
	    break;
	return;

      case SPR_BSKU:
	if (!player->cards[it_blueskull])
	    player->message = pinter_messages[MSG_GOTBLUESKUL];
	P_GiveCard (player, it_blueskull);
	if (!netgame)
	    break;
	return;

      case SPR_YSKU:
	if (!player->cards[it_yellowskull])
	    player->message = pinter_messages[MSG_GOTYELWSKUL];
	P_GiveCard (player, it_yellowskull);
	if (!netgame)
	    break;
	return;

      case SPR_RSKU:
	if (!player->cards[it_redskull])
	    player->message = pinter_messages[MSG_GOTREDSKULL];
	P_GiveCard (player, it_redskull);
	if (!netgame)
	    break;
	return;

	/* medikits, heals*/
      case SPR_STIM:
	if (!P_GiveBody (player, 10))
	    return;
	player->message = pinter_messages[MSG_GOTSTIM];
	break;

      case SPR_MEDI:
	if (!P_GiveBody (player, 25))
	    return;

	if (player->health < 25)
	    player->message = pinter_messages[MSG_GOTMEDINEED];
	else
	    player->message = pinter_messages[MSG_GOTMEDIKIT];
	break;


	/* power ups*/
      case SPR_PINV:
	if (!P_GivePower (player, pw_invulnerability))
	    return;
	player->message = pinter_messages[MSG_GOTINVUL];
	sound = sfx_getpow;
	break;

      case SPR_PSTR:
	if (!P_GivePower (player, pw_strength))
	    return;
	player->message = pinter_messages[MSG_GOTBERSERK];
	if (player->readyweapon != wp_fist)
	    player->pendingweapon = wp_fist;
	sound = sfx_getpow;
	break;

      case SPR_PINS:
	if (!P_GivePower (player, pw_invisibility))
	    return;
	player->message = pinter_messages[MSG_GOTINVIS];
	sound = sfx_getpow;
	break;

      case SPR_SUIT:
	if (!P_GivePower (player, pw_ironfeet))
	    return;
	player->message = pinter_messages[MSG_GOTSUIT];
	sound = sfx_getpow;
	break;

      case SPR_PMAP:
	if (!P_GivePower (player, pw_allmap))
	    return;
	player->message = pinter_messages[MSG_GOTMAP];
	sound = sfx_getpow;
	break;

      case SPR_PVIS:
	if (!P_GivePower (player, pw_infrared))
	    return;
	player->message = pinter_messages[MSG_GOTVISOR];
	sound = sfx_getpow;
	break;

	/* ammo*/
      case SPR_CLIP:
	if (special->flags & MF_DROPPED)
	{
	    if (!P_GiveAmmo (player,am_clip,0))
		return;
	}
	else
	{
	    if (!P_GiveAmmo (player,am_clip,1))
		return;
	}
	player->message = pinter_messages[MSG_GOTCLIP];
	break;

      case SPR_AMMO:
	if (!P_GiveAmmo (player, am_clip,5))
	    return;
	player->message = pinter_messages[MSG_GOTCLIPBOX];
	break;

      case SPR_ROCK:
	if (!P_GiveAmmo (player, am_misl,1))
	    return;
	player->message = pinter_messages[MSG_GOTROCKET];
	break;

      case SPR_BROK:
	if (!P_GiveAmmo (player, am_misl,5))
	    return;
	player->message = pinter_messages[MSG_GOTROCKBOX];
	break;

      case SPR_CELL:
	if (!P_GiveAmmo (player, am_cell,1))
	    return;
	player->message = pinter_messages[MSG_GOTCELL];
	break;

      case SPR_CELP:
	if (!P_GiveAmmo (player, am_cell,5))
	    return;
	player->message = pinter_messages[MSG_GOTCELLBOX];
	break;

      case SPR_SHEL:
	if (!P_GiveAmmo (player, am_shell,1))
	    return;
	player->message = pinter_messages[MSG_GOTSHELLS];
	break;

      case SPR_SBOX:
	if (!P_GiveAmmo (player, am_shell,5))
	    return;
	player->message = pinter_messages[MSG_GOTSHELLBOX];
	break;

      case SPR_BPAK:
	if (!player->backpack)
	{
	    for (i=0 ; i<NUMAMMO ; i++)
		player->maxammo[i] *= 2;
	    player->backpack = true;
	}
	for (i=0 ; i<NUMAMMO ; i++)
	    P_GiveAmmo (player, i, 1);
	player->message = pinter_messages[MSG_GOTBACKPACK];
	break;

	/* weapons*/
      case SPR_BFUG:
	if (!P_GiveWeapon (player, wp_bfg, false) )
	    return;
	player->message = pinter_messages[MSG_GOTBFG9000];
	sound = sfx_wpnup;
	break;

      case SPR_MGUN:
	if (!P_GiveWeapon (player, wp_chaingun, special->flags&MF_DROPPED) )
	    return;
	player->message = pinter_messages[MSG_GOTCHAINGUN];
	sound = sfx_wpnup;
	break;

      case SPR_CSAW:
	if (!P_GiveWeapon (player, wp_chainsaw, false) )
	    return;
	player->message = pinter_messages[MSG_GOTCHAINSAW];
	sound = sfx_wpnup;
	break;

      case SPR_LAUN:
	if (!P_GiveWeapon (player, wp_missile, false) )
	    return;
	player->message = pinter_messages[MSG_GOTLAUNCHER];
	sound = sfx_wpnup;
	break;

      case SPR_PLAS:
	if (!P_GiveWeapon (player, wp_plasma, false) )
	    return;
	player->message = pinter_messages[MSG_GOTPLASMA];
	sound = sfx_wpnup;
	break;

      case SPR_SHOT:
	if (!P_GiveWeapon (player, wp_shotgun, special->flags&MF_DROPPED ) )
	    return;
	player->message = pinter_messages[MSG_GOTSHOTGUN];
	sound = sfx_wpnup;
	break;

      case SPR_SGN2:
	if (!P_GiveWeapon (player, wp_supershotgun, special->flags&MF_DROPPED ) )
	    return;
	player->message = pinter_messages[MSG_GOTSHOTGUN2];
	sound = sfx_wpnup;
	break;

      default:
	I_Error ("P_SpecialThing: Unknown gettable thing");
    }

    if (special->flags & MF_COUNTITEM)
	player->itemcount++;
    P_RemoveMobj (special);
    player->bonuscount += BONUSADD;
    if (player == &players[consoleplayer])
    {
	S_StartSound (NULL, sound);
    }
}



/* KillMobj*/

void
P_KillMobj
( mobj_t*	source,
  mobj_t*	target )
{
    mobjtype_t	item;
    mobj_t*	mo;

    target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY);

    if (target->type != MT_SKULL)
	target->flags &= ~MF_NOGRAVITY;

    target->flags |= MF_CORPSE|MF_DROPOFF;
    target->height >>= 2;

    if (source && source->player)
    {
	/* count for intermission*/
	if (target->flags & MF_COUNTKILL)
	    source->player->killcount++;

	if (target->player)
	    source->player->frags[target->player-players]++;
    }
    else if (!netgame && (target->flags & MF_COUNTKILL) )
    {
	/* count all monster deaths,*/
	/* even those caused by other monsters*/
	players[0].killcount++;
    }

    if (target->player)
    {
	/* count environment kills against you*/
	if (!source)
	    target->player->frags[target->player-players]++;

	target->flags &= ~MF_SOLID;
	target->player->playerstate = PST_DEAD;
	P_DropWeapon (target->player);

	if (target->player == &players[consoleplayer]
	    && automapactive)
	{
	    /* don't die in auto map,*/
	    /* switch view prior to dying*/
	    AM_Stop ();
	}

    }

    if (target->health < -target->info->spawnhealth
	&& target->info->xdeathstate)
    {
	P_SetMobjState (target, target->info->xdeathstate);
    }
    else
	P_SetMobjState (target, target->info->deathstate);
    target->tics -= P_Random()&3;

    if (target->tics < 1)
	target->tics = 1;

    /*	I_StartSound (&actor->r, actor->info->deathsound);*/


    /* Drop stuff.*/
    /* This determines the kind of object spawned*/
    /* during the death frame of a thing.*/
    switch (target->type)
    {
      case MT_WOLFSS:
      case MT_POSSESSED:
	item = MT_CLIP;
	break;

      case MT_SHOTGUY:
	item = MT_SHOTGUN;
	break;

      case MT_CHAINGUY:
	item = MT_CHAINGUN;
	break;

      default:
	return;
    }

    mo = P_SpawnMobj (target->x,target->y,ONFLOORZ, item);
    mo->flags |= MF_DROPPED;	/* special versions of items*/
}





/* P_DamageMobj*/
/* Damages both enemies and players*/
/* "inflictor" is the thing that caused the damage*/
/*  creature or missile, can be NULL (slime, etc)*/
/* "source" is the thing to target after taking damage*/
/*  creature or NULL*/
/* Source and inflictor are the same for melee attacks.*/
/* Source can be NULL for slime, barrel explosions*/
/* and other environmental stuff.*/

void
P_DamageMobj
( mobj_t*	target,
  mobj_t*	inflictor,
  mobj_t*	source,
  int 		damage )
{
    unsigned	ang;
    int		saved;
    player_t*	player;
    fixed_t	thrust;
    int		temp;

    if ( !(target->flags & MF_SHOOTABLE) )
	return;	/* shouldn't happen...*/

    if (target->health <= 0)
	return;

    if ( target->flags & MF_SKULLFLY )
    {
	target->momx = target->momy = target->momz = 0;
    }

    player = target->player;
    if (player && gameskill == sk_baby)
	damage >>= 1; 	/* take half damage in trainer mode*/


    /* Some close combat weapons should not*/
    /* inflict thrust and push the victim out of reach,*/
    /* thus kick away unless using the chainsaw.*/
    if (inflictor
	&& !(target->flags & MF_NOCLIP)
	&& (!source
	    || !source->player
	    || source->player->readyweapon != wp_chainsaw))
    {
	ang = R_PointToAngle2 ( inflictor->x,
				inflictor->y,
				target->x,
				target->y);

	thrust = damage*(FRACUNIT>>3)*100/target->info->mass;

	/* make fall forwards sometimes*/
	if ( damage < 40
	     && damage > target->health
	     && target->z - inflictor->z > 64*FRACUNIT
	     && (P_Random ()&1) )
	{
	    ang += ANG180;
	    thrust *= 4;
	}

	ang >>= ANGLETOFINESHIFT;
	target->momx += FixedMul (thrust, finecosine[ang]);
	target->momy += FixedMul (thrust, finesine[ang]);
    }

    /* player specific*/
    if (player)
    {
	/* end of game hell hack*/
	if (target->subsector->sector->special == 11
	    && damage >= target->health)
	{
	    damage = target->health - 1;
	}


	/* Below certain threshold,*/
	/* ignore damage in GOD mode, or with INVUL power.*/
	if ( damage < 1000
	     && ( (player->cheats&CF_GODMODE)
		  || player->powers[pw_invulnerability] ) )
	{
	    return;
	}

	if (player->armortype)
	{
	    if (player->armortype == 1)
		saved = damage/3;
	    else
		saved = damage/2;

	    if (player->armorpoints <= saved)
	    {
		/* armor is used up*/
		saved = player->armorpoints;
		player->armortype = 0;
	    }
	    player->armorpoints -= saved;
	    damage -= saved;
	}
	player->health -= damage; 	/* mirror mobj health here for Dave*/
	if (player->health < 0)
	    player->health = 0;

	player->attacker = source;
	player->damagecount += damage;	/* add damage after armor / invuln*/

	if (player->damagecount > 100)
	    player->damagecount = 100;	/* teleport stomp does 10k points...*/

	temp = damage < 100 ? damage : 100;

	if (player == &players[consoleplayer])
	    I_Tactile (40,10,40+temp*2);
    }

    /* do the damage	*/
    target->health -= damage;
    if (target->health <= 0)
    {
	P_KillMobj (source, target);
	return;
    }

    if ( (P_Random () < target->info->painchance)
	 && !(target->flags&MF_SKULLFLY) )
    {
	target->flags |= MF_JUSTHIT;	/* fight back!*/

	P_SetMobjState (target, target->info->painstate);
    }

    target->reactiontime = 0;		/* we're awake now...	*/

    if ( (!target->threshold || target->type == MT_VILE)
	 && source && source != target
	 && source->type != MT_VILE)
    {
	/* if not intent on another player,*/
	/* chase after this one*/
#ifdef DIYBOOM
        if (!target->lastenemy || !target->lastenemy->player || target->lastenemy->health<=0)
            target->lastenemy = target->target;
#endif
	target->target = source;
	target->threshold = BASETHRESHOLD;
	if (target->state == &states[target->info->spawnstate]
	    && target->info->seestate != S_NULL)
	    P_SetMobjState (target, target->info->seestate);
    }

}

