Files
wnsrc/lua/entities/npc_vj_strider_synth_z/init.lua
lifestorm 94063e4369 Upload
2024-08-04 22:55:00 +03:00

866 lines
37 KiB
Lua

--[[
| 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")
ENT.Model = "models/Combine_Strider.mdl"
ENT.VJ_NPC_Class = {"CLASS_COMBINE"}
ENT.StartHealth = 720
--ENT.CustomBlood_Particle = {"blood_impact_synth_01"}
ENT.BloodColor = "White"
ENT.VJ_IsHugeMonster = true
ENT.TurningSpeed = 8 -- How fast it can turn
ENT.SightAngle = 100
ENT.DeathCorpseApplyForce = false
ENT.PoseParameterLooking_Names = {yaw={"minigunYaw"}} -- Custom pose parameters to use, can put as many as needed
ENT.PoseParameterLooking_InvertYaw = true -- Inverts the pitch poseparameters (X)
ENT.InvestigateSoundDistance = 18
ENT.CallForHelpDistance = 10000 -- -- How far away the SNPC's call for help goes | Counted in World Units
ENT.NoChaseAfterCertainRange = true -- Should the SNPC not be able to chase when it's between number x and y?
ENT.NoChaseAfterCertainRange_FarDistance = 1000 -- 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.HasMeleeAttack = false
ENT.MeleeAttackDamage = 200
ENT.MeleeAttackDistance = 100 -- How close does it have to be until it attacks?
ENT.MeleeAttackDamageDistance = 115 -- How far does the damage go?
ENT.MovementType = VJ_MOVETYPE_AERIAL
ENT.AA_MinWanderDist = 500 -- Minimum distance that the NPC should go to when wandering
ENT.Aerial_FlyingSpeed_Calm = 160 -- The speed it should fly with, when it's wandering, moving slowly, etc. | Basically walking compared to ground SNPCs
ENT.Aerial_FlyingSpeed_Alerted = 210 -- The speed it should fly with, when it's chasing an enemy, moving away quickly, etc. | Basically running compared to ground SNPCs
ENT.Aerial_AnimTbl_Calm = {ACT_WALK} -- Animations it plays when it's wandering around while idle
ENT.Aerial_AnimTbl_Alerted = {ACT_RUN} -- Animations it plays when it's moving while alerted
ENT.AA_GroundLimit = 450 -- If the NPC's distance from itself to the ground is less than this, it will attempt to move up
ENT.AA_MoveAccelerate = 2 -- The NPC will gradually speed up to the max movement speed as it moves towards its destination | Calculation = FrameTime * x
ENT.AA_MoveDecelerate = 2 -- The NPC will slow down as it approaches its destination | Calculation = MaxSpeed / x
ENT.SoundTbl_CombatIdle = {
"npc/strider/dvs_stridervoc_00_35_18.wav",
"npc/strider/strider_hunt1.wav",
"npc/strider/strider_hunt2.wav",
"npc/strider/strider_hunt3.wav",
}
ENT.SoundTbl_Alert = {
"npc/strider/strider_distant1.wav",
"npc/strider/strider_distant2.wav",
"npc/strider/strider_distant3.wav",
}
ENT.SoundTbl_Pain = {
"npc/strider/striderx_alert2.wav",
"npc/strider/striderx_alert4.wav",
"npc/strider/striderx_alert5.wav",
"npc/strider/striderx_alert6.wav",
"npc/strider/striderx_pain2.wav",
"npc/strider/striderx_pain5.wav",
"npc/strider/striderx_pain7.wav",
"npc/strider/striderx_pain8.wav",
}
ENT.SoundTbl_Idle = {"npc/strider/strider_legstretch1.wav","npc/strider/strider_legstretch2.wav","npc/strider/strider_legstretch3.wav"}
ENT.SoundTbl_Death = {"npc/strider/striderx_die1.wav"}
ENT.IdleSoundLevel = 95
ENT.AlertSoundLevel = 115
ENT.PainSoundLevel = 105
ENT.DeathSoundLevel = 105
ENT.CombatIdleSoundLevel = 105
-- Shooting in general
ENT.Fire_TooCloseDist = 0
ENT.FireDistance = 6000
ENT.BulletSpread = 0.03
-- Regular shoot attack
ENT.ShootDamage = 12
ENT.GunAttackFireDelay = 0.15
-- Burst shoot attack
ENT.ConsiderBurstDelay = 4
ENT.DoBurstChance = 3
ENT.BurstFireDelay = 0.1
ENT.BurstFireBulletAmt = 6
ENT.BurstAccuracyMult = 2
-- Strider cannon:
ENT.ConsiderCannonDelay = 6
ENT.DoCannonChance = 2
ENT.StriderCannonDamage = 150
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function ENT:Controller_IntMsg(ply, controlEnt)
ply:ChatPrint("NOTE: Controlling is not really supported for this SNPC!!")
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function ENT:GiveBullseyes()
self.bullseye_front = ents.Create("obj_vj_Bullseye")
self.bullseye_behind = ents.Create("obj_vj_Bullseye")
self.bullseye_left = ents.Create("obj_vj_Bullseye")
self.bullseye_right = ents.Create("obj_vj_Bullseye")
self.bullseye_over = ents.Create("obj_vj_Bullseye")
self.bullseye_under = ents.Create("obj_vj_Bullseye")
local center = self:GetPos()+self:OBBCenter()
local bullseye_dist = 65
self.bullseyes = {
{
ent = self.bullseye_front,
pos = center+self:GetForward()*bullseye_dist,
},
{
ent = self.bullseye_behind,
pos = center-self:GetForward()*bullseye_dist,
},
{
ent = self.bullseye_left,
pos = center-self:GetRight()*bullseye_dist,
},
{
ent = self.bullseye_right,
pos = center+self:GetRight()*bullseye_dist,
},
{
ent = self.bullseye_over,
pos = center+self:GetUp()*bullseye_dist,
},
{
ent = self.bullseye_under,
pos = center-self:GetUp()*bullseye_dist,
},
}
for _,bullseye_data in pairs(self.bullseyes) do
local bullseye = bullseye_data.ent
bullseye:SetModel("models/hunter/blocks/cube05x05x05.mdl")
bullseye:SetPos(bullseye_data.pos)
bullseye:SetParent(self,12)
bullseye:SetAngles(self:GetAngles())
bullseye:Spawn()
bullseye:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
bullseye:AddEFlags(EFL_DONTBLOCKLOS)
bullseye:DrawShadow(true)
bullseye:SetNoDraw(true)
bullseye:SetSolid(SOLID_NONE)
bullseye.VJ_NPC_Class = self.VJ_NPC_Class
self:DeleteOnRemove(bullseye)
end
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
local strider_mins, strider_maxs = Vector(-65, -65, -65), Vector(65, 65, 65)
function ENT:CustomOnInitialize()
self:SetCollisionBounds(strider_mins, strider_maxs)
timer.Simple(0.03, function() if IsValid(self) then self:SetPos(self:GetPos()+Vector(0,0,self.AA_GroundLimit)) end end)
self.bulletprop1 = ents.Create("base_gmodentity")
self.bulletprop1:SetModel("models/hunter/blocks/cube025x025x025.mdl")
self.bulletprop1:SetPos(self:GetAttachment(10).Pos)
self.bulletprop1:SetParent(self,10)
self.bulletprop1:SetSolid(SOLID_NONE)
self.bulletprop1:AddEFlags(EFL_DONTBLOCKLOS)
self.bulletprop1:SetNoDraw(true)
self.bulletprop1:Spawn()
self.NextStriderFootStepSound = CurTime()
self.Strider_Current_Height_Mode = "high"
self.CurrentHeightPoseParam = 500
self:GiveBullseyes()
self.NextRegularShoot = CurTime()
self.NextConsiderBurst = CurTime()
self.NextConsiderCannon = CurTime()
self.BurstRepsLeft = 0
self.NextTogggleRandomCrouch = CurTime()
self.RandomCrouch = math.random(1, 2) == 1
self.BurstAttackShootTimerName = "VJ_Z_StriderBurstShoot" .. self:EntIndex()
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ENT.CurAimPitch = 0
function ENT:CustomOn_PoseParameterLookingCode(pitch, yaw, roll)
local ideal_pitch = pitch
if IsValid(self:GetEnemy()) then
ideal_pitch = pitch-45
end
self.CurAimPitch = Lerp(0.8, self.CurAimPitch, ideal_pitch)
self:SetPoseParameter("minigunPitch",self.CurAimPitch)
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function ENT:Strider_ChangeHeight(mode)
if self.Strider_Adjusting_Hight then return end
local adjust_time = nil
local new_strider_height = nil
local new_strider_poseparam = nil
if mode == "high" && self.Strider_Current_Height_Mode != "high" then
adjust_time = 4
new_strider_poseparam = 500
self:VJ_ACT_PLAYACTIVITY("stand",true,adjust_time,true)
self.Strider_Current_Height_Mode = "high"
elseif mode == "low" && self.Strider_Current_Height_Mode != "low" then
adjust_time = 4
new_strider_poseparam = 0
self:VJ_ACT_PLAYACTIVITY("crouch",true,adjust_time,true)
self.Strider_Current_Height_Mode = "low"
end
if new_strider_poseparam then
self.Strider_Adjusting_Hight = true
timer.Simple(1, function() if IsValid(self) then self.CurrentHeightPoseParam = new_strider_poseparam end end)
timer.Simple(adjust_time, function() if IsValid(self) then self.Strider_Adjusting_Hight = false end end)
end
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnThink()
local mypos = self:GetPos()
-- Decide if we should move or not:
local should_move = GetConVar("ai_disabled"):GetInt() == 0 && self.Strider_Current_Height_Mode != "low" && !self.PathObstructed && !self.InMidAir && !self.EnemyInNoChaseDist && !self.EnemyPosUnreachable && !self.IsBeingDroppedByDropship
if self.MovementType == VJ_MOVETYPE_AERIAL && !should_move then
self:AA_StopMoving()
self.MovementType = VJ_MOVETYPE_STATIONARY
--print("stopping...")
elseif self.MovementType == VJ_MOVETYPE_STATIONARY && should_move then
self.MovementType = VJ_MOVETYPE_AERIAL
--print("moving!")
end
-- Update collision bounds to match head position if it's crouching:
if self.Strider_Current_Height_Mode == "low" or self.Strider_Adjusting_Hight then
self:SetCollisionBoundsWS(strider_mins+self:GetBonePosition(0), strider_maxs+self:GetBonePosition(0))
self.DefaultCollisionBoundsSet = false
elseif !self.DefaultCollisionBoundsSet then
self:SetCollisionBounds(strider_mins, strider_maxs)
self.DefaultCollisionBoundsSet = true
end
-- Update bullseye's npc classes:
for _,bullseye in pairs(self.bullseyes) do
bullseye.VJ_NPC_Class = self.VJ_NPC_Class
end
-- Update pose parameters:
self:SetPoseParameter("body_height",self.CurrentHeightPoseParam)
-- If stationary, then ease in velocity.
if self.MovementType == VJ_MOVETYPE_STATIONARY then
local ease_in_vel = -self:GetVelocity()
self:SetLocalVelocity( Vector(ease_in_vel.x,ease_in_vel.y,0) )
end
-- Cannon charge effect:
if self.StriderCannonCharging then
local effectdata = EffectData()
effectdata:SetStart(self:GetAttachment(9).Pos)
util.Effect("cguard_suck", effectdata)
end
-- Footstep sounds:
local seq = self:GetSequenceName(self:GetSequence())
if self.NextStriderFootStepSound < CurTime() && (seq == "walk_all" or seq == "fastwalk_all") then
self:EmitSound("^npc/strider/strider_step"..math.random(1, 6)..".wav", 110, math.random(65, 75))
if seq == "walk_all" then
self.NextStriderFootStepSound = CurTime()+0.9
elseif seq == "fastwalk_all" then
self.NextStriderFootStepSound = CurTime()+0.6
end
end
---------------------------------------------------------------------------------------------------------------------------------------------------
-- Simulate gravity based on body height, and make sure height is correct:
---------------------------------------------------------------------------------------------------------------------------------------------------
local tr = util.TraceLine({
start = mypos,
endpos = mypos - Vector(0,0,10000),
mask = MASK_NPCWORLDSTATIC,
})
local ground_dist = math.abs(mypos.z - tr.HitPos.z)
-- local testprop = ents.Create("prop_dynamic")
-- testprop:SetModel("models/props_c17/FurnitureChair001a.mdl")
-- testprop:SetPos(mypos)
local fallspeed = 50
local repel_ground_speed = 50
local extra_height_tolerance = 25
local vec = Vector(0,0,0)
self.InMidAir = false
if ground_dist > self.AA_GroundLimit+100 then
self.InMidAir = true
end
if ground_dist > self.AA_GroundLimit+extra_height_tolerance then
vec = Vector(0,0,-fallspeed)
elseif ground_dist < self.AA_GroundLimit-extra_height_tolerance then
vec = Vector(0,0,repel_ground_speed)
--print("going up")
else
-- If its head is at an acceptable height, then ease in velocity.
vec = Vector(0,0,-self:GetVelocity().z*0.5)
--print("good height")
end
if self.MovementType == VJ_MOVETYPE_AERIAL then
self:SetVelocity(vec)
elseif self.MovementType == VJ_MOVETYPE_STATIONARY then
self:SetLocalVelocity(vec*10)
end
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnThink_AIEnabled()
if self.IsBeingDroppedByDropship then return end
local enemy = self:GetEnemy()
local enemyexists = IsValid(enemy)
local enemydist = nil
if enemyexists then
enemydist = enemy:GetPos():Distance(self:GetPos())
end
---------------------------------------------------------------------------------------------------------------------------------------------------
-- Custom enemy visibility check:
---------------------------------------------------------------------------------------------------------------------------------------------------
local enemy_visible = false
if enemyexists then
enemy_visible = self:Visible(enemy)
if !enemy_visible then
-- Double check enemy visibility with a traceline
local tr_visibility = util.TraceLine({
start = self:GetAttachment(10).Pos, -- Muzzle
endpos = enemy:GetPos()+enemy:OBBCenter(),
filter = {self,enemy},
mask = MASK_BLOCKLOS,
})
enemy_visible = !tr_visibility.Hit
if enemy_visible then
-- This sadly doesn't seem to work.
self:VJ_DoSetEnemy(enemy, false, true)
end
end
end
---------------------------------------------------------------------------------------------------------------------------------------------------
-- Crouch:
---------------------------------------------------------------------------------------------------------------------------------------------------
local enemy_under_self = false
local tr_crouch1
local tr_crouch2
if self.NextTogggleRandomCrouch < CurTime() then
self.RandomCrouch = !self.RandomCrouch
self.NextTogggleRandomCrouch = CurTime() + math.Rand(8, 15)
print(self.RandomCrouch)
end
if enemyexists then
enemy_under_self = enemy:GetPos().z - self:GetPos().z < 0
-- Check if the strider can't reach its enemy in a standing position:
tr_crouch1 = util.TraceLine({
start = self:GetPos(), -- Normal head position, when it's not crouching.
endpos = enemy:GetPos()+enemy:OBBCenter(),
filter = {self,enemy},
mask = MASK_NPCWORLDSTATIC,
})
-- Check if the strider can reach the enemy when crouched:
tr_crouch2 = util.TraceLine({
start = self:GetPos()-Vector(0,0,310),
endpos = enemy:GetPos()+enemy:OBBCenter(),
filter = {self,enemy},
mask = MASK_SHOT,
})
end
local should_crouch = enemyexists && !tr_crouch2.Hit && ((tr_crouch1.Hit && enemy_under_self) or self.RandomCrouch) && enemydist < self.FireDistance
local new_strider_height = nil
if self.Strider_Current_Height_Mode == "high" then
-- Consider crouching:
if should_crouch then
new_strider_height = "low"
if !self.NextHeightModeSwitch then
self.NextHeightModeSwitch = CurTime()+2.5
end
-- If the enemy has been occluded for a certain time, crouch:
if self.NextHeightModeSwitch && self.NextHeightModeSwitch < CurTime() then
self:Strider_ChangeHeight(new_strider_height)
self.NextHeightModeSwitch = nil
--print("crouching!")
end
elseif !should_crouch then
-- Reset Timer:
self.NextHeightModeSwitch = nil
end
elseif self.Strider_Current_Height_Mode == "low" then
-- Consider uncrouching:
if !should_crouch then
new_strider_height = "high"
if !self.NextHeightModeSwitch then
self.NextHeightModeSwitch = CurTime()+6
end
-- If the enemy has been visible for a certain time, stand up again:
if self.NextHeightModeSwitch && self.NextHeightModeSwitch < CurTime() then
self:Strider_ChangeHeight(new_strider_height)
self.NextHeightModeSwitch = nil
--print("standing!")
end
elseif should_crouch then
-- Reset Timer:
self.NextHeightModeSwitch = nil
end
end
-- if self.NextHeightModeSwitch then
-- print("time until switch to " .. new_strider_height .. ": " .. self.NextHeightModeSwitch-CurTime())
-- end
---------------------------------------------------------------------------------------------------------------------------------------------------
-- Movement stuff:
---------------------------------------------------------------------------------------------------------------------------------------------------
local tr_path_obstructed = util.TraceHull({
start = self:GetPos(),
endpos = self:GetPos()+self:GetForward()*250,
mins = strider_mins*0.33 - Vector(0,0,175),
maxs = strider_maxs*0.33,
mask = MASK_NPCWORLDSTATIC,
})
if tr_path_obstructed.Hit then
self.PathObstructed = true
else
self.PathObstructed = false
end
if enemyexists && enemy_visible && enemydist < self.NoChaseAfterCertainRange_FarDistance && enemydist > self.NoChaseAfterCertainRange_CloseDistance then
--print("no chase dist")
self.EnemyInNoChaseDist = true
else
self.EnemyInNoChaseDist = false
end
-- Stop moving if enemy is too far up or down from us.
if enemyexists then
local move_ang_pitch = self:WorldToLocalAngles( (enemy:GetPos()+enemy:OBBCenter()-self:GetPos()):Angle() ).x
if enemyexists && math.abs(move_ang_pitch) > 30 && enemydist > self.NoChaseAfterCertainRange_CloseDistance then
self.EnemyPosUnreachable = true
else
self.EnemyPosUnreachable = false
end
else
self.EnemyPosUnreachable = false
end
---------------------------------------------------------------------------------------------------------------------------------------------------
-- Attacks:
---------------------------------------------------------------------------------------------------------------------------------------------------
if enemyexists then
if enemy:GetPos():Distance(self:GetAttachment(1).Pos) < self.MeleeAttackDistance then
self:Strider_MeleeAttack()
end
if enemy_visible then
self.ShootPos = enemy:GetPos()+enemy:OBBCenter()
end
-- Consider doing strider cannon attack every now and then:
if self.NextConsiderCannon < CurTime() then
if !self.ShouldDoStriderCannon && math.random(1, self.DoCannonChance) == 1 then
self.ShouldDoStriderCannon = true
end
self.NextConsiderCannon = CurTime()+self.ConsiderCannonDelay
end
-- Consider doing burst attack every now and then:
if self.NextConsiderBurst < CurTime() then
if self.BurstRepsLeft < 1 && math.random(1, self.DoBurstChance) == 1 then
self.BurstRepsLeft = 3
end
self.NextConsiderBurst = CurTime()+self.ConsiderBurstDelay
end
-- If we are close to a position to shoot at, do any available range attack.
if self.ShootPos then
local dist = self.ShootPos:Distance(self:GetPos())
if dist > self.Fire_TooCloseDist && dist < self.FireDistance then
if self.ShouldDoStriderCannon then
-- If we should do the strider cannon attack, do it first.
self:StriderCannon()
elseif self.BurstRepsLeft > 0 then
-- If we have burst attacks to do, do them before regular gun attack.
self:GunBurstAttack()
else
-- Otherwise use regular gun attack.
self:GunAttack()
end
end
end
else
self:StopBurstAttack()
self.BurstRepsLeft = 0
self.ShootPos = nil
end
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function ENT:Strider_MeleeAttack()
if self.Strider_MeleeAttacking then return end
self.Strider_MeleeAttacking = true
local anim = "bighitl"
local time_total = 2.5
local time_until_damage = 1.5
self:VJ_ACT_PLAYACTIVITY(anim,true,time_total,true)
timer.Simple(time_until_damage, function() if IsValid(self) then
local hitents = util.VJ_SphereDamage(self,self, self:GetAttachment(1).Pos + Vector(0,0,35) ,self.MeleeAttackDamageDistance,self.MeleeAttackDamage,bit.bor(DMG_CRUSH, DMG_SLASH),true,false,false,false)
for _,ent in pairs(hitents) do
if ent:IsSolid() then
self:EmitSound("npc/strider/strider_skewer1.wav",95,math.random(85, 100))
break
end
end
end end)
timer.Simple(time_total, function() if IsValid(self) then
self.Strider_MeleeAttacking = false
end end)
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function ENT:Shoot(damage,soundpitch,accuracy_mult,tracer)
local source = self:GetAttachment(10).Pos
local pos = self.ShootPos or self:GetPos()+self:OBBCenter()+self:GetForward()*100
local shootdir = pos - self.bulletprop1:GetPos()
accuracy_mult = accuracy_mult or 1
tracer = tracer or "AirboatGunHeavyTracer"
ParticleEffectAttach("vj_rifle_full_blue",PATTACH_POINT_FOLLOW,self,10)
local expLight = ents.Create("light_dynamic")
expLight:SetKeyValue("brightness", "5")
expLight:SetKeyValue("distance", "250")
expLight:Fire("Color", "0 75 255")
expLight:SetPos(source)
expLight:Spawn()
expLight:SetParent(self,10)
expLight:Fire("TurnOn", "", 0)
timer.Simple(0.1,function() if IsValid(expLight) then expLight:Remove() end end)
self:DeleteOnRemove(expLight)
self.bulletprop1:FireBullets({
Src = self.bulletprop1:GetPos(),
Dir = shootdir:GetNormalized(),
Damage = damage*0.5,
Force = 25,
TracerName = tracer,
Spread = Vector( self.BulletSpread/accuracy_mult,self.BulletSpread/accuracy_mult,self.BulletSpread/accuracy_mult ),
Num = 1,
Tracer = 1,
Attacker = self,
Inflictor = self,
Filter = self,
Callback = function(attacker, tracer)
if math.random(1, 2) == 1 then
local effectdata = EffectData()
effectdata:SetOrigin(tracer.HitPos)
effectdata:SetNormal(tracer.HitNormal)
effectdata:SetRadius( 10 )
util.Effect( "cball_bounce", effectdata )
end
effects.BeamRingPoint( tracer.HitPos, 0.2, 0, 70, 12, 12, Color(255,255,175) )
util.VJ_SphereDamage(self,self,tracer.HitPos,20,damage*0.5,DMG_SONIC,true,false,false,false)
end,
})
local randpitch = math.random(soundpitch-10, soundpitch+10)
self:EmitSound("^npc/strider/strider_minigun.wav",140,randpitch,1,CHAN_STATIC)
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function ENT:GunAttack()
if self.NextRegularShoot > CurTime() then return end
self.NextRegularShoot = CurTime() + self.GunAttackFireDelay
self:Shoot(self.ShootDamage,80)
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function ENT:StopBurstAttack()
timer.Remove(self.BurstAttackShootTimerName)
self.BurstAttacking = false
self.BurstRepsLeft = self.BurstRepsLeft - 1
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function ENT:GunBurstAttack()
if self.BurstAttacking then return end
self.BurstAttacking = true
local time_until_burst = 0.75
local burst_cool_off_time = 0.33
self:EmitSound("npc/attack_helicopter/aheli_charge_up.wav", 100, math.random(120, 140))
self:EmitSound("weapons/cguard/charging.wav", 100, math.random(90, 100) , 0.33)
timer.Simple(time_until_burst, function() if IsValid(self) then
timer.Create(self.BurstAttackShootTimerName, self.BurstFireDelay, self.BurstFireBulletAmt, function()
self:Shoot(self.ShootDamage,110,self.BurstAccuracyMult,"AirboatGunTracer")
if timer.RepsLeft(self.BurstAttackShootTimerName) == 0 then
timer.Simple(burst_cool_off_time, function() if IsValid(self) then
self:StopBurstAttack()
end end)
end
end)
end end)
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function ENT:StriderCannon()
if self.DoingStriderCannon then return end
self.DoingStriderCannon = true
local pos = self.ShootPos
local time_until_fire = 1.1
-- Charge cannon
self.StriderCannonCharging = true
self:SetNWBool("StriderChargingCannon", true)
self:SetNWVector("StriderCannonBeamPos",pos)
self:EmitSound("npc/strider/charging.wav",120,math.random(80, 90),0.75)
self:EmitSound("npc/attack_helicopter/aheli_charge_up.wav", 120, math.random(110, 120))
self:EmitSound("weapons/cguard/charging.wav", 120, math.random(70, 80) , 0.33)
local effectdata = EffectData()
effectdata:SetStart(pos)
effectdata:SetMagnitude(time_until_fire)
effectdata:SetEntity(self)
effectdata:SetAttachment(9)
effectdata:SetScale(0.66)
util.Effect("cguard_warp", effectdata)
self.BlackHoleLight = ents.Create("light_dynamic")
self.BlackHoleLight:SetKeyValue("brightness", "3")
self.BlackHoleLight:SetKeyValue("distance", "250")
self.BlackHoleLight:Fire("Color", "0 75 255")
self.BlackHoleLight:SetPos(self:GetAttachment(9).Pos)
self.BlackHoleLight:SetParent(self,9)
self.BlackHoleLight:Spawn()
self.BlackHoleLight:Fire("TurnOn", "", 0)
self:DeleteOnRemove(self.BlackHoleLight)
-- Fire cannon
timer.Simple(time_until_fire, function() if IsValid(self) then
self.StriderCannonCharging = false
self:SetNWBool("StriderChargingCannon", false)
self.BlackHoleLight:Remove()
if self.ShootPos then
local tr = util.TraceLine({
start = self:GetAttachment(9).Pos,
endpos = self:GetAttachment(9).Pos + (pos-self:GetAttachment(9).Pos):GetNormalized()*10000,
mask = MASK_SHOT,
filter = self,
})
local expLight2 = ents.Create("light_dynamic")
expLight2:SetKeyValue("brightness", "9")
expLight2:SetKeyValue("distance", "600")
expLight2:Fire("Color", "0 75 255")
expLight2:SetPos(tr.HitPos)
expLight2:Spawn()
expLight2:Fire("TurnOn", "", 0)
timer.Simple(0.1,function() if IsValid(expLight2) then expLight2:Remove() end end)
self:DeleteOnRemove(expLight2)
local effectdata2 = EffectData()
effectdata2:SetOrigin(tr.HitPos)
effectdata2:SetNormal(tr.HitNormal)
effectdata2:SetRadius( 300 )
effectdata2:SetScale( 100 )
if tr.HitWorld then
util.Effect( "cball_bounce", effectdata2 )
util.Effect( "AR2Explosion", effectdata2 )
end
util.Effect( "ThumperDust", effectdata2 )
util.Effect( "ThumperDust", effectdata2 )
--ParticleEffect( "striderbuster_explode_dummy_core", tr.HitPos, Angle(0,0,00) )
util.ScreenShake(tr.HitPos, 16, 200, 2, 4000)
util.Decal( "Scorch", tr.HitPos + tr.HitNormal, tr.HitPos - tr.HitNormal)
effects.BeamRingPoint( tr.HitPos, 0.3, 0, 600, 25, 25, Color(255,255,175) )
util.VJ_SphereDamage(self,self,tr.HitPos,225,self.StriderCannonDamage,bit.bor(DMG_DISSOLVE,DMG_SHOCK,DMG_BLAST),true,true,false,false)
self:EmitSound("npc/strider/fire.wav",140,math.random(70, 80),1)
self:EmitSound("npc/vj_combine_guard_z/cguard_fire.wav", 140, math.random(70, 80))
sound.Play( "Weapon_Mortar.Impact", tr.HitPos, 120, 100, 1 )
end
end end)
timer.Simple(2, function() if IsValid(self) then
self.DoingStriderCannon = false
self.ShouldDoStriderCannon = false
end end)
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function ENT:DoSpark(pos,intensity)
intensity = intensity or 1
local spark = ents.Create("env_spark")
spark:SetKeyValue("Magnitude",tostring(intensity))
spark:SetKeyValue("Spark Trail Length",tostring(intensity))
spark:SetPos(pos)
spark:Spawn()
spark:Fire("StartSpark", "", 0)
timer.Simple(0.1, function() if IsValid(spark) then spark:Remove() end end)
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnTakeDamage_BeforeDamage(dmginfo, hitgroup)
self.HasPainSounds = true -- If set to false, it won't play the pain sounds
self.Bleeds = false
local infl = dmginfo:GetInflictor()
local comballdamage = false
if infl && IsValid(infl) then
if infl:GetClass() == "prop_combine_ball" then
infl:Fire("Explode")
comballdamage = true
end
if !infl.DamagedVJ_ZHunter && infl:GetClass() == "obj_vj_combineball" then
infl.DamagedVJ_ZHunter = true
infl:DeathEffects()
comballdamage = true
end
end
if !dmginfo:IsExplosionDamage() && !comballdamage then
if dmginfo:IsBulletDamage() then
dmginfo:SetDamage(dmginfo:GetDamage() * 0.05) -- Since bullet damage delivered to its head hitgroup is doubled.
else
dmginfo:SetDamage(dmginfo:GetDamage() * 0.1)
end
if math.random(1, 4) == 1 then
self.Bleeds = true
self:EmitSound("physics/metal/metal_sheet_impact_bullet1.wav", 92, math.random(70, 90))
self.Spark1 = ents.Create("env_spark")
self.Spark1:SetPos(dmginfo:GetDamagePosition())
self.Spark1:Spawn()
self.Spark1:Fire("StartSpark", "", 0)
self.Spark1:Fire("StopSpark", "", 0.001)
self:DeleteOnRemove(self.Spark1)
end
self.HasPainSounds = false -- If set to false, it won't play the pain sounds
else
self.Bleeds = true
if comballdamage then
dmginfo:SetDamage(150)
end
if dmginfo:GetDamage() >= 30 then
self:AddGesture(self:GetSequenceActivity( self:LookupSequence("flinch_gesture") ))
else
self.HasPainSounds = false -- If set to false, it won't play the pain sounds
end
self:DoSpark(dmginfo:GetDamagePosition(),5)
end
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function ENT:SetUpGibesOnDeath(dmginfo, hitgroup)
self.HasDeathSounds = false
local headpos = self:GetBonePosition(0)
self:EmitSound("NPC_CombineGunship.Explode", 120, 100)
local effectdata = EffectData()
effectdata:SetOrigin(headpos)
effectdata:SetScale( 500 )
util.Effect( "Explosion", effectdata )
self:EmitSound( "Explo.ww2bomb", 130, 100)
ParticleEffect("vj_explosion1", headpos, Angle(0,0,0), nil)
ParticleEffect( "striderbuster_explode_goop", headpos, self:GetAngles() )
self:CreateGibEntity("obj_vj_gib","models/gibs/strider_gib1.mdl",{Pos = headpos, BloodType = "",Vel = self:GetVelocity()+VectorRand()*600, CollideSound = {"SolidMetal.ImpactSoft"}})
self:CreateGibEntity("obj_vj_gib","models/gibs/strider_gib2.mdl",{Pos = headpos, BloodType = "",Vel = self:GetVelocity()+VectorRand()*600, CollideSound = {"SolidMetal.ImpactSoft"}})
self:CreateGibEntity("obj_vj_gib","models/gibs/strider_gib3.mdl",{Pos = headpos, BloodType = "",Vel = self:GetVelocity()+VectorRand()*600, CollideSound = {"SolidMetal.ImpactSoft"}})
self:CreateGibEntity("obj_vj_gib","models/gibs/strider_gib4.mdl",{Pos = headpos, BloodType = "",Vel = self:GetVelocity()+VectorRand()*600, CollideSound = {"SolidMetal.ImpactSoft"}})
self:CreateGibEntity("obj_vj_gib","models/gibs/strider_gib5.mdl",{Pos = headpos, BloodType = "",Vel = self:GetVelocity()+VectorRand()*600, CollideSound = {"SolidMetal.ImpactSoft"}})
self:CreateGibEntity("obj_vj_gib","models/gibs/strider_gib6.mdl",{Pos = headpos, BloodType = "",Vel = self:GetVelocity()+VectorRand()*600, CollideSound = {"SolidMetal.ImpactSoft"}})
self:CreateGibEntity("obj_vj_gib","models/gibs/strider_gib7.mdl",{Pos = headpos, BloodType = "",Vel = self:GetVelocity()+VectorRand()*600, CollideSound = {"SolidMetal.ImpactSoft"}})
self:CreateGibEntity("prop_ragdoll","models/gibs/strider_head.mdl",{Pos = headpos-Vector(0,0,500), BloodType = "", Ang = self:GetAngles(), Vel = self:GetVelocity(), AngVel = VectorRand()*500, CollideSound = {"SolidMetal.ImpactSoft"}})
self:CreateGibEntity("prop_ragdoll","models/gibs/strider_weapon.mdl",{Pos = headpos-Vector(0,0,500), BloodType = "", Ang = self:GetAngles(), Vel = self:GetVelocity(), AngVel = VectorRand()*500, CollideSound = {"SolidMetal.ImpactSoft"}})
self:CreateGibEntity("prop_ragdoll","models/gibs/strider_back_leg.mdl",{Pos = self:GetPos()-Vector(0,0,500), BloodType = "", Ang = self:GetAngles(), Vel = self:GetVelocity(), AngVel = VectorRand()*500, CollideSound = {"SolidMetal.ImpactSoft"}})
self:CreateGibEntity("prop_ragdoll","models/gibs/strider_left_leg.mdl",{Pos = self:GetPos()-Vector(0,0,500), BloodType = "", Ang = self:GetAngles(), Vel = self:GetVelocity(), AngVel = VectorRand()*500, CollideSound = {"SolidMetal.ImpactSoft"}})
self:CreateGibEntity("prop_ragdoll","models/gibs/strider_right_leg.mdl",{Pos = self:GetPos()-Vector(0,0,500), BloodType = "", Ang = self:GetAngles(), Vel = self:GetVelocity(), AngVel = VectorRand()*500, CollideSound = {"SolidMetal.ImpactSoft"}})
return true
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnRemove()
timer.Remove(self.BurstAttackShootTimerName)
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------