@@ -9,18 +9,14 @@ function HumanBehaviors.GetTeamShootingSkill(team)
9
9
end
10
10
11
11
local aimSpeed , aimSkill
12
- if skill < Activity .AVERAGESKILL then -- the AI shoot later and tracks the target slower
13
- aimSpeed = - 0.025 * skill + 3.3 -- affects the delay before the shooting starts [3.30 .. 1.55]
14
- aimSkill = - 0.011 * skill + 2.2 -- affects the precision of the shots [2.20 .. 1.43]
15
- elseif skill >= Activity .UNFAIRSKILL then
16
- aimSpeed = 0.05
17
- aimSkill = 0.05
12
+ if skill >= Activity .UNFAIRSKILL then
13
+ aimSpeed = 0.04
14
+ aimSkill = 0.04
18
15
else
19
16
-- the AI shoot sooner and with slightly better precision
20
- aimSpeed = 1 / (0.55 / (2.9 - math.exp (skill * 0.01 ))) -- [1.42 .. 0.38]
21
- aimSkill = 1 / (0.65 / (3.0 - math.exp (skill * 0.01 ))) -- [1.36 .. 0.48]
17
+ aimSpeed = 1 / (0.65 / (2.9 - math.exp (skill * 0.01 )))
18
+ aimSkill = 1 / (0.75 / (3.0 - math.exp (skill * 0.01 )))
22
19
end
23
-
24
20
return aimSpeed , aimSkill , skill
25
21
end
26
22
36
32
37
33
-- spot targets by casting a ray in a random direction
38
34
function HumanBehaviors .LookForTargets (AI , Owner , Skill )
39
- local viewAngDeg = RangeRand (50 , 120 ) * Owner .Perceptiveness * (0.5 + Skill / 200 )
35
+ local viewAngDeg = RangeRand (50 , 120 ) * Owner .Perceptiveness * (0.5 + Skill / 200 )
40
36
if AI .deviceState == AHuman .AIMING then
41
37
AI .Ctrl :SetState (Controller .AIM_SHARP , true ) -- reinforce sharp aim controller state to enable SharpLength in LookForMOs
42
- viewAngDeg = 15 * Owner .Perceptiveness + (Skill / 10 )
38
+ viewAngDeg = 15 * Owner .Perceptiveness + (Skill / 10 )
43
39
end
44
40
45
41
local FoundMO = Owner :LookForMOs (viewAngDeg , rte .grassID , false )
@@ -84,6 +80,8 @@ function HumanBehaviors.CheckEnemyLOS(AI, Owner, Skill)
84
80
85
81
local LookTarget
86
82
if Enemy .ClassName == " ADoor" then
83
+ -- TO-DO: use explosive weapons on doors?
84
+
87
85
local Door = ToADoor (Enemy ).Door
88
86
if Door and Door :IsAttached () then
89
87
LookTarget = Door .Pos
@@ -489,7 +487,6 @@ function HumanBehaviors.Sentry(AI, Owner, Abort)
489
487
aimTime = RangeRand (6000 , 12000 ) * angDiff
490
488
AI .deviceState = AHuman .AIMING
491
489
end
492
-
493
490
if Owner .AIMode ~= Actor .AIMODE_SQUAD then
494
491
if SceneMan :ShortestDistance (Owner .Pos , AI .SentryPos , false ).Magnitude > Owner .Height * 0.7 then
495
492
AI .SentryPos = SceneMan :MovePointToGround (AI .SentryPos , Owner .Height * 0.25 , 3 )
@@ -500,8 +497,7 @@ function HumanBehaviors.Sentry(AI, Owner, Abort)
500
497
elseif AI .SentryFacing and Owner .HFlipped ~= AI .SentryFacing then
501
498
Owner .HFlipped = AI .SentryFacing -- turn to the direction we have been order to guard
502
499
break -- restart this behavior
503
- elseif math.random () < Owner .Perceptiveness then
504
-
500
+ elseif AI .TargetLostTimer :IsPastSimTimeLimit () and math.random () < Owner .Perceptiveness then
505
501
-- turn around occasionally if there is open space behind our back
506
502
local backAreaRay = Vector (- math.random (FrameMan .PlayerScreenWidth / 4 , FrameMan .PlayerScreenWidth / 2 ) * Owner .FlipFactor , 0 ):DegRotate (math.random (- 25 , 25 ) * Owner .Perceptiveness )
507
503
if not SceneMan :CastStrengthRay (Owner .EyePos , backAreaRay , 10 , Vector (), 10 , rte .grassID , SceneMan .SceneWrapsX ) then
@@ -1682,13 +1678,12 @@ function HumanBehaviors.GoToWpt(AI, Owner, Abort)
1682
1678
else
1683
1679
nextLatMove = Actor .LAT_STILL
1684
1680
end
1685
- if not (Owner .FGLeg and Owner .BGLeg ) and not AI .jump then
1686
- Owner :GetController ():SetState (Controller .BODY_CROUCH ,true ) -- crawl if no legs
1687
- end
1688
1681
end
1689
1682
elseif ((CurrDist .X < - 5 and Owner .HFlipped ) or (CurrDist .X > 5 and not Owner .HFlipped )) and math.abs (Owner .Vel .X ) < 1 then
1690
1683
-- no legs, jump forward
1691
1684
AI .jump = true
1685
+ elseif not AI .jump then
1686
+ AI .proneState = AHuman .GOPRONE
1692
1687
end
1693
1688
1694
1689
if Waypoint .Type == " right" then
@@ -1821,7 +1816,7 @@ function HumanBehaviors.GoToWpt(AI, Owner, Abort)
1821
1816
local delta = SceneMan :ShortestDistance (Waypoint .Pos , FallPos , false ).Magnitude - Facings [1 ].range
1822
1817
if delta < 1 then
1823
1818
AI .jump = false
1824
- elseif AI .flying or delta > 15 then
1819
+ elseif AI .flying or delta > 25 then
1825
1820
AI .jump = true
1826
1821
nextAimAngle = Owner :GetAimAngle (false ) * 0.5 + Facings [1 ].aim * 0.5 -- adjust jetpack nozzle direction
1827
1822
nextLatMove = Actor .LAT_STILL
@@ -2146,32 +2141,32 @@ function HumanBehaviors.GetProjectileData(Owner)
2146
2141
2147
2142
-- find muzzle velocity
2148
2143
PrjDat .vel = Weapon :GetAIFireVel ()
2149
-
2150
2144
-- half of the theoretical upper limit for the total amount of material strength this weapon can destroy in 250ms
2151
- PrjDat .pen = Weapon :GetAIPenetration () * math.max ((Weapon .RateOfFire / 240 ), 1 )
2152
2145
2153
2146
PrjDat .g = SceneMan .GlobalAcc .Y * 0.67 * Weapon :GetBulletAccScalar () -- underestimate gravity
2154
2147
PrjDat .vsq = PrjDat .vel ^ 2 -- muzzle velocity squared
2155
2148
PrjDat .vqu = PrjDat .vsq ^ 2 -- muzzle velocity quad
2156
2149
PrjDat .drg = 1 - Projectile .AirResistance * TimerMan .DeltaTimeSecs -- AirResistance is stored as the ini-value times 60
2157
2150
PrjDat .thr = math.min (Projectile .AirThreshold , PrjDat .vel )
2151
+ PrjDat .pen = (Projectile .Mass * Projectile .Sharpness * PrjDat .vel ) * PrjDat .drg
2158
2152
2159
2153
-- estimate theoretical max range with ...
2160
2154
local lifeTime = Weapon :GetAIBulletLifeTime ()
2161
2155
if lifeTime < 1 then -- infinite life time
2162
2156
PrjDat .rng = math.huge
2163
2157
elseif PrjDat .drg < 1 then -- AirResistance
2164
2158
PrjDat .rng = 0
2165
- local threshold = PrjDat .thr * GetPPM () * TimerMan .DeltaTimeSecs -- AirThreshold in pixels/frame
2166
- local vel = PrjDat .vel * GetPPM () * TimerMan .DeltaTimeSecs -- muzzle velocity in pixels/frame
2159
+ local threshold = PrjDat .thr * rte .PxTravelledPerFrame -- AirThreshold in pixels/frame
2160
+ local vel = PrjDat .vel * rte .PxTravelledPerFrame -- muzzle velocity in pixels/frame
2161
+
2167
2162
for _ = 0 , math.ceil (lifeTime / TimerMan .DeltaTimeMS ) do
2168
2163
PrjDat .rng = PrjDat .rng + vel
2169
2164
if vel > threshold then
2170
2165
vel = vel * PrjDat .drg
2171
2166
end
2172
2167
end
2173
2168
else -- no AirResistance
2174
- PrjDat .rng = PrjDat .vel * GetPPM () * TimerMan . DeltaTimeSecs * (lifeTime / TimerMan .DeltaTimeMS )
2169
+ PrjDat .rng = PrjDat .vel * rte . PxTravelledPerFrame * (lifeTime / TimerMan .DeltaTimeMS )
2175
2170
end
2176
2171
2177
2172
-- Artificially decrease reported range to make sure AI
@@ -2629,13 +2624,13 @@ end
2629
2624
-- throw a grenade at the selected target
2630
2625
function HumanBehaviors .ThrowTarget (AI , Owner , Abort )
2631
2626
local ThrowTimer = Timer ()
2632
- local aimTime = 1000
2627
+ local aimTime = Owner . ThrowPrepTime
2633
2628
local scan = 0
2634
2629
local miss = 0 -- stop scanning after a few missed attempts
2635
2630
local AimPoint , Dist , MO , ID , rootID , LOS , aim
2636
-
2631
+
2637
2632
AI .TargetLostTimer :SetSimTimeLimitMS (1500 )
2638
-
2633
+
2639
2634
while true do
2640
2635
if not MovableMan :ValidMO (AI .Target ) then
2641
2636
break
@@ -2649,8 +2644,11 @@ function HumanBehaviors.ThrowTarget(AI, Owner, Abort)
2649
2644
else
2650
2645
AI .fire = false
2651
2646
end
2652
- else
2653
- break -- no grenades left
2647
+ else -- no grenades left, continue attack
2648
+ if not (Owner .AIMode == Actor .AIMODE_SENTRY or Owner .AIMode == Actor .AIMODE_SQUAD ) then
2649
+ AI :CreateAttackBehavior (Owner )
2650
+ end
2651
+ break
2654
2652
end
2655
2653
else
2656
2654
if scan < 1 then
@@ -2715,7 +2713,7 @@ function HumanBehaviors.ThrowTarget(AI, Owner, Abort)
2715
2713
end
2716
2714
elseif MO .ClassName == " ACrab" then
2717
2715
AI .Target = ToACrab (MO )
2718
- local Legs = AI .Target .LFGLeg or AI .Target .LFGLeg or AI .Target .LBGLeg or AI .Target .RFGLeg -- the legs
2716
+ local Legs = AI .Target .LeftFGLeg or AI .Target .RightFGLeg or AI .Target .LeftBGLeg or AI .Target .RightFGLeg -- the legs
2719
2717
if Legs then
2720
2718
AimPoint = Legs .Pos
2721
2719
end
@@ -2747,13 +2745,13 @@ function HumanBehaviors.ThrowTarget(AI, Owner, Abort)
2747
2745
aim = HumanBehaviors .GetGrenadeAngle (AimPoint , Vector (), Grenade .MuzzlePos , Grenade .MaxThrowVel )
2748
2746
if aim then
2749
2747
ThrowTimer :Reset ()
2750
- aimTime = RangeRand (1000 , 1200 )
2748
+ aimTime = Owner . ThrowPrepTime * RangeRand (0.9 , 1.1 )
2751
2749
local maxAim = aim
2752
2750
2753
2751
-- try again with an average throw vel
2754
2752
aim = HumanBehaviors .GetGrenadeAngle (AimPoint , Vector (), Grenade .MuzzlePos , (Grenade .MaxThrowVel + Grenade .MinThrowVel )/ 2 )
2755
2753
if aim then
2756
- aimTime = RangeRand (450 , 550 )
2754
+ aimTime = Owner . ThrowPrepTime * RangeRand (0.45 , 0.55 )
2757
2755
else
2758
2756
aim = maxAim
2759
2757
end
@@ -2829,24 +2827,22 @@ function HumanBehaviors.AttackTarget(AI, Owner, Abort)
2829
2827
local meleeDist = 0
2830
2828
local startPos = Vector (Owner .EyePos .X , Owner .EyePos .Y )
2831
2829
2832
- if Owner .EquippedItem then
2833
- if Owner .EquippedItem :HasObjectInGroup (" Tools - Diggers" ) or Owner .EquippedItem :HasObjectInGroup (" Weapons - Melee" ) then
2834
- meleeDist = Owner .Radius + 25
2835
- startPos = Vector (Owner .EquippedItem .Pos .X , Owner .EquippedItem .Pos .Y )
2836
- end
2830
+ if Owner :EquipDeviceInGroup (" Tools - Diggers" , true ) or Owner :EquipDeviceInGroup (" Weapons - Melee" , true ) then
2831
+ meleeDist = Owner .Radius + 25
2832
+ startPos = Vector (Owner .EquippedItem .Pos .X , Owner .EquippedItem .Pos .Y )
2837
2833
elseif Owner .armSway then
2838
- if Owner .FGArm then
2839
- meleeDist = Owner .Radius + Owner .FGArm .Radius
2840
- startPos = Owner .FGArm .Pos
2841
- elseif Owner .BGArm then
2842
- meleeDist = Owner .Radius + Owner .BGArm .Radius
2843
- startPos = Owner .BGArm .Pos
2834
+ local arm = Owner .FGArm or Owner .BGArm
2835
+ if arm then
2836
+ meleeDist = arm .Radius + arm .Radius
2837
+ startPos = arm .Pos
2844
2838
end
2845
2839
end
2846
2840
if meleeDist > 0 then
2847
- local dist = SceneMan :ShortestDistance (startPos , AI .Target .Pos , false )
2841
+ local attackPos = (AI .Target .ClassName == " ADoor" and ToADoor (AI .Target ).Door and ToADoor (AI .Target ).Door :IsAttached ()) and ToADoor (AI .Target ).Door .Pos or AI .Target .Pos
2842
+ local dist = SceneMan :ShortestDistance (startPos , attackPos , false )
2848
2843
if dist .Magnitude < meleeDist then
2849
- AI .Ctrl .AnalogAim = SceneMan :ShortestDistance (Owner .EyePos , AI .Target .Pos , false ).Normalized
2844
+ AI .lateralMoveState = Actor .LAT_STILL
2845
+ AI .Ctrl .AnalogAim = SceneMan :ShortestDistance (Owner .EyePos , attackPos , false ).Normalized
2850
2846
AI .fire = true
2851
2847
else
2852
2848
AI .fire = false
0 commit comments