From 6e1081d79333a9103c545f8d3f2c4df7351e83b7 Mon Sep 17 00:00:00 2001 From: Giegue Date: Wed, 15 Feb 2023 21:19:57 -0300 Subject: [PATCH] Start making monstermod aware of normal HL monsters. This is still heavily experimental and its use is not yet recommended. --- src/dlls/cmbasemonster.h | 15 ++ src/dlls/combat.cpp | 322 +++++++++++++++++++++++++++------------ src/dlls/monsters.cpp | 157 ++++++++++++++----- src/dlls/util.cpp | 12 +- src/dlls/util.h | 1 + src/dlls/weapons.cpp | 2 + 6 files changed, 368 insertions(+), 141 deletions(-) diff --git a/src/dlls/cmbasemonster.h b/src/dlls/cmbasemonster.h index 5f762c4..446a984 100644 --- a/src/dlls/cmbasemonster.h +++ b/src/dlls/cmbasemonster.h @@ -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,7 +1657,16 @@ 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 diff --git a/src/dlls/combat.cpp b/src/dlls/combat.cpp index 5afbd27..8a9bc1f 100644 --- a/src/dlls/combat.cpp +++ b/src/dlls/combat.cpp @@ -1060,8 +1060,8 @@ void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacke { if ( pEntity->v.takedamage != DAMAGE_NO ) { - if (UTIL_IsPlayer(pEntity)) - { + if (UTIL_IsPlayer(pEntity)) + { // blast's don't tavel into or out of water if (bInWater && pEntity->v.waterlevel == 0) continue; @@ -1100,11 +1100,11 @@ void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacke else { 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 CMBaseEntity *pMonster = GetClassPtr((CMBaseEntity *)VARS(pEntity)); @@ -1151,7 +1151,60 @@ void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacke else { 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)); pMonster->TakeDamage( pev, pev, iDamage, iDmgType ); } + else if (!UTIL_IsPlayer(pEntity)) + UTIL_TakeDamageExternal( pEntity, pev, pev, iDamage, iDmgType ); } return pEntity; @@ -1386,8 +1441,8 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin // do damage, paint decals 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; if ( iDamage ) @@ -1396,60 +1451,63 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); DecalGunshot( &tr, iBulletType ); } - else switch(iBulletType) + else { - default: - 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 ) + switch(iBulletType) { + default: + 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_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)); if ( iDamage ) @@ -1458,56 +1516,124 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); DecalGunshot( &tr, iBulletType ); } - else switch(iBulletType) + else { - default: - 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 ) + switch(iBulletType) { + default: + 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 ) + { + 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: - pMonster->TraceAttack(pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET); + } + } + 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 ); - - 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) + } + else + { + switch(iBulletType) { - 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; + } } } } diff --git a/src/dlls/monsters.cpp b/src/dlls/monsters.cpp index ef1a59f..55a55d0 100644 --- a/src/dlls/monsters.cpp +++ b/src/dlls/monsters.cpp @@ -120,7 +120,7 @@ void CMBaseMonster :: Look ( int iDistance ) // 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); - m_edictList_count = 0; + m_edictList_count = 0; 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]; // !!!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)) - { + // 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++; + m_edictList[m_edictList_count] = pSightEnt; + m_edictList_count++; - // if we see a client, remember that (mostly for scripted AI) - iSighted |= bits_COND_SEE_CLIENT; + // if we see a client, remember that (mostly for scripted AI) + iSighted |= bits_COND_SEE_CLIENT; // is this monster NOT a scientist? if (strcmp(STRING(pev->model), "models/scientist.mdl") != 0) @@ -166,19 +163,19 @@ void CMBaseMonster :: Look ( int iDistance ) iSighted |= bits_COND_SEE_ENEMY; } } - } - } - else if (pSightEnt->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pSightEnt)); + } + } + 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++; + m_edictList[m_edictList_count] = pSightEnt; + m_edictList_count++; 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 ); 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 ]; } //========================================================= @@ -2019,38 +2068,39 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void ) int iNearest; int iDist; int iBestRelationship; - edict_t *pReturn; - edict_t *pEnt; - int edictList_index = 0; + edict_t *pReturn; + edict_t *pEnt; + int edictList_index = 0; iNearest = 8192;// so first visible entity will become the closest. 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) ) - { - // it's a player... + if ( UTIL_IsPlayer(pEnt) ) + { + // it's a player... iDist = ( pEnt->v.origin - pev->origin ).Length(); 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) - { + } + 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,13 +2109,13 @@ 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 // get mad at it if it is closer. iDist = ( pNextEnt->pev->origin - pev->origin ).Length(); - + if ( iDist <= iNearest ) { 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; diff --git a/src/dlls/util.cpp b/src/dlls/util.cpp index f34fff1..d8e9304 100644 --- a/src/dlls/util.cpp +++ b/src/dlls/util.cpp @@ -1807,7 +1807,7 @@ int UTIL_TakeDamage( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAtt WRITE_COORD( pevInflictor->origin.z ); MESSAGE_END(); } - + return fTookDamage; } @@ -1888,7 +1888,7 @@ void UTIL_TraceAttack( edict_t *pEdict, entvars_t *pevAttacker, float flDamage, { 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 ); } @@ -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 ); +} diff --git a/src/dlls/util.h b/src/dlls/util.h index 9070c11..23506f5 100644 --- a/src/dlls/util.h +++ b/src/dlls/util.h @@ -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 ); diff --git a/src/dlls/weapons.cpp b/src/dlls/weapons.cpp index 16d1950..0a36ccb 100644 --- a/src/dlls/weapons.cpp +++ b/src/dlls/weapons.cpp @@ -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 ); }