#include "../tempents" class projectile_qgrenade : ScriptBaseEntity { float m_fExplodeTime; void Spawn() { g_EntityFuncs.SetModel(self, "models/quake1/grenade.mdl"); g_EntityFuncs.SetSize(self.pev, Vector(-0.5, -0.5, -0.5), Vector(0.5, 0.5, 0.5)); g_EntityFuncs.SetOrigin(self, self.pev.origin); m_fExplodeTime = g_Engine.time + 3.0; self.pev.movetype = MOVETYPE_BOUNCE; self.pev.solid = SOLID_BBOX; self.pev.nextthink = g_Engine.time + 2.5; self.pev.avelocity = Vector(300, 300, 300); SetThink(ThinkFunction(Explode)); } void Explode() { q1_Explode(self, self.pev.dmg); g_EntityFuncs.Remove(self); } void Touch(CBaseEntity@ pOther) { if (pOther is null || pOther.IsBSPModel() || pOther.edict() is self.pev.owner) { // bounce or some shit if (self.pev.velocity.Length() > 15.0) { g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_AUTO, "quake1/weapons/bounce.wav", 1.0, ATTN_NORM, 0, 100); } else { self.pev.angles.x = 0.0; self.pev.avelocity = Vector(0.0, 0.0, 0.0); } self.pev.velocity = self.pev.velocity * 0.5; return; } Explode(); } } class projectile_qrocket : ScriptBaseEntity { float m_fExplodeTime; void Spawn() { g_EntityFuncs.SetModel(self, "models/quake1/rocket.mdl"); g_EntityFuncs.SetSize(self.pev, Vector(0, 0, 0), Vector(0, 0, 0)); g_EntityFuncs.SetOrigin(self, self.pev.origin); m_fExplodeTime = g_Engine.time + 10.0; self.pev.nextthink = g_Engine.time + 0.05; self.pev.movetype = MOVETYPE_FLYMISSILE; self.pev.solid = SOLID_BBOX; self.pev.effects |= EF_DIMLIGHT; self.pev.nextthink = g_Engine.time + 10.0; SetThink(ThinkFunction(Explode)); } void Explode() { q1_Explode(self, self.pev.dmg); g_EntityFuncs.Remove(self); } void Touch(CBaseEntity@ pOther) { Explode(); } } class projectile_qmeat : ScriptBaseEntity { float m_fExplodeTime; void Spawn() { g_EntityFuncs.SetModel(self, "models/quake1/zombiegib.mdl"); m_fExplodeTime = g_Engine.time + 5.0; self.pev.movetype = MOVETYPE_BOUNCE; self.pev.solid = SOLID_BBOX; self.pev.mins = Vector(-1, -1, -1); self.pev.maxs = Vector(1, 1, 1); self.pev.nextthink = g_Engine.time + 0.1; self.pev.avelocity = Vector(3000, 1000, 2000); } void Explode() { g_EntityFuncs.Remove(self); } void Think() { q1_TE_BloodStream(self.pev.origin, (-self.pev.velocity).Normalize(), 73); if (g_Engine.time >= m_fExplodeTime) Explode(); else self.pev.nextthink = g_Engine.time + 0.1; } void Touch(CBaseEntity@ pOther) { if (pOther is g_EntityFuncs.Instance(self.pev.owner)) return; if (pOther !is null && !pOther.IsBSPModel() && pOther.pev.takedamage != 0) { pOther.TakeDamage(self.pev, self.pev.owner.vars, self.pev.dmg, DMG_GENERIC); g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_AUTO, "quake1/monsters/zombie/hit.wav", 1.0, ATTN_NORM, 0, 100); } else { g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_AUTO, "quake1/monsters/zombie/miss.wav", 1.0, ATTN_NORM, 0, 100); } Explode(); } } class projectile_qscragspike : ScriptBaseEntity { float m_fExplodeTime; Vector m_vecStart; void Spawn() { g_EntityFuncs.SetModel(self, "models/quake1/spike.mdl"); m_fExplodeTime = g_Engine.time + 5.0; self.pev.movetype = MOVETYPE_FLYMISSILE; self.pev.solid = SOLID_BBOX; self.pev.angles = Math.VecToAngles(self.pev.velocity); m_vecStart = self.pev.origin; g_EntityFuncs.SetSize(self.pev, Vector(-0.5, -0.5, -0.5), Vector(0.5, 0.5, 0.5)); self.pev.nextthink = g_Engine.time + 0.1; } void Think() { if (m_fExplodeTime < g_Engine.time) { g_EntityFuncs.Remove(self); return; } q1_TE_BloodStream(self.pev.origin, (-self.pev.velocity).Normalize()); self.pev.angles = Math.VecToAngles(self.pev.velocity); self.pev.nextthink = g_Engine.time + 0.1; } void Touch(CBaseEntity@ pOther) { if (pOther is g_EntityFuncs.Instance(self.pev.owner)) return; g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_AUTO, "quake1/monsters/scrag/hit.wav", 1.0, ATTN_NORM, 0, 100); if (pOther !is null && !pOther.IsBSPModel() && pOther.pev.takedamage != 0) { g_WeaponFuncs.SpawnBlood(self.pev.origin, pOther.BloodColor(), self.pev.dmg); pOther.TakeDamage(self.pev, self.pev.owner.vars, self.pev.dmg, DMG_GENERIC); } g_EntityFuncs.Remove(self); } } class projectile_qspike : ScriptBaseEntity { void Spawn() { g_EntityFuncs.SetModel(self, "models/quake1/spike.mdl"); self.pev.movetype = MOVETYPE_FLYMISSILE; self.pev.solid = SOLID_BBOX; g_EntityFuncs.SetSize(self.pev, Vector(0, 0, 0), Vector(0, 0, 0)); self.pev.nextthink = g_Engine.time + 10.0; } void Think() { g_EntityFuncs.Remove(self); } void Touch(CBaseEntity@ pOther) { if (pOther is g_EntityFuncs.Instance(self.pev.owner)) return; if (pOther !is null && !pOther.IsBSPModel() && pOther.pev.takedamage != 0) { pOther.TakeDamage(self.pev, self.pev.owner.vars, self.pev.dmg, DMG_GENERIC); g_WeaponFuncs.SpawnBlood(self.pev.origin, pOther.BloodColor(), self.pev.dmg); } else { g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_AUTO, "quake1/weapons/tink1.wav", 1.0, ATTN_NORM, 0, 100); g_Utility.Sparks(self.pev.origin); if (pOther !is null && pOther.pev.takedamage != 0) pOther.TakeDamage(self.pev, self.pev.owner.vars, self.pev.dmg, DMG_GENERIC); } g_EntityFuncs.Remove(self); } } void q1_RadiusDamage(Vector vecCenter, CBaseEntity@ pInflictor, CBaseEntity@ pAttacker, float flDamage, float flRadius, int bitsDamage) { array aEnts(32); int iNum = g_EntityFuncs.MonstersInSphere(aEnts, vecCenter, flRadius); for (int i = 0; i < iNum; ++i) { CBaseEntity@ pEnt = aEnts[i]; if (pEnt.pev.takedamage != 0 && pEnt !is pInflictor) { Vector vecOrg = pEnt.pev.origin + (pEnt.pev.mins + pEnt.pev.maxs) * 0.5; TraceResult tr; g_Utility.TraceLine(vecCenter, vecOrg, ignore_monsters, dont_ignore_glass, pInflictor.edict(), tr); if (tr.flFraction <= 0.999) continue; float flPoints = (vecOrg - vecCenter).Length() * 0.5; if (flPoints < 0) flPoints = 0; flPoints = flDamage - flPoints; if (pEnt is pAttacker) flPoints *= 0.5; if (flPoints > 0) { if (pEnt.GetClassname() == "monster_qshambler") pEnt.TakeDamage(pInflictor.pev, pAttacker.pev, flPoints * 0.5, bitsDamage); else pEnt.TakeDamage(pInflictor.pev, pAttacker.pev, flPoints, bitsDamage); } } } } void q1_Explode(CBaseEntity@ proj, float dmg) { // fake explosion because explosions in goldsrc are shit g_EntityFuncs.CreateExplosion(proj.pev.origin, proj.pev.angles, proj.pev.owner, 64.0, false); // apply quake-like explosion damage manually // g_WeaponFuncs.RadiusDamage(proj.pev.origin, proj.pev, proj.pev.owner.vars, dmg, dmg + 40.0, 0, DMG_BLAST); q1_RadiusDamage(proj.pev.origin, proj, g_EntityFuncs.Instance(proj.pev.owner), dmg, 140.0, DMG_BLAST); // alert nearby monsters q1_AlertMonsters(g_EntityFuncs.Instance(proj.pev.owner), proj.pev.origin, 1600); } CBaseEntity@ q1_ShootCustomProjectile(string classname, string mdl, Vector ori, Vector vel, Vector angles, CBaseEntity@ owner) { if (classname.Length() == 0) return null; dictionary keys; Vector boltAngles = angles * Vector(-1, 1, 1); keys["origin"] = ori.ToString(); keys["angles"] = boltAngles.ToString(); keys["velocity"] = vel.ToString(); // replace model or use error.mdl if no model specified and not a standard entity string model = mdl.Length() > 0 ? mdl : "models/error.mdl"; keys["model"] = model; if (mdl.Length() == 0) keys["rendermode"] = "1"; // don't render the model CBaseEntity@ shootEnt = g_EntityFuncs.CreateEntity(classname, keys, false); @shootEnt.pev.owner = owner.edict(); // do this or else crash g_EntityFuncs.DispatchSpawn(shootEnt.edict()); return shootEnt; } // had to move these here because shit doesn't work otherwise void q1_ScragDelayedSpike(EHandle hOwner, EHandle hEnemy, Vector vecSrc, Vector vecOffset) { if ( !hOwner || !hEnemy ) return; CBaseEntity@ pOwner = hOwner.GetEntity(), pEnemy = hEnemy.GetEntity(); if( pOwner is null || pEnemy is null || pOwner.pev.health <= 0 ) return; pOwner.pev.effects |= EF_MUZZLEFLASH; Vector dst = pEnemy.pev.origin - vecOffset; Vector vec = (dst - vecSrc).Normalize(); g_SoundSystem.EmitSoundDyn(pOwner.edict(), CHAN_VOICE, "quake1/monsters/scrag/shoot.wav", 1.0, ATTN_NORM, 0, 100); auto @pBolt = q1_ShootCustomProjectile("projectile_qscragspike", "models/quake1/spike.mdl", vecSrc, vec * 600, Math.VecToAngles(vec), pOwner); pBolt.pev.dmg = 9; } void q1_ScragDelaySpike(CBaseEntity@ pOwner, CBaseEntity@ pEnemy, Vector vecSrc, Vector vecOffset, float time) { g_Scheduler.SetTimeout("q1_ScragDelayedSpike", time, EHandle( @pOwner ), EHandle( @pEnemy ), vecSrc, vecOffset); } void q1_ZombieMissile(CBaseEntity@ pOwner, CBaseEntity@ pEnemy, Vector vecOrigin, Vector vecOffset) { g_EngineFuncs.MakeVectors(pOwner.pev.angles); Vector vecOrg = vecOrigin + vecOffset.x * g_Engine.v_forward + vecOffset.y * g_Engine.v_right + (vecOffset.z - 24) * g_Engine.v_up; Vector vecVelocity = (pEnemy.EyePosition() - vecOrg).Normalize() * 600; vecVelocity.z = 200; auto @pBolt = q1_ShootCustomProjectile("projectile_qmeat", "models/quake1/zombiegib.mdl", vecOrg, vecVelocity, Math.VecToAngles(vecVelocity), pOwner); pBolt.pev.dmg = 10; } void q1_RegisterProjectiles() { g_CustomEntityFuncs.RegisterCustomEntity("projectile_qgrenade", "projectile_qgrenade"); g_CustomEntityFuncs.RegisterCustomEntity("projectile_qrocket", "projectile_qrocket"); g_CustomEntityFuncs.RegisterCustomEntity("projectile_qspike", "projectile_qspike"); g_CustomEntityFuncs.RegisterCustomEntity("projectile_qscragspike", "projectile_qscragspike"); g_CustomEntityFuncs.RegisterCustomEntity("projectile_qmeat", "projectile_qmeat"); }