Smarter monster lua

Discuss and unveil current Marathon projects.

Smarter monster lua

Post Jan 27th '19, 16:21

Inspired by the current M2 commentary, I decided to look into whether I could make monsters act any "smarter" in certain circumstances using only Lua. This won't erase some of their stupid behaviors that people enjoy exploiting, but it will sometimes enable them to take corrective action.

So far, this script does one of two things:

If a monster at less than 25% health hits a friendly monster with its attack, the script will search for roughly the nearest live enemy and have the monster attack it.

Otherwise, if any monster's attack hits either a friendly monster or itself, the script will politely ask the monster to move in a preferably forward direction.

With this script, I've seen snipers jump down if they're hitting their own soldiers, berserk cyborgs respond to friendly fire but once before turning back to the player, BoBs get off their asses to move up and take the place of a fallen comrade they've mistakenly killed, and troopers reposition themselves when grenading their own feet. None of this happens reliably, but having it happen occasionally gives the monsters a little extra bit of spice and challenge!

I'd certainly welcome any input as to other things monsters might possibly be scripted to do, or point out any potential problems with this code before I release it as a plugin. Sadly, further ideas I've had rely on being able to find a monsters target, which as far as I can tell is only possible if they naturally fire a guided projectile.

Wrkncacnter gave me the two angle functions at the end on discord, he was not sure as to where he found them.
Also, at are two important points there are a trio of commented-out lines that if un-commented will print debug output to the screen. The script:

Code: Select all
Triggers = {}

function Triggers.monster_damaged(monster, aggressor_monster, damage_type, damage_amount, projectile)
  if not monster._zerkvit then
    monster._zerkvit = ( monster.vitality + damage_amount ) / 4
  end
  if aggressor_monster and not aggressor_monster.player and aggressor_monster.valid then
    if not aggressor_monster._zerkvit then
      aggressor_monster._zerkvit = ( aggressor_monster.vitality + damage_amount ) / 4
    end
    if aggressor_monster._zerkvit >= aggressor_monster.vitality and aggressor_monster ~= monster and aggressor_monster.type.friends[monster.type.class] then
      local delta = 524288
-- find nearest enemy target to attack
      for m in Monsters() do
        if m.valid and m.visible then
          if aggressor_monster.type.enemies[m.type.class] and m.life > 0 then
            local maybe = math.abs(math.abs(aggressor_monster.facing - angle_between_points(m,aggressor_monster))-180) + math.abs(m.x - aggressor_monster.x) + math.abs(m.y - aggressor_monster.y) + math.abs(m.z - aggressor_monster.z) * 7
            if maybe < delta then
              badzerktarget = m
              delta = maybe
            end
          end
        end
      end
    end
    if badzerktarget then
      aggressor_monster:attack(badzerktarget)
--      local bz = "berserking "
--      local on = " on "
--      Players.print(bz..tostring(aggressor_monster)..on..tostring(badzerktarget))
      badzerktarget = nil
    else
      if aggressor_monster == monster or aggressor_monster.type.friends[monster.type.class] then
        local dest = Players[0].monster.polygon
        local delta = 512
-- find anything to run towards, preferably but not necessarily forewards
        for m in Monsters() do
          local maybe = math.abs(math.abs(aggressor_monster.facing - angle_between_points(m,aggressor_monster))-180)
          if maybe < delta then
            dest = m.polygon
            delta = maybe
          end
        end
        aggressor_monster:move_by_path(dest)
--   local mv = "moving "
--   local to = " to "
--   Players.print(mv..tostring(aggressor_monster)..to..tostring(dest))
      end
    end
  end
end

function angleOfPoint(pt)
    local x, y = pt.x, pt.y
    local radian = math.atan2(y, x)
    local angle = radian * 180 / math.pi
    if angle < 0 then
        angle = 360 + angle
    end
    return angle
end

-- returns the degrees between two points (note: 0 degrees is 'east')
function angle_between_points(a, b)
    local x, y = b.x - a.x, b.y - a.y
    return angleOfPoint({x = x, y = y})
end


I have very, very occasionally crashed A1 with a "monster is unused" error when running this script, for example:
Code: Select all
vhalt: monsters.cpp:321: monster index #39 (0x22f9770) is unused (csalerts_sdl.cpp:273)
FATAL: monsters.cpp:321: monster index #39 (0x22f9770) is unused
Aborted
User avatar

ravenshining
Hawai'i

Post Jan 27th '19, 18:05

This is a super cool idea :0 I'll have to give it a try when I have a second.
User avatar

$lave

Post Jan 27th '19, 19:50

ravenshining wrote:Wrkncacnter gave me the two angle functions at the end on discord, he was not sure as to where he found them.

Actually, I know where I found them, the link is just dead now. This comment is what I have in my PiD code:

Code: Select all
---- The following functions taken from https://gist.github.com/ignisdesign/4590736


Edit: Looks like this is the new version https://gist.github.com/kirubz/fa843750 ... 8e0ae3aed8.

Looks like he added a new function that would help you, since your logic to find angle deltas won't work on the 360/0 boundary.
User avatar

Wrkncacnter

Post Jan 28th '19, 00:30

Wrkncacnter wrote:Edit: Looks like this is the new version https://gist.github.com/kirubz/fa843750 ... 8e0ae3aed8.

Looks like he added a new function that would help you, since your logic to find angle deltas won't work on the 360/0 boundary.


Thanks! I'll have to reach out to them.

I rewrote the angle delta line since our conversation because of that reason like so - note there are now two absolute value functions:

local maybe = math.abs(math.abs(aggressor_monster.facing - angle_between_points(m,aggressor_monster))-180)

The arithmetic worked in a spreadsheet, at any rate.

Another behaviour for another weekend would be to make monsters run from exploding monsters. I think I can manage that.
User avatar

ravenshining
Hawai'i


Return to Projects



Who is online

Users browsing this forum: No registered users

cron