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

4689 lines
288 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/
--]]
if (!file.Exists("autorun/vj_base_autorun.lua","LUA")) then return end
AddCSLuaFile("shared.lua")
include("vj_base/npc_general.lua")
include("vj_base/npc_schedules.lua")
include("vj_base/npc_movetype_aa.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.
--------------------------------------------------*/
AccessorFunc(ENT, "m_iClass", "NPCClass", FORCE_NUMBER)
AccessorFunc(ENT, "m_fMaxYawSpeed", "MaxYawSpeed", FORCE_NUMBER)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------ Core Variables ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ENT.Model = {} -- The game will pick a random model from the table when the SNPC is spawned | Add as many as you want
ENT.VJ_IsHugeMonster = false -- This is mostly used for massive or boss SNPCs, it affects certain part of the SNPC, for example the SNPC won't receive any knock back
-- ====== Health ====== --
ENT.StartHealth = 50 -- The starting health of the NPC
ENT.HasHealthRegeneration = false -- Can the SNPC regenerate its health?
ENT.HealthRegenerationAmount = 4 -- How much should the health increase after every delay?
ENT.HealthRegenerationDelay = VJ_Set(2, 4) -- How much time until the health increases
ENT.HealthRegenerationResetOnDmg = true -- Should the delay reset when it receives damage?
-- ====== Collision / Hitbox Variables ====== --
ENT.HullType = HULL_HUMAN
ENT.HasHull = true -- Set to false to disable HULL
ENT.HullSizeNormal = true -- set to false to cancel out the self:SetHullSizeNormal()
ENT.HasSetSolid = true -- set to false to disable SetSolid
-- ====== Sight & Speed Variables ====== --
ENT.SightDistance = 10000 -- How far it can see | Remember to call "self:SetSightDistance(dist)" if you want to set a new value after initialize!
ENT.SightAngle = 80 -- The sight angle | Example: 180 would make the it see all around it | Measured in degrees and then converted to radians
ENT.TurningSpeed = 20 -- 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)
ENT.AnimationPlaybackRate = 1 -- Controls the playback rate of all the animations
-- ====== Movement Variables ====== --
-- Types: VJ_MOVETYPE_GROUND | VJ_MOVETYPE_AERIAL | VJ_MOVETYPE_AQUATIC | VJ_MOVETYPE_STATIONARY | VJ_MOVETYPE_PHYSICS
ENT.MovementType = VJ_MOVETYPE_GROUND -- How does the SNPC move?
ENT.UsePlayerModelMovement = false -- If true, it will allow the NPC to use player models properly by calculating the direction it needs to go to and setting the appropriate values
-- Jumping Variables:
-- Requires "CAP_MOVE_JUMP" | Applied automatically by the base if "ACT_JUMP" is valid on the NPC's model
ENT.AllowMovementJumping = true -- Should the NPC be allowed to jump from one node to another?
ENT.MaxJumpLegalDistance = VJ_Set(150, 280) -- The max distance the NPC can jump (Usually from one node to another) | ( UP, DOWN )
-- Stationary Move Type Variables:
ENT.CanTurnWhileStationary = true -- If true, the NPC will be able to turn while it's stationary
ENT.Stationary_UseNoneMoveType = false -- Technical variable, used if there is any issues with the NPC's position (It has its downsides, use only when needed!)
-- ====== NPC Controller Data ====== --
ENT.VJC_Data = {
CameraMode = 1, -- Sets the default camera mode | 1 = Third Person, 2 = First Person
ThirdP_Offset = Vector(0, 0, 0), -- The offset for the controller when the camera is in third person
FirstP_Bone = "ValveBiped.Bip01_Head1", -- If left empty, the base will attempt to calculate a position for first person
FirstP_Offset = Vector(0, 0, 5), -- 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
}
-- ====== Miscellaneous Variables ====== --
ENT.HasEntitiesToNoCollide = true -- If set to false, it won't run the EntitiesToNoCollide code
ENT.EntitiesToNoCollide = {} -- Entities to not collide with when HasEntitiesToNoCollide is set to true
ENT.AllowPrintingInChat = true -- Should this SNPC be allowed to post in player's chat? Example: "Blank no longer likes you."
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------ AI / Relationship Variables ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ENT.CanOpenDoors = true -- Can it open doors?
ENT.HasAllies = true -- Put to false if you want it not to have any allies
ENT.VJ_NPC_Class = {} -- NPCs with the same class with be allied to each other
-- Common Classes: Combine = CLASS_COMBINE || Zombie = CLASS_ZOMBIE || Antlions = CLASS_ANTLION || Xen = CLASS_XEN || Player Friendly = CLASS_PLAYER_ALLY
ENT.FriendsWithAllPlayerAllies = false -- Should this SNPC be friends with all other player allies that are running on VJ Base?
ENT.Behavior = VJ_BEHAVIOR_AGGRESSIVE -- The behavior of the SNPC
-- VJ_BEHAVIOR_AGGRESSIVE = Default behavior, attacks enemies || VJ_BEHAVIOR_NEUTRAL = Neutral to everything, unless provoked
-- VJ_BEHAVIOR_PASSIVE = Doesn't attack, but can be attacked by others || VJ_BEHAVIOR_PASSIVE_NATURE = Doesn't attack and is allied with everyone
ENT.IsGuard = false -- If set to false, it will attempt to stick to its current position at all times
ENT.AlertedToIdleTime = VJ_Set(4, 6) -- How much time until it calms down after the enemy has been killed/disappeared | Sets self.Alerted to false after the timer expires
ENT.MoveOutOfFriendlyPlayersWay = true -- Should the SNPC move out of the way when a friendly player comes close to it?
ENT.BecomeEnemyToPlayer = false -- Should the friendly SNPC become enemy towards the player if it's damaged by it or it witnesses another ally killed by it
ENT.BecomeEnemyToPlayerLevel = 2 -- Any time the player does something bad, the NPC's anger level raises by 1, if it surpasses this, it will become enemy!
-- ====== Old Variables (Can still be used, but it's recommended not to use them) ====== --
ENT.PlayerFriendly = false -- Makes the SNPC friendly to the player and HL2 Resistance
-- ====== Passive Behavior Variables ====== --
ENT.Passive_RunOnTouch = true -- Should it run away and make a alert sound when something collides with it?
ENT.Passive_NextRunOnTouchTime = VJ_Set(3, 4) -- How much until it can run away again when something collides with it?
ENT.Passive_RunOnDamage = true -- Should it run when it's damaged? | This doesn't impact how self.Passive_AlliesRunOnDamage works
ENT.Passive_AlliesRunOnDamage = true -- Should its allies (other passive SNPCs) also run when it's damaged?
ENT.Passive_AlliesRunOnDamageDistance = 800 -- Any allies within this distance will run when it's damaged
ENT.Passive_NextRunOnDamageTime = VJ_Set(6, 7) -- How much until it can run the code again?
-- ====== On Player Sight Variables ====== --
ENT.HasOnPlayerSight = false -- Should do something when it sees the enemy? Example: Play a sound
ENT.OnPlayerSightDistance = 200 -- How close should the player be until it runs the code?
ENT.OnPlayerSightDispositionLevel = 1 -- 0 = Run it every time | 1 = Run it only when friendly to player | 2 = Run it only when enemy to player
ENT.OnPlayerSightOnlyOnce = true -- If true, it will only run the code once | Sets self.HasOnPlayerSight to false once it runs!
ENT.OnPlayerSightNextTime = VJ_Set(15, 20) -- How much time should it pass until it runs the code again?
-- ====== Call For Help Variables ====== --
ENT.CallForHelp = true -- Does the SNPC call for help?
ENT.CallForHelpDistance = 2000 -- -- How far away the SNPC's call for help goes | Counted in World Units
ENT.NextCallForHelpTime = 4 -- Time until it calls for help again
ENT.HasCallForHelpAnimation = true -- if true, it will play the call for help animation
ENT.AnimTbl_CallForHelp = {ACT_SIGNAL_ADVANCE, ACT_SIGNAL_FORWARD} -- Call For Help Animations
ENT.CallForHelpAnimationDelay = 0 -- It will wait certain amount of time before playing the animation
ENT.CallForHelpAnimationPlayBackRate = 1 -- How fast should the animation play? | Currently only for gestures!
ENT.CallForHelpStopAnimations = true -- Should it stop attacks for a certain amount of time?
-- To let the base automatically detect the animation duration, set this to false:
ENT.CallForHelpStopAnimationsTime = false -- How long should it stop attacks?
ENT.CallForHelpAnimationFaceEnemy = true -- Should it face the enemy when playing the animation?
ENT.NextCallForHelpAnimationTime = 30 -- How much time until it can play the animation again?
-- ====== Medic Variables ====== --
ENT.IsMedicSNPC = false -- Is this SNPC a medic? Does it heal other friendly friendly SNPCs, and players(If friendly)
ENT.AnimTbl_Medic_GiveHealth = {ACT_SPECIAL_ATTACK1} -- Animations is plays when giving health to an ally
ENT.Medic_DisableAnimation = false -- if true, it will disable the animation code
ENT.Medic_TimeUntilHeal = false -- Time until the ally receives health | Set to false to let the base decide the time
ENT.Medic_CheckDistance = 600 -- How far does it check for allies that are hurt? | World units
ENT.Medic_HealDistance = 100 -- How close does it have to be until it stops moving and heals its ally?
ENT.Medic_HealthAmount = 25 -- How health does it give?
ENT.Medic_NextHealTime = VJ_Set(10, 15) -- How much time until it can give health to an ally again
ENT.Medic_SpawnPropOnHeal = true -- Should it spawn a prop, such as small health vial at a attachment when healing an ally?
ENT.Medic_SpawnPropOnHealModel = "models/healthvial.mdl" -- The model that it spawns
ENT.Medic_SpawnPropOnHealAttachment = "anim_attachment_LH" -- The attachment it spawns on
ENT.Medic_CanBeHealed = true -- If set to false, this SNPC can't be healed!
-- ====== Follow System Variables ====== --
-- Associated variables: self.FollowData, self.IsFollowing
ENT.FollowMinDistance = 150 -- Minimum distance the NPC should come to the player | The base automatically adds the NPC's size to this variable to account for different sizes!
ENT.NextFollowUpdateTime = 0.5 -- Time until it checks if it should move to the player again | Lower number = More performance loss
ENT.FollowPlayer = true -- Should the NPC follow the player when the player presses a certain key? | Restrictions: Player has to be friendly and the NPC's move type cannot be stationary!
-- ====== Movement & Idle Variables ====== --
ENT.AnimTbl_IdleStand = nil -- The idle animation table when AI is enabled | DEFAULT: {ACT_IDLE}
ENT.AnimTbl_Walk = {ACT_WALK} -- Set the walking animations | Put multiple to let the base pick a random animation when it moves
ENT.AnimTbl_Run = {ACT_RUN} -- Set the running animations | Put multiple to let the base pick a random animation when it moves
ENT.IdleAlwaysWander = false -- If set to true, it will make the SNPC always wander when idling
ENT.DisableWandering = false -- Disables wandering when the SNPC is idle
ENT.DisableChasingEnemy = false -- Disables the SNPC chasing the enemy
-- ====== Constantly Face Enemy Variables ====== --
ENT.ConstantlyFaceEnemy = false -- Should it face the enemy constantly?
ENT.ConstantlyFaceEnemy_IfVisible = true -- Should it only face the enemy if it's visible?
ENT.ConstantlyFaceEnemy_IfAttacking = false -- Should it face the enemy when attacking?
ENT.ConstantlyFaceEnemy_Postures = "Both" -- "Both" = Moving or standing | "Moving" = Only when moving | "Standing" = Only when standing
ENT.ConstantlyFaceEnemyDistance = 2500 -- How close does it have to be until it starts to face the enemy?
-- ====== Combat Face Enemy Variables ====== --
-- Mostly used by base tasks
ENT.CombatFaceEnemy = true -- If enemy exists and is visible
-- ====== Pose Parameter Variables ====== --
ENT.HasPoseParameterLooking = true -- Does it look at its enemy using poseparameters?
ENT.PoseParameterLooking_CanReset = true -- Should it reset its pose parameters if there is no enemies?
ENT.PoseParameterLooking_InvertPitch = false -- Inverts the pitch poseparameters (X)
ENT.PoseParameterLooking_InvertYaw = false -- Inverts the yaw poseparameters (Y)
ENT.PoseParameterLooking_InvertRoll = false -- Inverts the roll poseparameters (Z)
ENT.PoseParameterLooking_TurningSpeed = 10 -- How fast does the parameter turn?
ENT.PoseParameterLooking_Names = {pitch={}, yaw={}, roll={}} -- Custom pose parameters to use, can put as many as needed
-- ====== Investigation Variables ====== --
-- Showcase: https://www.youtube.com/watch?v=cCqoqSDFyC4
ENT.CanInvestigate = true -- Can it detect and investigate possible enemy disturbances? | EX: Sounds, movement and flashlight
ENT.InvestigateSoundDistance = 9 -- How far can the NPC hear sounds? | This number is multiplied by the calculated volume of the detectable sound
-- ====== Danger & Grenade Detection Variables ====== --
-- Showcase: https://www.youtube.com/watch?v=XuaMWPTe6rA
-- EXAMPLES: Props that are one fire, especially objects like barrels that are about to explode, Combine mine that is triggered and about to explode, The location that the Antlion Worker's spit is going to hit, Combine Flechette that is about to explode,
-- Antlion Guard that is charging towards the NPC, Player that is driving a vehicle at high speed towards the NPC, Manhack that has opened its blades, Rollermine that is about to self-destruct, Combine Helicopter that is about to drop bombs or is firing a turret near the NPC,
-- Combine Gunship's is about to fire its belly cannon near the NPC, Turret impact locations fired by Combine Gunships, or Combine Dropships, or Striders, The location that a Combine Dropship is going to deploy soldiers, Strider is moving on top of the NPC,
-- The location that the Combine or HECU mortar is going to hit, SMG1 grenades that are flying close by, A Combine soldier that is rappelling on top of the NPC, Stalker's laser impact location, Combine APC that is driving towards the NPC
ENT.CanDetectDangers = true -- Should the NPC detect dangers? | This includes grenades!
ENT.DangerDetectionDistance = 400 -- Max danger detection distance | WARNING: Most of the non-grenade dangers ignore this max value
ENT.CanThrowBackDetectedGrenades = true -- Should it pick up the detected grenade and throw it away or to the enemy?
-- NOTE: Can only throw grenades away if it has a grenade attack AND can detect dangers
-- ====== Taking Cover Variables ====== --
ENT.AnimTbl_TakingCover = {ACT_COVER_LOW} -- The animation it plays when hiding in a covered position
ENT.AnimTbl_MoveToCover = {ACT_RUN_CROUCH} -- The animation it plays when moving to a covered position
-- ====== Control Variables ====== --
-- Use these variables very careful! One wrong change can mess up the whole SNPC!
ENT.FindEnemy_UseSphere = false -- Should the SNPC be able to see all around him? (360) | Objects and walls can still block its sight!
ENT.FindEnemy_CanSeeThroughWalls = false -- Should it be able to see through walls and objects? | Can be useful if you want to make it know where the enemy is at all times
ENT.DisableFindEnemy = false -- Disables FindEnemy code, friendly code still works though
ENT.DisableSelectSchedule = false -- Disables Schedule code, Custom Schedule can still work
ENT.DisableTakeDamageFindEnemy = false -- Disable the SNPC finding the enemy when being damaged
ENT.DisableTouchFindEnemy = false -- Disable the SNPC finding the enemy when being touched
ENT.DisableMakingSelfEnemyToNPCs = false -- Disables the "AddEntityRelationship" that runs in think
ENT.TimeUntilEnemyLost = 15 -- Time until it resets its enemy if the enemy is not visible
ENT.NextProcessTime = 1 -- Time until it runs the essential part of the AI, which can be performance heavy!
-- ====== Miscellaneous Variables ====== --
ENT.DisableInitializeCapabilities = false -- If enabled, all of the Capabilities will be disabled, allowing you to add your own
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------ Damaged / Injured Variables ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ====== Blood-Related Variables ====== --
-- Leave custom blood tables empty to let the base decide depending on the blood type
ENT.Bleeds = true -- Does the SNPC bleed? (Blood decal, particle, etc.)
ENT.BloodColor = "" -- The blood type, this will determine what it should use (decal, particle, etc.)
-- Types: "Red" || "Yellow" || "Green" || "Orange" || "Blue" || "Purple" || "White" || "Oil"
ENT.HasBloodParticle = true -- Does it spawn a particle when damaged?
ENT.CustomBlood_Particle = {} -- Particles to spawn when it's damaged
ENT.HasBloodPool = true -- Does it have a blood pool?
ENT.CustomBlood_Pool = {} -- Blood pool types after it dies
ENT.BloodPoolSize = "Normal" -- What's the size of the blood pool? | Sizes: "Normal" || "Small" || "Tiny"
ENT.HasBloodDecal = true -- Does it spawn a decal when damaged?
ENT.CustomBlood_Decal = {} -- Decals to spawn when it's damaged
ENT.BloodDecalUseGMod = false -- Should use the current default decals defined by Garry's Mod? (This only applies for certain blood types only!)
ENT.BloodDecalDistance = 150 -- How far the decal can spawn in world units
-- ====== Immunity Variables ====== --
ENT.GodMode = false -- Immune to everything
ENT.Immune_AcidPoisonRadiation = false -- Immune to Acid, Poison and Radiation
ENT.Immune_Bullet = false -- Immune to bullet type damages
ENT.Immune_Blast = false -- Immune to explosive-type damages
ENT.Immune_Dissolve = false -- Immune to dissolving | Example: Combine Ball
ENT.Immune_Electricity = false -- Immune to electrical-type damages | Example: shock or laser
ENT.Immune_Fire = false -- Immune to fire-type damages
ENT.Immune_Melee = false -- Immune to melee-type damage | Example: Crowbar, slash damages
ENT.Immune_Physics = false -- Immune to physics impacts, won't take damage from props
ENT.Immune_Sonic = false -- Immune to sonic-type damages
ENT.ImmuneDamagesTable = {} -- Makes the SNPC immune to specific type of damages | Takes DMG_ enumerations
ENT.GetDamageFromIsHugeMonster = false -- Should it get damaged no matter what by SNPCs that are tagged as VJ_IsHugeMonster?
ENT.AllowIgnition = true -- Can this SNPC be set on fire?
-- ====== Flinching Variables ====== --
ENT.CanFlinch = 0 -- 0 = Don't flinch | 1 = Flinch at any damage | 2 = Flinch only from certain damages
ENT.FlinchDamageTypes = {DMG_BLAST} -- If it uses damage-based flinching, which types of damages should it flinch from?
ENT.FlinchChance = 16 -- Chance of it flinching from 1 to x | 1 will make it always flinch
-- To let the base automatically detect the animation duration, set this to false:
ENT.NextMoveAfterFlinchTime = false -- How much time until it can move, attack, etc.
ENT.NextFlinchTime = 5 -- How much time until it can flinch again?
ENT.AnimTbl_Flinch = {ACT_FLINCH_PHYSICS} -- If it uses normal based animation, use this
ENT.FlinchAnimationDecreaseLengthAmount = 0 -- This will decrease the time it can move, attack, etc. | Use it to fix animation pauses after it finished the flinch animation
ENT.HitGroupFlinching_DefaultWhenNotHit = true -- If it uses hitgroup flinching, should it do the regular flinch if it doesn't hit any of the specified hitgroups?
ENT.HitGroupFlinching_Values = nil -- EXAMPLES: {{HitGroup = {HITGROUP_HEAD}, Animation = {ACT_FLINCH_HEAD}}, {HitGroup = {HITGROUP_LEFTARM}, Animation = {ACT_FLINCH_LEFTARM}}, {HitGroup = {HITGROUP_RIGHTARM}, Animation = {ACT_FLINCH_RIGHTARM}}, {HitGroup = {HITGROUP_LEFTLEG}, Animation = {ACT_FLINCH_LEFTLEG}}, {HitGroup = {HITGROUP_RIGHTLEG}, Animation = {ACT_FLINCH_RIGHTLEG}}}
-- ====== Damage By Player Variables ====== --
ENT.HasDamageByPlayer = true -- Should the SNPC do something when it's hit by a player? Example: Play a sound or animation
ENT.DamageByPlayerDispositionLevel = 1 -- 0 = Run it every time | 1 = Run it only when friendly to player | 2 = Run it only when enemy to player
ENT.DamageByPlayerTime = VJ_Set(2, 2) -- How much time until it can run the Damage By Player code?
-- ====== Call For Back On Damage Variables ====== --
-- NOTE: This AI component only runs when there is NO enemy detected!
ENT.CallForBackUpOnDamage = true -- Should the NPC call for help when damaged?
ENT.CallForBackUpOnDamageDistance = 800 -- How far away does the call for help go?
ENT.CallForBackUpOnDamageLimit = 4 -- How many allies should it call? | 0 = Unlimited
ENT.NextCallForBackUpOnDamageTime = VJ_Set(9, 11) -- How much time until it can run this AI component again
ENT.CallForBackUpOnDamageAnimation = {ACT_SIGNAL_GROUP} -- Animations played when it calls for help on damage
ENT.CallForBackUpOnDamageAnimationTime = false -- How much time until it can use activities | false = Base automatically decides the animation duration
ENT.DisableCallForBackUpOnDamageAnimation = false -- Disables the animations from playing
-- ====== Move Or Hide On Damage Variables ====== --
ENT.MoveOrHideOnDamageByEnemy = true -- Should the SNPC move or hide when being damaged by an enemy?
ENT.MoveOrHideOnDamageByEnemy_OnlyMove = false -- Should it only move and not hide?
ENT.MoveOrHideOnDamageByEnemy_HideTime = VJ_Set(3, 4) -- How long should it hide?
ENT.NextMoveOrHideOnDamageByEnemy1 = 3 -- How much time until it moves or hides on damage by enemy? | The first # in math.random
ENT.NextMoveOrHideOnDamageByEnemy2 = 3.5 -- How much time until it moves or hides on damage by enemy? | The second # in math.random
-- ====== Miscellaneous Variables ====== --
ENT.HideOnUnknownDamage = 5 -- number = Hide on unknown damage, defines the time until it can hide again | false = Disable this AI component
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------ Death & Corpse Variables ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ====== Ally Reaction On Death Variables ====== --
-- Default: Creature base uses BringFriends and Human base uses AlertFriends
-- BringFriendsOnDeath takes priority over AlertFriendsOnDeath!
ENT.BringFriendsOnDeath = false -- Should the SNPC's friends come to its position before it dies?
ENT.BringFriendsOnDeathDistance = 800 -- How far away does the signal go? | Counted in World Units
ENT.BringFriendsOnDeathLimit = 3 -- How many people should it call? | 0 = Unlimited
ENT.AlertFriendsOnDeath = true -- Should the SNPCs allies get alerted when it dies? | Its allies will also need to have this variable set to true!
ENT.AlertFriendsOnDeathDistance = 800 -- How far away does the signal go? | Counted in World Units
ENT.AlertFriendsOnDeathLimit = 50 -- How many people should it alert?
ENT.AnimTbl_AlertFriendsOnDeath = {ACT_RANGE_ATTACK1} -- Animations it plays when an ally dies that also has AlertFriendsOnDeath set to true
-- ====== Death Animation Variables ====== --
ENT.HasDeathAnimation = false -- Does it play an animation when it dies?
ENT.AnimTbl_Death = {} -- Death Animations
-- To let the base automatically detect the animation duration, set this to false:
ENT.DeathAnimationTime = false -- Time until the SNPC spawns its corpse and gets removed
ENT.DeathAnimationChance = 1 -- Put 1 if you want it to play the animation all the time
ENT.DeathAnimationDecreaseLengthAmount = 0 -- This will decrease the time until it turns into a corpse
-- ====== Corpse Variables ====== --
ENT.HasDeathRagdoll = true -- If set to false, it will not spawn the regular ragdoll of the SNPC
ENT.DeathCorpseEntityClass = "UseDefaultBehavior" -- The entity class it creates | "UseDefaultBehavior" = Let the base automatically detect the type
ENT.DeathCorpseModel = {} -- The corpse model that it will spawn when it dies | Leave empty to use the NPC's model | Put as many models as desired, the base will pick a random one.
ENT.DeathCorpseCollisionType = COLLISION_GROUP_DEBRIS -- Collision type for the corpse | SNPC Options Menu can only override this value if it's set to COLLISION_GROUP_DEBRIS!
ENT.DeathCorpseSkin = -1 -- Used to override the death skin | -1 = Use the skin that the SNPC had before it died
ENT.DeathCorpseSetBodyGroup = true -- Should it get the models bodygroups and set it to the corpse? When set to false, it uses the model's default bodygroups
ENT.DeathCorpseBodyGroup = VJ_Set(-1, -1) -- #1 = the category of the first bodygroup | #2 = the value of the second bodygroup | Set -1 for #1 to let the base decide the corpse's bodygroup
ENT.DeathCorpseSubMaterials = nil -- Apply a table of indexes that correspond to a sub material index, this will cause the base to copy the NPC's sub material to the corpse.
ENT.DeathCorpseFade = false -- Fades the ragdoll on death
ENT.DeathCorpseFadeTime = 10 -- How much time until the ragdoll fades | Unit = Seconds
ENT.DeathCorpseSetBoneAngles = true -- This can be used to stop the corpse glitching or flying on death
ENT.DeathCorpseApplyForce = true -- If false, force will not be applied to the corpse
ENT.WaitBeforeDeathTime = 0 -- Time until the SNPC spawns its corpse and gets removed
-- ====== Dismemberment/Gib Variables ====== --
ENT.AllowedToGib = true -- Is it allowed to gib in general? This can be on death or when shot in a certain place
ENT.HasGibOnDeath = true -- Is it allowed to gib on death?
ENT.GibOnDeathDamagesTable = {"UseDefault"} -- Damages that it gibs from | "UseDefault" = Uses default damage types | "All" = Gib from any damage
ENT.HasGibOnDeathSounds = true -- Does it have gib sounds? | Mostly used for the settings menu
ENT.HasGibDeathParticles = true -- Does it spawn particles on death or when it gibs? | Mostly used for the settings menu
-- ====== Item Drops On Death Variables ====== --
ENT.HasItemDropsOnDeath = true -- Should it drop items on death?
ENT.ItemDropsOnDeathChance = 14 -- If set to 1, it will always drop it
ENT.ItemDropsOnDeath_EntityList = {"weapon_frag", "item_healthvial"} -- List of items it will randomly pick from | Leave it empty to drop nothing or to make your own dropping code (Using CustomOn...)
ENT.DropWeaponOnDeath = true -- Should it drop its weapon on death?
ENT.DropWeaponOnDeathAttachment = "anim_attachment_RH" -- Which attachment should it use for the weapon's position
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------ Melee Attack Variables ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ENT.HasMeleeAttack = true -- Should the SNPC have a melee attack?
ENT.MeleeAttackDamage = 10
ENT.MeleeAttackDamageType = DMG_CLUB -- Type of Damage
ENT.HasMeleeAttackKnockBack = true -- Should knockback be applied on melee hit? | Use self:MeleeAttackKnockbackVelocity() to edit the velocity
-- ====== Animation Variables ====== --
ENT.AnimTbl_MeleeAttack = {ACT_MELEE_ATTACK1} -- Melee Attack Animations
ENT.MeleeAttackAnimationDelay = 0 -- It will wait certain amount of time before playing the animation
ENT.MeleeAttackAnimationFaceEnemy = true -- Should it face the enemy while playing the melee attack animation?
ENT.MeleeAttackAnimationDecreaseLengthAmount = 0 -- This will decrease the time until starts chasing again. Use it to fix animation pauses until it chases the enemy.
ENT.MeleeAttackAnimationAllowOtherTasks = false -- If set to true, the animation will not stop other tasks from playing, such as chasing | Useful for gesture attacks!
-- ====== Distance Variables ====== --
ENT.MeleeAttackDistance = 30 -- How close does it have to be until it attacks?
ENT.MeleeAttackAngleRadius = 100 -- What is the attack angle radius? | 100 = In front of the SNPC | 180 = All around the SNPC
ENT.MeleeAttackDamageDistance = 70 -- How far does the damage go?
ENT.MeleeAttackDamageAngleRadius = 100 -- What is the damage angle radius? | 100 = In front of the SNPC | 180 = All around the SNPC
-- ====== Timer Variables ====== --
-- To use event-based attacks, set this to false:
ENT.TimeUntilMeleeAttackDamage = 0.5 -- This counted in seconds | This calculates the time until it hits something
ENT.NextMeleeAttackTime = 0 -- How much time until it can use a melee attack?
ENT.NextMeleeAttackTime_DoRand = false -- False = Don't use random time | Number = Picks a random number between the regular timer and this timer
-- To let the base automatically detect the attack duration, set this to false:
ENT.NextAnyAttackTime_Melee = false -- How much time until it can use any attack again? | Counted in Seconds
ENT.NextAnyAttackTime_Melee_DoRand = false -- False = Don't use random time | Number = Picks a random number between the regular timer and this timer
ENT.MeleeAttackReps = 1 -- How many times does it run the melee attack code?
ENT.MeleeAttackExtraTimers = nil -- Extra melee attack timers, EX: {1, 1.4} | it will run the damage code after the given amount of seconds
ENT.StopMeleeAttackAfterFirstHit = false -- Should it stop the melee attack from running rest of timers when it hits an enemy?
-- ====== Control Variables ====== --
ENT.DisableMeleeAttackAnimation = false -- if true, it will disable the animation code
ENT.DisableDefaultMeleeAttackCode = false -- When set to true, it will completely disable the melee attack code
ENT.DisableDefaultMeleeAttackDamageCode = false -- Disables the default melee attack damage code
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------ Weapon Attack Variables ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ENT.WeaponSpread = 1 -- What's the spread of the weapon? | Closer to 0 = better accuracy, Farther than 1 = worse accuracy
ENT.DisableWeapons = false -- If set to true, it won't be able to use weapons
ENT.Weapon_NoSpawnMenu = false -- If set to true, the NPC weapon setting in the spawn menu will not be applied for this SNPC
-- ====== Distance Variables ====== --
ENT.Weapon_FiringDistanceFar = 3000 -- How far away it can shoot
ENT.Weapon_FiringDistanceClose = 10 -- How close until it stops shooting
ENT.HasWeaponBackAway = true -- Should the SNPC back away if the enemy is close?
ENT.WeaponBackAway_Distance = 150 -- When the enemy is this close, the SNPC will back away | 0 = Never back away
-- ====== Standing-Firing Variables ====== --
ENT.AnimTbl_WeaponAim = nil -- Animations played when the NPC is supposed to raise/aim its weapon | EX: Gun is out of ammo, combat idle, etc.| DEFAULT: {ACT_IDLE_ANGRY}
ENT.AnimTbl_WeaponAttack = {ACT_RANGE_ATTACK1} -- Animation played when the SNPC does weapon attack
ENT.CanCrouchOnWeaponAttack = true -- Can it crouch while shooting?
ENT.AnimTbl_WeaponAttackCrouch = {ACT_RANGE_ATTACK1_LOW} -- Animation played when the SNPC does weapon attack while crouching | For VJ Weapons
ENT.CanCrouchOnWeaponAttackChance = 2 -- How much chance of crouching? | 1 = Crouch every time
ENT.AnimTbl_WeaponAttackFiringGesture = {ACT_GESTURE_RANGE_ATTACK1} -- Firing Gesture animations used when the SNPC is firing the weapon
ENT.DisableWeaponFiringGesture = false -- If set to true, it will disable the weapon firing gestures
-- ====== Secondary Fire Variables ====== --
ENT.CanUseSecondaryOnWeaponAttack = true -- Can the NPC use a secondary fire if it's available?
ENT.AnimTbl_WeaponAttackSecondary = {"shootAR2alt"} -- Animations played when the SNPC fires a secondary weapon attack
ENT.WeaponAttackSecondaryTimeUntilFire = 0.9 -- The weapon uses this integer to set the time until the firing code is ran
-- ====== Moving-Firing Variables ====== --
ENT.HasShootWhileMoving = true -- Can it shoot while moving?
ENT.AnimTbl_ShootWhileMovingRun = {ACT_RUN_AIM} -- Animations it will play when shooting while running
ENT.AnimTbl_ShootWhileMovingWalk = {ACT_WALK_AIM} -- Animations it will play when shooting while walking
-- ====== Reloading Variables ====== --
ENT.AllowWeaponReloading = true -- If false, the NPC will not reload
ENT.DisableWeaponReloadAnimation = false -- if true, it will disable the animation code when reloading
ENT.AnimTbl_WeaponReload = {ACT_RELOAD} -- Animations that play when the NPC reloads
ENT.AnimTbl_WeaponReloadBehindCover = {ACT_RELOAD_LOW} -- Animations that play when the NPC reloads, but behind cover
ENT.WeaponReload_FindCover = true -- Should it first find cover before reloading?
ENT.WeaponReloadAnimationFaceEnemy = true -- Should it face the enemy while playing the weapon reload animation?
ENT.WeaponReloadAnimationDecreaseLengthAmount = 0 -- This will decrease the time until it starts moving or attack again. Use it to fix animation pauses until it chases the enemy.
ENT.WeaponReloadAnimationDelay = 0 -- It will wait certain amount of time before playing the animation
-- ====== Weapon Inventory Variables ====== --
-- Weapons are given on spawn and the NPC will only switch to those if the requirements are met
-- The items that are stored in self.WeaponInventory:
-- Primary - Default weapon
-- AntiArmor - Current enemy is an armored enemy tank/vehicle or a boss
-- Melee - Current enemy is (very close and the NPC is out of ammo) OR (in regular melee attack distance) + NPC must have more than 25% health
ENT.WeaponInventory_AntiArmor = false -- If true, the NPC will spawn with one of the given weapons (Will only be given the weapon if it already has another!)
ENT.WeaponInventory_AntiArmorList = {} -- It will randomly be given one of these weapons
ENT.WeaponInventory_Melee = false -- If true, the NPC will spawn with one of the given weapons (Will only be given the weapon if it already has another!)
ENT.WeaponInventory_MeleeList = {} -- It will randomly be given one of these weapons
-- ====== Move Randomly While Firing Variables ====== --
ENT.MoveRandomlyWhenShooting = true -- Should it move randomly when shooting?
ENT.NextMoveRandomlyWhenShootingTime1 = 3 -- How much time until it can move randomly when shooting? | First number in math.random
ENT.NextMoveRandomlyWhenShootingTime2 = 6 -- How much time until it can move randomly when shooting? | Second number in math.random
-- ====== Wait For Enemy To Come Out Variables ====== --
ENT.WaitForEnemyToComeOut = true -- Should it wait for the enemy to come out from hiding?
ENT.WaitForEnemyToComeOutTime = VJ_Set(3, 5) -- How much time should it wait until it starts chasing the enemy?
ENT.WaitForEnemyToComeOutDistance = 100 -- If it's this close to the enemy, it won't do it
ENT.HasLostWeaponSightAnimation = false -- Set to true if you would like the SNPC to play a different animation when it has lost sight of the enemy and can't fire at it
-- ====== Scared Behavior Variables ====== --
ENT.NoWeapon_UseScaredBehavior = true -- Should it use the scared behavior when it sees an enemy and doesn't have a weapon?
ENT.AnimTbl_ScaredBehaviorStand = nil -- Animations it will play while scared and standing | Replaces the idle stand animation | DEFAULT: {ACT_COWER}
ENT.AnimTbl_ScaredBehaviorMovement = {} -- Animations it will play while scared and moving | Leave empty for the base to decide the animation
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------ Grenade Attack Variables ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ENT.HasGrenadeAttack = false -- Should the SNPC have a grenade attack?
ENT.GrenadeAttackEntity = "obj_vj_grenade" -- The entity that the SNPC throws | Half Life 2 Grenade: "npc_grenade_frag"
ENT.GrenadeAttackModel = {} -- Picks a random model from this table to override the model of the grenade
ENT.GrenadeAttackAttachment = "anim_attachment_LH" -- The attachment that the grenade will spawn at | false = Custom position
-- ====== Animation Variables ====== --
ENT.AnimTbl_GrenadeAttack = {"grenThrow"} -- Grenade Attack Animations
ENT.GrenadeAttackAnimationDelay = 0 -- It will wait certain amount of time before playing the animation
ENT.GrenadeAttackAnimationFaceEnemy = true -- Should it face the enemy while playing the grenade attack animation?
-- ====== Distance & Chance Variables ====== --
ENT.NextThrowGrenadeTime = VJ_Set(10, 15) -- Time until it can throw a grenade again
ENT.ThrowGrenadeChance = 4 -- Chance that it will throw the grenade | Set to 1 to throw all the time
ENT.GrenadeAttackThrowDistance = 1500 -- How far it can throw grenades
ENT.GrenadeAttackThrowDistanceClose = 400 -- How close until it stops throwing grenades
-- ====== Timer Variables ====== --
ENT.TimeUntilGrenadeIsReleased = 0.72 -- Time until the grenade is released
ENT.GrenadeAttackAnimationStopAttacks = true -- Should it stop attacks for a certain amount of time?
-- To let the base automatically detect the attack duration, set this to false:
ENT.GrenadeAttackAnimationStopAttacksTime = false -- How long should it stop attacks?
ENT.GrenadeAttackFussTime = 3 -- Time until the grenade explodes
-- ====== Control Variables ====== --
ENT.DisableGrenadeAttackAnimation = false -- if true, it will disable the animation code when doing grenade attack
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------ Sound Variables ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ENT.HasSounds = true -- Put to false to disable ALL sounds!
-- ====== Footstep Sound / World Shake On Move Variables ====== --
ENT.DisableFootStepSoundTimer = false -- If set to true, it will disable the time system for the footstep sound code, allowing you to use other ways like model events
ENT.FootStepTimeRun = 0.5 -- Next foot step sound when it is running
ENT.FootStepTimeWalk = 1 -- Next foot step sound when it is walking
ENT.DisableFootStepOnRun = false -- It will not play the footstep sound when running
ENT.DisableFootStepOnWalk = false -- It will not play the footstep sound when walking
ENT.HasWorldShakeOnMove = false -- Should the world shake when it's moving?
ENT.WorldShakeOnMoveAmplitude = 10 -- How much the screen will shake | From 1 to 16, 1 = really low 16 = really high
ENT.WorldShakeOnMoveRadius = 1000 -- How far the screen shake goes, in world units
ENT.WorldShakeOnMoveDuration = 0.4 -- How long the screen shake will last, in seconds
ENT.WorldShakeOnMoveFrequency = 100 -- Just leave it to 100
-- ====== Idle Sound Variables ====== --
ENT.IdleSounds_PlayOnAttacks = false -- It will be able to continue and play idle sounds when it performs an attack
ENT.IdleSounds_NoRegularIdleOnAlerted = false -- if set to true, it will not play the regular idle sound table if the combat idle sound table is empty
-- ====== Idle dialogue Sound Variables ====== --
-- When an allied SNPC or a allied player is in range, the SNPC will play a different sound table. If the ally is a VJ SNPC and has dialogue answer sounds, it will respond to this SNPC
ENT.HasIdleDialogueSounds = true -- If set to false, it won't play the idle dialogue sounds
ENT.HasIdleDialogueAnswerSounds = true -- If set to false, it won't play the idle dialogue answer sounds
ENT.IdleDialogueDistance = 400 -- How close should the ally be for the SNPC to talk to the ally?
ENT.IdleDialogueCanTurn = true -- If set to false, it won't turn when a dialogue occurs
-- ====== Miscellaneous Variables ====== --
ENT.AlertSounds_OnlyOnce = false -- After it plays it once, it will never play it again
ENT.BeforeMeleeAttackSounds_WaitTime = 0 -- Time until it starts playing the Before Melee Attack sounds
ENT.OnlyDoKillEnemyWhenClear = true -- If set to true, it will only play the OnKilledEnemy sound when there isn't any other enemies
-- ====== Main Control Variables ====== --
ENT.HasFootStepSound = true -- Should the SNPC make a footstep sound when it's moving?
ENT.HasBreathSound = true -- Should it make a breathing sound?
ENT.HasIdleSounds = true -- If set to false, it won't play the idle sounds
ENT.HasOnReceiveOrderSounds = true -- If set to false, it won't play any sound when it receives an order from an ally
ENT.HasFollowPlayerSounds_Follow = true -- If set to false, it won't play the follow player sounds
ENT.HasFollowPlayerSounds_UnFollow = true -- If set to false, it won't play the unfollow player sounds
ENT.HasMoveOutOfPlayersWaySounds = true -- If set to false, it won't play any sounds when it moves out of the player's way
ENT.HasMedicSounds_BeforeHeal = true -- If set to false, it won't play any sounds before it gives a med kit to an ally
ENT.HasMedicSounds_AfterHeal = true -- If set to false, it won't play any sounds after it gives a med kit to an ally
ENT.HasMedicSounds_ReceiveHeal = true -- If set to false, it won't play any sounds when it receives a medkit
ENT.HasOnPlayerSightSounds = true -- If set to false, it won't play the saw player sounds
ENT.HasInvestigateSounds = true -- If set to false, it won't play any sounds when it's investigating something
ENT.HasLostEnemySounds = true -- If set to false, it won't play any sounds when it looses it enemy
ENT.HasAlertSounds = true -- If set to false, it won't play the alert sounds
ENT.HasCallForHelpSounds = true -- If set to false, it won't play any sounds when it calls for help
ENT.HasBecomeEnemyToPlayerSounds = true -- If set to false, it won't play the become enemy to player sounds
ENT.HasSuppressingSounds = true -- If set to false, it won't play any sounds when firing a weapon
ENT.HasWeaponReloadSounds = true -- If set to false, it won't play any sound when reloading
ENT.HasMeleeAttackSounds = true -- If set to false, it won't play the melee attack sound
ENT.HasExtraMeleeAttackSounds = false -- Set to true to use the extra melee attack sounds
ENT.HasMeleeAttackMissSounds = true -- If set to false, it won't play the melee attack miss sound
ENT.HasGrenadeAttackSounds = true -- If set to false, it won't play any sound when doing grenade attack
ENT.HasOnGrenadeSightSounds = true -- If set to false, it won't play any sounds when it sees a grenade
ENT.HasOnDangerSightSounds = true -- If set to false, it won't play any sounds when it detects a danger
ENT.HasOnKilledEnemySound = true -- Should it play a sound when it kills an enemy?
ENT.HasAllyDeathSound = true -- Should it paly a sound when an ally dies?
ENT.HasPainSounds = true -- If set to false, it won't play the pain sounds
ENT.HasImpactSounds = true -- If set to false, it won't play the impact sounds
ENT.HasDamageByPlayerSounds = true -- If set to false, it won't play the damage by player sounds
ENT.HasDeathSounds = true -- If set to false, it won't play the death sounds
ENT.HasSoundTrack = false -- Does the SNPC have a sound track?
-- ====== File Path Variables ====== --
-- Leave blank if you don't want any sounds to play
ENT.SoundTbl_FootStep = {}
ENT.SoundTbl_Breath = {}
ENT.SoundTbl_Idle = {}
ENT.SoundTbl_IdleDialogue = {}
ENT.SoundTbl_IdleDialogueAnswer = {}
ENT.SoundTbl_CombatIdle = {}
ENT.SoundTbl_OnReceiveOrder = {}
ENT.SoundTbl_FollowPlayer = {}
ENT.SoundTbl_UnFollowPlayer = {}
ENT.SoundTbl_MoveOutOfPlayersWay = {}
ENT.SoundTbl_MedicBeforeHeal = {}
ENT.SoundTbl_MedicAfterHeal = {}
ENT.SoundTbl_MedicReceiveHeal = {}
ENT.SoundTbl_OnPlayerSight = {}
ENT.SoundTbl_Investigate = {}
ENT.SoundTbl_LostEnemy = {}
ENT.SoundTbl_Alert = {}
ENT.SoundTbl_CallForHelp = {}
ENT.SoundTbl_BecomeEnemyToPlayer = {}
ENT.SoundTbl_Suppressing = {}
ENT.SoundTbl_WeaponReload = {}
ENT.SoundTbl_BeforeMeleeAttack = {}
ENT.SoundTbl_MeleeAttack = {}
ENT.SoundTbl_MeleeAttackExtra = {}
ENT.SoundTbl_MeleeAttackMiss = {}
ENT.SoundTbl_GrenadeAttack = {}
ENT.SoundTbl_OnGrenadeSight = {}
ENT.SoundTbl_OnDangerSight = {}
ENT.SoundTbl_OnKilledEnemy = {}
ENT.SoundTbl_AllyDeath = {}
ENT.SoundTbl_Pain = {}
ENT.SoundTbl_Impact = {}
ENT.SoundTbl_DamageByPlayer = {}
ENT.SoundTbl_Death = {}
ENT.SoundTbl_SoundTrack = {}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------ ///// WARNING: Don't change anything in this box! \\\\\ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- These are the default file paths in case the user doesn't put one (tables above).
local DefaultSoundTbl_FootStep = {"npc/metropolice/gear1.wav","npc/metropolice/gear2.wav","npc/metropolice/gear3.wav","npc/metropolice/gear4.wav","npc/metropolice/gear5.wav","npc/metropolice/gear6.wav"}
local DefaultSoundTbl_MedicAfterHeal = {"items/smallmedkit1.wav"}
ENT.DefaultSoundTbl_MeleeAttack = {"physics/body/body_medium_impact_hard1.wav","physics/body/body_medium_impact_hard2.wav","physics/body/body_medium_impact_hard3.wav","physics/body/body_medium_impact_hard4.wav","physics/body/body_medium_impact_hard5.wav","physics/body/body_medium_impact_hard6.wav"}
local DefaultSoundTbl_MeleeAttackMiss = {"npc/zombie/claw_miss1.wav","npc/zombie/claw_miss2.wav"}
local DefaultSoundTbl_Impact = {"physics/flesh/flesh_impact_bullet1.wav","physics/flesh/flesh_impact_bullet2.wav","physics/flesh/flesh_impact_bullet3.wav","physics/flesh/flesh_impact_bullet4.wav","physics/flesh/flesh_impact_bullet5.wav"}
------ ///// WARNING: Don't change anything in this box! \\\\\ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ====== Fade Out Time Variables ====== --
-- Put to 0 if you want it to stop instantly
ENT.SoundTrackFadeOutTime = 2
-- ====== Sound Chance Variables ====== --
-- Higher number = less chance of playing | 1 = Always play
ENT.IdleSoundChance = 3
ENT.IdleDialogueAnswerSoundChance = 1
ENT.CombatIdleSoundChance = 1
ENT.OnReceiveOrderSoundChance = 1
ENT.FollowPlayerSoundChance = 1
ENT.UnFollowPlayerSoundChance = 1
ENT.MoveOutOfPlayersWaySoundChance = 2
ENT.MedicBeforeHealSoundChance = 1
ENT.MedicAfterHealSoundChance = 1
ENT.MedicReceiveHealSoundChance = 1
ENT.OnPlayerSightSoundChance = 1
ENT.InvestigateSoundChance = 1
ENT.LostEnemySoundChance = 1
ENT.AlertSoundChance = 1
ENT.CallForHelpSoundChance = 1
ENT.BecomeEnemyToPlayerChance = 1
ENT.BeforeMeleeAttackSoundChance = 1
ENT.MeleeAttackSoundChance = 1
ENT.ExtraMeleeSoundChance = 1
ENT.MeleeAttackMissSoundChance = 1
ENT.GrenadeAttackSoundChance = 1
ENT.OnGrenadeSightSoundChance = 1
ENT.OnDangerSightSoundChance = 1
ENT.SuppressingSoundChance = 2
ENT.WeaponReloadSoundChance = 1
ENT.OnKilledEnemySoundChance = 1
ENT.AllyDeathSoundChance = 4
ENT.PainSoundChance = 1
ENT.ImpactSoundChance = 1
ENT.DamageByPlayerSoundChance = 1
ENT.DeathSoundChance = 1
ENT.SoundTrackChance = 1
-- ====== Timer Variables ====== --
-- Randomized time between the two variables, x amount of time has to pass for the sound to play again | Counted in seconds
ENT.NextSoundTime_Breath = true -- true = Base will decide the time | VJ_Set(1, 2) = Custom time
ENT.NextSoundTime_Idle = VJ_Set(8, 25)
ENT.NextSoundTime_Investigate = VJ_Set(5, 5)
ENT.NextSoundTime_LostEnemy = VJ_Set(5, 6)
ENT.NextSoundTime_Alert = VJ_Set(2, 3)
ENT.NextSoundTime_OnGrenadeSight = VJ_Set(3, 3)
ENT.NextSoundTime_OnDangerSight = VJ_Set(3, 3)
ENT.NextSoundTime_Suppressing = VJ_Set(7, 15)
ENT.NextSoundTime_WeaponReload = VJ_Set(3, 5)
ENT.NextSoundTime_OnKilledEnemy = VJ_Set(3, 5)
ENT.NextSoundTime_AllyDeath = VJ_Set(3, 5)
ENT.NextSoundTime_Pain = true -- true = Base will decide the time | VJ_Set(1, 2) = Custom time
ENT.NextSoundTime_DamageByPlayer = VJ_Set(2, 2.3)
-- ====== Volume Variables ====== --
-- Number must be between 0 and 1
-- 0 = No sound, 1 = normal/loudest
ENT.SoundTrackVolume = 1
-- ====== Sound Level Variables ====== --
-- The proper number are usually range from 0 to 180, though it can go as high as 511
-- More Information: https://developer.valvesoftware.com/wiki/Soundscripts#SoundLevel_Flags
ENT.FootStepSoundLevel = 70
ENT.BreathSoundLevel = 60
ENT.IdleSoundLevel = 75
ENT.IdleDialogueSoundLevel = 75
ENT.IdleDialogueAnswerSoundLevel = 75
ENT.CombatIdleSoundLevel = 80
ENT.OnReceiveOrderSoundLevel = 80
ENT.FollowPlayerSoundLevel = 75
ENT.UnFollowPlayerSoundLevel = 75
ENT.MoveOutOfPlayersWaySoundLevel = 75
ENT.BeforeHealSoundLevel = 75
ENT.AfterHealSoundLevel = 75
ENT.MedicReceiveHealSoundLevel = 75
ENT.OnPlayerSightSoundLevel = 75
ENT.InvestigateSoundLevel = 80
ENT.LostEnemySoundLevel = 75
ENT.AlertSoundLevel = 80
ENT.CallForHelpSoundLevel = 80
ENT.BecomeEnemyToPlayerSoundLevel = 75
ENT.BeforeMeleeAttackSoundLevel = 75
ENT.MeleeAttackSoundLevel = 75
ENT.ExtraMeleeAttackSoundLevel = 75
ENT.MeleeAttackMissSoundLevel = 75
ENT.SuppressingSoundLevel = 80
ENT.WeaponReloadSoundLevel = 80
ENT.GrenadeAttackSoundLevel = 80
ENT.OnGrenadeSightSoundLevel = 80
ENT.OnDangerSightSoundLevel = 80
ENT.OnKilledEnemySoundLevel = 80
ENT.AllyDeathSoundLevel = 80
ENT.PainSoundLevel = 80
ENT.ImpactSoundLevel = 60
ENT.DamageByPlayerSoundLevel = 75
ENT.DeathSoundLevel = 80
//ENT.SoundTrackLevel = 0.9
-- ====== Sound Pitch Variables ====== --
-- Range: 0 - 255 | Lower pitch < x > Higher pitch
ENT.UseTheSameGeneralSoundPitch = true -- If set to true, the base will decide a number when the NPC spawns and uses it for all sound pitches set to false
-- It picks the number between these two variables below:
-- These two variables control any sound pitch variable that is set to false
ENT.GeneralSoundPitch1 = 90
ENT.GeneralSoundPitch2 = 100
-- To not use the variables above, set the pitch to something other than false
ENT.FootStepPitch = VJ_Set(80, 100)
ENT.BreathSoundPitch = VJ_Set(100, 100)
ENT.IdleSoundPitch = VJ_Set(false, false)
ENT.IdleDialogueSoundPitch = VJ_Set(false, false)
ENT.IdleDialogueAnswerSoundPitch = VJ_Set(false, false)
ENT.CombatIdleSoundPitch = VJ_Set(false, false)
ENT.OnReceiveOrderSoundPitch = VJ_Set(false, false)
ENT.FollowPlayerPitch = VJ_Set(false, false)
ENT.UnFollowPlayerPitch = VJ_Set(false, false)
ENT.MoveOutOfPlayersWaySoundPitch = VJ_Set(false, false)
ENT.BeforeHealSoundPitch = VJ_Set(false, false)
ENT.AfterHealSoundPitch = VJ_Set(100, 100)
ENT.MedicReceiveHealSoundPitch = VJ_Set(false, false)
ENT.OnPlayerSightSoundPitch = VJ_Set(false, false)
ENT.InvestigateSoundPitch = VJ_Set(false, false)
ENT.LostEnemySoundPitch = VJ_Set(false, false)
ENT.AlertSoundPitch = VJ_Set(false, false)
ENT.CallForHelpSoundPitch = VJ_Set(false, false)
ENT.BecomeEnemyToPlayerPitch = VJ_Set(false, false)
ENT.BeforeMeleeAttackSoundPitch = VJ_Set(false, false)
ENT.MeleeAttackSoundPitch = VJ_Set(95, 100)
ENT.ExtraMeleeSoundPitch = VJ_Set(80, 100)
ENT.MeleeAttackMissSoundPitch = VJ_Set(90, 100)
ENT.SuppressingPitch = VJ_Set(false, false)
ENT.WeaponReloadSoundPitch = VJ_Set(false, false)
ENT.GrenadeAttackSoundPitch = VJ_Set(false, false)
ENT.OnGrenadeSightSoundPitch = VJ_Set(false, false)
ENT.OnDangerSightSoundPitch = VJ_Set(false, false)
ENT.OnKilledEnemySoundPitch = VJ_Set(false, false)
ENT.AllyDeathSoundPitch = VJ_Set(false, false)
ENT.PainSoundPitch = VJ_Set(false, false)
ENT.ImpactSoundPitch = VJ_Set(80, 100)
ENT.DamageByPlayerPitch = VJ_Set(false, false)
ENT.DeathSoundPitch = VJ_Set(false, false)
-- ====== Playback Rate Variables ====== --
-- Decides how fast the sound should play
-- Examples: 1 = normal, 2 = twice the normal speed, 0.5 = half the normal speed
ENT.SoundTrackPlaybackRate = 1
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------ Customization Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Use the functions below to customize certain parts of the base or to add new custom systems
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnPreInitialize() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnInitialize()
-- self:SetCollisionBounds(Vector(50, 50, 100), Vector(-50, -50, 0)) -- Collision bounds of the NPC | WARNING: All 4 Xs and Ys should be the same!
-- self:SetSurroundingBounds(Vector(-300, -300, 0), Vector(300, 300, 500)) -- Damage bounds of the NPC, doesn't effect collision or OBB | NOTE: Only set this if the base one is not good enough! | Use "cl_ent_absbox" to view the bounds
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnThink() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnThink_AIEnabled() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnSetupRelationships(ent, entFri, entDist) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnChangeMovementType(movType) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnIsJumpLegal(startPos, apex, endPos) end -- Return nothing to let base decide, return true to make it jump, return false to disallow jumping
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOn_PoseParameterLookingCode(pitch, yaw, roll) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnSetupWeaponHoldTypeAnims(hType) return false end -- return true to disable the base code
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnSchedule() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnChangeActivity(newAct) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:ExpressionFinished(strExp) end
---------------------------------------------------------------------------------------------------------------------------------------------
-- Uncomment to use | Called whenever "VJ_CreateSound" or "VJ_EmitSound" is called | return a new file path to replace the one that is about to play
-- function ENT:OnCreateSound(sdFile) return "example/sound.wav" end
---------------------------------------------------------------------------------------------------------------------------------------------
-- Uncomment to use | Called whenever a sound starts playing through "VJ_CreateSound"
-- function ENT:OnPlayCreateSound(sdData, sdFile) end
---------------------------------------------------------------------------------------------------------------------------------------------
-- Uncomment to use | Called whenever a sound starts playing through "VJ_EmitSound"
-- function ENT:OnPlayEmitSound(sdFile) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:OnFireBullet(ent, data) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnTouch(ent) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnCondition(cond) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnAcceptInput(key, activator, caller, data) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnHandleAnimEvent(ev, evTime, evCycle, evType, evOptions) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnFollowPlayer(ply) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnUnFollowPlayer(ply) end
---------------------------------------------------------------------------------------------------------------------------------------------
--[[---------------------------------------------------------
Called every time a change occurs in the eating system
- ent = The entity that it is checking OR speaking with
- status = The change that occurred, possible changes:
- "CheckEnt" = Possible friendly entity found, should we speak to it? | return anything other than nil or "false" to skip and not speak to this entity!
- "Speak" = Everything passed, start speaking
- "Answer" = Another entity has spoken to me, answer back! | return anything other than nil or "false" to not play an answer back dialogue!
- statusInfo = Some status may have extra info, possible infos:
- For "CheckEnt" = Boolean value, whether or not the entity can answer back
- For "Speak" = Duration of our sentence
Returns
- ONLY used for "CheckEnt" & "Answer" | Check above for what each status return does
-----------------------------------------------------------]]
function ENT:CustomOnIdleDialogue(ent, status, statusInfo) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnMedic_BeforeHeal() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnMedic_OnHeal(ent) return true end -- Return false to NOT update its ally's health and NOT clear its decals, allowing to custom code it
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnMedic_OnReset() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnPlayerSight(ent) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnFootStepSound() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnFootStepSound_Run() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnFootStepSound_Walk() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnWorldShakeOnMove() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnDoChangeWeapon(newWeapon, oldWeapon, invSwitch) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnInvestigate(ent) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnResetEnemy() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnAlert(ent) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnCallForHelp(ally) end
---------------------------------------------------------------------------------------------------------------------------------------------
-- The NPC's sight direction | Used by main sight angle, all attack angle radiuses, etc.
function ENT:GetSightDirection()
//return self:GetAttachment(self:LookupAttachment("mouth")).Ang:Forward() -- Attachment example
//return select(2, self:GetBonePosition(self:LookupBone("bip01 head"))):Forward() -- Bone example
return self:GetForward() -- Make sure to return a direction!
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:GetDynamicOrigin()
return self:GetPos() + self:GetForward() -- Override this to use a different position
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomAttack(ene, eneVisible) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnMeleeAttack_BeforeStartTimer(seed) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnMeleeAttack_AfterStartTimer(seed) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnMeleeAttack_BeforeChecks() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:GetMeleeAttackDamageOrigin()
return self:GetPos() + self:GetForward() -- Override this to use a different position
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnMeleeAttack_AfterChecks(hitEnt, isProp) return false end -- return true to disable the attack and move onto the next entity!
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:MeleeAttackKnockbackVelocity(hitEnt)
return self:GetForward()*math.random(100, 140) + self:GetUp()*10
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnMeleeAttack_Miss() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnIsAbleToShootWeapon()
return true -- Set this to false to disallow shooting
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnWeaponAttack() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnMoveRandomlyWhenShooting() end -- Returning false will disable the base code
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnWaitForEnemyToComeOut() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnWeaponReload() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnWeaponReload_AfterRanToCover() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnGrenadeAttack_BeforeStartTimer() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnGrenadeAttack_SpawnPosition() return self:GetPos() end -- if self.GrenadeAttackAttachment is set to false, it will use the returned position here!
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnGrenadeAttack_ThrowVelocity(grEnt, grTargetPos) return (grTargetPos - grEnt:GetPos()) + (self:GetUp()*200 + self:GetForward()*500 + self:GetRight()*math.Rand(-20, 20)) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnGrenadeAttack_OnThrow(grEnt) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnDoKilledEnemy(ent, attacker, inflictor) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnTakeDamage_BeforeImmuneChecks(dmginfo, hitgroup) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnTakeDamage_BeforeDamage(dmginfo, hitgroup) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnTakeDamage_AfterDamage(dmginfo, hitgroup) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnTakeDamage_OnBleed(dmginfo, hitgroup) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnFlinch_BeforeFlinch(dmginfo, hitgroup) end -- Return false to disallow the flinch from playing
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnFlinch_AfterFlinch(dmginfo, hitgroup) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnDamageByPlayer(dmginfo, hitgroup) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnBecomeEnemyToPlayer(dmginfo, hitgroup) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnSetEnemyOnDamage(dmginfo, hitgroup) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:SetUpGibesOnDeath(dmginfo, hitgroup)
return false -- Return to true if it gibbed!
/*--------------------------------------
-- Extra Features --
Extra features allow you to have more control over the gibbing system.
--/// Types \\\--
AllowCorpse -- Should it allow corpse to spawn?
DeathAnim -- Should it allow death animation?
--/// Implementing it \\\--
1. Let's use type DeathAnim as an example. NOTE: You can have as many types as you want!
2. Put a comma next to return. ===> return true,
3. Make a table after the comma. ===> return true, {}
4. Put the type(s) that you want. ===> return true, {DeathAnim=true}
5. And you are done!
Example with multiple types: ===> return true, {DeathAnim=true,AllowCorpse=true}
--------------------------------------*/
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomGibOnDeathSounds(dmginfo, hitgroup) return true end -- returning false will make the default gibbing sounds not play
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnAllyDeath(ent) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnInitialKilled(dmginfo, hitgroup) end -- Ran the moment the NPC dies!
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnPriorToKilled(dmginfo, hitgroup) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomDeathAnimationCode(dmginfo, hitgroup) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnKilled(dmginfo, hitgroup) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomRareDropsOnDeathCode(dmginfo, hitgroup) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnDropWeapon(dmginfo, hitgroup) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnDropWeapon_AfterWeaponSpawned(dmginfo, hitgroup, wepEnt) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnDeath_BeforeCorpseSpawned(dmginfo, hitgroup) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnDeath_AfterCorpseSpawned(dmginfo, hitgroup, corpseEnt) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnRemove() end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:Controller_Initialize(ply, controlEnt) end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:Controller_IntMsg(ply, controlEnt)
//ply:ChatPrint("CTRL + MOUSE2: Rocket Attack") -- Example
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------ ///// WARNING: Don't touch anything below this line! \\\\\ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ENT.Alerted = false
ENT.Dead = false
ENT.Flinching = false
ENT.vACT_StopAttacks = false
ENT.IsFollowing = false
ENT.FollowingPlayer = false
ENT.VJ_IsBeingControlled = false
ENT.VJ_PlayingSequence = false
ENT.PlayingAttackAnimation = false
ENT.DoingWeaponAttack = false
ENT.DoingWeaponAttack_Standing = false
ENT.WaitingForEnemyToComeOut = false
ENT.VJ_DEBUG = false
ENT.DidWeaponAttackAimParameter = false
ENT.Medic_Status = false -- false = Not active | "Active" = Attempting to heal ally (Going after etc.) | "Healing" = Has reached ally and is healing it
ENT.VJ_PlayingInterruptSequence = false
ENT.IsAbleToMeleeAttack = true
ENT.AllowToDo_WaitForEnemyToComeOut = true
ENT.HasBeenGibbedOnDeath = false
ENT.DeathAnimationCodeRan = false
ENT.VJ_IsBeingControlled_Tool = false
ENT.WeaponUseEnemyEyePos = false
ENT.LastHiddenZone_CanWander = true
ENT.NoWeapon_UseScaredBehavior_Active = false
ENT.CurIdleStandMove = false
ENT.CurrentWeaponAnimationIsAim = false
ENT.VJ_TheController = NULL
ENT.VJ_TheControllerEntity = NULL
ENT.VJ_TheControllerBullseye = NULL
ENT.Medic_CurrentEntToHeal = NULL
ENT.Medic_SpawnedProp = NULL
ENT.CurrentWeaponEntity = NULL
ENT.LastPlayedVJSound = nil
ENT.NextDangerDetectionT = 0
ENT.NextFollowUpdateT = 0
ENT.AngerLevelTowardsPlayer = 0
ENT.NextBreathSoundT = 0
ENT.FootStepT = 0
ENT.PainSoundT = 0
ENT.AllyDeathSoundT = 0
ENT.WorldShakeWalkT = 0
ENT.NextIdleSoundT = 0
ENT.NextNoWeaponT = 0
ENT.NextCallForHelpT = 0
ENT.NextProcessT = 0
ENT.NextThrowGrenadeT = 0
ENT.NextCallForBackUpOnDamageT = 0
ENT.NextOnGrenadeSightSoundT = 0
ENT.NextOnDangerSightSoundT = 0
ENT.NextMoveOrHideOnDamageByEnemyT = 0
ENT.NextAlertSoundT = 0
ENT.NextCallForHelpAnimationT = 0
ENT.CurrentAttackAnimation = 0
ENT.CurrentAttackAnimationDuration = 0
ENT.NextIdleTime = 0
ENT.NextChaseTime = 0
ENT.OnPlayerSightNextT = 0
ENT.NextDamageByPlayerT = 0
ENT.NextDamageByPlayerSoundT = 0
ENT.NextWeaponReloadSoundT = 0
ENT.Medic_NextHealT = 0
ENT.Weapon_TimeSinceLastShot = 0
ENT.NextMoveRandomlyWhenShootingT = 0
ENT.NextWeaponAttackT = 0
ENT.NextMeleeWeaponAttackT = 0
ENT.CurrentWeaponAnimation = -1
ENT.NextGrenadeAttackSoundT = 0
ENT.CurrentAnim_IdleStand = 0
ENT.NextSuppressingSoundT = 0
ENT.TakingCoverT = 0
ENT.NextFlinchT = 0
ENT.NextCanGetCombineBallDamageT = 0
ENT.UseTheSameGeneralSoundPitch_PickedNumber = 0
ENT.OnKilledEnemySoundT = 0
ENT.LastHiddenZoneT = 0
ENT.NextIdleStandTime = 0
ENT.NextMoveOnGunCoveredT = 0
ENT.NextWanderTime = 0
ENT.Weapon_DoingCrouchAttackT = 0
ENT.NextInvestigationMove = 0
ENT.NextInvestigateSoundT = 0
ENT.NextCallForHelpSoundT = 0
ENT.LostEnemySoundT = 0
ENT.NextDoAnyAttackT = 0
ENT.NearestPointToEnemyDistance = 0
ENT.LatestEnemyDistance = 0
ENT.HealthRegenerationDelayT = 0
ENT.NextWeaponAttackT_Base = 0 -- This is handled by the base, used to avoid running shoot animation twice
ENT.CurAttackSeed = 0
ENT.CurAnimationSeed = 0
ENT.GuardingPosition = nil
ENT.GuardingFacePosition = nil
ENT.SelectedDifficulty = 1
ENT.ModelAnimationSet = VJ_MODEL_ANIMSET_NONE
ENT.AIState = VJ_STATE_NONE
ENT.AttackType = VJ_ATTACK_NONE
ENT.AttackStatus = VJ_ATTACK_STATUS_NONE
ENT.WeaponState = VJ_WEP_STATE_READY
ENT.WeaponInventoryStatus = VJ_WEP_INVENTORY_NONE
ENT.FacingStatus = VJ_FACING_NONE
ENT.FacingData = nil
ENT.TimersToRemove = {"timer_weapon_state_reset","timer_state_reset","timer_act_seqreset","timer_facing_end","timer_act_flinching","timer_act_playingattack","timer_act_stopattacks","timer_melee_finished","timer_melee_start","timer_melee_finished_abletomelee","timer_reload_end","timer_alerted_reset"}
ENT.FollowData = {Ent = NULL, MinDist = 0, Moving = false, StopAct = false}
ENT.EnemyData = {
TimeSet = 0, -- Last time an enemy was set | Updated whenever "VJ_DoSetEnemy" is ran successfully
TimeSinceAcquired = 0, -- Time since it acquired an enemy (Switching enemies does NOT reset this!)
IsVisible = false, -- Is the enemy visible? | Updated every "Think" run!
LastVisibleTime = 0, -- Last time it saw the current enemy
LastVisiblePos = Vector(0, 0, 0), -- Last visible position of the current enemy
VisibleCount = 0, -- Number of visible enemies
SightDiff = 0, -- Difference between enemy's position and NPC's sight direction | Examples: Determine if the enemy is within the NPC's sight angle or melee attack radius
Reset = true, -- Enemy has reset | Mostly a backend function
}
//ENT.DefaultGibOnDeathDamageTypes = {[DMG_ALWAYSGIB]=true,[DMG_ENERGYBEAM]=true,[DMG_BLAST]=true,[DMG_VEHICLE]=true,[DMG_CRUSH]=true,[DMG_DISSOLVE]=true,[DMG_SLOWBURN]=true,[DMG_PHYSGUN]=true,[DMG_PLASMA]=true,[DMG_SONIC]=true}
//ENT.SavedDmgInfo = {} -- Set later
-- Localized static values
local destructibleEnts = {func_breakable=true, func_physbox=true, prop_door_rotating=true} // func_breakable_surf
local defPos = Vector(0, 0, 0)
local defAng = Angle(0, 0, 0)
local IsProp = VJ_IsProp
local CurTime = CurTime
local IsValid = IsValid
local GetConVar = GetConVar
local isstring = isstring
local isnumber = isnumber
local tonumber = tonumber
local math_clamp = math.Clamp
local math_rad = math.rad
local math_cos = math.cos
local math_angApproach = math.ApproachAngle
local math_angDif = math.AngleDifference
local varCAnt = "CLASS_ANTLION"
local varCCom = "CLASS_COMBINE"
local varCZom = "CLASS_ZOMBIE"
---------------------------------------------------------------------------------------------------------------------------------------------
local function ConvarsOnInit(self)
--<>-- Convars that run on Initialize --<>--
if GetConVar("vj_npc_usedevcommands"):GetInt() == 1 then self.VJ_DEBUG = true end
self.NextProcessTime = GetConVar("vj_npc_processtime"):GetInt()
if GetConVar("vj_npc_sd_nosounds"):GetInt() == 1 then self.HasSounds = false end
if GetConVar("vj_npc_vjfriendly"):GetInt() == 1 then self:VJTags_Add(VJ_TAG_VJ_FRIENDLY) end
if GetConVar("vj_npc_playerfriendly"):GetInt() == 1 then self.PlayerFriendly = true end
if GetConVar("vj_npc_antlionfriendly"):GetInt() == 1 then self.VJ_NPC_Class[#self.VJ_NPC_Class + 1] = varCAnt end
if GetConVar("vj_npc_combinefriendly"):GetInt() == 1 then self.VJ_NPC_Class[#self.VJ_NPC_Class + 1] = varCCom end
if GetConVar("vj_npc_zombiefriendly"):GetInt() == 1 then self.VJ_NPC_Class[#self.VJ_NPC_Class + 1] = varCZom end
if GetConVar("vj_npc_noallies"):GetInt() == 1 then self.HasAllies = false self.PlayerFriendly = false end
if GetConVar("vj_npc_nocorpses"):GetInt() == 1 then self.HasDeathRagdoll = false end
if GetConVar("vj_npc_itemdrops"):GetInt() == 0 then self.HasItemDropsOnDeath = false end
if GetConVar("vj_npc_nowandering"):GetInt() == 1 then self.DisableWandering = true end
if GetConVar("vj_npc_nochasingenemy"):GetInt() == 1 then self.DisableChasingEnemy = true end
if GetConVar("vj_npc_noflinching"):GetInt() == 1 then self.CanFlinch = false end
if GetConVar("vj_npc_nomelee"):GetInt() == 1 then self.HasMeleeAttack = false end
if GetConVar("vj_npc_nobleed"):GetInt() == 1 then self.Bleeds = false end
if GetConVar("vj_npc_godmodesnpc"):GetInt() == 1 then self.GodMode = true end
if GetConVar("vj_npc_noreload"):GetInt() == 1 then self.AllowWeaponReloading = false end
if GetConVar("vj_npc_nobecomeenemytoply"):GetInt() == 1 then self.BecomeEnemyToPlayer = false end
if GetConVar("vj_npc_nocallhelp"):GetInt() == 1 then self.CallForHelp = false end
if GetConVar("vj_npc_noeating"):GetInt() == 1 then self.CanEat = false end
if GetConVar("vj_npc_nofollowplayer"):GetInt() == 1 then self.FollowPlayer = false end
if GetConVar("vj_npc_nosnpcchat"):GetInt() == 1 then self.AllowPrintingInChat = false end
if GetConVar("vj_npc_noweapon"):GetInt() == 1 then self.DisableWeapons = true end
if GetConVar("vj_npc_nothrowgrenade"):GetInt() == 1 then self.HasGrenadeAttack = false end
if GetConVar("vj_npc_nodangerdetection"):GetInt() == 1 then self.CanDetectDangers = false end
if GetConVar("vj_npc_dropweapon"):GetInt() == 0 then self.DropWeaponOnDeath = false end
if GetConVar("vj_npc_nomedics"):GetInt() == 1 then self.IsMedicSNPC = false end
if GetConVar("vj_npc_novfx_gibdeath"):GetInt() == 1 then self.HasGibDeathParticles = false end
if GetConVar("vj_npc_nogib"):GetInt() == 1 then self.AllowedToGib = false self.HasGibOnDeath = false end
if GetConVar("vj_npc_usegmoddecals"):GetInt() == 1 then self.BloodDecalUseGMod = true end
if GetConVar("vj_npc_knowenemylocation"):GetInt() == 1 then self.FindEnemy_UseSphere = true self.FindEnemy_CanSeeThroughWalls = true end
if GetConVar("vj_npc_sd_gibbing"):GetInt() == 1 then self.HasGibOnDeathSounds = false end
if GetConVar("vj_npc_sd_soundtrack"):GetInt() == 1 then self.HasSoundTrack = false end
if GetConVar("vj_npc_sd_footstep"):GetInt() == 1 then self.HasFootStepSound = false end
if GetConVar("vj_npc_sd_idle"):GetInt() == 1 then self.HasIdleSounds = false end
if GetConVar("vj_npc_sd_breath"):GetInt() == 1 then self.HasBreathSound = false end
if GetConVar("vj_npc_sd_alert"):GetInt() == 1 then self.HasAlertSounds = false end
if GetConVar("vj_npc_sd_ondangersight"):GetInt() == 1 then self.HasOnGrenadeSightSounds = false self.HasOnDangerSightSounds = false end
if GetConVar("vj_npc_sd_meleeattack"):GetInt() == 1 then self.HasMeleeAttackSounds = false self.HasExtraMeleeAttackSounds = false end
if GetConVar("vj_npc_sd_meleeattackmiss"):GetInt() == 1 then self.HasMeleeAttackMissSounds = false end
if GetConVar("vj_npc_sd_pain"):GetInt() == 1 then self.HasPainSounds = false end
if GetConVar("vj_npc_sd_death"):GetInt() == 1 then self.HasDeathSounds = false end
if GetConVar("vj_npc_sd_followplayer"):GetInt() == 1 then self.HasFollowPlayerSounds_Follow = false self.HasFollowPlayerSounds_UnFollow = false end
if GetConVar("vj_npc_sd_becomenemytoply"):GetInt() == 1 then self.HasBecomeEnemyToPlayerSounds = false end
if GetConVar("vj_npc_sd_damagebyplayer"):GetInt() == 1 then self.HasDamageByPlayerSounds = false end
if GetConVar("vj_npc_sd_onplayersight"):GetInt() == 1 then self.HasOnPlayerSightSounds = false end
if GetConVar("vj_npc_sd_medic"):GetInt() == 1 then self.HasMedicSounds_BeforeHeal = false self.HasMedicSounds_AfterHeal = false self.HasMedicSounds_ReceiveHeal = false end
if GetConVar("vj_npc_sd_reload"):GetInt() == 1 then self.HasWeaponReloadSounds = false end
if GetConVar("vj_npc_sd_grenadeattack"):GetInt() == 1 then self.HasGrenadeAttackSounds = false end
if GetConVar("vj_npc_sd_suppressing"):GetInt() == 1 then self.HasSuppressingSounds = false end
if GetConVar("vj_npc_sd_callforhelp"):GetInt() == 1 then self.HasCallForHelpSounds = false end
if GetConVar("vj_npc_sd_onreceiveorder"):GetInt() == 1 then self.HasOnReceiveOrderSounds = false end
local corpseCollision = GetConVar("vj_npc_corpsecollision"):GetInt()
if corpseCollision != 0 && self.DeathCorpseCollisionType == COLLISION_GROUP_DEBRIS then
if corpseCollision == 1 then
self.DeathCorpseCollisionType = COLLISION_GROUP_NONE
elseif corpseCollision == 2 then
self.DeathCorpseCollisionType = COLLISION_GROUP_WORLD
elseif corpseCollision == 3 then
self.DeathCorpseCollisionType = COLLISION_GROUP_INTERACTIVE
elseif corpseCollision == 4 then
self.DeathCorpseCollisionType = COLLISION_GROUP_WEAPON
elseif corpseCollision == 5 then
self.DeathCorpseCollisionType = COLLISION_GROUP_PASSABLE_DOOR
elseif corpseCollision == 6 then
self.DeathCorpseCollisionType = COLLISION_GROUP_NONE
end
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
local defIdleTbl = {ACT_IDLE}
local defWeaponAimTbl = {ACT_IDLE_ANGRY}
local defScaredStandTbl = {ACT_COWER}
--
function ENT:Initialize()
if self.AnimTbl_IdleStand == nil then self.AnimTbl_IdleStand = defIdleTbl end
if self.AnimTbl_WeaponAim == nil then self.AnimTbl_WeaponAim = defWeaponAimTbl end
if self.AnimTbl_ScaredBehaviorStand == nil then self.AnimTbl_ScaredBehaviorStand = defScaredStandTbl end
self:CustomOnPreInitialize()
self:SetSpawnEffect(false)
self:SetRenderMode(RENDERMODE_NORMAL) // RENDERMODE_TRANSALPHA
self:AddEFlags(EFL_NO_DISSOLVE)
self:SetUseType(SIMPLE_USE)
if self:GetName() == "" then
self:SetName((self.PrintName == "" and list.Get("NPC")[self:GetClass()].Name) or self.PrintName)
end
self.SelectedDifficulty = GetConVar("vj_npc_difficulty"):GetInt()
if VJ_PICK(self.Model) != false then self:SetModel(VJ_PICK(self.Model)) end
if self.HasHull == true then self:SetHullType(self.HullType) end
if self.HullSizeNormal == true then self:SetHullSizeNormal() end
if self.HasSetSolid == true then self:SetSolid(SOLID_BBOX) end // SOLID_OBB
self:SetCollisionGroup(COLLISION_GROUP_NPC)
//self:SetCustomCollisionCheck() -- Used for the hook GM:ShouldCollide, not reliable!
self:SetMaxYawSpeed(self.TurningSpeed)
ConvarsOnInit(self)
self:DoChangeMovementType()
if self.SetSurroundingBoundsType then -- !!!!!!!!!!!!!! Outdated GMod Compatibility! !!!!!!!!!!!!!!
self:SetSurroundingBoundsType(BOUNDS_HITBOXES) // BOUNDS_COLLISION
end
self.ExtraCorpsesToRemove_Transition = {}
self.VJ_AddCertainEntityAsEnemy = {}
self.VJ_AddCertainEntityAsFriendly = {}
self.CurrentPossibleEnemies = {}
self.WeaponAnimTranslations = {}
self.WeaponInventory = {}
self.NextThrowGrenadeT = CurTime() + math.Rand(1, 5)
self.NextIdleSoundT_RegularChange = CurTime() + math.random(0.3, 6)
self.UseTheSameGeneralSoundPitch_PickedNumber = (self.UseTheSameGeneralSoundPitch and math.random(self.GeneralSoundPitch1, self.GeneralSoundPitch2)) or 0
self:SetupBloodColor(self.BloodColor)
if self.Behavior == VJ_BEHAVIOR_PASSIVE or self.Behavior == VJ_BEHAVIOR_PASSIVE_NATURE then
self.DisableWeapons = true
self.Weapon_NoSpawnMenu = true
end
if self.DisableInitializeCapabilities == false then self:SetInitializeCapabilities() end
self:SetHealth((GetConVar("vj_npc_allhealth"):GetInt() > 0) and GetConVar("vj_npc_allhealth"):GetInt() or self:VJ_GetDifficultyValue(self.StartHealth))
self.StartHealth = self:Health()
//if self.HasSquad == true then self:Fire("setsquad", self.SquadName, 0) end
self:CustomOnInitialize()
self:CustomInitialize() -- !!!!!!!!!!!!!! DO NOT USE THIS FUNCTION !!!!!!!!!!!!!! [Backwards Compatibility!]
self.NextWanderTime = ((self.NextWanderTime != 0) and self.NextWanderTime) or (CurTime() + (self.IdleAlwaysWander and 0 or 1)) -- If self.NextWanderTime isn't given a value THEN if self.IdleAlwaysWander isn't true, wait at least 1 sec before wandering
self.SightDistance = (GetConVar("vj_npc_seedistance"):GetInt() > 0) and GetConVar("vj_npc_seedistance"):GetInt() or self.SightDistance
timer.Simple(0.15, function()
if IsValid(self) then
self:SetSightDistance(self.SightDistance)
if self:GetNPCState() <= NPC_STATE_NONE then self:SetNPCState(NPC_STATE_IDLE) end
if IsValid(self:GetCreator()) && self:GetCreator():GetInfoNum("vj_npc_spawn_guard", 0) == 1 then self.IsGuard = true end
self:StartSoundTrack()
-- pitch
if self:LookupPoseParameter("aim_pitch") then
self.PoseParameterLooking_Names.pitch[#self.PoseParameterLooking_Names.pitch + 1] = "aim_pitch"
end
if self:LookupPoseParameter("head_pitch") then
self.PoseParameterLooking_Names.pitch[#self.PoseParameterLooking_Names.pitch + 1] = "head_pitch"
end
-- yaw
if self:LookupPoseParameter("aim_yaw") then
self.PoseParameterLooking_Names.yaw[#self.PoseParameterLooking_Names.yaw + 1] = "aim_yaw"
end
if self:LookupPoseParameter("head_yaw") then
self.PoseParameterLooking_Names.yaw[#self.PoseParameterLooking_Names.yaw + 1] = "head_yaw"
end
-- roll
if self:LookupPoseParameter("aim_roll") then
self.PoseParameterLooking_Names.roll[#self.PoseParameterLooking_Names.roll + 1] = "aim_roll"
end
if self:LookupPoseParameter("head_roll") then
self.PoseParameterLooking_Names.roll[#self.PoseParameterLooking_Names.roll + 1] = "head_roll"
end
end
end)
duplicator.RegisterEntityClass(self:GetClass(), VJ.CreateDupe_NPC, "Class", "Equipment", "SpawnFlags", "Data")
if !self.DisableWeapons then
timer.Simple(0.1, function()
if IsValid(self) then
local wep = self:GetActiveWeapon()
if IsValid(wep) then
self.WeaponInventory.Primary = wep
if IsValid(self:GetCreator()) && self.AllowPrintingInChat == true && !wep.IsVJBaseWeapon then
self:GetCreator():PrintMessage(HUD_PRINTTALK, "WARNING: "..self:GetName().." requires a VJ Base weapon to work properly!")
end
if self.WeaponInventory_AntiArmor == true then
local antiArmor = VJ_PICK(self.WeaponInventory_AntiArmorList)
if antiArmor != false && wep:GetClass() != antiArmor then -- If the list isn't empty and it's not the current active weapon
self.WeaponInventory.AntiArmor = self:Give(antiArmor)
end
self:SelectWeapon(wep) -- Change the weapon back to the original weapon
wep:Equip(self)
end
if self.WeaponInventory_Melee == true then
local melee = VJ_PICK(self.WeaponInventory_MeleeList)
if melee != false && wep:GetClass() != melee then -- If the list isn't empty and it's not the current active weapon
self.WeaponInventory.Melee = self:Give(melee)
end
self:SelectWeapon(wep) -- Change the weapon back to the original weapon
wep:Equip(self)
end
elseif IsValid(self:GetCreator()) && self.AllowPrintingInChat == true && self.Weapon_NoSpawnMenu == false then
self:GetCreator():PrintMessage(HUD_PRINTTALK, "WARNING: "..self:GetName().." needs a weapon!")
end
end
end)
end
//self:SetSaveValue("m_debugOverlays", 1) -- Enables source engine debug overlays (some commands like 'npc_conditions' need it)
end
-- !!!!!!!!!!!!!! DO NOT USE THESE !!!!!!!!!!!!!! [Backwards Compatibility!]
ENT.MeleeAttacking = false
ENT.ThrowingGrenade = false
function ENT:CustomInitialize() end
function ENT:SetNearestPointToEntityPosition() return self:GetDynamicOrigin() end
function ENT:SetMeleeAttackDamagePosition() return self:GetMeleeAttackDamageOrigin() end
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:SetInitializeCapabilities()
self:CapabilitiesAdd(bit.bor(CAP_SKIP_NAV_GROUND_CHECK))
self:CapabilitiesAdd(bit.bor(CAP_ANIMATEDFACE))
self:CapabilitiesAdd(bit.bor(CAP_TURN_HEAD))
if self.CanOpenDoors == true then
self:CapabilitiesAdd(bit.bor(CAP_OPEN_DOORS))
self:CapabilitiesAdd(bit.bor(CAP_AUTO_DOORS))
self:CapabilitiesAdd(bit.bor(CAP_USE))
end
self:CapabilitiesAdd(bit.bor(CAP_DUCK))
//if self.HasSquad == true then self:CapabilitiesAdd(bit.bor(CAP_SQUAD)) end
if self.DisableWeapons == false && self.Weapon_NoSpawnMenu == false then
self:CapabilitiesAdd(bit.bor(CAP_USE_WEAPONS))
self:CapabilitiesAdd(bit.bor(CAP_WEAPON_RANGE_ATTACK1))
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:DoChangeMovementType(movType)
movType = movType or -1
if movType != -1 then self.MovementType = movType end
if self.MovementType == VJ_MOVETYPE_GROUND then
self:RemoveFlags(FL_FLY)
self:SetNavType(NAV_GROUND)
self:SetMoveType(MOVETYPE_STEP)
self:CapabilitiesRemove(CAP_MOVE_FLY)
self:CapabilitiesAdd(bit.bor(CAP_MOVE_GROUND))
if (VJ_AnimationExists(self,ACT_JUMP) == true && GetConVar("vj_npc_human_canjump"):GetInt() == 1) or self.UsePlayerModelMovement == true then self:CapabilitiesAdd(bit.bor(CAP_MOVE_JUMP)) end
//if VJ_AnimationExists(self,ACT_CLIMB_UP) == true then self:CapabilitiesAdd(bit.bor(CAP_MOVE_CLIMB)) end
if self.DisableWeapons == false then self:CapabilitiesAdd(bit.bor(CAP_MOVE_SHOOT)) end
elseif self.MovementType == VJ_MOVETYPE_AERIAL or self.MovementType == VJ_MOVETYPE_AQUATIC then
self:SetGroundEntity(NULL)
self:AddFlags(FL_FLY)
self:SetNavType(NAV_FLY)
self:SetMoveType(MOVETYPE_STEP) // MOVETYPE_FLY, causes issues like Lerp functions not being smooth
self:CapabilitiesRemove(CAP_MOVE_GROUND)
self:CapabilitiesRemove(CAP_MOVE_JUMP)
self:CapabilitiesRemove(CAP_MOVE_CLIMB)
self:CapabilitiesRemove(CAP_MOVE_SHOOT)
self:CapabilitiesAdd(bit.bor(CAP_MOVE_FLY))
elseif self.MovementType == VJ_MOVETYPE_STATIONARY then
self:RemoveFlags(FL_FLY)
self:SetNavType(NAV_NONE)
if self.Stationary_UseNoneMoveType == true then
self:SetMoveType(MOVETYPE_NONE)
else
self:SetMoveType(MOVETYPE_FLY)
end
self:CapabilitiesRemove(CAP_MOVE_GROUND)
self:CapabilitiesRemove(CAP_MOVE_JUMP)
self:CapabilitiesRemove(CAP_MOVE_CLIMB)
self:CapabilitiesRemove(CAP_MOVE_SHOOT)
self:CapabilitiesRemove(CAP_MOVE_FLY)
elseif self.MovementType == VJ_MOVETYPE_PHYSICS then
self:RemoveFlags(FL_FLY)
self:SetNavType(NAV_NONE)
self:SetMoveType(MOVETYPE_VPHYSICS)
self:CapabilitiesRemove(CAP_MOVE_GROUND)
self:CapabilitiesRemove(CAP_MOVE_JUMP)
self:CapabilitiesRemove(CAP_MOVE_CLIMB)
self:CapabilitiesRemove(CAP_MOVE_SHOOT)
self:CapabilitiesRemove(CAP_MOVE_FLY)
end
self:CustomOnChangeMovementType(movType)
end
---------------------------------------------------------------------------------------------------------------------------------------------
--[[---------------------------------------------------------
The main animation function, it can play activities, sequences and gestures
- animation = The animation to play, it can be a table OR string OR ACT_*
- Adding "vjseq_" to a string will make it play as a sequence
- Adding "vjges_" to a string will make it play as a gesture
- If it's a string AND "vjseq_" or "vjges_" is NOT added:
- The base will attempt to convert it activity, if it fails, it will play it as a sequence
- This behavior can be overridden by AlwaysUseSequence & AlwaysUseGesture options
- stopActivities = If true, it will stop activities such as idle, chasing, attacking, etc. for a given amount of time | DEFAULT: false
- stopActivitiesTime = How long it will stop the activities such as idle, chasing, attacking, etc. | DEFAULT: 0
- false = Base calculates the time (recommended)
- faceEnemy = Should it constantly face the enemy while playing this animation? | DEFAULT: false
- animDelay = Delays the animation by the given number | DEFAULT: 0
- extraOptions = Table that holds extra options to modify parts of the code
- OnFinish(interrupted, anim) = A function that runs when the animation finishes | DEFAULT: nil
- interrupted = Was the animation cut off? Basically something else stopped it before the animation fully completed
- anim = The animation it played, it can be a string or an activity enumeration
- AlwaysUseSequence = The base will force attempt to play this animation as a sequence regardless of the other options | DEFAULT: false
- AlwaysUseGesture = The base will force attempt to play this animation as a gesture regardless of the other options | DEFAULT: false
- SequenceInterruptible = Can this sequence be interrupted? | DEFAULT: false
- SequenceDuration = How long is the sequence? | DEFAULT: Base decides
- WARNING: Recommended to not change this option, it's mostly used internally by the base!
- PlayBackRate = How fast should the animation play? | DEFAULT: self.AnimationPlaybackRate
- PlayBackRateCalculated = If the playback rate is already calculated in the stopActivitiesTime, then set this to true! | DEFAULT: false
- customFunc() = TODO: NOT FINISHED
Returns
- Number, Accurate animation play time after taking everything in account
- WARNING: If "animDelay" parameter is used, result may be inaccurate!
-----------------------------------------------------------]]
local varGes = "vjges_"
local varSeq = "vjseq_"
--
function ENT:VJ_ACT_PLAYACTIVITY(animation, stopActivities, stopActivitiesTime, faceEnemy, animDelay, extraOptions, customFunc)
animation = VJ_PICK(animation)
if animation == false then return 0 end
stopActivities = stopActivities or false
if stopActivitiesTime == nil then -- If user didn't put anything, then default it to 0
stopActivitiesTime = 0 -- Set this value to false to let the base calculate the time
end
faceEnemy = faceEnemy or false -- Should it face the enemy while playing this animation?
animDelay = tonumber(animDelay) or 0 -- How much time until it starts playing the animation (seconds)
extraOptions = extraOptions or {}
local finalPlayBackRate = extraOptions.PlayBackRate or self.AnimationPlaybackRate -- How fast should the animation play?
local isGesture = false
local isSequence = false
local isString = isstring(animation)
-- Handle "vjges_" and "vjseq_"
if isString then
local strExpGes = string.Explode(varGes, animation)
-- Gestures
if strExpGes[2] then -- If 2 exists, then vjges_ was found!
isGesture = true
animation = strExpGes[2]
-- If current name is -1 then it's probably han activity, so turn it into an activity | EX: "vjges_"..ACT_MELEE_ATTACK1
if self:LookupSequence(animation) == -1 then
animation = tonumber(animation)
isString = false
end
else -- Sequences
local strExpSeq = string.Explode(varSeq, animation)
if strExpSeq[2] then -- If 2 exists, then vjseq_ was found!
isSequence = true
animation = strExpSeq[2]
end
end
end
if extraOptions.AlwaysUseGesture == true then isGesture = true end -- Must play a gesture
if extraOptions.AlwaysUseSequence == true then -- Must play a sequence
isGesture = false
isSequence = true
if isnumber(animation) then -- If it's an activity, then convert it to a string
animation = self:GetSequenceName(self:SelectWeightedSequence(animation))
end
elseif isString && !isSequence then -- Only for regular & gesture strings
-- If it can be played as an activity, then convert it!
local result = self:GetSequenceActivity(self:LookupSequence(animation))
if result == nil or result == -1 then -- Leave it as string
isSequence = true
else -- Set it as an activity
animation = result
end
end
-- If the given animation doesn't exist, then check to see if it does in the weapon translation list
if VJ_AnimationExists(self, animation) == false then
if !isString && IsValid(self:GetActiveWeapon()) then -- If it's an activity and has a valid weapon then check for weapon translation
-- If it returns the same activity as animation, then there isn't even a translation for it so don't play any animation =(
if self:GetActiveWeapon().IsVJBaseWeapon && self:TranslateToWeaponAnim(animation) == animation then return 0 end
else
return 0 -- No animation =(
end
end
-- Seed the current animation, used for animation delaying & on complete check
local seed = CurTime(); self.CurAnimationSeed = seed
local function PlayAct()
local animTime = self:DecideAnimationLength(animation, false)
if stopActivities == true then
if stopActivitiesTime == false then -- false = Let the base calculate the time
stopActivitiesTime = animTime
elseif !extraOptions.PlayBackRateCalculated then -- Make sure not to calculate the playback rate when it already has!
stopActivitiesTime = stopActivitiesTime / self:GetPlaybackRate()
animTime = stopActivitiesTime
end
self:StopAttacks(true)
self.vACT_StopAttacks = true
self.NextChaseTime = CurTime() + stopActivitiesTime
self.NextIdleTime = CurTime() + stopActivitiesTime
-- If there is already a timer, then adjust it instead of creating a new one
if !timer.Adjust("timer_act_stopattacks"..self:EntIndex(), stopActivitiesTime, 1, function() self.vACT_StopAttacks = false end) then
timer.Create("timer_act_stopattacks"..self:EntIndex(), stopActivitiesTime, 1, function() self.vACT_StopAttacks = false end)
end
end
self.CurAnimationSeed = seed -- We need to set it again because self:StopAttacks() above will reset it when it calls to chase enemy!
local vsched = ai_vj_schedule.New("vj_act_"..animation)
if (customFunc) then customFunc(vsched, animation) end
self.NextIdleStandTime = 0
self.VJ_PlayingSequence = false
self.VJ_PlayingInterruptSequence = false
self.AnimationPlaybackRate = finalPlayBackRate
self:SetPlaybackRate(finalPlayBackRate)
if isGesture == true then
local gesture = false
if isstring(animation) then
gesture = self:AddGestureSequence(self:LookupSequence(animation))
else
gesture = self:AddGesture(animation)
end
if gesture != false then
//self:ClearSchedule()
//self:SetLayerBlendIn(1, 0)
//self:SetLayerBlendOut(1, 0)
self:SetLayerPriority(gesture, 1) // 2
//self:SetLayerWeight(gesture, 1)
self:SetLayerPlaybackRate(gesture, finalPlayBackRate * 0.5)
//self:SetLayerDuration(gesture, 3)
//print(self:GetLayerDuration(gesture))
end
elseif isSequence == true then
local dur = (extraOptions.SequenceDuration or self:SequenceDuration(self:LookupSequence(animation))) / self.AnimationPlaybackRate
if faceEnemy == true then
self:FaceCertainEntity(self:GetEnemy(), true, dur)
end
self:VJ_PlaySequence(animation, finalPlayBackRate, extraOptions.SequenceDuration != false, dur, extraOptions.SequenceInterruptible or false)
end
if isGesture == false then -- If it's sequence or activity
-- For humans, internally the base will set these variables back to true after this function if it's called by weapon attack animations!
self.DoingWeaponAttack = false
self.DoingWeaponAttack_Standing = false
//self:StartEngineTask(GetTaskList("TASK_RESET_ACTIVITY"), 0) //vsched:EngTask("TASK_RESET_ACTIVITY", 0)
//if self.Dead then vsched:EngTask("TASK_STOP_MOVING", 0) end
//self:FrameAdvance(0)
self:TaskComplete()
self:StopMoving()
self:ClearSchedule()
self:ClearGoal()
if isSequence == false then -- Only if activity
//self:SetActivity(ACT_RESET)
self.VJ_PlayingSequence = false
if self.MovementType == VJ_MOVETYPE_AERIAL or self.MovementType == VJ_MOVETYPE_AQUATIC then
self:ResetIdealActivity(animation)
//vsched:EngTask("TASK_SET_ACTIVITY", animation) -- To avoid AutoMovement stopping the velocity
//elseif faceEnemy == true then
//vsched:EngTask("TASK_PLAY_SEQUENCE_FACE_ENEMY", animation)
else
if faceEnemy == true then
self:FaceCertainEntity(self:GetEnemy(), true, animTime)
end
-- This fixes: Animation NOT applying walk frames if the previous animation was the same
if self:GetActivity() == animation then
self:ResetSequenceInfo()
self:SetSaveValue("sequence", 0)
end
vsched:EngTask("TASK_PLAY_SEQUENCE", animation)
//self:ResetIdealActivity(animation)
//self:AutoMovement(self:GetAnimTimeInterval())
end
end
vsched.IsPlayActivity = true
self:StartSchedule(vsched)
end
-- If it has a OnFinish function, then set the timer to run it when it finishes!
if (extraOptions.OnFinish) then
timer.Simple(animTime, function()
if IsValid(self) && !self.Dead then
extraOptions.OnFinish(self.CurAnimationSeed != seed, animation)
end
end)
end
return animTime
end
-- For delay system
if animDelay > 0 then
timer.Simple(animDelay, function()
if IsValid(self) && self.CurAnimationSeed == seed then
PlayAct()
end
end)
return animDelay + self:DecideAnimationLength(animation, false) -- Approximation, this may be inaccurate!
else
return PlayAct()
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
local task_chaseEnemyLOS = ai_vj_schedule.New("vj_chase_enemy_los")
task_chaseEnemyLOS:EngTask("TASK_GET_PATH_TO_ENEMY_LOS", 0)
//task_chaseEnemyLOS:EngTask("TASK_RUN_PATH", 0)
task_chaseEnemyLOS:EngTask("TASK_WAIT_FOR_MOVEMENT", 0)
//task_chaseEnemyLOS:EngTask("TASK_FACE_ENEMY", 0)
//task_chaseEnemyLOS.ResetOnFail = true
task_chaseEnemyLOS.CanShootWhenMoving = true
task_chaseEnemyLOS.CanBeInterrupted = true
task_chaseEnemyLOS.IsMovingTask = true
task_chaseEnemyLOS.MoveType = 1
--
local task_chaseEnemy = ai_vj_schedule.New("vj_chase_enemy")
task_chaseEnemy:EngTask("TASK_GET_PATH_TO_ENEMY", 0)
//task_chaseEnemy:EngTask("TASK_RUN_PATH", 0)
task_chaseEnemy:EngTask("TASK_WAIT_FOR_MOVEMENT", 0)
//task_chaseEnemy:EngTask("TASK_FACE_ENEMY", 0)
//task_chaseEnemy.ResetOnFail = true
task_chaseEnemy.CanShootWhenMoving = true
//task_chaseEnemy.StopScheduleIfNotMoving = true
task_chaseEnemy.CanBeInterrupted = true
task_chaseEnemy.IsMovingTask = true
task_chaseEnemy.MoveType = 1
--
local varChaseEnemy = "vj_chase_enemy"
function ENT:VJ_TASK_CHASE_ENEMY(doLOSChase)
doLOSChase = doLOSChase or false
self:ClearCondition(COND_ENEMY_UNREACHABLE)
if self.MovementType == VJ_MOVETYPE_AERIAL or self.MovementType == VJ_MOVETYPE_AQUATIC then self:AA_ChaseEnemy() return end
//if self.CurrentSchedule != nil && self.CurrentSchedule.Name == "vj_chase_enemy" then return end
if self:GetNavType() == NAV_JUMP or self:GetNavType() == NAV_CLIMB then return end
//if (CurTime() <= self.JumpLegalLandingTime && (self:GetActivity() == ACT_JUMP or self:GetActivity() == ACT_GLIDE or self:GetActivity() == ACT_LAND)) or self:GetActivity() == ACT_CLIMB_UP or self:GetActivity() == ACT_CLIMB_DOWN or self:GetActivity() == ACT_CLIMB_DISMOUNT then return end
if (self:GetEnemyLastKnownPos():Distance(self:GetEnemy():GetPos()) <= 12) && self.CurrentSchedule != nil && self.CurrentSchedule.Name == varChaseEnemy then return end
self:SetMovementActivity(VJ_PICK(self.AnimTbl_Run))
if doLOSChase == true then
task_chaseEnemyLOS.RunCode_OnFinish = function()
local ene = self:GetEnemy()
if IsValid(ene) then
self:RememberUnreachable(ene, 0)
self:VJ_TASK_CHASE_ENEMY(false)
end
end
self:StartSchedule(task_chaseEnemyLOS)
else
self:StartSchedule(task_chaseEnemy)
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
local table_remove = table.remove
--
function ENT:VJ_TASK_IDLE_STAND()
if self:IsMoving() or (self.NextIdleTime > CurTime()) or (self.AA_CurrentMoveTime > CurTime()) or self:GetNavType() == NAV_JUMP or self:GetNavType() == NAV_CLIMB then return end // self.CurrentSchedule != nil
if (self.MovementType == VJ_MOVETYPE_AERIAL or self.MovementType == VJ_MOVETYPE_AQUATIC) && self:BusyWithActivity() then return end
//if (self.CurrentSchedule != nil && self.CurrentSchedule.Name == "vj_idle_stand") or (self.CurrentAnim_CustomIdle != 0 && VJ_IsCurrentAnimation(self,self.CurrentAnim_CustomIdle) == true) then return end
//if (self.MovementType == VJ_MOVETYPE_AERIAL or self.MovementType == VJ_MOVETYPE_AQUATIC) && self:GetVelocity():Length() > 0 then return end
//if self.MovementType == VJ_MOVETYPE_AERIAL or self.MovementType == VJ_MOVETYPE_AQUATIC then self:AA_StopMoving() return end
/*local vschedIdleStand = ai_vj_schedule.New("vj_idle_stand")
//vschedIdleStand:EngTask("TASK_FACE_REASONABLE")
vschedIdleStand:EngTask("TASK_STOP_MOVING")
vschedIdleStand:EngTask("TASK_WAIT_INDEFINITE")
vschedIdleStand.CanBeInterrupted = true
self:StartSchedule(vschedIdleStand)*/
local idleAnimTbl = self.NoWeapon_UseScaredBehavior_Active == true and self.AnimTbl_ScaredBehaviorStand or ((self.Alerted && self:GetWeaponState() != VJ_WEP_STATE_HOLSTERED && IsValid(self:GetActiveWeapon())) and self.AnimTbl_WeaponAim or self.AnimTbl_IdleStand)
local sameAnimFound = false -- If true then it one of the animations in the table is the same as the current!
//local numOfAnims = 0 -- Number of valid animations found
for k, v in ipairs(idleAnimTbl) do
v = VJ_SequenceToActivity(self, v) -- Translate any sequence to activity
if v != false then -- Its a valid activity
//numOfAnims = numOfAnims + 1
idleAnimTbl[k] = v -- In case it was a sequence, override it with the translated activity number
-- Check if its the current idle animation...
if sameAnimFound == false && self.CurrentAnim_IdleStand == v then
sameAnimFound = true
//break
end
else -- Get rid of any animations that aren't valid!
table_remove(idleAnimTbl, k)
end
end
//PrintTable(idleAnimTbl)
-- If there is more than 1 animation in the table AND one of the animations is the current animation AND time hasn't expired, then return!
/*if #idleAnimTbl > 1 && sameAnimFound == true && self.NextIdleStandTime > CurTime() then
return
end*/
local pickedAnim = VJ_PICK(idleAnimTbl)
-- If no animation was found, then use ACT_IDLE
if pickedAnim == false then
pickedAnim = ACT_IDLE
//sameAnimFound = true
end
-- If sequence and it has no activity, then don't continue!
//pickedAnim = VJ_SequenceToActivity(self,pickedAnim)
//if pickedAnim == false then return false end
if (!sameAnimFound /*or (sameAnimFound && numOfAnims == 1 && CurTime() > self.NextIdleStandTime)*/) or (CurTime() > self.NextIdleStandTime) then
self.CurrentAnim_IdleStand = pickedAnim
self.CurIdleStandMove = false
-- Old system
/*if (self.MovementType == VJ_MOVETYPE_AERIAL or self.MovementType == VJ_MOVETYPE_AQUATIC) then
if self:BusyWithActivity() == true then return end // self:GetSequence() == 0
self:AA_StopMoving()
self:VJ_ACT_PLAYACTIVITY(pickedAnim, false, 0, false, 0, {SequenceDuration=false, SequenceInterruptible=true}) // AlwaysUseSequence=true
end
if self.CurrentSchedule == nil then -- If it's not doing a schedule then reset the activity to make sure it's not already playing the same idle activity!
self:StartEngineTask(GetTaskList("TASK_RESET_ACTIVITY"), 0)
//self:SetIdealActivity(ACT_RESET)
end*/
//self:StartEngineTask(GetTaskList("TASK_PLAY_SEQUENCE"),pickedAnim)
if (self.MovementType == VJ_MOVETYPE_AERIAL or self.MovementType == VJ_MOVETYPE_AQUATIC) then self:AA_StopMoving() end
self.CurAnimationSeed = 0
self.VJ_PlayingSequence = false
self.VJ_PlayingInterruptSequence = false
self:ResetIdealActivity(pickedAnim)
timer.Simple(0.01, function() -- So we can make sure the engine has enough time to set the animation
if IsValid(self) && self.NextIdleStandTime != 0 then
local getSeq = self:GetSequence()
self.CurIdleStandMove = self:GetSequenceMoveDist(getSeq) > 0
local seqToAct = VJ_SequenceToActivity(self,self:GetSequenceName(getSeq))
if seqToAct == pickedAnim or (IsValid(self:GetActiveWeapon()) && seqToAct == self:TranslateToWeaponAnim(pickedAnim)) then -- Nayir yete himagva animation e nooynene
self.NextIdleStandTime = CurTime() + ((self:SequenceDuration(getSeq) - 0.15) / self:GetPlaybackRate()) -- Yete nooynene ooremen jamanage tir animation-en yergarootyan chap!
end
end
end)
self.NextIdleStandTime = CurTime() + 0.15 -- This is temp, timer above overrides it
elseif self.CurIdleStandMove && !self:IsSequenceFinished() then
self:AutoMovement(self:GetAnimTimeInterval())
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:DoIdleAnimation(idleType)
if self:GetState() == VJ_STATE_ONLY_ANIMATION_CONSTANT or self.Dead or self.VJ_IsBeingControlled or self.PlayingAttackAnimation == true or (self.NextIdleTime > CurTime()) or (self.AA_CurrentMoveTime > CurTime()) or (self.CurrentSchedule != nil && self.CurrentSchedule.Name == "vj_act_resetenemy") then return end
idleType = idleType or 0 -- 0 = Random | 1 = Wander | 2 = Idle Stand
if self.IdleAlwaysWander == true then idleType = 1 end
-- Things that override can't bypass, Forces the NPC to ONLY idle stand!
if self.DisableWandering == true or self.IsGuard == true or self.MovementType == VJ_MOVETYPE_STATIONARY or self.IsVJBaseSNPC_Tank == true or self.LastHiddenZone_CanWander == false or self.NextWanderTime > CurTime() or self.IsFollowing == true or self.Medic_Status then
idleType = 2
end
if idleType == 0 then -- Random (Wander & Idle Stand)
if math.random(1, 3) == 1 then
self:VJ_TASK_IDLE_WANDER() else self:VJ_TASK_IDLE_STAND()
end
elseif idleType == 1 then -- Wander
self:VJ_TASK_IDLE_WANDER()
elseif idleType == 2 then -- Idle Stand
self:VJ_TASK_IDLE_STAND()
return -- Don't set self.NextWanderTime below
end
self.NextWanderTime = CurTime() + math.Rand(3, 6) // self.NextIdleTime
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:DoChaseAnimation(alwaysChase)
local ene = self:GetEnemy()
if self:GetState() == VJ_STATE_ONLY_ANIMATION_CONSTANT or self.Dead or self.VJ_IsBeingControlled or self.PlayingAttackAnimation == true or self.Flinching == true or self.IsVJBaseSNPC_Tank == true or !IsValid(ene) or (self.NextChaseTime > CurTime()) or (CurTime() < self.TakingCoverT) or (self.PlayingAttackAnimation == true && self.MovementType != VJ_MOVETYPE_AERIAL && self.MovementType != VJ_MOVETYPE_AQUATIC) then return end
if self:VJ_GetNearestPointToEntityDistance(ene) < self.MeleeAttackDistance && self.EnemyData.IsVisible && (self.EnemyData.SightDiff > math_cos(math_rad(self.MeleeAttackAngleRadius))) then self:VJ_TASK_IDLE_STAND() return end -- Not melee attacking yet but it is in range, so stop moving!
alwaysChase = alwaysChase or false -- true = Chase no matter what
-- Things that override can't bypass, Forces the NPC to ONLY idle stand!
if self.MovementType == VJ_MOVETYPE_STATIONARY or self.IsFollowing == true or self.Medic_Status or self:GetState() == VJ_STATE_ONLY_ANIMATION then
self:VJ_TASK_IDLE_STAND()
return
end
-- For non-aggressive SNPCs
if self.Behavior == VJ_BEHAVIOR_PASSIVE or self.Behavior == VJ_BEHAVIOR_PASSIVE_NATURE then
self:VJ_TASK_COVER_FROM_ENEMY("TASK_RUN_PATH")
self.NextChaseTime = CurTime() + 3
return
end
if alwaysChase == false && (self.DisableChasingEnemy == true or self.IsGuard == true) then self:VJ_TASK_IDLE_STAND() return end
-- If the enemy is not reachable
if (self:HasCondition(COND_ENEMY_UNREACHABLE) or self:IsUnreachable(ene)) && (IsValid(self:GetActiveWeapon()) == true && (!self:GetActiveWeapon().IsMeleeWeapon)) then
self:VJ_TASK_CHASE_ENEMY(true)
self:RememberUnreachable(ene, 2)
else -- Is reachable, so chase the enemy!
self:VJ_TASK_CHASE_ENEMY(false)
end
-- Set the next chase time
if self.NextChaseTime > CurTime() then return end -- Don't set it if it's already set!
self.NextChaseTime = CurTime() + (((self.LatestEnemyDistance > 2000) and 1) or 0.1) -- If the enemy is far, increase the delay!
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:SetupWeaponHoldTypeAnims(hType)
-- Decide what type of animation set it uses
if VJ_AnimationExists(self, "signal_takecover") == true && VJ_AnimationExists(self, "grenthrow") == true && VJ_AnimationExists(self, "bugbait_hit") == true then
self.ModelAnimationSet = VJ_MODEL_ANIMSET_COMBINE -- Combine
elseif VJ_AnimationExists(self, ACT_WALK_AIM_PISTOL) == true && VJ_AnimationExists(self, ACT_RUN_AIM_PISTOL) == true && VJ_AnimationExists(self, ACT_POLICE_HARASS1) == true then
self.ModelAnimationSet = VJ_MODEL_ANIMSET_METROCOP -- Metrocop
elseif VJ_AnimationExists(self, "coverlow_r") == true && VJ_AnimationExists(self, "wave_smg1") == true && VJ_AnimationExists(self, ACT_BUSY_SIT_GROUND) == true then
self.ModelAnimationSet = VJ_MODEL_ANIMSET_REBEL -- Rebel
elseif VJ_AnimationExists(self, "gmod_breath_layer") == true then
self.ModelAnimationSet = VJ_MODEL_ANIMSET_PLAYER -- Player
end
self.WeaponAnimTranslations = {}
self.NextIdleStandTime = 0
if self:CustomOnSetupWeaponHoldTypeAnims(hType) == true then return end
if self.ModelAnimationSet == VJ_MODEL_ANIMSET_COMBINE then -- Combine =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
-- Use rifle animations with minor edits if it's holding a handgun
local rifle_idle = ACT_IDLE_SMG1
local rifle_walk = VJ_PICK({ACT_WALK_RIFLE, VJ_SequenceToActivity(self, "walkeasy_all")})
if hType == "pistol" or hType == "revolver" or hType == "melee" or hType == "melee2" or hType == "knife" then
rifle_idle = VJ_SequenceToActivity(self, "idle_unarmed")
rifle_walk = VJ_SequenceToActivity(self, "walkunarmed_all")
end
-- "Leanwall_CrouchLeft_A_idle", "Leanwall_CrouchLeft_B_idle", "Leanwall_CrouchLeft_C_idle", "Leanwall_CrouchLeft_D_idle"
self.WeaponAnimTranslations[ACT_COVER_LOW] = {ACT_COVER, "vjseq_Leanwall_CrouchLeft_A_idle", "vjseq_Leanwall_CrouchLeft_B_idle", "vjseq_Leanwall_CrouchLeft_C_idle", "vjseq_Leanwall_CrouchLeft_D_idle"}
if hType == "ar2" or hType == "smg" or hType == "rpg" or hType == "pistol" or hType == "revolver" or hType == "melee" or hType == "melee2" or hType == "knife" then
if hType == "ar2" or hType == "pistol" or hType == "revolver" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_RANGE_ATTACK_AR2
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_GESTURE_RANGE_ATTACK_AR2
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_RANGE_ATTACK_AR2_LOW
//self.WeaponAnimTranslations[ACT_RELOAD] = ACT_RELOAD_SMG1 -- No need to translate
//self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_IDLE_ANGRY -- No need to translate, it's already the correct animation
elseif hType == "smg" or hType == "rpg" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_RANGE_ATTACK_SMG1
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_GESTURE_RANGE_ATTACK_SMG1
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_RANGE_ATTACK_SMG1_LOW
//self.WeaponAnimTranslations[ACT_RELOAD] = ACT_RELOAD_SMG1 -- No need to translate
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_IDLE_ANGRY_SMG1
elseif hType == "melee" or hType == "melee2" or hType == "knife" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_MELEE_ATTACK1
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = false -- Don't play anything!
//self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_RANGE_ATTACK_SMG1_LOW -- Not used for melee
//self.WeaponAnimTranslations[ACT_RELOAD] = ACT_RELOAD_SMG1 -- Not used for melee
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = rifle_idle
end
//self.WeaponAnimTranslations[ACT_RELOAD_LOW] = ACT_RELOAD_SMG1_LOW -- No need to translate
self.WeaponAnimTranslations[ACT_IDLE] = rifle_idle
self.WeaponAnimTranslations[ACT_WALK] = rifle_walk
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_WALK_AIM_RIFLE
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_WALK_CROUCH_RIFLE
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_WALK_CROUCH_AIM_RIFLE
self.WeaponAnimTranslations[ACT_RUN] = ACT_RUN_RIFLE
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_RUN_AIM_RIFLE
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_RUN_CROUCH_RIFLE
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_RUN_CROUCH_AIM_RIFLE
elseif hType == "crossbow" or hType == "shotgun" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_RANGE_ATTACK_SHOTGUN
if hType == "crossbow" then
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_GESTURE_RANGE_ATTACK_AR2
else
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_GESTURE_RANGE_ATTACK_SHOTGUN
end
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_RANGE_ATTACK_SHOTGUN_LOW
//self.WeaponAnimTranslations[ACT_RELOAD] = ACT_RELOAD_SHOTGUN -- No need to translate
//self.WeaponAnimTranslations[ACT_RELOAD_LOW] = ACT_RELOAD_SMG1_LOW -- No need to translate
self.WeaponAnimTranslations[ACT_IDLE] = ACT_SHOTGUN_IDLE4
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_IDLE_ANGRY_SHOTGUN
self.WeaponAnimTranslations[ACT_WALK] = ACT_WALK_AIM_SHOTGUN
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_WALK_AIM_SHOTGUN
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_WALK_CROUCH_RIFLE
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_WALK_CROUCH_AIM_RIFLE
self.WeaponAnimTranslations[ACT_RUN] = ACT_RUN_RIFLE
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_RUN_AIM_SHOTGUN
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_RUN_CROUCH_RIFLE
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_RUN_CROUCH_AIM_RIFLE
end
elseif self.ModelAnimationSet == VJ_MODEL_ANIMSET_METROCOP then -- Metrocop =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
-- Do not translate crouch walking and also make the crouch running a walking one instead
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_WALK_CROUCH
if hType == "smg" or hType == "rpg" or hType == "ar2" or hType == "crossbow" or hType == "shotgun" then
-- Note: Metrocops must use smg animation, they don't have any animations for AR2!
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_RANGE_ATTACK_SMG1
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_GESTURE_RANGE_ATTACK_SMG1
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_RANGE_ATTACK_SMG1_LOW
self.WeaponAnimTranslations[ACT_RELOAD] = ACT_RELOAD_SMG1
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_COVER_SMG1_LOW
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = ACT_RELOAD_SMG1_LOW
self.WeaponAnimTranslations[ACT_IDLE] = ACT_IDLE_SMG1
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_IDLE_ANGRY_SMG1
self.WeaponAnimTranslations[ACT_WALK] = ACT_WALK_RIFLE
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_WALK_AIM_RIFLE
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_WALK_CROUCH_AIM_RIFLE
self.WeaponAnimTranslations[ACT_RUN] = ACT_RUN_RIFLE
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_RUN_AIM_RIFLE
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_RUN_CROUCH_AIM_RIFLE
elseif hType == "pistol" or hType == "revolver" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_RANGE_ATTACK_PISTOL
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_GESTURE_RANGE_ATTACK_PISTOL
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_RANGE_ATTACK_PISTOL_LOW
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_COVER_PISTOL_LOW
self.WeaponAnimTranslations[ACT_RELOAD] = ACT_RELOAD_PISTOL
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = ACT_RELOAD_PISTOL_LOW
self.WeaponAnimTranslations[ACT_IDLE] = ACT_IDLE_PISTOL
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_IDLE_ANGRY_PISTOL
self.WeaponAnimTranslations[ACT_WALK] = VJ_PICK({ACT_WALK, ACT_WALK_PISTOL})
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_WALK_AIM_PISTOL
//self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_WALK_CROUCH_RIFLE -- No need to translate
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_WALK_CROUCH_AIM_RIFLE
self.WeaponAnimTranslations[ACT_RUN] = VJ_PICK({ACT_RUN, ACT_RUN_PISTOL})
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_RUN_AIM_PISTOL
//self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_RUN_CROUCH_RIFLE -- No need to translate
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_RUN_CROUCH_AIM_RIFLE
elseif hType == "melee" or hType == "melee2" or hType == "knife" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_MELEE_ATTACK_SWING
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = false //ACT_MELEE_ATTACK_SWING_GESTURE -- Don't play anything!
//self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_RANGE_ATTACK_SMG1_LOW -- Not used for melee
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_COWER
//self.WeaponAnimTranslations[ACT_RELOAD] = ACT_RELOAD_SMG1 -- Not used for melee
//self.WeaponAnimTranslations[ACT_RELOAD_LOW] = ACT_RELOAD_SMG1_LOW -- Not used for melee
self.WeaponAnimTranslations[ACT_IDLE] = {ACT_IDLE, ACT_IDLE, VJ_SequenceToActivity(self, "plazathreat1")}
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_IDLE_ANGRY_MELEE
self.WeaponAnimTranslations[ACT_WALK] = VJ_PICK({ACT_WALK, ACT_WALK_ANGRY})
//self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_WALK_AIM_RIFLE -- Not used for melee
//self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_WALK_CROUCH_RIFLE -- No need to translate
//self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_WALK_CROUCH_AIM_RIFLE -- Not used for melee
//self.WeaponAnimTranslations[ACT_RUN] = ACT_RUN -- No need to translate
//self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_RUN_AIM_RIFLE -- Not used for melee
//self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_RUN_CROUCH_RIFLE -- No need to translate
//self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_RUN_CROUCH_AIM_RIFLE -- Not used for melee
end
elseif self.ModelAnimationSet == VJ_MODEL_ANIMSET_REBEL then -- Rebel =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
local isFemale = VJ_AnimationExists(self, ACT_IDLE_ANGRY_PISTOL)
-- handguns use a different set!
self.WeaponAnimTranslations[ACT_COVER_LOW] = {ACT_COVER_LOW_RPG, ACT_COVER_LOW, "vjseq_coverlow_l", "vjseq_coverlow_r"}
if hType == "ar2" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_RANGE_ATTACK_AR2
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_GESTURE_RANGE_ATTACK_AR2
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_RANGE_ATTACK_AR2_LOW
self.WeaponAnimTranslations[ACT_RELOAD] = VJ_SequenceToActivity(self, "reload_ar2")
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = ACT_RELOAD_SMG1_LOW
self.WeaponAnimTranslations[ACT_IDLE] = VJ_PICK({VJ_SequenceToActivity(self, "idle_relaxed_ar2_1"), VJ_SequenceToActivity(self, "idle_alert_ar2_1"), VJ_SequenceToActivity(self, "idle_angry_ar2")})
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = VJ_SequenceToActivity(self, "idle_ar2_aim")
self.WeaponAnimTranslations[ACT_WALK] = VJ_PICK({VJ_SequenceToActivity(self, "walk_ar2_relaxed_all"), VJ_SequenceToActivity(self, "walkalerthold_ar2_all1"), VJ_SequenceToActivity(self, "walkholdall1_ar2")})
self.WeaponAnimTranslations[ACT_WALK_AIM] = VJ_PICK({VJ_SequenceToActivity(self, "walkaimall1_ar2"), VJ_SequenceToActivity(self, "walkalertaim_ar2_all1")})
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_WALK_CROUCH_RPG
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_WALK_CROUCH_AIM_RIFLE
self.WeaponAnimTranslations[ACT_RUN] = VJ_PICK({VJ_SequenceToActivity(self, "run_alert_holding_ar2_all"), VJ_SequenceToActivity(self, "run_ar2_relaxed_all"), VJ_SequenceToActivity(self, "run_holding_ar2_all")})
self.WeaponAnimTranslations[ACT_RUN_AIM] = VJ_PICK({ACT_RUN_AIM_RIFLE, VJ_SequenceToActivity(self, "run_alert_aiming_ar2_all")})
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_RUN_CROUCH_RPG
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_RUN_CROUCH_AIM_RIFLE
elseif hType == "smg" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_RANGE_ATTACK_SMG1
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_GESTURE_RANGE_ATTACK_SMG1
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_RANGE_ATTACK_SMG1_LOW
self.WeaponAnimTranslations[ACT_RELOAD] = ACT_RELOAD_SMG1
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = ACT_RELOAD_SMG1_LOW
self.WeaponAnimTranslations[ACT_IDLE] = VJ_PICK({ACT_IDLE_SMG1_RELAXED, ACT_IDLE_SMG1_STIMULATED, ACT_IDLE_SMG1, VJ_SequenceToActivity(self, "idle_smg1_relaxed")})
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_IDLE_ANGRY_SMG1
self.WeaponAnimTranslations[ACT_WALK] = VJ_PICK({ACT_WALK_RIFLE, ACT_WALK_RIFLE_RELAXED, ACT_WALK_RIFLE_STIMULATED})
self.WeaponAnimTranslations[ACT_WALK_AIM] = VJ_PICK({ACT_WALK_AIM_RIFLE, ACT_WALK_AIM_RIFLE_STIMULATED})
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_WALK_CROUCH_RIFLE
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_WALK_CROUCH_AIM_RIFLE
self.WeaponAnimTranslations[ACT_RUN] = VJ_PICK({ACT_RUN_RIFLE, ACT_RUN_RIFLE_STIMULATED, ACT_RUN_RIFLE_RELAXED})
self.WeaponAnimTranslations[ACT_RUN_AIM] = VJ_PICK({ACT_RUN_AIM_RIFLE, ACT_RUN_AIM_RIFLE_STIMULATED})
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_RUN_CROUCH_RIFLE
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_RUN_CROUCH_AIM_RIFLE
elseif hType == "crossbow" or hType == "shotgun" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_RANGE_ATTACK_SHOTGUN
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_GESTURE_RANGE_ATTACK_SHOTGUN
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_RANGE_ATTACK_SMG1_LOW
self.WeaponAnimTranslations[ACT_RELOAD] = ACT_RELOAD_SHOTGUN
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = ACT_RELOAD_SMG1_LOW //ACT_RELOAD_SHOTGUN_LOW
self.WeaponAnimTranslations[ACT_IDLE] = VJ_PICK({ACT_IDLE_SHOTGUN_RELAXED, ACT_IDLE_SHOTGUN_STIMULATED})
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = VJ_SequenceToActivity(self, "idle_ar2_aim")
self.WeaponAnimTranslations[ACT_WALK] = VJ_PICK({VJ_SequenceToActivity(self, "walk_ar2_relaxed_all"), VJ_SequenceToActivity(self, "walkalerthold_ar2_all1"), VJ_SequenceToActivity(self, "walkholdall1_ar2")})
self.WeaponAnimTranslations[ACT_WALK_AIM] = VJ_PICK({VJ_SequenceToActivity(self, "walkaimall1_ar2"), VJ_SequenceToActivity(self, "walkalertaim_ar2_all1")})
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_WALK_CROUCH_RPG
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_WALK_CROUCH_AIM_RIFLE
self.WeaponAnimTranslations[ACT_RUN] = VJ_PICK({VJ_SequenceToActivity(self, "run_alert_holding_ar2_all"), VJ_SequenceToActivity(self, "run_ar2_relaxed_all"), VJ_SequenceToActivity(self, "run_holding_ar2_all")})
self.WeaponAnimTranslations[ACT_RUN_AIM] = VJ_PICK({ACT_RUN_AIM_RIFLE, VJ_SequenceToActivity(self, "run_alert_aiming_ar2_all")})
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_RUN_CROUCH_RPG
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_RUN_CROUCH_AIM_RIFLE
elseif hType == "rpg" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_RANGE_ATTACK_RPG
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_GESTURE_RANGE_ATTACK_RPG
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_RANGE_ATTACK_SMG1_LOW
self.WeaponAnimTranslations[ACT_RELOAD] = ACT_RELOAD_SMG1
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = ACT_RELOAD_SMG1_LOW
self.WeaponAnimTranslations[ACT_IDLE] = VJ_PICK({ACT_IDLE_RPG, ACT_IDLE_RPG_RELAXED})
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_IDLE_ANGRY_RPG
self.WeaponAnimTranslations[ACT_WALK] = VJ_PICK({ACT_WALK_RPG, ACT_WALK_RPG_RELAXED})
self.WeaponAnimTranslations[ACT_WALK_AIM] = VJ_PICK({VJ_SequenceToActivity(self, "walkaimall1_ar2"), VJ_SequenceToActivity(self, "walkalertaim_ar2_all1")})
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_WALK_CROUCH_RPG
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_WALK_CROUCH_AIM_RIFLE
self.WeaponAnimTranslations[ACT_RUN] = VJ_PICK({ACT_RUN_RPG, ACT_RUN_RPG_RELAXED})
self.WeaponAnimTranslations[ACT_RUN_AIM] = VJ_PICK({ACT_RUN_AIM_RIFLE, VJ_SequenceToActivity(self, "run_alert_aiming_ar2_all")})
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_RUN_CROUCH_RPG
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_RUN_CROUCH_AIM_RIFLE
elseif hType == "pistol" or hType == "revolver" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_RANGE_ATTACK_PISTOL
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_GESTURE_RANGE_ATTACK_PISTOL
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_RANGE_ATTACK_PISTOL_LOW
self.WeaponAnimTranslations[ACT_COVER_LOW] = {"crouchidle_panicked4", "vjseq_crouchidlehide"}
self.WeaponAnimTranslations[ACT_RELOAD] = ACT_RELOAD_PISTOL
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = isFemale and ACT_RELOAD_SMG1_LOW or ACT_RELOAD_PISTOL_LOW -- Only males have covered pistol reload!
self.WeaponAnimTranslations[ACT_IDLE] = ACT_IDLE_PISTOL
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = isFemale and ACT_IDLE_ANGRY_PISTOL or VJ_SequenceToActivity(self, "idle_ar2_aim") -- Only females have angry pistol animation
self.WeaponAnimTranslations[ACT_WALK] = ACT_WALK_PISTOL
self.WeaponAnimTranslations[ACT_WALK_AIM] = VJ_PICK({VJ_SequenceToActivity(self, "walkaimall1_ar2"), VJ_SequenceToActivity(self, "walkalertaim_ar2_all1")})
//self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_WALK_CROUCH_RIFLE -- No need to translate
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_WALK_CROUCH_AIM_RIFLE
self.WeaponAnimTranslations[ACT_RUN] = ACT_RUN_PISTOL
self.WeaponAnimTranslations[ACT_RUN_AIM] = VJ_SequenceToActivity(self, "run_alert_aiming_ar2_all")
//self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_RUN_CROUCH_RIFLE -- No need to translate
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_RUN_CROUCH_AIM_RIFLE
elseif hType == "melee" or hType == "melee2" or hType == "knife" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_MELEE_ATTACK_SWING
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = false -- Don't play anything!
//self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_RANGE_ATTACK_SMG1_LOW -- Not used for melee
self.WeaponAnimTranslations[ACT_COVER_LOW] = {"crouchidle_panicked4", "vjseq_crouchidlehide"}
//self.WeaponAnimTranslations[ACT_RELOAD] = ACT_RELOAD_SMG1 -- Not used for melee
//self.WeaponAnimTranslations[ACT_RELOAD_LOW] = ACT_RELOAD_SMG1_LOW -- Not used for melee
self.WeaponAnimTranslations[ACT_IDLE] = ACT_IDLE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_IDLE_ANGRY_MELEE
//self.WeaponAnimTranslations[ACT_WALK] = ACT_WALK -- No need to translate
//self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_WALK_AIM_RIFLE -- Not used for melee
//self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_WALK_CROUCH_RIFLE -- No need to translate
//self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_WALK_CROUCH_AIM_RIFLE -- Not used for melee
self.WeaponAnimTranslations[ACT_RUN] = VJ_SequenceToActivity(self, "run_all_panicked")
//self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_RUN_AIM_RIFLE -- Not used for melee
//self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_RUN_CROUCH_RIFLE -- No need to translate
//self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_RUN_CROUCH_AIM_RIFLE -- Not used for melee
end
elseif self.ModelAnimationSet == VJ_MODEL_ANIMSET_PLAYER then -- Player =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
if hType == "ar2" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_HL2MP_IDLE_AR2
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_HL2MP_GESTURE_RANGE_ATTACK_AR2
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_HL2MP_IDLE_CROUCH_AR2
self.WeaponAnimTranslations[ACT_RELOAD] = "vjges_reload_ar2"
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = "vjges_reload_ar2"
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_HL2MP_IDLE_CROUCH_AR2
self.WeaponAnimTranslations[ACT_IDLE] = ACT_HL2MP_IDLE_PASSIVE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_HL2MP_IDLE_AR2
self.WeaponAnimTranslations[ACT_JUMP] = ACT_HL2MP_JUMP_AR2
self.WeaponAnimTranslations[ACT_GLIDE] = ACT_HL2MP_JUMP_AR2
self.WeaponAnimTranslations[ACT_LAND] = ACT_HL2MP_IDLE_AR2
self.WeaponAnimTranslations[ACT_WALK] = ACT_HL2MP_WALK_PASSIVE
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_HL2MP_WALK_AR2
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH_PASSIVE
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_AR2
self.WeaponAnimTranslations[ACT_RUN] = ACT_HL2MP_RUN_PASSIVE
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_HL2MP_RUN_AR2
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_HL2MP_WALK_CROUCH_PASSIVE
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_AR2
elseif hType == "pistol" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_HL2MP_IDLE_PISTOL
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_HL2MP_IDLE_CROUCH_PISTOL
self.WeaponAnimTranslations[ACT_RELOAD] = "vjges_reload_pistol"
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = "vjges_reload_pistol"
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_HL2MP_IDLE_CROUCH_PISTOL
self.WeaponAnimTranslations[ACT_IDLE] = ACT_HL2MP_IDLE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_HL2MP_IDLE_PISTOL
self.WeaponAnimTranslations[ACT_JUMP] = ACT_HL2MP_JUMP_PISTOL
self.WeaponAnimTranslations[ACT_GLIDE] = ACT_HL2MP_JUMP_PISTOL
self.WeaponAnimTranslations[ACT_LAND] = ACT_HL2MP_IDLE_PISTOL
self.WeaponAnimTranslations[ACT_WALK] = ACT_HL2MP_WALK
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_HL2MP_WALK_PISTOL
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_PISTOL
self.WeaponAnimTranslations[ACT_RUN] = ACT_HL2MP_RUN_FAST
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_HL2MP_RUN_PISTOL
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_PISTOL
elseif hType == "smg" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_HL2MP_IDLE_SMG1
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG1
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_HL2MP_IDLE_CROUCH_SMG1
self.WeaponAnimTranslations[ACT_RELOAD] = "vjges_reload_smg1"
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = "vjges_reload_smg1"
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_HL2MP_IDLE_CROUCH_SMG1
self.WeaponAnimTranslations[ACT_IDLE] = ACT_HL2MP_IDLE_PASSIVE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_HL2MP_IDLE_SMG1
self.WeaponAnimTranslations[ACT_JUMP] = ACT_HL2MP_JUMP_SMG1
self.WeaponAnimTranslations[ACT_GLIDE] = ACT_HL2MP_JUMP_SMG1
self.WeaponAnimTranslations[ACT_LAND] = ACT_HL2MP_IDLE_SMG1
self.WeaponAnimTranslations[ACT_WALK] = ACT_HL2MP_WALK_PASSIVE
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_HL2MP_WALK_SMG1
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH_PASSIVE
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_SMG1
self.WeaponAnimTranslations[ACT_RUN] = ACT_HL2MP_RUN_PASSIVE
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_HL2MP_RUN_SMG1
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_HL2MP_WALK_CROUCH_PASSIVE
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_SMG1
elseif hType == "grenade" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_HL2MP_IDLE_GRENADE
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_HL2MP_GESTURE_RANGE_ATTACK_GRENADE
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_HL2MP_IDLE_CROUCH_GRENADE
-- self.WeaponAnimTranslations[ACT_RELOAD] = "vjges_reload_pistol"
-- self.WeaponAnimTranslations[ACT_RELOAD_LOW] = "vjges_reload_pistol"
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_HL2MP_IDLE_CROUCH_GRENADE
self.WeaponAnimTranslations[ACT_IDLE] = ACT_HL2MP_IDLE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_HL2MP_IDLE_GRENADE
self.WeaponAnimTranslations[ACT_JUMP] = ACT_HL2MP_JUMP_GRENADE
self.WeaponAnimTranslations[ACT_GLIDE] = ACT_HL2MP_JUMP_GRENADE
self.WeaponAnimTranslations[ACT_LAND] = ACT_HL2MP_IDLE_GRENADE
self.WeaponAnimTranslations[ACT_WALK] = ACT_HL2MP_WALK
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_HL2MP_WALK_GRENADE
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_GRENADE
self.WeaponAnimTranslations[ACT_RUN] = ACT_HL2MP_RUN
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_HL2MP_RUN_GRENADE
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_GRENADE
elseif hType == "shotgun" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_HL2MP_IDLE_SHOTGUN
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_HL2MP_GESTURE_RANGE_ATTACK_SHOTGUN
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_HL2MP_IDLE_CROUCH_SHOTGUN
self.WeaponAnimTranslations[ACT_RELOAD] = "vjges_reload_shotgun"
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = "vjges_reload_shotgun"
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_HL2MP_IDLE_CROUCH_SHOTGUN
self.WeaponAnimTranslations[ACT_IDLE] = ACT_HL2MP_IDLE_PASSIVE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_HL2MP_IDLE_SHOTGUN
self.WeaponAnimTranslations[ACT_JUMP] = ACT_HL2MP_JUMP_SHOTGUN
self.WeaponAnimTranslations[ACT_GLIDE] = ACT_HL2MP_JUMP_SHOTGUN
self.WeaponAnimTranslations[ACT_LAND] = ACT_HL2MP_IDLE_SHOTGUN
self.WeaponAnimTranslations[ACT_WALK] = ACT_HL2MP_WALK_PASSIVE
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_HL2MP_WALK_SHOTGUN
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH_PASSIVE
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_SHOTGUN
self.WeaponAnimTranslations[ACT_RUN] = ACT_HL2MP_RUN_PASSIVE
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_HL2MP_RUN_SHOTGUN
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_HL2MP_WALK_CROUCH_PASSIVE
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_SHOTGUN
elseif hType == "rpg" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_HL2MP_IDLE_RPG
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_HL2MP_GESTURE_RANGE_ATTACK_RPG
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_HL2MP_IDLE_CROUCH_RPG
self.WeaponAnimTranslations[ACT_RELOAD] = "vjges_reload_ar2"
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = "vjges_reload_ar2"
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_HL2MP_IDLE_CROUCH_RPG
self.WeaponAnimTranslations[ACT_IDLE] = ACT_HL2MP_IDLE_PASSIVE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_HL2MP_IDLE_RPG
self.WeaponAnimTranslations[ACT_JUMP] = ACT_HL2MP_JUMP_RPG
self.WeaponAnimTranslations[ACT_GLIDE] = ACT_HL2MP_JUMP_RPG
self.WeaponAnimTranslations[ACT_LAND] = ACT_HL2MP_IDLE_RPG
self.WeaponAnimTranslations[ACT_WALK] = ACT_HL2MP_WALK_PASSIVE
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_HL2MP_WALK_RPG
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH_PASSIVE
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_RPG
self.WeaponAnimTranslations[ACT_RUN] = ACT_HL2MP_RUN_PASSIVE
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_HL2MP_RUN_RPG
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_HL2MP_WALK_CROUCH_PASSIVE
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_RPG
elseif hType == "physgun" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_HL2MP_IDLE_PHYSGUN
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_HL2MP_GESTURE_RANGE_ATTACK_SHOTGUN
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_HL2MP_IDLE_CROUCH_PHYSGUN
self.WeaponAnimTranslations[ACT_RELOAD] = "vjges_reload_ar2"
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = "vjges_reload_ar2"
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_HL2MP_IDLE_CROUCH_PHYSGUN
self.WeaponAnimTranslations[ACT_IDLE] = ACT_HL2MP_IDLE_PASSIVE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_HL2MP_IDLE_PHYSGUN
self.WeaponAnimTranslations[ACT_JUMP] = ACT_HL2MP_JUMP_PHYSGUN
self.WeaponAnimTranslations[ACT_GLIDE] = ACT_HL2MP_JUMP_PHYSGUN
self.WeaponAnimTranslations[ACT_LAND] = ACT_HL2MP_IDLE_PHYSGUN
self.WeaponAnimTranslations[ACT_WALK] = ACT_HL2MP_WALK_PASSIVE
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_HL2MP_WALK_PHYSGUN
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH_PASSIVE
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_PHYSGUN
self.WeaponAnimTranslations[ACT_RUN] = ACT_HL2MP_RUN_PASSIVE
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_HL2MP_RUN_PHYSGUN
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_HL2MP_WALK_CROUCH_PASSIVE
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_PHYSGUN
elseif hType == "crossbow" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_HL2MP_IDLE_CROSSBOW
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_HL2MP_GESTURE_RANGE_ATTACK_CROSSBOW
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_HL2MP_IDLE_CROUCH_CROSSBOW
self.WeaponAnimTranslations[ACT_RELOAD] = "vjges_reload_ar2"
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = "vjges_reload_ar2"
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_HL2MP_IDLE_CROUCH_CROSSBOW
self.WeaponAnimTranslations[ACT_IDLE] = ACT_HL2MP_IDLE_PASSIVE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_HL2MP_IDLE_CROSSBOW
self.WeaponAnimTranslations[ACT_JUMP] = ACT_HL2MP_JUMP_CROSSBOW
self.WeaponAnimTranslations[ACT_GLIDE] = ACT_HL2MP_JUMP_CROSSBOW
self.WeaponAnimTranslations[ACT_LAND] = ACT_HL2MP_IDLE_CROSSBOW
self.WeaponAnimTranslations[ACT_WALK] = ACT_HL2MP_WALK_PASSIVE
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_HL2MP_WALK_CROSSBOW
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH_PASSIVE
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_CROSSBOW
self.WeaponAnimTranslations[ACT_RUN] = ACT_HL2MP_RUN_PASSIVE
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_HL2MP_RUN_CROSSBOW
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_HL2MP_WALK_CROUCH_PASSIVE
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_CROSSBOW
elseif hType == "slam" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_HL2MP_IDLE_SLAM
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_HL2MP_GESTURE_RANGE_ATTACK_SLAM
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_HL2MP_IDLE_CROUCH_SLAM
-- self.WeaponAnimTranslations[ACT_RELOAD] = "vjges_reload_pistol"
-- self.WeaponAnimTranslations[ACT_RELOAD_LOW] = "vjges_reload_pistol"
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_HL2MP_IDLE_CROUCH_SLAM
self.WeaponAnimTranslations[ACT_IDLE] = ACT_HL2MP_IDLE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_HL2MP_IDLE_SLAM
self.WeaponAnimTranslations[ACT_JUMP] = ACT_HL2MP_JUMP_SLAM
self.WeaponAnimTranslations[ACT_GLIDE] = ACT_HL2MP_JUMP_SLAM
self.WeaponAnimTranslations[ACT_LAND] = ACT_HL2MP_IDLE_SLAM
self.WeaponAnimTranslations[ACT_WALK] = ACT_HL2MP_WALK
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_HL2MP_WALK_SLAM
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_SLAM
self.WeaponAnimTranslations[ACT_RUN] = ACT_HL2MP_RUN
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_HL2MP_RUN_SLAM
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_SLAM
elseif hType == "duel" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_HL2MP_IDLE_DUEL
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_HL2MP_GESTURE_RANGE_ATTACK_DUEL
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_HL2MP_IDLE_CROUCH_DUEL
self.WeaponAnimTranslations[ACT_RELOAD] = "vjges_reload_duel"
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = "vjges_reload_duel"
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_HL2MP_IDLE_CROUCH_DUEL
self.WeaponAnimTranslations[ACT_IDLE] = ACT_HL2MP_IDLE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_HL2MP_IDLE_DUEL
self.WeaponAnimTranslations[ACT_JUMP] = ACT_HL2MP_JUMP_DUEL
self.WeaponAnimTranslations[ACT_GLIDE] = ACT_HL2MP_JUMP_DUEL
self.WeaponAnimTranslations[ACT_LAND] = ACT_HL2MP_IDLE_DUEL
self.WeaponAnimTranslations[ACT_WALK] = ACT_HL2MP_WALK
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_HL2MP_WALK_DUEL
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_DUEL
self.WeaponAnimTranslations[ACT_RUN] = ACT_HL2MP_RUN
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_HL2MP_RUN_DUEL
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_DUEL
elseif hType == "revolver" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_HL2MP_IDLE_REVOLVER
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_HL2MP_GESTURE_RANGE_ATTACK_REVOLVER
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_HL2MP_IDLE_CROUCH_REVOLVER
self.WeaponAnimTranslations[ACT_RELOAD] = "vjges_reload_revolver"
self.WeaponAnimTranslations[ACT_RELOAD_LOW] = "vjges_reload_revolver"
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_HL2MP_IDLE_CROUCH_REVOLVER
self.WeaponAnimTranslations[ACT_IDLE] = ACT_HL2MP_IDLE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_HL2MP_IDLE_REVOLVER
self.WeaponAnimTranslations[ACT_JUMP] = ACT_HL2MP_JUMP_REVOLVER
self.WeaponAnimTranslations[ACT_GLIDE] = ACT_HL2MP_JUMP_REVOLVER
self.WeaponAnimTranslations[ACT_LAND] = ACT_HL2MP_IDLE_REVOLVER
self.WeaponAnimTranslations[ACT_WALK] = ACT_HL2MP_WALK
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_HL2MP_WALK_REVOLVER
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_REVOLVER
self.WeaponAnimTranslations[ACT_RUN] = ACT_HL2MP_RUN
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_HL2MP_RUN_REVOLVER
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_REVOLVER
elseif hType == "melee" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_HL2MP_IDLE_MELEE
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_HL2MP_IDLE_CROUCH_MELEE
-- self.WeaponAnimTranslations[ACT_RELOAD] = "vjges_reload_pistol"
-- self.WeaponAnimTranslations[ACT_RELOAD_LOW] = "vjges_reload_pistol"
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_HL2MP_IDLE_CROUCH_MELEE
self.WeaponAnimTranslations[ACT_IDLE] = ACT_HL2MP_IDLE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_HL2MP_IDLE_MELEE
self.WeaponAnimTranslations[ACT_JUMP] = ACT_HL2MP_JUMP_MELEE
self.WeaponAnimTranslations[ACT_GLIDE] = ACT_HL2MP_JUMP_MELEE
self.WeaponAnimTranslations[ACT_LAND] = ACT_HL2MP_IDLE_MELEE
self.WeaponAnimTranslations[ACT_WALK] = ACT_HL2MP_WALK
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_HL2MP_WALK_MELEE
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_MELEE
self.WeaponAnimTranslations[ACT_RUN] = ACT_HL2MP_RUN
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_HL2MP_RUN_MELEE
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_MELEE
elseif hType == "melee2" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_HL2MP_IDLE_MELEE2
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE2
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_HL2MP_IDLE_CROUCH_MELEE2
-- self.WeaponAnimTranslations[ACT_RELOAD] = "vjges_reload_pistol"
-- self.WeaponAnimTranslations[ACT_RELOAD_LOW] = "vjges_reload_pistol"
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_HL2MP_IDLE_CROUCH_MELEE2
self.WeaponAnimTranslations[ACT_IDLE] = ACT_HL2MP_IDLE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_HL2MP_IDLE_MELEE2
self.WeaponAnimTranslations[ACT_JUMP] = ACT_HL2MP_JUMP_MELEE2
self.WeaponAnimTranslations[ACT_GLIDE] = ACT_HL2MP_JUMP_MELEE2
self.WeaponAnimTranslations[ACT_LAND] = ACT_HL2MP_IDLE_MELEE2
self.WeaponAnimTranslations[ACT_WALK] = ACT_HL2MP_WALK
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_HL2MP_WALK_MELEE2
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_MELEE2
self.WeaponAnimTranslations[ACT_RUN] = ACT_HL2MP_RUN
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_HL2MP_RUN_MELEE2
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_MELEE2
elseif hType == "knife" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_HL2MP_IDLE_KNIFE
self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_HL2MP_GESTURE_RANGE_ATTACK_KNIFE
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_HL2MP_IDLE_CROUCH_KNIFE
-- self.WeaponAnimTranslations[ACT_RELOAD] = "vjges_reload_pistol"
-- self.WeaponAnimTranslations[ACT_RELOAD_LOW] = "vjges_reload_pistol"
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_HL2MP_IDLE_CROUCH_KNIFE
self.WeaponAnimTranslations[ACT_IDLE] = ACT_HL2MP_IDLE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_HL2MP_IDLE_KNIFE
self.WeaponAnimTranslations[ACT_JUMP] = ACT_HL2MP_JUMP_KNIFE
self.WeaponAnimTranslations[ACT_GLIDE] = ACT_HL2MP_JUMP_KNIFE
self.WeaponAnimTranslations[ACT_LAND] = ACT_HL2MP_IDLE_KNIFE
self.WeaponAnimTranslations[ACT_WALK] = ACT_HL2MP_WALK
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_HL2MP_WALK_KNIFE
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_KNIFE
self.WeaponAnimTranslations[ACT_RUN] = ACT_HL2MP_RUN
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_HL2MP_RUN_KNIFE
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_KNIFE
elseif hType == "camera" then
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1] = ACT_HL2MP_IDLE_CAMERA
-- self.WeaponAnimTranslations[ACT_GESTURE_RANGE_ATTACK1] = ACT_HL2MP_GESTURE_RANGE_ATTACK_CAMERA
self.WeaponAnimTranslations[ACT_RANGE_ATTACK1_LOW] = ACT_HL2MP_IDLE_CROUCH_CAMERA
-- self.WeaponAnimTranslations[ACT_RELOAD] = "vjges_reload_pistol"
-- self.WeaponAnimTranslations[ACT_RELOAD_LOW] = "vjges_reload_pistol"
self.WeaponAnimTranslations[ACT_COVER_LOW] = ACT_HL2MP_IDLE_CROUCH_CAMERA
self.WeaponAnimTranslations[ACT_IDLE] = ACT_HL2MP_IDLE
self.WeaponAnimTranslations[ACT_IDLE_ANGRY] = ACT_HL2MP_IDLE_CAMERA
self.WeaponAnimTranslations[ACT_JUMP] = ACT_HL2MP_JUMP_CAMERA
self.WeaponAnimTranslations[ACT_GLIDE] = ACT_HL2MP_JUMP_CAMERA
self.WeaponAnimTranslations[ACT_LAND] = ACT_HL2MP_IDLE_CAMERA
self.WeaponAnimTranslations[ACT_WALK] = ACT_HL2MP_WALK
self.WeaponAnimTranslations[ACT_WALK_AIM] = ACT_HL2MP_WALK_CAMERA
self.WeaponAnimTranslations[ACT_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_WALK_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_CAMERA
self.WeaponAnimTranslations[ACT_RUN] = ACT_HL2MP_RUN
self.WeaponAnimTranslations[ACT_RUN_AIM] = ACT_HL2MP_RUN_CAMERA
self.WeaponAnimTranslations[ACT_RUN_CROUCH] = ACT_HL2MP_WALK_CROUCH
self.WeaponAnimTranslations[ACT_RUN_CROUCH_AIM] = ACT_HL2MP_WALK_CROUCH_CAMERA
end
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:TranslateToWeaponAnim(act)
local translate = self.WeaponAnimTranslations[act]
if translate == nil then -- If no animation found, then just return the given activity
return act
else -- Found an animation!
return VJ_PICK(translate)
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
local sdWepSwitch = {"physics/metal/weapon_impact_soft1.wav","physics/metal/weapon_impact_soft2.wav","physics/metal/weapon_impact_soft3.wav"}
--
function ENT:DoChangeWeapon(wep, invSwitch)
wep = wep or nil -- The weapon to give or setup | Setting it nil will only setup the current active weapon
invSwitch = invSwitch or false -- If true, it will not delete the previous weapon!
local curWep = self:GetActiveWeapon()
-- If not supposed to have a weapon, then return!
if self.DisableWeapons && IsValid(curWep) then
curWep:Remove()
return NULL
end
-- Only remove and actually give the weapon if the function is given a weapon class to set
if wep != nil then
if invSwitch then
self:SelectWeapon(wep)
VJ_EmitSound(self, sdWepSwitch, 70)
curWep = wep
else
if IsValid(curWep) && self.WeaponInventoryStatus <= VJ_WEP_INVENTORY_PRIMARY then
curWep:Remove()
end
curWep = self:Give(wep)
self.WeaponInventory.Primary = curWep
end
end
-- If we are given a new weapon or switching weapon, then do all of the necessary set up
if IsValid(curWep) then
self.CurrentWeaponAnimation = -1
self:SetWeaponState() -- Reset the weapon state because we do NOT want previous weapon's state to be used!
if invSwitch then
if curWep.IsVJBaseWeapon then curWep:Equip(self) end
else -- If we are not switching weapons, then we know curWep is the primary weapon
self.WeaponInventoryStatus = VJ_WEP_INVENTORY_PRIMARY
-- If this is completely new weapon, then set the weapon inventory's primary to this weapon
local curPrimary = self.WeaponInventory.Primary
if curWep != self.WeaponInventory.Primary then
if IsValid(curPrimary) then curPrimary:Remove() end -- Remove the old primary weapon
self.WeaponInventory.Primary = curWep
end
end
self:SetupWeaponHoldTypeAnims(curWep:GetHoldType())
self:CustomOnDoChangeWeapon(curWep, self.CurrentWeaponEntity, invSwitch)
self.CurrentWeaponEntity = curWep
else
self.WeaponInventoryStatus = VJ_WEP_INVENTORY_NONE
end
return curWep
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:SetWeaponState(state, time)
time = time or -1
self.WeaponState = state or VJ_WEP_STATE_READY
if time >= 0 then
timer.Create("timer_weapon_state_reset"..self:EntIndex(), time, 1, function()
self:SetWeaponState()
end)
else
timer.Remove("timer_weapon_state_reset"..self:EntIndex())
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:GetWeaponState()
return self.WeaponState
end
---------------------------------------------------------------------------------------------------------------------------------------------
local finishAttack = {
[VJ_ATTACK_MELEE] = function(self, skipStopAttacks)
if skipStopAttacks != true then
timer.Create("timer_melee_finished"..self:EntIndex(), self:DecideAttackTimer(self.NextAnyAttackTime_Melee, self.NextAnyAttackTime_Melee_DoRand, self.TimeUntilMeleeAttackDamage, self.CurrentAttackAnimationDuration), 1, function()
self:StopAttacks()
self:DoChaseAnimation()
end)
end
timer.Create("timer_melee_finished_abletomelee"..self:EntIndex(), self:DecideAttackTimer(self.NextMeleeAttackTime, self.NextMeleeAttackTime_DoRand), 1, function()
self.IsAbleToMeleeAttack = true
end)
end
}
---------------------------------------------------------------------------------------------------------------------------------------------
//function ENT:OnActiveWeaponChanged(old, new) print(old, new) end
---------------------------------------------------------------------------------------------------------------------------------------------
/* Variable Notes:
m_flMoveWaitFinished = Current move and wait time, used for things like when opening doors and have to stop for a second
m_hOpeningDoor = The door entity it's opening
m_vDefaultEyeOffset = The eye position, it's very close to self:EyePos()
m_flTimeEnemyAcquired = Every time setenemy is called (including NULL!) --> print(math.abs(self:GetInternalVariable("m_flTimeEnemyAcquired")))
m_flGroundChangeTime = Time since it touched the ground (Must be from high place)
m_bSequenceFinished = Is it playing a animation?
m_vecLean = How much it's leaning (ex: Drag around with physgun)
m_flAnimTime = Changes the self:GetAnimTimeInterval()
m_bIsMoving = Same as self:IsMoving()
m_flLastEventCheck = Cycle index of when events were last checked
m_flGroundSpeed = Computed linear movement rate for current sequence
m_flOriginalYaw = This is the direction facing when the level designer placed the NPC in the level.
m_spawnEquipment = Class name of the weapon it spawned with, stays even when weapon is removed or another weapon from its inventory is used!
m_takedamage = Defines how it can take damage
#define DAMAGE_NO 0
#define DAMAGE_EVENTS_ONLY 1 // Call damage functions, but don't modify health
#define DAMAGE_YES 2
#define DAMAGE_AIM 3
m_nWaterType = Type of water the entity is in --> 1 = water, 2 = slime
-- Following is just used for the face and eye looking:
m_hLookTarget = The entity it's looking at
m_flNextRandomLookTime = Next time it can look at something (Can be used to set it as well)
m_flEyeIntegRate = How fast the eyes move
m_viewtarget = Returns the position the NPC's eye pupils are looking at (Can be used to set it as well)
m_flBlinktime = Time until it blinks again (Can be used to set it as well)
-- Change movement speed:
self:SetLocalVelocity(self:GetMoveVelocity() * 1.5)
-- To test sound hints:
local test = getSdHint(bit.bor(SOUND_DANGER, SOUND_CONTEXT_PLAYER_VEHICLE), self:GetPos())
if istable(test) then
print("---------------------")
print(test.origin:Distance(self:GetPos()))
PrintTable(test)
end
*/
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:Think()
//PrintTable(self:GetAnimInfo(self:GetActivity()))
//self:MoveStop()
//self:ResetMoveCalc()
//print("---------------------")
//PrintTable(self:GetSaveTable())
//print(self:GetInternalVariable("m_flFieldOfView"))
//print(self:GetInternalVariable("m_flMoveWaitFinished") - CurTime())
//self:SetSaveValue("m_flMoveWaitFinished", CurTime() + 2)
self:SetCondition(1) -- Fix attachments, bones, positions, angles etc. being broken in NPCs! This condition is used as a backup in case sv_pvsskipanimation isn't disabled!
//if self.CurrentSchedule != nil then PrintTable(self.CurrentSchedule) end
//if self.CurrentTask != nil then PrintTable(self.CurrentTask) end
if self.MovementType == VJ_MOVETYPE_GROUND && self:GetVelocity():Length() <= 0 && !self:IsEFlagSet(EFL_IS_BEING_LIFTED_BY_BARNACLE) /*&& curSched.IsMovingTask == true*/ then self:DropToFloor() end
local curSched = self.CurrentSchedule
if curSched != nil then
if self:IsMoving() then
if curSched.MoveType == 0 && !VJ_HasValue(self.AnimTbl_Walk, self:GetMovementActivity()) then
self:SetMovementActivity(VJ_PICK(self.AnimTbl_Walk))
elseif curSched.MoveType == 1 && !VJ_HasValue(self.AnimTbl_Run, self:GetMovementActivity()) then
self:SetMovementActivity(VJ_PICK(self.AnimTbl_Run))
end
end
local blockingEnt = self:GetBlockingEntity()
-- No longer needed as the engine now does detects and opens the doors
//if self.CanOpenDoors && IsValid(blockingEnt) && (blockingEnt:GetClass() == "func_door" or blockingEnt:GetClass() == "func_door_rotating") && (blockingEnt:HasSpawnFlags(256) or blockingEnt:HasSpawnFlags(1024)) && !blockingEnt:HasSpawnFlags(512) then
//blockingEnt:Fire("Open")
//end
if (curSched.StopScheduleIfNotMoving == true or curSched.StopScheduleIfNotMoving_Any == true) && (!self:IsMoving() or (IsValid(blockingEnt) && (blockingEnt:IsNPC() or curSched.StopScheduleIfNotMoving_Any == true))) then // (self:GetGroundSpeedVelocity():Length() <= 0) == true
self:ScheduleFinished(curSched)
//self:SetCondition(COND_TASK_FAILED)
//self:StopMoving()
end
-- self:OnMovementFailed() handles some of them, but we do still need this for non-movement failures (EX: Finding cover area)
if self:HasCondition(COND_TASK_FAILED) then
//print("VJ Base: Task Failed Condition Identified! "..self:GetName())
if self:DoRunCode_OnFail(curSched) == true then
self:ClearCondition(COND_TASK_FAILED)
end
if curSched.ResetOnFail == true then
self:ClearCondition(COND_TASK_FAILED)
self:StopMoving()
//self:SelectSchedule()
end
end
end
//print("------------------")
//print(self:GetActiveWeapon())
//PrintTable(self:GetWeapons())
if self.DoingWeaponAttack == false then self.DoingWeaponAttack_Standing = false end
if self.CurrentWeaponEntity != self:GetActiveWeapon() then self.CurrentWeaponEntity = self:DoChangeWeapon() end
self:CustomOnThink()
local curTime = CurTime()
if !self.Dead && self.HasBreathSound && self.HasSounds && curTime > self.NextBreathSoundT then
local sdtbl = VJ_PICK(self.SoundTbl_Breath)
local dur = 1
if sdtbl != false then
VJ_STOPSOUND(self.CurrentBreathSound)
dur = (self.NextSoundTime_Breath == true and SoundDuration(sdtbl)) or math.Rand(self.NextSoundTime_Breath.a, self.NextSoundTime_Breath.b)
self.CurrentBreathSound = VJ_CreateSound(self, sdtbl, self.BreathSoundLevel, self:VJ_DecideSoundPitch(self.BreathSoundPitch.a, self.BreathSoundPitch.b))
end
self.NextBreathSoundT = curTime + dur
end
--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
if GetConVar("ai_disabled"):GetInt() == 0 && self:GetState() != VJ_STATE_FREEZE && !self:IsEFlagSet(EFL_IS_BEING_LIFTED_BY_BARNACLE) then
if self.VJ_DEBUG == true then
if GetConVar("vj_npc_printcurenemy"):GetInt() == 1 then print(self:GetClass().."'s Enemy: ",self:GetEnemy()," Alerted? ",self.Alerted) end
if GetConVar("vj_npc_printtakingcover"):GetInt() == 1 then if curTime > self.TakingCoverT == true then print(self:GetClass().." Is Not Taking Cover") else print(self:GetClass().." Is Taking Cover ("..self.TakingCoverT-curTime..")") end end
if GetConVar("vj_npc_printlastseenenemy"):GetInt() == 1 then PrintMessage(HUD_PRINTTALK, (curTime - self.EnemyData.LastVisibleTime).." ("..self:GetName()..")") end
if IsValid(self.CurrentWeaponEntity) && GetConVar("vj_npc_dev_printwepinfo"):GetInt() == 1 then print(self:GetName().." -->", self.CurrentWeaponEntity, "Ammo: "..self.CurrentWeaponEntity:Clip1().."/"..self.CurrentWeaponEntity:GetMaxClip1().." | Accuracy: "..self.WeaponSpread) end
end
local eneData = self.EnemyData
self:SetPlaybackRate(self.AnimationPlaybackRate)
if self:GetArrivalActivity() == -1 then
self:SetArrivalActivity(self.CurrentAnim_IdleStand)
end
if self.UsePlayerModelMovement == true && self.MovementType == VJ_MOVETYPE_GROUND then
local moveDir = self:GetMoveDirection(true)
if moveDir != defPos then
self:SetPoseParameter("move_x", moveDir.x)
self:SetPoseParameter("move_y", moveDir.y)
if curSched != nil && !curSched.ConstantlyFaceEnemy then
self:FaceCertainPosition(self:GetCurWaypointPos())
end
end
end
self:CustomOnThink_AIEnabled()
if self.DisableFootStepSoundTimer == false then self:FootStepSoundCode() end
-- Update follow system's data
//print("------------------")
//PrintTable(self.FollowData)
if self.IsFollowing == true then
local followData = self.FollowData
local followEnt = followData.Ent
local followIsLiving = followData.IsLiving
//print(self:GetTarget())
if IsValid(followEnt) && (!followIsLiving or (followIsLiving && (self:Disposition(followEnt) == D_LI or self:GetClass() == followEnt:GetClass()) && VJ_IsAlive(followEnt))) then
if curTime > self.NextFollowUpdateT && !self.VJTags[VJ_TAG_HEALING] then
local distToPly = self:GetPos():Distance(followEnt:GetPos())
local busy = self:BusyWithActivity()
self:SetTarget(followEnt)
followData.StopAct = false
if distToPly > followData.MinDist then -- Entity is far away, move towards it!
local isFar = distToPly > (followData.MinDist * 4)
-- IF (we are busy but far) OR (not busy) THEN move
if (busy && isFar) or (!busy) then
followData.Moving = true
-- If we are far then stop all activities (ex: attacks) and just go there already!
if isFar then
followData.StopAct = true
end
-- If we are close then walk otherwise run
self:VJ_TASK_GOTO_TARGET((distToPly < (followData.MinDist * 1.5) and "TASK_WALK_PATH") or "TASK_RUN_PATH", function(x)
x.CanShootWhenMoving = true
x.ConstantlyFaceEnemyVisible = (IsValid(self:GetActiveWeapon()) and true) or false
end)
end
elseif followData.Moving == true then -- Entity is very close, stop moving!
if !busy then -- If not busy then make it stop moving and do something
self:StopMoving()
self:SelectSchedule()
end
followData.Moving = false
end
self.NextFollowUpdateT = curTime + self.NextFollowUpdateTime
end
else
self:FollowReset()
end
end
//print("MAX CLIP: ", self.CurrentWeaponEntity:GetMaxClip1())
//print("CLIP: ", self.CurrentWeaponEntity:Clip1())
-- Turn to the current face position or entity
if self.FacingStatus == VJ_FACING_POSITION then
local faceAng = self.FacingData
if self.TurningUseAllAxis == true then
local myAngs = self:GetAngles()
self:SetAngles(LerpAngle(FrameTime()*self.TurningSpeed, myAngs, Angle(faceAng.p, myAngs.y, faceAng.r)))
end
self:SetIdealYawAndUpdate(faceAng.y)
elseif self.FacingStatus == VJ_FACING_ENTITY then
local faceEnt = self.FacingData
if IsValid(faceEnt) then
local faceAng = self:GetFaceAngle((faceEnt:GetPos() - self:GetPos()):Angle())
if self.TurningUseAllAxis == true then
local myAngs = self:GetAngles()
self:SetAngles(LerpAngle(FrameTime()*self.TurningSpeed, myAngs, Angle(faceAng.p, myAngs.y, faceAng.r)))
end
self:SetIdealYawAndUpdate(faceAng.y)
end
end
/*VJ_CreateTestObject(self:GetEnemyLastSeenPos())
print(self:HasEnemyMemory())
print(curTime - self:GetEnemyLastTimeSeen())
print(curTime - self:GetEnemyFirstTimeSeen())*/
if !self.Dead then
-- Health Regeneration System
if self.HasHealthRegeneration == true && curTime > self.HealthRegenerationDelayT then
local myHP = self:Health()
self:SetHealth(math_clamp(myHP + self.HealthRegenerationAmount, myHP, self:GetMaxHealth()))
self.HealthRegenerationDelayT = curTime + math.Rand(self.HealthRegenerationDelay.a, self.HealthRegenerationDelay.b)
end
-- Run the heavy processes
if curTime > self.NextProcessT then
self:SetupRelationships()
self:CheckForDangers()
self:DoMedicCheck()
self.NextProcessT = curTime + self.NextProcessTime
end
local plyControlled = self.VJ_IsBeingControlled
local myPos = self:GetPos()
local ene = self:GetEnemy()
local eneValid = IsValid(ene)
if eneData.Reset == false then
-- Reset enemy if it doesn't exist or it's dead
if (!eneValid) or (eneValid && ene:Health() <= 0) then
eneData.Reset = true
self:ResetEnemy(true)
ene = self:GetEnemy()
eneValid = IsValid(ene)
end
-- Reset enemy if it has been unseen for a while
if (curTime - eneData.LastVisibleTime) > ((self.LatestEnemyDistance < 4000 and self.TimeUntilEnemyLost) or (self.TimeUntilEnemyLost / 2)) && (!self.IsVJBaseSNPC_Tank) then
self:PlaySoundSystem("LostEnemy")
eneData.Reset = true
self:ResetEnemy(true)
ene = self:GetEnemy()
eneValid = IsValid(ene)
end
end
if self.DoingWeaponAttack == true then self:CapabilitiesRemove(CAP_TURN_HEAD) else self:CapabilitiesAdd(bit.bor(CAP_TURN_HEAD)) end -- Fixes their heads breaking
-- If we have a valid weapon...
if IsValid(self.CurrentWeaponEntity) then
-- Weapon Inventory System
if !plyControlled && !self:BusyWithActivity() then // self.IsReloadingWeapon == false
if eneValid then
if IsValid(self.WeaponInventory.Melee) && ((self.LatestEnemyDistance < self.MeleeAttackDistance) or (self.LatestEnemyDistance < 300 && self.CurrentWeaponEntity:Clip1() <= 0)) && (self:Health() > self:GetMaxHealth() * 0.25) && self.CurrentWeaponEntity != self.WeaponInventory.Melee then
if self:GetWeaponState() == VJ_WEP_STATE_RELOADING then self:SetWeaponState() end -- Since the reloading can be cut off, reset it back to false, or else it can mess up its behavior!
//timer.Remove("timer_reload_end"..self:EntIndex()) -- No longer needed
self.WeaponInventoryStatus = VJ_WEP_INVENTORY_MELEE
self:DoChangeWeapon(self.WeaponInventory.Melee, true)
elseif self:GetWeaponState() != VJ_WEP_STATE_RELOADING && IsValid(self.WeaponInventory.AntiArmor) && (ene.IsVJBaseSNPC_Tank == true or ene.VJ_IsHugeMonster == true) && self.CurrentWeaponEntity != self.WeaponInventory.AntiArmor then
self.WeaponInventoryStatus = VJ_WEP_INVENTORY_ANTI_ARMOR
self:DoChangeWeapon(self.WeaponInventory.AntiArmor, true)
end
end
if self:GetWeaponState() != VJ_WEP_STATE_RELOADING then
-- Reset weapon status from melee to primary
if self.WeaponInventoryStatus == VJ_WEP_INVENTORY_MELEE && (!eneValid or (eneValid && self.LatestEnemyDistance >= 300)) then
self.WeaponInventoryStatus = VJ_WEP_INVENTORY_PRIMARY
self:DoChangeWeapon(self.WeaponInventory.Primary, true)
-- Reset weapon status from anti-armor to primary
elseif self.WeaponInventoryStatus == VJ_WEP_INVENTORY_ANTI_ARMOR && (!eneValid or (eneValid && ene.IsVJBaseSNPC_Tank != true && ene.VJ_IsHugeMonster != true)) then
self.WeaponInventoryStatus = VJ_WEP_INVENTORY_PRIMARY
self:DoChangeWeapon(self.WeaponInventory.Primary, true)
end
end
end
-- Weapon Reloading
if self.AllowWeaponReloading && !self:BusyWithActivity() && self:GetWeaponState() == VJ_WEP_STATE_READY && (!self.CurrentWeaponEntity.IsMeleeWeapon) && self.AttackType == VJ_ATTACK_NONE && self.VJ_PlayingSequence == false && ((!plyControlled && ((!eneValid && self.CurrentWeaponEntity:GetMaxClip1() > self.CurrentWeaponEntity:Clip1() && (curTime - eneData.TimeSet) > math.random(3, 8) && !self:IsMoving()) or (eneValid && self.CurrentWeaponEntity:Clip1() <= 0))) or (plyControlled && self.VJ_TheController:KeyDown(IN_RELOAD) && self.CurrentWeaponEntity:GetMaxClip1() > self.CurrentWeaponEntity:Clip1())) then
self.DoingWeaponAttack = false
self.DoingWeaponAttack_Standing = false
if !plyControlled then self:SetWeaponState(VJ_WEP_STATE_RELOADING) end
self.NextChaseTime = curTime + 2
if eneValid == true then self:PlaySoundSystem("WeaponReload") end -- tsayn han e minag yete teshnami ga!
self:CustomOnWeaponReload()
if self.DisableWeaponReloadAnimation == false then
local function DoReloadAnimation(anim)
if VJ_AnimationExists(self, anim) then -- Only if the given animation actually exists!
local dur = self:DecideAnimationLength(anim, false, self.WeaponReloadAnimationDecreaseLengthAmount)
local wep = self.CurrentWeaponEntity
if wep.IsVJBaseWeapon == true then wep:NPC_Reload() end
timer.Create("timer_reload_end"..self:EntIndex(), dur, 1, function()
if IsValid(self) && IsValid(wep) && self:GetWeaponState() == VJ_WEP_STATE_RELOADING then
wep:SetClip1(wep:GetMaxClip1())
if wep.IsVJBaseWeapon == true then wep:CustomOnReload_Finish() end
self:SetWeaponState()
end
end)
self:VJ_ACT_PLAYACTIVITY(anim, true, dur, self.WeaponReloadAnimationFaceEnemy, self.WeaponReloadAnimationDelay, {SequenceDuration=dur, PlayBackRateCalculated=true})
self.AllowToDo_WaitForEnemyToComeOut = false
-- If NOT controlled by a player AND is a gesture make it stop moving so it doesn't run after the enemy right away
if !plyControlled && string.find(anim, "vjges_") then
self:StopMoving()
end
return true -- We have successfully ran the animation!
end
return false -- The given animation was invalid!
end
-- Controlled by a player...
if plyControlled == true then
self:SetWeaponState(VJ_WEP_STATE_RELOADING)
DoReloadAnimation(self:TranslateToWeaponAnim(VJ_PICK(self.AnimTbl_WeaponReload)))
-- NOT controlled by a player...
else
-- NPC is hidden, so attempt to crouch reload
if eneValid == true && self:VJ_ForwardIsHidingZone(self:NearestPoint(myPos + self:OBBCenter()), ene:EyePos(), false, {SetLastHiddenTime=true}) == true then -- Behvedadz
-- if It does NOT have a cover reload animation, then just play the regular standing reload animation
if !DoReloadAnimation(self:TranslateToWeaponAnim(VJ_PICK(self.AnimTbl_WeaponReloadBehindCover))) then
DoReloadAnimation(self:TranslateToWeaponAnim(VJ_PICK(self.AnimTbl_WeaponReload)))
end
else -- NPC is NOT hidden...
-- Under certain situations, simply do standing reload without running to a hiding spot
if self.IsGuard == true or self.IsFollowing == true or self.VJ_IsBeingControlled_Tool == true or eneValid == false or self.MovementType == VJ_MOVETYPE_STATIONARY or self.LatestEnemyDistance < 650 then
DoReloadAnimation(self:TranslateToWeaponAnim(VJ_PICK(self.AnimTbl_WeaponReload)))
else -- If all is good, then run to a hiding spot and then reload!
if self.WeaponReload_FindCover == true then
self:SetMovementActivity(VJ_PICK(self.AnimTbl_Run))
local vsched = ai_vj_schedule.New("vj_weapon_reload")
vsched:EngTask("TASK_FIND_COVER_FROM_ENEMY", 0)
vsched:EngTask("TASK_WAIT_FOR_MOVEMENT", 0)
vsched.StopScheduleIfNotMoving = true
vsched.IsMovingTask = true
vsched.MoveType = 1
vsched.RunCode_OnFinish = function()
if self:GetWeaponState() == VJ_WEP_STATE_RELOADING then
-- If the current situation isn't favorable, then abandon the current reload, and try again!
if (self.AttackType != VJ_ATTACK_NONE) or (IsValid(self:GetEnemy()) && self.HasWeaponBackAway == true && (self:GetPos():Distance(self:GetEnemy():GetPos()) <= self.WeaponBackAway_Distance)) then
self:SetWeaponState()
//timer.Remove("timer_reload_end"..self:EntIndex()) -- Remove the timer to make sure it doesn't set reloading to false at a random time (later on)
else -- Our hiding spot is good, so reload!
DoReloadAnimation(self:TranslateToWeaponAnim(VJ_PICK(self.AnimTbl_WeaponReload)))
self:CustomOnWeaponReload_AfterRanToCover()
end
end
end
self:StartSchedule(vsched)
else
DoReloadAnimation(self:TranslateToWeaponAnim(VJ_PICK(self.AnimTbl_WeaponReload)))
end
end
end
end
else -- If the reload animation is disabled
if self:GetWeaponState() == VJ_WEP_STATE_RELOADING then self:SetWeaponState() end
self.CurrentWeaponEntity:SetClip1(self.CurrentWeaponEntity:GetMaxClip1())
self.CurrentWeaponEntity:NPC_Reload()
end
end
end
if eneValid then
local enePos = ene:GetPos()
if self.DoingWeaponAttack then self:PlaySoundSystem("Suppressing") end
-- Set latest enemy information
self:UpdateEnemyMemory(ene, enePos)
eneData.Reset = false
eneData.IsVisible = plyControlled and self:VisibleVec(enePos) or self:Visible(ene) -- Need to use VisibleVec when controlled because "Visible" will return false randomly
eneData.SightDiff = self:GetSightDirection():Dot((enePos - myPos):GetNormalized())
self.LatestEnemyDistance = myPos:Distance(enePos)
self.NearestPointToEnemyDistance = self:VJ_GetNearestPointToEntityDistance(ene)
if (eneData.SightDiff > math_cos(math_rad(self.SightAngle))) && (self.LatestEnemyDistance < self:GetMaxLookDistance()) && eneData.IsVisible then
eneData.LastVisibleTime = curTime
eneData.LastVisiblePos = enePos
end
-- Turning / Facing Enemy
if self.ConstantlyFaceEnemy then self:DoConstantlyFaceEnemy() end
if self.FacingStatus == VJ_FACING_ENEMY or (self.CombatFaceEnemy == true && self.CurrentSchedule != nil && ((self.CurrentSchedule.ConstantlyFaceEnemy == true) or (self.CurrentSchedule.ConstantlyFaceEnemyVisible == true && eneData.IsVisible))) then
local faceAng = self:GetFaceAngle((enePos - myPos):Angle())
if self.TurningUseAllAxis == true then
local myAngs = self:GetAngles()
self:SetAngles(LerpAngle(FrameTime()*self.TurningSpeed, myAngs, Angle(faceAng.p, myAngs.y, faceAng.r)))
end
self:SetIdealYawAndUpdate(faceAng.y)
end
-- Call for help
if self.AttackType != VJ_ATTACK_GRENADE && self.CallForHelp == true && curTime > self.NextCallForHelpT then
self:Allies_CallHelp(self.CallForHelpDistance)
self.NextCallForHelpT = curTime + self.NextCallForHelpTime
end
-- Grenade attack
if self.HasGrenadeAttack && self:GetState() != VJ_STATE_ONLY_ANIMATION_NOATTACK && self:GetWeaponState() != VJ_WEP_STATE_RELOADING && !self:BusyWithActivity() && curTime > self.NextThrowGrenadeT && curTime > self.TakingCoverT then
if plyControlled && self.VJ_TheController:KeyDown(IN_JUMP) then
self:ThrowGrenadeCode()
self.NextThrowGrenadeT = curTime + math.random(self.NextThrowGrenadeTime.a, self.NextThrowGrenadeTime.b)
elseif !plyControlled then
local chance = self.ThrowGrenadeChance
-- If chance is above 4, then half it by 2 if the enemy is a tank OR not visible
if math.random(1, (chance > 3 && (ene.IsVJBaseSNPC_Tank or !eneData.IsVisible) and math.floor(chance / 2)) or chance) == 1 && self.LatestEnemyDistance < self.GrenadeAttackThrowDistance && self.LatestEnemyDistance > self.GrenadeAttackThrowDistanceClose then
self:ThrowGrenadeCode()
end
self.NextThrowGrenadeT = curTime + math.random(self.NextThrowGrenadeTime.a, self.NextThrowGrenadeTime.b)
end
end
-- Sets the scared behavior movement activity
if !IsValid(self.CurrentWeaponEntity) && self.NoWeapon_UseScaredBehavior && !plyControlled then
local anim = VJ_PICK(self.AnimTbl_ScaredBehaviorMovement)
if anim != false then
self:SetMovementActivity(anim)
else
if VJ_AnimationExists(self, ACT_RUN_PROTECTED) == true then
self:SetMovementActivity(ACT_RUN_PROTECTED)
elseif VJ_AnimationExists(self, ACT_RUN_CROUCH_RIFLE) == true then
self:SetMovementActivity(ACT_RUN_CROUCH_RIFLE)
end
end
//self:SetArrivalActivity(VJ_PICK(self.AnimTbl_ScaredBehaviorStand)) -- Already done by self.CurrentAnim_IdleStand
end
if !eneData.IsVisible then
self.DoingWeaponAttack = false
self.DoingWeaponAttack_Standing = false
end
self:DoWeaponAttackMovementCode()
self:DoPoseParameterLooking()
-- Face enemy for stationary types OR attacks
if (self.MovementType == VJ_MOVETYPE_STATIONARY && self.CanTurnWhileStationary == true) or (self.MeleeAttackAnimationFaceEnemy == true && self.AttackType == VJ_ATTACK_MELEE) or (self.GrenadeAttackAnimationFaceEnemy == true && self.AttackType == VJ_ATTACK_GRENADE && eneData.IsVisible == true) then
self:FaceCertainEntity(ene, true)
end
if !self.vACT_StopAttacks && self:GetState() != VJ_STATE_ONLY_ANIMATION_NOATTACK && self.Behavior != VJ_BEHAVIOR_PASSIVE && self.Behavior != VJ_BEHAVIOR_PASSIVE_NATURE && curTime > self.NextDoAnyAttackT then
self:CustomAttack(ene, eneData.IsVisible) -- Custom attack
-- Melee Attack
if self.HasMeleeAttack == true && !self.vACT_StopAttacks && !self.Flinching && !self.FollowData.StopAct && self.AttackType == VJ_ATTACK_NONE && self.IsAbleToMeleeAttack && (!IsValid(self.CurrentWeaponEntity) or (IsValid(self.CurrentWeaponEntity) && (!self.CurrentWeaponEntity.IsMeleeWeapon))) && ((plyControlled == true && self.VJ_TheController:KeyDown(IN_ATTACK)) or (plyControlled == false && (self.NearestPointToEnemyDistance < self.MeleeAttackDistance && eneData.IsVisible) && (eneData.SightDiff > math_cos(math_rad(self.MeleeAttackAngleRadius))))) then
local seed = curTime; self.CurAttackSeed = seed
self.AttackType = VJ_ATTACK_MELEE
self.AttackStatus = VJ_ATTACK_STATUS_STARTED
self.MeleeAttacking = true
self.IsAbleToMeleeAttack = false
self:FaceCertainEntity(ene, true)
self:CustomOnMeleeAttack_BeforeStartTimer(seed)
timer.Simple(self.BeforeMeleeAttackSounds_WaitTime, function() if IsValid(self) then self:PlaySoundSystem("BeforeMeleeAttack") end end)
self.NextAlertSoundT = curTime + 0.4
if self.DisableMeleeAttackAnimation == false then
self.CurrentAttackAnimation = VJ_PICK(self.AnimTbl_MeleeAttack)
self.CurrentAttackAnimationDuration = self:DecideAnimationLength(self.CurrentAttackAnimation, false, self.MeleeAttackAnimationDecreaseLengthAmount)
if self.MeleeAttackAnimationAllowOtherTasks == false then -- Useful for gesture-based attacks
self.PlayingAttackAnimation = true
timer.Create("timer_act_playingattack"..self:EntIndex(), self.CurrentAttackAnimationDuration, 1, function() self.PlayingAttackAnimation = false end)
end
self:VJ_ACT_PLAYACTIVITY(self.CurrentAttackAnimation,false,0,false,self.MeleeAttackAnimationDelay,{SequenceDuration=self.CurrentAttackAnimationDuration})
end
if self.TimeUntilMeleeAttackDamage == false then
finishAttack[VJ_ATTACK_MELEE](self)
else -- If it's not event based...
timer.Create("timer_melee_start"..self:EntIndex(), self.TimeUntilMeleeAttackDamage / self:GetPlaybackRate(), self.MeleeAttackReps, function() if self.CurAttackSeed == seed then self:MeleeAttackCode() end end)
if self.MeleeAttackExtraTimers then
for k, t in ipairs(self.MeleeAttackExtraTimers) do
self:DoAddExtraAttackTimers("timer_melee_start"..curTime + k, t, function() if self.CurAttackSeed == seed then self:MeleeAttackCode() end end)
end
end
end
self:CustomOnMeleeAttack_AfterStartTimer(seed)
end
end
else -- No Enemy
self.DoingWeaponAttack = false
self.DoingWeaponAttack_Standing = false
if !self.Alerted && self.DidWeaponAttackAimParameter && !plyControlled then
self:ClearPoseParameters()
self.DidWeaponAttackAimParameter = false
end
if self:GetArrivalActivity() == self.CurrentWeaponAnimation then
self:SetArrivalActivity(self.CurrentAnim_IdleStand)
end
eneData.TimeSinceAcquired = 0
if eneData.Reset == false && (!self.IsVJBaseSNPC_Tank) then self:PlaySoundSystem("LostEnemy") eneData.Reset = true self:ResetEnemy(true) end
end
-- Guarding Position
if self.IsGuard == true && self.IsFollowing == false then
if self.GuardingPosition == nil then -- If it hasn't been set then set the guard position to its current position
self.GuardingPosition = myPos
self.GuardingFacePosition = myPos + self:GetForward()*51
end
-- If it's far from the guarding position, then go there!
if !self:IsMoving() && self:BusyWithActivity() == false then
local dist = myPos:Distance(self.GuardingPosition) -- Distance to the guard position
if dist > 50 then
self:SetLastPosition(self.GuardingPosition)
self:VJ_TASK_GOTO_LASTPOS(dist <= 800 and "TASK_WALK_PATH" or "TASK_RUN_PATH", function(x) x.CanShootWhenMoving = true x.ConstantlyFaceEnemy = true
x.RunCode_OnFinish = function()
timer.Simple(0.01, function()
if IsValid(self) && !self:IsMoving() && self:BusyWithActivity() == false && self.GuardingFacePosition != nil then
self:SetLastPosition(self.GuardingFacePosition)
self:VJ_TASK_FACE_X("TASK_FACE_LASTPOSITION")
end
end)
end
end)
end
end
end
end
else -- AI Not enabled
self.DoingWeaponAttack = false
end
self:NextThink(curTime + (0.069696968793869 + FrameTime()))
return true
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:MeleeAttackCode(customEnt)
if self.Dead or self.vACT_StopAttacks or self.Flinching or self.AttackType == VJ_ATTACK_GRENADE or (self.StopMeleeAttackAfterFirstHit && self.AttackStatus == VJ_ATTACK_STATUS_EXECUTED_HIT) then return end
local curEnemy = customEnt or self:GetEnemy()
if self.MeleeAttackAnimationFaceEnemy then self:FaceCertainEntity(curEnemy, true) end
//self.MeleeAttacking = true
self:CustomOnMeleeAttack_BeforeChecks()
if self.DisableDefaultMeleeAttackCode then return end
local myPos = self:GetPos()
local hitRegistered = false
for _, v in ipairs(ents.FindInSphere(self:GetMeleeAttackDamageOrigin(), self.MeleeAttackDamageDistance)) do
if (self.VJ_IsBeingControlled && self.VJ_TheControllerBullseye == v) or (v:IsPlayer() && v.IsControlingNPC == true) then continue end -- If controlled and v is the bullseye OR it's a player controlling then don't damage!
if v != self && v:GetClass() != self:GetClass() && (((v:IsNPC() or (v:IsPlayer() && v:Alive() && !VJ_CVAR_IGNOREPLAYERS)) && self:Disposition(v) != D_LI) or IsProp(v) == true or v:GetClass() == "func_breakable_surf" or destructibleEnts[v:GetClass()] or v.VJ_AddEntityToSNPCAttackList == true) && self:GetSightDirection():Dot((Vector(v:GetPos().x, v:GetPos().y, 0) - Vector(myPos.x, myPos.y, 0)):GetNormalized()) > math_cos(math_rad(self.MeleeAttackDamageAngleRadius)) then
local vProp = IsProp(v)
if self:CustomOnMeleeAttack_AfterChecks(v, vProp) == true then continue end
-- Knockback
if self.HasMeleeAttackKnockBack && v.MovementType != VJ_MOVETYPE_STATIONARY && (!v.VJ_IsHugeMonster or v.IsVJBaseSNPC_Tank) then
v:SetGroundEntity(NULL)
v:SetVelocity(self:MeleeAttackKnockbackVelocity(v))
end
-- Apply actual damage
if !self.DisableDefaultMeleeAttackDamageCode then
local applyDmg = DamageInfo()
applyDmg:SetDamage(self:VJ_GetDifficultyValue(self.MeleeAttackDamage))
applyDmg:SetDamageType(self.MeleeAttackDamageType)
//applyDmg:SetDamagePosition(self:VJ_GetNearestPointToEntity(v).MyPosition)
if v:IsNPC() or v:IsPlayer() then applyDmg:SetDamageForce(self:GetForward() * ((applyDmg:GetDamage() + 100) * 70)) end
applyDmg:SetInflictor(self)
applyDmg:SetAttacker(self)
v:TakeDamageInfo(applyDmg, self)
end
if v:IsPlayer() then
v:ViewPunch(Angle(math.random(-1, 1) * self.MeleeAttackDamage, math.random(-1, 1) * self.MeleeAttackDamage, math.random(-1, 1) * self.MeleeAttackDamage))
end
VJ_DestroyCombineTurret(self,v)
if !vProp then -- Only for non-props...
hitRegistered = true
end
end
end
if self.AttackStatus < VJ_ATTACK_STATUS_EXECUTED then
self.AttackStatus = VJ_ATTACK_STATUS_EXECUTED
if self.TimeUntilMeleeAttackDamage != false then
finishAttack[VJ_ATTACK_MELEE](self)
end
end
if hitRegistered == true then
self:PlaySoundSystem("MeleeAttack")
self.AttackStatus = VJ_ATTACK_STATUS_EXECUTED_HIT
else
self:CustomOnMeleeAttack_Miss()
self:PlaySoundSystem("MeleeAttackMiss", {}, VJ_EmitSound)
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:ThrowGrenadeCode(customEnt, noOwner)
if self.Dead or self.Flinching == true or self.AttackType == VJ_ATTACK_MELEE /*or (IsValid(self:GetEnemy()) && !self:Visible(self:GetEnemy()))*/ then return end
//if self:VJ_ForwardIsHidingZone(self:NearestPoint(self:GetPos() + self:OBBCenter()),self:GetEnemy():EyePos()) == true then return end
noOwner = noOwner or false
local getIsCustom = false
local gerModel = VJ_PICK(self.GrenadeAttackModel)
local gerClass = self.GrenadeAttackEntity
local gerFussTime = self.GrenadeAttackFussTime
local eneData = self.EnemyData
if IsValid(self:GetEnemy()) && !eneData.IsVisible then
if self:VisibleVec(eneData.LastVisiblePos) && self:GetEnemy():GetPos():Distance(eneData.LastVisiblePos) <= 600 then
self:FaceCertainPosition(eneData.LastVisiblePos)
else
return
end
end
if self.DisableGrenadeAttackAnimation == false then
self.CurrentAttackAnimation = VJ_PICK(self.AnimTbl_GrenadeAttack)
self.CurrentAttackAnimationDuration = self:DecideAnimationLength(self.CurrentAttackAnimation, false, 0.2)
self.PlayingAttackAnimation = true
timer.Create("timer_act_playingattack"..self:EntIndex(), self.CurrentAttackAnimationDuration, 1, function() self.PlayingAttackAnimation = false end)
self:VJ_ACT_PLAYACTIVITY(self.CurrentAttackAnimation, self.GrenadeAttackAnimationStopAttacks, self:DecideAnimationLength(self.CurrentAttackAnimation, self.GrenadeAttackAnimationStopAttacksTime), true, self.GrenadeAttackAnimationDelay, {PlayBackRateCalculated=true})
end
if IsValid(customEnt) then -- Custom nernagner gamal nernagner vor yete bidi nede
local getSpawnPos = self.GrenadeAttackAttachment
local getSpawnAngle;
if getSpawnPos == false then
getSpawnPos = self:CustomOnGrenadeAttack_SpawnPosition()
getSpawnAngle = getSpawnPos:Angle()
else
getSpawnPos = self:GetAttachment(self:LookupAttachment(self.GrenadeAttackAttachment)).Pos
getSpawnAngle = self:GetAttachment(self:LookupAttachment(self.GrenadeAttackAttachment)).Ang
end
getIsCustom = true
gerModel = customEnt:GetModel()
gerClass = customEnt:GetClass()
customEnt:SetMoveType(MOVETYPE_NONE)
customEnt:SetParent(self)
if self.GrenadeAttackAttachment == false then
customEnt:SetPos(getSpawnPos)
else
customEnt:Fire("SetParentAttachment", self.GrenadeAttackAttachment)
end
customEnt:SetAngles(getSpawnAngle)
if gerClass == "obj_vj_grenade" then
gerFussTime = math.abs(customEnt.FussTime - customEnt.TimeSinceSpawn)
elseif gerClass == "obj_handgrenade" or gerClass == "obj_spore" then
gerFussTime = 1
elseif gerClass == "npc_grenade_frag" or gerClass == "doom3_grenade" or gerClass == "fas2_thrown_m67" or gerClass == "cw_grenade_thrown" or gerClass == "cw_flash_thrown" or gerClass == "cw_smoke_thrown" then
gerFussTime = 1.5
elseif gerClass == "obj_cpt_grenade" then
gerFussTime = 2
end
end
if !IsValid(self:GetEnemy()) then
local sideCheck = VJ_PICK(self:VJ_CheckAllFourSides(200, true))
if sideCheck then
self:FaceCertainPosition(sideCheck, self.CurrentAttackAnimationDuration or 1.5)
doit = true
end
end
self.AttackType = VJ_ATTACK_GRENADE
self.ThrowingGrenade = true
self:CustomOnGrenadeAttack_BeforeStartTimer()
self:PlaySoundSystem("GrenadeAttack")
timer.Simple(self.TimeUntilGrenadeIsReleased, function()
if getIsCustom == true && !IsValid(customEnt) then return end
if IsValid(customEnt) then customEnt.VJ_IsPickedUpDanger = false customEnt:Remove() end
if IsValid(self) && !self.Dead /*&& IsValid(self:GetEnemy())*/ then -- Yete SNPC ter artoon e...
local getSpawnPos = self.GrenadeAttackAttachment
local getSpawnAngle;
if getSpawnPos == false then
getSpawnPos = self:CustomOnGrenadeAttack_SpawnPosition()
getSpawnAngle = getSpawnPos:Angle()
else
getSpawnPos = self:GetAttachment(self:LookupAttachment(self.GrenadeAttackAttachment)).Pos
getSpawnAngle = self:GetAttachment(self:LookupAttachment(self.GrenadeAttackAttachment)).Ang
end
local greTargetPos = self:GetPos() + self:GetForward()*200
if IsValid(self:GetEnemy()) then
eneData = self.EnemyData
if !eneData.IsVisible && self:VisibleVec(eneData.LastVisiblePos) && self:GetEnemy():GetPos():Distance(eneData.LastVisiblePos) <= 600 then
greTargetPos = eneData.LastVisiblePos
self:FaceCertainPosition(greTargetPos, self.CurrentAttackAnimationDuration - self.TimeUntilGrenadeIsReleased)
else
greTargetPos = self:GetEnemy():GetPos()
end
else -- Yete teshnami chooni, nede amenan lav goghme
if !IsValid(self:GetEnemy()) then
local test = self:VJ_CheckAllFourSides(200, true)
local sideCheck = VJ_PICK(test)
if sideCheck then
greTargetPos = sideCheck
self:FaceCertainPosition(sideCheck, self.CurrentAttackAnimationDuration - self.TimeUntilGrenadeIsReleased)
end
end
end
local gent = ents.Create(gerClass)
if noOwner == false then gent:SetOwner(self) end
gent:SetPos(getSpawnPos)
gent:SetAngles(getSpawnAngle)
if gerModel then gent:SetModel(Model(gerModel)) end
local getThrowVel = self:CustomOnGrenadeAttack_ThrowVelocity(gent, greTargetPos, getSpawnPos)
-- Set the timers for all the different grenade entities
if gerClass == "obj_vj_grenade" then
gent.FussTime = gerFussTime
elseif gerClass == "obj_cpt_grenade" then
gent:SetTimer(gerFussTime)
elseif gerClass == "obj_spore" then
gent:SetGrenade(true)
elseif gerClass == "ent_hl1_grenade" then
gent:ShootTimed(customEnt, getThrowVel, gerFussTime)
elseif gerClass == "doom3_grenade" or gerClass == "obj_handgrenade" then
gent:SetExplodeDelay(gerFussTime)
elseif gerClass == "cw_grenade_thrown" or gerClass == "cw_flash_thrown" or gerClass == "cw_smoke_thrown" then
gent:SetOwner(self)
gent:Fuse(gerFussTime)
end
gent:Spawn()
gent:Activate()
if gerClass == "npc_grenade_frag" then gent:Input("SetTimer",self:GetOwner(),self:GetOwner(),gerFussTime) end
local phys = gent:GetPhysicsObject()
if IsValid(phys) then
phys:Wake()
phys:AddAngleVelocity(Vector(math.Rand(500, 500), math.Rand(500, 500), math.Rand(500, 500)))
phys:SetVelocity(getThrowVel)
end
self:CustomOnGrenadeAttack_OnThrow(gent)
end
if self.AttackType == VJ_ATTACK_GRENADE then
self.AttackType = VJ_ATTACK_NONE
end
self.ThrowingGrenade = false
end)
end
---------------------------------------------------------------------------------------------------------------------------------------------
/* Old system (Replaced by condition system)
local getSdHint = sound.GetLoudestSoundHint
local sdBitSource = bit.bor(SOUND_DANGER, SOUND_CONTEXT_REACT_TO_SOURCE) ---> Combine dropship impact position, Combine gunship turret impact position, Strider minigun impact position
local sdBitCombine = bit.bor(SOUND_DANGER, SOUND_CONTEXT_EXCLUDE_COMBINE) ---> Flechette impact position, Strider foot impact position
local sdBitPlyVehicle = bit.bor(SOUND_DANGER, SOUND_CONTEXT_PLAYER_VEHICLE) ---> Player driving a vehicle
local sdBitMortar = bit.bor(SOUND_DANGER, SOUND_CONTEXT_MORTAR) ---> Combine mortars impact position
--
function ENT:GetPossibleDangers()
local myPos = self:GetPos()
return getSdHint(SOUND_DANGER, myPos) or getSdHint(sdBitSource, myPos) or getSdHint(sdBitCombine, myPos) or getSdHint(sdBitPlyVehicle, myPos) or getSdHint(sdBitMortar, myPos)
end
*/
---------------------------------------------------------------------------------------------------------------------------------------------
--[[---------------------------------------------------------
3 types of danger detections:
- ent.VJ_IsDetectableGrenade
- Detected as a grenade
- Distance based on self.DangerDetectionDistance
- Ignores grenades from allies
- BEST USE: Grenade type of entities
- ent.VJ_IsDetectableDanger
- Detected as a danger
- Distance based on self.DangerDetectionDistance
- Ignores dangers from allies
- BEST USE: Entities that should NOT scare the owner's allies, commonly used for projectiles
- NPC Conditions (Old system: sound.EmitHint)
- Detected as a danger
- Distance based on the sound hint's volume/distance
- Does NOT ignore, is detected by everyone that catches the hint, including allies
- BEST USE: Sounds that should scare the owner's allies
-----------------------------------------------------------]]
function ENT:CheckForDangers()
if !self.CanDetectDangers or self.AttackType == VJ_ATTACK_GRENADE or self.NextDangerDetectionT > CurTime() or self.VJ_IsBeingControlled then return end
local regDangerDetected = false -- A regular non-grenade danger has been found (This is done to make sure grenades take priority over other dangers!)
for _, v in ipairs(ents.FindInSphere(self:GetPos(), self.DangerDetectionDistance)) do
if (v.VJ_IsDetectableDanger or v.VJ_IsDetectableGrenade) && self:Visible(v) then
local vOwner = v:GetOwner()
if !(IsValid(vOwner) && vOwner.IsVJBaseSNPC && ((self:GetClass() == vOwner:GetClass()) or (self:Disposition(vOwner) == D_LI))) then
if v.VJ_IsDetectableDanger then regDangerDetected = true continue end -- If it's a regular danger then just skip it for now
self:PlaySoundSystem("OnGrenadeSight")
self.NextDangerDetectionT = CurTime() + 4
self.TakingCoverT = CurTime() + 4
-- If has the ability to throw it back, then throw the grenade!
if self.CanThrowBackDetectedGrenades && self.HasGrenadeAttack && v.VJ_IsPickupableDanger && !v.VJ_IsPickedUpDanger && v:GetVelocity():Length() < 400 && self:VJ_GetNearestPointToEntityDistance(v) < 100 then
self.NextGrenadeAttackSoundT = CurTime() + 3
self:ThrowGrenadeCode(v, true)
v.VJ_IsPickedUpDanger = true
//v:Remove()
return
end
self:VJ_TASK_COVER_FROM_ORIGIN("TASK_RUN_PATH", function(x)
x.CanShootWhenMoving = true
x.ConstantlyFaceEnemy = true
end)
return
end
end
end
if regDangerDetected or self:HasCondition(COND_HEAR_DANGER) or self:HasCondition(COND_HEAR_PHYSICS_DANGER) or self:HasCondition(COND_HEAR_MOVE_AWAY) then
self:PlaySoundSystem("OnDangerSight")
self.NextDangerDetectionT = CurTime() + 4
self.TakingCoverT = CurTime() + 4
self:VJ_TASK_COVER_FROM_ORIGIN("TASK_RUN_PATH", function(x)
x.CanShootWhenMoving = true
x.ConstantlyFaceEnemy = true
end)
return
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:StopAttacks(checkTimers)
if self:Health() <= 0 then return end
if self.VJ_DEBUG == true && GetConVar("vj_npc_printstoppedattacks"):GetInt() == 1 then print(self:GetClass().." Stopped all Attacks!") end
if checkTimers == true && self.AttackType == VJ_ATTACK_MELEE && self.AttackStatus < VJ_ATTACK_STATUS_EXECUTED then
finishAttack[VJ_ATTACK_MELEE](self, true)
end
self.AttackType = VJ_ATTACK_NONE
self.AttackStatus = VJ_ATTACK_STATUS_DONE
self.CurAttackSeed = 0
self.MeleeAttacking = false
self:DoChaseAnimation()
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:WeaponAimPoseParameters(resetPoses) self:DoPoseParameterLooking(resetPoses) end -- !!!!!!!!!!!!!! DO NOT USE THIS FUNCTION !!!!!!!!!!!!!! [Backwards Compatibility!]
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:DoPoseParameterLooking(resetPoses)
if (self.HasPoseParameterLooking == false) or (self.VJ_IsBeingControlled == false && self.DoingWeaponAttack == false) then return end
resetPoses = resetPoses or false
//self:GetPoseParameters(true)
local ent = (self.VJ_IsBeingControlled and self.VJ_TheController) or self:GetEnemy()
local p_enemy = 0 -- Pitch
local y_enemy = 0 -- Yaw
local r_enemy = 0 -- Roll
if IsValid(ent) && !resetPoses then
local enemy_pos = (self.VJ_IsBeingControlled and self.VJ_TheControllerBullseye:GetPos()) or ent:GetPos() + ent:OBBCenter()
local self_ang = self:GetAngles()
local enemy_ang = (enemy_pos - (self:GetPos() + self:OBBCenter())):Angle()
p_enemy = math_angDif(enemy_ang.p, self_ang.p)
if self.PoseParameterLooking_InvertPitch == true then p_enemy = -p_enemy end
y_enemy = math_angDif(enemy_ang.y, self_ang.y)
if self.PoseParameterLooking_InvertYaw == true then y_enemy = -y_enemy end
r_enemy = math_angDif(enemy_ang.z, self_ang.z)
if self.PoseParameterLooking_InvertRoll == true then r_enemy = -r_enemy end
elseif !self.PoseParameterLooking_CanReset then -- Should it reset its pose parameters if there is no enemies?
return
end
self:CustomOn_PoseParameterLookingCode(p_enemy, y_enemy, r_enemy)
local names = self.PoseParameterLooking_Names
for x = 1, #names.pitch do
self:SetPoseParameter(names.pitch[x], math_angApproach(self:GetPoseParameter(names.pitch[x]), p_enemy, self.PoseParameterLooking_TurningSpeed))
end
for x = 1, #names.yaw do
self:SetPoseParameter(names.yaw[x], math_angApproach(self:GetPoseParameter(names.yaw[x]), y_enemy, self.PoseParameterLooking_TurningSpeed))
end
for x = 1, #names.roll do
self:SetPoseParameter(names.roll[x], math_angApproach(self:GetPoseParameter(names.roll[x]), r_enemy, self.PoseParameterLooking_TurningSpeed))
end
self.DidWeaponAttackAimParameter = true
end
--------------------------------------------------------------------------------------------------------------------------------------------
function ENT:DoWeaponAttackMovementCode(override, moveType)
override = override or false -- Overrides some of the checks, only used for the internal task system!
moveType = moveType or 0 -- This is used with override | 0 = Run, 1 = Walk
if (self.CurrentWeaponEntity.IsMeleeWeapon) then
self.DoingWeaponAttack = true
elseif self.HasShootWhileMoving == true then
if self.EnemyData.IsVisible && self:IsAbleToShootWeapon(true, false) == true && ((self:IsMoving() && (self.CurrentSchedule != nil && self.CurrentSchedule.CanShootWhenMoving == true)) or (override == true)) then
if (override == true && moveType == 0) or (self.CurrentSchedule != nil && self.CurrentSchedule.MoveType == 1) then
local anim = self:TranslateToWeaponAnim(VJ_PICK(self.AnimTbl_ShootWhileMovingRun))
if VJ_AnimationExists(self,anim) == true then
self.DoingWeaponAttack = true
self.DoingWeaponAttack_Standing = false
self:CapabilitiesAdd(bit.bor(CAP_MOVE_SHOOT))
self:SetMovementActivity(anim)
self:SetArrivalActivity(self.CurrentWeaponAnimation)
end
elseif (override == true && moveType == 1) or (self.CurrentSchedule != nil && self.CurrentSchedule.MoveType == 0) then
local anim = self:TranslateToWeaponAnim(VJ_PICK(self.AnimTbl_ShootWhileMovingWalk))
if VJ_AnimationExists(self,anim) == true then
self.DoingWeaponAttack = true
self.DoingWeaponAttack_Standing = false
self:CapabilitiesAdd(bit.bor(CAP_MOVE_SHOOT))
self:SetMovementActivity(anim)
self:SetArrivalActivity(self.CurrentWeaponAnimation)
end
end
end
else -- Can't move shoot!
self:CapabilitiesRemove(CAP_MOVE_SHOOT) -- Remove the capability if it can't even move-shoot
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:IsAbleToShootWeapon(checkDistance, checkDistanceOnly, enemyDist)
checkDistance = checkDistance or false -- Check for distance and weapon time as well?
checkDistanceOnly = checkDistanceOnly or false -- Should it only check the above statement?
enemyDist = enemyDist or self:EyePos():Distance(self:GetEnemy():EyePos()) -- Distance used for checkDistance
if self:CustomOnIsAbleToShootWeapon() == false then return end
local hasDist = false
local hasChecks = false
if self:GetWeaponState() == VJ_WEP_STATE_HOLSTERED or self.vACT_StopAttacks then return false end
if self.VJ_IsBeingControlled then checkDistance = false checkDistanceOnly = false end
if checkDistance == true && CurTime() > self.NextWeaponAttackT && enemyDist < self.Weapon_FiringDistanceFar && ((enemyDist > self.Weapon_FiringDistanceClose) or self.CurrentWeaponEntity.IsMeleeWeapon) then
hasDist = true
end
if checkDistanceOnly == true then
if hasDist == true then
return true
else
return false
end
end
if IsValid(self:GetActiveWeapon()) && self.AttackType != VJ_ATTACK_GRENADE && self:BusyWithActivity() == false && ((self:GetActiveWeapon().IsMeleeWeapon) or (self:GetWeaponState() != VJ_WEP_STATE_RELOADING && self.AttackType != VJ_ATTACK_MELEE && self.NearestPointToEnemyDistance > self.MeleeAttackDistance)) then
hasChecks = true
if checkDistance == false then return true end
end
if checkDistanceOnly == false && hasDist == true && hasChecks == true then return true end
return false
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:SelectSchedule()
if self.VJ_IsBeingControlled then return end
self:CustomOnSchedule()
if self.DisableSelectSchedule == true or self.Dead then return end
local ene = self:GetEnemy()
-- Idle Behavior --
if !IsValid(ene) then
self:IdleSoundCode()
if self.AttackType != VJ_ATTACK_GRENADE then
self:DoIdleAnimation()
end
if self.Alerted == false then
self.TakingCoverT = 0
end
self.NoWeapon_UseScaredBehavior_Active = false
-- Combat Behavior --
else
local wep = self:GetActiveWeapon()
local myPos = self:GetPos()
-- If the enemy is in sight then continue
if self.LatestEnemyDistance < self:GetMaxLookDistance() then
self:IdleSoundCode()
-- Check for weapon validity
if !IsValid(wep) then
-- Scared behavior system
if self.NoWeapon_UseScaredBehavior then
if !self:IsBusy() && CurTime() > self.NextChaseTime then
self.NoWeapon_UseScaredBehavior_Active = true -- Tells the idle system to use the scared behavior animation
if self.IsFollowing == false && self.EnemyData.IsVisible then
self:VJ_TASK_COVER_FROM_ENEMY("TASK_RUN_PATH")
return
end
end
elseif self.HasMeleeAttack then -- If it doesn't do scared behavior, then make it chase the enemy if it can melee!
self.NoWeapon_UseScaredBehavior_Active = false -- In case it was scared, return it back to normal
self.NextDangerDetectionT = CurTime() + 4 -- Ignore dangers while chasing!
self:DoChaseAnimation()
return
end
self:DoIdleAnimation(2)
return
end
self.NoWeapon_UseScaredBehavior_Active = false -- In case it was scared, return it back to normal
local enePos_Eye = ene:EyePos()
local eneDist_Eye = self:EyePos():Distance(enePos_Eye)
local myPosCentered = myPos + self:OBBCenter()
local canAttack = true
-- Back away from the enemy if it's to close
if self.HasWeaponBackAway == true && (!wep.IsMeleeWeapon) && self.LatestEnemyDistance <= self.WeaponBackAway_Distance && CurTime() > self.TakingCoverT && CurTime() > self.NextChaseTime && self.AttackType == VJ_ATTACK_NONE && !self.IsFollowing && !self.VJ_PlayingSequence && ene.Behavior != VJ_BEHAVIOR_PASSIVE && self:VJ_ForwardIsHidingZone(self:NearestPoint(myPosCentered), enePos_Eye) == false then
local moveCheck = VJ_PICK(self:VJ_CheckAllFourSides(200, true, "0111"))
if moveCheck then
self:SetLastPosition(moveCheck)
if self:GetWeaponState() == VJ_WEP_STATE_RELOADING then self:SetWeaponState() end
self.TakingCoverT = CurTime() + 2
canAttack = false
self:VJ_TASK_GOTO_LASTPOS("TASK_RUN_PATH", function(x) x:EngTask("TASK_FACE_ENEMY", 0) x.CanShootWhenMoving = true x.ConstantlyFaceEnemy = true end)
end
end
if canAttack && self:IsAbleToShootWeapon(false, false, eneDist_Eye) == true && self:GetState() != VJ_STATE_ONLY_ANIMATION_NOATTACK then
-- Enemy to far away or not allowed to fire a weapon
if eneDist_Eye > self.Weapon_FiringDistanceFar or CurTime() < self.NextWeaponAttackT then
self:DoChaseAnimation()
self.AllowToDo_WaitForEnemyToComeOut = false
-- Check if enemy is in sight, then continue...
elseif self:IsAbleToShootWeapon(true, true, eneDist_Eye) == true then
//self:VJ_ForwardIsHidingZone(self:EyePos(), enePos_Eye, true, {Debug=true})
-- If I can't see the enemy then either wait for it or charge at the enemy
if self:VJ_ForwardIsHidingZone(self:EyePos(), enePos_Eye, true) == true && self:VJ_ForwardIsHidingZone(self:NearestPoint(myPosCentered) + self:GetUp()*30, enePos_Eye + self:GetUp()*30, true) /*or self:VJ_ForwardIsHidingZone(util.VJ_GetWeaponPos(self),enePos_Eye) == true*/ /*or (!self.EnemyData.IsVisible)*/ then
if self:GetWeaponState() != VJ_WEP_STATE_RELOADING then
-- Wait for the enemy to come out
if self.WaitForEnemyToComeOut && !self.WaitingForEnemyToComeOut && (!wep.IsMeleeWeapon) && self.AllowToDo_WaitForEnemyToComeOut && ((CurTime() - self.Weapon_TimeSinceLastShot) <= 4.5) && (eneDist_Eye > self.WaitForEnemyToComeOutDistance) then
self.WaitingForEnemyToComeOut = true
if self.HasLostWeaponSightAnimation == true then
self:VJ_ACT_PLAYACTIVITY(self.AnimTbl_WeaponAim, false, 0, true)
end
self.NextChaseTime = CurTime() + math.Rand(self.WaitForEnemyToComeOutTime.a, self.WaitForEnemyToComeOutTime.b)
-- If I am not supposed to wait for the enemy, then go after the enemy!
elseif /*self.DisableChasingEnemy == false &&*/ CurTime() > self.LastHiddenZoneT then
self.DoingWeaponAttack = false
self.DoingWeaponAttack_Standing = false
self:DoChaseAnimation()
end
end
else -- I can see the enemy...
self.AllowToDo_WaitForEnemyToComeOut = true
if (wep.IsVJBaseWeapon) then -- VJ Base weapons
self:FaceCertainEntity(ene, true)
local noAttack = false
// self:DoChaseAnimation()
-- if covered, try to move forward by calculating the distance between the prop and the NPC
local cover_npc, cover_npc_tr = self:VJ_ForwardIsHidingZone(self:NearestPoint(myPosCentered), enePos_Eye, false, {SetLastHiddenTime=true})
local cover_npc_ent = cover_npc_tr.Entity
local cover_wep, cover_wep_tr = self:VJ_ForwardIsHidingZone(wep:GetNW2Vector("VJ_CurBulletPos"), enePos_Eye, false)
local cover_wep_ent = cover_wep_tr.Entity
//print("Is covered? ", cover_npc)
//print("Is gun covered? ", cover_wep)
local cover_npc_isObj = true -- The covered entity is NOT an NPC / Player
if cover_npc == false or (IsValid(cover_npc_ent) and (cover_npc_ent:IsNPC() or cover_npc_ent:IsPlayer())) then
cover_npc_isObj = false
end
if !wep.IsMeleeWeapon then
-- If friendly in line of fire, then move!
if !cover_npc_isObj && self.DoingWeaponAttack_Standing == true && CurTime() > self.TakingCoverT && IsValid(cover_wep_ent) && cover_wep_ent:IsNPC() && cover_wep_ent != self && (self:Disposition(cover_wep_ent) == D_LI or self:Disposition(cover_wep_ent) == D_NU) && cover_wep_tr.HitPos:Distance(cover_wep_tr.StartPos) <= 3000 then
local moveCheck = VJ_PICK(self:VJ_CheckAllFourSides(50, true, "0011"))
if moveCheck then
self:StopMoving()
if self.IsGuard then self.GuardingPosition = moveCheck end -- Set the guard position to this new position that avoids friendly fire
self:SetLastPosition(moveCheck)
self.NextChaseTime = CurTime() + 1
self:VJ_TASK_GOTO_LASTPOS("TASK_WALK_PATH", function(x) x:EngTask("TASK_FACE_ENEMY", 0) x.CanShootWhenMoving = true x.ConstantlyFaceEnemy = true end)
end
end
-- If the NPC is behind cover...
if cover_npc == true then
self.WeaponUseEnemyEyePos = true -- Make the bullet direction go towards the head of the enemy
-- Behind cover and I am taking cover, don't fire!
if CurTime() < self.TakingCoverT then
noAttack = true
elseif CurTime() > self.NextMoveOnGunCoveredT && ((cover_npc_tr.HitPos:Distance(myPos) > 150 && cover_npc_isObj == true) or (cover_wep == true && !cover_wep_ent:IsNPC() && !cover_wep_ent:IsPlayer())) then
local nearestPos;
local enePos;
if IsValid(cover_npc_ent) then
nearestPos, nearestEnePos = self:VJ_GetNearestPointToEntity(cover_npc_ent, true)
else
nearestPos, nearestEnePos = self:VJ_GetNearestPointToVector(cover_npc_tr.HitPos, true)
end
enePos = nearestEnePos - self:GetForward()*15
if nearestPos:Distance(enePos) <= (self.IsGuard and 60 or 1000) then
if self.IsGuard then self.GuardingPosition = enePos end -- Set the guard position to this new position that provides cover
self:SetLastPosition(enePos)
//VJ_CreateTestObject(enePos, self:GetAngles(), Color(0,255,255))
local vsched = ai_vj_schedule.New("vj_goto_cover")
vsched:EngTask("TASK_GET_PATH_TO_LASTPOSITION", 0)
vsched:EngTask("TASK_WAIT_FOR_MOVEMENT", 0)
vsched.IsMovingTask = true
vsched.ConstantlyFaceEnemy = true
vsched.StopScheduleIfNotMoving_Any = true
local coverRunAnim = self:TranslateToWeaponAnim(VJ_PICK(self.AnimTbl_MoveToCover))
if VJ_AnimationExists(self, coverRunAnim) == true then
self:SetMovementActivity(coverRunAnim)
else
vsched.CanShootWhenMoving = true
vsched.MoveType = 1
end
self:StartSchedule(vsched)
//self:VJ_TASK_GOTO_LASTPOS("TASK_WALK_PATH",function(x) x:EngTask("TASK_FACE_ENEMY", 0) x.CanShootWhenMoving = true x.ConstantlyFaceEnemy = true end)
end
self.NextMoveOnGunCoveredT = CurTime() + 2
end
else -- NPC not covered
self.WeaponUseEnemyEyePos = false -- Make the bullet direction go towards the center of the enemy rather then its head
end
end
if noAttack == false && CurTime() > self.NextWeaponAttackT && CurTime() > self.NextWeaponAttackT_Base /*&& self.DoingWeaponAttack == false*/ then
-- Melee weapons
if (wep.IsMeleeWeapon) then
self:CustomOnWeaponAttack()
local finalAnim = self:TranslateToWeaponAnim(VJ_PICK(self.AnimTbl_WeaponAttack))
if CurTime() > self.NextMeleeWeaponAttackT && VJ_AnimationExists(self, finalAnim) == true /*&& VJ_IsCurrentAnimation(self, finalAnim) == false*/ then
local animDur = VJ_GetSequenceDuration(self, finalAnim)
wep.NPC_NextPrimaryFire = animDur -- Make melee weapons dynamically change the next primary fire
VJ_EmitSound(self, wep.NPC_BeforeFireSound, wep.NPC_BeforeFireSoundLevel, math.Rand(wep.NPC_BeforeFireSoundPitch.a, wep.NPC_BeforeFireSoundPitch.b))
self.NextMeleeWeaponAttackT = CurTime() + animDur
self.CurrentWeaponAnimation = finalAnim
self:VJ_ACT_PLAYACTIVITY(finalAnim, false, false, true)
self.DoingWeaponAttack = true
end
-- Normal ranged weapons
else
local hasAmmo = wep:Clip1() > 0 // AllowWeaponReloading
if !hasAmmo && !self.CurrentWeaponAnimationIsAim then
self.CurrentWeaponAnimation = -1
end
-- If the current animation is already a firing animation, then just tell the base it's already firing and do NOT restart the animation
if VJ_IsCurrentAnimation(self, self:TranslateToWeaponAnim(self.CurrentWeaponAnimation)) == true then
self.DoingWeaponAttack = true
self.DoingWeaponAttack_Standing = true
-- If the current activity isn't the last weapon animation and it's not a transition, then continue
elseif self:GetActivity() != self.CurrentWeaponAnimation && self:GetActivity() != ACT_TRANSITION then
self:CustomOnWeaponAttack()
self.WaitingForEnemyToComeOut = false
self.Weapon_TimeSinceLastShot = CurTime()
//self.NextMoveRandomlyWhenShootingT = CurTime() + 2
local finalAnim;
-- Check if the NPC has ammo
if !hasAmmo then
finalAnim = self:TranslateToWeaponAnim(VJ_PICK(self.AnimTbl_WeaponAim))
self.CurrentWeaponAnimationIsAim = true
else
local anim_crouch = self:TranslateToWeaponAnim(VJ_PICK(self.AnimTbl_WeaponAttackCrouch))
if self.CanCrouchOnWeaponAttack == true && cover_npc == false && cover_wep == false && eneDist_Eye > 500 && VJ_AnimationExists(self, anim_crouch) == true && ((math.random(1, self.CanCrouchOnWeaponAttackChance) == 1) or (CurTime() <= self.Weapon_DoingCrouchAttackT)) && self:VJ_ForwardIsHidingZone(wep:GetNW2Vector("VJ_CurBulletPos") + self:GetUp()*-18, enePos_Eye, false) == false then
finalAnim = anim_crouch
self.Weapon_DoingCrouchAttackT = CurTime() + 2 -- Asiga bedke vor vestah elank yed votgi cheler hemen
else -- Not crouching
finalAnim = self:TranslateToWeaponAnim(VJ_PICK(self.AnimTbl_WeaponAttack))
end
end
if VJ_AnimationExists(self, finalAnim) == true && ((VJ_IsCurrentAnimation(self, finalAnim) == false) or (!self.DoingWeaponAttack)) then
VJ_EmitSound(self, wep.NPC_BeforeFireSound, wep.NPC_BeforeFireSoundLevel, math.Rand(wep.NPC_BeforeFireSoundPitch.a, wep.NPC_BeforeFireSoundPitch.b))
self.CurrentWeaponAnimation = finalAnim
self.NextWeaponAttackT_Base = CurTime() + 0.2
self:VJ_ACT_PLAYACTIVITY(finalAnim, false, 0, true)
self.DoingWeaponAttack = true
self.DoingWeaponAttack_Standing = true
end
end
end
end
-- Move randomly when shooting
if self.MoveRandomlyWhenShooting && cover_npc == false && !self.IsGuard && !self.IsFollowing && (!wep.IsMeleeWeapon) && (!wep.NPC_StandingOnly) && self.DoingWeaponAttack && self.DoingWeaponAttack_Standing && CurTime() > self.NextMoveRandomlyWhenShootingT && (CurTime() - self.EnemyData.TimeSinceAcquired) > 2 && (eneDist_Eye < (self.Weapon_FiringDistanceFar / 1.25)) && self:VJ_ForwardIsHidingZone(self:NearestPoint(myPosCentered), enePos_Eye) == false then
if self:CustomOnMoveRandomlyWhenShooting() != false then
local moveCheck = VJ_PICK(self:VJ_CheckAllFourSides(math.random(150, 400), true, "0111"))
if moveCheck then
self:StopMoving()
self:SetLastPosition(moveCheck)
self:VJ_TASK_GOTO_LASTPOS(VJ_PICK({"TASK_RUN_PATH", "TASK_WALK_PATH"}), function(x) x:EngTask("TASK_FACE_ENEMY", 0) x.CanShootWhenMoving = true x.ConstantlyFaceEnemy = true end)
end
end
self.NextMoveRandomlyWhenShootingT = CurTime() + math.Rand(self.NextMoveRandomlyWhenShootingTime1, self.NextMoveRandomlyWhenShootingTime2)
end
else -- None VJ Base weapons
self:FaceCertainEntity(ene, true)
self.WaitingForEnemyToComeOut = false
self.DoingWeaponAttack = true
self.DoingWeaponAttack_Standing = true
self:CustomOnWeaponAttack()
self.Weapon_TimeSinceLastShot = CurTime()
//wep:SetClip1(99999)
self:VJ_SetSchedule(SCHED_RANGE_ATTACK1)
end
end
end
end
else -- Not in sight, reset the enemy
self:ResetEnemy(false)
end
self.LatestEnemyDistance = ene:GetPos():Distance(myPos)
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:ResetEnemy(checkAlliesEnemy)
if /*self.NextResetEnemyT > CurTime() or*/ self.Dead then self.EnemyData.Reset = false return false end
checkAlliesEnemy = checkAlliesEnemy or false
local moveToEnemy = false
local ene = self:GetEnemy()
local eneValid = IsValid(ene)
if checkAlliesEnemy == true then
local eneData = self.EnemyData
local getAllies = self:Allies_Check(1000)
if getAllies != false then
for _, v in ipairs(getAllies) do
local allyEne = v:GetEnemy()
if IsValid(allyEne) && (CurTime() - v.EnemyData.LastVisibleTime) < self.TimeUntilEnemyLost && VJ_IsAlive(allyEne) && self:CheckRelationship(allyEne) == D_HT && self:GetPos():Distance(allyEne:GetPos()) <= self:GetMaxLookDistance() then
self:VJ_DoSetEnemy(allyEne, false)
eneData.Reset = false
return false
end
end
end
local curEnemies = eneData.VisibleCount //self.CurrentReachableEnemies
-- If the current number of reachable enemies is higher then 1, then don't reset
if (eneValid && (curEnemies - 1) >= 1) or (!eneValid && curEnemies >= 1) then
//self:VJ_DoSetEnemy(v, false, true)
self:SetupRelationships() -- Select a new enemy
self.NextProcessT = CurTime() + self.NextProcessTime
eneData.Reset = false
return false
end
end
self:SetNPCState(NPC_STATE_ALERT)
timer.Create("timer_alerted_reset"..self:EntIndex(), math.Rand(self.AlertedToIdleTime.a, self.AlertedToIdleTime.b), 1, function() if !IsValid(self:GetEnemy()) then self.Alerted = false self:SetNPCState(NPC_STATE_IDLE) end end)
self:CustomOnResetEnemy()
if self.VJ_DEBUG == true && GetConVar("vj_npc_printresetenemy"):GetInt() == 1 then print(self:GetName().." has reseted its enemy") end
if eneValid then
if self.IsFollowing == false && self.VJ_PlayingSequence == false && (!self.IsVJBaseSNPC_Tank) && self:GetEnemyLastKnownPos() != defPos then
self:SetLastPosition(self:GetEnemyLastKnownPos())
moveToEnemy = true
end
self:MarkEnemyAsEluded(ene)
//self:ClearEnemyMemory(ene) // Completely resets the enemy memory
self:AddEntityRelationship(ene, D_NU, 10)
end
self.LastHiddenZone_CanWander = CurTime() > self.LastHiddenZoneT and true or false
self.LastHiddenZoneT = 0
-- Clear memory of the enemy if it's not a player AND it's dead
if eneValid && !ene:IsPlayer() && !VJ_IsAlive(ene) then
//print("Clear memory", ene)
self:ClearEnemyMemory(ene)
end
//self:UpdateEnemyMemory(self,self:GetPos())
//local vsched = ai_vj_schedule.New("vj_act_resetenemy")
//if eneValid then vsched:EngTask("TASK_FORGET", ene) end
//vsched:EngTask("TASK_IGNORE_OLD_ENEMIES", 0)
self.NextWanderTime = CurTime() + math.Rand(3, 5)
-- This is needed for the human base because when taking cover from enemy, the AI can get stuck in a loop (EX: When self.NoWeapon_UseScaredBehavior_Active is true!)
local curSched = self.CurrentSchedule
if (curSched != nil && (curSched.Name == "vj_cover_from_enemy" or curSched.Name == "vj_cover_from_enemy_fail")) then
self:StopMoving()
end
if !self:IsBusy() && !self.IsGuard && self.Behavior != VJ_BEHAVIOR_PASSIVE && self.Behavior != VJ_BEHAVIOR_PASSIVE_NATURE && self.VJ_IsBeingControlled == false && moveToEnemy == true && self.LastHiddenZone_CanWander == true && !self.NoWeapon_UseScaredBehavior_Active then
//ParticleEffect("explosion_turret_break", self.LatestEnemyPosition, Angle(0,0,0))
self:SetMovementActivity(VJ_PICK(self.AnimTbl_Walk))
local vsched = ai_vj_schedule.New("vj_act_resetenemy")
vsched:EngTask("TASK_GET_PATH_TO_LASTPOSITION", 0)
//vsched:EngTask("TASK_WALK_PATH", 0)
vsched:EngTask("TASK_WAIT_FOR_MOVEMENT", 0)
vsched.ResetOnFail = true
vsched.CanShootWhenMoving = true
vsched.ConstantlyFaceEnemy = true
vsched.CanBeInterrupted = true
vsched.IsMovingTask = true
vsched.MoveType = 0
//self.NextIdleTime = CurTime() + 10
self:StartSchedule(vsched)
end
//if vsched.TaskCount > 0 then
//self:StartSchedule(vsched)
//end
self:SetEnemy(NULL)
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:OnTakeDamage(dmginfo)
local dmgInflictor = dmginfo:GetInflictor()
local hitgroup = self:GetLastDamageHitGroup()
if IsValid(dmgInflictor) && dmgInflictor:GetClass() == "prop_ragdoll" && dmgInflictor:GetVelocity():Length() <= 100 then return 0 end -- Avoid taking damage when walking on ragdolls
self:CustomOnTakeDamage_BeforeImmuneChecks(dmginfo, hitgroup)
if self.GodMode or dmginfo:GetDamage() <= 0 then return 0 end
if self:IsOnFire() && self:WaterLevel() == 2 then self:Extinguish() end -- If we are in water, then extinguish the fire
local dmgAttacker = dmginfo:GetAttacker()
local dmgType = dmginfo:GetDamageType()
local curTime = CurTime()
local isFireDmg = self:IsOnFire() && IsValid(dmgInflictor) && IsValid(dmgAttacker) && dmgInflictor:GetClass() == "entityflame" && dmgAttacker:GetClass() == "entityflame"
-- If it should always take damage from huge monsters, then skip immunity checks!
if self.GetDamageFromIsHugeMonster && dmgAttacker.VJ_IsHugeMonster then
goto skip_immunity
end
if VJ_HasValue(self.ImmuneDamagesTable, dmgType) then return 0 end
if !self.AllowIgnition && isFireDmg then self:Extinguish() return 0 end
if self.Immune_Fire == true && (dmgType == DMG_BURN or dmgType == DMG_SLOWBURN or isFireDmg) then return 0 end
if (self.Immune_AcidPoisonRadiation == true && (dmgType == DMG_ACID or dmgType == DMG_RADIATION or dmgType == DMG_POISON or dmgType == DMG_NERVEGAS or dmgType == DMG_PARALYZE)) or (self.Immune_Bullet == true && (dmginfo:IsBulletDamage() or dmgType == DMG_BULLET or dmgType == DMG_AIRBOAT or dmgType == DMG_BUCKSHOT)) or (self.Immune_Blast == true && (dmgType == DMG_BLAST or dmgType == DMG_BLAST_SURFACE)) or (self.Immune_Dissolve == true && dmgType == DMG_DISSOLVE) or (self.Immune_Electricity == true && (dmgType == DMG_SHOCK or dmgType == DMG_ENERGYBEAM or dmgType == DMG_PHYSGUN)) or (self.Immune_Melee == true && (dmgType == DMG_CLUB or dmgType == DMG_SLASH)) or (self.Immune_Physics == true && dmgType == DMG_CRUSH) or (self.Immune_Sonic == true && dmgType == DMG_SONIC) then return 0 end
if (IsValid(dmgInflictor) && dmgInflictor:GetClass() == "prop_combine_ball") or (IsValid(dmgAttacker) && dmgAttacker:GetClass() == "prop_combine_ball") then
if self.Immune_Dissolve == true then return 0 end
-- Make sure combine ball does reasonable damage and doesn't spam it!
if curTime > self.NextCanGetCombineBallDamageT then
dmginfo:SetDamage(math.random(400, 500))
dmginfo:SetDamageType(DMG_DISSOLVE)
self.NextCanGetCombineBallDamageT = curTime + 0.2
else
return 0
end
end
::skip_immunity::
local function DoBleed()
if self.Bleeds == true then
self:CustomOnTakeDamage_OnBleed(dmginfo, hitgroup)
-- Spawn the blood particle only if it's not caused by the default fire entity [Causes the damage position to be at Vector(0, 0, 0)]
if self.HasBloodParticle == true && !isFireDmg then self:SpawnBloodParticles(dmginfo, hitgroup) end
if self.HasBloodDecal == true then self:SpawnBloodDecal(dmginfo, hitgroup) end
self:PlaySoundSystem("Impact", nil, VJ_EmitSound)
end
end
if self.Dead then DoBleed() return 0 end -- If dead then just bleed but take no damage
self:CustomOnTakeDamage_BeforeDamage(dmginfo, hitgroup)
if dmginfo:GetDamage() <= 0 then return 0 end -- Only take damage if it's above 0!
-- Why? Because GMod resets/randomizes dmginfo after a tick...
self.SavedDmgInfo = {
dmginfo = dmginfo, -- The actual CTakeDamageInfo object | WARNING: Can be corrupted after a tick, recommended not to use this!
attacker = dmginfo:GetAttacker(),
inflictor = dmginfo:GetInflictor(),
amount = dmginfo:GetDamage(),
pos = dmginfo:GetDamagePosition(),
type = dmginfo:GetDamageType(),
force = dmginfo:GetDamageForce(),
ammoType = dmginfo:GetAmmoType(),
hitgroup = hitgroup,
}
self:SetHealth(self:Health() - dmginfo:GetDamage())
if self.VJ_DEBUG == true && GetConVar("vj_npc_printondamage"):GetInt() == 1 then print(self:GetClass().." Got Damaged! | Amount = "..dmginfo:GetDamage()) end
if self.HasHealthRegeneration == true && self.HealthRegenerationResetOnDmg == true then
self.HealthRegenerationDelayT = curTime + (math.Rand(self.HealthRegenerationDelay.a, self.HealthRegenerationDelay.b) * 1.5)
end
self:SetSaveValue("m_iDamageCount", self:GetSaveTable().m_iDamageCount + 1)
self:SetSaveValue("m_flLastDamageTime", curTime)
self:CustomOnTakeDamage_AfterDamage(dmginfo, hitgroup)
DoBleed()
-- I/O events, from: https://github.com/ValveSoftware/source-sdk-2013/blob/0d8dceea4310fde5706b3ce1c70609d72a38efdf/sp/src/game/server/ai_basenpc.cpp#L764
if IsValid(dmgAttacker) then
self:TriggerOutput("OnDamaged", dmgAttacker)
self:MarkTookDamageFromEnemy(dmgAttacker)
else
self:TriggerOutput("OnDamaged", self)
end
local stillAlive = self:Health() > 0
if stillAlive then self:PlaySoundSystem("Pain") end
if GetConVar("ai_disabled"):GetInt() == 0 && self:GetState() != VJ_STATE_FREEZE then
-- Make passive NPCs move away | RESULT: May move away AND may cause other passive NPCs to move as well
if (self.Behavior == VJ_BEHAVIOR_PASSIVE or self.Behavior == VJ_BEHAVIOR_PASSIVE_NATURE) && curTime > self.TakingCoverT then
if stillAlive && self.Passive_RunOnDamage then
self:VJ_TASK_COVER_FROM_ORIGIN("TASK_RUN_PATH")
end
if self.Passive_AlliesRunOnDamage then -- Make passive allies run too!
local allies = self:Allies_Check(self.Passive_AlliesRunOnDamageDistance)
if allies != false then
for _, v in ipairs(allies) do
v.TakingCoverT = curTime + math.Rand(v.Passive_NextRunOnDamageTime.b, v.Passive_NextRunOnDamageTime.a)
v:VJ_TASK_COVER_FROM_ORIGIN("TASK_RUN_PATH")
v:PlaySoundSystem("Alert")
end
end
end
self.TakingCoverT = curTime + math.Rand(self.Passive_NextRunOnDamageTime.a, self.Passive_NextRunOnDamageTime.b)
end
if stillAlive then
self:DoFlinch(dmginfo, hitgroup)
-- React to damage by a player
-- 0 = Run it every time | 1 = Run it only when friendly to player | 2 = Run it only when enemy to player
if self.HasDamageByPlayer && dmgAttacker:IsPlayer() && curTime > self.NextDamageByPlayerT && self:Visible(dmgAttacker) && (self.DamageByPlayerDispositionLevel == 0 or (self.DamageByPlayerDispositionLevel == 1 && (self:Disposition(dmgAttacker) == D_LI or self:Disposition(dmgAttacker) == D_NU)) or (self.DamageByPlayerDispositionLevel == 2 && self:Disposition(dmgAttacker) != D_HT)) then
self:CustomOnDamageByPlayer(dmginfo, hitgroup)
self:PlaySoundSystem("DamageByPlayer")
self.NextDamageByPlayerT = curTime + math.Rand(self.DamageByPlayerTime.a, self.DamageByPlayerTime.b)
end
self:PlaySoundSystem("Pain")
-- Move or hide when damaged by an enemy | RESULT: May play a hiding animation OR may move to take cover from enemy
if self.MoveOrHideOnDamageByEnemy && self.Behavior != VJ_BEHAVIOR_PASSIVE && self.Behavior != VJ_BEHAVIOR_PASSIVE_NATURE && IsValid(self:GetEnemy()) && curTime > self.NextMoveOrHideOnDamageByEnemyT && !self.IsFollowing && self.AttackType == VJ_ATTACK_NONE && curTime > self.TakingCoverT && self.EnemyData.IsVisible && self.VJ_IsBeingControlled == false && self:GetWeaponState() != VJ_WEP_STATE_RELOADING && self:EyePos():Distance(self:GetEnemy():EyePos()) < self.Weapon_FiringDistanceFar then
local wep = self:GetActiveWeapon()
if !self.MoveOrHideOnDamageByEnemy_OnlyMove && self:VJ_ForwardIsHidingZone(self:NearestPoint(self:GetPos() + self:OBBCenter()) ,self:GetEnemy():EyePos()) == true then
local anim = VJ_PICK(self:TranslateToWeaponAnim(VJ_PICK(self.AnimTbl_TakingCover)))
if VJ_AnimationExists(self, anim) == true then
local hideTime = math.Rand(self.MoveOrHideOnDamageByEnemy_HideTime.a, self.MoveOrHideOnDamageByEnemy_HideTime.b)
self:VJ_ACT_PLAYACTIVITY(anim, false, hideTime, false, 0, {SequenceDuration=hideTime}) -- Don't set stopActivities because we want it to shoot if an enemy is suddenly visible!
self.NextChaseTime = curTime + hideTime
self.TakingCoverT = curTime + hideTime
self.DoingWeaponAttack = false
end
self.NextMoveOrHideOnDamageByEnemyT = curTime + math.random(self.NextMoveOrHideOnDamageByEnemy1, self.NextMoveOrHideOnDamageByEnemy2)
elseif !self:IsMoving() && (!IsValid(wep) or (IsValid(wep) && !wep.IsMeleeWeapon)) then -- Run away if not moving AND has a non-melee weapon
self:VJ_TASK_COVER_FROM_ENEMY("TASK_RUN_PATH", function(x) x.CanShootWhenMoving = true x.ConstantlyFaceEnemy = true end)
self.NextMoveOrHideOnDamageByEnemyT = curTime + math.random(self.NextMoveOrHideOnDamageByEnemy1, self.NextMoveOrHideOnDamageByEnemy2)
end
end
-- Call for back on damage | RESULT: May play an animation OR it may move away, AND it may bring allies to its location
if self.CallForBackUpOnDamage && curTime > self.NextCallForBackUpOnDamageT && self.AttackType != VJ_ATTACK_GRENADE && !IsValid(self:GetEnemy()) && self.IsFollowing == false && self.Behavior != VJ_BEHAVIOR_PASSIVE && self.Behavior != VJ_BEHAVIOR_PASSIVE_NATURE && !isFireDmg then
local allies = self:Allies_Check(self.CallForBackUpOnDamageDistance)
if allies != false then
self:Allies_Bring("Diamond", self.CallForBackUpOnDamageDistance, allies, self.CallForBackUpOnDamageLimit)
self:ClearSchedule()
self.NextFlinchT = curTime + 1
local chosenAnim = VJ_PICK(self.CallForBackUpOnDamageAnimation)
local playedAnim = !self.DisableCallForBackUpOnDamageAnimation and self:VJ_ACT_PLAYACTIVITY(chosenAnim, true, self:DecideAnimationLength(chosenAnim, self.CallForBackUpOnDamageAnimationTime), true, 0, {PlayBackRateCalculated=true}) or 0
if playedAnim == 0 && !self:BusyWithActivity() then
self:VJ_TASK_COVER_FROM_ORIGIN("TASK_RUN_PATH", function(x) x.CanShootWhenMoving = true x.ConstantlyFaceEnemy = true end)
end
self.NextCallForBackUpOnDamageT = curTime + math.Rand(self.NextCallForBackUpOnDamageTime.a, self.NextCallForBackUpOnDamageTime.b)
end
end
-- Become enemy to a friendly player | RESULT: May become alerted
if self.BecomeEnemyToPlayer == true && self.VJ_IsBeingControlled == false && dmgAttacker:IsPlayer() && self:CheckRelationship(dmgAttacker) == D_LI then
self.AngerLevelTowardsPlayer = self.AngerLevelTowardsPlayer + 1
if self.AngerLevelTowardsPlayer > self.BecomeEnemyToPlayerLevel then
if self:Disposition(dmgAttacker) != D_HT then
self:CustomOnBecomeEnemyToPlayer(dmginfo, hitgroup)
if self.IsFollowing == true && self.FollowData.Ent == dmgAttacker then self:FollowReset() end
self.VJ_AddCertainEntityAsEnemy[#self.VJ_AddCertainEntityAsEnemy + 1] = dmgAttacker
self:AddEntityRelationship(dmgAttacker, D_HT, 2)
self.TakingCoverT = curTime + 2
self:PlaySoundSystem("BecomeEnemyToPlayer")
if !IsValid(self:GetEnemy()) then
self:StopMoving()
self:SetTarget(dmgAttacker)
self:VJ_TASK_FACE_X("TASK_FACE_TARGET")
end
if self.AllowPrintingInChat == true then
dmgAttacker:PrintMessage(HUD_PRINTTALK, self:GetName().." no longer likes you.")
end
end
self.Alerted = true
self:SetNPCState(NPC_STATE_ALERT)
end
end
-- Attempt to find who damaged me | RESULT: May become alerted if enemy is visible OR it may move away
if !self.DisableTakeDamageFindEnemy && !self:BusyWithActivity() && !IsValid(self:GetEnemy()) && curTime > self.TakingCoverT && self.VJ_IsBeingControlled == false && self.Behavior != VJ_BEHAVIOR_PASSIVE && self.Behavior != VJ_BEHAVIOR_PASSIVE_NATURE then // self.Alerted == false
local eneFound = false
local sightDist = self:GetMaxLookDistance()
sightDist = math_clamp(sightDist / 2, sightDist <= 1000 and sightDist or 1000, sightDist)
-- IF normal sight dist is less than 1000 then change nothing, OR ELSE use half the distance with 1000 as minimum
for _, v in ipairs(ents.FindInSphere(self:GetPos(), sightDist)) do
if (curTime - self.EnemyData.TimeSet) > 2 && self:Visible(v) && self:CheckRelationship(v) == D_HT then
self:CustomOnSetEnemyOnDamage(dmginfo, hitgroup)
self.NextCallForHelpT = curTime + 1
self:VJ_DoSetEnemy(v, true)
self:DoChaseAnimation()
eneFound = true
break
end
end
if !eneFound && self.HideOnUnknownDamage && !self.IsFollowing && self.MovementType != VJ_MOVETYPE_STATIONARY then
self:VJ_TASK_COVER_FROM_ORIGIN("TASK_RUN_PATH", function(x) x.CanShootWhenMoving = true x.ConstantlyFaceEnemy = true end)
self.TakingCoverT = curTime + self.HideOnUnknownDamage
end
end
end
end
-- If eating, stop!
if self.CanEat && self.VJTags[VJ_TAG_EATING] then
self.EatingData.NextCheck = curTime + 15
self:EatingReset("Injured")
end
if self:Health() <= 0 && !self.Dead then
self:RemoveEFlags(EFL_NO_DISSOLVE)
if (dmginfo:IsDamageType(DMG_DISSOLVE)) or (IsValid(dmgInflictor) && dmgInflictor:GetClass() == "prop_combine_ball") then
local dissolve = DamageInfo()
dissolve:SetDamage(self:Health())
dissolve:SetAttacker(dmgAttacker)
dissolve:SetDamageType(DMG_DISSOLVE)
self:TakeDamageInfo(dissolve)
end
self:PriorToKilled(dmginfo, hitgroup)
end
return 1
end
---------------------------------------------------------------------------------------------------------------------------------------------
local vecZ500 = Vector(0, 0, 500)
local vecZ4 = Vector(0, 0, 4)
--
function ENT:PriorToKilled(dmginfo, hitgroup)
self:CustomOnInitialKilled(dmginfo, hitgroup)
if self.Medic_Status then self:DoMedicReset() end
local dmgInflictor = dmginfo:GetInflictor()
local dmgAttacker = dmginfo:GetAttacker()
local allies = self:Allies_Check(math.max(800, self.BringFriendsOnDeathDistance, self.AlertFriendsOnDeathDistance))
if allies != false then
local noAlert = true -- Don't run the AlertFriendsOnDeath if we have BringFriendsOnDeath enabled!
if self.BringFriendsOnDeath == true then
self:Allies_Bring("Diamond", self.BringFriendsOnDeathDistance, allies, self.BringFriendsOnDeathLimit, true)
noAlert = false
end
local doBecomeEnemyToPlayer = (self.BecomeEnemyToPlayer == true && dmgAttacker:IsPlayer() && GetConVar("ai_disabled"):GetInt() == 0 && !VJ_CVAR_IGNOREPLAYERS) or false
local it = 0 -- Number of allies that have been alerted
for _, v in ipairs(allies) do
v:CustomOnAllyDeath(self)
v:PlaySoundSystem("AllyDeath")
-- AlertFriendsOnDeath
if noAlert == true && self.AlertFriendsOnDeath == true && !IsValid(v:GetEnemy()) && v.AlertFriendsOnDeath == true && it != self.AlertFriendsOnDeathLimit && self:GetPos():Distance(v:GetPos()) < self.AlertFriendsOnDeathDistance && (!IsValid(v:GetActiveWeapon()) or (IsValid(v:GetActiveWeapon()) && !(v:GetActiveWeapon().IsMeleeWeapon))) then
it = it + 1
v:FaceCertainPosition(self:GetPos(), 1)
v:VJ_ACT_PLAYACTIVITY(VJ_PICK(v.AnimTbl_AlertFriendsOnDeath))
v.NextIdleTime = CurTime() + math.Rand(5, 8)
end
-- BecomeEnemyToPlayer
if doBecomeEnemyToPlayer && v.BecomeEnemyToPlayer == true && v:Disposition(dmgAttacker) == D_LI then
v.AngerLevelTowardsPlayer = v.AngerLevelTowardsPlayer + 1
if v.AngerLevelTowardsPlayer > v.BecomeEnemyToPlayerLevel then
if v:Disposition(dmgAttacker) != D_HT then
v:CustomOnBecomeEnemyToPlayer(dmginfo, hitgroup)
if v.IsFollowing == true && v.FollowData.Ent == dmgAttacker then v:FollowReset() end
v.VJ_AddCertainEntityAsEnemy[#v.VJ_AddCertainEntityAsEnemy+1] = dmgAttacker
v:AddEntityRelationship(dmgAttacker,D_HT,2)
if v.AllowPrintingInChat == true then
dmgAttacker:PrintMessage(HUD_PRINTTALK, v:GetName().." no longer likes you.")
end
v:PlaySoundSystem("BecomeEnemyToPlayer")
end
v.Alerted = true
end
end
end
end
local function DoKilled()
if IsValid(self) then
if self.WaitBeforeDeathTime == 0 then
self:OnKilled(dmginfo, hitgroup)
else
timer.Simple(self.WaitBeforeDeathTime, function()
if IsValid(self) then
self:OnKilled(dmginfo, hitgroup)
end
end)
end
end
end
-- Blood decal on the ground
if self.Bleeds == true && self.HasBloodDecal == true then
local bloodDecal = VJ_PICK(self.CustomBlood_Decal)
if bloodDecal != false then
local decalPos = self:GetPos() + vecZ4
self:SetLocalPos(decalPos) -- NPC is too close to the ground, we need to move it up a bit
local tr = util.TraceLine({
start = decalPos,
endpos = decalPos - vecZ500,
filter = self
})
util.Decal(bloodDecal, tr.HitPos + tr.HitNormal, tr.HitPos - tr.HitNormal)
end
end
self.Dead = true
if self.IsFollowing == true then self:FollowReset() end
self:RemoveTimers()
self.AttackType = VJ_ATTACK_NONE
self.MeleeAttacking = false
self.HasMeleeAttack = false
self:StopAllCommonSounds()
if IsValid(dmgAttacker) then
if dmgAttacker:GetClass() == "npc_barnacle" then self.HasDeathRagdoll = false end -- Don't make a corpse if it's killed by a barnacle!
if GetConVar("vj_npc_addfrags"):GetInt() == 1 && dmgAttacker:IsPlayer() then dmgAttacker:AddFrags(1) end
if IsValid(dmgInflictor) then
gamemode.Call("OnNPCKilled", self, dmgAttacker, dmgInflictor, dmginfo)
end
end
self:CustomOnPriorToKilled(dmginfo, hitgroup)
self:SetCollisionGroup(1)
self:RunGibOnDeathCode(dmginfo, hitgroup)
self:PlaySoundSystem("Death")
//self:AA_StopMoving()
-- I/O events, from: https://github.com/ValveSoftware/source-sdk-2013/blob/0d8dceea4310fde5706b3ce1c70609d72a38efdf/mp/src/game/server/basecombatcharacter.cpp#L1582
if IsValid(dmgAttacker) then -- Someone else killed me
self:TriggerOutput("OnDeath", dmgAttacker)
dmgAttacker:Fire("KilledNPC", "", 0, self, self) -- Allows player companions (npc_citizen) to respond to kill
else
self:TriggerOutput("OnDeath", self)
end
if self.HasDeathAnimation == true && !dmginfo:IsDamageType(DMG_REMOVENORAGDOLL) then
if IsValid(dmgInflictor) && dmgInflictor:GetClass() == "prop_combine_ball" then DoKilled() return end
if GetConVar("vj_npc_nodeathanimation"):GetInt() == 0 && GetConVar("ai_disabled"):GetInt() == 0 && !dmginfo:IsDamageType(DMG_DISSOLVE) && math.random(1, self.DeathAnimationChance) == 1 then
self:RemoveAllGestures()
self:CustomDeathAnimationCode(dmginfo, hitgroup)
local chosenAnim = VJ_PICK(self.AnimTbl_Death)
local animTime = self:DecideAnimationLength(chosenAnim, self.DeathAnimationTime) - self.DeathAnimationDecreaseLengthAmount
self:VJ_ACT_PLAYACTIVITY(chosenAnim, true, animTime, false, 0, {PlayBackRateCalculated=true})
self.DeathAnimationCodeRan = true
timer.Simple(animTime, DoKilled)
else
DoKilled()
end
else
DoKilled()
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:OnKilled(dmginfo, hitgroup)
if self.VJ_DEBUG == true && GetConVar("vj_npc_printdied"):GetInt() == 1 then print(self:GetClass().." Died!") end
self:CustomOnKilled(dmginfo, hitgroup)
self:RunItemDropsOnDeathCode(dmginfo, hitgroup) -- Item drops on death
self:ClearEnemyMemory()
//self:ClearSchedule()
//self:SetNPCState(NPC_STATE_DEAD)
if bit.band(self.SavedDmgInfo.type, DMG_REMOVENORAGDOLL) == 0 then self:DropWeaponOnDeathCode(dmginfo, hitgroup) self:CreateDeathCorpse(dmginfo, hitgroup) end
self:Remove()
end
---------------------------------------------------------------------------------------------------------------------------------------------
local colorGrey = Color(90, 90, 90)
--
function ENT:CreateDeathCorpse(dmginfo, hitgroup)
-- In case it was not set
-- NOTE: dmginfo at this point can be incorrect/corrupted, but its better than leaving the self.SavedDmgInfo empty!
if !self.SavedDmgInfo then
self.SavedDmgInfo = {
dmginfo = dmginfo, -- The actual CTakeDamageInfo object | WARNING: Can be corrupted after a tick, recommended not to use this!
attacker = dmginfo:GetAttacker(),
inflictor = dmginfo:GetInflictor(),
amount = dmginfo:GetDamage(),
pos = dmginfo:GetDamagePosition(),
type = dmginfo:GetDamageType(),
force = dmginfo:GetDamageForce(),
ammoType = dmginfo:GetAmmoType(),
hitgroup = hitgroup,
}
end
self:CustomOnDeath_BeforeCorpseSpawned(dmginfo, hitgroup)
if self.HasDeathRagdoll == true then
local corpseMdl = self:GetModel()
local corpseMdlCustom = VJ_PICK(self.DeathCorpseModel)
if corpseMdlCustom != false then corpseMdl = corpseMdlCustom end
local corpseType = "prop_physics"
if self.DeathCorpseEntityClass == "UseDefaultBehavior" then
if util.IsValidRagdoll(corpseMdl) == true then
corpseType = "prop_ragdoll"
elseif util.IsValidProp(corpseMdl) == false or util.IsValidModel(corpseMdl) == false then
if IsValid(self.TheDroppedWeapon) then self.TheDroppedWeapon:Remove() end
return false
end
else
corpseType = self.DeathCorpseEntityClass
end
//if self.VJCorpseDeleted == true then
self.Corpse = ents.Create(corpseType) //end
self.Corpse:SetModel(corpseMdl)
self.Corpse:SetPos(self:GetPos())
self.Corpse:SetAngles(self:GetAngles())
self.Corpse:Spawn()
self.Corpse:Activate()
self.Corpse:SetColor(self:GetColor())
self.Corpse:SetMaterial(self:GetMaterial())
if corpseMdlCustom == false && self.DeathCorpseSubMaterials != nil then -- Take care of sub materials
for _, x in ipairs(self.DeathCorpseSubMaterials) do
if self:GetSubMaterial(x) != "" then
self.Corpse:SetSubMaterial(x, self:GetSubMaterial(x))
end
end
-- This causes lag, not a very good way to do it.
/*for x = 0, #self:GetMaterials() do
if self:GetSubMaterial(x) != "" then
self.Corpse:SetSubMaterial(x, self:GetSubMaterial(x))
end
end*/
end
//self.Corpse:SetName("self.Corpse" .. self:EntIndex())
//self.Corpse:SetModelScale(self:GetModelScale())
self.Corpse.FadeCorpseType = (self.Corpse:GetClass() == "prop_ragdoll" and "FadeAndRemove") or "kill"
self.Corpse.IsVJBaseCorpse = true
self.Corpse.DamageInfo = dmginfo
self.Corpse.ExtraCorpsesToRemove = self.ExtraCorpsesToRemove_Transition
self.Corpse.BloodData = {Color = self.BloodColor, Particle = self.CustomBlood_Particle, Decal = self.CustomBlood_Decal}
if self.Bleeds == true && self.HasBloodPool == true && GetConVar("vj_npc_nobloodpool"):GetInt() == 0 then
self:SpawnBloodPool(dmginfo, hitgroup)
end
-- Collision --
self.Corpse:SetCollisionGroup(self.DeathCorpseCollisionType)
if GetConVar("ai_serverragdolls"):GetInt() == 1 then
undo.ReplaceEntity(self, self.Corpse)
else -- Keep corpses is not enabled...
VJ_AddCorpse(self.Corpse)
//hook.Call("VJ_CreateSNPCCorpse", nil, self.Corpse, self)
if GetConVar("vj_npc_undocorpse"):GetInt() == 1 then undo.ReplaceEntity(self, self.Corpse) end -- Undoable
end
cleanup.ReplaceEntity(self, self.Corpse) -- Delete on cleanup
-- Miscellaneous --
self.Corpse:SetSkin((self.DeathCorpseSkin == -1 and self:GetSkin()) or self.DeathCorpseSkin)
if self.DeathCorpseSetBodyGroup == true then -- Yete asega true-e, ooremen gerna bodygroup tenel
for i = 0,18 do -- 18 = Bodygroup limit
self.Corpse:SetBodygroup(i,self:GetBodygroup(i))
end
if self.DeathCorpseBodyGroup.a != -1 then -- Yete asiga nevaz meg chene, user-in teradz tevere kordzadze
self.Corpse:SetBodygroup(self.DeathCorpseBodyGroup.a, self.DeathCorpseBodyGroup.b)
end
end
if self:IsOnFire() then -- If was on fire then...
self.Corpse:Ignite(math.Rand(8, 10), 0)
self.Corpse:SetColor(colorGrey)
//self.Corpse:SetMaterial("models/props_foliage/tree_deciduous_01a_trunk")
end
//gamemode.Call("CreateEntityRagdoll",self,self.Corpse)
-- Dissolve --
if (bit.band(self.SavedDmgInfo.type, DMG_DISSOLVE) != 0) or (IsValid(self.SavedDmgInfo.inflictor) && self.SavedDmgInfo.inflictor:GetClass() == "prop_combine_ball") then
self.Corpse:SetName("vj_dissolve_corpse")
local dissolver = ents.Create("env_entity_dissolver")
dissolver:SetPos(self.Corpse:GetPos())
dissolver:Spawn()
dissolver:Activate()
//dissolver:SetKeyValue("target","vj_dissolve_corpse")
dissolver:SetKeyValue("magnitude",100)
dissolver:SetKeyValue("dissolvetype",0)
dissolver:Fire("Dissolve","vj_dissolve_corpse")
if IsValid(self.TheDroppedWeapon) then
self.TheDroppedWeapon:SetName("vj_dissolve_weapon")
dissolver:Fire("Dissolve","vj_dissolve_weapon")
end
dissolver:Fire("Kill", "", 0.1)
//dissolver:Remove()
end
-- Bone and Angle --
-- If it's a bullet, it will use localized velocity on each bone depending on how far away the bone is from the dmg position
local useLocalVel = (bit.band(self.SavedDmgInfo.type, DMG_BULLET) != 0 and self.SavedDmgInfo.pos != defPos) or false
local dmgForce = (self.SavedDmgInfo.force / 40) + self:GetMoveVelocity() + self:GetVelocity()
if self.DeathAnimationCodeRan then
useLocalVel = false
dmgForce = self:GetMoveVelocity() == defPos and self:GetGroundSpeedVelocity() or self:GetMoveVelocity()
end
local totalSurface = 0
local physCount = self.Corpse:GetPhysicsObjectCount()
for boneLimit = 0, physCount - 1 do -- 128 = Bone Limit
local childphys = self.Corpse:GetPhysicsObjectNum(boneLimit)
if IsValid(childphys) then
totalSurface = totalSurface + childphys:GetSurfaceArea()
local childphys_bonepos, childphys_boneang = self:GetBonePosition(self.Corpse:TranslatePhysBoneToBone(boneLimit))
if (childphys_bonepos) then
//if math.Round(math.abs(childphys_boneang.r)) != 90 then -- Fixes ragdolls rotating, no longer needed! ---> sv_pvsskipanimation 0
if self.DeathCorpseSetBoneAngles == true then childphys:SetAngles(childphys_boneang) end
childphys:SetPos(childphys_bonepos)
//end
if self.Corpse:GetName() == "vj_dissolve_corpse" then
childphys:EnableGravity(false)
childphys:SetVelocity(self:GetForward()*-150 + self:GetRight()*math.Rand(100,-100) + self:GetUp()*50)
else
if self.DeathCorpseApplyForce == true /*&& self.DeathAnimationCodeRan == false*/ then
childphys:SetVelocity(dmgForce / math.max(1, (useLocalVel and childphys_bonepos:Distance(self.SavedDmgInfo.pos)/12) or 1))
end
end
elseif physCount == 1 then -- If it's only 1, then it's likely a regular physics model with no bones
if self.Corpse:GetName() == "vj_dissolve_corpse" then
childphys:EnableGravity(false)
childphys:SetVelocity(self:GetForward()*-150 + self:GetRight()*math.Rand(100,-100) + self:GetUp()*50)
else
if self.DeathCorpseApplyForce == true /*&& self.DeathAnimationCodeRan == false*/ then
childphys:SetVelocity(dmgForce / math.max(1, (useLocalVel and self.Corpse:GetPos():Distance(self.SavedDmgInfo.pos)/12) or 1))
end
end
end
end
end
if self.Corpse:Health() <= 0 then
local hpCalc = totalSurface / 60 // self.Corpse:OBBMaxs():Distance(self.Corpse:OBBMins())
self.Corpse:SetMaxHealth(hpCalc)
self.Corpse:SetHealth(hpCalc)
end
VJ_AddStinkyEnt(self.Corpse, true)
if IsValid(self.TheDroppedWeapon) then self.Corpse.ExtraCorpsesToRemove[#self.Corpse.ExtraCorpsesToRemove+1] = self.TheDroppedWeapon end
if self.DeathCorpseFade == true then self.Corpse:Fire(self.Corpse.FadeCorpseType,"",self.DeathCorpseFadeTime) end
if GetConVar("vj_npc_corpsefade"):GetInt() == 1 then self.Corpse:Fire(self.Corpse.FadeCorpseType,"",GetConVar("vj_npc_corpsefadetime"):GetInt()) end
self:CustomOnDeath_AfterCorpseSpawned(dmginfo, hitgroup, self.Corpse)
self.Corpse:CallOnRemove("vj_"..self.Corpse:EntIndex(),function(ent,exttbl)
for _,v in ipairs(exttbl) do
if IsValid(v) then
if v:GetClass() == "prop_ragdoll" then v:Fire("FadeAndRemove","",0) else v:Fire("kill","",0) end
end
end
end,self.Corpse.ExtraCorpsesToRemove)
hook.Call("CreateEntityRagdoll", nil, self, self.Corpse)
return self.Corpse
else
if IsValid(self.TheDroppedWeapon) then self.TheDroppedWeapon:Remove() end
for _,v in ipairs(self.ExtraCorpsesToRemove_Transition) do
if v.IsVJBase_Gib == true && v.RemoveOnCorpseDelete == true then
v:Remove()
end
end
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
local wepDropTbl1 = {weapon_ar2=true, weapon_vj_ar2=true, weapon_vj_blaster=true, weapon_pistol=true, weapon_vj_9mmpistol=true, weapon_vj_357=true, weapon_shotgun=true, weapon_vj_spas12=true, weapon_annabelle=true, weapon_rpg = true}
local wepDropTbl2 = {weapon_crowbar=true, weapon_stunstick=true}
local wepAng180y = Angle(0, 180, 0)
local wepAng90x = Angle(90, 0, 0)
--
function ENT:DropWeaponOnDeathCode(dmginfo, hitgroup)
if self.DropWeaponOnDeath != true or !IsValid(self:GetActiveWeapon()) /*or dmginfo:IsDamageType(DMG_DISSOLVE)*/ then return end
self:CustomOnDropWeapon(dmginfo, hitgroup)
-- Rotate the weapon for certain guns
self.CurrentWeaponEntity = self:GetActiveWeapon()
local gunang = defAng
if wepDropTbl1[self.CurrentWeaponEntity:GetClass()] == true then
gunang = wepAng180y
elseif wepDropTbl2[self.CurrentWeaponEntity:GetClass()] == true then
gunang = wepAng90x
end
local getAttach = false -- true = Found an attachment on the NPC's model
if !self.CurrentWeaponEntity.WorldModel_UseCustomPosition then
for _,v in ipairs(self:GetAttachments()) do
if v.name == self.DropWeaponOnDeathAttachment then
getAttach = self:GetAttachment(self:LookupAttachment(self.DropWeaponOnDeathAttachment))
break
end
end
end
self.TheDroppedWeapon = ents.Create(self.CurrentWeaponEntity:GetClass())
if getAttach != false then
self.TheDroppedWeapon:SetPos(getAttach.Pos)
else
self.TheDroppedWeapon:SetPos(self.CurrentWeaponEntity:GetPos())
end
if getAttach != false then
self.TheDroppedWeapon:SetAngles(getAttach.Ang + gunang)
else
self.TheDroppedWeapon:SetAngles(self.CurrentWeaponEntity:GetAngles() + gunang)
end
self.TheDroppedWeapon:Spawn()
self.TheDroppedWeapon:Activate()
local phys = self.TheDroppedWeapon:GetPhysicsObject()
if IsValid(phys) then
if (bit.band(self.SavedDmgInfo.type, DMG_DISSOLVE) != 0) or (IsValid(self.SavedDmgInfo.inflictor) && self.SavedDmgInfo.inflictor:GetClass() == "prop_combine_ball") then
phys:EnableGravity(false)
phys:SetVelocity(self:GetForward()*-150 + self:GetRight()*math.Rand(100,-100) + self:GetUp()*50)
else
local dmgForce = (self.SavedDmgInfo.force / 40) + self:GetMoveVelocity() + self:GetVelocity()
if self.DeathAnimationCodeRan then
dmgForce = self:GetMoveVelocity() == defPos and self:GetGroundSpeedVelocity() or self:GetMoveVelocity()
end
phys:SetMass(1)
phys:ApplyForceCenter(dmgForce)
end
end
self:CustomOnDropWeapon_AfterWeaponSpawned(dmginfo, hitgroup, self.TheDroppedWeapon)
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:PlaySoundSystem(sdSet, customSd, sdType)
if self.HasSounds == false or sdSet == nil then return end
sdType = sdType or VJ_CreateSound
local cTbl = VJ_PICK(customSd)
if sdSet == "GeneralSpeech" then -- Used to just play general speech sounds (Custom by developers)
if cTbl != false then
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT_RegularChange = CurTime() + ((((SoundDuration(cTbl) > 0) and SoundDuration(cTbl)) or 2) + 1)
self.CurrentGeneralSpeechSound = sdType(self, cTbl, 80, self:VJ_DecideSoundPitch(self.GeneralSoundPitch1, self.GeneralSoundPitch2))
end
return
elseif sdSet == "FollowPlayer" then
if self.HasFollowPlayerSounds_Follow == true then
local sdtbl = VJ_PICK(self.SoundTbl_FollowPlayer)
if (math.random(1, self.FollowPlayerSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT_RegularChange = CurTime() + math.random(3, 4)
self.CurrentFollowPlayerSound = sdType(self, sdtbl, self.FollowPlayerSoundLevel, self:VJ_DecideSoundPitch(self.FollowPlayerPitch.a, self.FollowPlayerPitch.b))
end
end
return
elseif sdSet == "UnFollowPlayer" then
if self.HasFollowPlayerSounds_UnFollow == true then
local sdtbl = VJ_PICK(self.SoundTbl_UnFollowPlayer)
if (math.random(1, self.UnFollowPlayerSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT_RegularChange = CurTime() + math.random(3, 4)
self.CurrentUnFollowPlayerSound = sdType(self, sdtbl, self.UnFollowPlayerSoundLevel, self:VJ_DecideSoundPitch(self.UnFollowPlayerPitch.a, self.UnFollowPlayerPitch.b))
end
end
return
elseif sdSet == "OnReceiveOrder" then
if self.HasOnReceiveOrderSounds == true then
local sdtbl = VJ_PICK(self.SoundTbl_OnReceiveOrder)
if (math.random(1, self.OnReceiveOrderSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT = self.NextIdleSoundT + 2
self.NextAlertSoundT = CurTime() + 2
self.CurrentOnReceiveOrderSound = sdType(self, sdtbl, self.OnReceiveOrderSoundLevel, self:VJ_DecideSoundPitch(self.OnReceiveOrderSoundPitch.a, self.OnReceiveOrderSoundPitch.b))
end
end
return
elseif sdSet == "MoveOutOfPlayersWay" then
if self.HasMoveOutOfPlayersWaySounds == true then
local sdtbl = VJ_PICK(self.SoundTbl_MoveOutOfPlayersWay)
if (math.random(1, self.MoveOutOfPlayersWaySoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT_RegularChange = CurTime() + math.random(3, 4)
self.CurrentMoveOutOfPlayersWaySound = sdType(self, sdtbl, self.MoveOutOfPlayersWaySoundLevel, self:VJ_DecideSoundPitch(self.MoveOutOfPlayersWaySoundPitch.a, self.MoveOutOfPlayersWaySoundPitch.b))
end
end
return
elseif sdSet == "MedicBeforeHeal" then
if self.HasMedicSounds_BeforeHeal == true then
local sdtbl = VJ_PICK(self.SoundTbl_MedicBeforeHeal)
if (math.random(1, self.MedicBeforeHealSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT_RegularChange = CurTime() + math.random(3, 4)
self.CurrentMedicBeforeHealSound = sdType(self, sdtbl, self.BeforeHealSoundLevel, self:VJ_DecideSoundPitch(self.BeforeHealSoundPitch.a, self.BeforeHealSoundPitch.b))
end
end
return
elseif sdSet == "MedicOnHeal" then
if self.HasMedicSounds_AfterHeal == true then
local sdtbl = VJ_PICK(self.SoundTbl_MedicAfterHeal)
if sdtbl == false then sdtbl = VJ_PICK(DefaultSoundTbl_MedicAfterHeal) end -- Default table
if (math.random(1, self.MedicAfterHealSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT_RegularChange = CurTime() + math.random(3, 4)
self.CurrentMedicAfterHealSound = sdType(self, sdtbl, self.AfterHealSoundLevel, self:VJ_DecideSoundPitch(self.AfterHealSoundPitch.a, self.AfterHealSoundPitch.b))
end
end
return
elseif sdSet == "MedicReceiveHeal" then
if self.HasMedicSounds_ReceiveHeal == true then
local sdtbl = VJ_PICK(self.SoundTbl_MedicReceiveHeal)
if (math.random(1, self.MedicReceiveHealSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT_RegularChange = CurTime() + math.random(3, 4)
self.CurrentMedicReceiveHealSound = sdType(self, sdtbl, self.MedicReceiveHealSoundLevel, self:VJ_DecideSoundPitch(self.MedicReceiveHealSoundPitch.a, self.MedicReceiveHealSoundPitch.b))
end
end
return
elseif sdSet == "OnPlayerSight" then
if self.HasOnPlayerSightSounds == true then
local sdtbl = VJ_PICK(self.SoundTbl_OnPlayerSight)
if (math.random(1, self.OnPlayerSightSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT_RegularChange = CurTime() + math.random(3, 4)
self.NextAlertSoundT = CurTime() + math.random(1,2)
self.CurrentOnPlayerSightSound = sdType(self, sdtbl, self.OnPlayerSightSoundLevel, self:VJ_DecideSoundPitch(self.OnPlayerSightSoundPitch.a, self.OnPlayerSightSoundPitch.b))
end
end
return
elseif sdSet == "InvestigateSound" then
if self.HasInvestigateSounds == true && CurTime() > self.NextInvestigateSoundT then
local sdtbl = VJ_PICK(self.SoundTbl_Investigate)
if (math.random(1, self.InvestigateSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT = self.NextIdleSoundT + 2
self.CurrentInvestigateSound = sdType(self, sdtbl, self.InvestigateSoundLevel, self:VJ_DecideSoundPitch(self.InvestigateSoundPitch.a, self.InvestigateSoundPitch.b))
end
self.NextInvestigateSoundT = CurTime() + math.Rand(self.NextSoundTime_Investigate.a, self.NextSoundTime_Investigate.b)
end
return
elseif sdSet == "LostEnemy" then
if self.HasLostEnemySounds == true && CurTime() > self.LostEnemySoundT then
local sdtbl = VJ_PICK(self.SoundTbl_LostEnemy)
if (math.random(1, self.LostEnemySoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT = self.NextIdleSoundT + 2
self.CurrentLostEnemySound = sdType(self, sdtbl, self.LostEnemySoundLevel, self:VJ_DecideSoundPitch(self.LostEnemySoundPitch.a, self.LostEnemySoundPitch.b))
end
self.LostEnemySoundT = CurTime() + math.Rand(self.NextSoundTime_LostEnemy.a, self.NextSoundTime_LostEnemy.b)
end
return
elseif sdSet == "Alert" then
if self.HasAlertSounds == true then
local sdtbl = VJ_PICK(self.SoundTbl_Alert)
if (math.random(1, self.AlertSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
local dur = CurTime() + ((((SoundDuration(sdtbl) > 0) and SoundDuration(sdtbl)) or 2) + 1)
self.NextIdleSoundT = dur
self.PainSoundT = dur
self.NextSuppressingSoundT = CurTime() + 4
self.NextAlertSoundT = CurTime() + math.Rand(self.NextSoundTime_Alert.a, self.NextSoundTime_Alert.b)
self.CurrentAlertSound = sdType(self, sdtbl, self.AlertSoundLevel, self:VJ_DecideSoundPitch(self.AlertSoundPitch.a, self.AlertSoundPitch.b))
end
end
return
elseif sdSet == "CallForHelp" then
if self.HasCallForHelpSounds == true && CurTime() > self.NextCallForHelpSoundT then
local sdtbl = VJ_PICK(self.SoundTbl_CallForHelp)
if (math.random(1, self.CallForHelpSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT = self.NextIdleSoundT + 2
self.NextSuppressingSoundT = CurTime() + math.random(2.5, 4)
self.CurrentCallForHelpSound = sdType(self, sdtbl, self.CallForHelpSoundLevel, self:VJ_DecideSoundPitch(self.CallForHelpSoundPitch.a, self.CallForHelpSoundPitch.b))
self.NextCallForHelpSoundT = CurTime() + 2
end
end
return
elseif sdSet == "BecomeEnemyToPlayer" then
if self.HasBecomeEnemyToPlayerSounds == true then
local sdtbl = VJ_PICK(self.SoundTbl_BecomeEnemyToPlayer)
if (math.random(1, self.BecomeEnemyToPlayerChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
timer.Simple(0.05,function() if IsValid(self) then VJ_STOPSOUND(self.CurrentPainSound) end end)
//timer.Simple(1.3,function() if IsValid(self) then VJ_STOPSOUND(self.CurrentAlertSound) end end)
local dur = CurTime() + ((((SoundDuration(sdtbl) > 0) and SoundDuration(sdtbl)) or 2) + 1)
self.PainSoundT = dur
self.NextAlertSoundT = dur
self.NextInvestigateSoundT = CurTime() + 2
self.NextIdleSoundT_RegularChange = CurTime() + math.random(2, 3)
self.NextSuppressingSoundT = CurTime() + math.random(2.5, 4)
self.CurrentBecomeEnemyToPlayerSound = sdType(self, sdtbl, self.BecomeEnemyToPlayerSoundLevel, self:VJ_DecideSoundPitch(self.BecomeEnemyToPlayerPitch.a, self.BecomeEnemyToPlayerPitch.b))
end
end
return
elseif sdSet == "OnKilledEnemy" then
if self.HasOnKilledEnemySound == true && CurTime() > self.OnKilledEnemySoundT then
local sdtbl = VJ_PICK(self.SoundTbl_OnKilledEnemy)
if (math.random(1, self.OnKilledEnemySoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT = self.NextIdleSoundT + 2
self.CurrentOnKilledEnemySound = sdType(self, sdtbl, self.OnKilledEnemySoundLevel, self:VJ_DecideSoundPitch(self.OnKilledEnemySoundPitch.a, self.OnKilledEnemySoundPitch.b))
end
self.OnKilledEnemySoundT = CurTime() + math.Rand(self.NextSoundTime_OnKilledEnemy.a, self.NextSoundTime_OnKilledEnemy.b)
end
return
elseif sdSet == "AllyDeath" then
if self.HasOnKilledEnemySound == true && CurTime() > self.AllyDeathSoundT then
local sdtbl = VJ_PICK(self.SoundTbl_AllyDeath)
if (math.random(1, self.AllyDeathSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT = self.NextIdleSoundT + 2
self.CurrentAllyDeathSound = sdType(self, sdtbl, self.AllyDeathSoundLevel, self:VJ_DecideSoundPitch(self.AllyDeathSoundPitch.a, self.AllyDeathSoundPitch.b))
end
self.AllyDeathSoundT = CurTime() + math.Rand(self.NextSoundTime_AllyDeath.a, self.NextSoundTime_AllyDeath.b)
end
return
elseif sdSet == "Pain" then
if self.HasPainSounds == true && CurTime() > self.PainSoundT then
local sdtbl = VJ_PICK(self.SoundTbl_Pain)
local sdDur = 2
if (math.random(1, self.PainSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
VJ_STOPSOUND(self.CurrentIdleSound)
self.NextIdleSoundT_RegularChange = CurTime() + 1
self.CurrentPainSound = sdType(self, sdtbl, self.PainSoundLevel, self:VJ_DecideSoundPitch(self.PainSoundPitch.a, self.PainSoundPitch.b))
sdDur = (SoundDuration(sdtbl) > 0 and SoundDuration(sdtbl)) or sdDur
end
self.PainSoundT = CurTime() + ((self.NextSoundTime_Pain == true and sdDur) or math.Rand(self.NextSoundTime_Pain.a, self.NextSoundTime_Pain.b))
end
return
elseif sdSet == "Impact" then
if self.HasImpactSounds == true then
local sdtbl = VJ_PICK(self.SoundTbl_Impact)
if sdtbl == false then sdtbl = VJ_PICK(DefaultSoundTbl_Impact) end -- Default table
if (math.random(1, self.ImpactSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self.CurrentImpactSound = sdType(self, sdtbl, self.ImpactSoundLevel, self:VJ_DecideSoundPitch(self.ImpactSoundPitch.a, self.ImpactSoundPitch.b))
end
end
return
elseif sdSet == "DamageByPlayer" then
if self.HasDamageByPlayerSounds == true && CurTime() > self.NextDamageByPlayerSoundT then
local sdtbl = VJ_PICK(self.SoundTbl_DamageByPlayer)
if (math.random(1, self.DamageByPlayerSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
local dur = CurTime() + ((((SoundDuration(sdtbl) > 0) and SoundDuration(sdtbl)) or 2) + 1)
self.PainSoundT = dur
self.NextIdleSoundT_RegularChange = CurTime() + dur
timer.Simple(0.05, function() if IsValid(self) then VJ_STOPSOUND(self.CurrentPainSound) end end)
self.CurrentDamageByPlayerSound = sdType(self, sdtbl, self.DamageByPlayerSoundLevel, self:VJ_DecideSoundPitch(self.DamageByPlayerPitch.a, self.DamageByPlayerPitch.b))
end
self.NextDamageByPlayerSoundT = CurTime() + math.Rand(self.NextSoundTime_DamageByPlayer.a, self.NextSoundTime_DamageByPlayer.b)
end
return
elseif sdSet == "Death" then
if self.HasDeathSounds == true then
local sdtbl = VJ_PICK(self.SoundTbl_Death)
if (math.random(1, self.DeathSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self.CurrentDeathSound = sdType(self, sdtbl, self.DeathSoundLevel, self:VJ_DecideSoundPitch(self.DeathSoundPitch.a, self.DeathSoundPitch.b))
end
end
return
--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-- Base-Specific Sound Tables --=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
elseif sdSet == "Suppressing" then
if self.HasSuppressingSounds == true && CurTime() > self.NextSuppressingSoundT then
local sdtbl = VJ_PICK(self.SoundTbl_Suppressing)
if (math.random(1, self.SuppressingSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT_RegularChange = CurTime() + 2
self.CurrentSuppressingSound = sdType(self, sdtbl, self.SuppressingSoundLevel, self:VJ_DecideSoundPitch(self.SuppressingPitch.a, self.SuppressingPitch.b))
end
self.NextSuppressingSoundT = CurTime() + math.Rand(self.NextSoundTime_Suppressing.a, self.NextSoundTime_Suppressing.b)
end
return
elseif sdSet == "WeaponReload" then
if self.HasWeaponReloadSounds == true && CurTime() > self.NextWeaponReloadSoundT then
local sdtbl = VJ_PICK(self.SoundTbl_WeaponReload)
if (math.random(1, self.WeaponReloadSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT_RegularChange = CurTime() + math.random(3, 4)
self.CurrentWeaponReloadSound = sdType(self, sdtbl, self.WeaponReloadSoundLevel, self:VJ_DecideSoundPitch(self.WeaponReloadSoundPitch.a, self.WeaponReloadSoundPitch.b))
end
self.NextWeaponReloadSoundT = CurTime() + math.Rand(self.NextSoundTime_WeaponReload.a, self.NextSoundTime_WeaponReload.b)
end
return
elseif sdSet == "BeforeMeleeAttack" then
if self.HasMeleeAttackSounds == true then
local sdtbl = VJ_PICK(self.SoundTbl_BeforeMeleeAttack)
if (math.random(1, self.BeforeMeleeAttackSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
if self.IdleSounds_PlayOnAttacks == false then VJ_STOPSOUND(self.CurrentIdleSound) end -- Don't stop idle sounds if we aren't suppose to
self.NextIdleSoundT_RegularChange = CurTime() + 1
self.CurrentBeforeMeleeAttackSound = sdType(self, sdtbl, self.BeforeMeleeAttackSoundLevel, self:VJ_DecideSoundPitch(self.BeforeMeleeAttackSoundPitch.a, self.BeforeMeleeAttackSoundPitch.b))
end
end
return
elseif sdSet == "MeleeAttack" then
if self.HasMeleeAttackSounds == true then
local sdtbl = VJ_PICK(self.SoundTbl_MeleeAttack)
if sdtbl == false then sdtbl = VJ_PICK(self.DefaultSoundTbl_MeleeAttack) end -- Default table
if (math.random(1, self.MeleeAttackSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
if self.IdleSounds_PlayOnAttacks == false then VJ_STOPSOUND(self.CurrentIdleSound) end -- Don't stop idle sounds if we aren't suppose to
self.NextIdleSoundT_RegularChange = CurTime() + 1
self.CurrentMeleeAttackSound = sdType(self, sdtbl, self.MeleeAttackSoundLevel, self:VJ_DecideSoundPitch(self.MeleeAttackSoundPitch.a, self.MeleeAttackSoundPitch.b))
end
if self.HasExtraMeleeAttackSounds == true then
sdtbl = VJ_PICK(self.SoundTbl_MeleeAttackExtra)
if (math.random(1, self.ExtraMeleeSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if self.IdleSounds_PlayOnAttacks == false then VJ_STOPSOUND(self.CurrentIdleSound) end -- Don't stop idle sounds if we aren't suppose to
self.CurrentExtraMeleeAttackSound = VJ_EmitSound(self, sdtbl, self.ExtraMeleeAttackSoundLevel, self:VJ_DecideSoundPitch(self.ExtraMeleeSoundPitch.a, self.ExtraMeleeSoundPitch.b))
end
end
end
return
elseif sdSet == "MeleeAttackMiss" then
if self.HasMeleeAttackMissSounds == true then
local sdtbl = VJ_PICK(self.SoundTbl_MeleeAttackMiss)
if sdtbl == false then sdtbl = VJ_PICK(DefaultSoundTbl_MeleeAttackMiss) end -- Default table
if (math.random(1, self.MeleeAttackMissSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
VJ_STOPSOUND(self.CurrentIdleSound)
self.NextIdleSoundT_RegularChange = CurTime() + 1
self.CurrentMeleeAttackMissSound = sdType(self, sdtbl, self.MeleeAttackMissSoundLevel, self:VJ_DecideSoundPitch(self.MeleeAttackMissSoundPitch.a, self.MeleeAttackMissSoundPitch.b))
end
end
return
elseif sdSet == "GrenadeAttack" then
if self.HasGrenadeAttackSounds == true && CurTime() > self.NextGrenadeAttackSoundT then
local sdtbl = VJ_PICK(self.SoundTbl_GrenadeAttack)
if (math.random(1, self.GrenadeAttackSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
if self.IdleSounds_PlayOnAttacks == false then self:StopAllCommonSpeechSounds() end -- Don't stop idle sounds if we aren't suppose to
self.NextIdleSoundT_RegularChange = CurTime() + math.random(3, 4)
self.CurrentGrenadeAttackSound = sdType(self, sdtbl, self.GrenadeAttackSoundLevel, self:VJ_DecideSoundPitch(self.GrenadeAttackSoundPitch.a, self.GrenadeAttackSoundPitch.b))
end
end
return
elseif sdSet == "OnGrenadeSight" then
if self.HasOnGrenadeSightSounds == true && CurTime() > self.NextOnGrenadeSightSoundT then
local sdtbl = VJ_PICK(self.SoundTbl_OnGrenadeSight)
if (math.random(1, self.OnGrenadeSightSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT_RegularChange = CurTime() + math.random(3, 4)
self.CurrentOnGrenadeSightSound = sdType(self, sdtbl, self.OnGrenadeSightSoundLevel, self:VJ_DecideSoundPitch(self.OnGrenadeSightSoundPitch.a, self.OnGrenadeSightSoundPitch.b))
end
self.NextOnGrenadeSightSoundT = CurTime() + math.Rand(self.NextSoundTime_OnGrenadeSight.a, self.NextSoundTime_OnGrenadeSight.b)
end
return
elseif sdSet == "OnDangerSight" then
if self.HasOnDangerSightSounds == true && CurTime() > self.NextOnDangerSightSoundT then
local sdtbl = VJ_PICK(self.SoundTbl_OnDangerSight)
if (math.random(1, self.OnDangerSightSoundChance) == 1 && sdtbl != false) or (cTbl != false) then
if cTbl != false then sdtbl = cTbl end
self:StopAllCommonSpeechSounds()
self.NextIdleSoundT_RegularChange = CurTime() + math.random(3, 4)
self.CurrentOnDangerSightSound = sdType(self, sdtbl, self.OnDangerSightSoundLevel, self:VJ_DecideSoundPitch(self.OnDangerSightSoundPitch.a, self.OnDangerSightSoundPitch.b))
end
self.NextOnDangerSightSoundT = CurTime() + math.Rand(self.NextSoundTime_OnDangerSight.a, self.NextSoundTime_OnDangerSight.b)
end
return
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:FootStepSoundCode(CustomTbl)
if self.HasSounds == false or self.HasFootStepSound == false or self.MovementType == VJ_MOVETYPE_STATIONARY then return end
if self:IsOnGround() && self:GetGroundEntity() != NULL then
if self.DisableFootStepSoundTimer == true then
self:CustomOnFootStepSound()
local soundtbl = self.SoundTbl_FootStep
if CustomTbl != nil && #CustomTbl != 0 then soundtbl = CustomTbl end
if VJ_PICK(soundtbl) == false then soundtbl = DefaultSoundTbl_FootStep end
VJ_EmitSound(self,soundtbl,self.FootStepSoundLevel,self:VJ_DecideSoundPitch(self.FootStepPitch.a,self.FootStepPitch.b))
if self.HasWorldShakeOnMove == true then util.ScreenShake(self:GetPos(), self.WorldShakeOnMoveAmplitude, self.WorldShakeOnMoveFrequency, self.WorldShakeOnMoveDuration, self.WorldShakeOnMoveRadius) end
return
elseif self:IsMoving() && CurTime() > self.FootStepT && self:GetInternalVariable("m_flMoveWaitFinished") <= 0 then
self:CustomOnFootStepSound()
local soundtbl = self.SoundTbl_FootStep
if CustomTbl != nil && #CustomTbl != 0 then soundtbl = CustomTbl end
if VJ_PICK(soundtbl) == false then soundtbl = DefaultSoundTbl_FootStep end
local curSched = self.CurrentSchedule
if self.DisableFootStepOnRun == false && ((VJ_HasValue(self.AnimTbl_Run,self:GetMovementActivity())) or (curSched != nil && curSched.MoveType == 1)) /*(VJ_HasValue(VJ_RunActivites,self:GetMovementActivity()) or VJ_HasValue(self.CustomRunActivites,self:GetMovementActivity()))*/ then
self:CustomOnFootStepSound_Run()
VJ_EmitSound(self,soundtbl,self.FootStepSoundLevel,self:VJ_DecideSoundPitch(self.FootStepPitch.a,self.FootStepPitch.b))
if self.HasWorldShakeOnMove == true then util.ScreenShake(self:GetPos(), self.WorldShakeOnMoveAmplitude, self.WorldShakeOnMoveFrequency, self.WorldShakeOnMoveDuration, self.WorldShakeOnMoveRadius) end
self.FootStepT = CurTime() + self.FootStepTimeRun
return
elseif self.DisableFootStepOnWalk == false && (VJ_HasValue(self.AnimTbl_Walk,self:GetMovementActivity()) or (curSched != nil && curSched.MoveType == 0)) /*(VJ_HasValue(VJ_WalkActivites,self:GetMovementActivity()) or VJ_HasValue(self.CustomWalkActivites,self:GetMovementActivity()))*/ then
self:CustomOnFootStepSound_Walk()
VJ_EmitSound(self,soundtbl,self.FootStepSoundLevel,self:VJ_DecideSoundPitch(self.FootStepPitch.a,self.FootStepPitch.b))
if self.HasWorldShakeOnMove == true then util.ScreenShake(self:GetPos(), self.WorldShakeOnMoveAmplitude, self.WorldShakeOnMoveFrequency, self.WorldShakeOnMoveDuration, self.WorldShakeOnMoveRadius) end
self.FootStepT = CurTime() + self.FootStepTimeWalk
return
end
end
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:StopAllCommonSpeechSounds()
VJ_STOPSOUND(self.CurrentGeneralSpeechSound)
VJ_STOPSOUND(self.CurrentIdleSound)
VJ_STOPSOUND(self.CurrentIdleDialogueAnswerSound)
VJ_STOPSOUND(self.CurrentInvestigateSound)
VJ_STOPSOUND(self.CurrentLostEnemySound)
VJ_STOPSOUND(self.CurrentAlertSound)
VJ_STOPSOUND(self.CurrentFollowPlayerSound)
VJ_STOPSOUND(self.CurrentUnFollowPlayerSound)
VJ_STOPSOUND(self.CurrentMoveOutOfPlayersWaySound)
VJ_STOPSOUND(self.CurrentBecomeEnemyToPlayerSound)
VJ_STOPSOUND(self.CurrentOnPlayerSightSound)
VJ_STOPSOUND(self.CurrentDamageByPlayerSound)
VJ_STOPSOUND(self.CurrentOnGrenadeSightSound)
VJ_STOPSOUND(self.CurrentOnDangerSightSound)
VJ_STOPSOUND(self.CurrentWeaponReloadSound)
VJ_STOPSOUND(self.CurrentMedicBeforeHealSound)
VJ_STOPSOUND(self.CurrentMedicAfterHealSound)
VJ_STOPSOUND(self.CurrentMedicReceiveHealSound)
VJ_STOPSOUND(self.CurrentCallForHelpSound)
VJ_STOPSOUND(self.CurrentOnReceiveOrderSound)
VJ_STOPSOUND(self.CurrentOnKilledEnemySound)
VJ_STOPSOUND(self.CurrentAllyDeathSound)
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:StopAllCommonSounds()
VJ_STOPSOUND(self.CurrentGeneralSpeechSound)
VJ_STOPSOUND(self.CurrentBreathSound)
VJ_STOPSOUND(self.CurrentIdleSound)
VJ_STOPSOUND(self.CurrentIdleDialogueAnswerSound)
VJ_STOPSOUND(self.CurrentInvestigateSound)
VJ_STOPSOUND(self.CurrentLostEnemySound)
VJ_STOPSOUND(self.CurrentAlertSound)
VJ_STOPSOUND(self.CurrentBeforeMeleeAttackSound)
//VJ_STOPSOUND(self.CurrentMeleeAttackSound)
//VJ_STOPSOUND(self.CurrentExtraMeleeAttackSound)
//VJ_STOPSOUND(self.CurrentMeleeAttackMissSound)
VJ_STOPSOUND(self.CurrentPainSound)
VJ_STOPSOUND(self.CurrentFollowPlayerSound)
VJ_STOPSOUND(self.CurrentUnFollowPlayerSound)
VJ_STOPSOUND(self.CurrentMoveOutOfPlayersWaySound)
VJ_STOPSOUND(self.CurrentBecomeEnemyToPlayerSound)
VJ_STOPSOUND(self.CurrentOnPlayerSightSound)
VJ_STOPSOUND(self.CurrentDamageByPlayerSound)
VJ_STOPSOUND(self.CurrentGrenadeAttackSound)
VJ_STOPSOUND(self.CurrentOnGrenadeSightSound)
VJ_STOPSOUND(self.CurrentOnDangerSightSound)
VJ_STOPSOUND(self.CurrentSuppressingSound)
VJ_STOPSOUND(self.CurrentWeaponReloadSound)
VJ_STOPSOUND(self.CurrentMedicBeforeHealSound)
VJ_STOPSOUND(self.CurrentMedicAfterHealSound)
VJ_STOPSOUND(self.CurrentMedicReceiveHealSound)
VJ_STOPSOUND(self.CurrentCallForHelpSound)
VJ_STOPSOUND(self.CurrentOnReceiveOrderSound)
VJ_STOPSOUND(self.CurrentOnKilledEnemySound)
VJ_STOPSOUND(self.CurrentAllyDeathSound)
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:GetAttackSpread(wep, target)
return
end