Skip to content

Commit 57ac434

Browse files
committed
Updated human weapon and tool search behaviours so they're less expensive (particularly on big maps)
Also made it a little less ugleh
1 parent f6f2118 commit 57ac434

File tree

1 file changed

+95
-86
lines changed

1 file changed

+95
-86
lines changed

Data/Base.rte/AI/HumanBehaviors.lua

Lines changed: 95 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -915,94 +915,100 @@ end
915915
-- find a weapon to pick up
916916
function HumanBehaviors.WeaponSearch(AI, Owner, Abort)
917917
local minDist;
918-
local Devices = {};
919918
local pickupDiggers = not Owner:HasObjectInGroup("Tools - Diggers");
920919

921920
if AI.isPlayerOwned then
922-
minDist = 100; -- don't move player actors more than 4m
921+
minDist = 100; -- don't move player actors too far
923922
else
924923
minDist = FrameMan.PlayerScreenWidth * 0.45;
925924
end
926925

927926
if Owner.AIMode == Actor.AIMODE_SENTRY then
928927
minDist = minDist * 0.6;
929928
end
930-
931-
local itemsFound = 0;
932-
for Item in MovableMan.Items do -- store all HeldDevices of the correct type and within a certain range in a table
933-
local HD = ToHeldDevice(Item);
934-
if HD and HD:IsPickupableBy(Owner) and not HD:IsActivated() and HD.Vel.Largest < 3 and SceneMan:ShortestDistance(Owner.Pos, HD.Pos, SceneMan.SceneWrapsX).Largest < minDist and not SceneMan:IsUnseen(HD.Pos.X, HD.Pos.Y, Owner.Team) then
935-
table.insert(Devices, HD);
936-
itemsFound = itemsFound + 1;
929+
930+
local devices = {};
931+
local mosSearched = 0;
932+
for movableObject in MovableMan:GetMOsInRadius(Owner.Pos, minDist, -1, true) do
933+
mosSearched = mosSearched + 1;
934+
if IsHeldDevice(movableObject) then
935+
local device = ToHeldDevice(movableObject);
936+
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) });
938+
end
937939
end
938940
end
939-
940-
if itemsFound > 0 then
941-
local _ai, _ownr, _abrt = coroutine.yield(); -- wait until next frame
941+
table.sort(devices, function(device, otherDevice) return device.distance.SqrMagnitude < otherDevice.distance.SqrMagnitude end);
942+
943+
if #devices > 0 then
944+
local _ai, _ownr, _abrt = coroutine.yield();
942945
if _abrt then return true end
943946

947+
local waypointDistance = 36;
944948
if AI.isPlayerOwned then
945-
minDist = 10; -- # of waypoints
946-
else
947-
minDist = 36;
949+
waypointDistance = 10;
948950
end
949951

950952
local waypoints, score;
951-
local DevicesToPickUp = {};
952-
for _, Item in pairs(Devices) do
953-
if MovableMan:ValidMO(Item) then
954-
waypoints = SceneMan.Scene:CalculatePath(Owner.Pos, Item.Pos, false, 1, Owner.Team);
955-
if waypoints < minDist and waypoints > -1 then
956-
-- estimate the walking distance to the item
957-
if Item:HasObjectInGroup("Weapons - Primary") then
958-
score = waypoints * 0.4; -- prioritize primaries
959-
elseif Item.ClassName == "TDExplosive" then
960-
score = waypoints * 1.4; -- avoid grenades if there are other weapons
961-
elseif Item:IsTool() then
962-
if pickupDiggers and Item:HasObjectInGroup("Tools - Diggers") then
963-
score = waypoints * 1.8; -- avoid diggers if there are other weapons
953+
local devicesToPickUp = {};
954+
for _, deviceEntry in pairs(devices) do
955+
local device = deviceEntry.device;
956+
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)
958+
local pathfinderNodeSize = 20; -- TODO this should be read from cpp
959+
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
962+
if device:HasObjectInGroup("Weapons - Primary") or device:HasObjectInGroup("Weapons - Heavy") then
963+
score = distanceToTarget * 0.4; -- prioritize primary or heavy weapons
964+
elseif device.ClassName == "TDExplosive" then
965+
score = distanceToTarget * 1.4; -- avoid grenades if there are other weapons
966+
elseif device:IsTool() then
967+
if pickupDiggers and device:HasObjectInGroup("Tools - Diggers") then
968+
score = distanceToTarget * 1.8; -- avoid diggers if there are other weapons
964969
else
965-
waypoints = minDist; -- don't pick up
970+
distanceToTarget = waypointDistance;
966971
end
967972
else
968-
score = waypoints;
973+
score = distanceToTarget;
969974
end
970975

971-
if waypoints < minDist then
972-
table.insert(DevicesToPickUp, {HD=Item, score=score});
976+
if distanceToTarget < waypointDistance then
977+
table.insert(devicesToPickUp, {device = device, score = score});
978+
end
979+
for i = 1, 2 do
980+
local _ai, _ownr, _abrt = coroutine.yield();
981+
if _abrt then return true end
973982
end
974983
end
975-
976-
local _ai, _ownr, _abrt = coroutine.yield(); -- wait until next frame
977-
if _abrt then return true end
978984
end
979985
end
980986

981987
AI.PickupHD = nil;
982-
table.sort(DevicesToPickUp, function(A,B) return A.score < B.score end); -- sort the items in order of discounted distance
983-
for _, Data in pairs(DevicesToPickUp) do
984-
if MovableMan:ValidMO(Data.HD) and Data.HD:IsDevice() then
985-
AI.PickupHD = Data.HD;
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
990+
if MovableMan:ValidMO(deviceToPickupEntry.device) and deviceToPickupEntry.device:IsDevice() then
991+
AI.PickupHD = deviceToPickupEntry.device;
986992
break;
987993
end
988994
end
989995

990996
if AI.PickupHD then
991997
-- where do we move after pick up?
992-
local PrevMoveTarget, PrevSeceneWaypoint;
998+
local prevMoveTarget, prevSceneWaypoint;
993999
if Owner.MOMoveTarget and MovableMan:ValidMO(Owner.MOMoveTarget) then
994-
PrevMoveTarget = Owner.MOMoveTarget;
1000+
prevMoveTarget = Owner.MOMoveTarget;
9951001
else
996-
PrevSeceneWaypoint = SceneMan:MovePointToGround(Owner:GetLastAIWaypoint(), Owner.Height/5, 4); -- last wpt or current pos
1002+
prevSceneWaypoint = SceneMan:MovePointToGround(Owner:GetLastAIWaypoint(), Owner.Height/5, 4); -- last wpt or current pos
9971003
end
9981004

9991005
Owner:ClearMovePath();
10001006
Owner:AddAIMOWaypoint(AI.PickupHD);
10011007

1002-
if PrevMoveTarget then
1003-
Owner:AddAIMOWaypoint(PrevMoveTarget);
1004-
elseif PrevSeceneWaypoint then
1005-
Owner:AddAISceneWaypoint(PrevSeceneWaypoint);
1008+
if prevMoveTarget then
1009+
Owner:AddAIMOWaypoint(prevMoveTarget);
1010+
elseif prevSceneWaypoint then
1011+
Owner:AddAISceneWaypoint(prevSceneWaypoint);
10061012
end
10071013

10081014
if Owner.AIMode == Actor.AIMODE_SENTRY then
@@ -1024,80 +1030,83 @@ function HumanBehaviors.ToolSearch(AI, Owner, Abort)
10241030
if Owner.AIMode == Actor.AIMODE_GOLDDIG then
10251031
minDist = FrameMan.PlayerScreenWidth * 0.5; -- move up to half a screen when digging
10261032
elseif AI.isPlayerOwned then
1027-
minDist = 60; -- don't move player actors more than 3m
1033+
minDist = 60; -- don't move player actors too far
10281034
else
10291035
minDist = FrameMan.PlayerScreenWidth * 0.3;
10301036
end
10311037

10321038
if Owner.AIMode == Actor.AIMODE_SENTRY then
10331039
minDist = minDist * 0.6;
10341040
end
1035-
1036-
local Devices = {};
1037-
local itemsFound = 0;
1038-
for Item in MovableMan.Items do -- store all HeldDevices of the correct type and within a certain range in a table
1039-
local HD = ToHeldDevice(Item);
1040-
if HD and not HD:IsActivated() and HD.Vel.Largest < 3 and
1041-
SceneMan:ShortestDistance(Owner.Pos, HD.Pos, false).Largest < minDist and
1042-
not SceneMan:IsUnseen(HD.Pos.X, HD.Pos.Y, Owner.Team)
1043-
then
1044-
table.insert(Devices, HD);
1045-
itemsFound = itemsFound + 1;
1041+
1042+
local devices = {};
1043+
for movableObject in MovableMan:GetMOsInRadius(Owner.Pos, minDist, -1, true) do
1044+
if IsHeldDevice(movableObject) then
1045+
local device = ToHeldDevice(movableObject);
1046+
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) });
1048+
end
10461049
end
10471050
end
1048-
1049-
if itemsFound > 0 then
1050-
local _ai, _ownr, _abrt = coroutine.yield(); -- wait until next frame
1051+
table.sort(devices, function(device, otherDevice) return device.distance.SqrMagnitude < otherDevice.distance.SqrMagnitude end);
1052+
1053+
if #devices > 0 then
1054+
local _ai, _ownr, _abrt = coroutine.yield();
10511055
if _abrt then return true end
10521056

1057+
local waypointDistance = 16;
10531058
if Owner.AIMode == Actor.AIMODE_GOLDDIG then
1054-
minDist = 30;
1059+
waypointDistance = 30;
10551060
elseif AI.isPlayerOwned then
1056-
minDist = 5;
1057-
else
1058-
minDist = 16;
1061+
waypointDistance = 5;
10591062
end
10601063

1061-
local DevicesToPickUp = {};
1062-
for _, Item in pairs(Devices) do
1063-
if MovableMan:ValidMO(Item) and Item:HasObjectInGroup("Tools - Diggers") then
1064-
-- estimate the walking distance to the item
1065-
local waypoints = SceneMan.Scene:CalculatePath(Owner.Pos, Item.Pos, false, 1, Owner.Team);
1066-
if waypoints < minDist and waypoints > -1 then
1067-
table.insert(DevicesToPickUp, {HD=Item, score=waypoints});
1064+
local waypoints, score;
1065+
local devicesToPickUp = {};
1066+
for _, deviceEntry in pairs(devices) do
1067+
local device = deviceEntry.device;
1068+
if MovableMan:ValidMO(device) then
1069+
local pathToItemIsObstructed = SceneMan:CastStrengthRay(Owner.Pos, deviceEntry.distance, 5, Vector(), 4, rte.grassID, true);
1070+
local pathfinderNodeSize = 20; -- TODO this should be read from cpp
1071+
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
1074+
table.insert(devicesToPickUp, {device = device, score = distanceToTarget});
1075+
1076+
for i = 1, 2 do
1077+
local _ai, _ownr, _abrt = coroutine.yield();
1078+
if _abrt then return true end
1079+
end
10681080
end
1069-
1070-
local _ai, _ownr, _abrt = coroutine.yield(); -- wait until next frame
1071-
if _abrt then return true end
10721081
end
10731082
end
10741083

10751084
AI.PickupHD = nil;
1076-
table.sort(DevicesToPickUp, function(A,B) return A.score < B.score end); -- sort the items in order of waypoints
1077-
for _, Data in pairs(DevicesToPickUp) do
1078-
if MovableMan:ValidMO(Data.HD) and Data.HD:IsDevice() then
1079-
AI.PickupHD = Data.HD;
1085+
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
1087+
if MovableMan:ValidMO(deviceToPickupEntry.device) and deviceToPickupEntry.device:IsDevice() then
1088+
AI.PickupHD = deviceToPickupEntry.device;
10801089
break;
10811090
end
10821091
end
10831092

10841093
if AI.PickupHD then
10851094
-- where do we move after pick up?
1086-
local PrevMoveTarget, PrevSeceneWaypoint;
1095+
local prevMoveTarget, prevSceneWaypoint;
10871096
if Owner.MOMoveTarget and MovableMan:ValidMO(Owner.MOMoveTarget) then
1088-
PrevMoveTarget = Owner.MOMoveTarget;
1097+
prevMoveTarget = Owner.MOMoveTarget;
10891098
else
1090-
PrevSeceneWaypoint = SceneMan:MovePointToGround(Owner:GetLastAIWaypoint(), Owner.Height/5, 4); -- last wpt or current pos
1099+
prevSceneWaypoint = SceneMan:MovePointToGround(Owner:GetLastAIWaypoint(), Owner.Height/5, 4); -- last wpt or current pos
10911100
end
10921101

10931102
Owner:ClearMovePath();
10941103
Owner:AddAIMOWaypoint(AI.PickupHD);
10951104

10961105
if Owner.AIMode ~= Actor.AIMODE_GOLDDIG then
1097-
if PrevMoveTarget then
1098-
Owner:AddAIMOWaypoint(PrevMoveTarget);
1099-
elseif PrevSeceneWaypoint then
1100-
Owner:AddAISceneWaypoint(PrevSeceneWaypoint);
1106+
if prevMoveTarget then
1107+
Owner:AddAIMOWaypoint(prevMoveTarget);
1108+
elseif prevSceneWaypoint then
1109+
Owner:AddAISceneWaypoint(prevSceneWaypoint);
11011110
end
11021111

11031112
if Owner.AIMode == Actor.AIMODE_SENTRY then

0 commit comments

Comments
 (0)