Page 1 of 1

Create AIs without own ships?

Posted: Tue Aug 15, 2017 11:24 am
by teatrader
Hi dear commanders,

I'm new to this forum and joined for a special mission I got in the lave academy just on my first visit there. I want to spice up my ooniverse with some new characters for interaction. I got the idea, I got the AI tutorial and I got an example AI from the Cobra Mk3-XT.

But do I have to create a whole new ship for my future characters? This seems to me at this moment. Can't I just tell them "grab you last money and buy yourself a cheap vessel to go along with"?

I'm not the professional programmer and new to javascript, but I like to learn. Except for my question, sources seem to be available for a quick hack of nice and bad guys.

TL;DR: How to set an AI in an already existing vessel like the Cobra Mk3?

Thank you in advance and bon vayage!

Re: Create AIs without own ships?

Posted: Tue Aug 15, 2017 12:38 pm
by Smivs
Hi teatrader, and welcome.
The AI a ship has is decided in a couple of ways. For example if spawned by the game with a role of trader, it will automatically be given the appropriate AI. The AI is also specified in the vessel's shipdata.plist, but this can be over-ridden by the game in some circumstances. You can use custom AIs however. In shipdata you would need something like

Code: Select all

         ai_type = "myCustomPersonalityAI.plist";
         autoAI = no;
The autoAI line tells the game not to replace the specified (custom) AI. This can be applied to new or existing ships, and just ask if you're not sure how to do this.
By the way, AIs can be written as plists (property lists) or in javascript which is the newer way, and is much more flexible.

Re: Create AIs without own ships?

Posted: Tue Aug 15, 2017 6:48 pm
by teatrader
Hello Smivs, thank you very much. It's great to see a still active and living community for this awesome game. I played it countless hours, days, years on the C64 and Oolite got the job so well done to come close to the original.

And thanks for your advice! I already got my first character into an adder and met him at the next witchpoint. And after he got familiar with the console, one of the next tries he even moved! Now to figure out why he doesn't follow me yet.

I am still confused, where and when to use .js or .plist, in which folder it has to be, what belongs into the AI-folder and what in the Script-folder as well as how to get the files relate to each other and when to use which language - but I'm on the way.

Did you say I can drop plist entirely and have a javascript in the AI folder? Then I still have to figure out how. My companion won't show up without a borrowed plist by now. Guess, he doesn't take me serious yet or just wants to give a hint about getting more money for the job...

Back to the scratchboard then. I'm optimistic.

Re: Create AIs without own ships?

Posted: Tue Aug 15, 2017 7:30 pm
by Norby
Welcome teatrader!

You can look into the code of existing OXPs like EscortDeck or Hired Guns for ideas. I think the old plist AI is more easy to understand and also able to do js tricks by calling functions in ship script, as you can see in the mentioned addons. Wiki is our friend. Happy coding! :)

Re: Create AIs without own ships?

Posted: Wed Aug 16, 2017 10:33 am
by teatrader
Hello and thank you Norby!

Yes, those OXPs seem to hold valuable ideas indeed. And your EscortDeck is quite a big one, respect.
At the moment though I'm still struggeling to even get a basic AI on start. I have all related wiki pages open, but at the moment they confuse me even more :?

May I just introduce my "Fool" to you? He just has to follow me by now, but acts quite strage instead.
(I really would like to only use JS, but am glad any time my fool just appears on screen, even when just drifting in space...)

Besides manifest.plist and requires.plist my folders and files are:

Code: Select all

Folder: AIs
- foolAI.plist   ("simply find player and attack ship")

Folder: Config
- shipdata.plist   (Take an adder and make a fool-ship out of it, use foolAI and the script fool-actions)
- world-scripts.js  (just start Scripts/fool.js)

Folder: Scripts
- fool.js             (let one fool near player position exit witchspace)
- fool-actions.js  (modified script from PriorityAI_Tutorial - mainly: follow player if he is near, flee combat, fly to witchpoint)
What the fool really does (it it because of the name?) is slowly to fly away minding his own business. If attacked, he will strike back for a second, then continue to fly away.

Confusing: In the shipdata.plist you have to define an AI as well as a script and you can't remove one or the other. Which one am I using the wrong way and for what is it normally used instead? And another question is, how should the fool know, when to follow me and when to fly to witchpoint?

Re: Create AIs without own ships?

Posted: Wed Aug 16, 2017 2:21 pm
by Norby
I guess you use world-scripts.plist and not .js if your fool.js is able to spawn ships around you, so you just mistyped it above.

For tests I recomment to use debug console and trunk version of Oolite, or at least an OXP developer version (the ones with -test in filename). In this way you can spawn ships or call your js functions by typing into the console, display the values of your global variables and even change the code of your js functions without reloading the game. Time acceleration is also very handy via pause, shift+F then left-right arrows.

If you place a plist AI into ai_type in shipdata then you must follow the old AI syntax. For the priority AI put your fool-actions.js into ai_type, in this case your plist AI will be ignored. Please decide which one you will use, would be easier for me giving answers in one syntax only. ;)

To follow the player you can set the ship.target to player.ship, then tell to the AI that the ship should fly to the target.

In the plist AI you can use my custom _locatePlayer function in EscortDeck which set the target to the player if nearby, then copy the whole "FOLLOW" section which use the setDestinationToTarget, checkCourseToDestination, performFlyToRangeFromDestination commands, moreover a separated followWaypointAI.plist to handle the case when must fly around an obstacle.

In priority AI you probably should use configurationAcquirePlayerAsTarget then behaviourFollowCurrentTarget. I am not a master of the new syntax, at last resort you can ask cim who made it.

Re: Create AIs without own ships?

Posted: Wed Aug 16, 2017 2:47 pm
by teatrader
Yay, I got one leap forward. Now I guess I have the PriorityAI in the right place and in the right language as foolAI.js in the AIs-folder. At least nothing got broke. The Fool still isn't following me but moves happily through space. Right, I'm using configurationAcquirePlayerAsTarget and behaviourFollowCurrentTarget under the conditionPlayerNearby. Maybe he got trumbles on his console or needs another HUD or glasses. But maybe I'm missing a ship.target, I will get into that. I bet that is the golden hint!

To use some debug tools is surely another great idea!

The fool-actions.js in the Scripts-folder now tries to communicate with me (instead of defining other behaviour than in the AI-file) in the case of a first meeting, getting attacked and getting killed. And I see a "this.ship.target = player.ship" there. Although he doesn't speak yet.

But I begin to feel solid beneath my feet. Could be just an asteroid, but things seem slowly to get in the right shape. I guess, the wiki will also make more sense to me now.

Thank you for your helpful support. I will update my progress soon. :D

Re: Create AIs without own ships?

Posted: Wed Aug 16, 2017 2:52 pm
by Cody
Welcome to the dark side, Commander - cookies in the jar!

Re: Create AIs without own ships?

Posted: Wed Aug 16, 2017 3:21 pm
by Norby
Feel free to post here the whole content of your js files if you have no idea where is the bug.

Re: Create AIs without own ships?

Posted: Thu Aug 17, 2017 7:33 am
by teatrader
Hi and thank you, Cody! Whoo, cookies - luxury goods!
*grabs cookie* Thanks.

Ok, the Fool is already following me.
The priorities in the priorityAI script work in the order of first appearance.
So if you put the "follow me" at first, he will stick to you to the death like a Tibecean dog, even if you are the attacker.
But for now I want him to be a coward, fleeing combat on first shot. Thus my

foolAI.js (in AIs)

Code: Select all

this.aiStarted = function() {
    var ai = new worldScripts["oolite-libPriorityAI"].PriorityAIController(this.ship);
    var priorities = [ 
    {
        condition: ai.conditionLosingCombat,
        behaviour: ai.behaviourFleeCombat,
        reconsider: 5
    },
    {
        condition: ai.conditionInCombat,
        configuration: ai.configurationAcquireCombatTarget,
        behaviour: ai.behaviourFleeCombat,
        reconsider: 5
    },
    {
        condition: ai.conditionPlayerNearby,
        configuration: ai.configurationAcquirePlayerAsTarget,
        behaviour: ai.behaviourFollowCurrentTarget,
        reconsider: 10
    },
    {       
        configuration: ai.configurationSetDestinationToWitchpoint,
        behaviour: ai.behaviourApproachDestination,
        reconsider: 10
    }
    ];
    ai.setPriorities(priorities);
}
So this works fine already. Simple enough.

shipdata.plist (in Config)

Code: Select all

{
	"Fool" =
	{
		like_ship = "adder";                        // this ship is a 'modified' adder
		ai_type = "foolAI.js";                      // the AI file in the AI folder
		autoAI = no;                                // take no other AI than this

		cargo_type = "CARGO_NOT_CARGO";
		forward_weapon_type = "WEAPON_BEAM_LASER";
		aft_weapon_type = "WEAPON_PULSE_LASER";
		auto_weapons = yes;
		has_escape_pod = 0;
		has_fuel_injection = 0;
		has_scoop = 0;
		has_shield_booster = 1;	
		has_shield_enhancer = 1;
		laser_color = "128 64 128";
		likely_cargo = 1;
		missiles = 1;

		name = "Adder Lovelace";
		roles = "Fool";
		script = "fool-actions.js";      // ?
	};
}
No problems here. The adder flies around, then follows, shoots in self-defense or spends a rocket and finally flees.

fool-actions.js (in Scripts, should be called by the shipdata.plist, not as ai_type but as script)

Code: Select all

"use strict";

this.name        = "Fool_Actions"; 
this.author      = "Marten";
this.copyright   = "© 2017 Marten";
this.description = "Fool's ship script"; 
this.version     = "1.0";

// find player and greet him
this.scanForNearestShipHavingRole: player = function ()
{
    // this.ship.target = player.ship;
    this.ship.setTargetToFoundTarget;
    this.ship.commsMessage(expandDescription("[FOOL_FIRST_MESSAGE]"),[TARGET_FOUND]);
    // this.ship.commsMessage(expandDescription("[FOOL_FIRST_MESSAGE]"),player.ship);
    // this.ship.sendTagetCommsMessage(expandDescription("[FOOL_FIRST_MESSAGE]"));
    player.consoleMessage("Adder should have said something now." ,1);
}

// say something when hurt
this.shipBeingAttacked = function(whom)
{
        if(whom.isPlayer)
        {
            // this.ship.commsMessage(expandDescription("[FOOL_ATTACKED_BY_PLAYER_MESSAGE]"),player.ship);
            this.ship.sendTargetCommsMessage(expandDescription("[FOOL_ATTACKED_BY_PLAYER_MESSAGE]"));
        }
        else
        {
            // this.ship.commsMessage(expandDescription("[FOOL_ATTACKED_MESSAGE]"),player.ship);
            this.ship.sendTargetCommsMessage(expandDescription("[FOOL_ATTACKED_BY_PLAYER_MESSAGE]"));
        }
}

// die and say goodbye
this.shipDied = function(whom,why)
{
        if(whom.isPlayer)
        {
            this.ship.commsMessage(expandDescription("[FOOL_KILLED_BY_PLAYER_MESSAGE]"),player.ship);
        }
        else
        {
            this.ship.commsMessage(expandDescription("[FOOL_KILLED_MESSAGE]"),player.ship);
        }
}
Problem here!
This seems to get totally ignored, no matter which method I try to use, though I think I read a death message from it twice.
But could it have been a standard message? Because the adder will occassionally send a common distress call when attacked.
Also there is no consoleMessage.

descriptions.plist (in Config)

Code: Select all

// fool's talk

{
	"FOOL_FIXED_DESC" = "Student of Lave Academy";
	"FOOL_FIRST_MESSAGE" = ("Hi there.","Isn't space huge?","Hey, what about some company?","Hi, let's travel together.","Finally company, how nice!", "Hi, can I join you?", "Hey friend. Let's team up! Good idea, right?");

	"FOOL_ATTACKED_MESSAGE" = ("Help me, [commander_name], I'm under attack!","I'm under attack!","Aaahhhh, I'm hit!","Ouch!","What's happening here?");
	"FOOL_ATTACKED_BY_PLAYER_MESSAGE" = ("Hey, please don't do that!","Hey, friend! I spilled my coffee!","Hey, you're firing at me, pal!","Hey, watch your weapons, dude!","Buddy, you hit me! Stop it, please!");

	"FOOL_KILLED_MESSAGE" = ("[commander_name], farewell!","Revenge me, [commander_name]!","Why did this happen?","[commander_name], they killed me!","Noooo...!");
	"FOOL_KILLED_BY_PLAYER_MESSAGE" = ("[commander_name], you killed me!","I'm sure, it was a mistake, [commander_name]!","Why did you do this, [commander_name]?","[commander_name], you accidently killed me!","[commander_name], nooo...!");
}

Re: Create AIs without own ships?

Posted: Thu Aug 17, 2017 8:29 am
by Norby
teatrader wrote: Thu Aug 17, 2017 7:33 am
Problem here!
You should not use plist AI commands in ship script like setTargetToFoundTarget and sendTargetCommsMessage. These can not work here.

Only those functions will work automatically which are defined in Ship script event handlers. If you create a custom one, like your scanForNearestShipHavingRole then you must call it from a working handler or a timer started in startUp or so.

I can not parse the ": player" part before "= function()", if I would be the interpreter then I would throw a syntax error. ;)

You should show the welcome message within shipTargetAcquired instead. You do not need to write code in ship script to set the target, your priority AI will do it. But an if(target==player.ship) is needed to do not show it when the Adder lock target on other ship.

In shipBeingAttacked I do not know why you display an attacked by player message when whom is not player. Note this handler is called at every laser hits, so you may suppress it for a while after displayed. For example set a global variable (like this.$suppress=true;) and start a timer which trigger only once after 60 seconds, where clear the flag.

Better if you omit this.version from your scripts, will be set automatically based on your oxz version in manifest.plist. We saw new versions appearing without updated version numbers too often in the past, this is why we recommend to do not set it in your scripts, moreover you will save some time at each update if you have many scripts in a package due to you must change it in the manifest only.

Also please put more line breaks into descriptions.plist, for example after commas.

Re: Create AIs without own ships?

Posted: Thu Aug 17, 2017 11:00 am
by teatrader
Good that I can omit the version, I'm also not the fan of changing that number all of the time. It was just a template I found in the wiki.
I also don't care if my name or a copyright is in there, but I guess that will be somehow important when and if my planned characters will become ready to be shared with others (license and stuff).

The descriptions.plist now looks so very nice this way, good hint.
Didn't know if I could and - like the included version - I wanted to make things the way they are suggested or already were successfully used in another script.
Bad if you hack your code over and over while the error is a missing version or a linefeed where no one belongs to.
By the way, is there a commonly used standard for whitespace? Space or Tab? Or is it just good habit to be consistent with one or the other per script?

I got scanForNearestShipHavingRole from AI_methods, but I'm glad it's not needed.

Right, the "attacked by player" was wrong, I guess I was just in the state of "now just say something - anything". Dangerous. Corrected this.

Well, the greeting works! After 3 greetings in a 5 seconds I realized though, that the AI would need to remember it already said hello.
Also it was a bit too early, to get a greeting before my whole ship really arrived in the system, so built in the timer.

Thank you, your feedback was really helpful! :D

Code: Select all

// find player and greet him once
this.$ai_said_hello = false;
this.shipTargetAcquired = function(target)
{
    if(target == player.ship && this.$ai_said_hello == false)
    {
        var $greetPlayerTimer = new Timer(this, function $greeting()
        {
            player.commsMessage(expandDescription("[FOOL_FIRST_MESSAGE]"));
            this.$ai_said_hello = true;
        }, 5, 0);
        
    }
}
I realize the commsMessage is not the right way to do it, because the message comes without the name of the sender. But I didn't find another way yet. How is messaging handled usually? Where do I have to look for it?

Also the AI greets and dies with the right text, but when attacked by me, it will use a standard response from the game.
I guess this has something to do with built-in Oolite Communication Keys, I found in the descriptions.plist of the game?
Do I have to find the right Keys and override them in my own descriptions.plist?

Re: Create AIs without own ships?

Posted: Thu Aug 17, 2017 12:54 pm
by Norby
There is no standard for whitespace, I saw various usages. The most stable is using spaces, the most annoying if a mixed usage cause zigzag because tab was set to different size at the author than at me so the spaced lines are not in line with tabbed ones.

Messages comes from a ship if called as a method of that ship. The timer should be saved into a global variable to prevent garbage collection while running. For example:

Code: Select all

this.$ai_said_hello = false;
this.$greetPlayerTimer = null;
this.shipTargetAcquired = function(target)
{
    if(target == player.ship && this.$ai_said_hello == false)
    {
        this.ship.commsMessage(expandDescription("[FOOL_FIRST_MESSAGE]"), player.ship);
        this.$ai_said_hello = true;
        this.$greetPlayerTimer = new Timer(this, function $greeting()
        {
            this.$ai_said_hello = false;
        }, 60, 0);
    }
}
In this way you will get a message maximum once in a minute.

The descriptions.plist of the game is used by all ships, if you override the content of a key then messages from other ships will change also. You can display your own text in shipBeingAttacked handler, or just call this.ship.broadcastDistressMessage() there which also alert nearby police ships.

Re: Create AIs without own ships?

Posted: Thu Aug 17, 2017 8:31 pm
by teatrader
Fantastic!

I did set the timer that way at first to let the AI be silent before greeting. You should have the chance to arrive in the system and adjust to the planet.
But if the situation changes during that time, it's silly if the AI greets you nice and friendly while it got under attack.
So now I have a timer just for a respectful silence. Then comes the greeting. But it shall be only used once and then never again.
Another timer lets the greeting pass by before going into conversation, where now finally the repeat-timer is set.
Also there is now a switch if the AI wants to talk anyway. Even a fool might check for dangers at first. (Not implemented yet.)

So far it works just fine:

Code: Select all

// communication protocol
this.$ai_waited_to_greet = false;
this.$ai_said_hello = false;
this.$ai_is_chatting = false;

// timers for communication pauses
this.$beSilentTimer = null;
this.$greetingTimer = null;
this.$chattingTimer = null;

// are you safe and got nothing better to do than chatting?
this.$ai_wants_to_talk = true;

// got target?
this.shipTargetAcquired = function(target)
{
    // AI wants to talk to player
    if(target == player.ship && this.$ai_wants_to_talk == true)
    {
        // respectful silence, let the player arrive before you speak up
        if(this.$ai_waited_to_greet == false)
        {
            this.$beSilentTimer = new Timer(this, function $beSilent()
                {
                    this.$ai_waited_to_greet = true;
                }, 5, 0);
        }
        // greet player after respectful silence
        if(this.$ai_said_hello == false && this.$ai_waited_to_greet == true)
        {
            this.ship.commsMessage(expandDescription("[FOOL_FIRST_MESSAGE]"), player.ship);
            this.$greetingTimer = new Timer(this, function $greeting()
            {
                this.$ai_said_hello = true;
            }, 7, 0);
        }
        // start chatting if greeting is done
        if(this.$ai_said_hello == true && this.$ai_is_chatting == false)
        {
            this.ship.commsMessage(expandDescription("[FOOL_CHATTING]"), player.ship);
            this.$ai_is_chatting = true;
            this.$chattingTimer = new Timer(this, function $chatting()
            {
                this.$ai_is_chatting = false;
            }, 5, 0);  // pause and repeat
        }
    } // end of: AI wants to talk
} // end of: got target
Of course this way you will shoot him soon, appearing at every witchpoint you arrive and chatting all the time :) But for testing just right.
Next should be checking for dangers and maybe install a counter, so that the AI could give a special clue every few useless comments or in whole switch into deeper conversations, not sure yet.
In every case I will snapshot this "OXP" now in this very very simple and seemingly stable form as a fallback option, if things go wrong from here. Version 0.1 reached or so. :D

Quick and dirty example statements will lead even faster to execution for now (but might improve a lot after time):

Code: Select all

    "FOOL_FIRST_MESSAGE" = ("Hi there.",
                            "Isn't space huge?",
                            "Hey, what about some company?",
                            "Hi, let's travel together.",
                            "Finally company, how nice!",
                            "Hi, can I join you?",
                            "Hey friend. Let's team up! Good idea, right?");

    "FOOL_CHATTING" = ("So where is the party?",
                        "Ever had a trumble? So cute...",
                        "Just made me some coffee.",
                        "You don't talk too much, do you?",
                        "Wanna karaoke together? No? Hmm ok.",
                        "Uh, this pizza is old.",
                        "Did you read about the hidden planet next to Tionisla?",
                        "Don't worry. If you're not in the mood to talk - I will do the job.",
                        "I'm not so much into narcotics. Not on a daily basis.",
                        "The hen or the egg - what do you think?",
                        "Damn, my cockpit is a mess. I have to clean up. One day.",
                        "Do you come here often? I love this place.",
                        "Hahahaha... hey, these lolcats are so funny!");
Thank you very much so far :!:
Now for some sleep.