@@ -915,94 +915,100 @@ end
915915-- find a weapon to pick up
916916function 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