Start making monstermod aware of normal HL monsters.

This is still heavily experimental and its use is not yet recommended.
This commit is contained in:
Giegue
2023-02-15 21:19:57 -03:00
parent 5830ac7908
commit 6e1081d793
6 changed files with 368 additions and 141 deletions

View File

@@ -134,6 +134,8 @@ public:
virtual void MonsterThink( void ); virtual void MonsterThink( void );
void EXPORT CallMonsterThink( void ) { this->MonsterThink(); } void EXPORT CallMonsterThink( void ) { this->MonsterThink(); }
virtual int IRelationship ( CMBaseEntity *pTarget ); virtual int IRelationship ( CMBaseEntity *pTarget );
virtual int IRelationship ( int iTargetClass );
int IRelationshipByClass ( int iClass );
virtual void MonsterInit ( void ); virtual void MonsterInit ( void );
virtual void MonsterInitDead( void ); // Call after animation/pose is set up virtual void MonsterInitDead( void ); // Call after animation/pose is set up
virtual void BecomeDead( void ); virtual void BecomeDead( void );
@@ -1655,7 +1657,16 @@ public:
void HandleAnimEvent(MonsterEvent_t *pEvent); void HandleAnimEvent(MonsterEvent_t *pEvent);
void SetActivity(Activity NewActivity); void SetActivity(Activity NewActivity);
Schedule_t *GetSchedule( void );
Schedule_t *GetScheduleOfType ( int Type );
BOOL CheckRangeAttack1( float flDot, float flDist );
BOOL CheckMeleeAttack1( float flDot, float flDist );
BOOL CheckRangeAttack2( float flDot, float flDist );
void Minigun(void); void Minigun(void);
CUSTOM_SCHEDULES
float m_flMinigunSpinTime; float m_flMinigunSpinTime;
}; };
@@ -1695,4 +1706,8 @@ public:
int m_iBodyGibs; int m_iBodyGibs;
}; };
//=========================================================
// Looking for Stukabat? It's located in cmflyingmonster.h
//=========================================================
#endif // BASEMONSTER_H #endif // BASEMONSTER_H

View File

@@ -1060,8 +1060,8 @@ void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacke
{ {
if ( pEntity->v.takedamage != DAMAGE_NO ) if ( pEntity->v.takedamage != DAMAGE_NO )
{ {
if (UTIL_IsPlayer(pEntity)) if (UTIL_IsPlayer(pEntity))
{ {
// blast's don't tavel into or out of water // blast's don't tavel into or out of water
if (bInWater && pEntity->v.waterlevel == 0) if (bInWater && pEntity->v.waterlevel == 0)
continue; continue;
@@ -1100,11 +1100,11 @@ void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacke
else else
{ {
UTIL_TakeDamage ( pEntity, pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); UTIL_TakeDamage ( pEntity, pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
} }
} }
} }
else if (pEntity->v.euser4 != NULL) else if (pEntity->v.euser4 != NULL)
{ {
// UNDONE: this should check a damage mask, not an ignore // UNDONE: this should check a damage mask, not an ignore
CMBaseEntity *pMonster = GetClassPtr((CMBaseEntity *)VARS(pEntity)); CMBaseEntity *pMonster = GetClassPtr((CMBaseEntity *)VARS(pEntity));
@@ -1151,7 +1151,60 @@ void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacke
else else
{ {
pMonster->TakeDamage ( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); pMonster->TakeDamage ( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
} }
}
}
else if (!UTIL_IsPlayer(pEntity))
{
// I'm doing really bad copypastes instead of making the code clean!
// Remind me to refactor this, this is not how this is supposed to be written!
// -Giegue
edict_t *pMonster = pEntity;
if ( iClassIgnore != CLASS_NONE && pMonster->v.iuser4 == iClassIgnore )
{// houndeyes don't hurt other houndeyes with their attack
continue;
}
// blast's don't tavel into or out of water
if (bInWater && pEntity->v.waterlevel == 0)
continue;
if (!bInWater && pEntity->v.waterlevel == 3)
continue;
vecSpot = UTIL_BodyTarget( pMonster, vecSrc );
UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), &tr );
if ( tr.flFraction == 1.0 || tr.pHit == pEntity )
{// the explosion can 'see' this entity, so hurt them!
if (tr.fStartSolid)
{
// if we're stuck inside them, fixup the position and distance
tr.vecEndPos = vecSrc;
tr.flFraction = 0.0;
}
// decrease damage for an ent that's farther from the bomb.
flAdjustedDamage = ( vecSrc - tr.vecEndPos ).Length() * falloff;
flAdjustedDamage = flDamage - flAdjustedDamage;
if ( flAdjustedDamage < 0 )
{
flAdjustedDamage = 0;
}
// ALERT( at_console, "hit %s\n", STRING( pEntity->pev->classname ) );
if (tr.flFraction != 1.0)
{
ClearMultiDamage( );
UTIL_TraceAttack( pMonster, pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize( ), &tr, bitsDamageType );
ApplyMultiDamage( pevInflictor, pevAttacker );
}
else
{
UTIL_TakeDamageExternal( pMonster, pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
}
} }
} }
} }
@@ -1207,6 +1260,8 @@ edict_t* CMBaseMonster :: CheckTraceHullAttack( float flDist, int iDamage, int i
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TakeDamage( pev, pev, iDamage, iDmgType ); pMonster->TakeDamage( pev, pev, iDamage, iDmgType );
} }
else if (!UTIL_IsPlayer(pEntity))
UTIL_TakeDamageExternal( pEntity, pev, pev, iDamage, iDmgType );
} }
return pEntity; return pEntity;
@@ -1386,8 +1441,8 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
// do damage, paint decals // do damage, paint decals
if (tr.flFraction != 1.0) if (tr.flFraction != 1.0)
{ {
if (UTIL_IsPlayer(tr.pHit)) // is this a player? if (UTIL_IsPlayer(tr.pHit)) // is this a player?
{ {
edict_t *pPlayer = tr.pHit; edict_t *pPlayer = tr.pHit;
if ( iDamage ) if ( iDamage )
@@ -1396,60 +1451,63 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType ); DecalGunshot( &tr, iBulletType );
} }
else switch(iBulletType) else
{ {
default: switch(iBulletType)
case BULLET_MONSTER_9MM:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_MP5:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_12MM:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET);
if ( !tracer )
{ {
default:
case BULLET_MONSTER_9MM:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType ); DecalGunshot( &tr, iBulletType );
}
break;
case BULLET_MONSTER_762:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_357:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_NONE: // FIX
UTIL_TraceAttack(pPlayer, 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; break;
case BULLET_MONSTER_MP5:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_12MM:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET);
if ( !tracer )
{
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
}
break;
case BULLET_MONSTER_762:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_357:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_NONE: // FIX
UTIL_TraceAttack(pPlayer, 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;
}
} }
} }
else if (tr.pHit->v.euser4 != NULL) else if (tr.pHit->v.euser4 != NULL) // monstermod monster
{ {
CMBaseEntity *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit)); CMBaseEntity *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
if ( iDamage ) if ( iDamage )
@@ -1458,56 +1516,124 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType ); DecalGunshot( &tr, iBulletType );
} }
else switch(iBulletType) else
{ {
default: switch(iBulletType)
case BULLET_MONSTER_9MM:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_MP5:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_12MM:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET);
if ( !tracer )
{ {
default:
case BULLET_MONSTER_9MM:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType ); DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_MP5:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_12MM:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET);
if ( !tracer )
{
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
}
break;
case BULLET_MONSTER_762:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_357:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_NONE: // FIX
pMonster->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;
} }
break; }
}
case BULLET_MONSTER_762: else if (!UTIL_IsPlayer(tr.pHit)) // normal game monster
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET); {
edict_t *pMonster = tr.pHit;
if ( iDamage )
{
UTIL_TraceAttack(pMonster, pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) );
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType ); DecalGunshot( &tr, iBulletType );
}
break; else
{
case BULLET_MONSTER_357: switch(iBulletType)
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_NONE: // FIX
pMonster->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) ); default:
} case BULLET_MONSTER_9MM:
UTIL_TraceAttack(pMonster, pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break; break;
case BULLET_MONSTER_MP5:
UTIL_TraceAttack(pMonster, pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_12MM:
UTIL_TraceAttack(pMonster, pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET);
if ( !tracer )
{
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
}
break;
case BULLET_MONSTER_762:
UTIL_TraceAttack(pMonster, pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_357:
UTIL_TraceAttack(pMonster, pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_NONE: // FIX
UTIL_TraceAttack(pMonster, 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;
}
} }
} }
} }

View File

@@ -120,7 +120,7 @@ void CMBaseMonster :: Look ( int iDistance )
// DON'T let visibility information from last frame sit around! // DON'T let visibility information from last frame sit around!
ClearConditions(bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR | bits_COND_SEE_NEMESIS | bits_COND_SEE_CLIENT); ClearConditions(bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR | bits_COND_SEE_NEMESIS | bits_COND_SEE_CLIENT);
m_edictList_count = 0; m_edictList_count = 0;
edict_t *pSightEnt = NULL;// the current visible entity that we're dealing with edict_t *pSightEnt = NULL;// the current visible entity that we're dealing with
@@ -137,23 +137,20 @@ void CMBaseMonster :: Look ( int iDistance )
{ {
pSightEnt = pList[i]; pSightEnt = pList[i];
// !!!temporarily only considering other monsters and clients, don't see prisoners // !!!temporarily only considering other monsters and clients, don't see prisoners
if ( pSightEnt != this->edict() && if ( pSightEnt != this->edict() && !FBitSet( pSightEnt->v.spawnflags, SF_MONSTER_PRISONER ) && pSightEnt->v.health > 0 )
!FBitSet( pSightEnt->v.spawnflags, SF_MONSTER_PRISONER ) &&
pSightEnt->v.health > 0 )
{ {
// is this a player AND are they alive? // is this a player AND are they alive?
if (UTIL_IsPlayer(pSightEnt) && UTIL_IsAlive(pSightEnt)) if (UTIL_IsPlayer(pSightEnt) && UTIL_IsAlive(pSightEnt))
{ {
// the looker will want to consider this entity // the looker will want to consider this entity
// don't check anything else about an entity that can't be seen. // don't check anything else about an entity that can't be seen.
if ( UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && if ( UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && !FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
!FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
{ {
m_edictList[m_edictList_count] = pSightEnt; m_edictList[m_edictList_count] = pSightEnt;
m_edictList_count++; m_edictList_count++;
// if we see a client, remember that (mostly for scripted AI) // if we see a client, remember that (mostly for scripted AI)
iSighted |= bits_COND_SEE_CLIENT; iSighted |= bits_COND_SEE_CLIENT;
// is this monster NOT a scientist? // is this monster NOT a scientist?
if (strcmp(STRING(pev->model), "models/scientist.mdl") != 0) if (strcmp(STRING(pev->model), "models/scientist.mdl") != 0)
@@ -166,19 +163,19 @@ void CMBaseMonster :: Look ( int iDistance )
iSighted |= bits_COND_SEE_ENEMY; iSighted |= bits_COND_SEE_ENEMY;
} }
} }
} }
} }
else if (pSightEnt->v.euser4 != NULL) else if (pSightEnt->v.euser4 != NULL)
{ {
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pSightEnt)); /* MonsterMod monster looking at another MonsterMod monster */
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pSightEnt));
// the looker will want to consider this entity // the looker will want to consider this entity
// don't check anything else about an entity that can't be seen, or an entity that you don't care about. // don't check anything else about an entity that can't be seen, or an entity that you don't care about.
if ( IRelationship( pMonster ) != R_NO && UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && if ( IRelationship( pMonster ) != R_NO && UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && !FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
!FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
{ {
m_edictList[m_edictList_count] = pSightEnt; m_edictList[m_edictList_count] = pSightEnt;
m_edictList_count++; m_edictList_count++;
if ( ENT(pMonster->pev) == m_hEnemy ) if ( ENT(pMonster->pev) == m_hEnemy )
{ {
@@ -210,6 +207,47 @@ void CMBaseMonster :: Look ( int iDistance )
} }
} }
} }
else if (!UTIL_IsPlayer(pSightEnt))
{
/* MonsterMod monster looking at a NON-MonsterMod monster */
// the looker will want to consider this entity
// don't check anything else about an entity that can't be seen, or an entity that you don't care about.
if ( IRelationship( pSightEnt->v.iuser4 ) != R_NO && UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && !FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
{
m_edictList[m_edictList_count] = pSightEnt;
m_edictList_count++;
if ( pSightEnt == m_hEnemy )
{
// we know this ent is visible, so if it also happens to be our enemy, store that now.
iSighted |= bits_COND_SEE_ENEMY;
}
// don't add the Enemy's relationship to the conditions. We only want to worry about conditions when
// we see monsters other than the Enemy.
switch ( IRelationship ( pSightEnt->v.iuser4 ) )
{
case R_NM:
iSighted |= bits_COND_SEE_NEMESIS;
break;
case R_HT:
iSighted |= bits_COND_SEE_HATE;
break;
case R_DL:
iSighted |= bits_COND_SEE_DISLIKE;
break;
case R_FR:
iSighted |= bits_COND_SEE_FEAR;
break;
case R_AL:
break;
default:
ALERT ( at_aiconsole, "%s can't assess %s\n", STRING(pev->classname), STRING(pSightEnt->v.classname ) );
break;
}
}
}
} }
} }
} }
@@ -1743,6 +1781,9 @@ void CMBaseMonster :: StartMonster ( void )
SetActivity( ACT_IDLE ); SetActivity( ACT_IDLE );
ChangeSchedule( GetScheduleOfType( SCHED_WAIT_TRIGGER ) ); ChangeSchedule( GetScheduleOfType( SCHED_WAIT_TRIGGER ) );
} }
// Notify normal game engine of monster classify
pev->iuser4 = Classify();
} }
@@ -1784,6 +1825,14 @@ int CMBaseMonster::TaskIsRunning( void )
// relationship between two types of monster. // relationship between two types of monster.
//========================================================= //=========================================================
int CMBaseMonster::IRelationship ( CMBaseEntity *pTarget ) int CMBaseMonster::IRelationship ( CMBaseEntity *pTarget )
{
return IRelationshipByClass( pTarget->Classify() );
}
int CMBaseMonster::IRelationship ( int iTargetClass )
{
return IRelationshipByClass( iTargetClass );
}
int CMBaseMonster::IRelationshipByClass ( int iClass )
{ {
static int iEnemy[16][16] = static int iEnemy[16][16] =
{ // NONE MACH PLYR HPASS HMIL AMIL APASS AMONST APREY APRED INSECT PLRALY PBWPN ABWPN RXPIT RXSHK { // NONE MACH PLYR HPASS HMIL AMIL APASS AMONST APREY APRED INSECT PLRALY PBWPN ABWPN RXPIT RXSHK
@@ -1805,7 +1854,7 @@ int CMBaseMonster::IRelationship ( CMBaseEntity *pTarget )
/*RXSHOCKTRP*/ { R_NO ,R_DL ,R_HT ,R_DL ,R_HT ,R_HT ,R_DL ,R_NO ,R_NO ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_AL, R_AL } /*RXSHOCKTRP*/ { R_NO ,R_DL ,R_HT ,R_DL ,R_HT ,R_HT ,R_DL ,R_NO ,R_NO ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_AL, R_AL }
}; };
return iEnemy[ Classify() ][ pTarget->Classify() ]; return iEnemy[ Classify() ][ iClass ];
} }
//========================================================= //=========================================================
@@ -2019,38 +2068,39 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void )
int iNearest; int iNearest;
int iDist; int iDist;
int iBestRelationship; int iBestRelationship;
edict_t *pReturn; edict_t *pReturn;
edict_t *pEnt; edict_t *pEnt;
int edictList_index = 0; int edictList_index = 0;
iNearest = 8192;// so first visible entity will become the closest. iNearest = 8192;// so first visible entity will become the closest.
iBestRelationship = R_NO; iBestRelationship = R_NO;
pReturn = NULL; pReturn = NULL;
while (edictList_index < m_edictList_count) while (edictList_index < m_edictList_count)
{ {
pEnt = m_edictList[edictList_index]; pEnt = m_edictList[edictList_index];
if ( UTIL_IsPlayer(pEnt) ) if ( UTIL_IsPlayer(pEnt) )
{ {
// it's a player... // it's a player...
iDist = ( pEnt->v.origin - pev->origin ).Length(); iDist = ( pEnt->v.origin - pev->origin ).Length();
if ( iDist <= iNearest ) if ( iDist <= iNearest )
{ {
iNearest = iDist; iNearest = iDist;
iBestRelationship = R_NM; // player is always nemsis iBestRelationship = R_NM; // player is always nemesis
pReturn = pEnt; pReturn = pEnt;
} }
} }
else if (pEnt->v.euser4 != NULL) else if (pEnt->v.euser4 != NULL)
{ {
// it's a monstermod monster...
CMBaseMonster *pNextEnt = GetClassPtr((CMBaseMonster *)VARS(pEnt)); CMBaseMonster *pNextEnt = GetClassPtr((CMBaseMonster *)VARS(pEnt));
if ( pNextEnt->IsAlive() ) if ( pNextEnt->IsAlive() )
{ {
if ( IRelationship( pNextEnt) > iBestRelationship ) if ( IRelationship( pNextEnt ) > iBestRelationship )
{ {
// this entity is disliked MORE than the entity that we // this entity is disliked MORE than the entity that we
// currently think is the best visible enemy. No need to do // currently think is the best visible enemy. No need to do
@@ -2059,13 +2109,13 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void )
iNearest = ( pNextEnt->pev->origin - pev->origin ).Length(); iNearest = ( pNextEnt->pev->origin - pev->origin ).Length();
pReturn = pEnt; pReturn = pEnt;
} }
else if ( IRelationship( pNextEnt) == iBestRelationship ) else if ( IRelationship( pNextEnt ) == iBestRelationship )
{ {
// this entity is disliked just as much as the entity that // this entity is disliked just as much as the entity that
// we currently think is the best visible enemy, so we only // we currently think is the best visible enemy, so we only
// get mad at it if it is closer. // get mad at it if it is closer.
iDist = ( pNextEnt->pev->origin - pev->origin ).Length(); iDist = ( pNextEnt->pev->origin - pev->origin ).Length();
if ( iDist <= iNearest ) if ( iDist <= iNearest )
{ {
iNearest = iDist; iNearest = iDist;
@@ -2074,9 +2124,34 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void )
} }
} }
} }
} }
else if (!UTIL_IsPlayer(pEnt))
{
// it's a game default monster...
if ( UTIL_IsAlive(pEnt) )
{
//repeat2
if ( IRelationship( pEnt->v.iuser4 ) > iBestRelationship )
{
iBestRelationship = IRelationship( pEnt->v.iuser4 );
iNearest = ( pEnt->v.origin - pev->origin ).Length();
pReturn = pEnt;
}
else if ( IRelationship( pEnt->v.iuser4 ) == iBestRelationship )
{
iDist = ( pEnt->v.origin - pev->origin ).Length();
edictList_index++; if ( iDist <= iNearest )
{
iNearest = iDist;
iBestRelationship = IRelationship( pEnt->v.iuser4 );
pReturn = pEnt;
}
}
}
}
edictList_index++;
} }
return pReturn; return pReturn;

View File

@@ -1807,7 +1807,7 @@ int UTIL_TakeDamage( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAtt
WRITE_COORD( pevInflictor->origin.z ); WRITE_COORD( pevInflictor->origin.z );
MESSAGE_END(); MESSAGE_END();
} }
return fTookDamage; return fTookDamage;
} }
@@ -1888,7 +1888,7 @@ void UTIL_TraceAttack( edict_t *pEdict, entvars_t *pevAttacker, float flDamage,
{ {
AddMultiDamage( pevAttacker, pEdict, flDamage, bitsDamageType ); AddMultiDamage( pevAttacker, pEdict, flDamage, bitsDamageType );
SpawnBlood(ptr->vecEndPos, BLOOD_COLOR_RED, flDamage);// a little surface blood. SpawnBlood(ptr->vecEndPos, BLOOD_COLOR_RED, flDamage);// a little surface blood.
UTIL_TraceBleed( pEdict, flDamage, vecDir, ptr, bitsDamageType ); UTIL_TraceBleed( pEdict, flDamage, vecDir, ptr, bitsDamageType );
} }
@@ -1968,3 +1968,11 @@ bool UTIL_IsBSPModel( edict_t *pent )
{ {
return (pent->v.solid == SOLID_BSP || pent->v.movetype == MOVETYPE_PUSHSTEP); return (pent->v.solid == SOLID_BSP || pent->v.movetype == MOVETYPE_PUSHSTEP);
} }
void UTIL_TakeDamageExternal( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
// Tell AMXX to call TakeDamage for us.
char extCmd[64];
sprintf( extCmd, "monster_hurt_entity %i %i %i %f %i\n", ENTINDEX( pEdict ), ENTINDEX( ENT( pevInflictor ) ), ENTINDEX( ENT( pevAttacker ) ), flDamage, bitsDamageType );
SERVER_COMMAND( extCmd );
}

View File

@@ -538,3 +538,4 @@ Vector UTIL_Center(edict_t *pEdict);
edict_t *UTIL_GetNextTarget( edict_t *pEntity ); edict_t *UTIL_GetNextTarget( edict_t *pEntity );
edict_t *UTIL_FindNearestPlayer(edict_t *pEdict, float m_flFieldOfView); edict_t *UTIL_FindNearestPlayer(edict_t *pEdict, float m_flFieldOfView);
bool UTIL_IsBSPModel( edict_t *pent ); bool UTIL_IsBSPModel( edict_t *pent );
void UTIL_TakeDamageExternal( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );

View File

@@ -79,6 +79,8 @@ void ApplyMultiDamage(entvars_t *pevInflictor, entvars_t *pevAttacker )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(gMultiDamage.pEntity)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(gMultiDamage.pEntity));
pMonster->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type ); pMonster->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type );
} }
else if (!UTIL_IsPlayer(gMultiDamage.pEntity))
UTIL_TakeDamageExternal(gMultiDamage.pEntity, pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type );
} }