--[[ | This file was obtained through the combined efforts | of Madbluntz & Plymouth Antiquarian Society. | | Credits: lifestorm, Gregory Wayne Rossel JR., | Maloy, DrPepper10 @ RIP, Atle! | | Visit for more: https://plymouth.thetwilightzone.ru/ --]] AddCSLuaFile("shared.lua") include("shared.lua") /*----------------------------------------------- *** Copyright (c) 2012-2023 by DrVrej, All rights reserved. *** No parts of this code or any of its contents may be reproduced, copied, modified or adapted, without the prior written consent of the author, unless otherwise indicated for stand-alone materials. -----------------------------------------------*/ local combatDistance = 4000 -- When closer then this, it will stop chasing and start firing ENT.Model = {"models/vj_hlr/hl1/osprey.mdl"} -- The game will pick a random model from the table when the SNPC is spawned | Add as many as you want ENT.VJ_IsHugeMonster = true ENT.StartHealth = 800 ENT.HullType = HULL_LARGE ENT.SightAngle = 180 -- The sight angle | Example: 180 would make the it see all around it | Measured in degrees and then converted to radians ENT.TurningSpeed = 2 -- How fast it can turn ENT.TurningUseAllAxis = false -- If set to true, angles will not be restricted to y-axis, it will change all axes (plural axis) -- ====== Movement Variables ====== -- ENT.MovementType = VJ_MOVETYPE_AERIAL -- How does the SNPC move? ENT.Aerial_FlyingSpeed_Alerted = 300 -- The speed it should fly with, when it's chasing an enemy, moving away quickly, etc. | Basically running compared to ground SNPCs ENT.Aerial_FlyingSpeed_Calm = ENT.Aerial_FlyingSpeed_Alerted -- The speed it should fly with, when it's wandering, moving slowly, etc. | Basically walking compared to ground SNPCs ENT.Aerial_AnimTbl_Calm = {nil} -- Animations it plays when it's wandering around while idle ENT.Aerial_AnimTbl_Alerted = {nil} -- Animations it plays when it's moving while alerted ENT.AA_GroundLimit = 1200 -- If the NPC's distance from itself to the ground is less than this, it will attempt to move up ENT.AA_MinWanderDist = 1000 -- Minimum distance that the NPC should go to when wandering ENT.AA_MoveAccelerate = 8 -- The NPC will gradually speed up to the max movement speed as it moves towards its destination | Calculation = FrameTime * x ENT.AA_MoveDecelerate = 4 -- The NPC will slow down as it approaches its destination | Calculation = MaxSpeed / x ENT.VJC_Data = { FirstP_Bone = "Osprey", -- If left empty, the base will attempt to calculate a position for first person FirstP_Offset = Vector(365, 0, -80), -- The offset for the controller when the camera is in first person FirstP_ShrinkBone = false, -- Should the bone shrink? Useful if the bone is obscuring the player's view } --------------------------------------------------------------------------------------------------------------------------------------------- ENT.VJ_NPC_Class = {"CLASS_UNITED_STATES"} -- NPCs with the same class with be allied to each other ENT.FindEnemy_UseSphere = true -- Should the SNPC be able to see all around him? (360) | Objects and walls can still block its sight! ENT.CombatFaceEnemy = false -- If enemy is exists and is visible ENT.NoChaseAfterCertainRange = true -- Should the SNPC not be able to chase when it"s between number x and y? ENT.NoChaseAfterCertainRange_FarDistance = combatDistance -- How far until it can chase again? | "UseRangeDistance" = Use the number provided by the range attack instead ENT.NoChaseAfterCertainRange_CloseDistance = 0 -- How near until it can chase again? | "UseRangeDistance" = Use the number provided by the range attack instead ENT.Bleeds = false ENT.Immune_AcidPoisonRadiation = true -- Immune to Acid, Poison and Radiation ENT.Immune_Bullet = true -- Immune to bullet type damages ENT.Immune_Fire = true -- Immune to fire-type damages ENT.ImmuneDamagesTable = {DMG_PHYSGUN} ENT.BringFriendsOnDeath = false -- Should the SNPC's friends come to its position before it dies? ENT.HasMeleeAttack = false -- Should the SNPC have a melee attack? ENT.HasDeathRagdoll = false ENT.Medic_CanBeHealed = false -- If set to false, this SNPC can't be healed! -- ====== Sound File Paths ====== -- -- Leave blank if you don't want any sounds to play ENT.SoundTbl_Death = {"vj_hlr/hl1_weapon/mortar/mortarhit.wav"} local sdExplosions = {"vj_hlr/hl1_weapon/explosion/explode3.wav", "vj_hlr/hl1_weapon/explosion/explode4.wav", "vj_hlr/hl1_weapon/explosion/explode5.wav"} ENT.GeneralSoundPitch1 = 100 ENT.DeathSoundLevel = 100 /* https://github.com/ValveSoftware/halflife/blob/master/dlls/osprey.cpp EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_rotor4.wav", 1.0, 0.15, 0, 110 ); death: EMIT_SOUND(ENT(pev), CHAN_STATIC, "weapons/mortarhit.wav", 1.0, 0.3); */ -- Custom ENT.Osprey_IsBlackOps = false ENT.Osprey_DropPos = nil ENT.Osprey_DropStatus = 0 -- -1 = Can NOT deploy | 0 = Not dropped off | 1 = Moving to drop zone | 2 = Dropping soldiers | 3 = Soldiers rappelling down | 4 = Soldiers fully on ground ENT.Osprey_DropSoldierStatus = 0 -- if this number reaches the max amount, then it means that all soldiers are on ground, Osprey can go back to normal! ENT.Osprey_DropSoldierStatusDead = 0 -- Similar to the one above, but this one keeps track of how many of the 4 soldiers have died! ENT.Osprey_NextDropT = 0 ENT.Heli_SmokeStatus = 0 -- 0 = No smoke | 1 = Left tail smoke | 2 = Left & Right tail smoke --------------------------------------------------------------------------------------------------------------------------------------------- local spawnPos = Vector(0, 0, 400) -- function ENT:CustomOnInitialize() self:VJTags_Add(VJ_TAG_VEHICLE, VJ_TAG_AIRCRAFT) self:SetNW2Int("Heli_SmokeLevel", 0) //self.ConstantlyFaceEnemyDistance = self.SightDistance -- Osprey does NOT face the enemy! self:SetCollisionBounds(Vector(300, 300, 250), Vector(-300, -300, 0)) self:SetPos(self:GetPos() + spawnPos) self.HeliSD_Rotor = VJ_CreateSound(self, "vj_hlr/hl1_npc/apache/ap_rotor4.wav", 120) self.HeliSD_Whine = VJ_CreateSound(self, "vj_hlr/hl1_npc/apache/ap_whine1.wav", 70) self.HeliSD_Distant = VJ_CreateSound(self, "vj_hlr/hl1_npc/apache/ap_rotor1.wav", 160) if GetConVar("vj_hlr1_osprey_deploysoldiers"):GetInt() == 0 then self.Osprey_DropStatus = -1 end self.Osprey_Gunners = {} self.Osprey_DroppedSoldiers = {} -- Create & Spawn the 2 gunners for i = 1, 2 do local att = self:GetAttachment(i) local gunner = ents.Create(self.Osprey_IsBlackOps and "npc_vj_hlrof_assassin_male" or "npc_vj_hlr1_hgrunt_serg") gunner:SetPos(att.Pos) gunner:SetAngles(att.Ang) gunner:SetOwner(self) gunner:SetParent(self) gunner.MovementType = VJ_MOVETYPE_STATIONARY gunner.DisableWeapons = true gunner.CanTurnWhileStationary = false gunner.NoWeapon_UseScaredBehavior = false gunner.Medic_CanBeHealed = false gunner.HasDeathAnimation = false gunner.VJ_NPC_Class = self.VJ_NPC_Class gunner:Spawn() table.insert(self.VJ_AddCertainEntityAsFriendly, gunner) -- In case relation class is changed dynamically! table.insert(gunner.VJ_AddCertainEntityAsFriendly, self) -- In case relation class is changed dynamically! if self.Osprey_IsBlackOps then gunner:SetBodygroup(2, 1) end -- Always give the Black Ops snipers! gunner.Weapon_FiringDistanceFar = self.SightDistance gunner:Fire("SetParentAttachment", i == 1 and "gunner_left" or "gunner_right", 0) gunner:SetState(VJ_STATE_ONLY_ANIMATION) self:DeleteOnRemove(gunner) self.Osprey_Gunners[i] = gunner end -- 1 red tail light (Flashing) + 1 green & 1 red solid lights local tailLight = ents.Create("env_sprite") tailLight:SetKeyValue("model","vj_base/sprites/vj_glow1.vmt") tailLight:SetKeyValue("scale", "1") tailLight:SetKeyValue("rendermode","5") tailLight:SetKeyValue("renderfx","9") tailLight:SetKeyValue("rendercolor","255 0 0") tailLight:SetKeyValue("spawnflags","1") -- If animated tailLight:SetParent(self) tailLight:Fire("SetParentAttachment", "flash_red") tailLight:Spawn() tailLight:Activate() self:DeleteOnRemove(tailLight) local sideLight1 = ents.Create("env_sprite") sideLight1:SetKeyValue("model","vj_base/sprites/vj_glow1.vmt") sideLight1:SetKeyValue("scale", "0.5") sideLight1:SetKeyValue("rendermode","5") sideLight1:SetKeyValue("rendercolor","255 0 0") sideLight1:SetKeyValue("spawnflags","1") -- If animated sideLight1:SetParent(self) sideLight1:Fire("SetParentAttachment", "light_red") sideLight1:Spawn() sideLight1:Activate() self:DeleteOnRemove(sideLight1) local sideLight2 = ents.Create("env_sprite") sideLight2:SetKeyValue("model","vj_base/sprites/vj_glow1.vmt") sideLight2:SetKeyValue("scale", "0.5") sideLight2:SetKeyValue("rendermode","5") sideLight2:SetKeyValue("rendercolor","0 255 0") sideLight2:SetKeyValue("spawnflags","1") -- If animated sideLight2:SetParent(self) sideLight2:Fire("SetParentAttachment", "light_green") sideLight2:Spawn() sideLight2:Activate() self:DeleteOnRemove(sideLight2) end --------------------------------------------------------------------------------------------------------------------------------------------- function ENT:Controller_IntMsg(ply, controlEnt) ply:ChatPrint("JUMP: Deploy soldiers, can redeploy after all die & cool down expires!") end --------------------------------------------------------------------------------------------------------------------------------------------- function ENT:CustomOnThink() -- Flying tilt (X & Y) local velNorm = self:GetVelocity():GetNormal() local speed = FrameTime()*4 self:SetPoseParameter("tilt_x", Lerp(speed, self:GetPoseParameter("tilt_x"), velNorm.x)) self:SetPoseParameter("tilt_y", Lerp(speed, self:GetPoseParameter("tilt_y"), velNorm.y)) -- If the helicopter healed, then make sure to stop the smoke particles as well! if self.Heli_SmokeStatus > 0 && self:Health() > (self:GetMaxHealth() * 0.25) then self:SetNW2Int("Heli_SmokeLevel", 0) self.Heli_SmokeStatus = 0 end end --------------------------------------------------------------------------------------------------------------------------------------------- local ropePos1 = Vector(0, 0, 112) local ropePos2 = Vector(0, 0, -4096) -- function ENT:CustomOnThink_AIEnabled() if self.Osprey_DropStatus == -1 then return end -- All have landed and died if self.Osprey_DropStatus == 4 then if self.Osprey_DropSoldierStatusDead >= 4 && CurTime() > self.Osprey_NextDropT then self.Osprey_DropStatus = 0 self.Osprey_DropSoldierStatusDead = 0 else return end end local ene = self:GetEnemy() if self.Osprey_DropStatus == 0 && IsValid(ene) && ((!self.VJ_IsBeingControlled) or (self.VJ_IsBeingControlled && self.VJ_TheController:KeyDown(IN_JUMP))) then self.Osprey_DropStatus = 1 local vecRand = VectorRand()*600 vecRand.z = math.abs(vecRand.z) -- Make sure negative Zs are cancelled out local tr = util.TraceHull({ start = self:GetPos() + self:OBBCenter(), endpos = (self.VJ_IsBeingControlled and self:GetPos()) or (ene:GetPos() + ene:GetUp()*600 + vecRand), filter = {self, self.Osprey_Gunners[1], self.Osprey_Gunners[2]}, mins = self:OBBMins(), maxs = self:OBBMaxs() }) self.Osprey_DropPos = tr.HitPos self:SetState(VJ_STATE_ONLY_ANIMATION) self.NoChaseAfterCertainRange = false elseif self.Osprey_DropStatus == 1 then self:AA_MoveTo(self.Osprey_DropPos, true, "Alert", {FaceDest=true, FaceDestTarget=false, IgnoreGround=true}) if self:GetPos():Distance(self.Osprey_DropPos) < 180 then self.Osprey_DropStatus = 2 end elseif self.Osprey_DropStatus == 2 then -- Update the relation class tables for all the gunners in case it has changed! if self.Osprey_Gunners[1] then self.Osprey_Gunners[1].VJ_NPC_Class = self.VJ_NPC_Class end if self.Osprey_Gunners[2] then self.Osprey_Gunners[2].VJ_NPC_Class = self.VJ_NPC_Class end self:AA_StopMoving() self.Osprey_DropStatus = 3 self.Osprey_DropSoldierStatus = 0 -- Start at 0 self.Osprey_NextDropT = CurTime() + 30 for i = 1, 4 do local att = self:GetAttachment((i % 2 == 0) && 2 or 1) local startPos = att.Pos + att.Ang:Forward()*100 + self:GetForward()*((i >= 3) && -30 or 60) local soldierClass = "npc_vj_hlr1_hgrunt" local soldierClass_blkops = "npc_vj_hlrof_assassin_male" if math.random(1, 20) == 1 then -- 5% for robot grunts to spawn soldierClass = "npc_vj_hlr1_rgrunt" soldierClass_blkops = "npc_vj_hlrof_assassin_rgrunt" else soldierClass = "npc_vj_hlr1_hgrunt" soldierClass_blkops = "npc_vj_hlrof_assassin_male" end local soldier = ents.Create(self.Osprey_IsBlackOps and soldierClass_blkops or soldierClass) soldier:SetPos(startPos) soldier:SetAngles(att.Ang) soldier:SetOwner(self) soldier.HECU_DeployedByOsprey = true soldier.HECU_Rappelling = true soldier.VJ_NPC_Class = self.VJ_NPC_Class soldier:Spawn() soldier:SetLocalVelocity(Vector(0, 0, math.Rand(-196, -128))) soldier:SetEnemy(ene) -- Create the rope local ropeStart = soldier:GetPos() + ropePos1 local tr = util.TraceLine({ start = ropeStart, endpos = soldier:GetPos() + ropePos2, filter = {self, soldier}, }) local rope = EffectData() rope:SetStart(ropeStart) rope:SetOrigin(tr.HitPos) rope:SetEntity(soldier) util.Effect("VJ_HLR_Rope", rope) self.Osprey_DroppedSoldiers[i] = soldier end elseif self.Osprey_DropStatus == 3 && self.Osprey_DropSoldierStatus >= 4 then -- Reset Osprey to normal self.Osprey_DropStatus = 4 self:SetState() self.NoChaseAfterCertainRange = true end end --------------------------------------------------------------------------------------------------------------------------------------------- local vec = Vector(0, 0, 0) -- function ENT:CustomOnTakeDamage_BeforeImmuneChecks(dmginfo, hitgroup) -- If hit in the engine area, then get damage by bullets if dmginfo:IsBulletDamage() && (hitgroup == 2 or hitgroup == 3) then dmginfo:ScaleDamage(0.05) self.Immune_Bullet = false -- To counter the dmginfo:IsBulletDamage() function else self.Immune_Bullet = true end if dmginfo:GetDamagePosition() != vec then local rico = EffectData() rico:SetOrigin(dmginfo:GetDamagePosition()) rico:SetScale(4) -- Size rico:SetMagnitude(math.random(1, 2)) -- Effect type | 1 = Animated | 2 = Basic util.Effect("VJ_HLR_Rico", rico) end end --------------------------------------------------------------------------------------------------------------------------------------------- function ENT:CustomOnTakeDamage_AfterDamage(dmginfo, hitgroup) if self.Heli_SmokeStatus == 2 then return end local maxHP = self:GetMaxHealth() local hp = self:Health() if hp <= (maxHP * 0.25) then -- Only set left tail smoke if we haven't set it already if self.Heli_SmokeStatus == 0 then self:SetNW2Int("Heli_SmokeLevel", 1) self.Heli_SmokeStatus = 1 end -- If even lower, then make the right tail smoke too if hp <= (maxHP * 0.15) then self:SetNW2Int("Heli_SmokeLevel", 2) self.Heli_SmokeStatus = 2 end end end --------------------------------------------------------------------------------------------------------------------------------------------- local colorHeliExp = Color(255, 255, 192, 128) local colorYellowOsprey = Color(255, 223, 137) local sdGibCollide = {"vj_hlr/fx/metal1.wav", "vj_hlr/fx/metal2.wav", "vj_hlr/fx/metal3.wav", "vj_hlr/fx/metal4.wav", "vj_hlr/fx/metal5.wav"} local heliExpGibs_Green = { -- For HECU "models/vj_hlr/gibs/metalgib_p1_g.mdl", "models/vj_hlr/gibs/metalgib_p2_g.mdl", "models/vj_hlr/gibs/metalgib_p3_g.mdl", "models/vj_hlr/gibs/metalgib_p4_g.mdl", "models/vj_hlr/gibs/metalgib_p5_g.mdl", "models/vj_hlr/gibs/metalgib_p6_g.mdl", "models/vj_hlr/gibs/metalgib_p7_g.mdl", "models/vj_hlr/gibs/metalgib_p8_g.mdl", "models/vj_hlr/gibs/metalgib_p9_g.mdl", "models/vj_hlr/gibs/metalgib_p10_g.mdl", "models/vj_hlr/gibs/metalgib_p11_g.mdl", "models/vj_hlr/gibs/rgib_screw.mdl", "models/vj_hlr/gibs/rgib_screw.mdl" } local heliExpGibs_Gray = { -- For Black Ops "models/vj_hlr/gibs/metalgib_p1.mdl", "models/vj_hlr/gibs/metalgib_p2.mdl", "models/vj_hlr/gibs/metalgib_p3.mdl", "models/vj_hlr/gibs/metalgib_p4.mdl", "models/vj_hlr/gibs/metalgib_p5.mdl", "models/vj_hlr/gibs/metalgib_p6.mdl", "models/vj_hlr/gibs/metalgib_p7.mdl", "models/vj_hlr/gibs/metalgib_p8.mdl", "models/vj_hlr/gibs/metalgib_p9.mdl", "models/vj_hlr/gibs/metalgib_p10.mdl", "models/vj_hlr/gibs/metalgib_p11.mdl", "models/vj_hlr/gibs/rgib_screw.mdl", "models/vj_hlr/gibs/rgib_screw.mdl" } local heliExpGibs_Main = { "models/vj_hlr/gibs/osprey_bodygib1.mdl", "models/vj_hlr/gibs/osprey_bodygib2.mdl", "models/vj_hlr/gibs/osprey_bodygib3.mdl", "models/vj_hlr/gibs/osprey_bodygib4.mdl", "models/vj_hlr/gibs/osprey_bodygib5.mdl", "models/vj_hlr/gibs/osprey_bodygib6.mdl", "models/vj_hlr/gibs/osprey_bodygib7.mdl", "models/vj_hlr/gibs/osprey_bodygib8.mdl", "models/vj_hlr/gibs/osprey_bodygib9.mdl", "models/vj_hlr/gibs/osprey_bodygib10.mdl", "models/vj_hlr/gibs/osprey_bodygib11.mdl", "models/vj_hlr/gibs/osprey_bodygib12.mdl", "models/vj_hlr/gibs/osprey_bodygib13.mdl", "models/vj_hlr/gibs/osprey_bodygib14.mdl", "models/vj_hlr/gibs/osprey_bodygib15.mdl", "models/vj_hlr/gibs/osprey_bodygib16.mdl", "models/vj_hlr/gibs/osprey_bodygib17.mdl", "models/vj_hlr/gibs/osprey_tailgib1.mdl", "models/vj_hlr/gibs/osprey_tailgib4.mdl" } -- function ENT:CustomOnInitialKilled(dmginfo, hitgroup) -- Spawn a animated model of the helicopter that explodes constantly and gets destroyed when it collides with something -- Based on source code local deathCorpse = ents.Create("prop_vj_animatable") deathCorpse:SetModel("models/vj_hlr/hl1/osprey_dead.mdl") deathCorpse:SetPos(self:GetPos() + Vector(0, 0, 100)) -- + vector fixes the positioning, because osprey_dead spawns a little below due to the way the model is. deathCorpse:SetAngles(self:GetAngles()) deathCorpse:SetSkin(self:GetModel() == "models/vj_hlr/hl1/osprey_blkops.mdl" and 1 or 0) function deathCorpse:Initialize() self:PhysicsInit(SOLID_VPHYSICS) self:SetMoveType(MOVETYPE_VPHYSICS) self:SetMoveCollide(MOVECOLLIDE_FLY_BOUNCE) self:SetCollisionGroup(COLLISION_GROUP_NONE) self:SetSolid(SOLID_CUSTOM) local phys = self:GetPhysicsObject() if IsValid(phys) then phys:Wake() phys:EnableGravity(true) phys:SetBuoyancyRatio(0) phys:SetVelocity(self:GetVelocity()) end end deathCorpse.NextExpT = 0 deathCorpse:Spawn() deathCorpse:Activate() -- Explode as it goes down function deathCorpse:Think() self:ResetSequence("idle_ground") if CurTime() > self.NextExpT then self.NextExpT = CurTime() + 0.2 local expPos = self:GetPos() + Vector(math.Rand(-150, 150), math.Rand(-150, 150), math.Rand(-150, -50)) local spr = ents.Create("env_sprite") spr:SetKeyValue("model","vj_hl/sprites/zerogxplode.vmt") spr:SetKeyValue("GlowProxySize","2.0") spr:SetKeyValue("HDRColorScale","1.0") spr:SetKeyValue("renderfx","14") spr:SetKeyValue("rendermode","5") spr:SetKeyValue("renderamt","255") spr:SetKeyValue("disablereceiveshadows","0") spr:SetKeyValue("mindxlevel","0") spr:SetKeyValue("maxdxlevel","0") spr:SetKeyValue("framerate","15.0") spr:SetKeyValue("spawnflags","0") spr:SetKeyValue("scale","5") spr:SetPos(expPos) spr:Spawn() spr:Fire("Kill", "", 0.9) timer.Simple(0.9, function() if IsValid(spr) then spr:Remove() end end) util.BlastDamage(self, self, expPos, 300, 100) VJ_EmitSound(self, sdExplosions, 100, 100) VJ_EmitSound(self, "vj_hlr/hl1_weapon/explosion/explode"..math.random(3,5).."_dist.wav", 140, 100) end self:NextThink(CurTime()) return true end -- Get destroyed when it collides with something function deathCorpse:PhysicsCollide(data, phys) if self.Dead then return end if data.HitEntity.IsVJBase_Gib then return end -- Do NOT explode if its an engine part self.Dead = true -- Create gibs local isBlackOps = self:GetModel() == "models/vj_hlr/hl1/osprey_dead.mdl" local gibTbl = isBlackOps and heliExpGibs_Gray or heliExpGibs_Green for _ = 1, 30 do local gib = ents.Create("obj_vj_gib") gib:SetModel(VJ_PICK(gibTbl)) gib:SetPos(self:GetPos() + Vector(math.random(-100, 100), math.random(-100, 100), math.random(20, 150))) gib:SetAngles(Angle(math.Rand(-180, 180), math.Rand(-180, 180), math.Rand(-180, 180))) gib.Collide_Decal = "" gib.CollideSound = sdGibCollide gib:Spawn() gib:Activate() if !isBlackOps then gib:SetColor(colorYellowOsprey) end local myPhys = gib:GetPhysicsObject() if IsValid(myPhys) then myPhys:AddVelocity(Vector(math.Rand(-300, 300), math.Rand(-300, 300), math.Rand(150, 250))) myPhys:AddAngleVelocity(Vector(math.Rand(-200, 200), math.Rand(-200, 200), math.Rand(-200, 200))) end if GetConVar("vj_npc_fadegibs"):GetInt() == 1 then timer.Simple(GetConVar("vj_npc_fadegibstime"):GetInt(), function() SafeRemoveEntity(gib) end) end end local gibSkin = isBlackOps and 1 or 0 for _, v in ipairs(heliExpGibs_Main) do local gib = ents.Create("obj_vj_gib") gib:SetModel(v) gib:SetPos(self:GetPos() + Vector(math.random(-100, 100), math.random(-100, 100), math.random(20, 150))) gib:SetAngles(Angle(math.Rand(-180, 180), math.Rand(-180, 180), math.Rand(-180, 180))) gib.Collide_Decal = "" gib.CollideSound = sdGibCollide gib:SetSkin(gibSkin) gib:Spawn() gib:Activate() local myPhys = gib:GetPhysicsObject() if IsValid(myPhys) then myPhys:AddVelocity(Vector(math.Rand(-300, 300), math.Rand(-300, 300), math.Rand(150, 250))) myPhys:AddAngleVelocity(Vector(math.Rand(-200, 200), math.Rand(-200, 200), math.Rand(-200, 200))) end if GetConVar("vj_npc_fadegibs"):GetInt() == 1 then timer.Simple(GetConVar("vj_npc_fadegibstime"):GetInt(), function() SafeRemoveEntity(gib) end) end end local expPos = self:GetPos() + Vector(0,0,math.Rand(150, 150)) local spr = ents.Create("env_sprite") spr:SetKeyValue("model","vj_hl/sprites/fexplo1.vmt") spr:SetKeyValue("GlowProxySize","2.0") spr:SetKeyValue("HDRColorScale","1.0") spr:SetKeyValue("renderfx","14") spr:SetKeyValue("rendermode","5") spr:SetKeyValue("renderamt","255") spr:SetKeyValue("disablereceiveshadows","0") spr:SetKeyValue("mindxlevel","0") spr:SetKeyValue("maxdxlevel","0") spr:SetKeyValue("framerate","15.0") spr:SetKeyValue("spawnflags","0") spr:SetKeyValue("scale","15") spr:SetPos(expPos) spr:Spawn() spr:Fire("Kill", "", 1.19) timer.Simple(1.19, function() if IsValid(spr) then spr:Remove() end end) util.BlastDamage(self, self, expPos, 600, 200) VJ_EmitSound(self, "vj_hlr/hl1_weapon/mortar/mortarhit.wav", 100, 100) VJ_EmitSound(self, "vj_hlr/hl1_weapon/mortar/mortarhit_dist.wav", 140, 100) -- flags 0 = No fade! effects.BeamRingPoint(self:GetPos(), 0.4, 0, 1500, 32, 0, colorHeliExp, {material="vj_hl/sprites/shockwave", framerate=0, flags=0}) self:Remove() end end --------------------------------------------------------------------------------------------------------------------------------------------- function ENT:CustomOnPriorToKilled(dmginfo, hitgroup) local expPos = self:GetAttachment(self:LookupAttachment("engine_left")).Pos local spr = ents.Create("env_sprite") spr:SetKeyValue("model","vj_hl/sprites/zerogxplode.vmt") spr:SetKeyValue("GlowProxySize","2.0") spr:SetKeyValue("HDRColorScale","1.0") spr:SetKeyValue("renderfx","14") spr:SetKeyValue("rendermode","5") spr:SetKeyValue("renderamt","255") spr:SetKeyValue("disablereceiveshadows","0") spr:SetKeyValue("mindxlevel","0") spr:SetKeyValue("maxdxlevel","0") spr:SetKeyValue("framerate","15.0") spr:SetKeyValue("spawnflags","0") spr:SetKeyValue("scale","5") spr:SetPos(expPos) spr:Spawn() spr:Fire("Kill", "", 0.9) timer.Simple(0.9, function() if IsValid(spr) then spr:Remove() end end) util.BlastDamage(self, self, expPos, 300, 100) VJ_EmitSound(self, sdExplosions, 100, 100) expPos = self:GetAttachment(self:LookupAttachment("engine_right")).Pos local spr2 = ents.Create("env_sprite") spr2:SetKeyValue("model","vj_hl/sprites/zerogxplode.vmt") spr2:SetKeyValue("GlowProxySize","2.0") spr2:SetKeyValue("HDRColorScale","1.0") spr2:SetKeyValue("renderfx","14") spr2:SetKeyValue("rendermode","5") spr2:SetKeyValue("renderamt","255") spr2:SetKeyValue("disablereceiveshadows","0") spr2:SetKeyValue("mindxlevel","0") spr2:SetKeyValue("maxdxlevel","0") spr2:SetKeyValue("framerate","15.0") spr2:SetKeyValue("spawnflags","0") spr2:SetKeyValue("scale","5") spr2:SetPos(expPos) spr2:Spawn() spr2:Fire("Kill", "", 0.9) timer.Simple(0.9, function() if IsValid(spr2) then spr2:Remove() end end) util.BlastDamage(self, self, expPos, 300, 100) VJ_EmitSound(self, sdExplosions, 100, 100) -- Engine gibs (Right side) -- FIXME: this needs to be implemented better, right now this is a basic idea, often causes osprey to just explode midair, but the shit looks beautiful when everything goes right. local pos = self:GetAttachment(self:LookupAttachment("engine_right")).Pos local gibSkin = self:GetModel() == "models/vj_hlr/hl1/osprey_blkops.mdl" and 1 or 0 self:CreateGibEntity("obj_vj_gib", "models/vj_hlr/gibs/osprey_enginegib1.mdl", {BloodDecal="",Pos=pos + Vector(90,0,-100),CollideSound=sdGibCollide}, function(gib) gib:SetSkin(gibSkin) end) self:CreateGibEntity("obj_vj_gib", "models/vj_hlr/gibs/osprey_enginegib2.mdl", {BloodDecal="",Pos=pos + Vector(90,0,0),CollideSound=sdGibCollide}, function(gib) gib:SetSkin(gibSkin) end) self:CreateGibEntity("obj_vj_gib", "models/vj_hlr/gibs/osprey_enginegib3.mdl", {BloodDecal="",Pos=pos + Vector(95,0,90),CollideSound=sdGibCollide}, function(gib) gib:SetSkin(gibSkin) end) self:CreateGibEntity("obj_vj_gib", "models/vj_hlr/gibs/osprey_enginegib9.mdl", {BloodDecal="",Pos=pos + Vector(95,0,93),CollideSound=sdGibCollide}, function(gib) gib:SetSkin(gibSkin) end) self:CreateGibEntity("obj_vj_gib", "models/vj_hlr/gibs/osprey_enginegib10.mdl", {BloodDecal="",Pos=pos + Vector(95,0,95),CollideSound=sdGibCollide}, function(gib) gib:SetSkin(gibSkin) end) self:CreateGibEntity("obj_vj_gib", "models/vj_hlr/gibs/osprey_enginegib11.mdl", {BloodDecal="",Pos=pos + Vector(95,0,96),CollideSound=sdGibCollide}, function(gib) gib:SetSkin(gibSkin) end) -- Make the gunners gib into pieces! -- Also unparent them because Source engine spawns them at a random location... local gunner1 = self.Osprey_Gunners[1] local gunner2 = self.Osprey_Gunners[2] if IsValid(gunner1) then gunner1:SetParent(NULL) gunner1:SetPos(self:GetAttachment(self:LookupAttachment("gunner_left")).Pos) local doDmg = DamageInfo() doDmg:SetDamage(gunner1:Health()) doDmg:SetDamageType(DMG_BLAST) gunner1:TakeDamageInfo(doDmg) end if IsValid(gunner2) then gunner2:SetParent(NULL) gunner2:SetPos(self:GetAttachment(self:LookupAttachment("gunner_right")).Pos) local doDmg = DamageInfo() doDmg:SetDamage(gunner2:Health()) doDmg:SetDamageType(DMG_BLAST) gunner2:TakeDamageInfo(doDmg) end end --------------------------------------------------------------------------------------------------------------------------------------------- function ENT:CustomOnRemove() VJ_STOPSOUND(self.HeliSD_Rotor) VJ_STOPSOUND(self.HeliSD_Whine) VJ_STOPSOUND(self.HeliSD_Distant) -- Remove soldiers if Osprey was removed (Not killed) if !self.Dead then for _, v in ipairs(self.Osprey_DroppedSoldiers) do if IsValid(v) then v:Remove() end end end end