Skip to content

Commit 0c449f5

Browse files
committed
Further changes to weapon/tool search.
* Humans use cheap search on big maps to avoid stuttering * Added yielding to MO in radius search to keep it light, since there can be a lot of actors doing it * Added early exit to MO in radius search if there's a device * Added early exit to scoring if there's something with a super low score (a bit redundant with the above one, but still helpful) * Changed a pairs to ipairs, cause that may have meant all this searching and scoring and such may not have really been used anyway lol
1 parent 57ac434 commit 0c449f5

File tree

2 files changed

+74
-35
lines changed

2 files changed

+74
-35
lines changed

Data/Base.rte/AI/HumanBehaviors.lua

Lines changed: 72 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -914,27 +914,38 @@ end
914914

915915
-- find a weapon to pick up
916916
function HumanBehaviors.WeaponSearch(AI, Owner, Abort)
917-
local minDist;
918917
local pickupDiggers = not Owner:HasObjectInGroup("Tools - Diggers");
919918

919+
local maxSearchDistance;
920920
if AI.isPlayerOwned then
921-
minDist = 100; -- don't move player actors too far
921+
maxSearchDistance = 100; -- don't move player actors too far
922922
else
923-
minDist = FrameMan.PlayerScreenWidth * 0.45;
923+
maxSearchDistance = FrameMan.PlayerScreenWidth * 0.45;
924924
end
925925

926926
if Owner.AIMode == Actor.AIMODE_SENTRY then
927-
minDist = minDist * 0.6;
927+
maxSearchDistance = maxSearchDistance * 0.6;
928928
end
929929

930930
local devices = {};
931931
local mosSearched = 0;
932-
for movableObject in MovableMan:GetMOsInRadius(Owner.Pos, minDist, -1, true) do
932+
for movableObject in MovableMan:GetMOsInRadius(Owner.Pos, maxSearchDistance, -1, true) do
933933
mosSearched = mosSearched + 1;
934-
if IsHeldDevice(movableObject) then
934+
if mosSearched % 30 == 0 then
935+
local _ai, _ownr, _abrt = coroutine.yield();
936+
if _abrt then return true end
937+
end
938+
939+
if IsHeldDevice(movableObject) and MovableMan:ValidMO(movableObject) then
935940
local device = ToHeldDevice(movableObject);
936941
if device:IsPickupableBy(Owner) and not device:IsActivated() and device.Vel.Largest < 3 and not SceneMan:IsUnseen(device.Pos.X, device.Pos.Y, Owner.Team) then
937-
table.insert(devices, { device = device, distance = SceneMan:ShortestDistance(Owner.Pos, device.Pos, SceneMan.SceneWrapsX or SceneMan.SceneWrapsY) });
942+
local distanceToDevice = SceneMan:ShortestDistance(Owner.Pos, device.Pos, SceneMan.SceneWrapsX or SceneMan.SceneWrapsY);
943+
if distanceToDevice:MagnitudeIsGreaterThan(Owner.Radius * 0.75) then
944+
table.insert(devices, { device = device, distance = distanceToDevice });
945+
else
946+
devices = {{ device = device, distance = distanceToDevice }};
947+
break;
948+
end
938949
end
939950
end
940951
end
@@ -944,21 +955,24 @@ function HumanBehaviors.WeaponSearch(AI, Owner, Abort)
944955
local _ai, _ownr, _abrt = coroutine.yield();
945956
if _abrt then return true end
946957

947-
local waypointDistance = 36;
958+
local maxWaypointDistance = 36;
948959
if AI.isPlayerOwned then
949-
waypointDistance = 10;
960+
maxWaypointDistance = 10;
950961
end
951962

952-
local waypoints, score;
953963
local devicesToPickUp = {};
954964
for _, deviceEntry in pairs(devices) do
955965
local device = deviceEntry.device;
956966
if MovableMan:ValidMO(device) then
957-
local pathToItemIsObstructed = SceneMan:CastStrengthRay(Owner.Pos, deviceEntry.distance, 5, Vector(), 4, rte.grassID, true); --TODO if still laggy, just use this entirely and don't bother calcuating a path. Maybe only calculate path on smaller maps (< 10,000,000 square px)
967+
local pathToItemIsObstructed = false;
968+
if AI.useExpensiveToolAndWeaponSearch then
969+
pathToItemIsObstructed = SceneMan:CastStrengthRay(Owner.Pos, deviceEntry.distance, 5, Vector(), 4, rte.grassID, true);
970+
end
958971
local pathfinderNodeSize = 20; -- TODO this should be read from cpp
959972

960-
local distanceToTarget = pathToItemIsObstructed and SceneMan.Scene:CalculatePath(Owner.Pos, device.Pos, false, 1, Owner.Team) or deviceEntry.distance.Magnitude / pathfinderNodeSize;
961-
if distanceToTarget < waypointDistance and distanceToTarget > -1 then
973+
local distanceToTarget = pathToItemIsObstructed and SceneMan.Scene:CalculatePath(Owner.Pos, device.Pos, false, 1, Owner.Team) or (deviceEntry.distance.Magnitude / pathfinderNodeSize);
974+
if distanceToTarget < maxWaypointDistance and distanceToTarget > -1 then
975+
local score = distanceToTarget
962976
if device:HasObjectInGroup("Weapons - Primary") or device:HasObjectInGroup("Weapons - Heavy") then
963977
score = distanceToTarget * 0.4; -- prioritize primary or heavy weapons
964978
elseif device.ClassName == "TDExplosive" then
@@ -967,14 +981,17 @@ function HumanBehaviors.WeaponSearch(AI, Owner, Abort)
967981
if pickupDiggers and device:HasObjectInGroup("Tools - Diggers") then
968982
score = distanceToTarget * 1.8; -- avoid diggers if there are other weapons
969983
else
970-
distanceToTarget = waypointDistance;
984+
distanceToTarget = maxWaypointDistance;
971985
end
972-
else
973-
score = distanceToTarget;
974986
end
975987

976-
if distanceToTarget < waypointDistance then
988+
if distanceToTarget < maxWaypointDistance then
977989
table.insert(devicesToPickUp, {device = device, score = score});
990+
if not pathToItemIsObstructed or score < 1 then
991+
local _ai, _ownr, _abrt = coroutine.yield();
992+
if _abrt then return true end
993+
break;
994+
end
978995
end
979996
for i = 1, 2 do
980997
local _ai, _ownr, _abrt = coroutine.yield();
@@ -984,9 +1001,10 @@ function HumanBehaviors.WeaponSearch(AI, Owner, Abort)
9841001
end
9851002
end
9861003

1004+
9871005
AI.PickupHD = nil;
988-
table.sort(devicesToPickUp, function(A,B) return A.score < B.score end); -- sort the items in order of discounted distance
989-
for _, deviceToPickupEntry in pairs(devicesToPickUp) do
1006+
table.sort(devicesToPickUp, function(A,B) return A.score < B.score end);
1007+
for _, deviceToPickupEntry in ipairs(devicesToPickUp) do
9901008
if MovableMan:ValidMO(deviceToPickupEntry.device) and deviceToPickupEntry.device:IsDevice() then
9911009
AI.PickupHD = deviceToPickupEntry.device;
9921010
break;
@@ -1026,25 +1044,38 @@ end
10261044

10271045
-- find a tool to pick up
10281046
function HumanBehaviors.ToolSearch(AI, Owner, Abort)
1029-
local minDist;
1047+
local maxSearchDistance;
10301048
if Owner.AIMode == Actor.AIMODE_GOLDDIG then
1031-
minDist = FrameMan.PlayerScreenWidth * 0.5; -- move up to half a screen when digging
1049+
maxSearchDistance = FrameMan.PlayerScreenWidth * 0.5; -- move up to half a screen when digging
10321050
elseif AI.isPlayerOwned then
1033-
minDist = 60; -- don't move player actors too far
1051+
maxSearchDistance = 60; -- don't move player actors too far
10341052
else
1035-
minDist = FrameMan.PlayerScreenWidth * 0.3;
1053+
maxSearchDistance = FrameMan.PlayerScreenWidth * 0.3;
10361054
end
10371055

10381056
if Owner.AIMode == Actor.AIMODE_SENTRY then
1039-
minDist = minDist * 0.6;
1057+
maxSearchDistance = maxSearchDistance * 0.6;
10401058
end
10411059

10421060
local devices = {};
1043-
for movableObject in MovableMan:GetMOsInRadius(Owner.Pos, minDist, -1, true) do
1044-
if IsHeldDevice(movableObject) then
1061+
local mosSearched = 0;
1062+
for movableObject in MovableMan:GetMOsInRadius(Owner.Pos, maxSearchDistance, -1, true) do
1063+
mosSearched = mosSearched + 1;
1064+
if mosSearched % 30 == 0 then
1065+
local _ai, _ownr, _abrt = coroutine.yield();
1066+
if _abrt then return true end
1067+
end
1068+
1069+
if IsHeldDevice(movableObject) and MovableMan:ValidMO(movableObject) then
10451070
local device = ToHeldDevice(movableObject);
10461071
if device:IsPickupableBy(Owner) and not device:IsActivated() and device.Vel.Largest < 3 and not SceneMan:IsUnseen(device.Pos.X, device.Pos.Y, Owner.Team) and device:HasObjectInGroup("Tools - Diggers") then
1047-
table.insert(devices, { device = device, distance = SceneMan:ShortestDistance(Owner.Pos, device.Pos, SceneMan.SceneWrapsX or SceneMan.SceneWrapsY) });
1072+
local distanceToDevice = SceneMan:ShortestDistance(Owner.Pos, device.Pos, SceneMan.SceneWrapsX or SceneMan.SceneWrapsY);
1073+
if distanceToDevice:MagnitudeIsGreaterThan(20) then
1074+
table.insert(devices, { device = device, distance = distanceToDevice });
1075+
else
1076+
devices = {{ device = device, distance = distanceToDevice }};
1077+
break;
1078+
end
10481079
end
10491080
end
10501081
end
@@ -1054,25 +1085,31 @@ function HumanBehaviors.ToolSearch(AI, Owner, Abort)
10541085
local _ai, _ownr, _abrt = coroutine.yield();
10551086
if _abrt then return true end
10561087

1057-
local waypointDistance = 16;
1088+
local maxWaypointDistance = 16;
10581089
if Owner.AIMode == Actor.AIMODE_GOLDDIG then
1059-
waypointDistance = 30;
1090+
maxWaypointDistance = 30;
10601091
elseif AI.isPlayerOwned then
1061-
waypointDistance = 5;
1092+
maxWaypointDistance = 5;
10621093
end
10631094

1064-
local waypoints, score;
10651095
local devicesToPickUp = {};
10661096
for _, deviceEntry in pairs(devices) do
10671097
local device = deviceEntry.device;
10681098
if MovableMan:ValidMO(device) then
1069-
local pathToItemIsObstructed = SceneMan:CastStrengthRay(Owner.Pos, deviceEntry.distance, 5, Vector(), 4, rte.grassID, true);
1099+
local pathToItemIsObstructed = false;
1100+
if AI.useExpensiveToolAndWeaponSearch then
1101+
pathToItemIsObstructed = SceneMan:CastStrengthRay(Owner.Pos, deviceEntry.distance, 5, Vector(), 4, rte.grassID, true);
1102+
end
10701103
local pathfinderNodeSize = 20; -- TODO this should be read from cpp
10711104

1072-
local distanceToTarget = pathToItemIsObstructed and SceneMan.Scene:CalculatePath(Owner.Pos, device.Pos, false, 1, Owner.Team) or deviceEntry.distance.Magnitude / pathfinderNodeSize;
1073-
if distanceToTarget < waypointDistance and distanceToTarget > -1 then
1105+
local distanceToTarget = pathToItemIsObstructed and SceneMan.Scene:CalculatePath(Owner.Pos, device.Pos, false, 1, Owner.Team) or (deviceEntry.distance.Magnitude / pathfinderNodeSize);
1106+
if distanceToTarget < maxWaypointDistance and distanceToTarget > -1 then
10741107
table.insert(devicesToPickUp, {device = device, score = distanceToTarget});
10751108

1109+
if not pathToItemIsObstructed or distanceToTarget < 1 then
1110+
break;
1111+
end
1112+
10761113
for i = 1, 2 do
10771114
local _ai, _ownr, _abrt = coroutine.yield();
10781115
if _abrt then return true end
@@ -1083,7 +1120,7 @@ function HumanBehaviors.ToolSearch(AI, Owner, Abort)
10831120

10841121
AI.PickupHD = nil;
10851122
table.sort(devicesToPickUp, function(A,B) return A.score < B.score end); -- sort the items in order of discounted distance
1086-
for _, deviceToPickupEntry in pairs(devicesToPickUp) do
1123+
for _, deviceToPickupEntry in ipairs(devicesToPickUp) do
10871124
if MovableMan:ValidMO(deviceToPickupEntry.device) and deviceToPickupEntry.device:IsDevice() then
10881125
AI.PickupHD = deviceToPickupEntry.device;
10891126
break;

Data/Base.rte/AI/NativeHumanAI.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ NativeHumanAI = {};
44

55
function NativeHumanAI:Create(Owner)
66
local Members = {};
7+
8+
Members.useExpensiveToolAndWeaponSearch = SceneMan.SceneWidth * SceneMan.SceneHeight < 10000000;
79

810
Members.lateralMoveState = Actor.LAT_STILL;
911
Members.proneState = AHuman.NOTPRONE;

0 commit comments

Comments
 (0)