halflife-photomode/dlls/combat.cpp

1706 lines
42 KiB
C++
Raw Normal View History

/***
2013-08-30 13:34:05 -07:00
*
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* Use, distribution, and modification of this source code and/or resulting
* object code is restricted to non-commercial enhancements to products from
* Valve LLC. All other use, distribution, or modification is prohibited
* without written permission from Valve LLC.
*
****/
/*
===== combat.cpp ========================================================
functions dealing with damage infliction & death
*/
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "soundent.h"
#include "decals.h"
#include "animation.h"
#include "weapons.h"
#include "func_break.h"
extern Vector VecBModelOrigin(entvars_t* pevBModel);
2013-08-30 13:34:05 -07:00
#define GERMAN_GIB_COUNT 4
#define HUMAN_GIB_COUNT 6
#define ALIEN_GIB_COUNT 4
2013-08-30 13:34:05 -07:00
// HACKHACK -- The gib velocity equations don't work
void CGib::LimitVelocity()
2013-08-30 13:34:05 -07:00
{
float length = pev->velocity.Length();
// ceiling at 1500. The gib velocity equation is not bounded properly. Rather than tune it
// in 3 separate places again, I'll just limit it here.
if (length > 1500.0)
pev->velocity = pev->velocity.Normalize() * 1500; // This should really be sv_maxvelocity * 0.75 or something
2013-08-30 13:34:05 -07:00
}
void CGib::SpawnStickyGibs(entvars_t* pevVictim, Vector vecOrigin, int cGibs)
2013-08-30 13:34:05 -07:00
{
int i;
if (g_Language == LANGUAGE_GERMAN)
2013-08-30 13:34:05 -07:00
{
// no sticky gibs in germany right now!
return;
2013-08-30 13:34:05 -07:00
}
for (i = 0; i < cGibs; i++)
2013-08-30 13:34:05 -07:00
{
CGib* pGib = GetClassPtr((CGib*)NULL);
2013-08-30 13:34:05 -07:00
pGib->Spawn("models/stickygib.mdl");
pGib->pev->body = RANDOM_LONG(0, 2);
2013-08-30 13:34:05 -07:00
if (pevVictim)
2013-08-30 13:34:05 -07:00
{
pGib->pev->origin.x = vecOrigin.x + RANDOM_FLOAT(-3, 3);
pGib->pev->origin.y = vecOrigin.y + RANDOM_FLOAT(-3, 3);
pGib->pev->origin.z = vecOrigin.z + RANDOM_FLOAT(-3, 3);
2013-08-30 13:34:05 -07:00
/*
pGib->pev->origin.x = pevVictim->absmin.x + pevVictim->size.x * (RANDOM_FLOAT ( 0 , 1 ) );
pGib->pev->origin.y = pevVictim->absmin.y + pevVictim->size.y * (RANDOM_FLOAT ( 0 , 1 ) );
pGib->pev->origin.z = pevVictim->absmin.z + pevVictim->size.z * (RANDOM_FLOAT ( 0 , 1 ) );
*/
// make the gib fly away from the attack vector
pGib->pev->velocity = g_vecAttackDir * -1;
// mix in some noise
pGib->pev->velocity.x += RANDOM_FLOAT(-0.15, 0.15);
pGib->pev->velocity.y += RANDOM_FLOAT(-0.15, 0.15);
pGib->pev->velocity.z += RANDOM_FLOAT(-0.15, 0.15);
2013-08-30 13:34:05 -07:00
pGib->pev->velocity = pGib->pev->velocity * 900;
pGib->pev->avelocity.x = RANDOM_FLOAT(250, 400);
pGib->pev->avelocity.y = RANDOM_FLOAT(250, 400);
2013-08-30 13:34:05 -07:00
// copy owner's blood color
pGib->m_bloodColor = (CBaseEntity::Instance(pevVictim))->BloodColor();
if (pevVictim->health > -50)
2013-08-30 13:34:05 -07:00
{
pGib->pev->velocity = pGib->pev->velocity * 0.7;
}
else if (pevVictim->health > -200)
2013-08-30 13:34:05 -07:00
{
pGib->pev->velocity = pGib->pev->velocity * 2;
}
else
{
pGib->pev->velocity = pGib->pev->velocity * 4;
}
2013-08-30 13:34:05 -07:00
pGib->pev->movetype = MOVETYPE_TOSS;
pGib->pev->solid = SOLID_BBOX;
UTIL_SetSize(pGib->pev, Vector(0, 0, 0), Vector(0, 0, 0));
pGib->SetTouch(&CGib::StickyGibTouch);
pGib->SetThink(NULL);
2013-08-30 13:34:05 -07:00
}
pGib->LimitVelocity();
}
}
void CGib::SpawnHeadGib(entvars_t* pevVictim)
2013-08-30 13:34:05 -07:00
{
CGib* pGib = GetClassPtr((CGib*)NULL);
2013-08-30 13:34:05 -07:00
if (g_Language == LANGUAGE_GERMAN)
2013-08-30 13:34:05 -07:00
{
pGib->Spawn("models/germangibs.mdl"); // throw one head
2013-08-30 13:34:05 -07:00
pGib->pev->body = 0;
}
else
{
pGib->Spawn("models/hgibs.mdl"); // throw one head
2013-08-30 13:34:05 -07:00
pGib->pev->body = 0;
}
if (pevVictim)
2013-08-30 13:34:05 -07:00
{
pGib->pev->origin = pevVictim->origin + pevVictim->view_ofs;
edict_t* pentPlayer = FIND_CLIENT_IN_PVS(pGib->edict());
if (RANDOM_LONG(0, 100) <= 5 && pentPlayer)
2013-08-30 13:34:05 -07:00
{
// 5% chance head will be thrown at player's face.
entvars_t* pevPlayer;
2013-08-30 13:34:05 -07:00
pevPlayer = VARS(pentPlayer);
pGib->pev->velocity = ((pevPlayer->origin + pevPlayer->view_ofs) - pGib->pev->origin).Normalize() * 300;
2013-08-30 13:34:05 -07:00
pGib->pev->velocity.z += 100;
}
else
{
pGib->pev->velocity = Vector(RANDOM_FLOAT(-100, 100), RANDOM_FLOAT(-100, 100), RANDOM_FLOAT(200, 300));
2013-08-30 13:34:05 -07:00
}
pGib->pev->avelocity.x = RANDOM_FLOAT(100, 200);
pGib->pev->avelocity.y = RANDOM_FLOAT(100, 300);
2013-08-30 13:34:05 -07:00
// copy owner's blood color
pGib->m_bloodColor = (CBaseEntity::Instance(pevVictim))->BloodColor();
if (pevVictim->health > -50)
2013-08-30 13:34:05 -07:00
{
pGib->pev->velocity = pGib->pev->velocity * 0.7;
}
else if (pevVictim->health > -200)
2013-08-30 13:34:05 -07:00
{
pGib->pev->velocity = pGib->pev->velocity * 2;
}
else
{
pGib->pev->velocity = pGib->pev->velocity * 4;
}
}
pGib->LimitVelocity();
}
void CGib::SpawnRandomGibs(entvars_t* pevVictim, int cGibs, bool human)
2013-08-30 13:34:05 -07:00
{
int cSplat;
for (cSplat = 0; cSplat < cGibs; cSplat++)
2013-08-30 13:34:05 -07:00
{
CGib* pGib = GetClassPtr((CGib*)NULL);
2013-08-30 13:34:05 -07:00
if (g_Language == LANGUAGE_GERMAN)
2013-08-30 13:34:05 -07:00
{
pGib->Spawn("models/germangibs.mdl");
pGib->pev->body = RANDOM_LONG(0, GERMAN_GIB_COUNT - 1);
2013-08-30 13:34:05 -07:00
}
else
{
if (human)
2013-08-30 13:34:05 -07:00
{
// human pieces
pGib->Spawn("models/hgibs.mdl");
pGib->pev->body = RANDOM_LONG(1, HUMAN_GIB_COUNT - 1); // start at one to avoid throwing random amounts of skulls (0th gib)
2013-08-30 13:34:05 -07:00
}
else
{
// aliens
pGib->Spawn("models/agibs.mdl");
pGib->pev->body = RANDOM_LONG(0, ALIEN_GIB_COUNT - 1);
2013-08-30 13:34:05 -07:00
}
}
if (pevVictim)
2013-08-30 13:34:05 -07:00
{
// spawn the gib somewhere in the monster's bounding volume
pGib->pev->origin.x = pevVictim->absmin.x + pevVictim->size.x * (RANDOM_FLOAT(0, 1));
pGib->pev->origin.y = pevVictim->absmin.y + pevVictim->size.y * (RANDOM_FLOAT(0, 1));
pGib->pev->origin.z = pevVictim->absmin.z + pevVictim->size.z * (RANDOM_FLOAT(0, 1)) + 1; // absmin.z is in the floor because the engine subtracts 1 to enlarge the box
2013-08-30 13:34:05 -07:00
// make the gib fly away from the attack vector
pGib->pev->velocity = g_vecAttackDir * -1;
// mix in some noise
pGib->pev->velocity.x += RANDOM_FLOAT(-0.25, 0.25);
pGib->pev->velocity.y += RANDOM_FLOAT(-0.25, 0.25);
pGib->pev->velocity.z += RANDOM_FLOAT(-0.25, 0.25);
2013-08-30 13:34:05 -07:00
pGib->pev->velocity = pGib->pev->velocity * RANDOM_FLOAT(300, 400);
2013-08-30 13:34:05 -07:00
pGib->pev->avelocity.x = RANDOM_FLOAT(100, 200);
pGib->pev->avelocity.y = RANDOM_FLOAT(100, 300);
2013-08-30 13:34:05 -07:00
// copy owner's blood color
pGib->m_bloodColor = (CBaseEntity::Instance(pevVictim))->BloodColor();
if (pevVictim->health > -50)
2013-08-30 13:34:05 -07:00
{
pGib->pev->velocity = pGib->pev->velocity * 0.7;
}
else if (pevVictim->health > -200)
2013-08-30 13:34:05 -07:00
{
pGib->pev->velocity = pGib->pev->velocity * 2;
}
else
{
pGib->pev->velocity = pGib->pev->velocity * 4;
}
pGib->pev->solid = SOLID_BBOX;
UTIL_SetSize(pGib->pev, Vector(0, 0, 0), Vector(0, 0, 0));
2013-08-30 13:34:05 -07:00
}
pGib->LimitVelocity();
}
}
bool CBaseMonster::HasHumanGibs()
2013-08-30 13:34:05 -07:00
{
int myClass = Classify();
if (myClass == CLASS_HUMAN_MILITARY ||
myClass == CLASS_PLAYER_ALLY ||
myClass == CLASS_HUMAN_PASSIVE ||
myClass == CLASS_PLAYER)
2013-08-30 13:34:05 -07:00
return true;
2013-08-30 13:34:05 -07:00
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
}
bool CBaseMonster::HasAlienGibs()
2013-08-30 13:34:05 -07:00
{
int myClass = Classify();
if (myClass == CLASS_ALIEN_MILITARY ||
myClass == CLASS_ALIEN_MONSTER ||
myClass == CLASS_ALIEN_PASSIVE ||
myClass == CLASS_INSECT ||
myClass == CLASS_ALIEN_PREDATOR ||
myClass == CLASS_ALIEN_PREY)
2013-08-30 13:34:05 -07:00
return true;
2013-08-30 13:34:05 -07:00
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
}
void CBaseMonster::FadeMonster()
2013-08-30 13:34:05 -07:00
{
StopAnimation();
pev->velocity = g_vecZero;
pev->movetype = MOVETYPE_NONE;
pev->avelocity = g_vecZero;
pev->animtime = gpGlobals->time;
pev->effects |= EF_NOINTERP;
SUB_StartFadeOut();
}
//=========================================================
// GibMonster - create some gore and get rid of a monster's
// model.
//=========================================================
void CBaseMonster::GibMonster()
2013-08-30 13:34:05 -07:00
{
TraceResult tr;
bool gibbed = false;
2013-08-30 13:34:05 -07:00
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM);
2013-08-30 13:34:05 -07:00
// only humans throw skulls !!!UNDONE - eventually monsters will have their own sets of gibs
if (HasHumanGibs())
2013-08-30 13:34:05 -07:00
{
if (CVAR_GET_FLOAT("violence_hgibs") != 0) // Only the player will ever get here
2013-08-30 13:34:05 -07:00
{
CGib::SpawnHeadGib(pev);
CGib::SpawnRandomGibs(pev, 4, true); // throw some human gibs.
2013-08-30 13:34:05 -07:00
}
2021-11-19 13:45:16 +01:00
gibbed = true;
2013-08-30 13:34:05 -07:00
}
else if (HasAlienGibs())
2013-08-30 13:34:05 -07:00
{
if (CVAR_GET_FLOAT("violence_agibs") != 0) // Should never get here, but someone might call it directly
2013-08-30 13:34:05 -07:00
{
CGib::SpawnRandomGibs(pev, 4, false); // Throw alien gibs
2013-08-30 13:34:05 -07:00
}
2021-11-19 13:45:16 +01:00
gibbed = true;
2013-08-30 13:34:05 -07:00
}
if (!IsPlayer())
2013-08-30 13:34:05 -07:00
{
if (gibbed)
2013-08-30 13:34:05 -07:00
{
// don't remove players!
SetThink(&CBaseMonster::SUB_Remove);
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time;
}
else
{
FadeMonster();
}
}
}
//=========================================================
// GetDeathActivity - determines the best type of death
// anim to play.
//=========================================================
Activity CBaseMonster::GetDeathActivity()
2013-08-30 13:34:05 -07:00
{
Activity deathActivity;
bool fTriedDirection;
float flDot;
TraceResult tr;
Vector vecSrc;
2013-08-30 13:34:05 -07:00
if (pev->deadflag != DEAD_NO)
2013-08-30 13:34:05 -07:00
{
// don't run this while dying.
return m_IdealActivity;
}
vecSrc = Center();
2021-11-19 13:43:33 +01:00
fTriedDirection = false;
deathActivity = ACT_DIESIMPLE; // in case we can't find any special deaths to do.
2013-08-30 13:34:05 -07:00
UTIL_MakeVectors(pev->angles);
flDot = DotProduct(gpGlobals->v_forward, g_vecAttackDir * -1);
2013-08-30 13:34:05 -07:00
switch (m_LastHitGroup)
2013-08-30 13:34:05 -07:00
{
// try to pick a region-specific death.
case HITGROUP_HEAD:
deathActivity = ACT_DIE_HEADSHOT;
break;
case HITGROUP_STOMACH:
deathActivity = ACT_DIE_GUTSHOT;
break;
case HITGROUP_GENERIC:
// try to pick a death based on attack direction
2021-11-19 13:45:16 +01:00
fTriedDirection = true;
2013-08-30 13:34:05 -07:00
if (flDot > 0.3)
2013-08-30 13:34:05 -07:00
{
deathActivity = ACT_DIEFORWARD;
}
else if (flDot <= -0.3)
2013-08-30 13:34:05 -07:00
{
deathActivity = ACT_DIEBACKWARD;
}
break;
default:
// try to pick a death based on attack direction
2021-11-19 13:45:16 +01:00
fTriedDirection = true;
2013-08-30 13:34:05 -07:00
if (flDot > 0.3)
2013-08-30 13:34:05 -07:00
{
deathActivity = ACT_DIEFORWARD;
}
else if (flDot <= -0.3)
2013-08-30 13:34:05 -07:00
{
deathActivity = ACT_DIEBACKWARD;
}
break;
}
// can we perform the prescribed death?
if (LookupActivity(deathActivity) == ACTIVITY_NOT_AVAILABLE)
2013-08-30 13:34:05 -07:00
{
// no! did we fail to perform a directional death?
if (fTriedDirection)
2013-08-30 13:34:05 -07:00
{
// if yes, we're out of options. Go simple.
deathActivity = ACT_DIESIMPLE;
}
else
{
// cannot perform the ideal region-specific death, so try a direction.
if (flDot > 0.3)
2013-08-30 13:34:05 -07:00
{
deathActivity = ACT_DIEFORWARD;
}
else if (flDot <= -0.3)
2013-08-30 13:34:05 -07:00
{
deathActivity = ACT_DIEBACKWARD;
}
}
}
if (LookupActivity(deathActivity) == ACTIVITY_NOT_AVAILABLE)
2013-08-30 13:34:05 -07:00
{
// if we're still invalid, simple is our only option.
deathActivity = ACT_DIESIMPLE;
}
if (deathActivity == ACT_DIEFORWARD)
2013-08-30 13:34:05 -07:00
{
// make sure there's room to fall forward
UTIL_TraceHull(vecSrc, vecSrc + gpGlobals->v_forward * 64, dont_ignore_monsters, head_hull, edict(), &tr);
2013-08-30 13:34:05 -07:00
if (tr.flFraction != 1.0)
{
deathActivity = ACT_DIESIMPLE;
}
2013-08-30 13:34:05 -07:00
}
if (deathActivity == ACT_DIEBACKWARD)
2013-08-30 13:34:05 -07:00
{
// make sure there's room to fall backward
UTIL_TraceHull(vecSrc, vecSrc - gpGlobals->v_forward * 64, dont_ignore_monsters, head_hull, edict(), &tr);
2013-08-30 13:34:05 -07:00
if (tr.flFraction != 1.0)
{
deathActivity = ACT_DIESIMPLE;
}
2013-08-30 13:34:05 -07:00
}
return deathActivity;
}
//=========================================================
// GetSmallFlinchActivity - determines the best type of flinch
// anim to play.
//=========================================================
Activity CBaseMonster::GetSmallFlinchActivity()
2013-08-30 13:34:05 -07:00
{
Activity flinchActivity;
bool fTriedDirection;
float flDot;
2013-08-30 13:34:05 -07:00
2021-11-19 13:43:33 +01:00
fTriedDirection = false;
UTIL_MakeVectors(pev->angles);
flDot = DotProduct(gpGlobals->v_forward, g_vecAttackDir * -1);
switch (m_LastHitGroup)
2013-08-30 13:34:05 -07:00
{
// pick a region-specific flinch
case HITGROUP_HEAD:
flinchActivity = ACT_FLINCH_HEAD;
break;
case HITGROUP_STOMACH:
flinchActivity = ACT_FLINCH_STOMACH;
break;
case HITGROUP_LEFTARM:
flinchActivity = ACT_FLINCH_LEFTARM;
break;
case HITGROUP_RIGHTARM:
flinchActivity = ACT_FLINCH_RIGHTARM;
break;
case HITGROUP_LEFTLEG:
flinchActivity = ACT_FLINCH_LEFTLEG;
break;
case HITGROUP_RIGHTLEG:
flinchActivity = ACT_FLINCH_RIGHTLEG;
break;
case HITGROUP_GENERIC:
default:
// just get a generic flinch.
flinchActivity = ACT_SMALL_FLINCH;
break;
}
// do we have a sequence for the ideal activity?
if (LookupActivity(flinchActivity) == ACTIVITY_NOT_AVAILABLE)
2013-08-30 13:34:05 -07:00
{
flinchActivity = ACT_SMALL_FLINCH;
}
return flinchActivity;
}
void CBaseMonster::BecomeDead()
2013-08-30 13:34:05 -07:00
{
pev->takedamage = DAMAGE_YES; // don't let autoaim aim at corpses.
// give the corpse half of the monster's original maximum health.
2013-08-30 13:34:05 -07:00
pev->health = pev->max_health / 2;
pev->max_health = 5; // max_health now becomes a counter for how many blood decals the corpse can place.
// make the corpse fly away from the attack vector
pev->movetype = MOVETYPE_TOSS;
//pev->flags &= ~FL_ONGROUND;
//pev->origin.z += 2;
//pev->velocity = g_vecAttackDir * -1;
//pev->velocity = pev->velocity * RANDOM_FLOAT( 300, 400 );
}
bool CBaseMonster::ShouldGibMonster(int iGib)
2013-08-30 13:34:05 -07:00
{
if ((iGib == GIB_NORMAL && pev->health < GIB_HEALTH_VALUE) || (iGib == GIB_ALWAYS))
2021-11-19 13:45:16 +01:00
return true;
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
}
void CBaseMonster::CallGibMonster()
2013-08-30 13:34:05 -07:00
{
bool fade = false;
2013-08-30 13:34:05 -07:00
if (HasHumanGibs())
2013-08-30 13:34:05 -07:00
{
if (CVAR_GET_FLOAT("violence_hgibs") == 0)
2021-11-19 13:45:16 +01:00
fade = true;
2013-08-30 13:34:05 -07:00
}
else if (HasAlienGibs())
2013-08-30 13:34:05 -07:00
{
if (CVAR_GET_FLOAT("violence_agibs") == 0)
2021-11-19 13:45:16 +01:00
fade = true;
2013-08-30 13:34:05 -07:00
}
pev->takedamage = DAMAGE_NO;
pev->solid = SOLID_NOT; // do something with the body. while monster blows up
2013-08-30 13:34:05 -07:00
if (fade)
2013-08-30 13:34:05 -07:00
{
FadeMonster();
}
else
{
pev->effects = EF_NODRAW; // make the model invisible.
GibMonster();
}
pev->deadflag = DEAD_DEAD;
FCheckAITrigger();
// don't let the status bar glitch for players.with <0 health.
if (pev->health < -99)
{
pev->health = 0;
}
if (ShouldFadeOnDeath() && !fade)
2013-08-30 13:34:05 -07:00
UTIL_Remove(this);
}
/*
============
Killed
============
*/
void CBaseMonster::Killed(entvars_t* pevAttacker, int iGib)
2013-08-30 13:34:05 -07:00
{
unsigned int cCount = 0;
bool fDone = false;
2013-08-30 13:34:05 -07:00
if (HasMemory(bits_MEMORY_KILLED))
2013-08-30 13:34:05 -07:00
{
if (ShouldGibMonster(iGib))
2013-08-30 13:34:05 -07:00
CallGibMonster();
return;
}
Remember(bits_MEMORY_KILLED);
2013-08-30 13:34:05 -07:00
// clear the deceased's sound channels.(may have been firing or reloading when killed)
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "common/null.wav", 1, ATTN_NORM);
m_IdealMonsterState = MONSTERSTATE_DEAD;
// Make sure this condition is fired too (TakeDamage breaks out before this happens on death)
SetConditions(bits_COND_LIGHT_DAMAGE);
2013-08-30 13:34:05 -07:00
// tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality.
CBaseEntity* pOwner = CBaseEntity::Instance(pev->owner);
if (pOwner)
2013-08-30 13:34:05 -07:00
{
pOwner->DeathNotice(pev);
2013-08-30 13:34:05 -07:00
}
if (ShouldGibMonster(iGib))
2013-08-30 13:34:05 -07:00
{
CallGibMonster();
return;
}
else if ((pev->flags & FL_MONSTER) != 0)
2013-08-30 13:34:05 -07:00
{
SetTouch(NULL);
2013-08-30 13:34:05 -07:00
BecomeDead();
}
2013-08-30 13:34:05 -07:00
// don't let the status bar glitch for players.with <0 health.
if (pev->health < -99)
{
pev->health = 0;
}
2013-08-30 13:34:05 -07:00
//pev->enemy = ENT( pevAttacker );//why? (sjb)
2013-08-30 13:34:05 -07:00
m_IdealMonsterState = MONSTERSTATE_DEAD;
}
//
// fade out - slowly fades a entity out, then removes it.
//
// DON'T USE ME FOR GIBS AND STUFF IN MULTIPLAYER!
2013-08-30 13:34:05 -07:00
// SET A FUTURE THINK AND A RENDERMODE!!
void CBaseEntity::SUB_StartFadeOut()
2013-08-30 13:34:05 -07:00
{
if (pev->rendermode == kRenderNormal)
{
pev->renderamt = 255;
pev->rendermode = kRenderTransTexture;
}
pev->solid = SOLID_NOT;
pev->avelocity = g_vecZero;
pev->nextthink = gpGlobals->time + 0.1;
SetThink(&CBaseEntity::SUB_FadeOut);
2013-08-30 13:34:05 -07:00
}
void CBaseEntity::SUB_FadeOut()
2013-08-30 13:34:05 -07:00
{
if (pev->renderamt > 7)
2013-08-30 13:34:05 -07:00
{
pev->renderamt -= 7;
pev->nextthink = gpGlobals->time + 0.1;
}
else
2013-08-30 13:34:05 -07:00
{
pev->renderamt = 0;
pev->nextthink = gpGlobals->time + 0.2;
SetThink(&CBaseEntity::SUB_Remove);
2013-08-30 13:34:05 -07:00
}
}
//=========================================================
// WaitTillLand - in order to emit their meaty scent from
// the proper location, gibs should wait until they stop
2013-08-30 13:34:05 -07:00
// bouncing to emit their scent. That's what this function
// does.
//=========================================================
void CGib::WaitTillLand()
2013-08-30 13:34:05 -07:00
{
if (!IsInWorld())
{
UTIL_Remove(this);
2013-08-30 13:34:05 -07:00
return;
}
if (pev->velocity == g_vecZero)
2013-08-30 13:34:05 -07:00
{
SetThink(&CGib::SUB_StartFadeOut);
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time + m_lifeTime;
// If you bleed, you stink!
if (m_bloodColor != DONT_BLEED)
2013-08-30 13:34:05 -07:00
{
// ok, start stinkin!
CSoundEnt::InsertSound(bits_SOUND_MEAT, pev->origin, 384, 25);
2013-08-30 13:34:05 -07:00
}
}
else
{
// wait and check again in another half second.
pev->nextthink = gpGlobals->time + 0.5;
}
}
//
// Gib bounces on the ground or wall, sponges some blood down, too!
//
void CGib::BounceGibTouch(CBaseEntity* pOther)
2013-08-30 13:34:05 -07:00
{
Vector vecSpot;
TraceResult tr;
2013-08-30 13:34:05 -07:00
//if ( RANDOM_LONG(0,1) )
// return;// don't bleed everytime
if ((pev->flags & FL_ONGROUND) != 0)
2013-08-30 13:34:05 -07:00
{
pev->velocity = pev->velocity * 0.9;
pev->angles.x = 0;
pev->angles.z = 0;
pev->avelocity.x = 0;
pev->avelocity.z = 0;
}
else
{
if (g_Language != LANGUAGE_GERMAN && m_cBloodDecals > 0 && m_bloodColor != DONT_BLEED)
2013-08-30 13:34:05 -07:00
{
vecSpot = pev->origin + Vector(0, 0, 8); //move up a bit, and trace down.
UTIL_TraceLine(vecSpot, vecSpot + Vector(0, 0, -24), ignore_monsters, ENT(pev), &tr);
2013-08-30 13:34:05 -07:00
UTIL_BloodDecalTrace(&tr, m_bloodColor);
2013-08-30 13:34:05 -07:00
m_cBloodDecals--;
2013-08-30 13:34:05 -07:00
}
if (m_material != matNone && RANDOM_LONG(0, 2) == 0)
2013-08-30 13:34:05 -07:00
{
float volume;
float zvel = fabs(pev->velocity.z);
2018-09-03 00:01:23 +02:00
volume = 0.8 * V_min(1.0, ((float)zvel) / 450.0);
2013-08-30 13:34:05 -07:00
CBreakable::MaterialSoundRandom(edict(), (Materials)m_material, volume);
2013-08-30 13:34:05 -07:00
}
}
}
//
// Sticky gib puts blood on the wall and stays put.
2013-08-30 13:34:05 -07:00
//
void CGib::StickyGibTouch(CBaseEntity* pOther)
2013-08-30 13:34:05 -07:00
{
Vector vecSpot;
TraceResult tr;
SetThink(&CGib::SUB_Remove);
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time + 10;
if (!FClassnameIs(pOther->pev, "worldspawn"))
2013-08-30 13:34:05 -07:00
{
pev->nextthink = gpGlobals->time;
return;
}
UTIL_TraceLine(pev->origin, pev->origin + pev->velocity * 32, ignore_monsters, ENT(pev), &tr);
2013-08-30 13:34:05 -07:00
UTIL_BloodDecalTrace(&tr, m_bloodColor);
2013-08-30 13:34:05 -07:00
pev->velocity = tr.vecPlaneNormal * -1;
pev->angles = UTIL_VecToAngles(pev->velocity);
pev->velocity = g_vecZero;
2013-08-30 13:34:05 -07:00
pev->avelocity = g_vecZero;
pev->movetype = MOVETYPE_NONE;
}
//
// Throw a chunk
//
void CGib::Spawn(const char* szGibModel)
2013-08-30 13:34:05 -07:00
{
pev->movetype = MOVETYPE_BOUNCE;
pev->friction = 0.55; // deading the bounce a bit
2013-08-30 13:34:05 -07:00
// sometimes an entity inherits the edict from a former piece of glass,
// and will spawn using the same render FX or rendermode! bad!
pev->renderamt = 255;
pev->rendermode = kRenderNormal;
pev->renderfx = kRenderFxNone;
pev->solid = SOLID_SLIDEBOX; /// hopefully this will fix the VELOCITY TOO LOW crap
2013-08-30 13:34:05 -07:00
pev->classname = MAKE_STRING("gib");
SET_MODEL(ENT(pev), szGibModel);
UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0));
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time + 4;
m_lifeTime = 25;
SetThink(&CGib::WaitTillLand);
SetTouch(&CGib::BounceGibTouch);
2013-08-30 13:34:05 -07:00
m_material = matNone;
m_cBloodDecals = 5; // how many blood decals this gib can place (1 per bounce until none remain).
2013-08-30 13:34:05 -07:00
}
// take health
bool CBaseMonster::TakeHealth(float flHealth, int bitsDamageType)
2013-08-30 13:34:05 -07:00
{
if (0 == pev->takedamage)
return false;
2013-08-30 13:34:05 -07:00
// clear out any damage types we healed.
// UNDONE: generic health should not heal any
// UNDONE: time-based damage
m_bitsDamageType &= ~(bitsDamageType & ~DMG_TIMEBASED);
2013-08-30 13:34:05 -07:00
return CBaseEntity::TakeHealth(flHealth, bitsDamageType);
}
/*
============
TakeDamage
The damage is coming from inflictor, but get mad at attacker
This should be the only function that ever reduces health.
bitsDamageType indicates the type of damage sustained, ie: DMG_SHOCK
Time-based damage: only occurs while the monster is within the trigger_hurt.
When a monster is poisoned via an arrow etc it takes all the poison damage at once.
GLOBALS ASSUMED SET: g_iSkillLevel
============
*/
bool CBaseMonster::TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType)
2013-08-30 13:34:05 -07:00
{
float flTake;
Vector vecDir;
2013-08-30 13:34:05 -07:00
if (0 == pev->takedamage)
return false;
2013-08-30 13:34:05 -07:00
if (!IsAlive())
2013-08-30 13:34:05 -07:00
{
return DeadTakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType);
2013-08-30 13:34:05 -07:00
}
if (pev->deadflag == DEAD_NO)
2013-08-30 13:34:05 -07:00
{
// no pain sound during death animation.
PainSound(); // "Ouch!"
2013-08-30 13:34:05 -07:00
}
//!!!LATER - make armor consideration here!
flTake = flDamage;
// set damage type sustained
m_bitsDamageType |= bitsDamageType;
// grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit).
vecDir = Vector(0, 0, 0);
if (!FNullEnt(pevInflictor))
2013-08-30 13:34:05 -07:00
{
CBaseEntity* pInflictor = CBaseEntity::Instance(pevInflictor);
2013-08-30 13:34:05 -07:00
if (pInflictor)
{
vecDir = (pInflictor->Center() - Vector(0, 0, 10) - Center()).Normalize();
2013-08-30 13:34:05 -07:00
vecDir = g_vecAttackDir = vecDir.Normalize();
}
}
// add to the damage total for clients, which will be sent as a single
// message at the end of the frame
// todo: remove after combining shotgun blasts?
if (IsPlayer())
2013-08-30 13:34:05 -07:00
{
if (pevInflictor)
2013-08-30 13:34:05 -07:00
pev->dmg_inflictor = ENT(pevInflictor);
pev->dmg_take += flTake;
// check for godmode or invincibility
if ((pev->flags & FL_GODMODE) != 0)
2013-08-30 13:34:05 -07:00
{
return false;
2013-08-30 13:34:05 -07:00
}
}
// if this is a player, move him around!
if ((!FNullEnt(pevInflictor)) && (pev->movetype == MOVETYPE_WALK) && (!pevAttacker || pevAttacker->solid != SOLID_TRIGGER))
2013-08-30 13:34:05 -07:00
{
pev->velocity = pev->velocity + vecDir * -DamageForce(flDamage);
2013-08-30 13:34:05 -07:00
}
// do the damage
pev->health -= flTake;
2013-08-30 13:34:05 -07:00
// HACKHACK Don't kill monsters in a script. Let them break their scripts first
if (m_MonsterState == MONSTERSTATE_SCRIPT)
2013-08-30 13:34:05 -07:00
{
SetConditions(bits_COND_LIGHT_DAMAGE);
return false;
2013-08-30 13:34:05 -07:00
}
if (pev->health <= 0)
2013-08-30 13:34:05 -07:00
{
g_pevLastInflictor = pevInflictor;
if ((bitsDamageType & DMG_ALWAYSGIB) != 0)
2013-08-30 13:34:05 -07:00
{
Killed(pevAttacker, GIB_ALWAYS);
2013-08-30 13:34:05 -07:00
}
else if ((bitsDamageType & DMG_NEVERGIB) != 0)
2013-08-30 13:34:05 -07:00
{
Killed(pevAttacker, GIB_NEVER);
2013-08-30 13:34:05 -07:00
}
else
{
Killed(pevAttacker, GIB_NORMAL);
2013-08-30 13:34:05 -07:00
}
g_pevLastInflictor = NULL;
return false;
2013-08-30 13:34:05 -07:00
}
// react to the damage (get mad)
if ((pev->flags & FL_MONSTER) != 0 && !FNullEnt(pevAttacker))
2013-08-30 13:34:05 -07:00
{
if ((pevAttacker->flags & (FL_MONSTER | FL_CLIENT)) != 0)
{ // only if the attack was a monster or client!
2013-08-30 13:34:05 -07:00
// enemy's last known position is somewhere down the vector that the attack came from.
if (pevInflictor)
{
if (m_hEnemy == NULL || pevInflictor == m_hEnemy->pev || !HasConditions(bits_COND_SEE_ENEMY))
{
m_vecEnemyLKP = pevInflictor->origin;
}
}
else
{
m_vecEnemyLKP = pev->origin + (g_vecAttackDir * 64);
2013-08-30 13:34:05 -07:00
}
MakeIdealYaw(m_vecEnemyLKP);
2013-08-30 13:34:05 -07:00
// add pain to the conditions
// !!!HACKHACK - fudged for now. Do we want to have a virtual function to determine what is light and
2013-08-30 13:34:05 -07:00
// heavy damage per monster class?
if (flDamage > 0)
2013-08-30 13:34:05 -07:00
{
SetConditions(bits_COND_LIGHT_DAMAGE);
}
if (flDamage >= 20)
2013-08-30 13:34:05 -07:00
{
SetConditions(bits_COND_HEAVY_DAMAGE);
}
}
}
return true;
2013-08-30 13:34:05 -07:00
}
//=========================================================
// DeadTakeDamage - takedamage function called when a monster's
// corpse is damaged.
//=========================================================
bool CBaseMonster::DeadTakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType)
2013-08-30 13:34:05 -07:00
{
Vector vecDir;
2013-08-30 13:34:05 -07:00
// grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit).
vecDir = Vector(0, 0, 0);
if (!FNullEnt(pevInflictor))
2013-08-30 13:34:05 -07:00
{
CBaseEntity* pInflictor = CBaseEntity::Instance(pevInflictor);
2013-08-30 13:34:05 -07:00
if (pInflictor)
{
vecDir = (pInflictor->Center() - Vector(0, 0, 10) - Center()).Normalize();
2013-08-30 13:34:05 -07:00
vecDir = g_vecAttackDir = vecDir.Normalize();
}
}
#if 0 // turn this back on when the bounding box issues are resolved.
2013-08-30 13:34:05 -07:00
pev->flags &= ~FL_ONGROUND;
pev->origin.z += 1;
// let the damage scoot the corpse around a bit.
if ( !FNullEnt(pevInflictor) && (pevAttacker->solid != SOLID_TRIGGER) )
{
pev->velocity = pev->velocity + vecDir * -DamageForce( flDamage );
}
#endif
// kill the corpse if enough damage was done to destroy the corpse and the damage is of a type that is allowed to destroy the corpse.
if ((bitsDamageType & DMG_GIB_CORPSE) != 0)
2013-08-30 13:34:05 -07:00
{
if (pev->health <= flDamage)
2013-08-30 13:34:05 -07:00
{
pev->health = -50;
Killed(pevAttacker, GIB_ALWAYS);
return false;
2013-08-30 13:34:05 -07:00
}
// Accumulate corpse gibbing damage, so you can gib with multiple hits
pev->health -= flDamage * 0.1;
}
return true;
2013-08-30 13:34:05 -07:00
}
float CBaseMonster::DamageForce(float damage)
{
2013-08-30 13:34:05 -07:00
float force = damage * ((32 * 32 * 72.0) / (pev->size.x * pev->size.y * pev->size.z)) * 5;
if (force > 1000.0)
2013-08-30 13:34:05 -07:00
{
force = 1000.0;
}
return force;
}
//
// RadiusDamage - this entity is exploding, or otherwise needs to inflict damage upon entities within a certain range.
//
2013-08-30 13:34:05 -07:00
// only damage ents that can clearly be seen by the explosion!
void RadiusDamage(Vector vecSrc, entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, float flRadius, int iClassIgnore, int bitsDamageType)
2013-08-30 13:34:05 -07:00
{
CBaseEntity* pEntity = NULL;
TraceResult tr;
float flAdjustedDamage, falloff;
Vector vecSpot;
2013-08-30 13:34:05 -07:00
if (0 != flRadius)
2013-08-30 13:34:05 -07:00
falloff = flDamage / flRadius;
else
falloff = 1.0;
const bool bInWater = (UTIL_PointContents(vecSrc) == CONTENTS_WATER);
2013-08-30 13:34:05 -07:00
vecSrc.z += 1; // in case grenade is lying on the ground
2013-08-30 13:34:05 -07:00
if (!pevAttacker)
2013-08-30 13:34:05 -07:00
pevAttacker = pevInflictor;
// iterate on all entities in the vicinity.
while ((pEntity = UTIL_FindEntityInSphere(pEntity, vecSrc, flRadius)) != NULL)
2013-08-30 13:34:05 -07:00
{
if (pEntity->pev->takedamage != DAMAGE_NO)
2013-08-30 13:34:05 -07:00
{
// UNDONE: this should check a damage mask, not an ignore
if (iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore)
{ // houndeyes don't hurt other houndeyes with their attack
2013-08-30 13:34:05 -07:00
continue;
}
// blast's don't tavel into or out of water
if (bInWater && pEntity->pev->waterlevel == 0)
continue;
if (!bInWater && pEntity->pev->waterlevel == 3)
continue;
vecSpot = pEntity->BodyTarget(vecSrc);
2013-08-30 13:34:05 -07:00
UTIL_TraceLine(vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), &tr);
if (tr.flFraction == 1.0 || tr.pHit == pEntity->edict())
{ // the explosion can 'see' this entity, so hurt them!
if (0 != tr.fStartSolid)
2013-08-30 13:34:05 -07:00
{
// if we're stuck inside them, fixup the position and distance
tr.vecEndPos = vecSrc;
tr.flFraction = 0.0;
}
2013-08-30 13:34:05 -07:00
// decrease damage for an ent that's farther from the bomb.
flAdjustedDamage = (vecSrc - tr.vecEndPos).Length() * falloff;
2013-08-30 13:34:05 -07:00
flAdjustedDamage = flDamage - flAdjustedDamage;
if (flAdjustedDamage < 0)
2013-08-30 13:34:05 -07:00
{
flAdjustedDamage = 0;
}
2013-08-30 13:34:05 -07:00
// ALERT( at_console, "hit %s\n", STRING( pEntity->pev->classname ) );
if (tr.flFraction != 1.0)
{
ClearMultiDamage();
pEntity->TraceAttack(pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize(), &tr, bitsDamageType);
ApplyMultiDamage(pevInflictor, pevAttacker);
2013-08-30 13:34:05 -07:00
}
else
{
pEntity->TakeDamage(pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType);
2013-08-30 13:34:05 -07:00
}
}
}
}
}
void CBaseMonster::RadiusDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType)
2013-08-30 13:34:05 -07:00
{
::RadiusDamage(pev->origin, pevInflictor, pevAttacker, flDamage, flDamage * 2.5, iClassIgnore, bitsDamageType);
2013-08-30 13:34:05 -07:00
}
void CBaseMonster::RadiusDamage(Vector vecSrc, entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType)
2013-08-30 13:34:05 -07:00
{
::RadiusDamage(vecSrc, pevInflictor, pevAttacker, flDamage, flDamage * 2.5, iClassIgnore, bitsDamageType);
2013-08-30 13:34:05 -07:00
}
//=========================================================
// CheckTraceHullAttack - expects a length to trace, amount
2013-08-30 13:34:05 -07:00
// of damage to do, and damage type. Returns a pointer to
// the damaged entity in case the monster wishes to do
// other stuff to the victim (punchangle, etc)
//
// Used for many contact-range melee attacks. Bites, claws, etc.
//=========================================================
CBaseEntity* CBaseMonster::CheckTraceHullAttack(float flDist, int iDamage, int iDmgType)
2013-08-30 13:34:05 -07:00
{
TraceResult tr;
if (IsPlayer())
UTIL_MakeVectors(pev->angles);
2013-08-30 13:34:05 -07:00
else
UTIL_MakeAimVectors(pev->angles);
2013-08-30 13:34:05 -07:00
Vector vecStart = pev->origin;
vecStart.z += pev->size.z * 0.5;
Vector vecEnd = vecStart + (gpGlobals->v_forward * flDist);
2013-08-30 13:34:05 -07:00
UTIL_TraceHull(vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr);
if (tr.pHit)
2013-08-30 13:34:05 -07:00
{
CBaseEntity* pEntity = CBaseEntity::Instance(tr.pHit);
2013-08-30 13:34:05 -07:00
if (iDamage > 0)
2013-08-30 13:34:05 -07:00
{
pEntity->TakeDamage(pev, pev, iDamage, iDmgType);
2013-08-30 13:34:05 -07:00
}
return pEntity;
}
return NULL;
}
//=========================================================
// FInViewCone - returns true is the passed ent is in
// the caller's forward view cone. The dot product is performed
// in 2d, making the view cone infinitely tall.
2013-08-30 13:34:05 -07:00
//=========================================================
bool CBaseMonster::FInViewCone(CBaseEntity* pEntity)
2013-08-30 13:34:05 -07:00
{
Vector2D vec2LOS;
float flDot;
2013-08-30 13:34:05 -07:00
UTIL_MakeVectors(pev->angles);
vec2LOS = (pEntity->pev->origin - pev->origin).Make2D();
2013-08-30 13:34:05 -07:00
vec2LOS = vec2LOS.Normalize();
flDot = DotProduct(vec2LOS, gpGlobals->v_forward.Make2D());
2013-08-30 13:34:05 -07:00
if (flDot > m_flFieldOfView)
2013-08-30 13:34:05 -07:00
{
2021-11-19 13:45:16 +01:00
return true;
2013-08-30 13:34:05 -07:00
}
else
{
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
}
}
//=========================================================
// FInViewCone - returns true is the passed vector is in
// the caller's forward view cone. The dot product is performed
// in 2d, making the view cone infinitely tall.
2013-08-30 13:34:05 -07:00
//=========================================================
bool CBaseMonster::FInViewCone(Vector* pOrigin)
2013-08-30 13:34:05 -07:00
{
Vector2D vec2LOS;
float flDot;
2013-08-30 13:34:05 -07:00
UTIL_MakeVectors(pev->angles);
vec2LOS = (*pOrigin - pev->origin).Make2D();
2013-08-30 13:34:05 -07:00
vec2LOS = vec2LOS.Normalize();
flDot = DotProduct(vec2LOS, gpGlobals->v_forward.Make2D());
2013-08-30 13:34:05 -07:00
if (flDot > m_flFieldOfView)
2013-08-30 13:34:05 -07:00
{
2021-11-19 13:45:16 +01:00
return true;
2013-08-30 13:34:05 -07:00
}
else
{
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
}
}
//=========================================================
// FVisible - returns true if a line can be traced from
// the caller's eyes to the target
//=========================================================
bool CBaseEntity::FVisible(CBaseEntity* pEntity)
2013-08-30 13:34:05 -07:00
{
TraceResult tr;
Vector vecLookerOrigin;
Vector vecTargetOrigin;
if (FBitSet(pEntity->pev->flags, FL_NOTARGET))
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
// don't look through water
if ((pev->waterlevel != 3 && pEntity->pev->waterlevel == 3) || (pev->waterlevel == 3 && pEntity->pev->waterlevel == 0))
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
vecLookerOrigin = pev->origin + pev->view_ofs; //look through the caller's 'eyes'
2013-08-30 13:34:05 -07:00
vecTargetOrigin = pEntity->EyePosition();
UTIL_TraceLine(vecLookerOrigin, vecTargetOrigin, ignore_monsters, ignore_glass, ENT(pev) /*pentIgnore*/, &tr);
2013-08-30 13:34:05 -07:00
if (tr.flFraction != 1.0)
{
return false; // Line of sight is not established
2013-08-30 13:34:05 -07:00
}
else
{
return true; // line of sight is valid.
2013-08-30 13:34:05 -07:00
}
}
//=========================================================
// FVisible - returns true if a line can be traced from
// the caller's eyes to the target vector
//=========================================================
bool CBaseEntity::FVisible(const Vector& vecOrigin)
2013-08-30 13:34:05 -07:00
{
TraceResult tr;
Vector vecLookerOrigin;
vecLookerOrigin = EyePosition(); //look through the caller's 'eyes'
UTIL_TraceLine(vecLookerOrigin, vecOrigin, ignore_monsters, ignore_glass, ENT(pev) /*pentIgnore*/, &tr);
2013-08-30 13:34:05 -07:00
if (tr.flFraction != 1.0)
{
return false; // Line of sight is not established
2013-08-30 13:34:05 -07:00
}
else
{
return true; // line of sight is valid.
2013-08-30 13:34:05 -07:00
}
}
/*
================
TraceAttack
================
*/
void CBaseEntity::TraceAttack(entvars_t* pevAttacker, float flDamage, Vector vecDir, TraceResult* ptr, int bitsDamageType)
2013-08-30 13:34:05 -07:00
{
Vector vecOrigin = ptr->vecEndPos - vecDir * 4;
if (0 != pev->takedamage)
2013-08-30 13:34:05 -07:00
{
AddMultiDamage(pevAttacker, this, flDamage, bitsDamageType);
2013-08-30 13:34:05 -07:00
int blood = BloodColor();
if (blood != DONT_BLEED)
2013-08-30 13:34:05 -07:00
{
SpawnBlood(vecOrigin, blood, flDamage); // a little surface blood.
TraceBleed(flDamage, vecDir, ptr, bitsDamageType);
2013-08-30 13:34:05 -07:00
}
}
}
/*
//=========================================================
// TraceAttack
//=========================================================
void CBaseMonster::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
{
Vector vecOrigin = ptr->vecEndPos - vecDir * 4;
ALERT ( at_console, "%d\n", ptr->iHitgroup );
if ( pev->takedamage )
{
AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType );
int blood = BloodColor();
if ( blood != DONT_BLEED )
{
SpawnBlood(vecOrigin, blood, flDamage);// a little surface blood.
}
}
}
*/
//=========================================================
// TraceAttack
//=========================================================
void CBaseMonster::TraceAttack(entvars_t* pevAttacker, float flDamage, Vector vecDir, TraceResult* ptr, int bitsDamageType)
2013-08-30 13:34:05 -07:00
{
if (0 != pev->takedamage)
2013-08-30 13:34:05 -07:00
{
m_LastHitGroup = ptr->iHitgroup;
switch (ptr->iHitgroup)
2013-08-30 13:34:05 -07:00
{
case HITGROUP_GENERIC:
break;
case HITGROUP_HEAD:
flDamage *= gSkillData.monHead;
break;
case HITGROUP_CHEST:
flDamage *= gSkillData.monChest;
break;
case HITGROUP_STOMACH:
flDamage *= gSkillData.monStomach;
break;
case HITGROUP_LEFTARM:
case HITGROUP_RIGHTARM:
flDamage *= gSkillData.monArm;
break;
case HITGROUP_LEFTLEG:
case HITGROUP_RIGHTLEG:
flDamage *= gSkillData.monLeg;
break;
default:
break;
}
SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage); // a little surface blood.
TraceBleed(flDamage, vecDir, ptr, bitsDamageType);
AddMultiDamage(pevAttacker, this, flDamage, bitsDamageType);
2013-08-30 13:34:05 -07:00
}
}
/*
================
FireBullets
Go to the trouble of combining multiple pellets into a single damage call.
This version is used by Monsters.
================
*/
void CBaseEntity::FireBullets(unsigned int cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t* pevAttacker)
2013-08-30 13:34:05 -07:00
{
static int tracerCount;
TraceResult tr;
Vector vecRight = gpGlobals->v_right;
Vector vecUp = gpGlobals->v_up;
if (pevAttacker == NULL)
pevAttacker = pev; // the default attacker is ourselves
2013-08-30 13:34:05 -07:00
ClearMultiDamage();
gMultiDamage.type = DMG_BULLET | DMG_NEVERGIB;
for (unsigned int iShot = 1; iShot <= cShots; iShot++)
2013-08-30 13:34:05 -07:00
{
// get circular gaussian spread
float x, y, z;
do
{
x = RANDOM_FLOAT(-0.5, 0.5) + RANDOM_FLOAT(-0.5, 0.5);
y = RANDOM_FLOAT(-0.5, 0.5) + RANDOM_FLOAT(-0.5, 0.5);
z = x * x + y * y;
2013-08-30 13:34:05 -07:00
} while (z > 1);
Vector vecDir = vecDirShooting +
x * vecSpread.x * vecRight +
y * vecSpread.y * vecUp;
Vector vecEnd;
vecEnd = vecSrc + vecDir * flDistance;
UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev) /*pentIgnore*/, &tr);
2013-08-30 13:34:05 -07:00
if (iTracerFreq != 0 && (tracerCount++ % iTracerFreq) == 0)
{
Vector vecTracerSrc;
if (IsPlayer())
{ // adjust tracer position for player
vecTracerSrc = vecSrc + Vector(0, 0, -4) + gpGlobals->v_right * 2 + gpGlobals->v_forward * 16;
2013-08-30 13:34:05 -07:00
}
else
{
vecTracerSrc = vecSrc;
}
switch (iBulletType)
2013-08-30 13:34:05 -07:00
{
case BULLET_MONSTER_MP5:
case BULLET_MONSTER_9MM:
case BULLET_MONSTER_12MM:
default:
MESSAGE_BEGIN(MSG_PAS, SVC_TEMPENTITY, vecTracerSrc);
WRITE_BYTE(TE_TRACER);
WRITE_COORD(vecTracerSrc.x);
WRITE_COORD(vecTracerSrc.y);
WRITE_COORD(vecTracerSrc.z);
WRITE_COORD(tr.vecEndPos.x);
WRITE_COORD(tr.vecEndPos.y);
WRITE_COORD(tr.vecEndPos.z);
2013-08-30 13:34:05 -07:00
MESSAGE_END();
break;
}
}
// do damage, paint decals
if (tr.flFraction != 1.0)
{
CBaseEntity* pEntity = CBaseEntity::Instance(tr.pHit);
2013-08-30 13:34:05 -07:00
if (0 != iDamage)
2013-08-30 13:34:05 -07:00
{
pEntity->TraceAttack(pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB));
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot(&tr, iBulletType);
}
else
switch (iBulletType)
{
case BULLET_PLAYER_BUCKSHOT:
// make distance based!
pEntity->TraceAttack(pevAttacker, gSkillData.plrDmgBuckshot, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot(&tr, iBulletType);
break;
2013-08-30 13:34:05 -07:00
default:
case BULLET_MONSTER_9MM:
pEntity->TraceAttack(pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET);
2013-08-30 13:34:05 -07:00
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot(&tr, iBulletType);
2013-08-30 13:34:05 -07:00
break;
case BULLET_MONSTER_MP5:
pEntity->TraceAttack(pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET);
2013-08-30 13:34:05 -07:00
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot(&tr, iBulletType);
2013-08-30 13:34:05 -07:00
break;
case BULLET_MONSTER_12MM:
pEntity->TraceAttack(pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot(&tr, iBulletType);
break;
case BULLET_NONE: // FIX
pEntity->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
// only decal glass
if (!FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0)
{
UTIL_DecalTrace(&tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0, 2));
}
break;
}
2013-08-30 13:34:05 -07:00
}
// make bullet trails
UTIL_BubbleTrail(vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0);
2013-08-30 13:34:05 -07:00
}
ApplyMultiDamage(pev, pevAttacker);
}
/*
================
FireBullets
Go to the trouble of combining multiple pellets into a single damage call.
This version is used by Players, uses the random seed generator to sync client and server side shots.
================
*/
Vector CBaseEntity::FireBulletsPlayer(unsigned int cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t* pevAttacker, int shared_rand)
2013-08-30 13:34:05 -07:00
{
static int tracerCount;
TraceResult tr;
Vector vecRight = gpGlobals->v_right;
Vector vecUp = gpGlobals->v_up;
float x = 0, y = 0, z;
2013-08-30 13:34:05 -07:00
if (pevAttacker == NULL)
pevAttacker = pev; // the default attacker is ourselves
2013-08-30 13:34:05 -07:00
ClearMultiDamage();
gMultiDamage.type = DMG_BULLET | DMG_NEVERGIB;
for (unsigned int iShot = 1; iShot <= cShots; iShot++)
2013-08-30 13:34:05 -07:00
{
//Use player's random seed.
// get circular gaussian spread
x = UTIL_SharedRandomFloat(shared_rand + iShot, -0.5, 0.5) + UTIL_SharedRandomFloat(shared_rand + (1 + iShot), -0.5, 0.5);
y = UTIL_SharedRandomFloat(shared_rand + (2 + iShot), -0.5, 0.5) + UTIL_SharedRandomFloat(shared_rand + (3 + iShot), -0.5, 0.5);
2013-08-30 13:34:05 -07:00
z = x * x + y * y;
Vector vecDir = vecDirShooting +
x * vecSpread.x * vecRight +
y * vecSpread.y * vecUp;
Vector vecEnd;
vecEnd = vecSrc + vecDir * flDistance;
UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev) /*pentIgnore*/, &tr);
2013-08-30 13:34:05 -07:00
// do damage, paint decals
if (tr.flFraction != 1.0)
{
CBaseEntity* pEntity = CBaseEntity::Instance(tr.pHit);
2013-08-30 13:34:05 -07:00
if (0 != iDamage)
2013-08-30 13:34:05 -07:00
{
pEntity->TraceAttack(pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB));
2013-08-30 13:34:05 -07:00
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot(&tr, iBulletType);
}
else
switch (iBulletType)
2013-08-30 13:34:05 -07:00
{
default:
case BULLET_PLAYER_9MM:
pEntity->TraceAttack(pevAttacker, gSkillData.plrDmg9MM, vecDir, &tr, DMG_BULLET);
break;
case BULLET_PLAYER_MP5:
pEntity->TraceAttack(pevAttacker, gSkillData.plrDmgMP5, vecDir, &tr, DMG_BULLET);
break;
case BULLET_PLAYER_BUCKSHOT:
// make distance based!
pEntity->TraceAttack(pevAttacker, gSkillData.plrDmgBuckshot, vecDir, &tr, DMG_BULLET);
break;
case BULLET_PLAYER_357:
pEntity->TraceAttack(pevAttacker, gSkillData.plrDmg357, vecDir, &tr, DMG_BULLET);
break;
case BULLET_NONE: // FIX
pEntity->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
// only decal glass
if (!FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0)
{
UTIL_DecalTrace(&tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0, 2));
}
2013-08-30 13:34:05 -07:00
break;
}
2013-08-30 13:34:05 -07:00
}
// make bullet trails
UTIL_BubbleTrail(vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0);
2013-08-30 13:34:05 -07:00
}
ApplyMultiDamage(pev, pevAttacker);
return Vector(x * vecSpread.x, y * vecSpread.y, 0.0);
2013-08-30 13:34:05 -07:00
}
void CBaseEntity::TraceBleed(float flDamage, Vector vecDir, TraceResult* ptr, int bitsDamageType)
2013-08-30 13:34:05 -07:00
{
if (BloodColor() == DONT_BLEED)
return;
2013-08-30 13:34:05 -07:00
if (flDamage == 0)
return;
if ((bitsDamageType & (DMG_CRUSH | DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB | DMG_MORTAR)) == 0)
2013-08-30 13:34:05 -07:00
return;
// make blood decal on the wall!
2013-08-30 13:34:05 -07:00
TraceResult Bloodtr;
Vector vecTraceDir;
2013-08-30 13:34:05 -07:00
float flNoise;
int cCount;
int i;
/*
2013-08-30 13:34:05 -07:00
if ( !IsAlive() )
{
// dealing with a dead monster.
if ( pev->max_health <= 0 )
{
// no blood decal for a monster that has already decalled its limit.
return;
}
else
{
pev->max_health--;
}
}
*/
if (flDamage < 10)
{
flNoise = 0.1;
cCount = 1;
}
else if (flDamage < 25)
{
flNoise = 0.2;
cCount = 2;
}
else
{
flNoise = 0.3;
cCount = 4;
}
for (i = 0; i < cCount; i++)
2013-08-30 13:34:05 -07:00
{
vecTraceDir = vecDir * -1; // trace in the opposite direction the shot came from (the direction the shot is going)
2013-08-30 13:34:05 -07:00
vecTraceDir.x += RANDOM_FLOAT(-flNoise, flNoise);
vecTraceDir.y += RANDOM_FLOAT(-flNoise, flNoise);
vecTraceDir.z += RANDOM_FLOAT(-flNoise, flNoise);
2013-08-30 13:34:05 -07:00
UTIL_TraceLine(ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * -172, ignore_monsters, ENT(pev), &Bloodtr);
2013-08-30 13:34:05 -07:00
if (Bloodtr.flFraction != 1.0)
2013-08-30 13:34:05 -07:00
{
UTIL_BloodDecalTrace(&Bloodtr, BloodColor());
2013-08-30 13:34:05 -07:00
}
}
}
//=========================================================
//=========================================================
void CBaseMonster::MakeDamageBloodDecal(int cCount, float flNoise, TraceResult* ptr, const Vector& vecDir)
2013-08-30 13:34:05 -07:00
{
// make blood decal on the wall!
2013-08-30 13:34:05 -07:00
TraceResult Bloodtr;
Vector vecTraceDir;
2013-08-30 13:34:05 -07:00
int i;
if (!IsAlive())
2013-08-30 13:34:05 -07:00
{
// dealing with a dead monster.
if (pev->max_health <= 0)
2013-08-30 13:34:05 -07:00
{
// no blood decal for a monster that has already decalled its limit.
return;
2013-08-30 13:34:05 -07:00
}
else
{
pev->max_health--;
}
}
for (i = 0; i < cCount; i++)
2013-08-30 13:34:05 -07:00
{
vecTraceDir = vecDir;
vecTraceDir.x += RANDOM_FLOAT(-flNoise, flNoise);
vecTraceDir.y += RANDOM_FLOAT(-flNoise, flNoise);
vecTraceDir.z += RANDOM_FLOAT(-flNoise, flNoise);
2013-08-30 13:34:05 -07:00
UTIL_TraceLine(ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * 172, ignore_monsters, ENT(pev), &Bloodtr);
2013-08-30 13:34:05 -07:00
/*
2013-08-30 13:34:05 -07:00
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_SHOWLINE);
WRITE_COORD( ptr->vecEndPos.x );
WRITE_COORD( ptr->vecEndPos.y );
WRITE_COORD( ptr->vecEndPos.z );
WRITE_COORD( Bloodtr.vecEndPos.x );
WRITE_COORD( Bloodtr.vecEndPos.y );
WRITE_COORD( Bloodtr.vecEndPos.z );
MESSAGE_END();
*/
if (Bloodtr.flFraction != 1.0)
2013-08-30 13:34:05 -07:00
{
UTIL_BloodDecalTrace(&Bloodtr, BloodColor());
2013-08-30 13:34:05 -07:00
}
}
}