Jump to content

Welcome to the Forum!

The best place to exchange builds and ideas! Vote for the best ideas and suggestions here.

Join the Avorion Discord!

Connect with other passionate players and talk about the latest news.
Discord

DLC Avorion Into the Rift Out Now!

Now available on Steam!
Steam

[MOD] Better Mining AI


SailorSat
 Share

Recommended Posts

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)

 

 


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

 

 

mine-r1.zip

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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?

Link to comment
Share on other sites

  • 1 month later...
  • 5 months later...
  • 3 months later...

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.

Link to comment
Share on other sites

  • 2 weeks later...
  • 7 months later...

Love this mod too, but i was wondering if it will be able to make it as a workshop mod or something in the mod directory instead of overwriting files ...

Looks like the auto-appending feature will be a lot of troubles for most mods around the forum.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...