From 4bd6d8374ded1eab8f73356395da23daf760c2ac Mon Sep 17 00:00:00 2001 From: StrawWagen <64710817+StrawWagen@users.noreply.github.com> Date: Wed, 11 Feb 2026 14:22:39 -0700 Subject: [PATCH 1/6] Test changes --- .github/workflows/{blank.yml => glualint.yml} | 0 lua/entities/terminator_nextbot_base/init.lua | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{blank.yml => glualint.yml} (100%) diff --git a/.github/workflows/blank.yml b/.github/workflows/glualint.yml similarity index 100% rename from .github/workflows/blank.yml rename to .github/workflows/glualint.yml diff --git a/lua/entities/terminator_nextbot_base/init.lua b/lua/entities/terminator_nextbot_base/init.lua index 29ed3b7..942d6d2 100644 --- a/lua/entities/terminator_nextbot_base/init.lua +++ b/lua/entities/terminator_nextbot_base/init.lua @@ -378,4 +378,4 @@ function terminator_Extras.teardownExpensiveHacks() meta.IsNPC = meta.term_Old_IsNPC meta.EyeAngles = meta.term_Old_EyeAngles -end \ No newline at end of file +end From 3c181470f6ea5d63d97d61cfd277920e11539c05 Mon Sep 17 00:00:00 2001 From: StrawWagen <64710817+StrawWagen@users.noreply.github.com> Date: Wed, 11 Feb 2026 15:54:29 -0700 Subject: [PATCH 2/6] Create .glualint.json --- .glualint.json | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .glualint.json diff --git a/.glualint.json b/.glualint.json new file mode 100644 index 0000000..dbaeb21 --- /dev/null +++ b/.glualint.json @@ -0,0 +1,48 @@ +{ + "lint_maxScopeDepth": 7, + "lint_syntaxErrors": true, + "lint_syntaxInconsistencies": true, + "lint_deprecated": true, + "lint_trailingWhitespace": true, + "lint_whitespaceStyle": true, + "lint_beginnerMistakes": true, + "lint_emptyBlocks": true, + "lint_shadowing": true, + "lint_gotos": true, + "lint_goto_identifier": true, + "lint_doubleNegations": true, + "lint_redundantIfStatements": true, + "lint_redundantParentheses": true, + "lint_duplicateTableKeys": true, + "lint_profanity": true, + "lint_unusedVars": true, + "lint_unusedParameters": true, + "lint_unusedLoopVars": true, + "lint_inconsistentVariableStyle": false, + "lint_spaceBeforeComma": true, + "lint_spaceAfterComma": true, + "lint_spaceBetweenParens": true, + "lint_spaceBetweenBrackets": true, + "lint_spaceBetweenBraces": true, + "lint_ignoreFiles": [ + "lua/entities/gmod_wire_expression2/core/custom/*.lua", + "lua/includes/modules/pon.lua", + "lua/entities/terminator_nextbot/weaponhacks.lua" + ], + "prettyprint_spaceBetweenParens": true, + "prettyprint_spaceBetweenBrackets": false, + "prettyprint_spaceBetweenBraces": true, + "prettyprint_spaceAfterLabel": true, + "prettyprint_spaceBeforeComma": false, + "prettyprint_spaceAfterComma": true, + "prettyprint_semicolons": false, + "prettyprint_cStyle": false, + "prettyprint_rejectInvalidCode": false, + "prettyprint_spaceEmptyParens": false, + "prettyprint_spaceEmptyBraces": false, + "prettyprint_removeRedundantParens":true, + "prettyprint_minimizeParens": true, + "prettyprint_assumeOperatorAssociativity": true, + "prettyprint_indentation": " ", + "log_format": "auto" +} \ No newline at end of file From 356cfa96d9fabb990d9085891100ba419bf7496e Mon Sep 17 00:00:00 2001 From: StrawWagen <64710817+StrawWagen@users.noreply.github.com> Date: Wed, 11 Feb 2026 15:55:14 -0700 Subject: [PATCH 3/6] Lint! --- lua/autorun/server/term_coroutine_counter.lua | 4 +- lua/autorun/server/terminator_alerter.lua | 2 +- lua/autorun/server/terminator_areapatcher.lua | 370 ++++++----- .../terminator_following_navpatcher.lua | 8 +- lua/autorun/server/terminator_funcs.lua | 6 +- .../server/terminator_optimizerhack.lua | 4 +- .../server/terminator_weapon_dropper.lua | 2 +- .../terminator_weapanalog_weightsystem.lua | 8 +- lua/effects/eff_term_goodarc.lua | 32 +- .../terminator_nextbot/behaviouroverrides.lua | 30 +- .../terminator_nextbot/compatibilityhacks.lua | 27 +- .../terminator_nextbot/enemyoverrides.lua | 110 +-- lua/entities/terminator_nextbot/footsteps.lua | 2 +- .../terminator_nextbot/motionoverrides.lua | 35 +- .../terminator_nextbot/pathoverrides.lua | 109 +-- lua/entities/terminator_nextbot/shared.lua | 626 +++++++++--------- .../terminator_nextbot/spokenlines.lua | 10 +- .../terminator_nextbot/taskoverride.lua | 6 +- .../terminator_nextbot/weapholstering.lua | 36 +- .../terminator_nextbot/weaponhacks.lua | 2 +- lua/entities/terminator_nextbot/weapons.lua | 20 +- .../terminator_nextbot/wraithcloaking.lua | 2 +- .../terminator_nextbot_base/drive.lua | 4 +- lua/entities/terminator_nextbot_csoldier.lua | 32 +- lua/weapons/weapon_frag_term.lua | 2 +- lua/weapons/weapon_terminatorfists_term.lua | 2 +- .../terminator/arnold/arnold_glasses.vmt | 2 +- .../terminator/arnold/arnold_metal_1.vmt | 2 +- 28 files changed, 776 insertions(+), 719 deletions(-) diff --git a/lua/autorun/server/term_coroutine_counter.lua b/lua/autorun/server/term_coroutine_counter.lua index 4df1a51..2851ef5 100644 --- a/lua/autorun/server/term_coroutine_counter.lua +++ b/lua/autorun/server/term_coroutine_counter.lua @@ -80,7 +80,7 @@ local function count() collectgarbage( "restart" ) print( "live coroutines:", coCount ) - print( "of which, " .. staleCoCount .. " are stale") + print( "of which, " .. staleCoCount .. " are stale" ) end @@ -90,7 +90,7 @@ concommand.Add( "term_countcoroutines", function() if not done then done = true startCounter() - print("Started tracking coroutines.") + print( "Started tracking coroutines." ) if luagc then print( "holylib found, using luagc... this can cause crashes!" ) diff --git a/lua/autorun/server/terminator_alerter.lua b/lua/autorun/server/terminator_alerter.lua index 68baadf..04fb5bb 100644 --- a/lua/autorun/server/terminator_alerter.lua +++ b/lua/autorun/server/terminator_alerter.lua @@ -160,7 +160,7 @@ local function bulletFireThink( entity, data ) end local customRange = nil if IsValid( weap ) then - customRange = customRanges[ entMeta.GetClass( weap ) ] + customRange = customRanges[entMeta.GetClass( weap )] end if customRange then diff --git a/lua/autorun/server/terminator_areapatcher.lua b/lua/autorun/server/terminator_areapatcher.lua index a106fdb..850b4ab 100644 --- a/lua/autorun/server/terminator_areapatcher.lua +++ b/lua/autorun/server/terminator_areapatcher.lua @@ -276,7 +276,7 @@ local function mergeWithNeighbors( data, vecsToPlace ) if not allGood then continue end for _, toMerge in ipairs( neighbors ) do - vecsToPlace[ toMerge.key ] = nil + vecsToPlace[toMerge.key] = nil end @@ -504,211 +504,216 @@ end local coroutine_yield = coroutine.yield --- Coroutine function to handle patching regions one-by-one -local function patchCoroutine() - while #regionsQueue > 0 do - terminator_Extras.IsLivePatching = true - coroutine_yield() +local function patchThink() + terminator_Extras.IsLivePatching = true + coroutine_yield() - -- Retrieve the next region from the queue - local region = table.remove( regionsQueue, 1 ) - updateGridSize( region.gridSize ) + -- Retrieve the next region from the queue + local region = table.remove( regionsQueue, 1 ) + updateGridSize( region.gridSize ) - local newGenCenter = region.pos1 + region.pos2 - newGenCenter = newGenCenter / 2 + local newGenCenter = region.pos1 + region.pos2 + newGenCenter = newGenCenter / 2 - if oldGenCenter and patchTbl.gridSize < 12.5 and oldGenCenter:Distance( newGenCenter ) < ( patchTbl.gridSize * 6 ) then -- we are stuck regenerating one point, try shuffling this - gridOffset = math.random( -4, 4 ) - debugPrint( "Area generation is stuck, offsetting grid by " .. gridOffset ) + if oldGenCenter and patchTbl.gridSize < 12.5 and oldGenCenter:Distance( newGenCenter ) < ( patchTbl.gridSize * 6 ) then -- we are stuck regenerating one point, try shuffling this + gridOffset = math.random( -4, 4 ) + debugPrint( "Area generation is stuck, offsetting grid by " .. gridOffset ) - else - gridOffset = 0 + else + gridOffset = 0 - end + end - local pos1 = VectorMin( region.pos1, region.pos2 ) - local pos2 = VectorMax( region.pos1, region.pos2 ) - SnapToGrid( pos1 ) - SnapToGrid( pos2 ) + local pos1 = VectorMin( region.pos1, region.pos2 ) + local pos2 = VectorMax( region.pos1, region.pos2 ) + SnapToGrid( pos1 ) + SnapToGrid( pos2 ) - oldGenCenter = newGenCenter + oldGenCenter = newGenCenter - debugPrint( "Patching region from", pos1, "to", pos2 ) + debugPrint( "Patching region from", pos1, "to", pos2 ) - local openVoxelsSeq = {} -- spots we are yet to check - local closedVoxels = {} -- spots that we dont need to check - local solidVoxels = {} -- solid spots - local vecsToPlace = {} -- good spots we found - local headroomTbl = {} -- headroom, for simple crouching checks - local validatedAreas = {} -- all the areas we made this pass + local openVoxelsSeq = {} -- spots we are yet to check + local closedVoxels = {} -- spots that we dont need to check + local solidVoxels = {} -- solid spots + local vecsToPlace = {} -- good spots we found + local headroomTbl = {} -- headroom, for simple crouching checks + local validatedAreas = {} -- all the areas we made this pass - local biggest = VectorMax( pos1, pos2 ) - local smallest = VectorMin( pos1, pos2 ) + local biggest = VectorMax( pos1, pos2 ) + local smallest = VectorMin( pos1, pos2 ) - local sizeInX = biggest.x - smallest.x - local sizeInY = biggest.y - smallest.y - local sizeInZ = biggest.z - smallest.z + local sizeInX = biggest.x - smallest.x + local sizeInY = biggest.y - smallest.y + local sizeInZ = biggest.z - smallest.z - for z = 0, sizeInZ / patchTbl.gridSize do -- z first - z = z * patchTbl.gridSize - coroutine_yield() + for z = 0, sizeInZ / patchTbl.gridSize do -- z first + z = z * patchTbl.gridSize + coroutine_yield() - for x = 0, sizeInX / patchTbl.gridSize do - x = x * patchTbl.gridSize - coroutine_yield() + for x = 0, sizeInX / patchTbl.gridSize do + x = x * patchTbl.gridSize + coroutine_yield() - for y = 0, sizeInY / patchTbl.gridSize do - y = y * patchTbl.gridSize - local voxel = Vector( smallest.x + x, smallest.y + y, smallest.z + z ) - table.insert( openVoxelsSeq, voxel ) + for y = 0, sizeInY / patchTbl.gridSize do + y = y * patchTbl.gridSize + local voxel = Vector( smallest.x + x, smallest.y + y, smallest.z + z ) + table.insert( openVoxelsSeq, voxel ) - end end end + end - debugPrint( "Total voxels to process:", #openVoxelsSeq ) + debugPrint( "Total voxels to process:", #openVoxelsSeq ) - -- Step 5: Process each voxel - while #openVoxelsSeq >= 1 do - local currVoxel = table.remove( openVoxelsSeq ) - if closedVoxels[vecAsKey( currVoxel )] then continue end + -- Step 5: Process each voxel + while #openVoxelsSeq >= 1 do + local currVoxel = table.remove( openVoxelsSeq ) + if closedVoxels[vecAsKey( currVoxel )] then continue end - coroutine_yield() - processVoxel( currVoxel, pos1, pos2, vecsToPlace, closedVoxels, headroomTbl, solidVoxels ) - if debugging and IsValid( Entity(1) ) and currVoxel:Distance( Entity(1):GetPos() ) < 250 then - coroutine_yield( "wait" ) + coroutine_yield() + processVoxel( currVoxel, pos1, pos2, vecsToPlace, closedVoxels, headroomTbl, solidVoxels ) + if debugging and IsValid( Entity( 1 ) ) and currVoxel:Distance( Entity( 1 ):GetPos() ) < 250 then + coroutine_yield( "wait" ) - end end + end - local count = table.Count( vecsToPlace ) - debugPrint( "Placing!" ) + local count = table.Count( vecsToPlace ) + debugPrint( "Placing!" ) - if count >= 1 then - debugPrint( "Pre-merging areas..." ) + if count >= 1 then + debugPrint( "Pre-merging areas..." ) - local merged = true - while merged do + local merged = true + while merged do + coroutine_yield() + merged = nil + for _, data in pairs( vecsToPlace ) do coroutine_yield() - merged = nil - for _, data in pairs( vecsToPlace ) do - coroutine_yield() - if mergeWithNeighbors( data, vecsToPlace ) then - merged = true - break - - end + if mergeWithNeighbors( data, vecsToPlace ) then + merged = true + break + end end + end - debugPrint( "Placing " .. count .. " navareas..." ) + debugPrint( "Placing " .. count .. " navareas..." ) - local justNewAreas = {} - local justNewAreasSeq = {} - for _, data in pairs( vecsToPlace ) do - coroutine_yield() - if debugging then - debugoverlay.Cross( data.corner1, 5, 10, Color( 0, 255, 0 ), true ) - debugoverlay.Cross( data.corner2, 5, 10, Color( 0, 255, 0 ), true ) + local justNewAreas = {} + local justNewAreasSeq = {} + for _, data in pairs( vecsToPlace ) do + coroutine_yield() + if debugging then + debugoverlay.Cross( data.corner1, 5, 10, Color( 0, 255, 0 ), true ) + debugoverlay.Cross( data.corner2, 5, 10, Color( 0, 255, 0 ), true ) - end - local newArea = navmesh.CreateNavArea( data.corner1, data.corner2 ) - table.insert( justNewAreasSeq, newArea ) - justNewAreas[newArea] = true - data.newArea = newArea - if data.crouch then - newArea:AddAttributes( NAV_MESH_CROUCH ) + end + local newArea = navmesh.CreateNavArea( data.corner1, data.corner2 ) + table.insert( justNewAreasSeq, newArea ) + justNewAreas[newArea] = true + data.newArea = newArea + if data.crouch then + newArea:AddAttributes( NAV_MESH_CROUCH ) - end end + end - debugPrint( "Connecting placed areas..." ) - coroutine_yield( "wait" ) + debugPrint( "Connecting placed areas..." ) + coroutine_yield( "wait" ) - local additionalSize = math_max( patchTbl.gridSize, 25 ) - local additional = Vector( additionalSize, additionalSize, additionalSize ) + local additionalSize = math_max( patchTbl.gridSize, 25 ) + local additional = Vector( additionalSize, additionalSize, additionalSize ) + + for _, data in pairs( vecsToPlace ) do + coroutine_yield() + local upOff = patchTbl.upCrouch / 2 + local newArea = data.newArea + local mins, maxs = navGetBounds( newArea ) + for _, otherArea in ipairs( navmesh.FindInBox( mins + -additional, maxs + additional ) ) do + local trivialDist -- defaults to 5 in following navpatcher + if not justNewAreas[otherArea] then + trivialDist = math_max( 25, patchTbl.gridSize ) -- not a new area, allow long connections! - for _, data in pairs( vecsToPlace ) do - coroutine_yield() - local upOff = patchTbl.upCrouch / 2 - local newArea = data.newArea - local mins, maxs = navGetBounds( newArea ) - for _, otherArea in ipairs( navmesh.FindInBox( mins + -additional, maxs + additional ) ) do - local trivialDist -- defaults to 5 in following navpatcher - if not justNewAreas[otherArea] then - trivialDist = math_max( 25, patchTbl.gridSize ) -- not a new area, allow long connections! - - end - coroutine_yield() - local connectable1 = terminator_Extras.AreasAreConnectable( newArea, otherArea, upOff, trivialDist ) - local connectable2 = terminator_Extras.AreasAreConnectable( otherArea, newArea, upOff, trivialDist ) - if connectable1 then - newArea:ConnectTo( otherArea ) - - end - if connectable2 then - otherArea:ConnectTo( newArea ) - - end end - end + coroutine_yield() + local connectable1 = terminator_Extras.AreasAreConnectable( newArea, otherArea, upOff, trivialDist ) + local connectable2 = terminator_Extras.AreasAreConnectable( otherArea, newArea, upOff, trivialDist ) + if connectable1 then + newArea:ConnectTo( otherArea ) - debugPrint( "Merging placed areas..." ) - coroutine_yield( "wait" ) + end + if connectable2 then + otherArea:ConnectTo( newArea ) - merged = true - while merged do - coroutine_yield() - merged = nil - for _, area in ipairs( justNewAreasSeq ) do - if not IsValid( area ) then continue end - for _, neighbor in ipairs( area:GetAdjacentAreas() ) do - merged, _, mergedArea = terminator_Extras.navmeshAttemptMerge( area, neighbor ) - if merged then - table.insert( justNewAreasSeq, mergedArea ) - coroutine_yield( "wait" ) - break - - end - end - if merged then - break - - end end end - validatedAreas = {} + end + + debugPrint( "Merging placed areas..." ) + coroutine_yield( "wait" ) + + merged = true + while merged do + coroutine_yield() + merged = nil for _, area in ipairs( justNewAreasSeq ) do - if IsValid( area ) then - table.insert( validatedAreas, area ) + if not IsValid( area ) then continue end + for _, neighbor in ipairs( area:GetAdjacentAreas() ) do + merged, _, mergedArea = terminator_Extras.navmeshAttemptMerge( area, neighbor ) + if not merged then continue end + + table.insert( justNewAreasSeq, mergedArea ) + coroutine_yield( "wait" ) + break + + end + if merged then + break end end + end + validatedAreas = {} + for _, area in ipairs( justNewAreasSeq ) do + if IsValid( area ) then + table.insert( validatedAreas, area ) - debugPrint( "checking for attributes..." ) + end + end - local trStruc = { - filter = filterFunc, - } - local upCrouchCheck = Vector( 0, 0, 10 ) - local endOffset = Vector( 0, 0, patchTbl.headroomStandRaw ) - for _, area in ipairs( validatedAreas ) do - local areasCenter = area:GetCenter() - trStruc.start = areasCenter + upCrouchCheck - trStruc.endpos = areasCenter + endOffset + debugPrint( "checking for attributes..." ) - local result = util.TraceLine( trStruc ) - if result.Hit then - area:AddAttributes( NAV_MESH_CROUCH ) + local trStruc = { + filter = filterFunc, + } + local upCrouchCheck = Vector( 0, 0, 10 ) + local endOffset = Vector( 0, 0, patchTbl.headroomStandRaw ) + for _, area in ipairs( validatedAreas ) do + local areasCenter = area:GetCenter() + trStruc.start = areasCenter + upCrouchCheck + trStruc.endpos = areasCenter + endOffset + + local result = util.TraceLine( trStruc ) + if result.Hit then + area:AddAttributes( NAV_MESH_CROUCH ) - end end end - terminator_Extras.IsLivePatching = nil - coroutine_yield( "waitlong" ) + end + terminator_Extras.IsLivePatching = nil + coroutine_yield( "waitlong" ) - local areaCreatedCount = #validatedAreas - hook.Run( "terminator_areapatcher_doneapatch", validatedAreas, areaCreatedCount ) + local areaCreatedCount = #validatedAreas + hook.Run( "terminator_areapatcher_doneapatch", validatedAreas, areaCreatedCount ) + +end + +-- Coroutine function to handle patching regions one-by-one +local function patchCoroutine() + while #regionsQueue > 0 do + patchThink() end @@ -745,38 +750,39 @@ function terminator_Extras.AddRegionToPatch( pos1, pos2, currGridSize ) thread = coroutine.create( patchCoroutine ) end - if thread then - local oldTime = SysTime() - - while math_abs( oldTime - SysTime() ) < areaPatchingRate do - inCoroutine = true - local noErrors, result = coroutine_resume( thread ) - inCoroutine = nil - if noErrors == false then -- errored - thread = nil - terminator_Extras.IsLivePatching = nil - ErrorNoHaltWithStack( result ) - break - elseif result == "wait" then -- it wants us to wait a tick - break + if not thread then return end - elseif result == "waitlong" then -- it wants us to wait a bit - nextThink = CurTime() + 0.5 - break + local oldTime = SysTime() - elseif result == "done" then -- all finished, clean up hook - thread = nil - terminator_Extras.IsLivePatching = nil - hook.Remove( "Think", "PatchThinkHook" ) - timer.Simple( 30, function() -- clean this up if patching stops - if terminator_Extras.IsLivePatching then return end - patchCleanup() + while math_abs( oldTime - SysTime() ) < areaPatchingRate do + inCoroutine = true + local noErrors, result = coroutine_resume( thread ) + inCoroutine = nil + if noErrors == false then -- errored + thread = nil + terminator_Extras.IsLivePatching = nil + ErrorNoHaltWithStack( result ) + break - end ) - break + elseif result == "wait" then -- it wants us to wait a tick + break + + elseif result == "waitlong" then -- it wants us to wait a bit + nextThink = CurTime() + 0.5 + break + + elseif result == "done" then -- all finished, clean up hook + thread = nil + terminator_Extras.IsLivePatching = nil + hook.Remove( "Think", "PatchThinkHook" ) + timer.Simple( 30, function() -- clean this up if patching stops + if terminator_Extras.IsLivePatching then return end + patchCleanup() + + end ) + break - end end end end ) @@ -836,4 +842,4 @@ concommand.Add( "terminator_areapatch_here", function( ply, _, args ) end -end, nil, "Patch a nav region centered at your crosshair using smallSize and specified grid size (default 11.25, superadmin only)") +end, nil, "Patch a nav region centered at your crosshair using smallSize and specified grid size (default 11.25, superadmin only)" ) diff --git a/lua/autorun/server/terminator_following_navpatcher.lua b/lua/autorun/server/terminator_following_navpatcher.lua index c2fc48a..2796dca 100644 --- a/lua/autorun/server/terminator_following_navpatcher.lua +++ b/lua/autorun/server/terminator_following_navpatcher.lua @@ -508,13 +508,13 @@ local function navPatchSelectivelyThink() -- we should always patch people being chased if we can! for _, ply in player.Iterator() do local lowCount = #playersToPatch < max - local chasedUntil = plysCurrentlyBeingChased[ ply ] + local chasedUntil = plysCurrentlyBeingChased[ply] if not lowCount then break elseif chasedUntil and chasedUntil > cur then table_insert( playersToPatch, ply ) - chasedPlayers[ ply ] = true + chasedPlayers[ply] = true someoneWasChased = true end @@ -526,7 +526,7 @@ local function navPatchSelectivelyThink() local lowCount = #playersToPatch < max if not lowCount then break - elseif not chasedPlayers[ ply ] and not ply:IsFlagSet( FL_NOTARGET ) then + elseif not chasedPlayers[ply] and not ply:IsFlagSet( FL_NOTARGET ) then table_insert( playersToPatch, ply ) end @@ -549,7 +549,7 @@ hook.Add( "terminator_nextbot_oneterm_exists", "setup_following_navpatcher", fun hook.Add( "terminator_enemythink", "terminator_cacheplysbeingchased", function( _, enemy ) if not IsValid( enemy ) then return end if not enemy:IsPlayer() then return end - plysCurrentlyBeingChased[ enemy ] = CurTime() + 5 + plysCurrentlyBeingChased[enemy] = CurTime() + 5 end ) end ) diff --git a/lua/autorun/server/terminator_funcs.lua b/lua/autorun/server/terminator_funcs.lua index 2dc3e1d..d8fb6bf 100644 --- a/lua/autorun/server/terminator_funcs.lua +++ b/lua/autorun/server/terminator_funcs.lua @@ -6,7 +6,7 @@ local plyMeta = FindMetaTable( "Player" ) local vecMeta = FindMetaTable( "Vector" ) local IsValid = IsValid -local negativeFiveHundredZ = Vector( 0,0,-500 ) +local negativeFiveHundredZ = Vector( 0, 0, -500 ) local solidMask = bit.bor( MASK_SOLID, CONTENTS_MONSTERCLIP ) local vec_zero = Vector( 0, 0, 0 ) @@ -96,7 +96,7 @@ terminator_Extras.PitchToPos = function( pos1, ang1, pos2, ang2 ) local len = localPos:Length() if len < 0 then return 0 end - return rad2deg * math.asin(localPos.z / len) + return rad2deg * math.asin( localPos.z / len ) end @@ -230,7 +230,7 @@ terminator_Extras.areaIsInterruptingSomeone = function( area, areasCenter, yield end end ---[[ find memory leaks! +--[[find memory leaks! for _, ent in ipairs( ents.FindByClass( "terminator_nextbot*" ) ) do local biggestSize = 0 local biggestKey diff --git a/lua/autorun/server/terminator_optimizerhack.lua b/lua/autorun/server/terminator_optimizerhack.lua index 3cce44b..3100f26 100644 --- a/lua/autorun/server/terminator_optimizerhack.lua +++ b/lua/autorun/server/terminator_optimizerhack.lua @@ -22,7 +22,7 @@ local bit_band = bit.band terminator_Extras = terminator_Extras or {} local terminator_Extras = terminator_Extras -local cornerIndexes = { 0,1,2,3 } +local cornerIndexes = { 0, 1, 2, 3 } local fiveSqared = 5^2 local function navSurfaceArea( navArea ) @@ -214,7 +214,7 @@ function terminator_Extras.navmeshAttemptMerge( start, next ) if ( IsConnected( start, twoWayArea ) or IsConnected( next, twoWayArea ) ) or ( IsConnected( twoWayArea, start ) or IsConnected( twoWayArea, next ) ) then table.insert( twoWayConnections, #twoWayConnections + 1, twoWayArea ) - connectionsFrom[ key ] = nil + connectionsFrom[key] = nil end end diff --git a/lua/autorun/server/terminator_weapon_dropper.lua b/lua/autorun/server/terminator_weapon_dropper.lua index e36eea8..2595ffc 100644 --- a/lua/autorun/server/terminator_weapon_dropper.lua +++ b/lua/autorun/server/terminator_weapon_dropper.lua @@ -53,7 +53,7 @@ local function setDropWeapons( ply, attacker, _ ) for _, wep in ipairs( weapsToDrop ) do if not IsValid( wep ) then continue end - if wep.ShouldDropOnDie and wep:ShouldDropOnDie() == false and not termHunter_WeaponAnalogs[ wep:GetClass() ] then continue end + if wep.ShouldDropOnDie and wep:ShouldDropOnDie() == false and not termHunter_WeaponAnalogs[wep:GetClass()] then continue end -- create a new weapon.... spaget -- pretty sure DropWeapon doesnt work this late into player's death tho diff --git a/lua/autorun/terminator_weapanalog_weightsystem.lua b/lua/autorun/terminator_weapanalog_weightsystem.lua index f838dd7..bd84255 100644 --- a/lua/autorun/terminator_weapanalog_weightsystem.lua +++ b/lua/autorun/terminator_weapanalog_weightsystem.lua @@ -29,16 +29,16 @@ terminator_Extras.SetupAnalogWeight = function( wep ) class = string.TrimLeft( class, "weapons/" ) -- HACK HACK HACK class = string.TrimRight( class, "_term" ) -- HACK - local weight = terminator_Extras.EngineAnalogWeights[ class ] + local weight = terminator_Extras.EngineAnalogWeights[class] if not weight then - terminator_Extras.EngineAnalogWeights[ class ] = wep.Weight - --print( terminator_Extras.EngineAnalogWeights[ class ] ) + terminator_Extras.EngineAnalogWeights[class] = wep.Weight + --print( terminator_Extras.EngineAnalogWeights[class] ) end end terminator_Extras.OverrideWeaponWeight = function( class, newWeight ) - terminator_Extras.EngineAnalogWeights[ class ] = newWeight + terminator_Extras.EngineAnalogWeights[class] = newWeight end diff --git a/lua/effects/eff_term_goodarc.lua b/lua/effects/eff_term_goodarc.lua index 8191070..922c3e1 100644 --- a/lua/effects/eff_term_goodarc.lua +++ b/lua/effects/eff_term_goodarc.lua @@ -167,17 +167,17 @@ function EFFECT:PlaySound() local vol = math.Clamp( 0.3 * self.Scale, 0, 1 ) local pitch = math.Clamp( 120 - self.Scale * 15 + math.random( -10, 10 ), 50, 200 ) - sound.Play( ZapSounds[ math.random( #ZapSounds ) ], self.EndPos, 75, pitch, vol ) + sound.Play( ZapSounds[math.random( #ZapSounds )], self.EndPos, 75, pitch, vol ) if self.Scale >= 1 and math.random() > 0.5 then - sound.Play( SparkSounds[ math.random( #SparkSounds ) ], self.EndPos, 70, pitch + math.random( -20, 20 ), vol * 0.6 ) + sound.Play( SparkSounds[math.random( #SparkSounds )], self.EndPos, 70, pitch + math.random( -20, 20 ), vol * 0.6 ) end if self.ParentMode then local pos = self.StartPos timer.Simple( 0.02, function() - sound.Play( ZapSounds[ math.random( #ZapSounds ) ], pos, 70, pitch + 10, vol * 0.5 ) + sound.Play( ZapSounds[math.random( #ZapSounds )], pos, 70, pitch + 10, vol * 0.5 ) end ) end @@ -307,7 +307,7 @@ function EFFECT:GenerateSegmentPoints( startPos, endPos, segCount, jitterFunc, s end - points[ #points + 1 ] = point + points[#points + 1] = point end @@ -338,14 +338,14 @@ function EFFECT:GenerateArc() self.EndPos = hitPoint self.SegmentCount = hitSegment if not self.NoDecal then - local scorchStart = points[ #points ] + local scorchStart = points[#points] local decalPath = self.Scale >= math.Rand( 1.5, 3 ) and "Scorch" or "SmallScorch" util.Decal( decalPath, scorchStart, self.EndPos ) end end - points[ #points + 1 ] = self.EndPos + points[#points + 1] = self.EndPos self.Points = points if not self.NoBranches and self.BranchCount > 0 and #points >= 4 then @@ -368,13 +368,13 @@ function EFFECT:GenerateBranches( mainDir, mainLen ) local branchStartPoint = math.random( 2, lastValidPoint ) for _ = 1, 10 do - if not used[ branchStartPoint ] then break end + if not used[branchStartPoint] then break end branchStartPoint = math.random( 2, lastValidPoint ) end - used[ branchStartPoint ] = true + used[branchStartPoint] = true - local start = points[ branchStartPoint ] + local start = points[branchStartPoint] local branchDir = VectorRand() + mainDir * 0.2 branchDir:Normalize() local len = mainLen * math.Rand( 0.15, 0.4 ) @@ -388,11 +388,11 @@ function EFFECT:GenerateBranches( mainDir, mainLen ) local branchEnd = start + branchDir * len local branch = self:GenerateSegmentPoints( start, branchEnd, segs, branchJitterFunc, nil ) branch.width = math.Rand( 0.4, 0.7 ) - branches[ #branches + 1 ] = branch + branches[#branches + 1] = branch -- Sub-branch if segs >= 3 and math.random() > 0.5 and #branch >= 2 then - local subStart = branch[ 2 ] + local subStart = branch[2] local subDir = VectorRand() + branchDir * 0.1 subDir:Normalize() @@ -406,7 +406,7 @@ function EFFECT:GenerateBranches( mainDir, mainLen ) local subEnd = subStart + subDir * subLen local sub = self:GenerateSegmentPoints( subStart, subEnd, 2, subJitterFunc, nil ) sub.width = branch.width * 0.5 - branches[ #branches + 1 ] = sub + branches[#branches + 1] = sub end end @@ -452,8 +452,8 @@ function EFFECT:Render() -- Main arc for i = 1, #points - 1 do - render.DrawBeam( points[ i ], points[ i + 1 ], width, 0, 1, renderCol ) - render.DrawBeam( points[ i ], points[ i + 1 ], width * 0.3, 0, 1, coreCol ) + render.DrawBeam( points[i], points[i + 1], width, 0, 1, renderCol ) + render.DrawBeam( points[i], points[i + 1], width * 0.3, 0, 1, coreCol ) end @@ -467,8 +467,8 @@ function EFFECT:Render() for i = 1, segCount do local taper = 1 - ( ( i - 1 ) / segCount ) * 0.5 local w = bw * taper - render.DrawBeam( branch[ i ], branch[ i + 1 ], w, 0, 1, renderCol ) - render.DrawBeam( branch[ i ], branch[ i + 1 ], w * 0.3, 0, 1, coreCol ) + render.DrawBeam( branch[i], branch[i + 1], w, 0, 1, renderCol ) + render.DrawBeam( branch[i], branch[i + 1], w * 0.3, 0, 1, coreCol ) end end diff --git a/lua/entities/terminator_nextbot/behaviouroverrides.lua b/lua/entities/terminator_nextbot/behaviouroverrides.lua index 72837c5..fe25fd7 100644 --- a/lua/entities/terminator_nextbot/behaviouroverrides.lua +++ b/lua/entities/terminator_nextbot/behaviouroverrides.lua @@ -133,7 +133,7 @@ function ENT:BehaveUpdate( interval ) threads.motionCor = nil threads.playerControlCor = nil threads.disabledCor = { - cor = coroutine.create( function( self, myTbl ) myTbl.DisabledBehaviourCoroutine( self, myTbl ) end, self:GetClass() ), + cor = coroutine.create( function( selfCor, myTblCor ) myTbl.DisabledBehaviourCoroutine( selfCor, myTblCor ) end, self:GetClass() ), } end @@ -168,7 +168,7 @@ function ENT:BehaveUpdate( interval ) threads.motionCor = nil threads.disabledCor = nil threads.playerControlCor = { - cor = coroutine.create( function( self, myTbl ) myTbl.BehaviourPlayerControlCoroutine( self, myTbl ) end, self:GetClass() ), + cor = coroutine.create( function( selfCor, myTblCor ) myTbl.BehaviourPlayerControlCoroutine( selfCor, myTblCor ) end, self:GetClass() ), } end @@ -179,47 +179,47 @@ function ENT:BehaveUpdate( interval ) if not threads.priorityCor then updated = true threads.priorityCor = { - cor = coroutine.create( function( self, myTbl ) myTbl.BehaviourPriorityCoroutine( self, myTbl ) end, self:GetClass() ), + cor = coroutine.create( function( selfCor, myTblCor ) myTbl.BehaviourPriorityCoroutine( selfCor, myTblCor ) end, self:GetClass() ), } end if not threads.motionCor then updated = true threads.motionCor = { - cor = coroutine.create( function( self, myTbl ) myTbl.BehaviourMotionCoroutine( self, myTbl ) end, self:GetClass() ), - onDone = function( self, myTbl ) - local demanded = myTbl.m_PathUpdatesDemanded + cor = coroutine.create( function( selfCor, myTblCor ) myTbl.BehaviourMotionCoroutine( selfCor, myTblCor ) end, self:GetClass() ), + onDone = function( _selfCor, myTblCor ) + local demanded = myTblCor.m_PathUpdatesDemanded if demanded <= 0 then return end - myTbl.m_PathUpdatesDemanded = demanded - 1 + myTblCor.m_PathUpdatesDemanded = demanded - 1 end, - whenBusy = function( self, myTbl, lastOne ) -- horrible, terrible hacks to fix equally horrible terrible visual stuttering when low CoroutineThresh bots are pathing - local demanded = myTbl.m_PathUpdatesDemanded + whenBusy = function( _selfCor, myTblCor, _lastOne ) -- horrible, terrible hacks to fix equally horrible terrible visual stuttering when low CoroutineThresh bots are pathing + local demanded = myTblCor.m_PathUpdatesDemanded if demanded <= 0 then return end - if myTbl.IsFodder then -- ratelimit fodder path updates - local nextUpdate = myTbl.m_NextPathUpdate or 0 + if myTblCor.IsFodder then -- ratelimit fodder path updates + local nextUpdate = myTblCor.m_NextPathUpdate or 0 local cur = CurTime() if nextUpdate > cur then return end - myTbl.m_NextPathUpdate = cur + pathUpdateIntervalFodder + myTblCor.m_NextPathUpdate = cur + pathUpdateIntervalFodder end - local path = myTbl.GetPath( self ) + local path = myTblCor.GetPath( self ) if not path or not pathMeta.IsValid( path ) then return end local currSegment = pathMeta.GetCurrentGoal( path ) local currType = currSegment.type local laddering = currType == 4 or currType == 5 if laddering then - myTbl.TermHandleLadder( self ) + myTblCor.TermHandleLadder( self ) return end - local loco = myTbl.loco + local loco = myTblCor.loco -- was setting bot's angle to their angle before the path:Update, but that was breaking prediction/velocity somehow -- this as it turns out, is the correct way to stop it from turning towards the path diff --git a/lua/entities/terminator_nextbot/compatibilityhacks.lua b/lua/entities/terminator_nextbot/compatibilityhacks.lua index d65a6f2..eb9efa1 100644 --- a/lua/entities/terminator_nextbot/compatibilityhacks.lua +++ b/lua/entities/terminator_nextbot/compatibilityhacks.lua @@ -21,17 +21,20 @@ if SERVER then function ENT:fakeVjBaseWeaponFiring( wep ) -- copied code from vj base github, hope it doesnt change in future and break timer.Simple( wep.NPC_TimeUntilFire, function() - if IsValid( wep ) and IsValid( self ) and IsValid( self:GetOwner() ) and CurTime() > wep.NPC_NextPrimaryFireT then - wep:PrimaryAttack() - if wep.NPC_NextPrimaryFire == false then return end -- Support for animation events - wep.NPC_NextPrimaryFireT = CurTime() + wep.NPC_NextPrimaryFire - for _, tv in ipairs( wep.NPC_TimeUntilFireExtraTimers ) do - timer.Simple( tv, function() - if not IsValid( wep ) or not IsValid( self ) or wep:NPCAbleToShoot() ~= true then return end - wep:PrimaryAttack() - - end ) - end + if not IsValid( wep ) then return end + if not IsValid( self ) then return end + if not IsValid( self:GetOwner() ) then return end + if CurTime() <= wep.NPC_NextPrimaryFireT then return end + + wep:PrimaryAttack() + if wep.NPC_NextPrimaryFire == false then return end -- Support for animation events + wep.NPC_NextPrimaryFireT = CurTime() + wep.NPC_NextPrimaryFire + for _, tv in ipairs( wep.NPC_TimeUntilFireExtraTimers ) do + timer.Simple( tv, function() + if not IsValid( wep ) or not IsValid( self ) or wep:NPCAbleToShoot() ~= true then return end + wep:PrimaryAttack() + + end ) end end ) end @@ -227,7 +230,7 @@ if SERVER then ["models/items/healthkit.mdl"] = true, } - local medkitOffset = Angle( 0,0,-90 ) + local medkitOffset = Angle( 0, 0, -90 ) hook.Add( "terminator_holstering_overrideangles", "fixthe_god_damn_MEDKITS!", function( model ) if not medkitModels[model] then return end return medkitOffset diff --git a/lua/entities/terminator_nextbot/enemyoverrides.lua b/lua/entities/terminator_nextbot/enemyoverrides.lua index 8b12779..667e777 100644 --- a/lua/entities/terminator_nextbot/enemyoverrides.lua +++ b/lua/entities/terminator_nextbot/enemyoverrides.lua @@ -156,6 +156,56 @@ do end + local function shootPosFromHitboxes( self, ent, random, entsTbl ) + local sets = entMeta.GetHitboxSetCount( ent ) + if sets then + local hitboxes = {} + local data = entsTbl.term_cachedHitboxData or nil + + if not data then + for num1 = 0, sets - 1 do + for num2 = 0, entMeta.GetHitBoxCount( ent, num1 ) - 1 do + local group = entMeta.GetHitBoxHitGroup( ent, num2, num1 ) + + hitboxes[group] = hitboxes[group] or {} + hitboxes[group][#hitboxes[group] + 1] = { entMeta.GetHitBoxBone( ent, num2, num1 ), entMeta.GetHitBoxBounds( ent, num2, num1 ) } + + end + end + + if hitboxes[HITGROUP_HEAD] then + data = hitboxes[HITGROUP_HEAD][random and math.random( #hitboxes[HITGROUP_HEAD] ) or 1] + + elseif hitboxes[HITGROUP_CHEST] then + data = hitboxes[HITGROUP_CHEST][random and math.random( #hitboxes[HITGROUP_CHEST] ) or 1] + + elseif hitboxes[HITGROUP_GENERIC] then + data = hitboxes[HITGROUP_GENERIC][random and math.random( #hitboxes[HITGROUP_GENERIC] ) or 1] + + end + entsTbl.term_cachedHitboxData = data + -- just in case their model changes + timer.Simple( math.Rand( 5, 10 ), function() + if not IsValid( self ) then return end + if not IsValid( ent ) then return end + entsTbl.term_cachedHitboxData = nil + + end ) + end + + if data then + local bonem = entMeta.GetBoneMatrix( ent, data[1] ) + if bonem then + local theCenter = data[2] + ( data[3] - data[2] ) / 2 + + local pos = LocalToWorld( theCenter, angle_zero, matrixMeta.GetTranslation( bonem ), matrixMeta.GetAngles( bonem ) ) + return cacheEntShootPos( ent, entsTbl, pos ) + + end + end + end + end + --[[------------------------------------ Name: ENT:EntShootPos Desc: Gets another npc/player/nextbot's shoot position. @@ -184,53 +234,9 @@ do local isCrouchingPlayer = isPly and ent:Crouching() if not isCrouchingPlayer then - local sets = entMeta.GetHitboxSetCount( ent ) - if sets then - local hitboxes = {} - local data = entsTbl.term_cachedHitboxData or nil - - if not data then - for num1 = 0, sets - 1 do - for num2 = 0, entMeta.GetHitBoxCount( ent, num1 ) - 1 do - local group = entMeta.GetHitBoxHitGroup( ent, num2, num1 ) - - hitboxes[group] = hitboxes[group] or {} - hitboxes[group][#hitboxes[group] + 1] = { entMeta.GetHitBoxBone( ent, num2, num1 ), entMeta.GetHitBoxBounds( ent, num2, num1 ) } - - end - end + local shootPos = shootPosFromHitboxes( self, ent, random, entsTbl ) + if shootPos then return shootPos end - if hitboxes[HITGROUP_HEAD] then - data = hitboxes[HITGROUP_HEAD][ random and math.random( #hitboxes[HITGROUP_HEAD] ) or 1 ] - - elseif hitboxes[HITGROUP_CHEST] then - data = hitboxes[HITGROUP_CHEST][ random and math.random( #hitboxes[HITGROUP_CHEST] ) or 1 ] - - elseif hitboxes[HITGROUP_GENERIC] then - data = hitboxes[HITGROUP_GENERIC][ random and math.random( #hitboxes[HITGROUP_GENERIC] ) or 1 ] - - end - entsTbl.term_cachedHitboxData = data - -- just in case their model changes - timer.Simple( math.Rand( 5, 10 ), function() - if not IsValid( self ) then return end - if not IsValid( ent ) then return end - entsTbl.term_cachedHitboxData = nil - - end ) - end - - if data then - local bonem = entMeta.GetBoneMatrix( ent, data[1] ) - if bonem then - local theCenter = data[2] + ( data[3] - data[2] ) / 2 - - local pos = LocalToWorld( theCenter, angle_zero, matrixMeta.GetTranslation( bonem ), matrixMeta.GetAngles( bonem ) ) - return cacheEntShootPos( ent, entsTbl, pos ) - - end - end - end end --debugoverlay.Cross( ent:WorldSpaceCenter(), 5, 10, Color( 255,255,255 ), true ) @@ -510,7 +516,7 @@ do local coroutine_yield = coroutine.yield local function processFindingEnt( self, myTbl, ent, fodder, myFov, ShouldBeEnemy, CanSeePosition, UpdateEnemyMemory, EntShootPos ) - if notEnemyCache[ ent ] then return end + if notEnemyCache[ent] then return end if ent == self then return end @@ -660,7 +666,7 @@ do d, priority = entr[1], entr[2] end - local classr = myTbl.m_ClassRelationships[ entMeta.GetClass( ent )] + local classr = myTbl.m_ClassRelationships[entMeta.GetClass( ent )] if classr and ( not priority or classr[2] > priority ) then d, priority = classr[1], classr[2] end @@ -1086,14 +1092,14 @@ function ENT:validSoundHint() if interesting then return true end local id = emitter:GetCreationID() - local oldCount = self.heardThingCounts[ id ] or 0 - self.heardThingCounts[ id ] = oldCount + 1 + local oldCount = self.heardThingCounts[id] or 0 + self.heardThingCounts[id] = oldCount + 1 --print( emitter, oldCount ) timer.Simple( 120, function() if not IsValid( self ) then return end - self.heardThingCounts[ id ] = self.heardThingCounts[ id ] + -1 + self.heardThingCounts[id] = self.heardThingCounts[id] + -1 end ) @@ -1128,7 +1134,7 @@ function ENT:RegisterForcedEnemyCheckPos( enemy ) end end - checkPositions[ enemy:GetCreationID() ] = enemy:GetPos() + checkPositions[enemy:GetCreationID()] = enemy:GetPos() end diff --git a/lua/entities/terminator_nextbot/footsteps.lua b/lua/entities/terminator_nextbot/footsteps.lua index ed47438..10d8577 100644 --- a/lua/entities/terminator_nextbot/footsteps.lua +++ b/lua/entities/terminator_nextbot/footsteps.lua @@ -19,7 +19,7 @@ local TrFilterNoSelf = terminator_Extras.TrFilterNoSelf -- CUSTOM FOOTSTEP SOUNDS PREFAB, say you just want footsteps to play your custom sound, not player footsteps -- ENT.Term_FootstepMode = "custom" -- set this to custom so the bot checks the below tbl, supported modes, "human" and "custom" ---[[ ENT.Term_FootstepSound = { -- sound to play when the bot steps +--[[ENT.Term_FootstepSound = { -- sound to play when the bot steps path = "npc/zombie_poison/pz_left_foot1.wav", pitch = 85, -- pitch of the sound volume = 1, -- volume of the sound diff --git a/lua/entities/terminator_nextbot/motionoverrides.lua b/lua/entities/terminator_nextbot/motionoverrides.lua index 550cb78..ed8be66 100644 --- a/lua/entities/terminator_nextbot/motionoverrides.lua +++ b/lua/entities/terminator_nextbot/motionoverrides.lua @@ -523,7 +523,8 @@ function ENT:OnUnStuck() self.m_StuckTime = CurTime() + 1 self.m_StuckTime2 = 0 - self:RunTask("OnUnStuck") + self:RunTask( "OnUnStuck" ) + end function ENT:isUnderWater() @@ -533,7 +534,7 @@ function ENT:isUnderWater() end -local vectorPositive125Z = Vector( 0,0,125 ) +local vectorPositive125Z = Vector( 0, 0, 125 ) function ENT:confinedSlope( area1, area2 ) if not IsValid( area1 ) then return end @@ -955,8 +956,8 @@ function ENT:CanStandAtPos( myTbl, pos, endPos ) end -local fivePositiveZ = Vector( 0,0,5 ) -local fiftyZOffset = Vector( 0,0,50 ) +local fivePositiveZ = Vector( 0, 0, 5 ) +local fiftyZOffset = Vector( 0, 0, 50 ) local vector25Z = Vector( 0, 0, 25 ) function ENT:BoundsAdjusted( hullSizeMul, assumeCrouch ) @@ -1011,7 +1012,7 @@ function ENT:PosThatWillBringUsTowards( startPos, aheadPos, maxAttempts ) local cacheTime = 0.8 - local b1,b2 = self:BoundsAdjusted( 0.75 ) + local b1, b2 = self:BoundsAdjusted( 0.75 ) local mask = self:GetSolidMask() local cgroup = self:GetCollisionGroup() @@ -1202,16 +1203,16 @@ function ENT:PosThatWillBringUsTowards( startPos, aheadPos, maxAttempts ) end end if currScore then - potentialClearPositionsScored[ currScore ] = newStartPos + potentialClearPositionsScored[currScore] = newStartPos - potentialClearHitPositions[ currScore ] = dirResult.HitPos - potentialClearPositionFractions[ currScore ] = fractionCurr + potentialClearHitPositions[currScore] = dirResult.HitPos + potentialClearPositionFractions[currScore] = fractionCurr bestScore = table.maxn( potentialClearPositionsScored ) end end - local bestFraction = potentialClearPositionsScored[ bestScore ] - local bestHitPosition = potentialClearHitPositions[ bestScore ] + local bestFraction = potentialClearPositionsScored[bestScore] + local bestHitPosition = potentialClearHitPositions[bestScore] if not bestHitPosition then return nil, true end @@ -1253,7 +1254,7 @@ local scalar = 0.75 -- simple check, can the bot exist left/right in the direction of the goal. function ENT:CanStepAside( dir, goal ) local pos = self:GetPos() + vec_up15 - local b1,b2 = self:BoundsAdjusted( scalar ) + local b1, b2 = self:BoundsAdjusted( scalar ) local mask = self:GetSolidMask() local cgroup = self:GetCollisionGroup() @@ -1323,7 +1324,7 @@ end function ENT:GetJumpBlockState( myTbl, dir, goal ) local enemy = myTbl.GetEnemy( self ) - local b1,b2 = myTbl.BoundsAdjusted( self, scalar ) + local b1, b2 = myTbl.BoundsAdjusted( self, scalar ) local step = myTbl.loco:GetStepHeight() * scalar local pos = entMeta.GetPos( self ) + vec_up15 local cgroup = entMeta.GetCollisionGroup( self ) @@ -1537,7 +1538,7 @@ function ENT:ChooseBasedOnVisible( check, potentiallyVisible ) local swimming = self:IsSwimming( self:GetTable() ) for index = 1, table.maxn( potentiallyVisible ) do - local potentialVisible = potentiallyVisible[ index ] + local potentialVisible = potentiallyVisible[index] if not potentialVisible then continue end if potentialVisible.z < check.z or swimming then @@ -1559,7 +1560,7 @@ function ENT:ChooseBasedOnVisible( check, potentiallyVisible ) --debugoverlay.Line( check, potentialVisible, 1, Color( 255,255,255 ), true ) return potentialVisible, index, hitBreakable - else + --else --print( not result.Hit, hitBreakable, result.Entity == enemy ) --debugoverlay.Line( check, result.HitPos, 1, Color( 255,0,0 ), true ) --debugoverlay.Box( result.HitPos, theTrace.mins, theTrace.maxs, 1, Color( 255,255,255, 25 ) ) @@ -3670,7 +3671,7 @@ function ENT:SetupSpeed( myTbl ) speed = myTbl.RunTask( self, "ModifyMovementSpeed", speed ) or speed - myTbl.loco:SetDesiredSpeed(speed) + myTbl.loco:SetDesiredSpeed( speed ) myTbl.m_Speed = speed end @@ -3682,7 +3683,7 @@ end --]]------------------------------------ function ENT:ShouldRun( myTbl ) if myTbl.IsControlledByPlayer( self, myTbl ) then - if self:ControlPlayerKeyDown(IN_SPEED) then + if self:ControlPlayerKeyDown( IN_SPEED ) then return true end @@ -3702,7 +3703,7 @@ end --]]------------------------------------ function ENT:ShouldWalk( myTbl ) if myTbl.IsControlledByPlayer( self, myTbl ) then - if self:ControlPlayerKeyDown(IN_WALK) then + if self:ControlPlayerKeyDown( IN_WALK ) then return true end diff --git a/lua/entities/terminator_nextbot/pathoverrides.lua b/lua/entities/terminator_nextbot/pathoverrides.lua index b06cfe8..a12018b 100644 --- a/lua/entities/terminator_nextbot/pathoverrides.lua +++ b/lua/entities/terminator_nextbot/pathoverrides.lua @@ -178,7 +178,7 @@ function ENT:GetPathHalfwayPoint() if not pathSegs then return myPos end local middlePathSegIndex = math.Round( #pathSegs / 2 ) - local middlePathSeg = pathSegs[ middlePathSegIndex ] + local middlePathSeg = pathSegs[middlePathSegIndex] return middlePathSeg.pos, middlePathSeg end @@ -306,7 +306,7 @@ function ENT:flagConnectionAsShit( area1, area2 ) local connectionsId = getConnId( area1:GetID(), area2:GetID() ) local superShitConnection = nil - if badConnections[ connectionsId ] then superShitConnection = true end + if badConnections[connectionsId] then superShitConnection = true end badConnections[connectionsId] = true lastBadFlags[connectionsId] = CurTime() @@ -377,8 +377,8 @@ function ENT:AddAreasToAvoid( areas, mul ) for _, avoid in ipairs( areas ) do -- lagspike if we try to flank around area that contains the destination if avoid and IsValid( avoid ) and ( avoid ~= myTbl.flankingDest ) then - local oldMul = myTbl.pathAreasAdditionalCost[ avoid:GetID() ] or 0 - myTbl.pathAreasAdditionalCost[ avoid:GetID() ] = oldMul + mul + local oldMul = myTbl.pathAreasAdditionalCost[avoid:GetID()] or 0 + myTbl.pathAreasAdditionalCost[avoid:GetID()] = oldMul + mul --debugoverlay.Cross( avoid:GetCenter(), 10, 10, color_white, true ) end @@ -561,7 +561,7 @@ function ENT:NavMeshPathCostGenerator( locoData, toArea, fromArea, ladder, connD if laddering then return cost end - local additionalCost = locoData.pathAreasAdditionalCost[ toAreasId ] + local additionalCost = locoData.pathAreasAdditionalCost[toAreasId] if additionalCost then cost = cost * additionalCost @@ -733,13 +733,13 @@ function ENT:FloodMarkAsUnreachable( startArea ) if terminator_Extras.IsLivePatching then return end local myTbl = entMeta.GetTable( self ) - local scoreData = {} + local scoreDataSetup = {} local invalidUnreachable local wasABlockedArea = false - scoreData.myArea = myTbl.GetCurrentNavArea( self, myTbl ) - scoreData.decreasingScores = {} - scoreData.droppedDownAreas = {} - scoreData.areasToUnreachable = {} + scoreDataSetup.myArea = myTbl.GetCurrentNavArea( self, myTbl ) + scoreDataSetup.decreasingScores = {} + scoreDataSetup.droppedDownAreas = {} + scoreDataSetup.areasToUnreachable = {} -- find areas around the path's end that we can't reach -- this prevents super obnoxous stutters on maps with tens of thousands of navareas @@ -777,7 +777,7 @@ function ENT:FloodMarkAsUnreachable( startArea ) return score end - self:findValidNavResult( scoreData, startArea, 2000, scoreFunction ) + self:findValidNavResult( scoreDataSetup, startArea, 2000, scoreFunction ) yieldIfWeCan() @@ -788,7 +788,7 @@ function ENT:FloodMarkAsUnreachable( startArea ) -- stop after marking the path dest, IF the unreachable finder was invalid! if not invalidUnreachable then - for _, area in ipairs( scoreData.areasToUnreachable ) do + for _, area in ipairs( scoreDataSetup.areasToUnreachable ) do self:rememberAsUnreachable( area ) end @@ -1117,7 +1117,7 @@ function ENT:findValidNavResult( data, start, radius, scoreFunc, noMoreOptionsMi for _, currClosedId in ipairs( closedSequential ) do local currClosedScore = scores[currClosedId] - if isnumber( currClosedScore ) and currClosedScore > bestClosedScore and isLadder[ currClosedId ] ~= true then + if isnumber( currClosedScore ) and currClosedScore > bestClosedScore and isLadder[currClosedId] ~= true then bestClosedScore = currClosedScore bestClosedAreaId = currClosedId @@ -1181,49 +1181,54 @@ local function adjacentAreasSkippingLadders( area, canUseLadders ) if not canUseLadders then return areaDatas end local ladders = navMeta.GetLadders( area ) - if #ladders > 0 then - local areasCenter = navMeta.GetCenter( area ) - local already = { [GetID( area )] = true } - for _, areaData in ipairs( areaDatas ) do - already[GetID( areaData.area )] = true + if #ladders <= 0 then return areaDatas end + -- Track areas we've already processed to avoid duplicates and to update existing entries with ladder data + local already = { [GetID( area )] = true } + for _, areaData in ipairs( areaDatas ) do + already[GetID( areaData.area )] = true - end - for _, ladder in ipairs( ladders ) do - local ladderAlreadyDone = { [GetID( area )] = true } - local ladderAdjacents = AreaOrLadderGetAdjacentAreas( ladder ) - for _, ladderAdj in ipairs( ladderAdjacents ) do - local ladderAdjID = GetID( ladderAdj ) - if ladderAlreadyDone[ladderAdjID] then continue end - - local adjacentsData - if already[ladderAdjID] then - for _, areaData in ipairs( areaDatas ) do - if areaData.area ~= ladderAdj then continue end - adjacentsData = areaData - break - end + end + for _, ladder in ipairs( ladders ) do + -- Prevent processing the same area multiple times per ladder, + -- since ladders have multiple exit points (top forward/behind/left/right, bottom) + local ladderAlreadyDone = { [GetID( area )] = true } + local ladderAdjacents = AreaOrLadderGetAdjacentAreas( ladder ) + for _, ladderAdj in ipairs( ladderAdjacents ) do + local ladderAdjID = GetID( ladderAdj ) + if ladderAlreadyDone[ladderAdjID] then continue end + + local adjacentsData + -- Check if this area already exists in our results from normal ground adjacency + if already[ladderAdjID] then + for _, areaData in ipairs( areaDatas ) do + if areaData.area ~= ladderAdj then continue end + adjacentsData = areaData + break end - if adjacentsData then - adjacentsData.dist = ladder:GetLength() - adjacentsData.ladder = ladder + end + if adjacentsData then + -- Update existing entry with ladder-specific distance and reference + -- because ladder travel cost should be based on ladder length, not ground distance + adjacentsData.dist = ladder:GetLength() + adjacentsData.ladder = ladder - else - adjacentsData = { - area = ladderAdj, - dist = ladder:GetLength(), - ladder = ladder, - --dir shouldnt need this - } - end + else + -- This area wasn't reachable by ground adjacency, so add it as a new ladder-only connection + adjacentsData = { + area = ladderAdj, + dist = ladder:GetLength(), + ladder = ladder, + } + end - areaDatas[#areaDatas + 1] = adjacentsData - ladderAlreadyDone[ladderAdjID] = true + areaDatas[#areaDatas + 1] = adjacentsData + ladderAlreadyDone[ladderAdjID] = true - end end end return areaDatas + end -- return "path" of navareas that get us where we're going @@ -1440,7 +1445,7 @@ function terminator_Extras.Astar( me, myTbl, startArea, goal, goalArea, NavMeshP removeFrom( neighborsId, closedSequential, closed ) addTo( neighborsId, openedSequential, opened ) - cameFrom[ neighborsId ] = { id = bestId, ladder = neighborDat.ladder, area = bestArea } + cameFrom[neighborsId] = { id = bestId, ladder = neighborDat.ladder, area = bestArea } coroutine_yield( terminator_Extras.BOT_COROUTINE_RESULTS.PATHING ) end @@ -1469,7 +1474,7 @@ local function generatorHack( area, fromArea, _ladder, _elevator, _length ) -- u local fromNext if fromArea then - fromNext = areaCorridorNexts[ GetID( fromArea ) ] + fromNext = areaCorridorNexts[GetID( fromArea )] if not fromNext then -- not on the path allowedDeviations = allowedDeviations + -1 -- allow some deviations if allowedDeviations <= 0 then @@ -1487,7 +1492,7 @@ local function generatorHack( area, fromArea, _ladder, _elevator, _length ) -- u if area then areaId = GetID( area ) - local areaMask = areaCorridorNexts[ areaId ] + local areaMask = areaCorridorNexts[areaId] if not areaMask then return 10000000000000 -- not in corridor, try this last @@ -1550,10 +1555,10 @@ local function AstarCompute( path, me, myTbl, goal, goalArea ) for i, currId in ipairs( corridorIds ) do local nextOne = corridorIds[i + 1] if nextOne then - areaCorridorNexts[ currId ] = nextOne -- force the compute to take the correct path + areaCorridorNexts[currId] = nextOne -- force the compute to take the correct path else - areaCorridorNexts[ currId ] = true + areaCorridorNexts[currId] = true end diff --git a/lua/entities/terminator_nextbot/shared.lua b/lua/entities/terminator_nextbot/shared.lua index 8b90f70..9cb1f11 100644 --- a/lua/entities/terminator_nextbot/shared.lua +++ b/lua/entities/terminator_nextbot/shared.lua @@ -167,7 +167,7 @@ ENT.MetallicMoveSounds = true ENT.Term_FootstepTiming = "timed" -- supported timings, "timed" and "perfect", see footsteps.lua ENT.Term_FootstepMode = "human" -- supported modes, "human" and "custom" ---[[ ENT.Term_FootstepSound = { -- sound that's played if Term_FootstepMode is "custom" +--[[ENT.Term_FootstepSound = { -- sound that's played if Term_FootstepMode is "custom" path = "custom.wav", pitch = 200, -- pitch of the sound volume = 0.1, -- volume of the sound @@ -250,9 +250,9 @@ local extremeUnstucking = CreateConVar( "termhunter_doextremeunstucking", 1, FCV local vec_zero = Vector( 0 ) local vectorUp = Vector( 0, 0, 1 ) local vecFiftyZ = Vector( 0, 0, 50 ) -local negativeFiveHundredZ = Vector( 0,0,-500 ) -local plus25Z = Vector( 0,0,25 ) -local plus15Z = Vector( 0,0,15 ) +local negativeFiveHundredZ = Vector( 0, 0, -500 ) +local plus25Z = Vector( 0, 0, 25 ) +local plus15Z = Vector( 0, 0, 15 ) local vecMeta = FindMetaTable( "Vector" ) local entMeta = FindMetaTable( "Entity" ) @@ -367,12 +367,12 @@ function ENT:AreaIsOrphan( potentialOrphan, ignoreMyNav ) local unConnectedAreasSequential = {} local unConnectedAreas = {} local connectedAreasSequential = {} - local scoreData = {} - scoreData.decreasingScores = {} - scoreData.encounteredABlockedArea = nil - scoreData.encounteredALadder = nil - scoreData.botsJumpHeight = self.loco:GetMaxJumpHeight() - scoreData.loopedBackAreas = loopedBackAreas + local scoreDataSetup = {} + scoreDataSetup.decreasingScores = {} + scoreDataSetup.encounteredABlockedArea = nil + scoreDataSetup.encounteredALadder = nil + scoreDataSetup.botsJumpHeight = self.loco:GetMaxJumpHeight() + scoreDataSetup.loopedBackAreas = loopedBackAreas --debugoverlay.Cross( myNav:GetCenter(), 10, 10, Color( 255, 255, 0 ), true ) @@ -395,7 +395,7 @@ function ENT:AreaIsOrphan( potentialOrphan, ignoreMyNav ) -- good connection else -- just return early if we're in the same group - if not ignoreMyNav and scoreData.loopedBackAreas[ area2 ] then + if not ignoreMyNav and scoreData.loopedBackAreas[area2] then scoreData.sameGroupAsUs = true --print( "SAMEGROUP" ) return math.huge @@ -422,10 +422,10 @@ function ENT:AreaIsOrphan( potentialOrphan, ignoreMyNav ) local checkRadius = 700 - local _, _, escaped = self:findValidNavResult( scoreData, potentialOrphan, checkRadius, scoreFunction, 80 ) + local _, _, escaped = self:findValidNavResult( scoreDataSetup, potentialOrphan, checkRadius, scoreFunction, 80 ) -- who cares if it's an orphan, we can get there - if scoreData.sameGroupAsUs then return nil, scoreData.encounteredABlockedArea end + if scoreDataSetup.sameGroupAsUs then return nil, scoreDataSetup.encounteredABlockedArea end local escapable = escaped @@ -433,13 +433,13 @@ function ENT:AreaIsOrphan( potentialOrphan, ignoreMyNav ) local isSubstantiallySized = checkedSurfaceArea > ( checkRadius^2 ) * 0.15 local isPotentiallyPartOfWhole = lessNoWaysBackThanWaysBack and isSubstantiallySized - --print( "orphanstatus", escapable, scoreData.sameGroupAsUs, isPotentiallyPartOfWhole, isSubstantiallySized ) + --print( "orphanstatus", escapable, scoreDataSetup.sameGroupAsUs, isPotentiallyPartOfWhole, isSubstantiallySized ) -- could not confirm that it's an orphan - if escapable or isPotentiallyPartOfWhole then return nil, scoreData.encounteredABlockedArea end + if escapable or isPotentiallyPartOfWhole then return nil, scoreDataSetup.encounteredABlockedArea end -- is an orphan - return true, scoreData.encounteredABlockedArea + return true, scoreDataSetup.encounteredABlockedArea end @@ -479,20 +479,20 @@ function ENT:EnemyIsBoxedIn() local areasThatAreEntrance = {} for _, entranceArea in ipairs( dangerAreas ) do - areasThatAreEntrance[ entranceArea ] = true + areasThatAreEntrance[entranceArea] = true end -- not boxed in, we're just close - if areasThatAreEntrance[ entranceArea ] then + if areasThatAreEntrance[entranceArea] then resetBoxedIn( self ) return false end - local scoreData = {} - scoreData.hasEscape = nil - scoreData.decreasingScores = {} - scoreData.hasEscape = nil + local scoreDataSetup = {} + scoreDataSetup.hasEscape = nil + scoreDataSetup.decreasingScores = {} + scoreDataSetup.hasEscape = nil local scoreFunction = function( scoreData, area1, area2 ) if area2:IsBlocked() then return 0 end @@ -502,7 +502,7 @@ function ENT:EnemyIsBoxedIn() local area2Id = area2:GetID() local score = scoreData.decreasingScores[area1Id] or 10000 - if areasThatAreEntrance[ area2 ] then + if areasThatAreEntrance[area2] then return 0 end @@ -515,9 +515,9 @@ function ENT:EnemyIsBoxedIn() end local checkRadius = 1350 - local _, _, escaped = self:findValidNavResult( scoreData, enemy:GetPos(), checkRadius, scoreFunction, 10 ) + local _, _, escaped = self:findValidNavResult( scoreDataSetup, enemy:GetPos(), checkRadius, scoreFunction, 10 ) - local boxedIn = not escaped and not scoreData.hasEscape + local boxedIn = not escaped and not scoreDataSetup.hasEscape self.term_CachedIsBoxedIn = boxedIn resetBoxedIn( self ) @@ -1672,8 +1672,8 @@ function ENT:canHitEnt( myTbl, ent ) end -local crouchingOffset = Vector( 0,0,30 ) -local standingOffset = Vector( 0,0,50 ) +local crouchingOffset = Vector( 0, 0, 30 ) +local standingOffset = Vector( 0, 0, 50 ) function ENT:crouchToGetCloserTo( pos ) local myPos = self:GetPos() @@ -1913,7 +1913,7 @@ local function HunterIsStuck( self, myTbl ) local notMoving = myTbl.StuckPos3 and myTbl.StuckPos5 -- laddering? check 3d dist, not 2d dist! if notMoving and myTbl.terminator_HandlingLadder then - notMoving = myPos:DistToSqr( myTbl.StuckPos3 ) < 20^2 and myPos:DistToSqr( myTbl.StuckPos5 ) < 20^2 + notMoving = myPos:DistToSqr( myTbl.StuckPos3 ) < 20^2 and myPos:DistToSqr( myTbl.StuckPos5 ) < 20^2 elseif notMoving then notMoving = DistToSqr2D( myPos, myTbl.StuckPos5 ) < 20^2 and DistToSqr2D( myPos, myTbl.StuckPos3 ) < 20^2 @@ -1991,6 +1991,30 @@ function ENT:IsUnderDisplacement() end +-- Helper function to flag bad connections that caused us to get stuck +-- Also sets bot backtracking path's direction +local function flagBadPathConnections( self, myTbl, myNav, myPos, path, scoreDataSetup ) + local _, aheadSegment = myTbl.GetNextPathArea( self, myNav ) -- top of the jump + if not aheadSegment then return end + + scoreDataSetup.dirToEnd = terminator_Extras.dirToPos( myPos, path:GetEnd() ) + + local currSegment = path:GetCurrentGoal() -- maybe bottom of the jump, paths are stupid + if not currSegment then return end + if not IsValid( aheadSegment.area ) then return end + + local dirPathGoes = myNav:ComputeDirection( aheadSegment.pos ) + local areasInDir = myNav:GetAdjacentAreasAtSide( dirPathGoes ) + + for _, area in ipairs( areasInDir ) do + --debugoverlay.Line( myNav:GetCenter(), area:GetCenter(), 5, Color( 255, 255, 0 ), true ) + myTbl.flagConnectionAsShit( self, myNav, area ) + end + myTbl.flagConnectionAsShit( self, currSegment.area, aheadSegment.area ) + + --debugoverlay.Line( currSegment.area:GetCenter(), aheadSegment.area:GetCenter(), 5, Color( 255, 255, 0 ), true ) +end + --do this so we can override the nextbot's current path function ENT:ControlPath2( AimMode ) local myTbl = self:GetTable() @@ -2030,41 +2054,18 @@ function ENT:ControlPath2( AimMode ) local myNav = myTbl.GetTrueCurrentNavArea( self ) or self:GetCurrentNavArea() if not IsValid( myNav ) then return end --- AAAAH - local scoreData = {} + local scoreDataSetup = {} - scoreData.canDoUnderWater = self:isUnderWater() - scoreData.self = self - scoreData.dirToEnd = self:GetForward() - scoreData.bearingPos = myTbl.startUnstuckPos + scoreDataSetup.canDoUnderWater = self:isUnderWater() + scoreDataSetup.self = self + scoreDataSetup.dirToEnd = self:GetForward() + scoreDataSetup.bearingPos = myTbl.startUnstuckPos coroutine_yield() if validPath then -- we were pathing, time to flag this connection local path = self:GetPath() - local _, aheadSegment = myTbl.GetNextPathArea( self, myNav ) -- top of the jump - local currSegment = path:GetCurrentGoal() -- maybe bottom of the jump, paths are stupid - local dirPathGoes - local areasInDir - - if not aheadSegment then goto skipTheShitConnectionFlag end - - scoreData.dirToEnd = terminator_Extras.dirToPos( myPos, path:GetEnd() ) - if not aheadSegment or not currSegment then goto skipTheShitConnectionFlag end - if not IsValid( aheadSegment.area ) then goto skipTheShitConnectionFlag end - - dirPathGoes = myNav:ComputeDirection( aheadSegment.pos ) - areasInDir = myNav:GetAdjacentAreasAtSide( dirPathGoes ) - - for _, area in ipairs( areasInDir ) do - --debugoverlay.Line( myNav:GetCenter(), area:GetCenter(), 5, Color( 255, 255, 0 ), true ) - myTbl.flagConnectionAsShit( self, myNav, area ) - - end - myTbl.flagConnectionAsShit( self, currSegment.area, aheadSegment.area ) - - --debugoverlay.Line( currSegment.area:GetCenter(), aheadSegment.area:GetCenter(), 5, Color( 255, 255, 0 ), true ) - - ::skipTheShitConnectionFlag:: + flagBadPathConnections( self, myTbl, myNav, myPos, path, scoreDataSetup ) coroutine_yield() @@ -2113,7 +2114,7 @@ function ENT:ControlPath2( AimMode ) coroutine_yield() - local _, escapeArea = self:findValidNavResult( scoreData, myPos, 1000, scoreFunction ) + local _, escapeArea = self:findValidNavResult( scoreDataSetup, myPos, 1000, scoreFunction ) if not IsValid( escapeArea ) then continue end --debugoverlay.Cross( escapeArea:GetCenter(), 50, 100, Color( 255, 255, 0 ), true ) self:SetupPathShell( escapeArea:GetRandomPoint(), true ) @@ -2311,7 +2312,7 @@ function ENT:GetLockedDoorToBash() local doors = self.awarenessLockedDoors for _, currDoor in ipairs( doors ) do - local currAttempts = globalAttempts[ currDoor:GetCreationID() ] or 0 + local currAttempts = globalAttempts[currDoor:GetCreationID()] or 0 if currAttempts < leastAttempts then bestDoor = currDoor leastAttempts = currAttempts @@ -2352,6 +2353,7 @@ function ENT:BashLockedDoor( currentTask ) end -- handle spotting enemy once here, instead of differently in every single task ever +-- returns true if task was ended function ENT:EnemyAcquired( currentTask ) if not IsValid( self ) then return end -- hrm... i dont think i need to start a new task in this case local enemy = self:GetEnemy() @@ -2413,17 +2415,24 @@ function ENT:EnemyAcquired( currentTask ) local doFlank = ( lowOrRand or enemy.isTerminatorHunterKiller ) and not SqrDistLessThan( distToEnemySqr, 400 ) if not seeEnemy and self.lastInterceptPos and self:interceptIfWeCan( currentTask ) then - -- we intercepted + return true + elseif isBeingFooled then self:TaskComplete( currentTask ) self:StartTask( "movement_watch", nil, "ea, this is gonna fool em so hard" ) + return true + elseif not seeEnemy then self:TaskComplete( currentTask ) self:StartTask( "movement_approachlastseen", nil, "ea, where'd they go" ) + return true + elseif doBaitWatch then self:TaskComplete( currentTask ) self:StartTask( "movement_watch", nil, "ea, another hunter will sneak up on them!" ) self:Anger( 12 ) + return true + elseif doNormalWatch or beginFirstWatch or stillInFirstWatch then if beginFirstWatch then self.PreventShooting = true @@ -2432,45 +2441,74 @@ function ENT:EnemyAcquired( currentTask ) end self:TaskComplete( currentTask ) self:StartTask( "movement_watch", nil, "ea, watching something" ) + return true + elseif campDangerousEnemy then self:TaskComplete( currentTask ) self:StartTask( "movement_camp", nil, "ea, enemy is scary! camp them" ) + return true + elseif doCamp then self:TaskComplete( currentTask ) local tolerance = self:campingTolerance() if tolerance > math.random( 1000, 3000 ) then self:StartTask( "movement_camp", { maxNoSeeing = tolerance }, "ea, camp or perch, camped because we're already in a good spot" ) + return true + else - self:StartTask( "movement_perch", { requiredTarget = enemPos, earlyQuitIfSeen = true, perchRadius = math.sqrt( distToEnemySqr ), distanceWeight = 0.01 }, "ea, camp or perch, perch" ) + local perchData = { + requiredTarget = enemPos, + earlyQuitIfSeen = true, + perchRadius = math.sqrt( distToEnemySqr ), + distanceWeight = 0.01, + } + self:StartTask( "movement_perch", perchData, "ea, camp or perch, perch" ) + return true + end elseif canRushKiller then self:TaskComplete( currentTask ) + if self.term_DoesntFlank then self:StartTask( "movement_followenemy", nil, "ea, rush a killer" ) + else self:StartTask( "movement_flankenemy", nil, "ea, rush a killer" ) + end self.PreventShooting = nil + return true + elseif tooDangerousToApproach and distToEnemySqr < self:GetRealDuelEnemyDist( entMeta.GetTable( self ) ) * 0.5 then self:TaskComplete( currentTask ) self:StartTask( "movement_backthehellup", nil, "ea, we gotta back UP!" ) + return true + elseif doStalk then self:TaskComplete( currentTask ) self:StartTask( "movement_stalkenemy", nil, "ea, stalk them" ) + return true + elseif doFlank then self:TaskComplete( currentTask ) + if self.term_DoesntFlank then self:StartTask( "movement_followenemy", nil, "ea, flank them but i dont like flanking" ) + else self:StartTask( "movement_flankenemy", nil, "ea, flank them" ) + end self.PreventShooting = nil + return true + else self:TaskComplete( currentTask ) self:StartTask( "movement_followenemy", nil, "ea, im just gonna rush them, nothing fancy" ) self.PreventShooting = nil + return true + end - return true end function ENT:markAsWalked( area ) @@ -2499,12 +2537,12 @@ function ENT:WalkArea( myTbl ) if not myTbl.areaIsReachable( self, walkedArea ) and myTbl.nextUnreachableWipe < cur then -- we got somewhere unreachable, probably should reset this if myTbl.IsFodder then -- order the fodder enemies to rebuild the unreachable cache local ourClass = entMeta.GetClass( self ) - terminator_Extras.unreachableAreasForClasses[ ourClass ] = {} + terminator_Extras.unreachableAreasForClasses[ourClass] = {} for _, ent in ipairs( ents.FindByClass( ourClass ) ) do if ent == myTbl then continue end -- self gets a special case - ent.unreachableAreas = terminator_Extras.unreachableAreasForClasses[ ourClass ] + ent.unreachableAreas = terminator_Extras.unreachableAreasForClasses[ourClass] ent.nextUnreachableWipe = cur + 15 -- never ever ever spam this end @@ -2526,11 +2564,11 @@ function ENT:WalkArea( myTbl ) end myTbl.nextFloodMarkWalkable = cur + add - local scoreData = {} - scoreData.currentWalked = myTbl.walkedAreas - scoreData.InitialArea = walkedArea - scoreData.checkOrigin = myTbl.GetShootPos( self ) - scoreData.self = self + local scoreDataSetup = {} + scoreDataSetup.currentWalked = myTbl.walkedAreas + scoreDataSetup.InitialArea = walkedArea + scoreDataSetup.checkOrigin = myTbl.GetShootPos( self ) + scoreDataSetup.self = self local dist = 750 local scoreFunction = function( scoreData, _, area2 ) @@ -2557,7 +2595,7 @@ function ENT:WalkArea( myTbl ) end - local _ = myTbl.findValidNavResult( self, scoreData, self:GetPos(), dist, scoreFunction ) + local _ = myTbl.findValidNavResult( self, scoreDataSetup, self:GetPos(), dist, scoreFunction ) coroutine_yield() end @@ -2813,9 +2851,9 @@ hook.Add( "terminator_nextbot_noterms_exist", "terminator_clear_playerstatuses", end ) function ENT:SetupDefaultCapabilities() - BaseClass.SetupDefaultCapabilities(self) + BaseClass.SetupDefaultCapabilities( self ) - self:CapabilitiesAdd(CAP_MOVE_JUMP) + self:CapabilitiesAdd( CAP_MOVE_JUMP ) end -- all other relationships are created by MakeFeud in enemyoverrides when something damages us @@ -2937,7 +2975,7 @@ function ENT:Initialize() local models = myTbl.Models local model if models then - model = models[ math.random( #models ) ] + model = models[math.random( #models )] end if not model then @@ -3041,8 +3079,8 @@ function ENT:Initialize() if myTbl.IsFodder then local ourClass = self:GetClass() terminator_Extras.unreachableAreasForClasses = terminator_Extras.unreachableAreasForClasses or {} - terminator_Extras.unreachableAreasForClasses[ ourClass ] = terminator_Extras.unreachableAreasForClasses[ ourClass ] or {} - myTbl.unreachableAreas = terminator_Extras.unreachableAreasForClasses[ ourClass ] + terminator_Extras.unreachableAreasForClasses[ourClass] = terminator_Extras.unreachableAreasForClasses[ourClass] or {} + myTbl.unreachableAreas = terminator_Extras.unreachableAreasForClasses[ourClass] end end ) @@ -3226,7 +3264,7 @@ function ENT:DoDefaultTasks() elseif myTbl.forcedCheckPositions and table.Count( myTbl.forcedCheckPositions ) >= 1 then -- nuke these for positionKey, position in pairs( myTbl.forcedCheckPositions ) do if SqrDistLessThan( distToSqr( position, myPos ), 200 ) then - myTbl.forcedCheckPositions[ positionKey ] = nil + myTbl.forcedCheckPositions[positionKey] = nil break end @@ -3381,7 +3419,7 @@ function ENT:DoDefaultTasks() if data.HasEnemy then local memory, _ = myTbl.getMemoryOfObject( self, myTbl, prevEnemy ) - if prevEnemy:IsPlayer() --[[ leaving this index, its rare ]] or memory == MEMORY_WEAPONIZEDNPC then + if prevEnemy:IsPlayer() --[[leaving this index, its rare]] or memory == MEMORY_WEAPONIZEDNPC then -- reset searching progress! myTbl.SearchCheckedNavs = myTbl.SearchBadNavAreas myTbl.SearchCheckedNavsCount = 0 @@ -3631,223 +3669,222 @@ function ENT:DoDefaultTasks() coroutine_yield() local nextCache = data.nextCache or 0 - if nextCache < CurTime() then -- heavy staggered checks - data.nextCache = CurTime() + 1 + if nextCache > CurTime() then return end -- heavy staggered checks + data.nextCache = CurTime() + 1 - local myPos = self:GetPos() - local currentNav = navmesh.GetNearestNavArea( myPos, false, 50, false, false, -2 ) -- pretty tight criteria - local size = 80 + local myPos = self:GetPos() + local currentNav = navmesh.GetNearestNavArea( myPos, false, 50, false, false, -2 ) -- pretty tight criteria + local size = 80 - --debugoverlay.Cross( myPos, 10, 10, Color( 255,255,255 ), true ) + --debugoverlay.Cross( myPos, 10, 10, Color( 255,255,255 ), true ) - local noNav = myTbl.loco:IsOnGround() and ( not IsValid( currentNav ) or #currentNav:GetAdjacentAreas() <= 0 ) - local doAddCount = 1 + local noNav = myTbl.loco:IsOnGround() and ( not IsValid( currentNav ) or #currentNav:GetAdjacentAreas() <= 0 ) + local doAddCount = 1 - if noNav then -- more likely to be stuck! - size = size / 8 - doAddCount = doAddCount * 4 - if not terminator_Extras.IsLivePatching then - self:TryGeneratingAreas() + if noNav then -- more likely to be stuck! + size = size / 8 + doAddCount = doAddCount * 4 + if not terminator_Extras.IsLivePatching then + self:TryGeneratingAreas() - end end - if myTbl.isUnstucking then - doAddCount = doAddCount * 2 + end + if myTbl.isUnstucking then + doAddCount = doAddCount * 2 - end + end - if myPos then - for _ = 1, doAddCount do - table.insert( data.historicPositions, 1, myPos ) + if myPos then + for _ = 1, doAddCount do + table.insert( data.historicPositions, 1, myPos ) - end end - if currentNav then - for _ = 1, doAddCount do - table.insert( data.historicNavs, 1, currentNav ) + end + if currentNav then + for _ = 1, doAddCount do + table.insert( data.historicNavs, 1, currentNav ) - end end + end - coroutine_yield() + coroutine_yield() - local stuck = nil - local sortaStuck = nil - local overrideStuck = myTbl.overrideVeryStuck + local stuck = nil + local sortaStuck = nil + local overrideStuck = myTbl.overrideVeryStuck - local nextDisplacementCheck = data.nextUnderDisplacementCheck - if nextDisplacementCheck < CurTime() then - local checkInterval = fodder and 15 or 5 - data.nextUnderDisplacementCheck = CurTime() + checkInterval - local isUnderDisplacement, maybeUnderDisplacement = self:IsUnderDisplacement() + local nextDisplacementCheck = data.nextUnderDisplacementCheck + if nextDisplacementCheck < CurTime() then + local checkInterval = fodder and 15 or 5 + data.nextUnderDisplacementCheck = CurTime() + checkInterval + local isUnderDisplacement, maybeUnderDisplacement = self:IsUnderDisplacement() - if isUnderDisplacement then - data.maybeUnderCount = data.maybeUnderCount + 3 + if isUnderDisplacement then + data.maybeUnderCount = data.maybeUnderCount + 3 - elseif maybeUnderDisplacement then - data.maybeUnderCount = data.maybeUnderCount + 1 + elseif maybeUnderDisplacement then + data.maybeUnderCount = data.maybeUnderCount + 1 - else - data.maybeUnderCount = 0 + else + data.maybeUnderCount = 0 - end end + end - local underDisplacementThresh = fodder and 3 or 6 + local underDisplacementThresh = fodder and 3 or 6 - local underDisplacement = data.maybeUnderCount >= underDisplacementThresh + local underDisplacement = data.maybeUnderCount >= underDisplacementThresh - if #data.historicPositions > size then -- we built up a stack of historic positions, use them to determine if we're stuck! - coroutine_yield() + if #data.historicPositions > size then -- we built up a stack of historic positions, use them to determine if we're stuck! + coroutine_yield() - if data.historicPositions[size + 1] then - table.remove( data.historicPositions, size + 1 ) - table.remove( data.historicPositions, size + 1 ) + if data.historicPositions[size + 1] then + table.remove( data.historicPositions, size + 1 ) + table.remove( data.historicPositions, size + 1 ) - end - if data.historicNavs[size + 1] then - table.remove( data.historicNavs, size + 1 ) - table.remove( data.historicNavs, size + 1 ) + end + if data.historicNavs[size + 1] then + table.remove( data.historicNavs, size + 1 ) + table.remove( data.historicNavs, size + 1 ) - end + end - -- start with assuming its true - stuck = true - sortaStuck = true + -- start with assuming its true + stuck = true + sortaStuck = true - for _, historicPos in ipairs( data.historicPositions ) do - local distSqr = myPos:DistToSqr( historicPos ) - --debugoverlay.Line( myPos, historicPos, 1, color_white, true ) - if SqrDistGreaterThan( distSqr, 15 ) then - stuck = nil - break + for _, historicPos in ipairs( data.historicPositions ) do + local distSqr = myPos:DistToSqr( historicPos ) + --debugoverlay.Line( myPos, historicPos, 1, color_white, true ) + if SqrDistGreaterThan( distSqr, 15 ) then + stuck = nil + break - end end - -- false if we haven't been here for x long - for _, historicNav in ipairs( data.historicNavs ) do - if historicNav ~= currentNav then - sortaStuck = nil - break + end + -- false if we haven't been here for x long + for _, historicNav in ipairs( data.historicNavs ) do + if historicNav ~= currentNav then + sortaStuck = nil + break - end end - -- instant unstuck check if we're REALLY off the navmesh - if noNav and not self:PathIsValid() and not stuck and not navmesh.GetNearestNavArea( myPos, false, 200, false, false, -2 ) then - stuck = true + end + -- instant unstuck check if we're REALLY off the navmesh + if noNav and not self:PathIsValid() and not stuck and not navmesh.GetNearestNavArea( myPos, false, 200, false, false, -2 ) then + stuck = true - end end + end - coroutine_yield() + coroutine_yield() - if stuck or sortaStuck or underDisplacement or overrideStuck then -- i have been in the same EXACT spot for S I Z E seconds - self:ReallyAnger( 60 ) + if stuck or sortaStuck or underDisplacement or overrideStuck then -- i have been in the same EXACT spot for S I Z E seconds + self:ReallyAnger( 60 ) - myTbl.overrideVeryStuck = nil - local distToEnemy = 0 - local enemyPos = self:GetPos() - if IsValid( self:GetEnemy() ) then - distToEnemy = myTbl.DistToEnemy - enemyPos = myTbl.EnemyLastPos or self:GetEnemy():GetPos() + myTbl.overrideVeryStuck = nil + local distToEnemy = 0 + local enemyPos = self:GetPos() + if IsValid( self:GetEnemy() ) then + distToEnemy = myTbl.DistToEnemy + enemyPos = myTbl.EnemyLastPos or self:GetEnemy():GetPos() - end + end - local freedomPos + local freedomPos - local nearestNavArea = navmesh.GetNearestNavArea( self:GetPos(), false, 10000, false, true, 2 ) + local nearestNavArea = navmesh.GetNearestNavArea( self:GetPos(), false, 10000, false, true, 2 ) - local myShootPos = self:GetShootPos() - local maxs = Vector( 1000, 1000, 1000 ) - local bestDist = math.huge - for _, area in ipairs( navmesh.FindInBox( myPos + maxs, myPos + -maxs ) ) do -- try and find a good freedomPos - if area == nearestNavArea then continue end - coroutine_yield() - if not IsValid( area ) then continue end + local myShootPos = self:GetShootPos() + local maxs = Vector( 1000, 1000, 1000 ) + local bestDist = math.huge + for _, area in ipairs( navmesh.FindInBox( myPos + maxs, myPos + -maxs ) ) do -- try and find a good freedomPos + if area == nearestNavArea then continue end + coroutine_yield() + if not IsValid( area ) then continue end - local areasCenter = area:GetCenter() + local areasCenter = area:GetCenter() - if areasCenter:Distance( enemyPos ) < distToEnemy then continue end -- dont unstuck us closer to the enemy - local distToMe = areasCenter:Distance( myPos ) + if areasCenter:Distance( enemyPos ) < distToEnemy then continue end -- dont unstuck us closer to the enemy + local distToMe = areasCenter:Distance( myPos ) - if not freedomPos then -- always pick some area - freedomPos = areasCenter + if not freedomPos then -- always pick some area + freedomPos = areasCenter - elseif distToMe < bestDist and area:IsVisible( myShootPos ) then -- perfect candidate, pick this one! - bestDist = distToMe - freedomPos = areasCenter + elseif distToMe < bestDist and area:IsVisible( myShootPos ) then -- perfect candidate, pick this one! + bestDist = distToMe + freedomPos = areasCenter - end end + end - --debugoverlay.Cross( freedomPos, 100, 20, Color( 255, 0, 0 ), true ) - --print( self:GetCreationID(), "bigunstuck ", stuck, sortastuck, underDisplacement, overrideStuck, noNavAndNotStaring ) - - local extremeStuck = underDisplacement or not freedomPos - local canGotoEscape = data.nextUnstuckGotoEscape < CurTime() and data.extremeUnstucking < CurTime() + --debugoverlay.Cross( freedomPos, 100, 20, Color( 255, 0, 0 ), true ) + --print( self:GetCreationID(), "bigunstuck ", stuck, sortastuck, underDisplacement, overrideStuck, noNavAndNotStaring ) - -- teleports us to the unstuck position if we dont see an enemy and we've tried walking there - if not myTbl.IsSeeEnemy and extremeUnstucking:GetBool() and ( extremeStuck or not canGotoEscape ) then - data.extremeUnstucking = 0 - data.freedomGotoPosSimple = nil - data.oldNavArea = nil - if freedomPos then -- teleport us there - coroutine_yield() + local extremeStuck = underDisplacement or not freedomPos + local canGotoEscape = data.nextUnstuckGotoEscape < CurTime() and data.extremeUnstucking < CurTime() - self:SetPosNoTeleport( freedomPos ) - self:InvalidatePath( "i was hard unstucked! bailing path." ) - myTbl.loco:SetVelocity( vec_zero ) - myTbl.loco:ClearStuck() + -- teleports us to the unstuck position if we dont see an enemy and we've tried walking there + if not myTbl.IsSeeEnemy and extremeUnstucking:GetBool() and ( extremeStuck or not canGotoEscape ) then + data.extremeUnstucking = 0 + data.freedomGotoPosSimple = nil + data.oldNavArea = nil + if freedomPos then -- teleport us there + coroutine_yield() - elseif GAMEMODE.getValidHunterPos then -- GLEE specific fallback - freedomPos = GAMEMODE:getValidHunterPos() + self:SetPosNoTeleport( freedomPos ) + self:InvalidatePath( "i was hard unstucked! bailing path." ) + myTbl.loco:SetVelocity( vec_zero ) + myTbl.loco:ClearStuck() - if freedomPos then - coroutine_yield() + elseif GAMEMODE.getValidHunterPos then -- GLEE specific fallback + freedomPos = GAMEMODE:getValidHunterPos() - self:SetPosNoTeleport( freedomPos ) - self:InvalidatePath( "i was hard unstucked! bailing path. 2" ) + if freedomPos then + coroutine_yield() - myTbl.loco:SetVelocity( vec_zero ) - myTbl.loco:ClearStuck() + self:SetPosNoTeleport( freedomPos ) + self:InvalidatePath( "i was hard unstucked! bailing path. 2" ) - end - -- only remove if its not pathing! - -- set ReallyStuckNeverRemove in a bot if you never want it to be removed - elseif not self:PathIsValid() and not myTbl.ReallyStuckNeverRemove then - SafeRemoveEntity( self ) + myTbl.loco:SetVelocity( vec_zero ) + myTbl.loco:ClearStuck() end - -- walk to the freedomPos instead - elseif data.extremeUnstucking < CurTime() then - coroutine_yield() + -- only remove if its not pathing! + -- set ReallyStuckNeverRemove in a bot if you never want it to be removed + elseif not self:PathIsValid() and not myTbl.ReallyStuckNeverRemove then + SafeRemoveEntity( self ) - if not freedomPos then -- fallback - local offset = VectorRand() - offset = offset * Vector( 1, 1, 0.5 ) -- flatten the vec - offset:Normalize() - - freedomPos = self:GetPos() + offset * math.random( 300, 600 ) - - end + end + -- walk to the freedomPos instead + elseif data.extremeUnstucking < CurTime() then + coroutine_yield() - data.nextUnstuckGotoEscape = CurTime() + 80 -- if the walk to freedomPos finishes, but we're still stuck, do the teleporting - data.freedomGotoPosSimple = freedomPos - data.extremeUnstucking = CurTime() + 10 -- try and walk to the freedomPos for this long - data.oldNavArea = currentNav - data.nextCache = CurTime() + 5 + if not freedomPos then -- fallback + local offset = VectorRand() + offset = offset * Vector( 1, 1, 0.5 ) -- flatten the vec + offset:Normalize() - self:KillAllTasksWith( "movement" ) -- jump us out of any infinite loops - self:StartTask( "movement_wait", { time = 11 }, "gotta let the reallystuck handler do its thing" ) + freedomPos = self:GetPos() + offset * math.random( 300, 600 ) end - data.nextUnderDisplacementCheck = 0 -- CHECK NOW! + data.nextUnstuckGotoEscape = CurTime() + 80 -- if the walk to freedomPos finishes, but we're still stuck, do the teleporting + data.freedomGotoPosSimple = freedomPos + data.extremeUnstucking = CurTime() + 10 -- try and walk to the freedomPos for this long + data.oldNavArea = currentNav + data.nextCache = CurTime() + 5 - data.historicPositions = {} - data.historicNavs = {} + self:KillAllTasksWith( "movement" ) -- jump us out of any infinite loops + self:StartTask( "movement_wait", { time = 11 }, "gotta let the reallystuck handler do its thing" ) end + + data.nextUnderDisplacementCheck = 0 -- CHECK NOW! + + data.historicPositions = {} + data.historicNavs = {} + end end, }, @@ -4381,8 +4418,7 @@ function ENT:DoDefaultTasks() if IsValid( data.object ) then -- BEATUP - local old = SysTime() - local valid, attacked, nearAndCanHit, closeAndCanHit, isNear, isClose, visible = myTbl.beatUpEnt( self, myTbl, data.object ) + local valid, attacked, _nearAndCanHit, _closeAndCanHit, _isNear, isClose, visible = myTbl.beatUpEnt( self, myTbl, data.object ) data.gotAHitIn = data.gotAHitIn or attacked if data.insane and not isClose and visible and self:GetWeaponRange() > 500 then @@ -4534,7 +4570,7 @@ function ENT:DoDefaultTasks() self:StartTask( "movement_followsound", { Sound = self.lastHeardSoundHint }, "i heard something" ) return - elseif self.awarenessMemory[ self:getAwarenessKey( data.object ) ] ~= MEMORY_MEMORIZING then -- we memorized this already + elseif self.awarenessMemory[self:getAwarenessKey( data.object )] ~= MEMORY_MEMORIZING then -- we memorized this already data.fail = true elseif data.object:GetParent() == self then @@ -5102,7 +5138,7 @@ function ENT:DoDefaultTasks() local toPick = { data.searchCenter, self.EnemyLastPosOffsetted, self:GetPos() } local pickedSearchCenter for index = 1, table.Count( toPick ) do - local picked = toPick[ index ] + local picked = toPick[index] if isvector( picked ) then pickedSearchCenter = picked break @@ -5131,15 +5167,15 @@ function ENT:DoDefaultTasks() data.searchWant = data.searchWant + -1 data.time = CurTime() + data.time - local scoreData = {} - scoreData.blockRadiusEnd = true - scoreData.searchRadius = data.searchRadius - scoreData.searchCenter = pickedSearchCenter - scoreData.canDoUnderWater = self:isUnderWater() - scoreData.decreasingScores = {} - scoreData.self = self - scoreData.walkedAreas = self.walkedAreas - scoreData.walkedAreaTimes = self.walkedAreas + local scoreDataSetup = {} + scoreDataSetup.blockRadiusEnd = true + scoreDataSetup.searchRadius = data.searchRadius + scoreDataSetup.searchCenter = pickedSearchCenter + scoreDataSetup.canDoUnderWater = self:isUnderWater() + scoreDataSetup.decreasingScores = {} + scoreDataSetup.self = self + scoreDataSetup.walkedAreas = self.walkedAreas + scoreDataSetup.walkedAreaTimes = self.walkedAreas hidingToCheck = nil local checkCenter @@ -5223,7 +5259,7 @@ function ENT:DoDefaultTasks() coroutine_yield() - checkCenter, checkNav = self:findValidNavResult( scoreData, self:GetPos(), scoreData.searchRadius, scoreFunction ) + checkCenter, checkNav = self:findValidNavResult( scoreDataSetup, self:GetPos(), scoreDataSetup.searchRadius, scoreFunction ) hidingToCheck = hidingToCheck or checkCenter coroutine_yield() @@ -5518,15 +5554,15 @@ function ENT:DoDefaultTasks() BehaveUpdateMotion = function( self, data ) local searchPos = data.searchPos if not data.searchPos then - local scoreData = {} - scoreData.canDoUnderWater = self:isUnderWater() - scoreData.self = self + local scoreDataSetup = {} + scoreDataSetup.canDoUnderWater = self:isUnderWater() + scoreDataSetup.self = self local scoreFunction = function( scoreData, area1, area2 ) local dropToArea = area1:ComputeAdjacentConnectionHeightChange( area2 ) local score = math.Rand( 100, 150 ) if scoreData.self.walkedAreas[area2:GetID()] then - local time = scoreData.self.walkedAreaTimes[ area2:GetID() ] + local time = scoreData.self.walkedAreaTimes[area2:GetID()] local timeSince = math.abs( time - CurTime() ) score = score / timeSince @@ -5546,7 +5582,7 @@ function ENT:DoDefaultTasks() end - searchPos = self:findValidNavResult( scoreData, self:GetPos(), math.random( 2000, 3500 ), scoreFunction ) + searchPos = self:findValidNavResult( scoreDataSetup, self:GetPos(), math.random( 2000, 3500 ), scoreFunction ) --debugoverlay.Cross( searchPos, 150, 10, Color( 255,255,255 ), true ) if not searchPos then data.InvalidAfterwards = true @@ -6143,23 +6179,23 @@ function ENT:DoDefaultTasks() end - local scoreData = {} - scoreData.lethalCloseRange = tooDangerousToApproach - scoreData.hateVisible = hp < maxHp * 0.5 - scoreData.enemyArea = enemyArea - scoreData.enemyAreaCenter = enemyAreaCenter - scoreData.innerBoundary = innerBoundary - scoreData.outerBoundary = outerBoundary - scoreData.hardInnerBoundary = hardInnerBoundary - scoreData.hardOuterBoundary = hardOuterBoundary - scoreData.lastStalkFromPos = data.lastStalkFromPos or myPos - scoreData.stalkStartPos = myPos - scoreData.unreachableAreasCached = self.unreachableAreas + local scoreDataSetup = {} + scoreDataSetup.lethalCloseRange = tooDangerousToApproach + scoreDataSetup.hateVisible = hp < maxHp * 0.5 + scoreDataSetup.enemyArea = enemyArea + scoreDataSetup.enemyAreaCenter = enemyAreaCenter + scoreDataSetup.innerBoundary = innerBoundary + scoreDataSetup.outerBoundary = outerBoundary + scoreDataSetup.hardInnerBoundary = hardInnerBoundary + scoreDataSetup.hardOuterBoundary = hardOuterBoundary + scoreDataSetup.lastStalkFromPos = data.lastStalkFromPos or myPos + scoreDataSetup.stalkStartPos = myPos + scoreDataSetup.unreachableAreasCached = self.unreachableAreas - scoreData.lowestHeightAllowed = math.min( scoreData.enemyAreaCenter.z, myPos.z ) - --debugoverlay.Cross( scoreData.lastStalkFromPos, 10, 20, color_white, true ) + scoreDataSetup.lowestHeightAllowed = math.min( scoreDataSetup.enemyAreaCenter.z, myPos.z ) + --debugoverlay.Cross( scoreDataSetup.lastStalkFromPos, 10, 20, color_white, true ) - scoreData.canGoUnderwater = self:isUnderWater() + scoreDataSetup.canGoUnderwater = self:isUnderWater() -- find area to my left or right, relative to enemy, basically circle the enemy local scoreFunction = function( scoreData, area1, area2 ) @@ -6167,7 +6203,7 @@ function ENT:DoDefaultTasks() if not area2:IsConnected( area1 ) then return 0 end local area2sId = area2:GetID() - if scoreData.unreachableAreasCached[ area2sId ] then return 0 end + if scoreData.unreachableAreasCached[area2sId] then return 0 end local area2Center = area2:GetCenter() local distanceTravelled = DistToSqr2D( area2Center, scoreData.lastStalkFromPos ) @@ -6248,7 +6284,7 @@ function ENT:DoDefaultTasks() coroutine_yield() - data.stalkPos = self:findValidNavResult( scoreData, self:GetPos(), math.Clamp( self.DistToEnemy * 2, 1000, math.Rand( 5000, 7000 ) ), scoreFunction ) + data.stalkPos = self:findValidNavResult( scoreDataSetup, self:GetPos(), math.Clamp( self.DistToEnemy * 2, 1000, math.Rand( 5000, 7000 ) ), scoreFunction ) coroutine_yield() @@ -6878,7 +6914,7 @@ function ENT:DoDefaultTasks() self:StartTask( "movement_search", { searchWant = 5 }, "i never went anywhere in the first place" ) end - self.forcedCheckPositions[ data.forcedCheckKey ] = nil + self.forcedCheckPositions[data.forcedCheckKey] = nil -- i see you... elseif goodEnemy then self:EnemyAcquired( "movement_approachforcedcheckposition" ) @@ -6886,7 +6922,7 @@ function ENT:DoDefaultTasks() elseif result == true and givenItAChance then self:TaskComplete( "movement_approachforcedcheckposition" ) self:StartTask( "movement_search", { searchWant = 60 }, "i got there but nobody's here" ) - self.forcedCheckPositions[ data.forcedCheckKey ] = nil + self.forcedCheckPositions[data.forcedCheckKey] = nil self.PreventShooting = nil -- bad path elseif ( self:MyPathLength() < 50 and Distance2D( self:GetPos(), self:GetPath():GetEnd() ) < 300 ) and givenItAChance then @@ -7397,7 +7433,7 @@ function ENT:DoDefaultTasks() -- determine where player CAN go -- dont build path to somewhere behind walls - local mymins,mymaxs = self:GetCollisionBounds() + local mymins, mymaxs = self:GetCollisionBounds() mymins = Vector( mymins.x, mymins.y, mymins.z ) mymaxs = Vector( mymaxs.x, mymaxs.y, mymaxs.z ) mymins = mymins * 0.5 @@ -7575,12 +7611,12 @@ function ENT:DoDefaultTasks() --normal path local Dir = data.Dir or self:GetForward() - local scoreData = {} - scoreData.canDoUnderWater = canDoUnderWater - scoreData.self = self - scoreData.forward = Dir:Angle() - scoreData.startArea = myNavArea - scoreData.startPos = scoreData.startArea:GetCenter() + local scoreDataSetup = {} + scoreDataSetup.canDoUnderWater = canDoUnderWater + scoreDataSetup.self = self + scoreDataSetup.forward = Dir:Angle() + scoreDataSetup.startArea = myNavArea + scoreDataSetup.startPos = scoreDataSetup.startArea:GetCenter() local scoreFunction = function( scoreData, area1, area2 ) local dropToArea = area2:ComputeAdjacentConnectionHeightChange( area1 ) @@ -7604,7 +7640,7 @@ function ENT:DoDefaultTasks() end - wanderPos = self:findValidNavResult( scoreData, self:GetPos(), math.random( 3000, 4000 ), scoreFunction ) + wanderPos = self:findValidNavResult( scoreDataSetup, self:GetPos(), math.random( 3000, 4000 ), scoreFunction ) if wanderPos then self:SetupPathShell( wanderPos ) @@ -7715,20 +7751,20 @@ function ENT:DoDefaultTasks() --normal path local dir = data.dir or self:GetForward() dir = -dir - local scoreData = {} - scoreData.canDoUnderWater = canDoUnderWater - scoreData.self = self - scoreData.forward = dir:Angle() - scoreData.startArea = myNavArea - scoreData.startPos = scoreData.startArea:GetCenter() - scoreData.beenAreas = data.beenAreas + local scoreDataSetup = {} + scoreDataSetup.canDoUnderWater = canDoUnderWater + scoreDataSetup.self = self + scoreDataSetup.forward = dir:Angle() + scoreDataSetup.startArea = myNavArea + scoreDataSetup.startPos = scoreDataSetup.startArea:GetCenter() + scoreDataSetup.beenAreas = data.beenAreas if anotherHuntersPos then - scoreData.doSpreadOut = true - scoreData.spreadOutAvoidAreas = {} + scoreDataSetup.doSpreadOut = true + scoreDataSetup.spreadOutAvoidAreas = {} local areasFound = navmesh.Find( anotherHuntersPos, 1500, 100, 100 ) for _, currArea in ipairs( areasFound ) do - scoreData.spreadOutAvoidAreas[currArea:GetID()] = true + scoreDataSetup.spreadOutAvoidAreas[currArea:GetID()] = true end end @@ -7770,7 +7806,7 @@ function ENT:DoDefaultTasks() end - wanderPos = self:findValidNavResult( scoreData, self:GetPos(), math.random( 5000, 6000 ), scoreFunction ) + wanderPos = self:findValidNavResult( scoreDataSetup, self:GetPos(), math.random( 5000, 6000 ), scoreFunction ) if foundSomewhereNotBeen == nil then data.beenAreas = nil @@ -7865,8 +7901,8 @@ function ENT:DoDefaultTasks() for _, potentiallyBeenArea in ipairs( potentiallyBeenAreas ) do local areaId = potentiallyBeenArea:GetID() - if not data.beenAreas[ areaId ] then - data.beenAreas[ areaId ] = true + if not data.beenAreas[areaId] then + data.beenAreas[areaId] = true --debugoverlay.Text( potentiallyBeenArea:GetCenter(), "IBEEN", 20 ) end @@ -8305,8 +8341,8 @@ function ENT:DoDefaultTasks() -- ask wep for score local scoreOfPos = scoringFunc( wep, self, checkPos ) - data.scoredPlaceables[ scoreOfPos ] = checkPos - data.scoredAreas[ scoreOfPos ] = toPlaceArea + data.scoredPlaceables[scoreOfPos] = checkPos + data.scoredAreas[scoreOfPos] = toPlaceArea end end @@ -8317,8 +8353,8 @@ function ENT:DoDefaultTasks() else data.bestPosScore = table.maxn( data.scoredPlaceables ) - data.bestPos = data.scoredPlaceables[ data.bestPosScore ] - data.bestArea = data.scoredAreas[ data.bestPosScore ] + data.bestPos = data.scoredPlaceables[data.bestPosScore] + data.bestArea = data.scoredAreas[data.bestPosScore] data.scoredPlaceables = nil --debugoverlay.Text( data.bestPos, "a" .. data.bestPosScore, 10, false ) @@ -8364,7 +8400,7 @@ function ENT:DoDefaultTasks() elseif result == false then self:TaskFail( "movement_placeweapon" ) self:StartTask( "movement_placeweapon", nil, "i couldnt path to the placing spot, try again please!" ) - self.failedPlacingAreas[ data.bestArea:GetID() ] = true + self.failedPlacingAreas[data.bestArea:GetID()] = true else data.giveUpTime = CurTime() + 1 @@ -8511,7 +8547,7 @@ function ENT:DoDefaultTasks() local score = ( ( distance * 0.01 ) + zOffset * 4 ) / nookScore score = score * canSeeTargetMul - data.scoredPerchables[ score ] = checkPos + data.scoredPerchables[score] = checkPos end @@ -8522,7 +8558,7 @@ function ENT:DoDefaultTasks() local bestPosScore = table.maxn( data.scoredPerchables ) if not bestPosScore then continue end - local bestPos = data.scoredPerchables[ bestPosScore ] + local bestPos = data.scoredPerchables[bestPosScore] if not bestPos then continue end data.bestPosSoFar = bestPos @@ -8584,7 +8620,7 @@ function ENT:DoDefaultTasks() end elseif not data.bestPos then data.bestPosScore = table.maxn( data.scoredPerchables ) - data.bestPos = data.scoredPerchables[ data.bestPosScore ] + data.bestPos = data.scoredPerchables[data.bestPosScore] if data.requiredTarget and data.bestPos then local _, canSeeTr = terminator_Extras.PosCanSeeComplex( data.bestPos, data.requiredTarget, self ) diff --git a/lua/entities/terminator_nextbot/spokenlines.lua b/lua/entities/terminator_nextbot/spokenlines.lua index ec16848..ebecfbe 100644 --- a/lua/entities/terminator_nextbot/spokenlines.lua +++ b/lua/entities/terminator_nextbot/spokenlines.lua @@ -72,7 +72,7 @@ function ENT:SpokenLinesThink( myTbl ) myTbl.term_IdleLoopingSound = nil end - local pickedSound = loopingSounds[ math.random( 1, #loopingSounds ) ] + local pickedSound = loopingSounds[math.random( 1, #loopingSounds )] local pitShift = asNumber( self, myTbl, myTbl.term_SoundPitchShift ) local lvlShift = asNumber( self, myTbl, myTbl.term_SoundLevelShift ) @@ -116,7 +116,7 @@ function ENT:SpokenLinesThink( myTbl ) local sentence if istable( sentenceIn ) then - sentence = sentenceIn[ math.random( 1, #sentenceIn ) ] + sentence = sentenceIn[math.random( 1, #sentenceIn )] elseif isstring( sentenceIn ) then sentence = sentenceIn @@ -155,7 +155,7 @@ function ENT:SpokenLinesThink( myTbl ) local path if istable( pathIn ) then - path = pathIn[ math.random( 1, #pathIn ) ] + path = pathIn[math.random( 1, #pathIn )] elseif isstring( pathIn ) then path = pathIn @@ -196,7 +196,7 @@ function ENT:Term_SpeakSoundNow( pathIn, specificPitchShift ) local dsp = asNumber( self, myTbl, myTbl.term_SoundDSP ) if istable( pathIn ) then - pathIn = pathIn[ math.random( 1, #pathIn ) ] + pathIn = pathIn[math.random( 1, #pathIn )] end @@ -229,7 +229,7 @@ function ENT:Term_SpeakSentenceNow( sentenceIn, specificPitchShift ) local lvlShift = asNumber( self, myTbl, myTbl.term_SoundLevelShift ) if istable( sentenceIn ) then - sentenceIn = sentenceIn[ math.random( 1, #sentenceIn ) ] + sentenceIn = sentenceIn[math.random( 1, #sentenceIn )] end local pitch = 100 + pitShift + specificPitchShift diff --git a/lua/entities/terminator_nextbot/taskoverride.lua b/lua/entities/terminator_nextbot/taskoverride.lua index 5221fd8..8620d90 100644 --- a/lua/entities/terminator_nextbot/taskoverride.lua +++ b/lua/entities/terminator_nextbot/taskoverride.lua @@ -183,7 +183,7 @@ function ENT:StartTask( task, data, reason ) m_ActiveTasksNum = {} myTbl.m_ActiveTasksNum = m_ActiveTasksNum end - m_ActiveTasksNum[ #m_ActiveTasksNum + 1 ] = { task, data } + m_ActiveTasksNum[#m_ActiveTasksNum + 1] = { task, data } myTbl.m_HollowEventCache = nil -- reset hollow event cache @@ -342,8 +342,8 @@ function ENT:SetupTasks( myTbl ) myTbl.DoClassTasks( self, myTbl ) -- adds class-specific behaviour for every class in the baseclass tree local taskListStatic = myTbl.m_TaskList - for k,v in pairs( myTbl.TaskList ) do - taskListStatic[k] = v + for taskName, task in pairs( myTbl.TaskList ) do + taskListStatic[taskName] = task end diff --git a/lua/entities/terminator_nextbot/weapholstering.lua b/lua/entities/terminator_nextbot/weapholstering.lua index 9e9b8dc..58b12c9 100644 --- a/lua/entities/terminator_nextbot/weapholstering.lua +++ b/lua/entities/terminator_nextbot/weapholstering.lua @@ -10,7 +10,7 @@ end function ENT:IsHolsteredWeap( wep ) if not IsValid( wep ) then return end local holsteredWeaps = self.m_HolsteredWeapons or {} - if holsteredWeaps[ wep ] then return true end + if holsteredWeaps[wep] then return true end end @@ -24,13 +24,13 @@ function ENT:CanHolsterWeap( wep ) -- validate slots for check, _ in pairs( holsteredWeps ) do if not IsValid( check ) then - holsteredWeps[ check ] = nil + holsteredWeps[check] = nil end end for slot, check in pairs( holsteredSlots ) do if not IsValid( check ) then - holsteredSlots[ slot ] = nil + holsteredSlots[slot] = nil end end @@ -43,7 +43,7 @@ function ENT:CanHolsterWeap( wep ) if not hasBoneForSlot then return false end -- already something there! - local alreadyThereWeap = holsteredSlots[ slot ] + local alreadyThereWeap = holsteredSlots[slot] local toEvict if alreadyThereWeap then local alreadyThereWeight = self:GetWeightOfWeapon( alreadyThereWeap ) @@ -64,13 +64,13 @@ local HOLSTER_BACK = 1 local HOLSTER_SIDEARM = 2 local bonesForSlots = { - [ HOLSTER_BACK ] = "ValveBiped.Bip01_Spine1", - [ HOLSTER_SIDEARM ] = "ValveBiped.Bip01_Spine", + [HOLSTER_BACK] = "ValveBiped.Bip01_Spine1", + [HOLSTER_SIDEARM] = "ValveBiped.Bip01_Spine", } function ENT:HasHolsterBone( slot ) - local boneId = self:LookupBone( bonesForSlots[ slot ] ) + local boneId = self:LookupBone( bonesForSlots[slot] ) if not boneId then return end return true, boneId @@ -85,16 +85,16 @@ sidearmOffset = sidearmOffset + -vector_back * 2.5 -- forwards a bit sidearmOffset = sidearmOffset + -vector_localUp * 5 -- down local offsetsForSlots = { - [ HOLSTER_BACK ] = { pos = ( vector_back * 3.5 ) + ( vector_localUp * 8 ), angOffset = Angle( 0, 7, 0 ) }, - [ HOLSTER_SIDEARM ] = { pos = sidearmOffset, angOffset = Angle( 0, 180, 90 ) }, + [HOLSTER_BACK] = { pos = ( vector_back * 3.5 ) + ( vector_localUp * 8 ), angOffset = Angle( 0, 7, 0 ) }, + [HOLSTER_SIDEARM] = { pos = sidearmOffset, angOffset = Angle( 0, 180, 90 ) }, } local rotationsForSizes = { - xy = { [ HOLSTER_BACK ] = Angle( 0, 90, 0 ), [ HOLSTER_SIDEARM ] = Angle( 90, 90, 90 ) }, -- done + xy = { [HOLSTER_BACK] = Angle( 0, 90, 0 ), [HOLSTER_SIDEARM] = Angle( 90, 90, 90 ) }, -- done xz = Angle( 0, 90, 0 ), yx = Angle( 0, 0, 0 ), -- done - yz = { [ HOLSTER_BACK ] = Angle( 90, 90, 90 ), [ HOLSTER_SIDEARM ] = Angle( 0, -80, 0 ) }, -- done, troublemaker though! + yz = { [HOLSTER_BACK] = Angle( 90, 90, 90 ), [HOLSTER_SIDEARM] = Angle( 0, -80, 0 ) }, -- done, troublemaker though! zx = Angle( 0, 0, 90 ), -- done zy = Angle( 0, 90, -90 ), -- done @@ -157,9 +157,9 @@ function ENT:GetHolsterData( wep ) -- no override? do the default checks if not rotation then local rotInd = thinnestIndex .. biggestIndex - rotation = rotationsForSizes[ rotInd ] + rotation = rotationsForSizes[rotInd] if istable( rotation ) then - rotation = rotation[ holsterDat.slot ] + rotation = rotation[holsterDat.slot] end --print( rotInd, thinnestIndex .. thinnestSize, biggestIndex .. biggestSize, wep:GetClass() ) @@ -168,7 +168,7 @@ function ENT:GetHolsterData( wep ) if not rotation then return end -- SLOT offsets - local offsets = offsetsForSlots[ holsterDat.slot ] + local offsets = offsetsForSlots[holsterDat.slot] if offsets.angOffset then rotation = rotation + offsets.angOffset @@ -224,13 +224,13 @@ function ENT:HolsterWeap( wep ) self.m_HolsteredWeapons = {} end - self.m_HolsteredWeapons[ wep ] = true + self.m_HolsteredWeapons[wep] = true if not self.m_HolsteringSlots then self.m_HolsteringSlots = {} end - self.m_HolsteringSlots[ holsterDat.slot ] = wep + self.m_HolsteringSlots[holsterDat.slot] = wep wep:CallOnRemove( "terminator_unholsteronremove", function( removedWep, myOwner ) if not IsValid( myOwner ) then return end @@ -245,8 +245,8 @@ end function ENT:UnHolsterWeap( wep ) local holsterDat = self:GetHolsterData( wep ) - self.m_HolsteredWeapons[ wep ] = nil - self.m_HolsteringSlots[ holsterDat.slot ] = nil + self.m_HolsteredWeapons[wep] = nil + self.m_HolsteringSlots[holsterDat.slot] = nil wep:RemoveCallOnRemove( "terminator_unholsteronremove" ) diff --git a/lua/entities/terminator_nextbot/weaponhacks.lua b/lua/entities/terminator_nextbot/weaponhacks.lua index d131ed1..9aaab9f 100644 --- a/lua/entities/terminator_nextbot/weaponhacks.lua +++ b/lua/entities/terminator_nextbot/weaponhacks.lua @@ -253,7 +253,7 @@ function ENT:DoWeaponHacks( wep ) while wepsTable and doneCount < max do doneCount = doneCount + 1 - local hack = terminator_WeaponHacks[ wepsTable.ClassName ] + local hack = terminator_WeaponHacks[wepsTable.ClassName] if hack then hack( wep ) diff --git a/lua/entities/terminator_nextbot/weapons.lua b/lua/entities/terminator_nextbot/weapons.lua index acc44d0..499dd05 100644 --- a/lua/entities/terminator_nextbot/weapons.lua +++ b/lua/entities/terminator_nextbot/weapons.lua @@ -117,7 +117,7 @@ function ENT:Give( wepname ) if not IsValid( wep ) then return end - isEngineAnalog = EngineAnalogs[ wepname ] + isEngineAnalog = EngineAnalogs[wepname] if not wep:IsScripted() and not isEngineAnalog then SafeRemoveEntity( wep ) @@ -240,7 +240,7 @@ function ENT:SetupWeapon( wep ) local wepsClass = entMeta.GetClass( wep ) -- Cannot hold engine weapons - if not wep:IsScripted() and not EngineAnalogs[ wepsClass ] then return end + if not wep:IsScripted() and not EngineAnalogs[wepsClass] then return end local myTbl = entMeta.GetTable( self ) @@ -263,8 +263,8 @@ function ENT:SetupWeapon( wep ) self:SetActiveWeapon( wep ) -- Custom lua weapon analog for engine weapon, this need to have WEAPON metatable - if EngineAnalogs[ wepsClass ] then - local actwep = ents.Create( EngineAnalogs[ wepsClass ] ) + if EngineAnalogs[wepsClass] then + local actwep = ents.Create( EngineAnalogs[wepsClass] ) actwep:SetOwner( self ) actwep:SetParent( wep ) actwep:Spawn() @@ -894,7 +894,7 @@ function ENT:CanPickupWeapon( wep, doingHolstered, myTbl, wepsTbl ) if not isCrate then if not wep:IsWeapon() then boring( wep ) return false end - if ( not wep:IsScripted() and not EngineAnalogs[ class ] ) then boring( wep ) return false end + if ( not wep:IsScripted() and not EngineAnalogs[class] ) then boring( wep ) return false end end @@ -1266,10 +1266,10 @@ function ENT:canGetWeapon() if not IsValid( currWeap ) then return true, newWeap end local analogClass = wepDat.engineAnalog - local analogsData = analogTblsCache[ analogClass ] + local analogsData = analogTblsCache[analogClass] if analogClass and not analogsData then analogsData = weapons.Get( analogClass ) - analogTblsCache[ analogClass ] = analogsData + analogTblsCache[analogClass] = analogsData end @@ -1422,7 +1422,7 @@ function ENT:FindWeapon( myTbl ) for _, potentialWeap in ipairs( found ) do - if notAWeaponCache[ potentialWeap ] then continue end + if notAWeaponCache[potentialWeap] then continue end local wepsTbl = entMeta.GetTable( potentialWeap ) if not CanPickupWeapon( self, potentialWeap, false, myTbl, wepsTbl ) then continue end @@ -1504,7 +1504,7 @@ local function getDamageTrackerOf( me, myTbl, wepOrClass ) if not isstring( class ) then return end - local tracker = trackers[ class ] + local tracker = trackers[class] if not tracker then -- tolerance for weapons local bonusAttackAttempts = 30 @@ -1546,7 +1546,7 @@ local function getDamageTrackerOf( me, myTbl, wepOrClass ) maxDistEverDamagedWith = 0, } - trackers[ class ] = tracker + trackers[class] = tracker end return tracker diff --git a/lua/entities/terminator_nextbot/wraithcloaking.lua b/lua/entities/terminator_nextbot/wraithcloaking.lua index 6340752..4a68455 100644 --- a/lua/entities/terminator_nextbot/wraithcloaking.lua +++ b/lua/entities/terminator_nextbot/wraithcloaking.lua @@ -1,4 +1,4 @@ ---[[ +--[[ ENT.IsWraith = true -- set to true on any bot to enable this file's logic ENT.NotSolidWhenCloaked = true -- if we're a wraith, we become non-solid when cloaked diff --git a/lua/entities/terminator_nextbot_base/drive.lua b/lua/entities/terminator_nextbot_base/drive.lua index 1e4c531..a610b87 100644 --- a/lua/entities/terminator_nextbot_base/drive.lua +++ b/lua/entities/terminator_nextbot_base/drive.lua @@ -36,7 +36,7 @@ local requiredActionKeys = { local function validateActions( sentTbl, mySpecialActions ) for actionName, actionDat in pairs( mySpecialActions ) do for _, reqKey in ipairs( requiredActionKeys ) do - if actionDat[ reqKey ] == nil then + if actionDat[reqKey] == nil then local str = { "Missing required key '", reqKey, "' for special action '", actionName, @@ -49,7 +49,7 @@ local function validateActions( sentTbl, mySpecialActions ) end end for key, val in pairs( actionDat ) do - local needed = specialActionsRequiredTypes[ key ] + local needed = specialActionsRequiredTypes[key] if not needed then continue end local valType = TypeID( val ) diff --git a/lua/entities/terminator_nextbot_csoldier.lua b/lua/entities/terminator_nextbot_csoldier.lua index 2344a83..ae8b81a 100644 --- a/lua/entities/terminator_nextbot_csoldier.lua +++ b/lua/entities/terminator_nextbot_csoldier.lua @@ -236,7 +236,7 @@ function ENT:GetCoverStatusOfPos( myTbl, coverPos, enemy, enemysShoot ) end ) end - local cached = coverposCache[ cacheStr ] + local cached = coverposCache[cacheStr] if cached then return cached @@ -271,12 +271,12 @@ function ENT:GetCoverStatusOfPos( myTbl, coverPos, enemy, enemysShoot ) end if willSeeEnemy and not willSeeEnemyCrouching and coverPos.z >= ( entMeta.GetPos( self ).z + -40 ) then - coverposCache[ cacheStr ] = COVER_SOFTCOVER + coverposCache[cacheStr] = COVER_SOFTCOVER return COVER_SOFTCOVER elseif not willSeeEnemy and not willSeeEnemyCrouching then --debugoverlay.Line( posIfStanding, enemysShoot, 1, Color( 255, 0, 0 ), true ) - coverposCache[ cacheStr ] = COVER_HARDCOVER + coverposCache[cacheStr] = COVER_HARDCOVER return COVER_HARDCOVER elseif hasBrains then -- brainy enemies can notice "crap" cover, otherwise treat all as no cover @@ -289,16 +289,16 @@ function ENT:GetCoverStatusOfPos( myTbl, coverPos, enemy, enemysShoot ) local notEvenCrapCover = not crapCoverTr.Hit or crapCoverTr.Entity == enemy notEvenCrapCover = notEvenCrapCover and not crapCoverTr.StartSolid if notEvenCrapCover then - coverposCache[ cacheStr ] = COVER_NONE + coverposCache[cacheStr] = COVER_NONE return COVER_NONE else - coverposCache[ cacheStr ] = COVER_CRAPCOVER + coverposCache[cacheStr] = COVER_CRAPCOVER return COVER_CRAPCOVER end else - coverposCache[ cacheStr ] = COVER_NONE + coverposCache[cacheStr] = COVER_NONE return COVER_NONE end @@ -346,14 +346,14 @@ function ENT:PopulateWithAreaOccupiedCounts( myTbl, toPopulate ) if IsValid( allyPathEndArea ) then local areaId = allyPathEndArea:GetID() - local oldCount = toPopulate[ areaId ] or 1 - toPopulate[ areaId ] = oldCount + spreadOutWeight / 2 + local oldCount = toPopulate[areaId] or 1 + toPopulate[areaId] = oldCount + spreadOutWeight / 2 end if IsValid( allyArea ) then local areaId = allyArea:GetID() - local oldCount = toPopulate[ areaId ] or 1 - toPopulate[ areaId ] = oldCount + spreadOutWeight + local oldCount = toPopulate[areaId] or 1 + toPopulate[areaId] = oldCount + spreadOutWeight end end @@ -394,7 +394,7 @@ function ENT:FindANearbyRecruitingLeader( myTbl ) local leaderPositions = {} for _, leader in ipairs( potentialLeaders ) do if not IsValid( leader ) then invalidLeaders[leader] = true continue end - leaderPositions[ leader ] = entMeta.GetPos( leader ) + leaderPositions[leader] = entMeta.GetPos( leader ) end local Distance = vecMeta.Distance @@ -973,7 +973,7 @@ function ENT:DoCustomTasks( defaultTasks ) scoreData.minCoverposZ = math.max( myPos.z + -50, enemysShoot.z + -100 ) -- dont go below the enemy's shoot pos if hasBrains and IsValid( enemysNav ) then - scoreData.occupiedAreaCosts[ enemysNav:GetID() ] = 100 + scoreData.occupiedAreaCosts[enemysNav:GetID()] = 100 end @@ -1037,8 +1037,8 @@ function ENT:DoCustomTasks( defaultTasks ) end end - if scoreData.occupiedAreaCosts[ area2sId ] then - local occupiedCount = scoreData.occupiedAreaCosts[ area2sId ] + if scoreData.occupiedAreaCosts[area2sId] then + local occupiedCount = scoreData.occupiedAreaCosts[area2sId] if occupiedCount and occupiedCount > 1 then score = score / occupiedCount @@ -2112,7 +2112,7 @@ function ENT:DoCustomTasks( defaultTasks ) end - if scoreData.AlreadyPatrolledAreas[ area2sId ] then + if scoreData.AlreadyPatrolledAreas[area2sId] then score = score / math.random( 2, 3 ) -- avoid already patrolled areas end @@ -2142,7 +2142,7 @@ function ENT:DoCustomTasks( defaultTasks ) end local finalPatrolCenter, finalPatrolArea = myTbl.findValidNavResult( self, scoreData, myPos, scoreData.searchRadius, scoreFunction ) if IsValid( finalPatrolArea ) then - data.AlreadyPatrolledAreas[ finalPatrolArea:GetID() ] = true + data.AlreadyPatrolledAreas[finalPatrolArea:GetID()] = true data.CurrentTaskGoalPos = finalPatrolCenter if followerCount <= 0 then data.WatchFromArea = finalPatrolArea -- im not a leader, so i'll watch from this area diff --git a/lua/weapons/weapon_frag_term.lua b/lua/weapons/weapon_frag_term.lua index fe8d739..f5a5c24 100644 --- a/lua/weapons/weapon_frag_term.lua +++ b/lua/weapons/weapon_frag_term.lua @@ -124,7 +124,7 @@ function SWEP:terminatorAimingFunc() end - local result = results[ smallestKey ] + local result = results[smallestKey] self.cachedTerminatorAimingFuncResult = result return result diff --git a/lua/weapons/weapon_terminatorfists_term.lua b/lua/weapons/weapon_terminatorfists_term.lua index 25c16d9..e619fea 100644 --- a/lua/weapons/weapon_terminatorfists_term.lua +++ b/lua/weapons/weapon_terminatorfists_term.lua @@ -546,7 +546,7 @@ end function SWEP:GetNPCBulletSpread( prof ) local spread = { 0,0,0,0,0 } - return spread[ prof + 1 ] + return spread[prof + 1] end function SWEP:ShouldWeaponAttackUseBurst() diff --git a/materials/models/terminator/arnold/arnold_glasses.vmt b/materials/models/terminator/arnold/arnold_glasses.vmt index 2569bd1..6913fe7 100644 --- a/materials/models/terminator/arnold/arnold_glasses.vmt +++ b/materials/models/terminator/arnold/arnold_glasses.vmt @@ -6,6 +6,6 @@ "$translucent" 1 "$nocull" 1 "$envmap" "env_cubemap" - "$envmaptint" "[ .3 .3 .3 ]" + "$envmaptint" "[.3 .3 .3]" "$normalmapalphaenvmapmask" "1" } diff --git a/materials/models/terminator/arnold/arnold_metal_1.vmt b/materials/models/terminator/arnold/arnold_metal_1.vmt index 9a76cc7..af46b92 100644 --- a/materials/models/terminator/arnold/arnold_metal_1.vmt +++ b/materials/models/terminator/arnold/arnold_metal_1.vmt @@ -4,6 +4,6 @@ "$bumpmap" "models/terminator/arnold/Arnold_metal_1_BM" "$normalmap" "models/terminator/arnold/Arnold_metal_1_BM" "$envmap" "env_cubemap" - "$envmaptint" "[ .45 .45 .45 ]" + "$envmaptint" "[.45 .45 .45]" "$normalmapalphaenvmapmask" "1" } From ab44a6813ec1bfbfc1957b2444bafafd91f90bb4 Mon Sep 17 00:00:00 2001 From: StrawWagen <64710817+StrawWagen@users.noreply.github.com> Date: Wed, 11 Feb 2026 15:56:46 -0700 Subject: [PATCH 4/6] Use our lint config --- .github/workflows/glualint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/glualint.yml b/.github/workflows/glualint.yml index 4d45617..015cb04 100644 --- a/.github/workflows/glualint.yml +++ b/.github/workflows/glualint.yml @@ -9,4 +9,4 @@ jobs: Lint: uses: FPtje/GLuaFixer/.github/workflows/glualint.yml@master with: - config: "https://cfc.gg/configs/gluafixer/glualint.json" + config: "https://raw.githubusercontent.com/StrawWagen/termhunter/refs/heads/main/.glualint.json" From 5deee9abeec0b0b22c11fe3b733e8ad868fd8077 Mon Sep 17 00:00:00 2001 From: StrawWagen <64710817+StrawWagen@users.noreply.github.com> Date: Wed, 11 Feb 2026 18:13:03 -0700 Subject: [PATCH 5/6] More lint + improve comments --- .../terminator_nextbot/compatibilityhacks.lua | 15 + lua/entities/terminator_nextbot/weapons.lua | 256 +++++++++++++----- 2 files changed, 199 insertions(+), 72 deletions(-) diff --git a/lua/entities/terminator_nextbot/compatibilityhacks.lua b/lua/entities/terminator_nextbot/compatibilityhacks.lua index eb9efa1..0209716 100644 --- a/lua/entities/terminator_nextbot/compatibilityhacks.lua +++ b/lua/entities/terminator_nextbot/compatibilityhacks.lua @@ -301,6 +301,21 @@ if SERVER then end + function ENT:GiveAmmo() + return nil + + end + + function ENT:GetAmmoCount() + return self:GetActiveWeapon():Clip1() or 0 + + end + + function ENT:PickupObject() + return + + end + function ENT:SetArmor() end diff --git a/lua/entities/terminator_nextbot/weapons.lua b/lua/entities/terminator_nextbot/weapons.lua index 499dd05..587a594 100644 --- a/lua/entities/terminator_nextbot/weapons.lua +++ b/lua/entities/terminator_nextbot/weapons.lua @@ -20,7 +20,7 @@ local EngineAnalogs = { local crateClass = "item_item_crate" local EngineAnalogsReverse = {} -for k,v in pairs( EngineAnalogs ) do EngineAnalogsReverse[v] = k end +for k, v in pairs( EngineAnalogs ) do EngineAnalogsReverse[v] = k end termHunter_WeaponAnalogs = EngineAnalogs @@ -121,11 +121,9 @@ function ENT:Give( wepname ) if not wep:IsScripted() and not isEngineAnalog then SafeRemoveEntity( wep ) - return NULL end - wep:Spawn() wep:Activate() wep:SetPos( self:GetPos() ) @@ -742,6 +740,7 @@ function ENT:CanWeaponSecondaryAttack() if CurTime() < wep:GetNextSecondaryFire() then return false end return true + end --[[------------------------------------ @@ -755,9 +754,13 @@ function ENT:WeaponSecondaryAttack() local wep = self:GetActiveLuaWeapon() - local successful = ProtectedCall(function() wep:NPCShoot_Secondary(self:GetShootPos(),self:GetAimVector()) end) + local successful = ProtectedCall( function() + wep:NPCShoot_Secondary( self:GetShootPos(), self:GetAimVector() ) + + end ) self:HateBuggyWeapon( wep, successful ) self:DoRangeGesture() + end --[[------------------------------------ @@ -769,6 +772,7 @@ end function ENT:DoRangeGesture() local act = self:TranslateActivity( ACT_MP_ATTACK_STAND_PRIMARYFIRE ) if not act or ( isnumber( act ) and act <= 0 ) then return end + local seq if isstring( act ) then seq = self:LookupSequence( act ) @@ -784,6 +788,7 @@ function ENT:DoRangeGesture() self:DoGesture( act ) return self:SequenceDuration( seq ) + end --[[------------------------------------ @@ -795,11 +800,13 @@ end function ENT:DoReloadGesture() local act = self:TranslateActivity( ACT_MP_RELOAD_STAND ) if not act or act <= 0 then return end + local seq = self:SelectWeightedSequence( act ) self:DoGesture( act ) return self:SequenceDuration( seq ) + end --[[------------------------------------ @@ -841,8 +848,9 @@ end Arg1: number | prof | Weapon proficiency Ret1: --]]------------------------------------ -function ENT:SetCurrentWeaponProficiency(prof) +function ENT:SetCurrentWeaponProficiency( prof ) self.m_WeaponProficiency = prof + end --[[------------------------------------ @@ -853,6 +861,7 @@ end --]]------------------------------------ function ENT:GetCurrentWeaponProficiency() return self.m_WeaponProficiency or WEAPON_PROFICIENCY_GOOD + end --[[------------------------------------ @@ -861,8 +870,9 @@ end Arg1: Entity | wep | Equiped weapon. It will be not lua analog. Ret1: --]]------------------------------------ -function ENT:OnWeaponEquip(wep) - self:RunTask("OnWeaponEquip",wep) +function ENT:OnWeaponEquip( wep ) + self:RunTask( "OnWeaponEquip", wep ) + end --[[------------------------------------ @@ -873,6 +883,7 @@ end --]]------------------------------------ function ENT:OnWeaponDrop( wep ) self:RunTask( "OnWeaponDrop", wep ) + end --[[------------------------------------ @@ -957,8 +968,9 @@ end Arg1: Weapon | wep | Current active weapon (this will be lua analog for engine weapon). Ret1: bool | Can drop. --]]------------------------------------ -function ENT:CanDropWeaponOnDie(wep) - return not self:HasSpawnFlags(SF_NPC_NO_WEAPON_DROP) +function ENT:CanDropWeaponOnDie( _wep ) + return not self:HasSpawnFlags( SF_NPC_NO_WEAPON_DROP ) + end --[[------------------------------------ @@ -967,8 +979,9 @@ end Arg1: Weapon | wep | Current active weapon (this will be lua analog for engine weapon). Ret1: bool | Should use bursts. --]]------------------------------------ -function ENT:ShouldWeaponAttackUseBurst(wep) +function ENT:ShouldWeaponAttackUseBurst( _wep ) return not self:IsControlledByPlayer() + end --[[------------------------------------ @@ -1001,11 +1014,25 @@ function ENT:IsMeleeWeapon( wep ) end +--[[------------------------------------ + Name: NEXTBOT:IsRangedWeapon + Desc: Opposite of above. + Arg1: (optional) Weapon | wep | + Weapon to check ( this should be lua analog for engine weapon ). + Defaults to active weapon. + Ret1: bool | Weapon is ranged weapon. +--]]------------------------------------ function ENT:IsRangedWeapon( wep ) return not self:IsMeleeWeapon( wep ) end +--[[------------------------------------ + Name: NEXTBOT:IsFists + Desc: Returns true if bot is using TERM_FISTS ( special weapon bot will use to crack open obstacles ). + Arg1: (optional) Table | myTbl | _index Optimisation table. + Ret1: bool | is using TERM_FISTS. +--]]------------------------------------ function ENT:IsFists( myTbl ) myTbl = myTbl or entMeta.GetTable( self ) @@ -1013,6 +1040,7 @@ function ENT:IsFists( myTbl ) end +-- INTERNAL function ENT:UpdateIsFists( myTbl ) myTbl = myTbl or entMeta.GetTable( self ) myTbl.term_IsFists = nil @@ -1027,6 +1055,12 @@ function ENT:UpdateIsFists( myTbl ) end +--[[------------------------------------ + Name: NEXTBOT:DoFists + Desc: Force bot to use ENT.TERM_FISTS. + Bot will drop current weapon if it has one, but only if DontDropPrimary is not set to true. + TERM_FISTS is expected to be a special, innate melee weapon that bot can use to conquer obstacles that are otherwise unbreakable/ +--]]------------------------------------ function ENT:DoFists() if self:IsFists() then return end if not self.DontDropPrimary then @@ -1038,8 +1072,10 @@ function ENT:DoFists() local fists = self:Give( self.TERM_FISTS ) fists.terminator_IgnoreWeaponUtility = true -- i was having issues with IsFists and engine overridden weps self:SetupFists( fists ) + end +-- INTERNAL, makes TERM_FISTS weapon respect self.FistRangeMul function ENT:SetupFists( fists ) if self.FistRangeMul then fists.Range = fists.Range * self.FistRangeMul @@ -1047,21 +1083,7 @@ function ENT:SetupFists( fists ) end end -function ENT:GiveAmmo() - return nil - -end - -function ENT:GetAmmoCount() - return self:GetActiveWeapon():Clip1() or 0 - -end - -function ENT:PickupObject() - return - -end - +-- find weapon's spread local function weapSpread( wep ) local spread = 0 @@ -1083,6 +1105,7 @@ local function weapSpread( wep ) end +-- find bullet count ( shotguns? ) local function weapBulletCount( wep ) local count = 1 local primary = wep.Primary @@ -1101,6 +1124,7 @@ local function weapBulletCount( wep ) end +-- find weapon's damage local function weapDamage( wep ) local dmg = 1 local primary = wep.Primary @@ -1122,58 +1146,60 @@ local function weapDamage( wep ) end ---[[------------------------------------ - Name: NEXTBOT:SetupEyeAngles - Desc: (INTERNAL) Aiming bot to desired direction. - Arg1: - Ret1: ---]]------------------------------------ - -local math_max = math.max -local math_min = math.min -local math_abs = math.abs +do + local math_max = math.max + local math_min = math.min + local math_abs = math.abs -function ENT:SetupEyeAngles( myTbl ) - -- old angles - local angp = myTbl.m_PitchAim - local angy = entMeta.GetAngles( self ).y + --[[------------------------------------ + Name: NEXTBOT:SetupEyeAngles + Desc: (INTERNAL) Aiming bot to desired direction. + Arg1: + Ret1: + --]]------------------------------------ + function ENT:SetupEyeAngles( myTbl ) + -- old angles + local angp = myTbl.m_PitchAim + local angy = entMeta.GetAngles( self ).y - -- new angles - local desired = myTbl.GetDesiredEyeAngles( self ) -- AddNetworkVar - local punch = myTbl.GetViewPunchAngles( self ) -- AddNetworkVar + -- new angles + local desired = myTbl.GetDesiredEyeAngles( self ) -- AddNetworkVar + local punch = myTbl.GetViewPunchAngles( self ) -- AddNetworkVar - if myTbl.IsControlledByPlayer( self, myTbl ) then - desired = myTbl.GetControlPlayer( self ):EyeAngles() - end + if myTbl.IsControlledByPlayer( self, myTbl ) then + desired = myTbl.GetControlPlayer( self ):EyeAngles() + end - local diffp = math.AngleDifference( desired.p, angp ) - local diffy = math.AngleDifference( desired.y, angy ) - local max = myTbl.BehaveInterval * myTbl.AimSpeed + local diffp = math.AngleDifference( desired.p, angp ) + local diffy = math.AngleDifference( desired.y, angy ) + local max = myTbl.BehaveInterval * myTbl.AimSpeed - diffp = diffp < 0 and math_max( -max, diffp ) or math_min( max, diffp ) - diffy = diffy < 0 and math_max( -max, diffy ) or math_min( max, diffy ) + diffp = diffp < 0 and math_max( -max, diffp ) or math_min( max, diffp ) + diffy = diffy < 0 and math_max( -max, diffy ) or math_min( max, diffy ) - angp = angp + diffp - angy = angy + diffy + angp = angp + diffp + angy = angy + diffy - -- evil, horrible rare bug - if math_abs( angp ) > 360 then - angp = 0 + -- evil, horrible rare bug + if math_abs( angp ) > 360 then + angp = 0 - end + end - entMeta.SetAngles( self, Angle( 0, angy, 0 ) ) + entMeta.SetAngles( self, Angle( 0, angy, 0 ) ) - myTbl.m_PitchAim = angp - myTbl.SetAimPitch( self, angp ) - entMeta.SetPoseParameter( self, "aim_pitch", self.m_PitchAim + punch.p ) - entMeta.SetPoseParameter( self, "aim_yaw", punch.y ) + myTbl.m_PitchAim = angp + myTbl.SetAimPitch( self, angp ) + entMeta.SetPoseParameter( self, "aim_pitch", self.m_PitchAim + punch.p ) + entMeta.SetPoseParameter( self, "aim_yaw", punch.y ) - self:SetEyeTarget( self:GetShootPos() + self:GetEyeAngles():Forward() * 100 ) + self:SetEyeTarget( self:GetShootPos() + self:GetEyeAngles():Forward() * 100 ) + end end -hook.Add( "PlayerCanPickupWeapon", "TerminatorNextBot", function( _ply,wep ) +-- dont pickup weapons in bot's hands! +hook.Add( "PlayerCanPickupWeapon", "TerminatorNextBot", function( _ply, wep ) -- Do not allow pickup when bot carries this weapon local owner = wep:GetOwner() if IsValid( owner ) and owner.TerminatorNextBot then @@ -1217,7 +1243,8 @@ function ENT:ResetWeaponSearchTimers() end -terminator_Extras.weapons_analogTblsCache = terminator_Extras.weapons_analogTblsCache or {} +-- this cache can only ever be as big as the EngineAnalogs table +terminator_Extras.weapons_analogTblsCache = terminator_Extras.weapons_analogTblsCache or {} local analogTblsCache = terminator_Extras.weapons_analogTblsCache hook.Add( "terminator_nextbot_oneterm_exists", "setup_analogtblscache", function() terminator_Extras.weapons_analogTblsCache = {} @@ -1265,6 +1292,8 @@ function ENT:canGetWeapon() local currWeap = self:GetActiveWeapon() if not IsValid( currWeap ) then return true, newWeap end + -- get the ENT table for the engine weapon analog, if it has one + -- to check for special properties like "worksWithoutSightline" local analogClass = wepDat.engineAnalog local analogsData = analogTblsCache[analogClass] if analogClass and not analogsData then @@ -1327,8 +1356,13 @@ function ENT:canGetWeapon() end end --- tells bot to get the best weapon it ever found, even if it's halfway across the map --- used in term tasks when enemy is unreachable +--[[------------------------------------ + Name: NEXTBOT:GetTheBestWeapon + Desc: tells bot to get the best weapon it ever found, even if it's halfway across the map + used in term tasks when enemy is unreachable + Arg1: + Ret1: +--]]------------------------------------ function ENT:GetTheBestWeapon() local nextGetBest = self.term_NextNeedsAWeaponNow if not nextGetBest then @@ -1352,7 +1386,14 @@ function ENT:GetTheBestWeapon() end --- can find item crates too +--[[------------------------------------ + Name: NEXTBOT:FindWeapon + Desc: Finds the best weapon in range, including holstered ones. + Uses a lot of factors to determine which weapon is best. + Caches the result, and only updates it every few seconds. + Arg1: Table | myTbl | _index optimisation table. + Ret1: Table | { wep = Entity, weight = number, range = number, isBox = bool } +--]]------------------------------------ function ENT:FindWeapon( myTbl ) local searchrange = myTbl.WeaponSearchRange local wep @@ -1486,6 +1527,14 @@ function ENT:FindWeapon( myTbl ) end +--[[------------------------------------ + Name: ENT:Term_GetDamageTrackerOf + Desc: (INTERNAL) Gets the damage tracker table for a weapon or class. Creates it if it doesn't exist. + Arg1: Entity | me | Bot to get tracker for. + Arg2: Table | myTbl | Bot's ENT table. + Arg3: string|Entity | wepOrClass | Weapon or class to get tracker for. + Ret1: Table | Damage tracker table. +--]]------------------------------------ local function getDamageTrackerOf( me, myTbl, wepOrClass ) local trackers = myTbl.term_WeaponDamageTrackers if not trackers then @@ -1555,6 +1604,14 @@ end ENT.Term_GetDamageTrackerOf = getDamageTrackerOf +--[[------------------------------------ + Name: ENT:Term_GetTrackedDamage + Desc: Gets the tracked damage for a weapon or class, used to judge weapons, stop using ones that never deal any damage. + Arg1: Entity | me | Bot to get tracker for. + Arg2: Table | myTbl | Bot's ENT table. + Arg3: string|Entity | wepOrClass | Weapon or class to get tracker for. + Ret1: number | Total damage tracked for this weapon or class. +--]]------------------------------------ local function getTrackedDamage( me, myTbl, wepOrClass ) local dmgTracker = getDamageTrackerOf( me, myTbl, wepOrClass ) if not dmgTracker then return end @@ -1565,6 +1622,15 @@ end ENT.Term_GetTrackedDamage = getTrackedDamage +--[[------------------------------------ + Name: addTrackedDamage + Desc: (INTERNAL) Used to update a tracker for a weapon/class upon damage event. + Arg1: Entity | me | Bot to add damage for. + Arg2: Table | myTbl | Bot's ENT table. + Arg3: string|Entity | wepOrClass | Weapon or class to add damage for. + Arg4: number | add | Amount of damage to add. + Ret1: +--]]------------------------------------ local function addTrackedDamage( me, myTbl, wepOrClass, add ) local dmgTracker = getDamageTrackerOf( me, myTbl, wepOrClass ) if not dmgTracker then return end @@ -1768,8 +1834,30 @@ end local dropWeps = CreateConVar( "termhunter_dropuselessweapons", 1, FCVAR_NONE, "Detect and drop useless weapons? Does not stop bot from dropping erroring weapons" ) --- is a weapon not being useful? - +--[[------------------------------------ + Name: ENT:JudgeWeapon + Desc: Evaluates weapon performance and adjusts bot behavior accordingly. + + This function implements a sophisticated weapon utility tracking system that: + - Tracks damage dealt vs. attack attempts to measure weapon effectiveness + - Records standing vs. crouching performance for future optimization + - Automatically drops weapons that consistently fail to deal damage + - Increases weight for highly effective weapons + - Decreases weight for underperforming weapons progressively + - Enables "no leading" mode if weapon underperforms with leading enabled + - Respects weapon ownership (won't drop default/property weapons) + - Skips judgment for fists, utility-ignored weapons + + The judgment system uses tolerance thresholds and bonus attack attempts that vary + by weapon type (melee, spread-based, etc.) to account for different firing patterns. + + Only judges when termhunter_dropuselessweapons is enabled and there is perfect + visibility to the enemy (NothingOrBreakableBetweenEnemy). + + Arg1: Table | myTbl | Bot's ENT table ( _index optimization ). + Arg2: Entity | myWeapon | Weapon entity to judge ( can be engine or scripted weapon ). + Ret1: nil | No return value. Side effects include dropping weapon, adjusting weights, and updating damage tracker. +--]]------------------------------------ function ENT:JudgeWeapon( myTbl, myWeapon ) if myTbl.IsFists( self, myTbl ) then return end if not dropWeps:GetBool() then return end -- can disable this @@ -1847,6 +1935,16 @@ function ENT:JudgeWeapon( myTbl, myWeapon ) end end +--[[------------------------------------ + Name: ENT:TryAndUseWeaponRight + Desc: Analyzes weapon damage tracker and crouches if weapon performs better while crouching. + Compares damage-per-attempt ratio between standing and crouching positions. + Only applies to bots with brains( optimisation ), and skips in dangerous/close-range situations. + Arg1: Table | myTbl | Bot's ENT table. + Arg2: Entity | _wep | Weapon entity ( unused parameter ). + Arg3: Table | dmgTracker | Damage tracker table for the weapon. + Ret1: nil | Sets self.overrideCrouch if crouching is determined to be more effective. +--]]------------------------------------ function ENT:TryAndUseWeaponRight( myTbl, _wep, dmgTracker ) if not myTbl.HasBrains then return end -- no brains, no crouching @@ -1877,7 +1975,13 @@ function ENT:TryAndUseWeaponRight( myTbl, _wep, dmgTracker ) end end - +--[[------------------------------------ + Name: ENT:WeaponIsPlacable + Desc: Checks if a weapon can be placed ( for tasks that involve placing weapons in the world ). + See slams engine override for functional example + Arg1: Entity | wep | Weapon entity to check ( defaults to active weapon if not provided ). + Ret1: bool | True if the weapon is placable, false otherwise. +--]]------------------------------------ function ENT:WeaponIsPlacable( wep ) wep = wep or self:GetWeapon() @@ -1888,8 +1992,16 @@ function ENT:WeaponIsPlacable( wep ) end - - +--[[------------------------------------ + Name: ENT:GetWeaponRange + Desc: Gets effective weapon range. Uses empirical combat data (maxDistEverDamagedWith) after weapon + has proven itself (20+ attacks, 250+ damage), otherwise falls back to weapon properties or + calculates from spread/bullet count. Returns math.huge for infinite range. + Arg1: Table | myTbl | Bot's ENT table (_index optimization). + Arg2: Entity | wep | Weapon entity to check (defaults to active weapon). + Arg3: Table | wepTable | Weapon's ENT table (optional optimization). + Ret1: number | Effective range in Hammer units, or math.huge for infinite range. +--]]------------------------------------ function ENT:GetWeaponRange( myTbl, wep, wepTable ) myTbl = myTbl or entMeta.GetTable( self ) From 1a7b828f6371c388fda183e5f9807046c9b1b302 Mon Sep 17 00:00:00 2001 From: StrawWagen <64710817+StrawWagen@users.noreply.github.com> Date: Wed, 18 Feb 2026 00:57:06 -0700 Subject: [PATCH 6/6] More tweaks! --- .../terminator_nextbot/motionoverrides.lua | 10 +-- .../terminator_nextbot/spokenlines.lua | 88 ++++++++++++++----- .../terminator_nextbot_base/motion.lua | 27 +++--- 3 files changed, 85 insertions(+), 40 deletions(-) diff --git a/lua/entities/terminator_nextbot/motionoverrides.lua b/lua/entities/terminator_nextbot/motionoverrides.lua index ed8be66..990517e 100644 --- a/lua/entities/terminator_nextbot/motionoverrides.lua +++ b/lua/entities/terminator_nextbot/motionoverrides.lua @@ -187,16 +187,16 @@ function ENT:hitBreakable( traceStruct, traceResult, skipDistCheck ) end --[[------------------------------------ -Name: NEXTBOT:DisableBehaviour -Desc: Decides should behaviour be disabled. -Arg1: -Ret1: bool | Return true to disable. + Name: NEXTBOT:DisableBehaviour + Desc: Decides should behaviour be disabled. + Arg1: myTbl optimisation table + Ret1: bool | Return true to disable. --]]------------------------------------ function ENT:DisableBehaviour( myTbl ) local disabledThinking = myTbl.DisabledThinking( self ) and not myTbl.IsControlledByPlayer( self, myTbl ) if disabledThinking then return true end - return myTbl.IsPostureActive( self ) or myTbl.IsGestureActive( self, true ) or myTbl.RunTask( self, "DisableBehaviour" ) + return myTbl.IsPostureActive( self, myTbl ) or myTbl.IsGestureActive( self, true ) or myTbl.RunTask( self, "DisableBehaviour" ) end diff --git a/lua/entities/terminator_nextbot/spokenlines.lua b/lua/entities/terminator_nextbot/spokenlines.lua index ebecfbe..73dc57b 100644 --- a/lua/entities/terminator_nextbot/spokenlines.lua +++ b/lua/entities/terminator_nextbot/spokenlines.lua @@ -1,4 +1,5 @@ +-- set these on ur bot to change its voice pitch, level, or DSP effect. -- ENT.term_SoundPitchShift -- ENT.term_SoundLevelShift -- ENT.term_SoundDSP @@ -21,10 +22,12 @@ function ENT:InitializeSpeaking() end -local function nextSpeakWhenSoundIsOver( ent, path, pitch ) +local function setNextSpeakWhenSoundIsOver( ent, path, pitch ) local duration = SoundDuration( path ) or 1 -- FOLLOW GMOD WIKI FOR ACCURATE MP3 DURATIONS! SAVE WITH CONSTANT BITRATE! local durDivisor = pitch / 100 + local additional = math.Rand( 0.1, 0.2 ) duration = duration / durDivisor + duration = duration + additional ent.NextTermSpeak = CurTime() + duration return duration @@ -35,9 +38,10 @@ local sndFlags = bit.bor( SND_CHANGE_PITCH, SND_CHANGE_VOL ) function ENT:SpokenLinesThink( myTbl ) if not myTbl.CanSpeak then return end - if myTbl.NextTermSpeak > CurTime() then return end + local noLines = #myTbl.StuffToSay <= 0 + if myTbl.AlwaysPlayLooping or noLines then -- play looping idle/angry sounds local loopingSounds = nil local oldState = myTbl.term_OldLoopingSoundState @@ -141,7 +145,7 @@ function ENT:SpokenLinesThink( myTbl ) local pitch = 100 + pitShift EmitSentence( sentence, self:GetShootPos(), self:EntIndex(), CHAN_VOICE, 1, 80 + lvlShift, SND_NOFLAGS, pitch ) - local additional = math.random( 10, 15 ) / 50 + local additional = math.Rand( 0.1, 0.2 ) local duration = SentenceDuration( sentence ) or 1 local durDivisor = pitch / 100 @@ -181,12 +185,38 @@ function ENT:SpokenLinesThink( myTbl ) self:EmitSound( path, 76 + lvlShift, pitch, 1, CHAN_VOICE, sndFlags, dsp ) - nextSpeakWhenSoundIsOver( self, path, pitch ) + setNextSpeakWhenSoundIsOver( self, path, pitch ) return end end +--[[------------------------------------ + Name: NEXTBOT:Term_SpeakSound + Desc: Make the bot speak something without interrupting whatever it's "currently" saying. + Arg1: string/table | pathIn | Sound path or table of sound paths to queue. + Arg2: (optional) function | conditionFunc | Condition function that must return true for sound to play. + Ret1: +--]]------------------------------------ +function ENT:Term_SpeakSound( pathIn, conditionFunc ) + if conditionFunc then + table.insert( self.StuffToSay, { path = pathIn, conditionFunc = conditionFunc } ) + + else + if #self.StuffToSay >= 4 then return end -- don't add infinite stuff to say. + if #self.StuffToSay >= 2 and math.random( 0, 100 ) >= 50 then return end + table.insert( self.StuffToSay, { path = pathIn } ) + + end +end + +--[[------------------------------------ + Name: NEXTBOT:Term_SpeakSoundNow + Desc: Make a bot say something NOW. Applies pitch, level, and DSP shifts. + Arg1: string/table | pathIn | Sound path or table of sound paths to play. + Arg2: (optional) number | specificPitchShift | Additional pitch shift to apply. Default is 0. + Ret1: number | Duration of the sound. +--]]------------------------------------ function ENT:Term_SpeakSoundNow( pathIn, specificPitchShift ) specificPitchShift = specificPitchShift or 0 @@ -203,24 +233,36 @@ function ENT:Term_SpeakSoundNow( pathIn, specificPitchShift ) local pitch = 100 + pitShift + specificPitchShift self:EmitSound( pathIn, 76 + lvlShift, pitch, 1, CHAN_VOICE, sndFlags, dsp ) - return nextSpeakWhenSoundIsOver( self, pathIn, pitch ) + return setNextSpeakWhenSoundIsOver( self, pathIn, pitch ) end --- puts line on a table of other lines that bot sifts through, will never overwrite existing lines. -function ENT:Term_SpeakSound( pathIn, conditionFunc ) +--[[------------------------------------ + Name: NEXTBOT:Term_SpeakSentence + Desc: Queues a sentence to be played. Won't interrupt current sentence/sound, but will play as soon as possible. Applies pitch and level shifts. + Arg1: string/table | sentenceIn | Sentence name or table of sentence names to queue. + Arg2: (optional) function | conditionFunc | Condition function that must return true for sentence to play. + Ret1: +--]]------------------------------------ +function ENT:Term_SpeakSentence( sentenceIn, conditionFunc ) if conditionFunc then - table.insert( self.StuffToSay, { path = pathIn, conditionFunc = conditionFunc } ) + table.insert( self.StuffToSay, { sent = sentenceIn, conditionFunc = conditionFunc } ) else if #self.StuffToSay >= 4 then return end -- don't add infinite stuff to say. if #self.StuffToSay >= 2 and math.random( 0, 100 ) >= 50 then return end - table.insert( self.StuffToSay, { path = pathIn } ) + table.insert( self.StuffToSay, { sent = sentenceIn } ) end end --- Immediately plays a sentence, bypassing StuffToSay queue +--[[------------------------------------ + Name: NEXTBOT:Term_SpeakSentenceNow + Desc: Immediately plays a sentence. + Arg1: string/table | sentenceIn | Sentence name or table of sentence names to play. + Arg2: (optional) number | specificPitchShift | Additional pitch shift to apply. Default is 0. + Ret1: number | Duration of the sentence. +--]]------------------------------------ function ENT:Term_SpeakSentenceNow( sentenceIn, specificPitchShift ) specificPitchShift = specificPitchShift or 0 @@ -235,7 +277,7 @@ function ENT:Term_SpeakSentenceNow( sentenceIn, specificPitchShift ) local pitch = 100 + pitShift + specificPitchShift EmitSentence( sentenceIn, self:GetShootPos(), self:EntIndex(), CHAN_VOICE, 1, 80 + lvlShift, SND_NOFLAGS, pitch ) - local additional = math.random( 10, 15 ) / 50 + local additional = math.Rand( 0.1, 0.2 ) local duration = SentenceDuration( sentenceIn ) or 1 local durDivisor = pitch / 100 duration = duration / durDivisor @@ -244,24 +286,24 @@ function ENT:Term_SpeakSentenceNow( sentenceIn, specificPitchShift ) end -function ENT:Term_SpeakSentence( sentenceIn, conditionFunc ) - if conditionFunc then - table.insert( self.StuffToSay, { sent = sentenceIn, conditionFunc = conditionFunc } ) - - else - if #self.StuffToSay >= 4 then return end -- don't add infinite stuff to say. - if #self.StuffToSay >= 2 and math.random( 0, 100 ) >= 50 then return end - table.insert( self.StuffToSay, { sent = sentenceIn } ) - - end -end - +--[[------------------------------------ + Name: NEXTBOT:Term_ClearStuffToSay + Desc: Clears all queued sounds/sentences and resets speak timer. + Arg1: + Ret1: +--]]------------------------------------ function ENT:Term_ClearStuffToSay() self.StuffToSay = {} self.NextTermSpeak = 0 end +--[[------------------------------------ + Name: NEXTBOT:Term_DontSpeakFor + Desc: Prevents bot from speaking for a specified duration. + Arg1: number | time | Time in seconds to block speaking. + Ret1: +--]]------------------------------------ function ENT:Term_DontSpeakFor( time ) self.NextTermSpeak = CurTime() + time diff --git a/lua/entities/terminator_nextbot_base/motion.lua b/lua/entities/terminator_nextbot_base/motion.lua index 42269c5..95885af 100644 --- a/lua/entities/terminator_nextbot_base/motion.lua +++ b/lua/entities/terminator_nextbot_base/motion.lua @@ -1,4 +1,5 @@ -local entMeta = FindMetaTable("Entity") +local entMeta = FindMetaTable( "Entity" ) +local nextbotMeta = FindMetaTable( "NextBot" ) -- Motion Type Enums TERMINATOR_NEXTBOT_MOTIONTYPE_IDLE = 0 @@ -278,11 +279,12 @@ end --[[------------------------------------ Name: NEXTBOT:IsPostureActive Desc: Returns whenever we currently playing a posture or not. - Arg1: + Arg1: myTbl (optional) | Optimization table. Ret1: bool | Posture active or not. --]]------------------------------------ -function ENT:IsPostureActive() - return self.m_CurPosture and (!self.m_CurPosture[2] or CurTime()