Make a start on pirate AI (doesn't work yet)

This commit is contained in:
cim 2013-07-14 23:11:25 +01:00
parent b8da00024b
commit 3e9f6741e7
3 changed files with 341 additions and 1 deletions

120
Resources/AIs/pirateAI.js Normal file
View File

@ -0,0 +1,120 @@
/*
pirateAI.js
Priority-based AI for pirates
Oolite
Copyright © 2004-2013 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
"use strict";
this.name = "Oolite Pirate AI";
this.version = "1.79";
this.aiStarted = function() {
var ai = new worldScripts["oolite-libPriorityAI"].AILib(this.ship);
ai.setParameter("oolite_flag_watchForCargo",true);
/* This communication is necessary but needs more variety and moving to descriptions.plist so it can be translated */
ai.setCommunication("oolite_piracyAlert","Your cargo or your life, [p1]! Give us [p2] containers and we'll let you go.");
ai.setPriorities([
/* Combat */
{
condition: ai.conditionLosingCombat,
behaviour: ai.behaviourFleeCombat,
reconsider: 5
},
{
condition: ai.conditionCargoDemandsMet,
/* Let them go if they've dropped enough cargo and stop firing back */
truebranch: [
{
condition: ai.conditionInCombat,
configuration: ai.configurationAcquireCombatTarget,
behaviour: ai.behaviourRepelCurrentTarget,
reconsider: 5
}
],
falsebranch: [
{
condition: ai.conditionInCombat,
configuration: ai.configurationAcquireCombatTarget,
behaviour: ai.behaviourDestroyCurrentTarget,
reconsider: 5
}
]
},
{
preconfiguration: ai.configurationCheckScanner,
condition: ai.conditionScannerContainsSalvage,
configuration: ai.configurationAcquireScannedTarget,
behaviour: ai.behaviourCollectSalvage,
reconsider: 20
},
/* Stay out of the way of hunters */
{
condition: ai.conditionScannerContainsHunters,
configuration: ai.configurationAcquireScannedTarget,
behaviour: ai.behaviourVicinityOfTarget,
reconsider: 20
},
/* Regroup if necessary */
{
preconfiguration: ai.configurationAppointGroupLeader,
condition: ai.conditionGroupIsSeparated,
configuration: ai.configurationSetDestinationToGroupLeader,
behaviour: ai.behaviourApproachDestination,
reconsider: 15
},
{
condition: ai.conditionGroupHasEnoughLoot,
/* Find a station to dock at */
truebranch: [
/* TODO */
],
/* Look for more loot */
falsebranch: [
{
condition: ai.conditionScannerContainsPirateVictims,
configuration: ai.configurationAcquireScannedTarget,
truebranch: [
condition: ai.conditionCombatOddsGood,
behaviour: ai.behaviourRobTarget,
reconsider: 5
]
},
/* TODO: move to a position on one of the space lanes, preferring lane 1 */
]
},
{
// full of loot, but stuck in system and no friendly stations
configuration: ai.configurationSetDestinationToWitchpoint,
// TODO: behaviour search for wormholes
behaviour: ai.behaviourApproachDestination,
reconsider: 30
}
]);
}

View File

@ -39,7 +39,8 @@ this.aiStarted = function() {
* before 1.79 */
ai.setCommunication("oolite_selectedStation","Heading for [p1]");
ai.setCommunication("oolite_selectedWitchspaceDestination","Prepare for witchspace jump to [p1]");
ai.setCommunication("oolite_engageWitchspaceDrive","Commencing witchspace countdown");
ai.setCommunication("oolite_agreeingToDumpCargo","Take our cargo but please let us go!");
ai.setPriorities([
{

View File

@ -480,6 +480,23 @@ this.AILib = function(ship)
});
}
this.conditionScannerContainsHunters = function()
{
return this.checkScannerWithPredicate(function(s) {
return s.primaryRole == "hunter" || s.scanClass == "CLASS_POLICE" || (s.isStation && s.isMainStation);
});
}
this.conditionScannerContainsPirateVictims = function()
{
return this.checkScannerWithPredicate(function(s) {
// is a pirate victim
// has some cargo on board
// hasn't already paid up
return s.isPirateVictim && s.cargoSpaceUsed > 0 && (!s.AIScript.oolite_intership || !s.AIScript.oolite_intership.cargodemandpaid);
});
}
this.conditionScannerContainsHuntableOffender = function()
{
return this.checkScannerWithPredicate(function(s) {
@ -709,6 +726,109 @@ this.AILib = function(ship)
return true;
}
this.conditionCargoDemandsMet = function()
{
if (!this.getParameter("oolite_flag_watchForCargo"))
{
log(this.name,"AI '"+this.ship.AIScript.name+"' for ship "+this.ship+" is asking if cargo demands are met but has not set 'oolite_flag_watchForCargo'");
return true;
}
var seen = this.getParameter("oolite_cargoDropped");
if (seen != null)
{
var demand = 0;
if (this.group)
{
if (this.group.leader && this.group.leader.AIScript.oolite_intership && this.group.leader.AIScript.oolite_intership.cargodemanded > 0)
{
demand = this.group.leader.AIScript.oolite_intership.cargodemanded;
}
else if (this.group.ships[0].AIScript.oolite_intership && this.group.ships[0].AIScript.oolite_intership.cargodemanded > 0)
{
demand = this.group.ships[0].AIScript.oolite_intership.cargodemanded;
}
}
else
{
if (this.ship.AIScript.oolite_intership.cargodemanded > 0)
{
demand = this.ship.AIScript.oolite_intership.cargodemanded;
}
}
if (demand == 0)
{
return false; // no demand made, so it can't have been met
}
if (demand <= seen)
{
return true;
}
}
return false;
}
this.conditionGroupIsSeparated = function()
{
if (!this.ship.group || !this.ship.group.leader)
{
return false;
}
return (this.ship.position.distanceTo(this.ship.group.leader) > this.ship.scannerRange);
}
this.conditionCombatOddsGood = function()
{
// TODO: this should consider what the ships are, somehow
var us = 1;
if (this.ship.group)
{
us += this.ship.group.count - 1;
}
if (this.ship.escortGroup)
{
us += this.ship.escortGroup.count - 1;
}
var them = 1;
if (this.ship.target.group)
{
them += this.ship.target.group.count - 1;
}
if (this.ship.target.escortGroup)
{
them += this.ship.target.escortGroup.count - 1;
}
return us >= them;
}
this.conditionGroupHasEnoughLoot = function()
{
var used = 0;
var available = 0;
if (!this.ship.group)
{
used = this.ship.cargoSpaceUsed;
available = this.ship.cargoSpaceAvailable;
}
else
{
for (var i = 0; i < this.ship.group.ships.length; i++)
{
used += this.ship.group.ships[i].cargoSpaceUsed;
available += this.ship.group.ships[i].cargoSpaceAvailable;
}
}
if (available < used || available == 0)
{
/* Over half-full. This will do for now. TODO: cutting
* losses if group is taking damage, losing ships, running
* low on consumables, etc. */
return true;
}
return false;
}
/* ****************** Behaviour functions ************** */
/* Behaviours. Behaviours are effectively a state definition,
@ -806,6 +926,7 @@ this.AILib = function(ship)
this.responsesAddStandard(handlers);
handlers.shipScoopedOther = function(other)
{
this.setParameter("oolite_cargoDropped",null);
this.reconsiderNow();
}
this.setUpHandlers(handlers);
@ -1142,11 +1263,74 @@ this.AILib = function(ship)
this.behaviourPayOffPirates = function()
{
this.ship.dumpCargo(this.ship.AIScript.oolite_intership.cargodemand);
this.communicate("oolite_agreeingToDumpCargo");
delete this.ship.AIScript.oolite_intership.cargodemand;
this.ship.AIScript.oolite_intership.cargodemandpaid = true;
this.behaviourFleeCombat();
}
this.behaviourLeaveVicinityOfTarget = function()
{
if (!this.ship.target)
{
this.reconsiderNow();
return;
}
this.ship.destination = this.ship.target.position;
this.ship.desiredRange = 27500;
this.ship.desiredSpeed = this.ship.maxSpeed;
var handlers = {};
this.responsesAddStandard(handlers);
this.setUpHandlers(handlers);
this.ship.performFlyToRangeFromDestination();
}
this.behaviourRobTarget = function()
{
var demand = null;
if (this.ship.group && this.ship.group.leader)
{
if (this.ship.group.leader.AIScript.oolite_intership && this.ship.group.leader.AIScript.oolite_intership.cargodemanded)
{
demand = this.ship.group.leader.AIScript.oolite_intership.cargodemanded;
}
}
else
{
if (this.ship.AIScript.oolite_intership.cargodemanded)
{
demand = this.ship.AIScript.oolite_intership.cargodemanded;
}
}
if (demand == null)
{
var hascargo = this.ship.target.cargoSpaceUsed;
var discount = hascargo/10; // if we blow them up we likely won't get more than this anyway
demand = Math.ceil(discount); // but round it up...
/* Record our demand with the group leader */
if (this.ship.group && this.ship.group.leader)
{
this.ship.group.leader.AIScript.oolite_intership.cargodemanded = demand;
}
else
{
this.ship.AIScript.oolite_intership.cargodemanded = demand;
}
/* Inform the victim of the demand, if possible */
if (this.ship.target.AIScript.oolite_intership)
{
this.ship.target.AIScript.oolite_intership.cargodemand = demand;
}
this.communicate("oolite_piracyAlert",this.ship.target,demand);
this.ship.requestHelpFromGroup();
}
var handlers = {};
this.responsesAddStandard(handlers);
this.setUpHandlers(handlers);
this.ship.performAttack();
}
this.behaviourReconsider = function()
{
var handlers = {};
@ -1375,6 +1559,20 @@ this.AILib = function(ship)
}
}
this.configurationSetDestinationToGroupLeader = function()
{
if (!this.ship.group || !this.ship.group.leader)
{
this.ship.destination = this.ship.position;
}
else
{
this.ship.destination = this.ship.group.leader.position;
}
this.ship.desiredRange = 2000;
this.ship.desiredSpeed = this.ship.maxSpeed;
}
this.configurationSetDestinationFromPatrolRoute = function()
{
this.ship.destination = this.getParameter("oolite_patrolRoute");
@ -1532,6 +1730,14 @@ this.AILib = function(ship)
}
this.configurationAppointGroupLeader = function()
{
if (this.ship.group && !this.ship.group.leader)
{
this.ship.group.leader = this.ship.group.ships[0];
}
}
/* ****************** Response definition functions ************** */
/* Standard state-machine responses. These set up a set of standard
@ -1653,6 +1859,19 @@ this.AILib = function(ship)
}
}
}
handlers.cargoDumpedNearby = function(cargo,ship)
{
if (this.getParameter("oolite_flag_watchForCargo"))
{
var previously = this.getParameter("oolite_cargoDropped");
if (previously == null)
{
previously = 0;
}
previously++;
this.setParameter("oolite_cargoDropped",previously);
}
}
handlers.approachingPlanetSurface = function()
{
this.reconsiderNow();