-
Notifications
You must be signed in to change notification settings - Fork 4
THE FUKIN SECONDARY FIRE FOR THE TERMS #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -372,7 +372,6 @@ function ENT:SetupWeapon( wep ) | |
| permaPrint( wep:GetModelScale() ) | ||
| if false then | ||
| self:DropWeapon() | ||
|
|
||
| end | ||
| end ) | ||
| --]] | ||
|
|
@@ -653,6 +652,69 @@ function ENT:CanWeaponPrimaryAttack( myTbl, wep, wepsTbl ) | |
|
|
||
| end | ||
|
|
||
| --[[------------------------------------ | ||
| Name: NEXTBOT:AI_ShouldUseSecondary | ||
| Desc: (INTERNAL) Decides if we should use secondary fire based on emergency status and weapon type. | ||
| Arg1: Weapon | wep | The active weapon. | ||
| Ret1: bool | True if we should use secondary fire. | ||
| --]]------------------------------------ | ||
| function ENT:AI_ShouldUseSecondary( wep ) | ||
| if not IsValid( wep ) then return false end | ||
|
|
||
| local enemy = self:GetEnemy() | ||
| if not IsValid(enemy) then return false end | ||
|
|
||
| -- EMERGENCY CHECK: Only consider secondary usage if we are in danger | ||
| local myTbl = entMeta.GetTable( self ) | ||
| local hpPct = self:Health() / self:GetMaxHealth() | ||
| local isEmergency = hpPct < 0.4 -- Less than 40% Health | ||
|
|
||
| -- Or if out of primary ammo | ||
| if wep:Clip1() <= 0 then isEmergency = true end | ||
|
|
||
| -- Or if the logic system says we are in serious danger | ||
| if not isEmergency and myTbl.inSeriousDanger and myTbl.inSeriousDanger(self) then | ||
| isEmergency = true | ||
| end | ||
|
|
||
| if not isEmergency then return false end | ||
| -- END EMERGENCY CHECK | ||
|
|
||
| local distSq = self:GetRangeSquaredTo(enemy) | ||
| local class = wep:GetClass() | ||
|
|
||
| -- Map engine analogs back to original names for checking logic, if applicable | ||
| if EngineAnalogsReverse[class] then | ||
| class = EngineAnalogsReverse[class] | ||
| end | ||
|
|
||
| -- Specific logic for Half-Life 2 weapons | ||
| if class == "weapon_smg1" then | ||
| -- SMG Grenade: Good for mid-range. Don't use too close (self damage) or too far. | ||
| -- Increased chance in emergency | ||
| if distSq > (300*300) and distSq < (1500*1500) then | ||
| return math.random(1, 100) <= 20 -- 20% chance in emergency | ||
| end | ||
| elseif class == "weapon_ar2" then | ||
| -- Energy Ball: Good for long range and flushing enemies. | ||
| if distSq > (400*400) and distSq < (2000*2000) then | ||
| return math.random(1, 100) <= 15 -- 15% chance in emergency | ||
| end | ||
| elseif class == "weapon_shotgun" then | ||
| -- Double Barrel: Devastating at close range. | ||
| if distSq < (300*300) then | ||
| return math.random(1, 100) <= 50 -- 50% chance when close and in danger | ||
| end | ||
| end | ||
|
|
||
| -- Generic fallback for scripted weapons that have secondary ammo | ||
| if wep:GetMaxClip2() > 0 and wep:Clip2() > 0 then | ||
| return math.random(1, 100) <= 10 | ||
| end | ||
|
Comment on lines
+710
to
+713
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good fallback, try testing it on multiple ( 10+ ) sweps though, sweps are so stupid |
||
|
|
||
| return false | ||
| end | ||
|
|
||
| --[[------------------------------------ | ||
| Name: NEXTBOT:WeaponPrimaryAttack | ||
| Desc: Does primary attack from bot's active weapon. This also uses burst data from weapon. | ||
|
|
@@ -668,6 +730,23 @@ function ENT:WeaponPrimaryAttack() | |
| local wepsTbl = entMeta.GetTable( wep ) | ||
| if myTbl.CanWeaponPrimaryAttack( self, myTbl, wep, wepsTbl ) ~= true then return false, "CanWeaponPrimaryAttack" end | ||
|
|
||
| -- START TRUE SECONDARY FIRE SUPPORT | ||
| -- Check if we should override primary fire with secondary fire | ||
| if not self:IsControlledByPlayer() and self:CanWeaponSecondaryAttack() and self:AI_ShouldUseSecondary(wep) then | ||
| self:WeaponSecondaryAttack() | ||
|
|
||
| -- Add a small delay after secondary fire before we can primary fire again | ||
| -- This prevents instant double attacks and mimics recoil/recovery | ||
| local delay = 1.0 | ||
| if wep:GetClass() == "weapon_shotgun_term" or wep:GetClass() == "weapon_shotgun" then | ||
| delay = 1.5 -- Double barrel takes longer | ||
| end | ||
|
|
||
| self.m_WeaponData.Primary.NextShootTime = CurTime() + delay | ||
| return true, "Fired_Secondary_Override" | ||
| end | ||
| -- END TRUE SECONDARY FIRE SUPPORT | ||
|
Comment on lines
+733
to
+748
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put this in the shooting_handler task |
||
|
|
||
| local data = self.m_WeaponData.Primary | ||
| local isSetupProperly = isfunction( wep.GetNPCBurstSettings ) and isfunction( wep.GetNPCRestTimes ) | ||
| local fireType = "NONE" | ||
|
|
@@ -739,8 +818,13 @@ function ENT:CanWeaponSecondaryAttack() | |
| if not self:HasWeapon() or CurTime() < self.m_WeaponData.Secondary.NextShootTime then return false end | ||
|
|
||
| local wep = self:GetActiveLuaWeapon() | ||
| if not IsValid(wep) then return false end | ||
|
|
||
| if CurTime() < wep:GetNextSecondaryFire() then return false end | ||
|
|
||
| -- Check if we actually have ammo for secondary | ||
| if wep:GetMaxClip2() > 0 and wep:Clip2() <= 0 then return false end | ||
|
|
||
| return true | ||
| end | ||
|
|
||
|
|
@@ -758,6 +842,11 @@ function ENT:WeaponSecondaryAttack() | |
| local successful = ProtectedCall(function() wep:NPCShoot_Secondary(self:GetShootPos(),self:GetAimVector()) end) | ||
| self:HateBuggyWeapon( wep, successful ) | ||
| self:DoRangeGesture() | ||
|
|
||
| -- Ensure we set the next time so we don't spam | ||
| local nextTime = wep:GetNextSecondaryFire() | ||
| if nextTime < CurTime() then nextTime = CurTime() + 1 end | ||
| self.m_WeaponData.Secondary.NextShootTime = nextTime | ||
| end | ||
|
|
||
| --[[------------------------------------ | ||
|
|
@@ -886,9 +975,14 @@ local DistToSqr = FindMetaTable( "Vector" ).DistToSqr | |
|
|
||
| function ENT:CanPickupWeapon( wep, doingHolstered, myTbl, wepsTbl ) | ||
| if not IsValid( wep ) then boring( wep ) return end | ||
| wepsTbl = wepsTbl or entMeta.GetTable( self ) | ||
|
|
||
| -- SAFETY FIX: Initialize tables early to prevent nil indexing later | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AI slop comment, this is actually an optimisation to reduce _index calls |
||
| myTbl = myTbl or entMeta.GetTable( self ) | ||
| if not myTbl then return false end -- Should not happen if self is valid, but safety first | ||
|
|
||
| wepsTbl = wepsTbl or entMeta.GetTable( wep ) -- LOGIC FIX: Use 'wep' not 'self' | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AI slop comment, should be removed, doesn't make code more readable |
||
| if not wepsTbl then boring( wep ) return end | ||
|
|
||
| if not wepsTbl then boring( wep ) return end -- ???? | ||
| local class = entMeta.GetClass( wep ) | ||
| local isCrate = class == crateClass | ||
| if not isCrate then | ||
|
|
@@ -906,7 +1000,6 @@ function ENT:CanPickupWeapon( wep, doingHolstered, myTbl, wepsTbl ) | |
|
|
||
| if doingHolstered and wepsParent ~= self then return false end | ||
|
|
||
| myTbl = myTbl or entMeta.GetTable( self ) | ||
| if doingHolstered and wepsParent == self and not myTbl.IsHolsteredWeap( self, wep ) then return false end -- we're already using this one... | ||
|
|
||
| local blockWeaponNoticing = wep.blockWeaponNoticing or 0 | ||
|
|
@@ -1680,6 +1773,91 @@ hook.Add( "PostEntityTakeDamage", "terminator_trackweapondamage", function( targ | |
| end | ||
| end ) | ||
|
|
||
| -- Fix for projectile ownership and self-collision (SMG Grenade) | ||
| hook.Add("OnEntityCreated", "Terminator_ProjectileFixes", function(ent) | ||
| -- Wait a tick for initialization | ||
| timer.Simple(0, function() | ||
| if not IsValid(ent) then return end | ||
|
|
||
| local class = ent:GetClass() | ||
|
|
||
| -- Detect common secondary projectiles | ||
| if class == "grenade_ar2" or class == "prop_combine_ball" or class == "rpg_missile" or class == "obj_vj_grenade" then | ||
|
|
||
| local owner = ent:GetOwner() | ||
| local bot = nil | ||
|
|
||
| -- Scenario A: Owner is set to the Weapon (common in scripted weapons) | ||
| if IsValid(owner) and owner:IsWeapon() then | ||
| local parent = owner:GetOwner() | ||
| if IsValid(parent) and parent.isTerminatorHunterBased then | ||
| bot = parent | ||
| end | ||
| -- Scenario B: Owner is already the Bot (ideal, but check collision) | ||
| elseif IsValid(owner) and owner.isTerminatorHunterBased then | ||
| bot = owner | ||
| -- Scenario C: Owner is nil. Check strict proximity to a firing Terminator. | ||
| elseif not IsValid(owner) then | ||
| local pos = ent:GetPos() | ||
| for _, v in ipairs(ents.FindInSphere(pos, 70)) do | ||
| if v.isTerminatorHunterBased and v:GetActiveWeapon():GetLastShootTime() > CurTime() - 0.5 then | ||
| bot = v | ||
| break | ||
| end | ||
| end | ||
| end | ||
|
|
||
| if IsValid(bot) then | ||
| -- Ensure kill credit goes to the bot, not the weapon or nil | ||
| ent:SetOwner(bot) | ||
| ent.Owner = bot -- Some SWEPs check this Lua field | ||
|
|
||
| -- Extra fix for Combine Ball logic in engine | ||
| if class == "prop_combine_ball" then | ||
| -- Trigger the engine input to set owner, which handles internal dissolved pointers | ||
| ent:Fire("SetOwner", "!activator", 0, bot) | ||
| end | ||
|
|
||
| -- Prevent SMG grenade (and others) from exploding instantly on the bot's face | ||
| -- We use NoCollide to ensure the physics engine ignores the bot for this projectile | ||
| constraint.NoCollide(ent, bot, 0, 0) | ||
|
|
||
| -- Specific fix for grenade_ar2 which is very sensitive | ||
| if class == "grenade_ar2" then | ||
| local phys = ent:GetPhysicsObject() | ||
| if IsValid(phys) then | ||
| -- Ensure it has forward velocity so it clears the bounding box | ||
| local vel = phys:GetVelocity() | ||
| local aim = bot:GetAimVector() | ||
|
|
||
| -- If velocity is too low or weird, force it forward | ||
| if vel:Length() < 500 then | ||
| phys:SetVelocity(aim * 1000) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end) | ||
| end) | ||
|
Comment on lines
+1776
to
+1842
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be handled inside every weapon override, the entities are already available to modify and mess with there. it shouldn't be a monolith inside this expensive hook. |
||
|
|
||
| -- Hook to forcibly attribute damage from these projectiles to the Terminator | ||
| -- This catches cases where the engine resets the attacker or uses the inflictor as attacker | ||
| hook.Add("EntityTakeDamage", "Terminator_ProjectileDamageCredit", function(target, dmginfo) | ||
| local inflictor = dmginfo:GetInflictor() | ||
| if not IsValid(inflictor) then return end | ||
|
|
||
| local class = inflictor:GetClass() | ||
| if class == "prop_combine_ball" or class == "grenade_ar2" then | ||
| local owner = inflictor:GetOwner() | ||
|
|
||
| -- If the projectile is owned by a terminator, ensure the damage is credited to them | ||
| if IsValid(owner) and owner.isTerminatorHunterBased then | ||
| dmginfo:SetAttacker(owner) | ||
| end | ||
| end | ||
| end) | ||
|
Comment on lines
+1844
to
+1859
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Kinda fair, but it should probably be in each weapon's folder. |
||
|
|
||
| do | ||
| local Vector = Vector | ||
| local vecMeta = FindMetaTable( "Vector" ) | ||
|
|
@@ -1955,4 +2133,4 @@ function ENT:GetWeaponRange( myTbl, wep, wepTable ) | |
|
|
||
| return math.huge | ||
|
|
||
| end | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should ask the actual luawep entity for a method
eg, luawep:IsCloseEnoughToSecondaryFire