File: atk_generic.lua

package info (click to toggle)
naev 0.8.2-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 386,084 kB
  • sloc: ansic: 93,149; xml: 87,292; python: 2,347; sh: 904; makefile: 654; lisp: 162; awk: 4
file content (240 lines) | stat: -rw-r--r-- 6,180 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
--[[
--    Generic attack functions
--]]


--[[
-- Required initialization function
--]]
function atk_generic_init ()
   mem.atk_think  = atk_generic_think
   mem.atk        = atk_generic
end


--[[
-- Mainly manages targeting nearest enemy.
--]]
function atk_generic_think ()
   local enemy  = ai.getenemy()
   local target = ai.target()

   -- Stop attacking if it doesn't exist
   if not target:exists() then
      ai.poptask()
      return
   end

   -- Get new target if it's closer
   if enemy ~= target and enemy ~= nil then
      local dist  = ai.dist( target )
      local range = ai.getweaprange( 3 )

      -- Shouldn't switch targets if close
      if dist > range * mem.atk_changetarget then
         ai.pushtask( "attack", enemy )
      end
   end
end


--[[
-- Attacked function.
--]]
function atk_generic_attacked( attacker )
   local target = ai.target()

   if mem.recharge then
      mem.recharge = false
   end

   -- If no target automatically choose it
   if not target:exists() then
      ai.pushtask("attack", attacker)
      return
   end

   local tdist  = ai.dist(target)
   local dist   = ai.dist(attacker)
   local range  = ai.getweaprange( 0 )

   if target ~= attacker and dist < tdist and
         dist < range * mem.atk_changetarget then
      ai.pushtask("attack", attacker)
   end
end


--[[
-- Generic "brute force" attack.  Doesn't really do anything interesting.
--]]
function atk_generic ()
   local target = _atk_com_think()
   if target == nil then return end

   -- Targeting stuff
   ai.hostile(target) -- Mark as hostile
   ai.settarget(target)

   -- See if the enemy is still seeable
   if not _atk_check_seeable() then return end

   -- Get stats about enemy
   local dist  = ai.dist( target ) -- get distance
   local range = ai.getweaprange( 3 )

   -- We first bias towards range
   if dist > range * mem.atk_approach then
      _atk_g_ranged( target, dist )

   -- Now we do an approach
   elseif dist > range * mem.atk_aim then
      _atk_g_approach( target, dist )

   -- Close enough to melee
   else
      _atk_g_melee( target, dist )
   end
end


--[[
-- Enters ranged combat with the target
--]]
function _atk_g_ranged( target, dist )

   -- Pilot thinks dogfight is the best
   if ai.relhp(target)*ai.reldps(target) >= 0.25 
         or ai.getweapspeed(4) < target:stats().speed_max*1.2 
         or ai.getweaprange(4) < ai.getweaprange(1)*1.5 then

      local dir
      if not mem.careful or dist < 3 * ai.getweaprange(3, 0) * mem.atk_approach then
         dir = ai.face(target) -- Normal face the target
      else
         dir = ai.careful_face(target) -- Careful method
      end

      -- Check if in range to shoot missiles
      if dist < ai.getweaprange( 4 ) and dir < 30 then
         ai.weapset( 4 )
      else
         -- Test if we should zz
         if ai.pilot():stats().mass < 400 and _atk_decide_zz() then
            ai.pushsubtask("_atk_zigzag")
         end
      end

      -- Approach for melee
      if dir < 10 then
         ai.accel()
      end

   else   --Pilot fears his enemy

   --[[ The pilot tries first to place himself at range and at constant velocity.
        When he is stabilized, he starts shooting until he has to correct his trajectory again

        If he doesn't manage to shoot missiles after a few seconds 
        (because the target dodges),
        he gives up and just faces the target and shoot (provided he is in range)
   ]]

      local p = ai.pilot()

      -- Estimate the range
      local radial_vel = ai.relvel(target, true)
      local range = ai.getweaprange( 4 )
      range = math.min ( range - dist * radial_vel / ( ai.getweapspeed( 4 ) - radial_vel ), range )

      local goal = ai.follow_accurate(target, range * 0.8, 0, 10, 20, "keepangle")
      local mod = vec2.mod(goal - p:pos())

      --Must approach or stabilize
      if mod > 3000 then
         -- mustapproach allows a hysteretic behaviour
         mem.mustapproach = true
      end
      if dist > range*0.95 then
         mem.outofrange = true
      end

      if (mem.mustapproach and not ai.timeup(1) ) or mem.outofrange then
         local dir   = ai.face(goal)
         if dir < 10 and mod > 300 then
            ai.accel()
            --mem.stabilized = false
         -- ship must be stabilized since 2 secs
         elseif ai.relvel(target) < 5 and not ai.timeup(1) then--[[if not mem.stabilized then
            mem.stabilized = true
            ai.settimer(0, 2000)
         elseif not ai.timeup(1) and ai.timeup(0) then
            -- If the ship manages to catch its mark, reset the timer]]
            --ai.settimer(1, 10000)
            mem.mustapproach = false
         end
         if dist < range*0.85 then
            mem.outofrange = false
         end

      else -- In range
         local dir  = ai.face(target)
         if dir < 30 then
            ai.set_shoot_indicator(false)
            ai.weapset( 4 )
            -- If he managed to shoot, reinitialize the timer
            if ai.shoot_indicator() and not ai.timeup(1) then
               ai.settimer(1, 13000)
            end
         end
      end

      --The pilot just arrived in the good zone : 
      --From now, if ship doesn't manage to stabilize within a few seconds, shoot anyway
      if dist < 1.5*range and not mem.inzone then
         mem.inzone = true
         ai.settimer(1, mod/p:stats().speed*700 )
      end

   end

   -- Always launch fighters for now
   ai.weapset( 5 )
end


--[[
-- Approaches the target
--]]
function _atk_g_approach( target, dist )
   dir = ai.idir(target)
   if dir < 10 and dir > -10 then
      _atk_keep_distance()
   else
      dir = ai.iface(target)
   end
   if dir < 10 then
      ai.accel()
   end
end


--[[
-- Melees the target
--]]
function _atk_g_melee( target, dist )
   local dir   = ai.aim(target) -- We aim instead of face
   local range = ai.getweaprange( 3 )
   ai.weapset( 3 ) -- Set turret/forward weaponset.

   -- Drifting away we'll want to get closer
   if dir < 10 and dist > 0.5*range and ai.relvel(target) > -10 then
      ai.accel()
   end

   -- Shoot if should be shooting.
   if dir < 10 then
      ai.shoot()
   end
   ai.shoot(true)
end