#include "monsters" #include "../weapons/projectile" #include "../items" const string Q1_ARMY_MODEL = "models/quake1/m_army.mdl"; const string Q1_ARMY_IDLE = "quake1/monsters/army/idle.wav"; const string Q1_ARMY_SIGHT = "quake1/monsters/army/sight.wav"; const array Q1_ARMY_PAIN = { "quake1/monsters/army/pain1.wav", "quake1/monsters/army/pain2.wav" }; const string Q1_ARMY_DEATH = "quake1/monsters/army/death.wav"; const string Q1_ARMY_SHOOT = "quake1/monsters/army/shoot.wav"; enum Q1_ARMY_EVENTS { ARMY_END_ATTACK = 1, ARMY_DROP_BACKPACK = 2, ARMY_SHOOT = 3, ARMY_IDLE_SOUND = 4 } class monster_qarmy : ScriptBaseMonsterEntity, monster_qgeneric { void Spawn() { Precache(); g_EntityFuncs.SetModel(self, Q1_ARMY_MODEL); self.m_bloodColor = BLOOD_COLOR_RED; self.pev.health = 30; self.pev.solid = SOLID_SLIDEBOX; self.pev.movetype = MOVETYPE_STEP; g_EntityFuncs.SetSize(self.pev, Vector(-16, -16, -24), Vector(16, 16, 40)); self.StartMonster(); self.m_MonsterState = MONSTERSTATE_NONE; self.m_FormattedName = "Grunt"; self.pev.pain_finished = 0.0; m_iGibHealth = -35; WalkMonsterInit(); } void Precache() { g_Game.PrecacheModel(Q1_ARMY_MODEL); g_SoundSystem.PrecacheSound(Q1_ARMY_IDLE); g_SoundSystem.PrecacheSound(Q1_ARMY_SIGHT); g_SoundSystem.PrecacheSound(Q1_ARMY_DEATH); g_SoundSystem.PrecacheSound(Q1_ARMY_SHOOT); for (uint i = 0; i < Q1_ARMY_PAIN.length(); ++i) g_SoundSystem.PrecacheSound(Q1_ARMY_PAIN[i]); } void MonsterIdle() { m_iAIState = STATE_IDLE; SetActivity(ACT_IDLE); m_flMonsterSpeed = 0; } void MonsterSight() { AI_Face(); g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_ARMY_SIGHT, Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + Math.RandomLong(0, 0xf)); } void MonsterWalk() { m_iAIState = STATE_WALK; SetActivity(ACT_WALK); m_flMonsterSpeed = 2.5; AI_Turn(); } void MonsterRun() { m_iAIState = STATE_RUN; SetActivity(ACT_RUN); m_flMonsterSpeed = 8; } void MonsterMissileAttack() { if (Math.RandomFloat(0, 1) < 0.3) m_fLeftY = !m_fLeftY; m_iAIState = STATE_ATTACK; SetActivity(ACT_RANGE_ATTACK1); } void MonsterPain(CBaseEntity@ pAttacker, float flDamage) { if (self.pev.pain_finished > g_Engine.time) return; int i = Math.RandomLong(0, 1); g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_ARMY_PAIN[i], Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + Math.RandomLong(0, 0xf)); m_iAIState = STATE_PAIN; self.SetActivity(ACT_BIG_FLINCH); m_flMonsterSpeed = 0; self.pev.pain_finished = g_Engine.time + 1.0 - 0.3; // TODO: replace the 1.0 with sequence duration } bool MonsterHasMissileAttack() { return true; } bool MonsterHasPain() { return true; } void MonsterAttack() { if (m_fAttackDone) { m_fAttackDone = false; m_fInAttack = false; if (CheckRefire()) MonsterMissileAttack(); else MonsterRun(); } AI_Face(); } void MonsterFire() { if (m_fInAttack) return; AI_Face(); g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_ARMY_SHOOT, Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + Math.RandomLong(0, 0xf)); // fire somewhat behind the player, so a dodging player is harder to hit CBaseEntity@ pEnemy = m_hEnemy; Vector vecDir = ((pEnemy.pev.origin - pEnemy.pev.velocity * 0.2f) - self.pev.origin).Normalize(); self.FireBullets(4, self.pev.origin, vecDir, Vector(0.1, 0.1, 0), 2048, BULLET_PLAYER_CUSTOMDAMAGE, 4, 4); self.pev.effects |= EF_MUZZLEFLASH; m_fInAttack = true; } void MonsterKilled(entvars_t@ pevAttacker, int iGib) { item_qbackpack@ pPack = q1_SpawnBackpack(self); pPack.m_iAmmoShells = 5; if (ShouldGibMonster(iGib)) { g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, "quake1/gib.wav", Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + Math.RandomLong(0, 0xf)); g_EntityFuncs.SpawnRandomGibs(self.pev, 1, 1); g_EntityFuncs.Remove(self); return; } g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_ARMY_DEATH, Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + Math.RandomLong(0, 0xf)); } void HandleAnimEvent(MonsterEvent@ pEvent) { switch (pEvent.event) { case ARMY_END_ATTACK: m_fAttackDone = true; m_fInAttack = true; break; case ARMY_DROP_BACKPACK: break; case ARMY_SHOOT: MonsterFire(); break; case ARMY_IDLE_SOUND: if (Math.RandomFloat(0, 1) < 0.2) g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_ARMY_IDLE, Math.RandomFloat(0.95, 1.0), ATTN_IDLE, 0, 93 + Math.RandomLong(0, 0xf)); break; default: BaseClass.HandleAnimEvent(pEvent); } } } void q1_RegisterMonster_ARMY() { g_CustomEntityFuncs.RegisterCustomEntity("monster_qarmy", "monster_qarmy"); }