Author Topic: [MOD] Better Mining AI  (Read 5897 times)

SailorSat

  • Newbie
  • *
  • Status:
    Offline
    Posts:
    7
    • View Profile
on: May 01, 2018, 05:09:45 PM
Hi folks,

I've tweaked the mining AI (based on beta 0.16.7).

features
  • fly towards mineable asteroids
    the stock ai goes into "attack mode" right away and hence crawls slowly across a sector to reach asteroids.
    this mod ai does check the distance and actually fly towards an asteroid before attacking (= way faster travel between asteroids)

  • basic stuck detection
    the stock ai might get "stuck" in between other asteroids.
    this mod ai does check ship movement and will switch to the nearest asteroid (even those without resources) after 15 seconds of near standstill.
    if the ai is stuck for 5 minutes, a warning will be sent to the player.
known issues
  • unstuck won't work if all mining turrets are on "auto fire"; have at least one of them on manual

possible features
  • if there are no rich asteroids left send a message to the player and start mining the other asteroids instead of going idle.

to install
  • go to data\scripts\entity\ai
  • copy mine.lua to mine.old
  • replace mine.lua with the mod one
history
  • 2018-05-01 r1: initial release (fly, stuck detection)
Spoiler: show

Code: [Select]

package.path = package.path .. ";data/scripts/lib/?.lua"

require ("stringutility")

-- Don't remove or alter the following comment, it tells the game the namespace this script lives in. If you remove it, the script will break.
-- namespace AIMine
AIMine = {}

-- start of config options
local lootDistance = 150 * 150   -- 1.5km
local mineDistance = 150 * 150   -- 1.5km
local stuckThreshold = 1.5 * 1.5 -- 15 m/s
-- end of config options


local canMine = nil

local minedLoot = nil
local minedAsteroid = nil

local lootCounter = 0

local stuckLevel = 0
local stuckCounter = 0

function AIMine.getUpdateInterval()
    return 1
end

-- this function will be executed every frame on the server only
function AIMine.updateServer(timeStep)
    local ship = Entity()

    if canMine == nil then
        AIMine.checkIfAbleToMine()
    end

    if ship.hasPilot or ship:getCrewMembers(CrewProfessionType.Captain) == 0 then
        terminate()
        return
    end

    -- find an asteroid that can be harvested
    AIMine.updateMining(timeStep)
end

-- check if there are mining turrets or fighters
function AIMine.checkIfAbleToMine()
    if onServer() then
        local ship = Entity()
        if ship.numTurrets > 0 then
            canMine = true
        else
            local hangar = Hangar()
            local squads = {hangar:getSquads()}

            for _, index in pairs(squads) do
                local category = hangar:getSquadMainWeaponCategory(index)
                if category == WeaponCategory.Mining then
                    canMine = true
                    break
                end
            end
        end

        if not canMine then
            local player = Player(Entity().factionIndex)
            if player then
                player:sendChatMessage("Server", ChatMessageType.Error, "Your ship needs mining turrets or fighters to mine."%_T)
            end
            terminate()
        end
    end
end

-- check the immediate region around the ship for loot that can be collected
-- if there is some, assign minedLoot
function AIMine.findMinedLoot()
    local loots = {Sector():getEntitiesByType(EntityType.Loot)}
    local ship = Entity()

    minedLoot = nil
    for _, loot in pairs(loots) do
        if loot:isCollectable(ship) and distance2(loot.translationf, ship.translationf) < lootDistance then
            minedLoot = loot
            break
        end
    end
end

-- check the sector for an asteroid that can be mined
-- if there is one, assign minedAsteroid
function AIMine.findMinedAsteroid(checkResources)
    local ship = Entity()
    local sector = Sector()

    minedAsteroid = nil

    local asteroids = {sector:getEntitiesByType(EntityType.Asteroid)}
    local nearest = math.huge

    for _, a in pairs(asteroids) do
        local validCandidate = true

        if checkResources then
            local resources = a:getMineableResources()
            validCandidate = resources ~= nil and resources > 0
        end

        if validCandidate then
            local dist = distance2(a.translationf, ship.translationf)
            if dist < nearest then
                nearest = dist
                minedAsteroid = a
            end
        end
    end

    if minedAsteroid then
        broadcastInvokeClientFunction("setMinedAsteroid", minedAsteroid.index)
    else
        local player = Player(Entity().factionIndex)
        if player then
            local x, y = Sector():getCoordinates()
            local coords = tostring(x) .. ":" .. tostring(y)

            player:sendChatMessage(ship.name or "", ChatMessageType.Error, "Your mining ship in sector %s can't find any more asteroids."%_T, coords)
            player:sendChatMessage(ship.name or "", ChatMessageType.Normal, "Sir, we can't find any more asteroids in \\s(%s)!"%_T, coords)
        end

        ShipAI(ship.index):setPassive()
        ship:invokeFunction("craftorders.lua", "setAIAction")
        terminate()
    end
end

-- check if we are stuck
-- if stuck, try to get free by mining nearest asteroid
function AIMine.checkIfStuck(timeStep)
    local ship = Entity()

    local v = Velocity(ship.index)
    if valid(v) then
        local s = length2(v.velocityf)
        if s < stuckThreshold then
            stuckLevel = stuckLevel + 1

            if stuckLevel == 15 then
                -- no movement for 15 seconds
                stuckLevel = 0
                stuckCounter = stuckCounter + 15

                -- switch target to nearest asteroid
                AIMine.findMinedAsteroid(false)
            end

            if stuckCounter == 300 then
                -- stuck for 300 seconds
                stuckCounter = 0

                local player = Player(Entity().factionIndex)
                if player then
                    local x, y = Sector():getCoordinates()
                    local coords = tostring(x) .. ":" .. tostring(y)

                    player:sendChatMessage(ship.name or "", ChatMessageType.Information, "Your mining ship in sector %s may be stuck."%_T, coords)
                    player:sendChatMessage(ship.name or "", ChatMessageType.Normal, "Sir, we may be stuck in \\s(%s)!"%_T, coords)
                end
            end
        else
            -- movement
            stuckLevel = 0
            stuckCounter = 0
        end
    end
end

function AIMine.updateMining(timeStep)
    -- highest priority is collecting the resources
    if not valid(minedAsteroid) and not valid(minedLoot) then
        -- first, check if there is loot to collect
        AIMine.findMinedLoot()

        -- then, if there's no loot, check if there is an asteroid to mine
        if not valid(minedLoot) then
            AIMine.findMinedAsteroid(true)
        end
    end

    local ship = Entity()
    local ai = ShipAI()

    if valid(minedLoot) then

        -- there is loot to collect, fly there
        lootCounter = lootCounter + timeStep
        if lootCounter > 3 then
            lootCounter = lootCounter - 3
            ai:setFly(minedLoot.translationf, 0)
        end

    elseif valid(minedAsteroid) then

        -- check if stuck
        AIMine.checkIfStuck()

        -- check distance to asteroid
        local d2 = distance2(ship.translationf, minedAsteroid.translationf)
        if d2 <= mineDistance then
            -- we are close, attack it
            if ship.selectedObject == nil
                or ship.selectedObject.index ~= minedAsteroid.index
                or ai.state ~= AIState.Attack then

                ai:setAttack(minedAsteroid)
            end
        else
            -- too far away, approach it
            ai:setFly(minedAsteroid.translationf, 0)
        end

    end
end

function AIMine.setMinedAsteroid(index)
    minedAsteroid = Entity(index)
end

---- this function will be executed every frame on the client only
--function updateClient(timeStep)
--
--    if valid(minedAsteroid) then
--        drawDebugSphere(minedAsteroid:getBoundingSphere(), ColorRGB(1, 0, 0))
--    end
--end

« Last Edit: May 02, 2018, 08:25:58 PM by SailorSat »



Devious

  • Hero Member
  • *****
  • Status:
    Offline
    Posts:
    579
  • Have you seen Rusty?
    • View Profile
    • Rusty's Universe and Community
on: May 02, 2018, 12:33:33 AM
Thank you for the mod, will give it a try!

What do you think the performance is like on a busy server compared to the regular script?

Having the script mine for the normal asteroids after the resource ones are gone is a good idea,
previously we used a copy of the script for a mine all feature where the lines that determines if an asteroid has resources in them were commented out, but with the recent updates to the script that doesn't seem to be working anymore, altough I have not fully tested it yet.



SailorSat

  • Newbie
  • *
  • Status:
    Offline
    Posts:
    7
    • View Profile
on: May 02, 2018, 12:42:02 AM
Hm... Well the script sure does some additional checks (distance to asteroid, velocity of the ship). I don't know how "fly mode" compares to "attack mode" performance wise.

I guess over all the performance should be similar - but I didn't profile that script yet.



SailorSat

  • Newbie
  • *
  • Status:
    Offline
    Posts:
    7
    • View Profile
on: May 02, 2018, 08:31:56 PM
I reuploaded r1 with a small bugfix (the old one would send you 15 messages if a ship is stuck)



Devious

  • Hero Member
  • *****
  • Status:
    Offline
    Posts:
    579
  • Have you seen Rusty?
    • View Profile
    • Rusty's Universe and Community
on: May 03, 2018, 12:18:11 AM
I haven't been able to test your method yet, but one thing that could improve the AI is for the mining ship to stop moving when it's in range and has a clear sight to the asteroid. Often the ship will  drift out of range/orbit and has to reorientate itself which stops the mining lasers.



SailorSat

  • Newbie
  • *
  • Status:
    Offline
    Posts:
    7
    • View Profile
on: May 03, 2018, 01:31:59 AM
Hm... As long as the AI switches to "attack mode" I doubt we can change that.
Same goes for salvaging I guess.

Another thing I've been thinking about are alliance ships - they currently don't report if they are done - I've seen that in the 0.17.1 beta one could send messages to a faction - on the other hand we might use the alliance chat for that.



Perq

  • Jr. Member
  • **
  • Status:
    Offline
    Posts:
    52
    • View Profile
on: May 08, 2018, 01:07:00 PM
I really wish fighters were standing still when mining instead of doing aggressive turns. What are they dodging? :(
Same goes for salvage.

Can that be added to the script, or not really?



SailorSat

  • Newbie
  • *
  • Status:
    Offline
    Posts:
    7
    • View Profile
on: May 08, 2018, 07:10:16 PM
I don't think so - whatever the ships do in "attack mode" is up the the core game.



Red Hawk

  • Newbie
  • *
  • Status:
    Offline
    Posts:
    1
    • View Profile
on: July 05, 2018, 09:09:00 PM
Is this Mod useable on a Server?



Kampfkrapfn

  • Jr. Member
  • **
  • Status:
    Offline
    Posts:
    68
    • View Profile
on: July 08, 2018, 02:27:30 AM
yes



kirknay

  • Newbie
  • *
  • Status:
    Offline
    Posts:
    5
    • View Profile
on: December 24, 2018, 05:56:18 AM
Even in current version (1.19.1) it works great! applying to private server now!



Th3MadMan0

  • Newbie
  • *
  • Status:
    Offline
    Posts:
    3
    • View Profile
on: April 02, 2019, 11:59:01 PM
I installed the package and did what the instructions said and it doesn't seem to work, does this mod work with the current version of Avorion (0.20.5) or have I just done something wrong?
Note: I created backups of all of my galaxies and did not test the mod on a new galaxy as of the time of writing this comment.



Th3MadMan0

  • Newbie
  • *
  • Status:
    Offline
    Posts:
    3
    • View Profile
on: April 03, 2019, 02:28:18 AM
Alas, my friends, it seems the problem has solved itself! Jolly good I say!



Thedreadpirateroberts

  • Newbie
  • *
  • Status:
    Offline
    Posts:
    3
    • View Profile
on: April 16, 2019, 08:44:52 PM
I love the mod! The slow-to-go mining AI was pitiful at best.

That being said, using this mod now VERY MUCH causes hilarity: the ship will blast its' way to the nearest asteroid, and like some sort of deranged maniac, crashes right into it.

HELP!

<3 Dread



SailorSat

  • Newbie
  • *
  • Status:
    Offline
    Posts:
    7
    • View Profile
on: April 16, 2019, 09:03:48 PM
I'll wait for 0.21 before trying to update anything :)