From 5830ac7908fdc67775af196e56f924deabf7b84f Mon Sep 17 00:00:00 2001 From: Giegue Date: Wed, 15 Feb 2023 21:14:13 -0300 Subject: [PATCH] Tweaked hwgrunt to be less crappy. --- src/dlls/hwgrunt.cpp | 579 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 573 insertions(+), 6 deletions(-) diff --git a/src/dlls/hwgrunt.cpp b/src/dlls/hwgrunt.cpp index 918b6b1..c3159b4 100644 --- a/src/dlls/hwgrunt.cpp +++ b/src/dlls/hwgrunt.cpp @@ -32,15 +32,15 @@ //========================================================= // monster-specific DEFINE's //========================================================= -#define HWGRUNT_9MM_CLIP_SIZE 36 // clip ammo per gun +#define HWGRUNT_9MM_CLIP_SIZE 17 // clip ammo per gun #define HWGRUNT_DGL_CLIP_SIZE 7 #define HWGRUNT_357_CLIP_SIZE 6 // Weapon flags -#define HWGRUNT_MINIGUN (1 << 0) -#define HWGRUNT_PISTOL_9MM (1 << 1) -#define HWGRUNT_PISTOL_DGL (1 << 2) -#define HWGRUNT_PISTOL_357 (1 << 3) +#define HWGRUNT_MINIGUN 0 +#define HWGRUNT_PISTOL_9MM 1 +#define HWGRUNT_PISTOL_DGL 2 +#define HWGRUNT_PISTOL_357 3 #define GUN_GROUP 1 @@ -57,6 +57,21 @@ #define HWGRUNT_AE_DEATH ( 11 ) #define HWGRUNT_AE_MINIGUN ( 5001 ) +//========================================================= +// monster-specific schedule types +//========================================================= +enum +{ + SCHED_HWGRUNT_SUPPRESS = LAST_COMMON_SCHEDULE + 1, + SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE,// move to a location to set up an attack against the enemy. (usually when a friendly is in the way). + SCHED_HWGRUNT_SWEEP, + SCHED_HWGRUNT_REPEL, + SCHED_HWGRUNT_REPEL_ATTACK, + SCHED_HWGRUNT_REPEL_LAND, + SCHED_HWGRUNT_WAIT_FACE_ENEMY, + SCHED_HWGRUNT_ELOF_FAIL, +}; + //========================================================= // Classify - indicates this monster's place in the // relationship table. @@ -72,6 +87,45 @@ int CMHWGrunt::Classify(void) } +//========================================================= +// CheckRangeAttack1 - HWGrunt doesn't care about melee +//========================================================= +BOOL CMHWGrunt :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if ( !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist <= 2048 && flDot >= 0.5 ) + { + TraceResult tr; + + Vector vecSrc = GetGunPosition(); + + // verify that a bullet fired from the gun will hit the enemy before the world. + UTIL_TraceLine( vecSrc, UTIL_BodyTarget(m_hEnemy, vecSrc), ignore_monsters, ignore_glass, ENT(pev), &tr); + + if ( tr.flFraction == 1.0 ) + { + return TRUE; + } + } + + return FALSE; +} + +//========================================================= +// CheckMeleeAttack1 - HWGrunt does not kick +//========================================================= +BOOL CMHWGrunt :: CheckMeleeAttack1 ( float flDot, float flDist ) +{ + return FALSE; +} + +//========================================================= +// CheckRangeAttack2 - HWGrunt has no grenades +//========================================================= +BOOL CMHWGrunt :: CheckRangeAttack2 ( float flDot, float flDist ) +{ + return FALSE; +} + //========================================================= // Shoot //========================================================= @@ -162,6 +216,7 @@ void CMHWGrunt::Spawn() m_flNextPainTime = gpGlobals->time; m_flMinigunSpinTime = 0; // be able to spin up/down minigun right away m_iSentence = -1; + m_fStanding = TRUE; //m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; @@ -180,7 +235,8 @@ void CMHWGrunt::Spawn() } */ m_cAmmoLoaded = 99; - + m_cClipSize = 99; + CMTalkMonster::g_talkWaitTime = 0; MonsterInit(); @@ -213,6 +269,353 @@ void CMHWGrunt::Precache() m_voicePitch = 93; // slight voice change for hwgrunt } +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + +//========================================================= +// GruntFail +//========================================================= +Task_t tlHWGruntFail[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT, (float)2 }, + { TASK_WAIT_PVS, (float)0 }, +}; + +Schedule_t slHWGruntFail[] = +{ + { + tlHWGruntFail, + ARRAYSIZE ( tlHWGruntFail ), + bits_COND_CAN_RANGE_ATTACK1, + 0, + "HWGrunt Fail" + }, +}; + +//========================================================= +// Grunt Combat Fail +//========================================================= +Task_t tlHWGruntCombatFail[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_FACE_ENEMY, (float)2 }, + { TASK_WAIT_PVS, (float)0 }, +}; + +Schedule_t slHWGruntCombatFail[] = +{ + { + tlHWGruntCombatFail, + ARRAYSIZE ( tlHWGruntCombatFail ), + bits_COND_CAN_RANGE_ATTACK1, + 0, + "HWGrunt Combat Fail" + }, +}; + +//========================================================= +// Victory dance! +//========================================================= +Task_t tlHWGruntVictoryDance[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_WAIT, (float)1.5 }, + { TASK_GET_PATH_TO_ENEMY_CORPSE, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_FACE_ENEMY, (float)0 } +}; + +Schedule_t slHWGruntVictoryDance[] = +{ + { + tlHWGruntVictoryDance, + ARRAYSIZE ( tlHWGruntVictoryDance ), + bits_COND_NEW_ENEMY, + 0, + "HWGruntVictoryDance" + }, +}; + + +//========================================================= +// ELOF fail, just wait and try again +//========================================================= +Task_t tlHWGruntELOFFail[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_WAIT, (float)1.5 }, + { TASK_SET_SCHEDULE, (float)SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE }, +}; + +Schedule_t slHWGruntELOFFail[] = +{ + { + tlHWGruntELOFFail, + ARRAYSIZE ( tlHWGruntELOFFail ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_CAN_RANGE_ATTACK1, + 0, + "HWGrunt Failed ELOF" + }, +}; + +//========================================================= +// Establish line of fire - move to a position that allows +// the grunt to attack. +//========================================================= +Task_t tlHWGruntEstablishLineOfFire[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_HWGRUNT_ELOF_FAIL }, + { TASK_GET_PATH_TO_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, +}; + +Schedule_t slHWGruntEstablishLineOfFire[] = +{ + { + tlHWGruntEstablishLineOfFire, + ARRAYSIZE ( tlHWGruntEstablishLineOfFire ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_HEAR_SOUND, + 0, + "HWGruntEstablishLineOfFire" + }, +}; + +//========================================================= +// GruntCombatFace Schedule +//========================================================= +Task_t tlHWGruntCombatFace1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_WAIT, (float)1.5 }, + { TASK_SET_SCHEDULE, (float)SCHED_HWGRUNT_SWEEP }, +}; + +Schedule_t slHWGruntCombatFace[] = +{ + { + tlHWGruntCombatFace1, + ARRAYSIZE ( tlHWGruntCombatFace1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_CAN_RANGE_ATTACK1, + 0, + "HWCombat Face" + }, +}; + +Task_t tlHWGruntSuppress[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slHWGruntSuppress[] = +{ + { + tlHWGruntSuppress, + ARRAYSIZE ( tlHWGruntSuppress ), + 0, + 0, + "HWSuppress" + }, +}; + + +//========================================================= +// grunt wait in cover - we don't allow danger or the ability +// to attack to break a grunt's run to cover schedule, but +// when a grunt is in cover, we do want them to attack if they can. +//========================================================= +Task_t tlHWGruntWaitInCover[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_FACE_ENEMY, (float)1 }, +}; + +Schedule_t slHWGruntWaitInCover[] = +{ + { + tlHWGruntWaitInCover, + ARRAYSIZE ( tlHWGruntWaitInCover ), + bits_COND_NEW_ENEMY | + bits_COND_HEAR_SOUND | + bits_COND_CAN_RANGE_ATTACK1, + 0, + "HWGruntWaitInCover" + }, +}; + + +//========================================================= +// Do a turning sweep of the area +//========================================================= +Task_t tlHWGruntSweep[] = +{ + { TASK_TURN_LEFT, (float)179 }, + { TASK_WAIT, (float)1 }, + { TASK_TURN_LEFT, (float)179 }, + { TASK_WAIT, (float)1 }, +}; + +Schedule_t slHWGruntSweep[] = +{ + { + tlHWGruntSweep, + ARRAYSIZE ( tlHWGruntSweep ), + bits_COND_NEW_ENEMY | + bits_COND_CAN_RANGE_ATTACK1, + 0, + "HWGrunt Sweep" + }, +}; + + +//========================================================= +// primary range attack. Overriden because base class stops attacking when the enemy is occluded. +// grunt's grenade toss requires the enemy be occluded. +//========================================================= +Task_t tlHWGruntRangeAttack1B[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_PLAY_SEQUENCE_FACE_ENEMY,(float)ACT_IDLE_ANGRY }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slHWGruntRangeAttack1B[] = +{ + { + tlHWGruntRangeAttack1B, + ARRAYSIZE ( tlHWGruntRangeAttack1B ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_ENEMY_OCCLUDED, + 0, + "HWRange Attack1B" + }, +}; + + +//========================================================= +// repel +//========================================================= +Task_t tlHWGruntRepel[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_GLIDE }, +}; + +Schedule_t slHWGruntRepel[] = +{ + { + tlHWGruntRepel, + ARRAYSIZE ( tlHWGruntRepel ), + bits_COND_SEE_ENEMY | + bits_COND_NEW_ENEMY, + 0, + "HWRepel" + }, +}; + + +//========================================================= +// repel +//========================================================= +Task_t tlHWGruntRepelAttack[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_FLY }, +}; + +Schedule_t slHWGruntRepelAttack[] = +{ + { + tlHWGruntRepelAttack, + ARRAYSIZE ( tlHWGruntRepelAttack ), + bits_COND_ENEMY_OCCLUDED, + 0, + "HWRepel Attack" + }, +}; + +//========================================================= +// repel land +//========================================================= +Task_t tlHWGruntRepelLand[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_LAND }, + { TASK_GET_PATH_TO_LASTPOSITION,(float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_CLEAR_LASTPOSITION, (float)0 }, +}; + +Schedule_t slHWGruntRepelLand[] = +{ + { + tlHWGruntRepelLand, + ARRAYSIZE ( tlHWGruntRepelLand ), + bits_COND_SEE_ENEMY | + bits_COND_NEW_ENEMY, + 0, + "HWRepel Land" + }, +}; + + +DEFINE_CUSTOM_SCHEDULES( CMHWGrunt ) +{ + slHWGruntFail, + slHWGruntCombatFail, + slHWGruntVictoryDance, + slHWGruntELOFFail, + slHWGruntEstablishLineOfFire, + slHWGruntCombatFace, + slHWGruntSuppress, + slHWGruntWaitInCover, + slHWGruntSweep, + slHWGruntRangeAttack1B, + slHWGruntRepel, + slHWGruntRepelAttack, + slHWGruntRepelLand, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CMHWGrunt, CMBaseMonster ); + //========================================================= // SetActivity - different set than normal hgrunt, adapt //========================================================= @@ -323,3 +726,167 @@ void CMHWGrunt :: SetActivity ( Activity NewActivity ) pev->sequence = 0; // Set to the reset anim (if it's there) } } + +//========================================================= +// Get Schedule! +//========================================================= +Schedule_t *CMHWGrunt :: GetSchedule( void ) +{ + // flying? If PRONE, barnacle has me. IF not, it's assumed I am rapelling. + if ( pev->movetype == MOVETYPE_FLY && m_MonsterState != MONSTERSTATE_PRONE ) + { + if (pev->flags & FL_ONGROUND) + { + // just landed + pev->movetype = MOVETYPE_STEP; + return GetScheduleOfType ( SCHED_HWGRUNT_REPEL_LAND ); + } + else + { + // repel down a rope, + if ( m_MonsterState == MONSTERSTATE_COMBAT ) + return GetScheduleOfType ( SCHED_HWGRUNT_REPEL_ATTACK ); + else + return GetScheduleOfType ( SCHED_HWGRUNT_REPEL ); + } + } + + switch ( m_MonsterState ) + { + case MONSTERSTATE_COMBAT: + { +// dead enemy + if ( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // call base class, all code to handle dead enemies is centralized there. + return CMBaseMonster :: GetSchedule(); + } + +// new enemy + if ( HasConditions(bits_COND_NEW_ENEMY) ) + { + if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + return GetScheduleOfType ( SCHED_HWGRUNT_SUPPRESS ); + } + else + { + return GetScheduleOfType ( SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE ); + } + } +// no ammo + else if ( HasConditions ( bits_COND_NO_AMMO_LOADED ) ) + { + // Stop believing you have no ammo! -Giegue + ClearConditions( bits_COND_NO_AMMO_LOADED ); + if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + return GetScheduleOfType ( SCHED_HWGRUNT_SUPPRESS ); + } + else + { + return GetScheduleOfType ( SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE ); + } + } +// can shoot + else if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + // Force attack! + return GetScheduleOfType ( SCHED_HWGRUNT_SUPPRESS ); + } +// can't see enemy + + if ( HasConditions( bits_COND_SEE_ENEMY ) && !HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + return GetScheduleOfType ( SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE ); + } + } + } + + // no special cases here, call the base class + return CMBaseMonster :: GetSchedule(); +} + +//========================================================= +//========================================================= +Schedule_t* CMHWGrunt :: GetScheduleOfType ( int Type ) +{ + switch ( Type ) + { + case SCHED_HWGRUNT_ELOF_FAIL: + { + // human grunt is unable to move to a position that allows him to attack the enemy. + UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_HWGRUNT_ELOF_FAIL\n" ); + return &slHWGruntELOFFail[ 0 ]; + } + break; + case SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE: + { + UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE\n" ); + return &slHWGruntEstablishLineOfFire[ 0 ]; + } + break; + case SCHED_RANGE_ATTACK1: + { + // no pistols yet, always do standing attack + UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_RANGE_ATTACK1\n" ); + return &slHWGruntRangeAttack1B[ 0 ]; + } + case SCHED_COMBAT_FACE: + { + UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_COMBAT_FACE\n" ); + return &slHWGruntCombatFace[ 0 ]; + } + case SCHED_HWGRUNT_WAIT_FACE_ENEMY: + { + UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_HWGRUNT_WAIT_FACE_ENEMY\n" ); + return &slHWGruntWaitInCover[ 0 ]; + } + case SCHED_HWGRUNT_SWEEP: + { + UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_HWGRUNT_SWEEP\n" ); + return &slHWGruntSweep[ 0 ]; + } + case SCHED_VICTORY_DANCE: + { + UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_VICTORY_DANCE\n" ); + return &slHWGruntVictoryDance[ 0 ]; + } + case SCHED_HWGRUNT_SUPPRESS: + { + UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_HWGRUNT_SUPPRESS\n" ); + return &slHWGruntSuppress[ 0 ]; + } + case SCHED_FAIL: + { + UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_FAIL\n" ); + if ( m_hEnemy != NULL ) + { + // grunt has an enemy, so pick a different default fail schedule most likely to help recover. + return &slHWGruntCombatFail[ 0 ]; + } + + return &slHWGruntFail[ 0 ]; + } + case SCHED_HWGRUNT_REPEL: + { + if (pev->velocity.z > -128) + pev->velocity.z -= 32; + return &slHWGruntRepel[ 0 ]; + } + case SCHED_HWGRUNT_REPEL_ATTACK: + { + if (pev->velocity.z > -128) + pev->velocity.z -= 32; + return &slHWGruntRepelAttack[ 0 ]; + } + case SCHED_HWGRUNT_REPEL_LAND: + { + return &slHWGruntRepelLand[ 0 ]; + } + default: + { + return CMBaseMonster :: GetScheduleOfType ( Type ); + } + } +}