mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 05:43:46 +03:00
552 lines
21 KiB
Lua
552 lines
21 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/vjcrabsynth_z.mdl"}
|
||
|
|
ENT.StartHealth = 415
|
||
|
|
ENT.VJ_NPC_Class = {"CLASS_COMBINE"}
|
||
|
|
ENT.TurningSpeed = 12 -- How fast it can turn
|
||
|
|
ENT.VJ_IsHugeMonster = true
|
||
|
|
|
||
|
|
ENT.BloodColor = "Yellow"
|
||
|
|
|
||
|
|
ENT.InvestigateSoundDistance = 36
|
||
|
|
ENT.CallForHelpDistance = 10000 -- -- How far away the SNPC's call for help goes | Counted in World Units
|
||
|
|
|
||
|
|
ENT.HasDeathAnimation = true -- Does it play an animation when it dies?
|
||
|
|
ENT.AnimTbl_Death = {
|
||
|
|
"death01",
|
||
|
|
"death02",
|
||
|
|
"death03",
|
||
|
|
}
|
||
|
|
|
||
|
|
ENT.SoundTbl_FootStep = {"npc/vj_crabsynth_z/cs_step01.wav","npc/vj_crabsynth_z/cs_step02.wav","npc/vj_crabsynth_z/cs_step03.wav"}
|
||
|
|
ENT.SoundTbl_Idle = {"npc/vj_crabsynth_z/cs_idle01.wav","npc/vj_crabsynth_z/cs_idle02.wav","npc/vj_crabsynth_z/cs_idle03.wav"}
|
||
|
|
ENT.SoundTbl_CombatIdle = ENT.SoundTbl_Idle
|
||
|
|
ENT.SoundTbl_Investigate = {"npc/vj_crabsynth_z/cs_distant01.wav","npc/vj_crabsynth_z/cs_distant02.wav"}
|
||
|
|
ENT.SoundTbl_Alert = {"npc/vj_crabsynth_z/cs_alert01.wav","npc/vj_crabsynth_z/cs_alert02.wav","npc/vj_crabsynth_z/cs_alert03.wav"}
|
||
|
|
ENT.SoundTbl_Pain = {"npc/vj_crabsynth_z/cs_roar01.wav","npc/vj_crabsynth_z/cs_roar02.wav"}
|
||
|
|
ENT.SoundTbl_Death = {"npc/vj_crabsynth_z/cs_die.wav"}
|
||
|
|
ENT.SoundTbl_BeforeMeleeAttack = {"npc/vj_crabsynth_z/cs_pissed01.wav"}
|
||
|
|
|
||
|
|
ENT.FootStepSoundLevel = 85
|
||
|
|
ENT.HasWorldShakeOnMove = true -- Should the world shake when it's moving?
|
||
|
|
|
||
|
|
ENT.AlertSoundLevel = 100
|
||
|
|
ENT.PainSoundLevel = 100
|
||
|
|
ENT.DeathSoundLevel = 100
|
||
|
|
ENT.InvestigateSoundLevel = 100
|
||
|
|
ENT.CombatIdleSoundLevel = 100
|
||
|
|
|
||
|
|
ENT.BeforeMeleeAttackSoundPitch = VJ_Set(85, 115)
|
||
|
|
ENT.BeforeMeleeAttackSoundLevel = 100
|
||
|
|
|
||
|
|
ENT.BulletSpread = 0.075
|
||
|
|
|
||
|
|
ENT.ShootDuration_Far = 5
|
||
|
|
ENT.ShootDuration_Close = 2
|
||
|
|
|
||
|
|
ENT.ShootCooldown_Far = 5
|
||
|
|
ENT.ShootCooldown_Close = 8
|
||
|
|
|
||
|
|
ENT.ChargeDuration = 5
|
||
|
|
ENT.ChargeCooldown = 5
|
||
|
|
ENT.ChargeDamage = 60
|
||
|
|
|
||
|
|
ENT.DisableDefaultMeleeAttackDamageCode = true -- Disables the default melee attack damage code
|
||
|
|
ENT.MeleeAttackDistance = 50 -- How close does it have to be until it attacks?
|
||
|
|
ENT.NextMeleeAttackTime = 0.66 -- How much time until it can use a melee attack?
|
||
|
|
ENT.MeleeDamage = 40
|
||
|
|
|
||
|
|
ENT.MinPrioritizeShootDist = 1000 -- If the enemy is closer than this then start prioritizing the charge attack instead.
|
||
|
|
ENT.MaxShootDist = 2000
|
||
|
|
ENT.MinShootDist = 500
|
||
|
|
ENT.MaxShootHeightDifference = 300
|
||
|
|
|
||
|
|
ENT.RunAwayOnUnknownDamage = false -- Should run away on damage
|
||
|
|
|
||
|
|
ENT.DeathCorpseApplyForce = false
|
||
|
|
|
||
|
|
|
||
|
|
ENT.VJC_Data = {
|
||
|
|
CameraMode = 1, -- Sets the default camera mode | 1 = Third Person, 2 = First Person
|
||
|
|
ThirdP_Offset = Vector(-50, 0, 50), -- The offset for the controller when the camera is in third person
|
||
|
|
FirstP_Bone = "Bip02 Neck", -- If left empty, the base will attempt to calculate a position for first person
|
||
|
|
FirstP_Offset = Vector(25, 0, 25), -- The offset for the controller when the camera is in first person
|
||
|
|
FirstP_ShrinkBone = true, -- Should the bone shrink? Useful if the bone is obscuring the player's view
|
||
|
|
FirstP_CameraBoneAng = 0, -- Should the camera's angle be affected by the bone's angle? | 0 = No, 1 = Pitch, 2 = Yaw, 3 = Roll
|
||
|
|
FirstP_CameraBoneAng_Offset = 0, -- How much should the camera's angle be rotated by? | Useful for weird bone angles
|
||
|
|
}
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:CustomOnInitialize()
|
||
|
|
|
||
|
|
self:SetCollisionBounds(Vector(-50,-50,0), Vector(50,50,85))
|
||
|
|
|
||
|
|
self.FireGunLoop = CreateSound(self,"npc/vj_crabsynth_z/minigun_loop.wav")
|
||
|
|
self.FireGunLoop:SetSoundLevel(140)
|
||
|
|
|
||
|
|
self.NextShootTime = CurTime()
|
||
|
|
self.NextChargeTime = CurTime()
|
||
|
|
|
||
|
|
self.Bullseye = ents.Create("obj_vj_Bullseye")
|
||
|
|
self.Bullseye:SetModel("models/hunter/blocks/cube1x1x025.mdl")
|
||
|
|
self.Bullseye:SetParent(self)
|
||
|
|
self.Bullseye:SetPos(self:GetPos() + self:GetForward()*100 + Vector(0,0,15))
|
||
|
|
self.Bullseye:Spawn()
|
||
|
|
self.Bullseye:SetNoDraw(true)
|
||
|
|
self.Bullseye:DrawShadow(false)
|
||
|
|
self.Bullseye:SetSolid(SOLID_NONE)
|
||
|
|
self.Bullseye.VJ_NPC_Class = self.VJ_NPC_Class
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:StopFiringRoutine(skipcooldown)
|
||
|
|
if !self.GunAttacking or self.DeathAnimationCodeRan then return end
|
||
|
|
|
||
|
|
self.ShootPos = nil
|
||
|
|
self.GunAttacking = false
|
||
|
|
|
||
|
|
self.FireGunLoop:Stop()
|
||
|
|
|
||
|
|
self:EmitSound("npc/vj_crabsynth_z/minigun_stop.wav",105,math.random(90, 110))
|
||
|
|
self:VJ_ACT_PLAYACTIVITY(ACT_IDLE, true, 0)
|
||
|
|
|
||
|
|
if !skipcooldown then
|
||
|
|
self.NextShootTime = CurTime() + self.CurrentShootCoolDown
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:StartGunAttack(duration)
|
||
|
|
if self.GunAttacking then return end
|
||
|
|
self.GunAttacking = true
|
||
|
|
|
||
|
|
local timeuntilstartfire = 1.6
|
||
|
|
self:EmitSound("npc/vj_crabsynth_z/minigun_start.ogg",105,math.random(90, 110))
|
||
|
|
|
||
|
|
self:VJ_ACT_PLAYACTIVITY(ACT_ARM, true, timeuntilstartfire, true)
|
||
|
|
|
||
|
|
timer.Simple(timeuntilstartfire, function() if IsValid(self) && self.GunAttacking && !self.DeathAnimationCodeRan then
|
||
|
|
|
||
|
|
self:VJ_ACT_PLAYACTIVITY(ACT_RANGE_ATTACK1, true, duration, false)
|
||
|
|
|
||
|
|
timer.Simple(duration, function() if IsValid(self) && self.GunAttacking then
|
||
|
|
self:StopFiringRoutine()
|
||
|
|
end end)
|
||
|
|
|
||
|
|
end end)
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:TryUpdateShootPos()
|
||
|
|
|
||
|
|
local enemy = self:GetEnemy()
|
||
|
|
|
||
|
|
if IsValid(enemy) && self:Visible(enemy) then
|
||
|
|
self.ShootPos = enemy:GetPos() + enemy:OBBCenter()
|
||
|
|
end
|
||
|
|
|
||
|
|
local mypos = self:GetPos() + self:OBBCenter()
|
||
|
|
if self.ShootPos then
|
||
|
|
self.ShootPos = Vector( self.ShootPos.x , self.ShootPos.y , math.Clamp(self.ShootPos.z,mypos.z-self.MaxShootHeightDifference,mypos.z+self.MaxShootHeightDifference) )
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:VisuallyAimGunTowardsEnemy()
|
||
|
|
|
||
|
|
local bullet_source = self:GetAttachment(1).Pos
|
||
|
|
local fire_dir = ( self.ShootPos - bullet_source ):GetNormalized()
|
||
|
|
local fire_ang = self:WorldToLocalAngles(fire_dir:Angle())
|
||
|
|
|
||
|
|
local poseparam_yaw = math.Clamp( fire_ang.y*2.5, -45, 45 )
|
||
|
|
|
||
|
|
self:SetPoseParameter( "weapon_yaw", poseparam_yaw )
|
||
|
|
self:SetPoseParameter( "weapon_pitch", fire_ang.x )
|
||
|
|
|
||
|
|
if math.abs(poseparam_yaw) == 45 then
|
||
|
|
self:SetIdealYawAndUpdate(fire_dir:Angle().y)
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:StopCharging(UseAnimation,nextcharge)
|
||
|
|
if self.DeathAnimationCodeRan then return end
|
||
|
|
|
||
|
|
if UseAnimation then self:VJ_ACT_PLAYACTIVITY("chargeend", true, duration, true) end
|
||
|
|
|
||
|
|
self.Charging = false
|
||
|
|
self.Charge_ShouldApplyForce = false
|
||
|
|
|
||
|
|
self.NextChargeTime = CurTime() + nextcharge
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:ChargeAtEnemy(duration)
|
||
|
|
if self.Charging then return end
|
||
|
|
self.Charging = true
|
||
|
|
self.Charge_ApplyForceCountdownStarted = false
|
||
|
|
self.Charge_ShouldApplyForce = false
|
||
|
|
|
||
|
|
self:VJ_ACT_PLAYACTIVITY(ACT_SPECIAL_ATTACK1, true, duration, false)
|
||
|
|
|
||
|
|
timer.Simple(duration, function() if IsValid(self) && self.Charging then
|
||
|
|
self:StopCharging(true,self.ChargeCooldown)
|
||
|
|
end end)
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:CustomOnMeleeAttack_AfterStartTimer(seed)
|
||
|
|
timer.Simple(0.2, function() if IsValid(self) && !self.DeathAnimationCodeRan then self:CustomMeleeDamage(self.MeleeDamage) end end)
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:CustomMeleeDamage(damage,damagetype)
|
||
|
|
|
||
|
|
damagetype = damagetype or DMG_SLASH
|
||
|
|
|
||
|
|
local realisticRadius = false
|
||
|
|
local damaged_ents = util.VJ_SphereDamage(self, self, self:GetPos() + self:GetForward()*50, 50, damage, damagetype, true, realisticRadius)
|
||
|
|
local NPCWasHit = false
|
||
|
|
|
||
|
|
|
||
|
|
for _,ent in pairs(damaged_ents) do
|
||
|
|
|
||
|
|
local hitpos = ent:GetPos() + ent:OBBCenter()
|
||
|
|
|
||
|
|
local attack_dir = (hitpos - self:GetPos()):GetNormalized()
|
||
|
|
|
||
|
|
if ent:GetClass() == "func_breakable_surf" then
|
||
|
|
ent:Fire("Shatter")
|
||
|
|
end
|
||
|
|
|
||
|
|
if ent:IsNPC() or ent:IsPlayer() then
|
||
|
|
if ent:IsPlayer() then
|
||
|
|
ent:SetVelocity( Vector( attack_dir.x , attack_dir.y , 0 ) + Vector(0,0,250) )
|
||
|
|
elseif !ent.VJ_IsHugeMonster then
|
||
|
|
ent:SetVelocity( Vector( attack_dir.x , attack_dir.y , 0 )*1500 + Vector(0,0,250) )
|
||
|
|
end
|
||
|
|
NPCWasHit = true
|
||
|
|
end
|
||
|
|
|
||
|
|
if ent:GetMoveType() == MOVETYPE_VPHYSICS && ent:IsSolid() then
|
||
|
|
local physobj = ent:GetPhysicsObject()
|
||
|
|
|
||
|
|
if IsValid(physobj) then
|
||
|
|
physobj:SetVelocity(attack_dir * 400)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
if NPCWasHit then
|
||
|
|
self:EmitSound("npc/vj_crabsynth_z/cs_skewer.wav",85,math.random(90, 110))
|
||
|
|
self:EmitSound("NPC_Hunter.ChargeHitEnemy")
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:ChargeThink()
|
||
|
|
|
||
|
|
if IsValid(self:GetEnemy()) && self:Visible(self:GetEnemy()) then
|
||
|
|
self:SetIdealYawAndUpdate( (self:GetEnemy():GetPos() - self:GetPos() ):Angle().y )
|
||
|
|
end
|
||
|
|
|
||
|
|
if !self.Charge_ApplyForceCountdownStarted && self:GetActivity() == ACT_SPECIAL_ATTACK1 then
|
||
|
|
|
||
|
|
self.Charge_ApplyForceCountdownStarted = true
|
||
|
|
|
||
|
|
timer.Simple(0.6, function() if IsValid(self) then
|
||
|
|
self.Charge_ShouldApplyForce = true
|
||
|
|
end end)
|
||
|
|
|
||
|
|
end
|
||
|
|
|
||
|
|
local speed = 950
|
||
|
|
if self.Charge_ShouldApplyForce && self:IsOnGround() then
|
||
|
|
self:SetVelocity(self:GetForward()*speed)
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
if self:CustomMeleeDamage(self.ChargeDamage, bit.bor(DMG_CLUB,DMG_CRUSH,DMG_SLASH)) == true then -- Player or NPC was hit.
|
||
|
|
self:StopCharging(false,self.ChargeCooldown)
|
||
|
|
self:VJ_ACT_PLAYACTIVITY("chargestop", true, duration, true)
|
||
|
|
end
|
||
|
|
|
||
|
|
local collision_positions = {
|
||
|
|
self:GetPos() + self:GetForward()*100,
|
||
|
|
self:GetPos() + self:GetForward()*100 + self:GetRight() * 65,
|
||
|
|
self:GetPos() + self:GetForward()*100 - self:GetRight() * 65,
|
||
|
|
}
|
||
|
|
|
||
|
|
for k,pos in pairs(collision_positions) do
|
||
|
|
if bit.band( util.PointContents(pos) , CONTENTS_SOLID ) == CONTENTS_SOLID then
|
||
|
|
self:StopCharging(true,self.ChargeCooldown*0.5)
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local trStartPos = self:GetPos()+self:GetForward()*50
|
||
|
|
local tr = util.TraceLine({
|
||
|
|
start = trStartPos,
|
||
|
|
endpos = trStartPos - Vector(0,0,15),
|
||
|
|
mask = MASK_NPCWORLDSTATIC,
|
||
|
|
})
|
||
|
|
|
||
|
|
if !tr.Hit then
|
||
|
|
self:StopCharging(true,self.ChargeCooldown*0.5)
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:CanChargeEnemy()
|
||
|
|
|
||
|
|
local tr = util.TraceHull({
|
||
|
|
start = self:GetPos(),
|
||
|
|
endpos = self:GetEnemy():GetPos(),
|
||
|
|
mask = MASK_NPCWORLDSTATIC,
|
||
|
|
mins = self:OBBMins(),
|
||
|
|
maxs = self:OBBMaxs(),
|
||
|
|
})
|
||
|
|
|
||
|
|
if self:Visible(self:GetEnemy()) && self:GetEnemy():IsOnGround() && !tr.Hit then
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:Controller_IntMsg(ply, controlEnt)
|
||
|
|
|
||
|
|
ply:ChatPrint("MOUSE2 (secondary attack key): Fire Minigun")
|
||
|
|
ply:ChatPrint("SPACE (jump key): Charge Attack")
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:Attacking() if self.Charging or self.GunAttacking or self.MeleeAttacking then return true end end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
ENT.CurrentShootCoolDown = ENT.ShootCooldown_Far
|
||
|
|
|
||
|
|
function ENT:CustomOnThink_AIEnabled()
|
||
|
|
if self.DeathAnimationCodeRan then return end
|
||
|
|
|
||
|
|
local enemy = self:GetEnemy()
|
||
|
|
|
||
|
|
if IsValid(enemy) then
|
||
|
|
|
||
|
|
if self.VJ_IsBeingControlled then
|
||
|
|
|
||
|
|
local controller = self.VJ_TheController
|
||
|
|
|
||
|
|
if !self:Attacking() then
|
||
|
|
|
||
|
|
if controller:KeyDown(IN_ATTACK2) then
|
||
|
|
self:StartGunAttack(3)
|
||
|
|
elseif controller:KeyDown(IN_JUMP) && self.NextChargeTime < CurTime() then
|
||
|
|
self:ChargeAtEnemy(5)
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
|
||
|
|
else
|
||
|
|
|
||
|
|
local enemydist = self:GetPos():Distance(enemy:GetPos())
|
||
|
|
|
||
|
|
local fireduration = self.ShootDuration_Far
|
||
|
|
self.CurrentShootCoolDown = self.ShootCooldown_Far
|
||
|
|
|
||
|
|
if enemydist < self.MinPrioritizeShootDist then
|
||
|
|
|
||
|
|
fireduration = self.ShootDuration_Close
|
||
|
|
self.CurrentShootCoolDown = self.ShootCooldown_Close
|
||
|
|
|
||
|
|
if !self:Attacking() && self.NextChargeTime < CurTime() && self:CanChargeEnemy() then
|
||
|
|
self:ChargeAtEnemy(self.ChargeDuration)
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
|
||
|
|
if self:Visible(self:GetEnemy()) then
|
||
|
|
self:TryUpdateShootPos()
|
||
|
|
end
|
||
|
|
|
||
|
|
if !self:Attacking() && self.NextShootTime < CurTime() && enemydist < self.MaxShootDist && ( enemydist > self.MinShootDist or !self:CanChargeEnemy() ) then
|
||
|
|
|
||
|
|
local mypos = self:GetPos() + self:OBBCenter()
|
||
|
|
local targetpos = self.ShootPos or enemy:GetPos()
|
||
|
|
|
||
|
|
if (self:Visible(enemy) or self.ShootPos) && math.abs(mypos.z - targetpos.z) < self.MaxShootHeightDifference then
|
||
|
|
self:StartGunAttack(fireduration)
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
|
||
|
|
if !self:Visible(enemy) && !self.ShootPos then
|
||
|
|
self:StopFiringRoutine(true)
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
|
||
|
|
if self.ShootPos && self.GunAttacking then self:VisuallyAimGunTowardsEnemy() end
|
||
|
|
if self.Charging then self:ChargeThink() end
|
||
|
|
|
||
|
|
elseif !IsValid(enemy) then
|
||
|
|
|
||
|
|
if self.Charging then
|
||
|
|
self:StopCharging(true,self.ChargeCooldown*0.5)
|
||
|
|
end
|
||
|
|
|
||
|
|
if self.GunAttacking then
|
||
|
|
self:StopFiringRoutine(true)
|
||
|
|
end
|
||
|
|
|
||
|
|
self.ShootPos = nil
|
||
|
|
|
||
|
|
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:CustomOnThink()
|
||
|
|
|
||
|
|
self.Bullseye.VJ_NPC_Class = self.VJ_NPC_Class
|
||
|
|
|
||
|
|
if GetConVar("ai_disabled"):GetInt() == 1 then
|
||
|
|
self:StopFiringRoutine(true)
|
||
|
|
end
|
||
|
|
|
||
|
|
if !self.GunAttacking && self.FireGunLoop:IsPlaying() then
|
||
|
|
self.FireGunLoop:Stop()
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:FireGun()
|
||
|
|
|
||
|
|
--self:TryUpdateShootPos()
|
||
|
|
|
||
|
|
local bullet_source = self:GetAttachment(1).Pos
|
||
|
|
local shootpos = self.ShootPos or bullet_source+self:GetForward()*100
|
||
|
|
local fire_dir = ( (shootpos) - bullet_source ):GetNormalized()
|
||
|
|
|
||
|
|
|
||
|
|
if math.random(1, 2) == 1 then
|
||
|
|
local expLight = ents.Create("light_dynamic")
|
||
|
|
expLight:SetKeyValue("brightness", "3")
|
||
|
|
expLight:SetKeyValue("distance", "250")
|
||
|
|
expLight:Fire("Color", "0 75 255")
|
||
|
|
expLight:SetPos(bullet_source)
|
||
|
|
expLight:Spawn()
|
||
|
|
expLight:SetParent(self,1)
|
||
|
|
expLight:Fire("TurnOn", "", 0)
|
||
|
|
timer.Simple(0.1,function() if IsValid(expLight) then expLight:Remove() end end)
|
||
|
|
self:DeleteOnRemove(expLight)
|
||
|
|
ParticleEffect("vj_rifle_full_blue",bullet_source,self:GetAttachment(2).Ang)
|
||
|
|
end
|
||
|
|
|
||
|
|
self:FireBullets({
|
||
|
|
Src = bullet_source,
|
||
|
|
Dir = fire_dir,
|
||
|
|
Damage = 3,
|
||
|
|
Force = 25,
|
||
|
|
TracerName = "AirboatGunHeavyTracer",
|
||
|
|
Tracer = 2,
|
||
|
|
Spread = Vector( self.BulletSpread,self.BulletSpread,self.BulletSpread ),
|
||
|
|
Num = 1,
|
||
|
|
Callback = function(attacker, tracer)
|
||
|
|
if math.random(1, 4) == 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,3,DMG_SONIC,true,false,false,false)
|
||
|
|
end,
|
||
|
|
})
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
ENT.DisableFootStepSoundTimer = true -- If set to true, it will disable the time system for the footstep sound code, allowing you to use other ways like model events
|
||
|
|
|
||
|
|
function ENT:CustomOnHandleAnimEvent(ev, evTime, evCycle, evType, evOptions)
|
||
|
|
|
||
|
|
if ev == 1004 then self:FootStepSoundCode() end
|
||
|
|
|
||
|
|
if ev == 28 && self.GunAttacking then
|
||
|
|
|
||
|
|
self:FireGun()
|
||
|
|
|
||
|
|
if !self.FireGunLoop:IsPlaying() then
|
||
|
|
self.FireGunLoop:Play()
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:CustomOnTakeDamage_BeforeDamage(dmginfo, hitgroup)
|
||
|
|
|
||
|
|
self.HasPainSounds = false
|
||
|
|
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:GetDamagePosition().z < (self:GetPos()+self:OBBCenter()+Vector(0,0,-8)).z && dmginfo:IsExplosionDamage() ) && !comballdamage then
|
||
|
|
|
||
|
|
dmginfo:SetDamage(dmginfo:GetDamage()*0.1)
|
||
|
|
|
||
|
|
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
|
||
|
|
|
||
|
|
else
|
||
|
|
|
||
|
|
self.Bleeds = true
|
||
|
|
self.HasPainSounds = true
|
||
|
|
|
||
|
|
if comballdamage then
|
||
|
|
dmginfo:SetDamage(150)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
function ENT:CustomOnRemove()
|
||
|
|
|
||
|
|
self.FireGunLoop:Stop()
|
||
|
|
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|