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 );
void EXPORT CallMonsterThink( void ) { this->MonsterThink(); }
virtual int IRelationship ( CMBaseEntity *pTarget );
virtual int IRelationship ( int iTargetClass );
int IRelationshipByClass ( int iClass );
virtual void MonsterInit ( void );
virtual void MonsterInitDead( void ); // Call after animation/pose is set up
virtual void BecomeDead( void );
@@ -1655,8 +1657,17 @@ public:
void HandleAnimEvent(MonsterEvent_t *pEvent);
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);
CUSTOM_SCHEDULES
float m_flMinigunSpinTime;
};
@@ -1695,4 +1706,8 @@ public:
int m_iBodyGibs;
};
//=========================================================
// Looking for Stukabat? It's located in cmflyingmonster.h
//=========================================================
#endif // BASEMONSTER_H

View File

@@ -1154,6 +1154,59 @@ void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacke
}
}
}
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));
pMonster->TakeDamage( pev, pev, iDamage, iDmgType );
}
else if (!UTIL_IsPlayer(pEntity))
UTIL_TakeDamageExternal( pEntity, pev, pev, iDamage, iDmgType );
}
return pEntity;
@@ -1396,7 +1451,9 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
}
else switch(iBulletType)
else
{
switch(iBulletType)
{
default:
case BULLET_MONSTER_9MM:
@@ -1448,7 +1505,8 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
break;
}
}
else if (tr.pHit->v.euser4 != NULL)
}
else if (tr.pHit->v.euser4 != NULL) // monstermod monster
{
CMBaseEntity *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
@@ -1458,7 +1516,9 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
}
else switch(iBulletType)
else
{
switch(iBulletType)
{
default:
case BULLET_MONSTER_9MM:
@@ -1511,6 +1571,72 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
}
}
}
else if (!UTIL_IsPlayer(tr.pHit)) // normal game monster
{
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);
DecalGunshot( &tr, iBulletType );
}
else
{
switch(iBulletType)
{
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;
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;
}
}
}
}
// make bullet trails
UTIL_BubbleTrail( vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0 );
}

View File

@@ -137,17 +137,14 @@ void CMBaseMonster :: Look ( int iDistance )
{
pSightEnt = pList[i];
// !!!temporarily only considering other monsters and clients, don't see prisoners
if ( pSightEnt != this->edict() &&
!FBitSet( pSightEnt->v.spawnflags, SF_MONSTER_PRISONER ) &&
pSightEnt->v.health > 0 )
if ( pSightEnt != this->edict() && !FBitSet( pSightEnt->v.spawnflags, SF_MONSTER_PRISONER ) && pSightEnt->v.health > 0 )
{
// is this a player AND are they alive?
if (UTIL_IsPlayer(pSightEnt) && UTIL_IsAlive(pSightEnt))
{
// the looker will want to consider this entity
// don't check anything else about an entity that can't be seen.
if ( UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) &&
!FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
if ( 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++;
@@ -170,12 +167,12 @@ void CMBaseMonster :: Look ( int iDistance )
}
else if (pSightEnt->v.euser4 != NULL)
{
/* MonsterMod monster looking at another MonsterMod monster */
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pSightEnt));
// 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( pMonster ) != R_NO && UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) &&
!FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
if ( IRelationship( pMonster ) != 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++;
@@ -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 );
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.
//=========================================================
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] =
{ // 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 }
};
return iEnemy[ Classify() ][ pTarget->Classify() ];
return iEnemy[ Classify() ][ iClass ];
}
//=========================================================
@@ -2041,16 +2090,17 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void )
if ( iDist <= iNearest )
{
iNearest = iDist;
iBestRelationship = R_NM; // player is always nemsis
iBestRelationship = R_NM; // player is always nemesis
pReturn = pEnt;
}
}
else if (pEnt->v.euser4 != NULL)
{
// it's a monstermod monster...
CMBaseMonster *pNextEnt = GetClassPtr((CMBaseMonster *)VARS(pEnt));
if ( pNextEnt->IsAlive() )
{
if ( IRelationship( pNextEnt) > iBestRelationship )
if ( IRelationship( pNextEnt ) > iBestRelationship )
{
// this entity is disliked MORE than the entity that we
// currently think is the best visible enemy. No need to do
@@ -2059,7 +2109,7 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void )
iNearest = ( pNextEnt->pev->origin - pev->origin ).Length();
pReturn = pEnt;
}
else if ( IRelationship( pNextEnt) == iBestRelationship )
else if ( IRelationship( pNextEnt ) == iBestRelationship )
{
// this entity is disliked just as much as the entity that
// we currently think is the best visible enemy, so we only
@@ -2075,6 +2125,31 @@ 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();
if ( iDist <= iNearest )
{
iNearest = iDist;
iBestRelationship = IRelationship( pEnt->v.iuser4 );
pReturn = pEnt;
}
}
}
}
edictList_index++;
}

View File

@@ -1968,3 +1968,11 @@ bool UTIL_IsBSPModel( edict_t *pent )
{
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_FindNearestPlayer(edict_t *pEdict, float m_flFieldOfView);
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));
pMonster->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type );
}
else if (!UTIL_IsPlayer(gMultiDamage.pEntity))
UTIL_TakeDamageExternal(gMultiDamage.pEntity, pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type );
}