void CASW_Harvester::Event_Killed( const CTakeDamageInfo &info ) { BaseClass::Event_Killed(info); // spawn a bunch of harvesites int iNumParasites = 4 + RandomInt(0,2); QAngle angParasiteFacing[6]; float fJumpDistance[6]; // for some reason if we calculate these inside the loop, the random numbers all come out the same. Worrying. angParasiteFacing[0] = GetAbsAngles(); angParasiteFacing[0].y = RandomFloat( -180.0f, 180.0f ); angParasiteFacing[1] = GetAbsAngles(); angParasiteFacing[1].y = RandomFloat( -180.0f, 180.0f ); angParasiteFacing[2] = GetAbsAngles(); angParasiteFacing[2].y = RandomFloat( -180.0f, 180.0f ); angParasiteFacing[3] = GetAbsAngles(); angParasiteFacing[3].y = RandomFloat( -180.0f, 180.0f ); angParasiteFacing[4] = GetAbsAngles(); angParasiteFacing[4].y = RandomFloat( -180.0f, 180.0f ); angParasiteFacing[5] = GetAbsAngles(); angParasiteFacing[5].y = RandomFloat( -180.0f, 180.0f ); fJumpDistance[0] = RandomFloat( 30.0f, 70.0f ); fJumpDistance[1] = RandomFloat( 30.0f, 70.0f ); fJumpDistance[2] = RandomFloat( 30.0f, 70.0f ); fJumpDistance[3] = RandomFloat( 30.0f, 70.0f ); fJumpDistance[4] = RandomFloat( 30.0f, 70.0f ); fJumpDistance[5] = RandomFloat( 30.0f, 70.0f ); for ( int i = 0; i < iNumParasites; i++ ) { bool bBlocked = true; int k = 0; Vector vecSpawnPos = GetAbsOrigin(); float fCircleDegree = ( static_cast< float >( i ) / iNumParasites ) * 2.0f * M_PI; vecSpawnPos.x += sinf( fCircleDegree ) * RandomFloat( 3.0f, 20.0f ); vecSpawnPos.y += cosf( fCircleDegree ) * RandomFloat( 3.0f, 20.0f ); vecSpawnPos.z += RandomFloat( 20.0f, 40.0f ); while ( bBlocked && k < 6 ) { if ( k > 0 ) { // Scooch it up vecSpawnPos.z += NAI_Hull::Maxs( HULL_TINY ).z - NAI_Hull::Mins( HULL_TINY ).z; } // check if there's room at this position trace_t tr; UTIL_TraceHull( vecSpawnPos, vecSpawnPos + Vector( 0.0f, 0.0f, 1.0f ), NAI_Hull::Mins(HULL_TINY) + Vector( -4.0f, -4.0f, -4.0f ),NAI_Hull::Maxs(HULL_TINY) + Vector( 4.0f, 4.0f, 4.0f ), MASK_NPCSOLID, this, ASW_COLLISION_GROUP_PARASITE, &tr ); //NDebugOverlay::Box(vecSpawnPos[i], NAI_Hull::Mins(HULL_TINY),NAI_Hull::Maxs(HULL_TINY), 255,255,0,255,15.0f); if ( tr.fraction == 1.0 ) { bBlocked = false; } k++; } if ( bBlocked ) continue; // couldn't find room for parasites CASW_Parasite *pParasite = dynamic_cast< CASW_Parasite* >( CreateNoSpawn("asw_parasite_defanged", vecSpawnPos, angParasiteFacing[i], this)); if ( pParasite ) { DispatchSpawn( pParasite ); pParasite->SetSleepState(AISS_WAITING_FOR_INPUT); pParasite->SetJumpFromEgg(true, fJumpDistance[i]); pParasite->Wake(); if ( IsOnFire() ) { pParasite->ASW_Ignite( 30.0f, 0, info.GetAttacker(), info.GetWeapon() ); } } } m_fGibTime = gpGlobals->curtime + random->RandomFloat(20.0f, 30.0f); }
//----------------------------------------------------------------------------- // Purpose: Catches the monster-specific events that occur when tagged animation // frames are played. // Input : pEvent - //----------------------------------------------------------------------------- void CNPC_PoisonZombie::HandleAnimEvent( animevent_t *pEvent ) { if ( pEvent->event == AE_ZOMBIE_POISON_PICKUP_CRAB ) { EnableCrab( m_nThrowCrab, false ); SetBodygroup( ZOMBIE_BODYGROUP_THROW, 1 ); return; } if ( pEvent->event == AE_ZOMBIE_POISON_THROW_WARN_SOUND ) { BreatheOffShort(); EmitSound( "NPC_PoisonZombie.ThrowWarn" ); return; } if ( pEvent->event == AE_ZOMBIE_POISON_THROW_SOUND ) { BreatheOffShort(); EmitSound( "NPC_PoisonZombie.Throw" ); return; } if ( pEvent->event == AE_ZOMBIE_POISON_THROW_CRAB ) { SetBodygroup( ZOMBIE_BODYGROUP_THROW, 0 ); CBlackHeadcrab *pCrab = (CBlackHeadcrab *)CreateNoSpawn( GetHeadcrabClassname(), EyePosition(), vec3_angle, this ); pCrab->AddSpawnFlags( SF_NPC_FALL_TO_GROUND ); // Fade if our parent is supposed to if ( HasSpawnFlags( SF_NPC_FADE_CORPSE ) ) { pCrab->AddSpawnFlags( SF_NPC_FADE_CORPSE ); } // make me the crab's owner to avoid collision issues pCrab->SetOwnerEntity( this ); pCrab->Spawn(); pCrab->SetLocalAngles( GetLocalAngles() ); pCrab->SetActivity( ACT_RANGE_ATTACK1 ); pCrab->SetNextThink( gpGlobals->curtime ); pCrab->PhysicsSimulate(); pCrab->GetMotor()->SetIdealYaw( GetAbsAngles().y ); if ( IsOnFire() ) { pCrab->Ignite( 100.0 ); } CBaseEntity *pEnemy = GetEnemy(); if ( pEnemy ) { Vector vecEnemyEyePos = pEnemy->EyePosition(); pCrab->ThrowAt( vecEnemyEyePos ); } if (m_nCrabCount == 0) { CapabilitiesRemove( bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_INNATE_RANGE_ATTACK2 ); } m_flNextCrabThrowTime = gpGlobals->curtime + random->RandomInt( ZOMBIE_THROW_MIN_DELAY, ZOMBIE_THROW_MAX_DELAY ); return; } if ( pEvent->event == AE_ZOMBIE_POISON_SPIT ) { Vector forward; QAngle qaPunch( 45, random->RandomInt(-5, 5), random->RandomInt(-5, 5) ); AngleVectors( GetLocalAngles(), &forward ); forward = forward * 200; ClawAttack( GetClawAttackRange(), sk_zombie_poison_dmg_spit.GetFloat(), qaPunch, forward, ZOMBIE_BLOOD_BITE ); return; } BaseClass::HandleAnimEvent( pEvent ); }
//----------------------------------------------------------------------------- // Purpose: The nest is dead! Evacuate the nest! // Input : bExplosion - We were evicted by an explosion so we should go a-flying. // flDamage - The damage that was done to cause the evacuation. //----------------------------------------------------------------------------- void CNPC_PoisonZombie::EvacuateNest( bool bExplosion, float flDamage, CBaseEntity *pAttacker ) { // HACK: if we were in mid-throw, drop the throwing crab also. if ( GetBodygroup( ZOMBIE_BODYGROUP_THROW ) ) { SetBodygroup( ZOMBIE_BODYGROUP_THROW, 0 ); m_nCrabCount++; } for( int i = 0; i < MAX_CRABS ; i++ ) { if( m_bCrabs[i] ) { Vector vecPosition; QAngle vecAngles; char szAttachment[64]; switch( i ) { case 0: strcpy( szAttachment, "headcrab2" ); break; case 1: strcpy( szAttachment, "headcrab3" ); break; case 2: strcpy( szAttachment, "headcrab4" ); break; } GetAttachment( szAttachment, vecPosition, vecAngles ); // Now slam the angles because the attachment point will have pitch and roll, which we can't use. vecAngles = QAngle( 0, random->RandomFloat( 0, 360 ), 0 ); CBlackHeadcrab *pCrab = (CBlackHeadcrab *)CreateNoSpawn( GetHeadcrabClassname(), vecPosition, vecAngles, this ); pCrab->Spawn(); if( !HeadcrabFits(pCrab) ) { UTIL_Remove(pCrab); continue; } float flVelocityScale = 2.0f; if ( bExplosion && ( flDamage > 10 ) ) { flVelocityScale = 0.1 * flDamage; } if (IsOnFire()) { pCrab->Ignite( 100.0 ); } pCrab->Eject( vecAngles, flVelocityScale, pAttacker ); EnableCrab( i, false ); } } }
//========================================================= // HandleAnimEvent - catches the monster-specific messages // that occur when tagged animation frames are played. //========================================================= void CNPC_Bullsquid::HandleAnimEvent( animevent_t *pEvent ) { switch( pEvent->event ) { case BSQUID_AE_SPIT: { if ( GetEnemy() ) { Vector vSpitPos; QAngle vSpitAngle; GetAttachment( "Mouth", vSpitPos, vSpitAngle); Vector vTarget = GetEnemy()->GetAbsOrigin(); Vector vToss; CBaseEntity* pBlocker; float flGravity = sv_gravity.GetFloat() * SPIT_GRAVITY; ThrowLimit(vSpitPos, vTarget, flGravity, 3, Vector(0,0,0), Vector(0,0,0), GetEnemy(), &vToss, &pBlocker); CGrenadeSpit *pGrenade = (CGrenadeSpit*)CreateNoSpawn( "grenade_spit", vSpitPos, vec3_angle, this ); //pGrenade->KeyValue( "velocity", vToss ); pGrenade->Spawn( ); pGrenade->SetOwner( this ); pGrenade->SetOwnerEntity( this ); pGrenade->SetSpitSize( 2 ); pGrenade->SetAbsVelocity( vToss ); // Tumble through the air pGrenade->SetLocalAngularVelocity( QAngle( random->RandomFloat ( -100, -500 ), random->RandomFloat ( -100, -500 ), random->RandomFloat ( -100, -500 ) ) ); AttackSound(); CPVSFilter filter( vSpitPos ); te->SpriteSpray( filter, 0.0, &vSpitPos, &vToss, m_nSquidSpitSprite, 5, 10, 15 ); } } break; case BSQUID_AE_BITE: { // SOUND HERE! CBaseEntity *pHurt = CheckTraceHullAttack( 70, Vector(-16,-16,-16), Vector(16,16,16), sk_bullsquid_dmg_bite.GetFloat(), DMG_SLASH ); if ( pHurt ) { Vector forward, up; AngleVectors( GetAbsAngles(), &forward, NULL, &up ); pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - (forward * 100) ); pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() + (up * 100) ); pHurt->RemoveFlag( FL_ONGROUND ); } } break; case BSQUID_AE_WHIP_SND: { EmitSound( "NPC_Bullsquid.TailWhip" ); break; } /* case BSQUID_AE_TAILWHIP: { CBaseEntity *pHurt = CheckTraceHullAttack( 70, Vector(-16,-16,-16), Vector(16,16,16), sk_bullsquid_dmg_whip.GetFloat(), DMG_SLASH | DMG_ALWAYSGIB ); if ( pHurt ) { Vector right, up; AngleVectors( GetAbsAngles(), NULL, &right, &up ); if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) ) pHurt->ViewPunch( QAngle( 20, 0, -20 ) ); pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() + (right * 200) ); pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() + (up * 100) ); } } break; */ case BSQUID_AE_BLINK: { // close eye. m_nSkin = 1; } break; case BSQUID_AE_HOP: { float flGravity = sv_gravity.GetFloat(); // throw the squid up into the air on this frame. if ( GetFlags() & FL_ONGROUND ) { RemoveFlag( FL_ONGROUND ); } // jump into air for 0.8 (24/30) seconds Vector vecVel = GetAbsVelocity(); vecVel.z += ( 0.625 * flGravity ) * 0.5; SetAbsVelocity( vecVel ); } break; case BSQUID_AE_THROW: { // squid throws its prey IF the prey is a client. CBaseEntity *pHurt = CheckTraceHullAttack( 70, Vector(-16,-16,-16), Vector(16,16,16), 0, 0 ); if ( pHurt ) { pHurt->ViewPunch( QAngle(20,0,-20) ); // screeshake transforms the viewmodel as well as the viewangle. No problems with seeing the ends of the viewmodels. UTIL_ScreenShake( pHurt->GetAbsOrigin(), 25.0, 1.5, 0.7, 2, SHAKE_START ); // If the player, throw him around if ( pHurt->IsPlayer()) { Vector forward, up; AngleVectors( GetLocalAngles(), &forward, NULL, &up ); pHurt->ApplyAbsVelocityImpulse( forward * 300 + up * 300 ); } // If not the player see if has bullsquid throw interatcion else { CBaseCombatCharacter *pVictim = ToBaseCombatCharacter( pHurt ); if (pVictim) { if ( pVictim->HandleInteraction( g_interactionBullsquidThrow, NULL, this ) ) { Vector forward, up; AngleVectors( GetLocalAngles(), &forward, NULL, &up ); pVictim->ApplyAbsVelocityImpulse( forward * 300 + up * 250 ); } } } } } break; default: BaseClass::HandleAnimEvent( pEvent ); } }