//========= Copyright © 2021, Overcharged. =================================// // // Purpose: Cremator cut enemy remake // // $NoKeywords: $ //=============================================================================// #pragma warning(disable:4706) #include "cbase.h" #include "npc_cremator.h" #include "weapon_physcannon.h" #define SF_CREMATOR_NO_GRENADES 1<<23 #define SF_CREMATOR_NO_FUEL_SPILLING 1<<24 #define SF_CREMATOR_NO_PATROL_BEHAVIOUR 1<<25 #define CREMATOR_SKIN_ALERT 0 // yellow eyes #define CREMATOR_SKIN_CALM 1 // blue eyes #define CREMATOR_SKIN_ANGRY 2 // red eyes #define CREMATOR_SKIN_DEAD 3 // black eyes #define CREMATOR_IMMOLATOR_RANGE 370 #define CREMATOR_AE_IMMO_START ( 6 ) #define CREMATOR_AE_IMMO_PARTICLE ( 7 ) #define CREMATOR_AE_IMMO_PARTICLEOFF ( 8 ) #define CREMATOR_AE_THROWGRENADE ( 9 ) #define CREMATOR_AE_SPECIAL_START ( 10 ) #define CREMATOR_AE_SPECIAL_MIDDLE ( 11 ) // bad name? #define CREMATOR_AE_SPECIAL_END ( 12 ) #define CREMATOR_AE_RELOAD ( 15 ) #define CREMATOR_AE_FLLEFT ( 98 ) #define CREMATOR_AE_FLRIGHT ( 99 ) #define CREMATOR_BEAM_ATTACH 1 #define CREMATOR_BURN_TIME 20 #define CREMATOR_DETECT_CORPSE_RANGE 300.0 enum { SCHED_CREMATOR_RANGE_ATTACK1 = LAST_SHARED_SCHEDULE + 100, SCHED_CREMATOR_RANGE_ATTACK2, SCHED_CREMATOR_CHASE_ENEMY, SCHED_CREMATOR_PATROL, SCHED_CREMATOR_INVESTIGATE_CORPSE, }; enum { TASK_CREMATOR_RANGE_ATTACK1 = LAST_SHARED_TASK + 1, TASK_CREMATOR_RANGE_ATTACK2, TASK_CREMATOR_RELOAD, TASK_CREMATOR_INVESTIGATE_CORPSE, TASK_CREMATOR_BURN_CORPSE, }; enum { COND_CREMATOR_OUT_OF_AMMO = LAST_SHARED_CONDITION + 1, COND_CREMATOR_ENEMY_WITH_HIGHER_PRIORITY, COND_CREMATOR_FOUND_CORPSE, // Cremator was patrolling the streets and found a corpse }; CSound *pInterestSound; CBaseEntity *pCorpse; CBaseEntity *pCorpseLast; CBaseEntity *pEnemy; LINK_ENTITY_TO_CLASS(npc_cremator, CNPC_Cremator); ConVar sk_cremator_health("sk_cremator_health", "180"); ConVar sk_cremator_firedamage("sk_cremator_firedamage", "0"); ConVar sk_cremator_radiusdamage("sk_cremator_radiusdamage", "0"); ConVar sk_cremator_corpse_search_radius("sk_cremator_corpse_search_radius", "1520"); ConVar sk_cremator_attackplayeronsight("sk_cremator_attackplayeronsight", "0"); Activity ACT_FIRESPREAD; Activity ACT_FIREIDLE; Activity ACT_CREMATOR_ARM; Activity ACT_CREMATOR_DISARM; Activity ACT_CREMATOR_RELOAD; BEGIN_DATADESC(CNPC_Cremator) DEFINE_FIELD(m_bIsFiring, FIELD_BOOLEAN), DEFINE_FIELD(m_flNextRangeAttack1Time, FIELD_TIME), DEFINE_FIELD(distance, FIELD_FLOAT), DEFINE_FIELD(DoFireUp, FIELD_BOOLEAN), DEFINE_FIELD(m_breath, FIELD_BOOLEAN), DEFINE_FIELD(m_iAmmo, FIELD_INTEGER), DEFINE_FIELD(m_bHeadshot, FIELD_BOOLEAN), DEFINE_FIELD(m_bIsPlayerEnemy, FIELD_BOOLEAN), DEFINE_FIELD(m_bIsNPCEnemy, FIELD_BOOLEAN), DEFINE_FIELD(m_bPlayAngrySound, FIELD_BOOLEAN), END_DATADESC(); CNPC_Cremator::CNPC_Cremator(void) { m_bIsBodygrouped = true; m_iBodyGroup = 1; m_iBodyGroupValue = 1; } void CNPC_Cremator::Precache() { PrecacheModel("models/Cremator_over.mdl"); //PrecacheModel("models/Cremator_headprop.mdl"); PrecacheModel(GUNMODEL); PrecacheParticleSystem("vapor_ray"); PrecacheScriptSound("NPC_Cremator.NPCAlert"); PrecacheScriptSound("NPC_Cremator.PlayerAlert"); PrecacheScriptSound("NPC_Cremator.BreathingAmb"); PrecacheScriptSound("NPC_Cremator.AngryAmb"); PrecacheScriptSound("NPC_Cremator.DeathAmb"); PrecacheScriptSound("Weapon_Immolator.Single"); PrecacheScriptSound("Weapon_Immolator.Stop"); PrecacheMaterial(CREMATOR_BEAM_SPRITE); PrecacheParticleSystem("vapor_ray"); PrecacheParticleSystem("immolator_normal"); PrecacheParticleSystem("immolator_sparks01"); PrecacheParticleSystem("flamethrower"); PrecacheParticleSystem("flamethrower_orange"); PrecacheParticleSystem("weapon_muzzle_smoke"); beamIndex = PrecacheModel("sprites/bluelaser1.vmt"); PrecacheScriptSound("NPC_Cremator.NPCAlert"); PrecacheScriptSound("NPC_Cremator.PlayerAlert"); PrecacheScriptSound("NPC_Cremator.BreathingAmb"); PrecacheScriptSound("NPC_Cremator.AngryAmb"); PrecacheScriptSound("NPC_Cremator.DeathAmb"); PrecacheScriptSound("Weapon_Immolator.Single"); PrecacheScriptSound("Weapon_Immolator.Stop"); PrecacheScriptSound("Weapon_Immolator.Start"); enginesound->PrecacheSound("npc/cremator/amb_loop.wav"); enginesound->PrecacheSound("npc/cremator/amb1.wav"); enginesound->PrecacheSound("npc/cremator/amb2.wav"); enginesound->PrecacheSound("npc/cremator/amb3.wav"); enginesound->PrecacheSound("npc/cremator/crem_die.wav"); enginesound->PrecacheSound("npc/cremator/alert_object.wav"); enginesound->PrecacheSound("npc/cremator/alert_player.wav"); PrecacheScriptSound("NPC_Cremator.FootstepLeft"); PrecacheScriptSound("NPC_Cremator.Breath"); PrecacheScriptSound("NPC_Cremator.Mad"); PrecacheScriptSound("NPC_Cremator.FindPlayer"); PrecacheScriptSound("NPC_Cremator.FootstepRight"); PrecacheScriptSound("Weapon_Immolator.Single"); PrecacheScriptSound("Weapon_Immolator.Stop"); CreateSounds(); CreateBreathSound(); BaseClass::Precache(); } void CNPC_Cremator::Spawn(void) { Precache(); SetModel("models/Cremator_over.mdl"); SetHullType(HULL_MEDIUM_TALL); SetHullSizeNormal(); SetBodygroup(1, 0); // the gun //SetBodygroup(2, 0); // the head // over: always enabled in remaster SetSolid(SOLID_BBOX); AddSolidFlags(FSOLID_NOT_STANDABLE); SetMoveType(MOVETYPE_STEP); m_bloodColor = BLOOD_COLOR_MECH; // TODO: basically turns blood into sparks. Need something more special. m_iHealth = sk_cremator_health.GetFloat(); m_flFieldOfView = VIEW_FIELD_WIDE; m_NPCState = NPC_STATE_NONE; CapabilitiesClear(); CapabilitiesAdd(bits_CAP_MOVE_GROUND | bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_DOORS_GROUP); // TODO: cremator thus can open doors but he is too tall to fit normal doors? NPCInit(); distance = 128.0f; SetDistLook(1280); m_flNextRangeAttack1Time = 0.f; pCorpse = NULL; pCorpseLast = NULL; pEnemy = NULL; m_bIsFiring = false; m_breath = false; m_bIsNPCEnemy = true; //StartBreathSound(); CPASAttenuationFilter filter(this, 4.f); enginesound->EmitSound(filter, entindex(), CHAN_VOICE, "npc/cremator/amb_loop.wav", 0.2f, ATTN_NORM); //m_flNextFlinchTime = gpGlobals->curtime + SequenceDuration(); oc_ragdoll_dissolved = false; Q_snprintf(pCorpseName, sizeof(pCorpseName), "prop_ragdoll"); } Disposition_t CNPC_Cremator::IRelationType(CBaseEntity *pTarget) { Disposition_t disp = BaseClass::IRelationType(pTarget); if (pTarget == NULL) return disp; if (pTarget->Classify() == CLASS_COMBINE) return D_NU; if (pTarget->IsNPC() && (pTarget->Classify() == CLASS_NONE || pTarget->Classify() == CLASS_BULLSEYE)) { return D_NU; } if (pTarget->Classify() == CLASS_PLAYER || pTarget->Classify() == CLASS_PLAYER_ALLY || pTarget->Classify() == CLASS_PLAYER_ALLY_VITAL) { if (sk_cremator_attackplayeronsight.GetBool()) { return D_HT; } else { return m_bIsPlayerEnemy ? D_HT : D_NU; } } else if (pTarget->IsNPC() && pTarget->Classify() != this->Classify() && pTarget->Classify() != CLASS_COMBINE && pTarget->Classify() != CLASS_COMBINE_HUNTER && pTarget->Classify() != CLASS_COMBINE_GUNSHIP && pTarget->Classify() != CLASS_MANHACK && pTarget->Classify() != CLASS_METROPOLICE && pTarget->Classify() != CLASS_SCANNER) { return m_bIsNPCEnemy ? D_HT : D_NU; } /*if (pTarget->IsNPC() && (pTarget->Classify() == CLASS_HEADCRAB || pTarget->Classify() == CLASS_HOUNDEYE || pTarget->Classify() == CLASS_ZOMBIE || pTarget->Classify() == CLASS_ANTLION || pTarget->Classify() == CLASS_BARNACLE || pTarget->Classify() == CLASS_VORTIGAUNT)) { return D_HT; }*/ return disp; } #if 0 void CNPC_Cremator::OnListened(void) { AISoundIter_t iter; CSound *pCurrentSound = GetSenses()->GetFirstHeardSound(&iter); static int ConditionsToClear[] = { COND_CREMATOR_DETECT_INTEREST, COND_CREMATOR_DETECT_NEW_INTEREST, }; ClearConditions(ConditionsToClear, ARRAYSIZE(ConditionsToClear)); while (pCurrentSound) { if (!pCurrentSound->FIsSound()) { if (pCurrentSound->m_iType & SOUND_CARCASS | SOUND_MEAT) { pInterestSound = pCurrentSound; Msg("Cremator smells a carcass\n"); SetCondition(COND_CREMATOR_FOUND_CORPSE); } } pCurrentSound = GetSenses()->GetNextHeardSound(&iter); } BaseClass::OnListened(); } #endif void CNPC_Cremator::SelectSkin(void) { switch (m_NPCState) { case NPC_STATE_COMBAT: { m_nSkin = CREMATOR_SKIN_ANGRY; break; } case NPC_STATE_ALERT: { m_nSkin = CREMATOR_SKIN_ALERT; break; } case NPC_STATE_IDLE: { m_nSkin = CREMATOR_SKIN_CALM; break; } case NPC_STATE_DEAD: { m_nSkin = CREMATOR_SKIN_DEAD; break; } default: m_nSkin = CREMATOR_SKIN_CALM; break; } } float CNPC_Cremator::MaxYawSpeed(void) { float flYS = 0; switch (GetActivity()) { case ACT_WALK_HURT: flYS = 30; break; case ACT_RUN: flYS = 90; break; case ACT_IDLE: flYS = 90; break; case ACT_RANGE_ATTACK1: flYS = 30; break; default: flYS = 90; break; } return flYS; } void CNPC_Cremator::AlertSound(void) { switch (random->RandomInt(0, 1)) { case 0: EmitSound("NPC_Cremator.NPCAlert"); break; case 1: EmitSound("NPC_Cremator.PlayerAlert"); break; } } void CNPC_Cremator::IdleSound(void) { int randSay = random->RandomInt(0, 2); if (randSay == 2) { if (m_bPlayAngrySound) { EmitSound("NPC_Cremator.AngryAmb"); } else { EmitSound("NPC_Cremator.BreathingAmb"); } } EmitSound("NPC_Cremator.ClothAmb"); } void CNPC_Cremator::DeathSound(const CTakeDamageInfo &info) { //EmitSound("NPC_Cremator.DeathAmb"); CPASAttenuationFilter filter(this); int iPitch = random->RandomInt(90, 105); enginesound->EmitSound(filter, entindex(), CHAN_VOICE, "npc/cremator/crem_die.wav", 1, ATTN_NORM, 0, iPitch); } int CNPC_Cremator::OnTakeDamage_Alive(const CTakeDamageInfo &info) { if (info.GetAttacker()) { if (info.GetAttacker()->IsPlayer()) { m_bIsPlayerEnemy = true; m_bPlayAngrySound = true; } else if (info.GetAttacker()->IsNPC() && info.GetAttacker()->Classify() != Classify()) { m_bIsNPCEnemy = true; m_bPlayAngrySound = true; } } /*if (GetActivity() == ACT_WALK && IsMoving() && m_iHealth < (sk_cremator_health.GetFloat() * 0.5)) SetActivity(ACT_WALK_HURT);*/ if (info.GetDamage() >= 20.f)//(infoCopy.GetDamageType() & DMG_BUCKSHOT) { //infoCopy.ScaleDamage(0.625); EmitSound("NPC_Cremator.Mad"); SetActivity(ACT_SMALL_FLINCH); } return BaseClass::OnTakeDamage_Alive(info); } void CNPC_Cremator::TraceAttack(const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator) { CTakeDamageInfo infoCopy = info; if (ptr->hitgroup == HITGROUP_HEAD) { m_bHeadshot = true; } if (infoCopy.GetDamageType() & DMG_BUCKSHOT) { infoCopy.ScaleDamage(0.625); } if (infoCopy.GetDamageType() & DMG_DISSOLVE) { infoCopy.ScaleDamage(0.05); } /*if (GetActivity() == ACT_WALK && IsMoving() && m_iHealth < (sk_cremator_health.GetFloat() * 0.5)) SetActivity(ACT_WALK_HURT);*/ /*if (infoCopy.GetDamage() >= 20.f)//(infoCopy.GetDamageType() & DMG_BUCKSHOT) { //infoCopy.ScaleDamage(0.625); SetActivity(ACT_SMALL_FLINCH); }*/ BaseClass::TraceAttack(infoCopy, vecDir, ptr, pAccumulator); } void CNPC_Cremator::Event_Killed(const CTakeDamageInfo &info) { DeathSound(info); DestroyEffect(); DoFireUp = false; StopFiring(); StopBreathSound(); this->StopLoopingSounds(); Vector Gun; QAngle Ang; GetAttachment(IMMOLATOR_ATTACHMENT, Gun); //VectorAngles(Gun, Ang); DropGun(Gun, this->GetAbsAngles()); if (PlayerHasMegaPhysCannon()) { StopParticleEffects(this); m_nSkin = CREMATOR_SKIN_DEAD; // turn the eyes black SetBodygroup(1, 1); // turn the gun off BaseClass::Event_Killed(info); return; } // over: NO head drop in overcharged /* if (m_bHeadshot && ((info.GetAmmoType() == GetAmmoDef()->Index(".50BMG")) // sniper ammo || (info.GetAmmoType() == GetAmmoDef()->Index("Buckshot")) // shotgun ammo || (info.GetAmmoType() == GetAmmoDef()->Index("Gauss")) // gauss ammo || (info.GetAmmoType() == GetAmmoDef()->Index("XBowBolt")) // crossbow ammo || (info.GetAmmoType() == GetAmmoDef()->Index("357")))) // revolver ammo { SetBodygroup(2, 1); // turn the head off Vector vecDamageDir = info.GetDamageForce(); VectorNormalize(vecDamageDir); DropHead(50, vecDamageDir); // spawn headprop } else if (info.GetDamageType() == DMG_BLAST) // blown up { SetBodygroup(2, 1); DropHead(300, Vector(0, 0, 1)); // spawn headprop } */ StopParticleEffects(this); m_nSkin = CREMATOR_SKIN_DEAD; // turn the eyes black SetBodygroup(1, 1); // turn the gun off BaseClass::Event_Killed(info); } // Not needed anymore /* const char *CNPC_Cremator::GetHeadpropModel(void) { return "models/cremator_headprop.mdl"; } void CNPC_Cremator::DropHead(int iVelocity, Vector &vecVelocity) { DestroyEffect(); DoFireUp = false; StopFiring(); CPhysicsProp *pGib = assert_cast(CreateEntityByName("prop_physics")); pGib->SetModel(GetHeadpropModel()); pGib->SetAbsOrigin(EyePosition()); pGib->SetAbsAngles(EyeAngles()); pGib->SetMoveType(MOVETYPE_VPHYSICS); pGib->SetCollisionGroup(COLLISION_GROUP_INTERACTIVE); pGib->SetOwnerEntity(this); pGib->Spawn(); pGib->SetAbsVelocity(vecVelocity * (iVelocity + RandomFloat(-10, 10))); } */ void CNPC_Cremator::DropGun(const Vector &vecVelocity, const QAngle &angles) { CPhysicsProp *pGib = assert_cast(CreateEntityByName("weapon_immolator")); //pGib->SetModel(GUNMODEL); // already in weapon_immolator script pGib->SetAbsOrigin(vecVelocity);//EyePosition() pGib->SetAbsAngles(angles);//EyeAngles() pGib->SetMoveType(MOVETYPE_VPHYSICS); pGib->SetCollisionGroup(COLLISION_GROUP_INTERACTIVE_DEBRIS); pGib->SetOwnerEntity(this); pGib->Spawn(); //pGib->SetAbsVelocity(vecVelocity * (iVelocity + RandomFloat(-10, 10))); DestroyEffect(); DoFireUp = false; StopFiring(); /* float flRandomVel = random->RandomFloat( -10, 10 ); pGib->GetMassCenter( &vecDir ); vecDir *= (iVelocity + flRandomVel) / 15; vecDir.z += 30.0f; AngularImpulse angImpulse = RandomAngularImpulse( -500, 500 ); IPhysicsObject *pObj = pGib->VPhysicsGetObject(); if ( pObj != NULL ) { pObj->AddVelocity( &vecDir, &angImpulse ); } */ } void CNPC_Cremator::HandleAnimEvent(animevent_t *pEvent) { switch (pEvent->event) { case CREMATOR_AE_FLLEFT: { LeftFootHit(pEvent->eventtime); } break; case CREMATOR_AE_FLRIGHT: { RightFootHit(pEvent->eventtime); } break; case CREMATOR_AE_IMMO_START: // for combat { if (m_flNextRangeAttack1Time < gpGlobals->curtime) { pEnemy = GetEnemyCombatCharacterPointer(); Assert(pEnemy != NULL); if (pEnemy && (pEnemy->IsPlayer() || pEnemy->IsNPC())) { DispatchSpray(pEnemy); } else if (pEnemy == NULL && pCorpse != NULL && GetAbsOrigin().DistTo(corpseCoord) <= distance) { pCorpse = dynamic_cast(MyCombatCharacterPointer()); DispatchSpray(pCorpse->GetAbsOrigin()); } else if (pEnemy == NULL && pCorpse != NULL && GetAbsOrigin().DistTo(corpseCoord) > distance) { DestroyEffect(); DoFireUp = false; StopFiring(); GetNavigator()->SetGoal(corpseCoord); GetMotor()->SetIdealYawToTargetAndUpdate(corpseCoord); } else if ((pCorpse == NULL && pEnemy == NULL)) { pCorpse = NULL; DestroyEffect(); DoFireUp = false; StopFiring(); if ((GetActivity() == ACT_FIREIDLE || GetActivity() == ACT_RANGE_ATTACK1)) { SetActivity(ACT_IDLE); ClearCondition(COND_CREMATOR_FOUND_CORPSE); } distance = 128.0f; Q_snprintf(pCorpseName, sizeof(pCorpseName), "prop_ragdoll"); } } } break; case CREMATOR_AE_IMMO_PARTICLE: { /*pEnemy = GetEnemyCombatCharacterPointer(); Assert(pEnemy != NULL); if (pEnemy && (pEnemy->IsPlayer() || pEnemy->IsNPC())) { DispatchSpray(pEnemy); } else if (pEnemy == NULL && pCorpse != NULL && GetAbsOrigin().DistTo(corpseCoord) <= distance) { pCorpse = dynamic_cast(MyCombatCharacterPointer()); DispatchSpray(pCorpse->GetAbsOrigin()); } else if (pEnemy == NULL && pCorpse != NULL && GetAbsOrigin().DistTo(corpseCoord) > distance) { DestroyEffect(); DoFireUp = false; StopFiring(); GetNavigator()->SetGoal(corpseCoord); GetMotor()->SetIdealYawToTargetAndUpdate(corpseCoord); } else if ((pCorpse == NULL && pEnemy == NULL)) { pCorpse = NULL; DestroyEffect(); DoFireUp = false; StopFiring(); if ((GetActivity() == ACT_FIREIDLE || GetActivity() == ACT_RANGE_ATTACK1)) { SetActivity(ACT_IDLE); ClearCondition(COND_CREMATOR_FOUND_CORPSE); } distance = 128.0f; Q_snprintf(pCorpseName, sizeof(pCorpseName), "prop_ragdoll"); }*/ } break; case CREMATOR_AE_IMMO_PARTICLEOFF: { StopFiring(); DoFireUp = false; StopParticleEffects(this); StopSound("Weapon_Immolator.Single"); //EmitSound("Weapon_Immolator.Stop"); } break; case CREMATOR_AE_RELOAD: { StopFiring(); ClearCondition(COND_CREMATOR_OUT_OF_AMMO); // Put your own ints here. This defines for how long a cremator would be able to fire at an enemy // Cremator gets shorter bursts on lower difficulty. On Hard, it can continously fire 60 attack cycles (1 ammo per cycle) if (g_pGameRules->IsSkillLevel(SKILL_EASY)) { m_iAmmo += 15; } else if (g_pGameRules->IsSkillLevel(SKILL_MEDIUM)) { m_iAmmo += 25; } else if (g_pGameRules->IsSkillLevel(SKILL_HARD)) { m_iAmmo += 60; } } break; case CREMATOR_AE_SPECIAL_START: // for corpse removal routine { m_flNextRangeAttack1Time = gpGlobals->curtime + 1.5f;//SequenceDuration(); EmitSound("Weapon_Immolator.Start"); DispatchParticleEffect("Immolator_line01", PATTACH_POINT_FOLLOW, this, IMMOLATOR_ATTACHMENT, false); DoFireUp = false; } break; case CREMATOR_AE_SPECIAL_MIDDLE: { DoFireUp = true; DispatchParticleEffect("Immolator_explodeMain", PATTACH_POINT_FOLLOW, this, IMMOLATOR_ATTACHMENT, false); StartFiring(); } break; case CREMATOR_AE_SPECIAL_END: { StopFiring(); StopParticleEffects(this); StopSound("Weapon_Immolator.Single"); //EmitSound("Weapon_Immolator.Stop"); } break; #if 0 case CREMATOR_AE_THROWGRENADE: { // DevMsg( "Throwing incendiary grenade!\n" ); ThrowIncendiaryGrenade(); if (g_pGameRules->IsSkillLevel(SKILL_EASY)) { m_flNextRangeAttack2Time = gpGlobals->curtime + random->RandomFloat(15.0f, 30.0f); } else if (g_pGameRules->IsSkillLevel(SKILL_HARD)) { m_flNextRangeAttack2Time = gpGlobals->curtime + random->RandomFloat(5.0f, 10.0f); } else { m_flNextRangeAttack2Time = gpGlobals->curtime + random->RandomFloat(10.0f, 20.0f); } } break; #endif default: BaseClass::HandleAnimEvent(pEvent); break; } } Vector CNPC_Cremator::LeftFootHit(float eventtime) { Vector footPosition; GetAttachment("footleft", footPosition); //CPASAttenuationFilter filter(this); //EmitSound(filter, entindex(), "NPC_Cremator.FootstepLeft", &footPosition, eventtime); FootstepEffect(footPosition); return footPosition; } Vector CNPC_Cremator::RightFootHit(float eventtime) { Vector footPosition; GetAttachment("footright", footPosition); //CPASAttenuationFilter filter(this); //EmitSound(filter, entindex(), "NPC_Cremator.FootstepRight", &footPosition, eventtime); FootstepEffect(footPosition); return footPosition; } void CNPC_Cremator::FootstepEffect(const Vector &origin) { trace_t tr; AI_TraceLine(origin, origin - Vector(0, 0, 0), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr); float yaw = random->RandomInt(0, 0); for (int i = 0; i < 2; i++) { if (UTIL_PointContents(tr.endpos + Vector(0, 0, 1)) & MASK_WATER) { float flWaterZ = UTIL_FindWaterSurface(tr.endpos, tr.endpos.z, tr.endpos.z + 100.0f); CEffectData data; data.m_fFlags = 0; data.m_vOrigin = tr.endpos; data.m_vOrigin.z = flWaterZ; data.m_vNormal = Vector(0, 0, 1); data.m_flScale = random->RandomFloat(10.0, 14.0); DispatchEffect("watersplash", data); } else { Vector dir = UTIL_YawToVector(yaw + i * 180) * 10; VectorNormalize(dir); dir.z = 0.25; VectorNormalize(dir); g_pEffects->Dust(tr.endpos, dir, 12, 50); } } } void CNPC_Cremator::RunAI(void) { /*if (pCorpse != NULL) { SearchForCorpses(); } if (!HasCondition(COND_CREMATOR_FOUND_CORPSE)) SearchForCorpses(); // FIXME: is it the best place for it? if (GetActivity() == ACT_WALK && IsMoving() && m_iHealth < (sk_cremator_health.GetFloat() * 0.5)) SetActivity(ACT_WALK_HURT); if (HasCondition(COND_CREMATOR_FOUND_CORPSE) && GetActivity() == ACT_IDLE && pCorpse != NULL && pEnemy == NULL && !this->IsMoving() ) { GetNavigator()->SetGoal(corpseCoord); } if (this->IsCurSchedule(SCHED_CREMATOR_INVESTIGATE_CORPSE) && pCorpse != NULL && GetAbsOrigin().DistTo(corpseCoord) <= distance) { GetNavigator()->StopMoving(); GetMotor()->SetIdealYawToTargetAndUpdate(corpseCoord); distance *= 3; } if ((GetActivity() == ACT_FIREIDLE || GetActivity() == ACT_RANGE_ATTACK1) && DoFireUp) { pEnemy = GetEnemyCombatCharacterPointer(); Assert(pEnemy != NULL); if (pEnemy && (pEnemy->IsPlayer() || pEnemy->IsNPC())) { DispatchSpray(pEnemy); } else if (pEnemy == NULL && pCorpse != NULL && GetAbsOrigin().DistTo(corpseCoord) <= distance) { pCorpse = dynamic_cast(MyCombatCharacterPointer()); DispatchSpray(pCorpse->GetAbsOrigin()); } else if (pEnemy == NULL && pCorpse != NULL && GetAbsOrigin().DistTo(corpseCoord) > distance) { DestroyEffect(); DoFireUp = false; StopFiring(); GetNavigator()->SetGoal(corpseCoord); GetMotor()->SetIdealYawToTargetAndUpdate(corpseCoord); } else if ((pCorpse == NULL && pEnemy == NULL)) { pCorpse = NULL; DestroyEffect(); DoFireUp = false; StopFiring(); if ((GetActivity() == ACT_FIREIDLE || GetActivity() == ACT_RANGE_ATTACK1)) { SetActivity(ACT_IDLE); ClearCondition(COND_CREMATOR_FOUND_CORPSE); } distance = 128.0f; Q_snprintf(pCorpseName, sizeof(pCorpseName), "prop_ragdoll"); } SetNextThink(gpGlobals->curtime + 0.05); } else { distance = 128.0f; StopFiring(); DoFireUp = false; if (m_pBeam1) { UTIL_Remove(m_pBeam1); m_pBeam1 = NULL; } if (m_pBeam2) { UTIL_Remove(m_pBeam2); m_pBeam2 = NULL; } if (m_pBeam3) { UTIL_Remove(m_pBeam3); m_pBeam3 = NULL; } SetNextThink(gpGlobals->curtime + 0.05); }*/ SetNextThink(gpGlobals->curtime + 0.05); BaseClass::RunAI(); } void CNPC_Cremator::StartTask(const Task_t *pTask) { switch (pTask->iTask) { case TASK_CREMATOR_INVESTIGATE_CORPSE: { AssertMsg(pCorpse != NULL, "The corpse the cremator was after, it's gone!\n"); if (pCorpse != NULL) { GetNavigator()->SetGoal(pCorpse->WorldSpaceCenter()); GetMotor()->SetIdealYawToTargetAndUpdate(corpseCoord); if (IsUnreachable(pCorpse)) { pCorpse->SetOwnerEntity(this); //HACKHACK: set the owner to prevent this unreachable corpse from being detected again. pCorpse = NULL; //forget about this corpse. ClearCondition(COND_CREMATOR_FOUND_CORPSE); TaskFail(FAIL_NO_ROUTE); } TaskComplete(); } SetNextThink(gpGlobals->curtime + 0.01); break; } case TASK_CREMATOR_BURN_CORPSE: { if (!pCorpse) { TaskFail(FAIL_NO_TARGET); ClearCondition(COND_CREMATOR_FOUND_CORPSE); } else { GetMotor()->SetIdealYawToTarget(pCorpse->GetAbsOrigin(), 0, 0); SetActivity(ACT_RANGE_ATTACK1); } break; } default: BaseClass::StartTask(pTask); break; } } void CNPC_Cremator::RunTask(const Task_t *pTask) { //DevMsg("pTask->iTask: %i \n", (int)pTask->iTask); switch (pTask->iTask) { case TASK_CREMATOR_INVESTIGATE_CORPSE: // FIXME: that never runs! { if (pCorpse != NULL && EnemyDistance(pCorpse) <= 15.f) { GetNavigator()->StopMoving(); TaskFail(FAIL_NO_TARGET); } SetNextThink(gpGlobals->curtime + 0.01); break; } case TASK_CREMATOR_BURN_CORPSE: { if (IsActivityFinished()) { TaskComplete(); ClearCondition(COND_CREMATOR_FOUND_CORPSE); } break; } case TASK_FACE_ENEMY: { if (pEnemy != NULL) { Vector flEnemyLKP = GetEnemyLKP(); GetMotor()->SetIdealYawToTargetAndUpdate(flEnemyLKP); } break; } case TASK_CREMATOR_RANGE_ATTACK1: { Assert(GetEnemy() != NULL); SetActivity(ACT_RANGE_ATTACK1); if (pEnemy != NULL) { Vector flEnemyLKP = GetEnemyLKP(); GetMotor()->SetIdealYawToTargetAndUpdate(flEnemyLKP); } if (m_iAmmo < 1 && IsActivityFinished()) { SetCondition(COND_CREMATOR_OUT_OF_AMMO); StopParticleEffects(this); StopSound("Weapon_Immolator.Single"); SetActivity(ACT_CREMATOR_RELOAD); TaskComplete(); SetNextThink(gpGlobals->curtime + 0.1f); } break; } default: BaseClass::RunTask(pTask); break; } } int CNPC_Cremator::RangeAttack1Conditions(float flDot, float flDist) { if (flDot < 0.7) { return COND_NOT_FACING_ATTACK; } else if (flDist > CREMATOR_IMMOLATOR_RANGE - 100) // create a buffer between us and the target { //DestroyEffect(); StopFiring(); StopSound(entindex(), "Weapon_Immolator.Start"); return COND_TOO_FAR_TO_ATTACK; } return COND_CAN_RANGE_ATTACK1; } NPC_STATE CNPC_Cremator::SelectIdealState(void) { switch (m_NPCState) { case NPC_STATE_COMBAT: { // COMBAT goes to ALERT upon death of enemy if (GetEnemy() == NULL) { //DestroyEffect(); m_bPlayAngrySound = false; m_nSkin = CREMATOR_SKIN_CALM; return NPC_STATE_IDLE; } break; } case NPC_STATE_IDLE: { if (HasCondition(COND_CREMATOR_FOUND_CORPSE)) { return NPC_STATE_ALERT; } break; } } return BaseClass::SelectIdealState(); } int CNPC_Cremator::TranslateSchedule(int scheduleType) { switch (scheduleType) { case SCHED_RANGE_ATTACK1: { return SCHED_CREMATOR_RANGE_ATTACK1; break; } case SCHED_RANGE_ATTACK2: { //DestroyEffect(); return SCHED_CREMATOR_RANGE_ATTACK2; break; } case SCHED_MOVE_TO_WEAPON_RANGE: { if (m_pBeam1 || m_pBeam2 || m_pBeam3) { StopFiring(); StopParticleEffects(this); } return SCHED_CREMATOR_CHASE_ENEMY; break; } } return BaseClass::TranslateSchedule(scheduleType); } //========================================================= // SetState //========================================================= void CNPC_Cremator::SetState(NPC_STATE State) { BaseClass::SetState(State); } int CNPC_Cremator::SelectSchedule(void) { SelectSkin(); switch (m_NPCState) { case NPC_STATE_IDLE: { if (m_pBeam1 || m_pBeam2 || m_pBeam3) { StopFiring(); StopParticleEffects(this); } if (!HasSpawnFlags(SF_CREMATOR_NO_PATROL_BEHAVIOUR)) return SCHED_CREMATOR_PATROL; } case NPC_STATE_ALERT: { if (HasCondition(COND_CREMATOR_FOUND_CORPSE) && !HasCondition(COND_LIGHT_DAMAGE | COND_HEAVY_DAMAGE) && GetEnemy() == NULL) { if (m_pBeam1 || m_pBeam2 || m_pBeam3) { StopFiring(); StopParticleEffects(this); } return SCHED_CREMATOR_INVESTIGATE_CORPSE; } else { if (m_pBeam1 || m_pBeam2 || m_pBeam3) { StopFiring(); StopParticleEffects(this); } } if (!HasSpawnFlags(SF_CREMATOR_NO_PATROL_BEHAVIOUR)) return SCHED_CREMATOR_PATROL; } case NPC_STATE_COMBAT: { if (HasCondition(COND_CAN_RANGE_ATTACK1)) { return SCHED_CREMATOR_RANGE_ATTACK1;//SCHED_RANGE_ATTACK1; } else { if (m_pBeam1 || m_pBeam2 || m_pBeam3) { StopFiring(); StopParticleEffects(this); } return SCHED_CREMATOR_CHASE_ENEMY; } #if 0 if (HasCondition(COND_CAN_RANGE_ATTACK2)) { return SCHED_CREMATOR_RANGE_ATTACK2; } if (HasCondition(COND_ENEMY_UNREACHABLE)) { return SCHED_CREMATOR_RANGE_ATTACK2; } #endif if (HasCondition(COND_ENEMY_DEAD)) { m_bPlayAngrySound = false; if (m_pBeam1 || m_pBeam2 || m_pBeam3) { StopFiring(); StopParticleEffects(this); } m_nSkin = CREMATOR_SKIN_CALM; return BaseClass::SelectSchedule(); } } } return BaseClass::SelectSchedule(); } void CNPC_Cremator::OnScheduleChange(void) { SelectSkin(); StopParticleEffects(this); //StopSound("Weapon_Immolator.Single"); BaseClass::OnScheduleChange(); } void CNPC_Cremator::PrescheduleThink(void) { BaseClass::PrescheduleThink(); #if 0 //if (pInterestSound && pInterestSound->m_iType & GetSoundInterests()) { DevMsg("Think... Searching corpses"); if (!HasCondition(COND_CREMATOR_FOUND_CORPSE)) SetCondition(COND_CREMATOR_FOUND_CORPSE); } #endif switch (m_NPCState) { case NPC_STATE_ALERT: { if (HasCondition(COND_CREMATOR_FOUND_CORPSE)) { if (HasCondition(COND_LIGHT_DAMAGE | COND_HEAVY_DAMAGE | COND_REPEATED_DAMAGE)) { ClearCondition(COND_CREMATOR_FOUND_CORPSE); // stop caring about stinks if we've been hit if (m_pBeam1 || m_pBeam2 || m_pBeam3) { StopFiring(); StopParticleEffects(this); } } } break; } } } void CNPC_Cremator::SearchForCorpses(void) { DevMsg("pCorpseName: %s \n", pCorpseName); pCorpse = gEntList.FindEntityGenericWithin(this, pCorpseName, GetAbsOrigin(), sk_cremator_corpse_search_radius.GetFloat());//prop_ragdoll pCorpse = gEntList.FindEntityGenericNearest(pCorpseName, GetAbsOrigin(), sk_cremator_corpse_search_radius.GetFloat()); if (pCorpse != NULL && pEnemy == NULL) EmitSound("NPC_Cremator.FindPlayer"); if (pCorpse) { //char tempName[150]; Q_snprintf(pCorpseName, sizeof(pCorpseName), "prop_ragdollCrem%d", this->entindex()); //pCorpseName = tempName; pCorpse->SetClassname(pCorpseName); corpseCoord.x = pCorpse->GetAbsOrigin().x; corpseCoord.y = pCorpse->GetAbsOrigin().y; corpseCoord.z = pCorpse->GetAbsOrigin().z; SetCondition(COND_CREMATOR_FOUND_CORPSE); } } void CNPC_Cremator::IncinerateCorpse(CBaseEntity *pTarget) { if (pTarget) { CEntityFlame *pFlame = CEntityFlame::Create(this); if (pFlame) { SetEffectEntity(NULL); pFlame->SetAbsOrigin(pTarget->GetAbsOrigin()); pFlame->AttachToEntity(pTarget); pFlame->AddEFlags(EFL_FORCE_CHECK_TRANSMIT); pFlame->AddEffects(EF_BRIGHTLIGHT); // create light from the fire pFlame->SetLifetime(20.0); // burn for 20 seconds pTarget->AddFlag(FL_ONFIRE); pTarget->SetEffectEntity(pFlame); pTarget->SetRenderColor(50, 50, 50); pTarget->SetOwnerEntity(this); // HACKHACK - we're marking this corpse so that it won't be picked again in the future. //DevMsg("This corpse has been handled. Moving on\n"); } } } void CNPC_Cremator::DispatchSpray(CBaseEntity *pEntity) { /*CBaseEntity *pVictum = GetEnemy(); if (pVictum) DevMsg("pVictum: %i \n", (int)pVictum->Classify());*/ Vector vecSrc, vecAim, vecAimCorpse; trace_t tr; //DevMsg("DispatchSpray \n"); Vector forward, right, up; AngleVectors(GetAbsAngles(), &forward, &right, &up); vecSrc = GetAbsOrigin() + up * 36; vecAim = GetShootEnemyDir(vecSrc); if (pEntity != NULL && (!pEntity->IsPlayer() && !pEntity->IsNPC())) vecAimCorpse = GetShootEnemyDir(pEntity->GetAbsOrigin()); //float deflection = 0.01; //vecAim = vecAim + 1 * right * random->RandomFloat(0, deflection) + up * random->RandomFloat(-deflection, deflection); //StartFiring(); Vector vAttachPos; GetAttachment(IMMOLATOR_ATTACHMENT, vAttachPos); Vector End = vAttachPos + (up + forward * MAX_TRACE_LENGTH); vecAim = GetShootEnemyDir(vAttachPos, false); //forward.x = pEntity->GetAbsOrigin().x; //forward.z = pEntity->GetAbsOrigin().z; //up.z = pEntity->GetAbsOrigin().z; if ((pEntity != NULL) && (pEntity->IsPlayer())) { //UTIL_TraceLine(vecSrc, vecSrc + vecAim * MAX_TRACE_LENGTH, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr); //AI_TraceLine(vAttachPos, right + up + forward * MAX_TRACE_LENGTH, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr); //AI_TraceLine(vAttachPos, pEntity->GetAbsOrigin() + forward * MAX_TRACE_LENGTH, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr); AI_TraceLine(vAttachPos, vecSrc + vecAim * MAX_TRACE_LENGTH, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr); //debugoverlay->AddLineOverlay(vAttachPos, End, 0, 255, 0, false, 10); } else if ((pEntity != NULL) && (pEntity->IsNPC())) { //AI_TraceLine(vecSrc, up + forward * MAX_TRACE_LENGTH, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr); AI_TraceLine(vAttachPos, vecSrc + vecAim * MAX_TRACE_LENGTH, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr); } pEntity = tr.m_pEnt; if (pEntity != NULL && m_takedamage) { ClearMultiDamage(); CTakeDamageInfo firedamage(this, this, sk_cremator_firedamage.GetFloat(), DMG_DISSOLVE); CTakeDamageInfo radiusdamage(this, this, sk_cremator_radiusdamage.GetFloat(), DMG_DISSOLVE); CalculateMeleeDamageForce(&firedamage, vecAim, tr.endpos); RadiusDamage(CTakeDamageInfo(this, this, 2, DMG_DISSOLVE), // AOE; this stuff makes cremators absurdly powerfull sometimes btw tr.endpos, 64.0f, CLASS_NONE, NULL); pEntity->DispatchTraceAttack((firedamage), vecAim, &tr); DestroyEffect(); //debugoverlay->AddLineOverlay(vAttachPos, vAttachPos + forward * MAX_TRACE_LENGTH, 0, 255, 0, false, 10); if (m_pBeam1 == NULL) { m_pBeam1 = CBeam::BeamCreate(CREMATOR_BEAM_SPRITE, 1.5); //if (pEntity->IsPlayer() || pEntity->IsNPC()) m_pBeam1->PointEntInit(vAttachPos + vecAim * MAX_TRACE_LENGTH, this); //else //m_pBeam1->PointEntInit(tr.endpos, this); //m_pBeam1->PointEntInit(End, this); m_pBeam1->SetEndAttachment(IMMOLATOR_ATTACHMENT); m_pBeam1->SetBrightness(99000); m_pBeam1->SetColor(0, 255, 0); m_pBeam1->AddSpawnFlags(SF_SPRITE_TEMPORARY); } if (m_pBeam2 == NULL) { m_pBeam2 = CBeam::BeamCreate(CREMATOR_BEAM_SPRITE, 1.0); //if (pEntity->IsPlayer() || pEntity->IsNPC()) m_pBeam2->PointEntInit(vAttachPos + vecAim * MAX_TRACE_LENGTH, this); //else //m_pBeam2->PointEntInit(tr.endpos, this); m_pBeam2->SetEndAttachment(IMMOLATOR_ATTACHMENT); m_pBeam2->AddSpawnFlags(SF_BEAM_TEMPORARY); m_pBeam2->SetBrightness(2255); m_pBeam2->SetColor(0, 255, 0); m_pBeam2->SetNoise(RandomFloat(0.5f, 2.0f)); } if (m_pBeam3 == NULL) { m_pBeam3 = CBeam::BeamCreate(CREMATOR_BEAM_SPRITE, 1.0); //if (pEntity->IsPlayer() || pEntity->IsNPC()) m_pBeam3->PointEntInit(vAttachPos + vecAim * MAX_TRACE_LENGTH, this); //else //m_pBeam3->PointEntInit(tr.endpos, this); m_pBeam3->SetEndAttachment(IMMOLATOR_ATTACHMENT); m_pBeam3->AddSpawnFlags(SF_BEAM_TEMPORARY); m_pBeam3->SetBrightness(2255); m_pBeam3->SetColor(0, 255, 0); m_pBeam3->SetNoise(RandomFloat(0.5f, 2.0f)); } DispatchParticleEffect("immolator_normal", PATTACH_POINT_FOLLOW, this, IMMOLATOR_ATTACHMENT, false); DispatchParticleEffect("immolator_sparks01", PATTACH_POINT_FOLLOW, this, IMMOLATOR_ATTACHMENT, false); SetNextThink(gpGlobals->curtime + 0.001); //if (pEntity != NULL && (pEntity->IsPlayer() || pEntity->IsNPC())) UpdateEffect(vAttachPos, tr.endpos);//tr.endpos //else //UpdateEffect(vAttachPos, pEntity->WorldSpaceCenter());//tr.endpos if (tr.DidHit()) { int beams; CPASFilter filter(GetAbsOrigin()); te->DynamicLight(filter, 0.0, &tr.endpos, 0, 255, 0, 0, 340, 0.02, 0); for (beams = 0; beams < 5; beams++) { // Vector vecDest; Vector vecDest; // Random unit vector vecDest.x = random->RandomFloat(-1, 1); vecDest.y = random->RandomFloat(-1, 1); vecDest.z = random->RandomFloat(-1, 1); // Push out to radius dist. vecDest = tr.endpos + vecDest * 1.1*55.0f; UTIL_Beam(tr.endpos, vecDest, beamIndex, 0, //halo index 0, //frame start 2.0f, //framerate 0.15f, //life 1.5, // width 1.15, // endwidth 0.75, // fadelength, 25, // noise 0, // red 255, // green 0, // blue, 128, // bright 100 // speed ); } } } m_iAmmo--; } void CNPC_Cremator::UpdateEffect(const Vector &startPoint, const Vector &endPoint) { if (m_pBeam1) { m_pBeam1->SetAbsOrigin(endPoint); } if (m_pBeam2) { m_pBeam2->SetAbsOrigin(endPoint); } if (m_pBeam3) { m_pBeam3->SetAbsOrigin(endPoint); } } void CNPC_Cremator::DestroyEffect(void) { //StopSound("Weapon_Immolator.Single"); //EmitSound("Weapon_Immolator.Stop"); if (m_pBeam1) { UTIL_Remove(m_pBeam1); m_pBeam1 = NULL; } if (m_pBeam2) { UTIL_Remove(m_pBeam2); m_pBeam2 = NULL; } if (m_pBeam3) { UTIL_Remove(m_pBeam3); m_pBeam3 = NULL; } } void CNPC_Cremator::DispatchSpray(const Vector &endPoint) { /*if (pCorpse != pCorpseLast) { DestroyEffect(); return; }*/ Vector vecSrc, vecZero, vecAim, vecAimCorpse; trace_t tr; //DevMsg("DispatchSpray \n"); Vector forward, right, up; AngleVectors(GetAbsAngles(), &forward, &right, &up); vecZero = pCorpse->WorldSpaceCenter(); vecSrc = GetAbsOrigin();// +up * 36; vecAim = GetShootEnemyDir(vecSrc); if (pCorpse != NULL) vecAimCorpse = GetShootEnemyDir(pCorpse->WorldSpaceCenter()); //float deflection = 0.01; //vecAim = vecAim + 1 * right * random->RandomFloat(0, deflection) + up * random->RandomFloat(-deflection, deflection); //DevMsg("pCorpse->GetAbsOrigin().x: %.2f \n", pCorpse->GetAbsOrigin().x); //DevMsg("pCorpse->GetAbsOrigin()y: %.2f \n", pCorpse->GetAbsOrigin().y); //DevMsg("pCorpse->GetAbsOrigin()z: %.2f \n", pCorpse->GetAbsOrigin().z); //StartFiring(); Vector vAttachPos, Dist; QAngle Ang; GetAttachment(IMMOLATOR_ATTACHMENT, vAttachPos); Vector End = (vAttachPos + up + right + forward) * MAX_TRACE_LENGTH; VectorAngles(End, Ang); Ang.x += -90; Vector X, Y, Z; AngleVectors(Ang, &X, &Y, &Z); Vector Complete = X + Y + Z; DevMsg("Firing in corpse \n"); Vector corpse; corpse.x = corpseCoord.x;//cvar->FindVar("oc_ragdoll_worldpos_x")->GetFloat(); corpse.y = corpseCoord.y;//cvar->FindVar("oc_ragdoll_worldpos_y")->GetFloat(); corpse.z = corpseCoord.z;//cvar->FindVar("oc_ragdoll_worldpos_z")->GetFloat(); GetMotor()->SetIdealYawToTargetAndUpdate(corpse); //UTIL_TraceLine(vecSrc, vecSrc + vecAim * 521, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr); //UTIL_TraceLine(vecSrc, vecSrc + vecAim * GetAbsOrigin().DistTo(pCorpse->WorldSpaceCenter()) * 2.7f, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr); AI_TraceLine(vAttachPos, corpse, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr); //UTIL_TraceLine(vecSrc, u, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr); //pCorpse = tr.m_pEnt; if (pCorpse != NULL && m_takedamage) { ClearMultiDamage(); CTakeDamageInfo firedamage(this, this, sk_cremator_firedamage.GetFloat(), DMG_DISSOLVE); CTakeDamageInfo radiusdamage(this, this, sk_cremator_radiusdamage.GetFloat(), DMG_DISSOLVE); CalculateMeleeDamageForce(&firedamage, vecAim, tr.endpos); RadiusDamage(CTakeDamageInfo(this, this, 2, DMG_DISSOLVE), // AOE; this stuff makes cremators absurdly powerfull sometimes btw tr.endpos, 64.0f, CLASS_NONE, NULL); pCorpse->DispatchTraceAttack((firedamage), vecAim, &tr); DestroyEffect(); //debugoverlay->AddLineOverlay(vAttachPos, vAttachPos + forward * MAX_TRACE_LENGTH, 0, 255, 0, false, 10); if (m_pBeam1 == NULL) { m_pBeam1 = CBeam::BeamCreate(CREMATOR_BEAM_SPRITE, 1.5); //if (pEntity->IsPlayer() || pEntity->IsNPC()) m_pBeam1->PointEntInit(vAttachPos + vecAim * MAX_TRACE_LENGTH, this); //else //m_pBeam1->PointEntInit(tr.endpos, this); //m_pBeam1->PointEntInit(End, this); m_pBeam1->SetEndAttachment(IMMOLATOR_ATTACHMENT); m_pBeam1->SetBrightness(99000); m_pBeam1->SetColor(0, 255, 0); m_pBeam1->AddSpawnFlags(SF_SPRITE_TEMPORARY); } if (m_pBeam2 == NULL) { m_pBeam2 = CBeam::BeamCreate(CREMATOR_BEAM_SPRITE, 1.0); //if (pEntity->IsPlayer() || pEntity->IsNPC()) m_pBeam2->PointEntInit(vAttachPos + vecAim * MAX_TRACE_LENGTH, this); //else //m_pBeam2->PointEntInit(tr.endpos, this); m_pBeam2->SetEndAttachment(IMMOLATOR_ATTACHMENT); m_pBeam2->AddSpawnFlags(SF_BEAM_TEMPORARY); m_pBeam2->SetBrightness(2255); m_pBeam2->SetColor(0, 255, 0); m_pBeam2->SetNoise(RandomFloat(0.5f, 2.0f)); } if (m_pBeam3 == NULL) { m_pBeam3 = CBeam::BeamCreate(CREMATOR_BEAM_SPRITE, 1.0); //if (pEntity->IsPlayer() || pEntity->IsNPC()) m_pBeam3->PointEntInit(vAttachPos + vecAim * MAX_TRACE_LENGTH, this); //else //m_pBeam3->PointEntInit(tr.endpos, this); m_pBeam3->SetEndAttachment(IMMOLATOR_ATTACHMENT); m_pBeam3->AddSpawnFlags(SF_BEAM_TEMPORARY); m_pBeam3->SetBrightness(2255); m_pBeam3->SetColor(0, 255, 0); m_pBeam3->SetNoise(RandomFloat(0.5f, 2.0f)); } DispatchParticleEffect("immolator_normal", PATTACH_POINT_FOLLOW, this, IMMOLATOR_ATTACHMENT, false); DispatchParticleEffect("immolator_sparks01", PATTACH_POINT_FOLLOW, this, IMMOLATOR_ATTACHMENT, false); SetNextThink(gpGlobals->curtime + 0.001); //if (pEntity != NULL && (pEntity->IsPlayer() || pEntity->IsNPC())) UpdateEffect(vAttachPos, tr.endpos);//tr.endpos //else //UpdateEffect(vAttachPos, pEntity->WorldSpaceCenter());//tr.endpos //if (tr.DidHit()) { int beams; CPASFilter filter(GetAbsOrigin()); te->DynamicLight(filter, 0.0, &tr.endpos, 0, 255, 0, 0, 340, 0.02, 0); for (beams = 0; beams < 5; beams++) { // Vector vecDest; Vector vecDest; // Random unit vector vecDest.x = random->RandomFloat(-1, 1); vecDest.y = random->RandomFloat(-1, 1); vecDest.z = random->RandomFloat(-1, 1); // Push out to radius dist. vecDest = tr.endpos + vecDest * 1.1*55.0f; UTIL_Beam(tr.endpos, vecDest, beamIndex, 0, //halo index 0, //frame start 2.0f, //framerate 0.15f, //life 2, // width 1.25, // endwidth 0.75, // fadelength, 15, // noise 0, // red 255, // green 0, // blue, 128, // bright 100 // speed ); } } } m_iAmmo--; } /*Activity CNPC_Cremator::TranslateActivity(Activity activity) //OLD { Assert(activity != ACT_INVALID); switch (activity) { case ACT_RUN: { return (Activity)ACT_WALK; } } return activity; }*/ Activity CNPC_Cremator::TranslateActivity(Activity activity) { Assert(activity != ACT_INVALID); switch (activity) { case ACT_RUN: { DestroyEffect(); DoFireUp = false; if (m_iHealth < (sk_cremator_health.GetFloat() * 0.5)) { return (Activity)ACT_WALK_HURT; } else return (Activity)ACT_WALK; } } /*if (this->GetDamage() > 10.f) { return ACT_SMALL_FLINCH; }*/ return activity; } Activity CNPC_Cremator::NPC_TranslateActivity(Activity activity) { float fiftyprecent = 0.5 * sk_cremator_health.GetInt(); if ((activity == ACT_WALK || activity == ACT_RUN) && (GetHealth() < fiftyprecent)) { return ACT_WALK_HURT; } /*if (this->GetDamage() > 10.f) { return ACT_SMALL_FLINCH; }*/ return activity; } int CNPC_Cremator::GetSoundInterests(void) { return SOUND_WORLD | SOUND_COMBAT | SOUND_BULLET_IMPACT | SOUND_CARCASS | SOUND_MEAT | SOUND_GARBAGE | SOUND_PLAYER | SOUND_MOVE_AWAY; } //----------------------------------------------------------------------------- // Create/destroy looping sounds //----------------------------------------------------------------------------- void CNPC_Cremator::CreateSounds() { CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); CPASAttenuationFilter filter(this); if (!m_pGunFiringSound) { m_pGunFiringSound = controller.SoundCreate(filter, entindex(), "Weapon_Immolator.Single"); controller.Play(m_pGunFiringSound, 0, 100); } } void CNPC_Cremator::DestroySounds() { CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); controller.SoundDestroy(m_pGunFiringSound); m_pGunFiringSound = NULL; } //----------------------------------------------------------------------------- // Stop Firing //----------------------------------------------------------------------------- void CNPC_Cremator::StartFiring() { if (!m_bIsFiring) { pController = &CSoundEnvelopeController::GetController(); m_bIsFiring = true; } float flVolume = pController->SoundGetVolume(m_pGunFiringSound); pController->SoundChangeVolume(m_pGunFiringSound, 1.4f, 0.1f * (1.0f - flVolume)); //int iPitch = 100; pController->SoundChangePitch(m_pGunFiringSound, 100 * cvar->FindVar("host_timescale")->GetFloat(), 1.0f); } void CNPC_Cremator::StopFiring() { DestroyEffect(); DoFireUp = false; if (m_bIsFiring) { pController = &CSoundEnvelopeController::GetController(); float flVolume = pController->SoundGetVolume(m_pGunFiringSound); pController->SoundChangeVolume(m_pGunFiringSound, 0.0f, 0.1f * flVolume); pController->SoundGetPitch(m_pGunFiringSound); //int iPitch = 100; pController->SoundChangePitch(m_pGunFiringSound, 100 * cvar->FindVar("host_timescale")->GetFloat(), 1.0f); StopSound("Weapon_Immolator.Start"); EmitSound("Weapon_Immolator.Stop"); m_bIsFiring = false; if (!this) return; // StopParticleEffects(pOwner->GetViewModel()); DispatchParticleEffect("weapon_muzzle_smoke_immolator", PATTACH_POINT_FOLLOW, this, IMMOLATOR_ATTACHMENT, true); DispatchParticleEffect("weapon_muzzle_smoke_immolator1", PATTACH_POINT_FOLLOW, this, IMMOLATOR_ATTACHMENT, false); } } void CNPC_Cremator::CreateBreathSound() { return; CSoundEnvelopeController &pBreath = CSoundEnvelopeController::GetController(); CPASAttenuationFilter filter(this); if (!m_pBreathSound) { m_pBreathSound = pBreath.SoundCreate(filter, entindex(), "NPC_Cremator.Breath"); pBreath.Play(m_pBreathSound, 0, 100); } } void CNPC_Cremator::DestroyBreathSound() { return; CSoundEnvelopeController &pBreath = CSoundEnvelopeController::GetController(); pBreath.SoundDestroy(m_pBreathSound); m_pBreathSound = NULL; } //----------------------------------------------------------------------------- // Stop Firing //----------------------------------------------------------------------------- void CNPC_Cremator::StartBreathSound() { return; if (!m_breath) { pBreath = &CSoundEnvelopeController::GetController(); m_breath = true; } float flVolume = pBreath->SoundGetVolume(m_pBreathSound); pBreath->SoundChangeVolume(m_pBreathSound, 1.4f, 0.1f * (1.0f - flVolume)); pBreath->SoundChangePitch(m_pBreathSound, 100 * cvar->FindVar("host_timescale")->GetFloat(), 1.0f); } void CNPC_Cremator::StopBreathSound() { return; if (m_breath) { pBreath = &CSoundEnvelopeController::GetController(); float flVolume = pBreath->SoundGetVolume(m_pBreathSound); pBreath->SoundChangeVolume(m_pBreathSound, 0.0f, 0.1f * flVolume); pBreath->SoundGetPitch(m_pBreathSound); //int iPitch = 100; pBreath->SoundChangePitch(m_pBreathSound, 100 * cvar->FindVar("host_timescale")->GetFloat(), 1.0f); m_breath = false; if (!this) return; } } AI_BEGIN_CUSTOM_NPC(npc_cremator, CNPC_Cremator) DECLARE_ACTIVITY(ACT_FIRESPREAD) DECLARE_ACTIVITY(ACT_FIREIDLE) DECLARE_ACTIVITY(ACT_CREMATOR_ARM) DECLARE_ACTIVITY(ACT_CREMATOR_DISARM) DECLARE_ACTIVITY(ACT_CREMATOR_RELOAD) DECLARE_CONDITION(COND_CREMATOR_OUT_OF_AMMO) DECLARE_CONDITION(COND_CREMATOR_ENEMY_WITH_HIGHER_PRIORITY) DECLARE_CONDITION(COND_CREMATOR_FOUND_CORPSE) DECLARE_TASK(TASK_CREMATOR_RANGE_ATTACK1) DECLARE_TASK(TASK_CREMATOR_RELOAD) DECLARE_TASK(TASK_CREMATOR_INVESTIGATE_CORPSE) DECLARE_TASK(TASK_CREMATOR_BURN_CORPSE) DEFINE_SCHEDULE( SCHED_CREMATOR_CHASE_ENEMY, " Tasks" " TASK_GET_CHASE_PATH_TO_ENEMY 300"//1024 " TASK_SET_TOLERANCE_DISTANCE 24"//250 " TASK_WALK_PATH 0" " TASK_WAIT_FOR_MOVEMENT 0" " TASK_FACE_ENEMY 0" "" " Interrupts" " COND_CAN_RANGE_ATTACK1" " COND_ENEMY_DEAD" " COND_LOST_ENEMY" " COND_CREMATOR_ENEMY_WITH_HIGHER_PRIORITY" ) DEFINE_SCHEDULE( SCHED_CREMATOR_RANGE_ATTACK1, " Tasks" " TASK_STOP_MOVING 0" //" TASK_FACE_ENEMY 0" //" TASK_ANNOUNCE_ATTACK 1" " TASK_PLAY_SEQUENCE ACTIVITY:ACT_CREMATOR_ARM" " TASK_CREMATOR_RANGE_ATTACK1 0" " TASK_PLAY_SEQUENCE ACTIVITY:ACT_CREMATOR_RELOAD" "" " Interrupts" " COND_HEAVY_DAMAGE" " COND_REPEATED_DAMAGE" " COND_ENEMY_DEAD" " COND_TOO_FAR_TO_ATTACK" " COND_CREMATOR_ENEMY_WITH_HIGHER_PRIORITY" ) DEFINE_SCHEDULE( SCHED_CREMATOR_PATROL, " Tasks" " TASK_STOP_MOVING 0" " TASK_GET_PATH_TO_RANDOM_NODE 1024" " TASK_WALK_PATH 0" " TASK_WAIT_FOR_MOVEMENT 0" " TASK_WAIT 5" " TASK_SET_SCHEDULE SCHEDULE:SCHED_CREMATOR_PATROL" "" " Interrupts" " COND_CREMATOR_FOUND_CORPSE" " COND_NEW_ENEMY" " COND_LIGHT_DAMAGE" " COND_HEAVY_DAMAGE" ) DEFINE_SCHEDULE( SCHED_CREMATOR_INVESTIGATE_CORPSE, // we're here because we have COND_CREMATOR_FOUND_CORPSE. " Tasks" " TASK_WAIT_FOR_MOVEMENT 0" " TASK_SOUND_WAKE 0" // Play the alert sound " TASK_CREMATOR_INVESTIGATE_CORPSE 64" // analogous to TASK_GET_PATH_TO_BESTSCENT //" TASK_WALK_PATH 0" " TASK_WAIT_FOR_MOVEMENT 0" " TASK_CREMATOR_BURN_CORPSE 0" // Play the firespread animation " TASK_WAIT 3" //" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CREMATOR_INVESTIGATE_CORPSE" " TASK_SET_SCHEDULE SCHEDULE:SCHED_CREMATOR_PATROL" // resume patroling "" " Interrupts" " COND_NEW_ENEMY" " COND_SEE_ENEMY" " COND_LIGHT_DAMAGE" " COND_HEAVY_DAMAGE" ) AI_END_CUSTOM_NPC()