#include "monsters" #include "../weapons/projectile" const string Q1_OGRE_MODEL = "models/quake1/m_ogre.mdl"; const array Q1_OGRE_IDLE = { "quake1/monsters/ogre/idle1.wav", "quake1/monsters/ogre/idle2.wav" }; const string Q1_OGRE_SIGHT = "quake1/monsters/ogre/sight.wav"; const string Q1_OGRE_PAIN = "quake1/monsters/ogre/pain.wav"; const string Q1_OGRE_DEATH = "quake1/monsters/ogre/death.wav"; const string Q1_OGRE_SHOOT = "quake1/weapons/grenade.wav"; const string Q1_OGRE_MELEE = "quake1/monsters/ogre/melee.wav"; const string Q1_OGRE_DRAG = "quake1/monsters/ogre/drag.wav"; enum Q1_OGRE_EVENTS { OGRE_DROP_BACKPACK = 1, OGRE_IDLE_SOUND = 2, OGRE_IDLE_SOUND2 = 3, OGRE_DRAG_SOUND = 4, OGRE_SHOOT_GRENADE = 5, OGRE_CHAINSAW = 6, OGRE_CHAINSAW_SOUND = 7 } class monster_qogre : ScriptBaseMonsterEntity, monster_qgeneric { void Spawn() { Precache(); g_EntityFuncs.SetModel(self, Q1_OGRE_MODEL); self.m_bloodColor = BLOOD_COLOR_RED; self.pev.health = 200; self.pev.solid = SOLID_SLIDEBOX; self.pev.movetype = MOVETYPE_STEP; g_EntityFuncs.SetSize(self.pev, Vector(-20, -20, -24), Vector(20, 20, 40)); self.StartMonster(); self.m_MonsterState = MONSTERSTATE_NONE; self.m_FormattedName = "Ogre"; self.pev.pain_finished = 0.0; m_iGibHealth = -80; WalkMonsterInit(); } void Precache() { g_Game.PrecacheModel(Q1_OGRE_MODEL); g_SoundSystem.PrecacheSound(Q1_OGRE_SIGHT); g_SoundSystem.PrecacheSound(Q1_OGRE_PAIN); g_SoundSystem.PrecacheSound(Q1_OGRE_DEATH); g_SoundSystem.PrecacheSound(Q1_OGRE_SHOOT); g_SoundSystem.PrecacheSound(Q1_OGRE_MELEE); g_SoundSystem.PrecacheSound(Q1_OGRE_DRAG); for (uint i = 0; i < Q1_OGRE_IDLE.length(); ++i) g_SoundSystem.PrecacheSound(Q1_OGRE_IDLE[i]); } void CornerReached() { g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_OGRE_DRAG, Math.RandomFloat(0.95, 1.0), ATTN_IDLE, 0, 93 + Math.RandomLong(0, 0xf)); } void MonsterIdle() { m_iAIState = STATE_IDLE; SetActivity(ACT_IDLE); m_flMonsterSpeed = 0; } void MonsterSight() { AI_Face(); g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_OGRE_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 = 5; AI_Turn(); } void MonsterRun() { m_iAIState = STATE_RUN; SetActivity(ACT_RUN); m_flMonsterSpeed = 16; } void MonsterMissileAttack() { m_iAIState = STATE_ATTACK; SetActivity(ACT_RANGE_ATTACK1); } void MonsterMeleeAttack() { m_iAIState = STATE_ATTACK; SetActivity(ACT_MELEE_ATTACK1); } void MonsterPain(CBaseEntity@ pAttacker, float flDamage) { if (self.pev.pain_finished > g_Engine.time) return; g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_OGRE_PAIN, 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 MonsterHasMeleeAttack() { return true; } bool MonsterHasPain() { return true; } void MonsterAttack() { if (m_Activity == ACT_RANGE_ATTACK1) AI_Face(); else if (m_Activity == ACT_MELEE_ATTACK1) AI_Charge(4); if (m_iAIState == STATE_ATTACK && self.m_fSequenceFinished) MonsterRun(); } void MonsterGrenade() { g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_OGRE_SHOOT, Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + Math.RandomLong(0, 0xf)); self.pev.effects |= EF_MUZZLEFLASH; CBaseEntity@ pEnemy = m_hEnemy; Vector vecVelocity = (pEnemy.pev.origin - self.pev.origin).Normalize() * 600.0f; vecVelocity.z = 200; auto @pGrenade = q1_ShootCustomProjectile("projectile_qgrenade", "models/quake1/grenade.mdl", self.pev.origin, vecVelocity, self.pev.angles, self); pGrenade.pev.dmg = 40; } void MonsterChainsaw(float side = 0.0) { if (!m_hEnemy) return; CBaseEntity@ pEnemy = m_hEnemy; if (pEnemy.pev.takedamage == 0) return; AI_Charge(10); Vector delta = pEnemy.pev.origin - self.pev.origin; if (delta.Length() > 100) return; float ldmg = (Math.RandomFloat(0.0, 1.0) + Math.RandomFloat(0.0, 1.0) + Math.RandomFloat(0.0, 1.0)) * 4; pEnemy.TakeDamage(self.pev, self.pev, ldmg, DMG_GENERIC); if (side != 0.0) { g_EngineFuncs.MakeVectors(self.pev.angles); if (side == 1) q1_SpawnMeatSpray(self.pev.origin + g_Engine.v_forward * 16, Math.RandomFloat(-100.0, 100.0) * g_Engine.v_right); else q1_SpawnMeatSpray(self.pev.origin + g_Engine.v_forward * 16, side * g_Engine.v_right); } } void MonsterKilled(entvars_t@ pevAttacker, int iGib) { item_qbackpack@ pPack = q1_SpawnBackpack(self); pPack.m_iAmmoRockets = 2; 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_OGRE_DEATH, Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + Math.RandomLong(0, 0xf)); } void HandleAnimEvent(MonsterEvent@ pEvent) { switch (pEvent.event) { case OGRE_DROP_BACKPACK: break; case OGRE_SHOOT_GRENADE: MonsterGrenade(); break; case OGRE_CHAINSAW: MonsterChainsaw(atof(pEvent.options())); break; case OGRE_CHAINSAW_SOUND: g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_WEAPON, Q1_OGRE_MELEE, Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + Math.RandomLong(0, 0xf)); break; case OGRE_IDLE_SOUND: if (Math.RandomFloat(0, 1) < 0.1) g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_OGRE_IDLE[0], Math.RandomFloat(0.95, 1.0), ATTN_IDLE, 0, 93 + Math.RandomLong(0, 0xf)); break; case OGRE_IDLE_SOUND2: if (Math.RandomFloat(0, 1) < 0.1) g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_OGRE_IDLE[1], Math.RandomFloat(0.95, 1.0), ATTN_IDLE, 0, 93 + Math.RandomLong(0, 0xf)); break; case OGRE_DRAG_SOUND: if (Math.RandomFloat(0, 1) < 0.05) g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_OGRE_DRAG, Math.RandomFloat(0.95, 1.0), ATTN_IDLE, 0, 93 + Math.RandomLong(0, 0xf)); default: BaseClass.HandleAnimEvent(pEvent); } } } void q1_RegisterMonster_OGRE() { g_CustomEntityFuncs.RegisterCustomEntity("monster_qogre", "monster_qogre"); }