| 
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -8,37 +8,221 @@ local _ENV = mkmodule('plugins.siege-engine')
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * clearTargetArea(building)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * setTargetArea(building, point1, point2) -> true/false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				--]]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * isLinkedToPile(building,pile) -> true/false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * getStockpileLinks(building) -> {pile}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * addStockpileLink(building,pile) -> true/false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * removeStockpileLink(building,pile) -> true/false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * saveWorkshopProfile(building) -> profile
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * getAmmoItem(building) -> item_type
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * setAmmoItem(building,item_type) -> true/false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * isPassableTile(pos) -> true/false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * isTreeTile(pos) -> true/false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * isTargetableTile(pos) -> true/false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * getTileStatus(building,pos) -> 'invalid/ok/out_of_range/blocked/semiblocked'
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * paintAimScreen(building,view_pos_xyz,left_top_xy,size_xy)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * canTargetUnit(unit) -> true/false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 proj_info = { target = pos, [delta = float/pos], [factor = int] }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * projPosAtStep(building,proj_info,step) -> pos
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * projPathMetrics(building,proj_info) -> {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				       hit_type = 'wall/floor/ceiling/map_edge/tree',
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				       collision_step = int,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				       collision_z_step = int,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				       goal_distance = int,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				       goal_step = int/nil,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				       goal_z_step = int/nil,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				       status = 'ok/out_of_range/blocked'
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				   }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * adjustToTarget(building,pos) -> pos,ok=true/false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * traceUnitPath(unit) -> { {x=int,y=int,z=int[,from=time][,to=time]} }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * unitPosAtTime(unit, time) -> pos
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * proposeUnitHits(building) -> { {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				       pos=pos, unit=unit, time=float, dist=int,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				       [lmargin=float,] [rmargin=float,]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				   } }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * computeNearbyWeight(building,hits,{[id/unit]=score}[,fname])
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				]]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				Z_STEP_COUNT = 15
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				Z_STEP = 1/31
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				function getMetrics(engine, path)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    path.metrics = path.metrics or projPathMetrics(engine, path)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return path.metrics
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				function findShotHeight(engine, target)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    local path = { target = target, delta = 0.0 }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if projPathMetrics(engine, path).goal_step then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if getMetrics(engine, path).goal_step then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return path
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for i = 1,Z_STEP_COUNT do
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        path.delta = i*Z_STEP
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if projPathMetrics(engine, path).goal_step then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    local tpath = { target = target, delta = Z_STEP_COUNT*Z_STEP }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if getMetrics(engine, tpath).goal_step then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        for i = 1,Z_STEP_COUNT-1 do
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            path = { target = target, delta = i*Z_STEP }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            if getMetrics(engine, path).goal_step then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                return path
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        path.delta = -i*Z_STEP
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if projPathMetrics(engine, path).goal_step then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return tpath
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    tpath = { target = target, delta = -Z_STEP_COUNT*Z_STEP }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if getMetrics(engine, tpath).goal_step then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        for i = 1,Z_STEP_COUNT-1 do
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            path = { target = target, delta = -i*Z_STEP }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            if getMetrics(engine, path).goal_step then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                return path
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return tpath
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				function findReachableTargets(engine, targets)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    local reachable = {}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for _,tgt in ipairs(targets) do
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        tgt.path = findShotHeight(engine, tgt.pos)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if tgt.path then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            table.insert(reachable, tgt)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return reachable
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				recent_targets = recent_targets or {}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				if dfhack.is_core_context then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    dfhack.onStateChange[_ENV] = function(code)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if code == SC_MAP_LOADED then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            recent_targets = {}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				function saveRecent(unit)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    local id = unit.id
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    local tgt = recent_targets
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    tgt[id] = (tgt[id] or 0) + 1
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    dfhack.timeout(3, 'days', function()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        tgt[id] = math.max(0, tgt[id]-1)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    end)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				function getBaseUnitWeight(unit)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if dfhack.units.isCitizen(unit) then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return -10
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    elseif unit.flags1.diplomat or unit.flags1.merchant then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return -2
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    elseif unit.flags1.tame and unit.civ_id == df.global.ui.civ_id then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return -1
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    else
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        local rv = 1
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if unit.flags1.marauder then rv = rv + 0.5 end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if unit.flags1.active_invader then rv = rv + 1 end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if unit.flags1.invader_origin then rv = rv + 1 end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if unit.flags1.invades then rv = rv + 1 end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if unit.flags1.hidden_ambusher then rv = rv + 1 end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return rv
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				function getUnitWeight(unit)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    local base = getBaseUnitWeight(unit)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return base * math.pow(0.7, recent_targets[unit.id] or 0)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				function unitWeightCache()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    local cache = {}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return cache, function(unit)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        local id = unit.id
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        cache[id] = cache[id] or getUnitWeight(unit)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return cache[id]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				function scoreTargets(engine, reachable)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    local ucache, get_weight = unitWeightCache()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for _,tgt in ipairs(reachable) do
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        tgt.score = get_weight(tgt.unit)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if tgt.lmargin and tgt.lmargin < 3 then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            tgt.score = tgt.score * tgt.lmargin / 3
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if tgt.rmargin and tgt.rmargin < 3 then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            tgt.score = tgt.score * tgt.rmargin / 3
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    computeNearbyWeight(engine, reachable, ucache)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for _,tgt in ipairs(reachable) do
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        tgt.score = (tgt.score + tgt.nearby_weight*0.7) * math.pow(0.995, tgt.time/3)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    table.sort(reachable, function(a,b)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return a.score > b.score or (a.score == b.score and a.time < b.time)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    end)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				function pickUniqueTargets(reachable)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    local unique = {}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if #reachable > 0 then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        local pos_table = {}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        local first_score = reachable[1].score
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        for i,tgt in ipairs(reachable) do
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            if tgt.score < 0 or tgt.score < 0.1*first_score then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                break
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            local x,y,z = pos2xyz(tgt.pos)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            local key = x..':'..y..':'..z
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            if pos_table[key] then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                table.insert(pos_table[key].units, tgt.unit)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            else
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                table.insert(unique, tgt)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                pos_table[key] = tgt
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                tgt.units = { tgt.unit }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return unique
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				function doAimProjectile(engine, item, target_min, target_max, skill)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    print(item, df.skill_rating[skill])
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    local targets = proposeUnitHits(engine)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if #targets > 0 then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        local rnd = math.random(#targets)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return findShotHeight(engine, targets[rnd].pos)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    local reachable = findReachableTargets(engine, targets)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    scoreTargets(engine, reachable)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    local unique = pickUniqueTargets(reachable)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if #unique > 0 then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        local cnt = math.max(math.min(#unique,5), math.min(10, math.floor(#unique/2)))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        local rnd = math.random(cnt)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        for _,u in ipairs(unique[rnd].units) do
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            saveRecent(u)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return unique[rnd].path
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				end
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
					 | 
				
			
			 | 
			 | 
			
				
 
 |