[Discussion] AI Behavior

The place to discuss scripting and game modifications for X Rebirth.

Moderators: Scripting / Modding Moderators, Moderators for English X Forum

Kierk
Posts: 115
Joined: Wed, 30. Oct 13, 16:38

[Discussion] AI Behavior

Post by Kierk »

So I've spent today looking through the behavior of different ships and I want to share what I've learned. Hopefully this will help folks.

Firstly we'll look at NPCs. Most NPC behavior is handled by mission director, with NPC_Staff covering the crew type NPCs. Within NPC_Staff scripts are assigned to characters when you give them the 'work here' command.

Player assigned NPC default AI

Engineers get assigned Engineer.ai

Defense Officers get fight.station.player regardless of where they work

Captains get assigned player.default.

fight.station.player only operates with subordinates which as far as I know only a station can have.

player.default waits for player orders and if in squad follows player from zone to zone, otherwise does nothing.

How NPCs respond to events.

NPC response is handled by an interrupt system. An interrupt is controlled by a handler. A handler is comprised of a set of conditions plus a set of actions. A very simple interrupt is like this

Code: Select all

<handler>
          <conditions>
            <event_object_attacked object="player.primaryship"/>
          </conditions>
          <actions>
            <show_help force="true" custom="'Player attacked'" />
          </actions>
        </handler>
Whenever you get hit, anything running that script will cause the Player attacked message to show on the screen.

Global and Local Interrupts

The preceeding interrupt only works if whatever script its put in is running at the time. Should the entity switch to a different script the behavior is lost. Sometimes you want the same behavior to persist over a wide range of scripts, like responding to an attack. This is what the interrupt.attack script is meant to handle.

The Global Interrupt structure is like this

Code: Select all

<interrupt>
    <library>
        <handler name="idostuff">
             <conditions>
                   stuff
              </conditions>
             <actions>
                 more stuff
              </actions>
           </conditions>
        </handler>
    </library>
</interrupt>
the library holds the names of the different handlers and they can be invoked in other scripts with the simple line

Code: Select all

<handler ref="idostuff" />
Combat Problems!

player.default does not include the handler for being attacked, so doesn't respond. It looks like ships subordinate to a defense manager on board a station would start acting like real ships.

I added the basic attack handler reference to player.default and my ships did indeed start responding to attacks, but the error log uidata gives warning messages about too many interrupt handlers defined in the script.

This is basically where I am now. The more handlers we can use, the better the AI will be able to respond to situations, but the basic player squad AI has so many of them tied up in waiting for commands. This really limits what we can do.
foxtrot76
Posts: 75
Joined: Fri, 12. Mar 10, 21:44

Post by foxtrot76 »

Very interesting read although I'm not a modder I did follow to a certain degree what you where describing.

What is the limitation in handler/interrupts amounts and what are the negative effects of having too many?

Can handlers be added or is that hardcoded?
User avatar
pilakin
Posts: 235
Joined: Sat, 16. Feb 08, 23:44

Re: [Discussion] AI Behavior

Post by pilakin »

Kierk wrote:fight.station.player only operates with subordinates which as far as I know only a station can have.
i figured out, you can assign smaller size ships to bigger ships or stations. for example you can assign L ships to XL ships and stations. you can assign XL ships to stations only.
Kierk
Posts: 115
Joined: Wed, 30. Oct 13, 16:38

Post by Kierk »

Important Update!

Correction to prior info, fight.station.player does work on player cap ships, it checks for subordinates and after dealing with them or if none are found calls fight.attack.object.station.

fight.attack.object.station seems to be where the problem lies. Vanilla script has an infinite wait with a series of interrupts to break out of the wait cycle and run the rest of the script. There are basically two checks. Check #1 is object around the ship gets attacked. Check #2 is if itself is attacked.
Problems are that Check #1's list doesn't populate for capships, so its checking an empty list. Check #2 uses this.station to check for attack, since ships aren't stations that check doesn't trigger so the capship is stuck in an infinite wait it can never break out of.

With that fixed capships, both player and npc, break out of the loop but their attacker and enemy lists never populate. This seems to be a problem with the hasrelation.enemy function, it was always returning false on neutral ships even after i shot them enought that the ship turned agressive. The relationto function does seem to return decent values. friendly get positive, unfriendly get negative.

more later.

edit: even with the script stuck npc capships fight back, doesn't seem the defense script doing it, probably from the police/watchdog stuff that I haven't really read into yet. In other words the ship isn't reacting because it's getting hit, it's reacting because other ships around are telling it that it's getting hit which might explain why they act so wonky.
User avatar
Tenlar Scarflame
Posts: 3359
Joined: Mon, 30. May 05, 04:51
xr

Post by Tenlar Scarflame »

Hm - so by default the list isn't even TRYING to populate enemies that are simply nearby? That seems less than proactive. Am I understanding that correctly?
My music - Von Neumann's Children - Lasers and Tactics

I'm on Twitch! 21:15 EST Sundays. Come watch me die a lot.
Kierk
Posts: 115
Joined: Wed, 30. Oct 13, 16:38

Post by Kierk »

correct, the script has three groups it deals with. The first is nearby objects (ships, stations, containers etc). The second is things that have attacked the object running the script. The third is things the object running the script considers enemies.

The first group, nearby objects was built for use on stations so the populate nearby elements stuff is probably meant to work with the mass traffic stuff that stations control. Mass traffic actually does things by the way! I think the visuals and the back end stuff don't exactly mesh up but things definitely happen.

The two other groups, attackers and enemies are there to handle stray shots, so your friends don't blow you up when you miss the bomber taking out their shields. Attack interrupsts get checked to see if the attacking party is an enemy, then added to the naughty list to be killed.

I discovered a problem with that check though. It uses the hasrelation.enemy function to return a true/false in the order victim.hasrelation.enemy.{attacker}. Not the exact code by the way, just an idea of how it shows. Anyway tested it with a neutral freighter reporting the value of the function to me every time i shot it. Even when it turned red and rep went down to-30 it still reported 0, meaning we weren't enemies. I changed up the reporting to use the relationto function and I got results that made more sense. relationto gives a numerical value between -1 to 1 with negatives being unfriendly and positives being friendly. The neutral freighter properly switched from slightly positive to negative after flipping to red as I shot it and my own personal ships stayed at 1 no matter what.

My goal now is to try to replace the hasrelation.enemy with relationto lt 0 and see if lists start to populate. More to come
Raapys
Posts: 159
Joined: Sat, 11. Sep 04, 22:22

Post by Raapys »

Could you use this one from the fight.attack.object.fighter?

Code: Select all

<do_if value="$targets.count == 0">
        <debug_text text="'Checking gravidar for enemies'" chance="$debugoutputchance" />
        <find_gravidar_contact groupname="$targets" functional="true" multiple="true" object="this.ship">
          <match_relation object="this.ship" relation="kill" comparison="le"/>
        </find_gravidar_contact>
      </do_if>
      <!-- if we *still* have no enemies, and are escorting something, see if there are enemies of any of them around -->
      <do_if value="$targets.count == 0 and @$escortees.count">
        <do_all exact="$escortees.count" counter="$i">
          <debug_text text="'Checking gravidar for enemies to escortee ' + $i + ': ' + $escortees.{$i}.knownname" chance="$debugoutputchance" />
          <find_gravidar_contact groupname="$targets" object="this.ship" functional="true" multiple="true">
            <match_relation object="$escortees.{$i}" relation="kill" comparison="le"/>
          </find_gravidar_contact>
          <do_if value="$targets.count">
            <break />
          </do_if>
          <wait min="200ms" max="500ms" />
        </do_all>
      </do_if>

      <!-- Clear non-enemies targets -->
      <!-- abort if the target no longer is an enemy or is docked-->
      <do_all exact="$targets.count" counter="$i" reverse="true">
        <do_if value="not this.hasrelation.enemy.{$targets.{$i}} or @$targets.{$i}.dockslot">
          <remove_from_group group="$targets" object="$targets.{$i}" />
        </do_if>
      </do_all>
Kierk
Posts: 115
Joined: Wed, 30. Oct 13, 16:38

Post by Kierk »

No need, you'll see why in a second.

Understanding Dawns!
I slightly altered Azalrion's excellent Combat System Extension mod to set not just player cap ships but all cap ships to the fight.defend.capital script and I made a major discovery. Scripts for a single 'job' type are not mutually exclusive!

I had it set so both fight.defend.capital and fight.attack.object.station had interrupts for the ship being under attack and appropriate debug text. When the ship got hit I got both sets of messages!

Also after testing the hasrelation.(type) functions do work, something must be up with the station script, further work ongoing.
Azalrion
Posts: 66
Joined: Sat, 9. Nov 13, 11:31
x4

Post by Azalrion »

Great find Kierk, definitely means that my mod needs to be replaced with a proper fix to fight.defend.station for capital ships to include its ability to use escorts / wing mates.

Or perhaps a modification of the two, one for handling of local ships in the area for capital ships and one for handling its own defences.
Kierk
Posts: 115
Joined: Wed, 30. Oct 13, 16:38

Post by Kierk »

With the ability to run multiple scripts at once we might be able to create a custom script for Capship subordinates. The station subordinate bits are designed for a stationary structure...though due to the 15km patrol range they might be useful for scout type ships.

More discoveries! fight.defend.capital calls fight.attack.object.capital to run until there are no more enemies around.

f.a.o.c runs a scan 5.5km around the ship for any enemies and categorizes them into station, capship or fighter/drone. If a primary target has been passed to f.a.o.c from the script that called it (f.d.c does not at present) it focuses fire on that target. Primary ship weapons have a 10 degree fire arc, secondaries have 180, flak type turrets should have 360. Different weapon types also have tracking rates (how fast they can turn to keep targets in sight.) After about 15-20 seconds it'll clear the enemy list and rescan. If no more enemies are in sight it'll either repeat from the start or if the run once parameter was called terminate.

Fun stuff: I hovered the skunk over the quad turrets on top of a Rahanes and saw how they couldn't target me right above them, saw the barrels move to track me and if i moved a little one way 2 of em would be able to shoot and a little further and all 4 open up. It was really cool.

Takeaway: The fact that it scans 5.5km and erases enemy list every 15-20 sec is probably why they seem to forget about you so fast. We might be able to increase the scan range to match their actual Gravidar range like stations do, but we'd need to update the firing script to take range into account.

Return to “X Rebirth - Scripts and Modding”