2013-07-13 11:36:28 +01:00
/ *
2013-07-26 18:52:11 +01:00
oolite - ailib . js
2013-07-13 11:36:28 +01:00
2013-07-26 18:52:11 +01:00
Priority - based Javascript AI library
2013-07-13 11:36:28 +01:00
2013-07-26 18:52:11 +01:00
Oolite
Copyright © 2004 - 2013 Giles C Williams and contributors
2013-07-13 11:36:28 +01:00
2013-07-26 18:52:11 +01:00
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 .
2013-07-13 11:36:28 +01:00
2013-07-26 18:52:11 +01:00
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 .
2013-07-13 11:36:28 +01:00
2013-07-26 18:52:11 +01:00
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 .
2013-07-13 11:36:28 +01:00
* /
"use strict" ;
2013-07-12 19:44:14 +01:00
/* AI Library */
this . name = "oolite-libPriorityAI" ;
this . version = "1.79" ;
2013-07-13 11:36:28 +01:00
this . copyright = "© 2008-2013 the Oolite team." ;
this . author = "cim" ;
2013-07-12 19:44:14 +01:00
/* Constructor */
this . AILib = function ( ship )
{
2013-08-01 22:40:23 +01:00
// the ship property must be read-only
Object . defineProperty ( this , "ship" , {
value : ship ,
writable : false ,
enumerable : true ,
2013-08-04 14:20:20 +01:00
configurable : true
2013-08-01 22:40:23 +01:00
} ) ;
this . ship . AIScript . oolite _intership = { } ;
this . ship . AIScript . oolite _priorityai = this ;
var activeHandlers = [ ] ;
var priorityList = null ;
var parameters = { } ;
var lastCommSent = 0 ;
var lastCommHeard = 0 ;
var commsRole = "generic" ;
var commsPersonality = "generic" ;
var waypointgenerator = null ;
/* Private utility functions. Cannot be called from external code */
/* Considers a priority list, potentially recursively */
function _reconsiderList ( priorities ) {
var l = priorities . length ;
for ( var i = 0 ; i < l ; i ++ )
{
var priority = priorities [ i ] ;
if ( this . getParameter ( "oolite_flag_behaviourLogging" ) )
{
if ( priority . label )
2013-08-04 12:29:44 +01:00
{
log ( this . ship . name , "Considering: " + priority . label ) ;
2013-07-12 19:44:14 +01:00
}
2013-08-01 22:40:23 +01:00
else
{
log ( this . ship . name , "Considering: entry " + i ) ;
}
}
// always call the preconfiguration function at this point
// to set up condition parameters
if ( priority . preconfiguration )
{
priority . preconfiguration . call ( this ) ;
}
// allow inverted conditions
var condmet = true ;
if ( priority . notcondition )
{
condmet = ! priority . notcondition . call ( this ) ;
}
else if ( priority . condition )
{
condmet = priority . condition . call ( this ) ;
}
// absent condition is always true
if ( condmet )
{
2013-07-17 22:44:26 +01:00
if ( this . getParameter ( "oolite_flag_behaviourLogging" ) )
{
2013-08-01 22:40:23 +01:00
log ( this . ship . name , "Conditions met" ) ;
2013-07-17 22:44:26 +01:00
}
2013-08-01 22:40:23 +01:00
// always call the configuration function at this point
if ( priority . configuration )
2013-07-13 19:13:30 +01:00
{
2013-08-01 22:40:23 +01:00
priority . configuration . call ( this ) ;
2013-07-13 19:13:30 +01:00
}
2013-08-01 22:40:23 +01:00
// this is what we're doing
if ( priority . behaviour )
{
if ( this . getParameter ( "oolite_flag_behaviourLogging" ) )
{
log ( this . ship . name , "Executing behaviour" ) ;
}
if ( priority . reconsider )
{
_resetReconsideration . call ( this , priority . reconsider ) ;
}
return priority . behaviour ;
2013-07-13 19:13:30 +01:00
}
2013-08-01 22:40:23 +01:00
// otherwise this is what we might be doing
if ( priority . truebranch )
{
if ( this . getParameter ( "oolite_flag_behaviourLogging" ) )
{
log ( this . ship . name , "Entering truebranch" ) ;
}
2013-07-13 19:13:30 +01:00
2013-08-01 22:40:23 +01:00
var branch = _reconsiderList . call ( this , priority . truebranch ) ;
if ( branch != null )
{
return branch ;
}
// otherwise nothing in the branch was usable, so move on
}
}
else
{
if ( priority . falsebranch )
2013-07-16 22:33:42 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . getParameter ( "oolite_flag_behaviourLogging" ) )
{
log ( this . ship . name , "Entering falsebranch" ) ;
}
var branch = _reconsiderList . call ( this , priority . falsebranch ) ;
if ( branch != null )
{
return branch ;
}
// otherwise nothing in the branch was usable, so move on
2013-07-16 22:33:42 +01:00
}
2013-08-01 22:40:23 +01:00
}
}
if ( this . getParameter ( "oolite_flag_behaviourLogging" ) )
{
log ( this . ship . name , "Exiting branch" ) ;
}
return null ; // nothing in the list is usable, so return
} ;
2013-07-13 19:13:30 +01:00
2013-08-01 22:40:23 +01:00
/* Only call this from aiAwoken to avoid loops */
function _reconsider ( ) {
if ( ! this . ship || ! this . ship . isValid || ! this . ship . isInSpace )
{
return ;
}
var newBehaviour = _reconsiderList . call ( this , priorityList ) ;
if ( newBehaviour == null ) {
log ( this . name , "AI '" + this . ship . AIScript . name + "' for ship " + this . ship + " had all priorities fail. All priority based AIs should end with an unconditional entry." ) ;
return false ;
}
2013-07-13 19:13:30 +01:00
2013-08-01 22:40:23 +01:00
if ( this . getParameter ( "oolite_flag_behaviourLogging" ) )
2013-07-12 19:44:14 +01:00
{
2013-08-01 22:40:23 +01:00
log ( this . ship . name , newBehaviour ) ;
}
newBehaviour . call ( this ) ;
return true ;
} ;
2013-07-12 19:44:14 +01:00
2013-08-01 22:40:23 +01:00
/* Resets the reconsideration timer. */
function _resetReconsideration ( delay )
{
2013-08-04 14:49:52 +01:00
if ( this . ship )
{
this . ship . AIScriptWakeTime = clock . adjustedSeconds + delay ;
}
2013-08-01 22:40:23 +01:00
} ;
2013-07-12 19:44:14 +01:00
2013-07-27 12:29:59 +01:00
2013-08-01 22:40:23 +01:00
/* ****************** General AI functions. ************** */
/ * T h e s e p r i v i l e g e d f u n c t i o n s i n t e r f a c e w i t h t h e p r i v a t e f u n c t i o n s
* and variables . Do not override them . * /
2013-07-13 19:13:30 +01:00
2013-08-01 22:40:23 +01:00
this . applyHandlers = function ( handlers )
{
// step 1: go through activeHandlers, and delete those
// functions from this.ship.AIScript
for ( var i = 0 ; i < activeHandlers . length ; i ++ )
{
delete this . ship . AIScript [ activeHandlers [ i ] ] ;
}
2013-07-13 19:13:30 +01:00
2013-08-01 22:40:23 +01:00
/ * T h i s h a n d l e r m u s t a l w a y s e x i s t f o r a p r i o r i t y A I , a n d m u s t
* be set here . * /
handlers . aiAwoken = function ( )
{
2013-08-04 14:20:20 +01:00
if ( this . ship )
{
_reconsider . call ( this ) ;
}
}
/ * T h i s h a n d l e r m u s t a l w a y s e x i s t f o r a p r i o r i t y A I , a n d m u s t
* be set here . * /
handlers . shipDied = function ( )
{
this . cleanup ( ) ;
2013-07-12 19:44:14 +01:00
}
2013-08-01 22:40:23 +01:00
// step 2: go through the keys in handlers and put those handlers
// into this.ship.AIScript and the keys into activeHandlers
activeHandlers = Object . keys ( handlers ) ;
for ( var i = 0 ; i < activeHandlers . length ; i ++ )
2013-07-12 19:44:14 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . AIScript [ activeHandlers [ i ] ] = handlers [ [ activeHandlers [ i ] ] ] . bind ( this ) ;
2013-07-12 19:44:14 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-27 12:29:59 +01:00
2013-08-01 22:40:23 +01:00
2013-08-04 14:49:52 +01:00
/* Do not call this directly. It is bcalled automatically on ship death */
2013-08-04 14:20:20 +01:00
this . cleanup = function ( )
{
// break links to disconnect this from GC roots a little sooner
delete this . ship . AIScript . oolite _priorityai ;
2013-08-04 14:49:52 +01:00
this . applyHandlers ( { } ) ;
2013-08-04 14:20:20 +01:00
this . ship . AIScriptWakeTime = 0 ;
delete this . ship . AIScript . aiAwoken ;
Object . defineProperty ( this , "ship" , {
value : ship ,
writable : true ,
enumerable : true ,
configurable : true
} ) ;
delete this . ship ;
delete this . parameters ; // might contain entities
}
2013-08-01 22:40:23 +01:00
this . communicate = function ( key , params , priority )
{
if ( priority > 1 )
2013-07-12 19:44:14 +01:00
{
2013-08-01 22:40:23 +01:00
var send = clock . adjustedSeconds - lastCommSent ;
if ( priority == 2 )
{
if ( send < 10 )
{
return ;
}
}
2013-08-02 22:16:54 +01:00
else
2013-08-01 22:40:23 +01:00
{
2013-08-02 22:16:54 +01:00
var recv = clock . adjustedSeconds - lastCommHeard ;
if ( priority == 3 )
2013-08-01 22:40:23 +01:00
{
2013-08-02 22:16:54 +01:00
if ( recv < 10 || send < 10 )
{
return ;
}
2013-08-01 22:40:23 +01:00
}
2013-08-02 22:16:54 +01:00
else
2013-07-12 19:44:14 +01:00
{
2013-08-02 22:16:54 +01:00
if ( recv < 60 || send < 60 )
{
return ;
}
2013-07-12 19:44:14 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-12 19:44:14 +01:00
}
2013-08-01 22:40:23 +01:00
var template = worldScripts [ "oolite-libPriorityAI" ] . _getCommunication ( commsRole , commsPersonality , key ) ;
if ( template != "" )
2013-07-19 20:18:53 +01:00
{
2013-08-01 22:40:23 +01:00
var message = expandDescription ( template , params ) ;
if ( message != "" )
{
this . ship . commsMessage ( message ) ;
lastCommSent = clock . adjustedSeconds ;
}
2013-08-02 22:16:54 +01:00
else
{
// this is for debugging: ordinarily this is legitimate
2013-08-04 12:29:44 +01:00
// log(this.name,"Empty message for "+key);
2013-08-02 22:16:54 +01:00
}
2013-07-19 20:18:53 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-19 20:18:53 +01:00
2013-08-01 22:40:23 +01:00
this . getParameter = function ( key )
{
if ( key in parameters )
2013-07-14 21:28:19 +01:00
{
2013-08-01 22:40:23 +01:00
return parameters [ key ] ;
2013-07-12 19:44:14 +01:00
}
2013-08-01 22:40:23 +01:00
return null ;
}
2013-07-12 19:44:14 +01:00
2013-07-13 19:13:30 +01:00
2013-08-01 22:40:23 +01:00
this . getWaypointGenerator = function ( )
{
return waypointgenerator ;
}
2013-07-12 19:44:14 +01:00
2013-07-15 23:14:47 +01:00
2013-08-01 22:40:23 +01:00
this . noteCommsHeard = function ( )
{
lastCommHeard = clock . adjustedSeconds ;
}
2013-07-12 19:44:14 +01:00
2013-08-01 22:40:23 +01:00
/* Requests reconsideration of behaviour ahead of schedule. */
this . reconsiderNow = function ( )
{
_resetReconsideration . call ( this , 0.05 ) ;
}
2013-07-27 12:29:59 +01:00
2013-08-01 22:40:23 +01:00
this . setCommunicationsRole = function ( role )
{
commsRole = role ;
}
this . setCommunicationsPersonality = function ( personality )
{
commsPersonality = personality ;
}
// parameters created by Oolite must always be prefixed oolite_
this . setParameter = function ( key , value )
{
parameters [ key ] = value ;
}
this . setPriorities = function ( priorities )
{
priorityList = priorities ;
this . applyHandlers ( { } ) ;
this . reconsiderNow ( ) ;
}
// set the waypoint generator function
this . setWaypointGenerator = function ( value )
{
waypointgenerator = value ;
}
2013-07-13 19:13:30 +01:00
2013-07-27 12:29:59 +01:00
2013-07-26 18:52:11 +01:00
} ; // end object constructor
2013-07-13 19:13:30 +01:00
2013-07-26 18:52:11 +01:00
/* Object prototype */
AILib . prototype . constructor = AILib ;
AILib . prototype . name = this . name ;
/* ****************** AI utility functions. ************** */
/ * T h e s e f u n c t i o n s p r o v i d e s t a n d a r d c h e c k s f o r c o n s i s t e n c y i n
* conditions and other functions . * /
AILib . prototype . allied = function ( ship1 , ship2 )
{
2013-08-01 22:40:23 +01:00
// ships in same group
if ( ship1 . group && ship1 . group . containsShip ( ship2 ) )
{
return true ;
}
if ( ship1 . group && ship1 . group . leader )
{
// ship1 is escort of ship in same group as ship2
if ( ship1 . group . leader . group && ship1 . group . leader . group . containsShip ( ship2 ) )
{
return true ;
}
}
// or in reverse, ship2 is the escort
if ( ship2 . group && ship2 . group . leader )
{
// ship2 is escort of ship in same group as ship1
if ( ship2 . group . leader . group && ship2 . group . leader . group . containsShip ( ship1 ) )
{
return true ;
}
}
// ship1 is escort of a ship, ship2 is escort of a ship, both
// those ships are in the same group
if ( ship1 . group && ship1 . group . leader && ship2 . group && ship2 . group . leader && ship1 . group . leader . group && ship1 . group . leader . group . containsShip ( ship2 . group . leader ) )
{
return true ;
}
2013-08-04 12:29:44 +01:00
if ( ship1 . scanClass == ship2 . scanClass )
{
// all thargoids are allied with each other
// all police are allied with each other
if ( ship1 . scanClass == "CLASS_THARGOID" || ship1 . scanClass == "CLASS_POLICE" )
{
return true ;
}
}
2013-08-01 22:40:23 +01:00
// Okay, these ships really do have nothing to do with each other...
return false ;
}
AILib . prototype . broadcastDistressMessage = function ( )
{
this . ship . broadcastDistressMessage ( ) ;
if ( this . ship . AIPrimaryAggressor )
{
this . communicate ( "oolite_makeDistressCall" , this . entityCommsParams ( this . ship . AIPrimaryAggressor ) , 2 ) ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-13 23:18:05 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . checkScannerWithPredicate = function ( predicate )
{
2013-08-01 22:40:23 +01:00
var scan = this . getParameter ( "oolite_scanResults" ) ;
if ( scan == null || predicate == null )
{
return false ;
}
for ( var i = 0 ; i < scan . length ; i ++ )
{
if ( predicate . call ( this , scan [ i ] ) )
2013-07-22 21:24:27 +01:00
{
2013-08-01 22:40:23 +01:00
this . setParameter ( "oolite_scanResultSpecific" , scan [ i ] ) ;
return true ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
return false ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . cruiseSpeed = function ( )
{
2013-08-01 22:40:23 +01:00
var cruise = this . ship . maxSpeed * 0.8 ;
if ( this . ship . group )
{
for ( var i = 0 ; i < this . ship . group . ships . length ; i ++ )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group . ships [ i ] . maxSpeed >= this . ship . maxSpeed / 4 )
{
if ( cruise > this . ship . group . ships [ i ] . maxSpeed )
2013-07-22 21:24:27 +01:00
{
2013-08-01 22:40:23 +01:00
cruise = this . ship . group . ships [ i ] . maxSpeed ;
2013-07-22 21:24:27 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-22 21:24:27 +01:00
}
2013-08-01 22:40:23 +01:00
}
if ( this . ship . escortGroup )
{
for ( var i = 0 ; i < this . ship . escortGroup . ships . length ; i ++ )
2013-07-13 23:18:05 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . escortGroup . ships [ i ] . maxSpeed >= this . ship . maxSpeed / 4 )
{
if ( cruise > this . ship . escortGroup . ships [ i ] . maxSpeed )
2013-07-13 23:18:05 +01:00
{
2013-08-01 22:40:23 +01:00
cruise = this . ship . escortGroup . ships [ i ] . maxSpeed ;
2013-07-13 23:18:05 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
return cruise ;
}
// gets a standard comms params object
AILib . prototype . entityCommsParams = function ( entity )
{
var params = { } ;
2013-08-02 22:16:54 +01:00
if ( entity )
2013-08-01 22:40:23 +01:00
{
2013-08-02 22:16:54 +01:00
if ( entity . isShip )
{
// TODO: extend the ship object so more precise names can be
// returned?
params [ "oolite_entityClass" ] = entity . name ; //ship.shipClassName;
params [ "oolite_entityName" ] = entity . displayName ; // ship.shipName;
}
else if ( entity . name )
{
params [ "oolite_entityClass" ] = entity . name ;
params [ "oolite_entityName" ] = entity . name ;
}
2013-08-01 22:40:23 +01:00
}
return params ;
2013-07-26 18:52:11 +01:00
}
2013-07-27 12:29:59 +01:00
AILib . prototype . fineThreshold = function ( )
{
2013-08-01 22:40:23 +01:00
return 50 - ( system . info . government * 7 ) ;
2013-07-27 12:29:59 +01:00
}
2013-07-26 18:52:11 +01:00
AILib . prototype . friendlyStation = function ( station )
{
2013-08-01 22:40:23 +01:00
if ( station . isMainStation && this . ship . bounty > this . fineThreshold ( ) )
{
return false ;
}
2013-08-05 14:14:50 +01:00
// this will do until we have a proper friendliness system for stations
if ( this . ship . primaryRole == "pirate" && station . bounty == 0 )
{
return false ;
}
if ( this . ship . scanClass == "CLASS_THARGOID" && station . scanClass != "CLASS_THARGOID" )
{
return false ;
}
if ( station . scanClass == "CLASS_THARGOID" && this . ship . scanClass != "CLASS_THARGOID" )
{
return false ;
}
2013-08-01 22:40:23 +01:00
return ( station . target != this . ship || ! station . hasHostileTarget ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . homeStation = function ( )
{
2013-08-01 22:40:23 +01:00
// home station might be the owner of the ship, or might just
// be a group member
if ( this . ship . owner && this . ship . owner . isStation && this . friendlyStation ( this . ship . owner ) )
{
return this . ship . owner ;
}
if ( this . ship . group )
{
for ( var i = 0 ; i < this . ship . group . ships . length ; i ++ )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group . ships [ i ] != this . ship && this . ship . group . ships [ i ] . isStation && this . friendlyStation ( this . ship . group . ships [ i ] . isStation ) )
{
return this . ship . group . ships [ i ] ;
}
2013-07-13 23:18:05 +01:00
}
2013-08-01 22:40:23 +01:00
}
return null ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . isAggressive = function ( ship )
{
2013-08-01 22:40:23 +01:00
if ( ship && ship . isPlayer )
{
return ! ship . isFleeing ;
}
return ship && ship . hasHostileTarget && ! ship . isFleeing && ! ship . isDerelict ;
2013-07-26 18:52:11 +01:00
}
2013-08-04 12:29:44 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . isFighting = function ( ship )
{
2013-08-01 22:40:23 +01:00
if ( ship . isStation )
{
return ship . alertCondition == 3 && ship . target ;
}
return ship && ship . hasHostileTarget ;
2013-07-26 18:52:11 +01:00
}
2013-08-04 12:29:44 +01:00
/ * C a l l j u s t b e f o r e s w i t c h i n g t a r g e t t o a m o r e s e r i o u s t h r e a t , w h o m i s
* the more serious threat * /
AILib . prototype . noteDistraction = function ( whom )
{
if ( this . ship . target )
{
if ( this . ship . target . script && this . ship . target . script . shipAttackerDistracted )
{
this . ship . target . script . shipAttackerDistracted ( whom ) ;
}
if ( this . ship . target . AIScript && this . ship . target . AIScript . shipAttackerDistracted )
{
this . ship . target . AIScript . shipAttackerDistracted ( whom ) ;
}
}
}
2013-08-05 14:14:50 +01:00
AILib . prototype . oddsAssessment = function ( )
{
if ( ! this . ship . target )
{
return 10 ;
}
var us = 0 ;
var them = 0 ;
var i = 0 ;
var ship ;
us += this . threatAssessment ( this . ship , true )
if ( this . ship . group )
{
for ( i = 0 ; i < this . ship . group . ships . length ; i ++ )
{
ship = this . ship . group . ships [ i ]
if ( ship != this . ship && ship . position . distanceTo ( this . ship . target ) < this . ship . scannerRange )
{
us += this . threatAssessment ( ship , true ) ;
}
}
if ( this . ship . group . leader && this . ship . group . leader . group != this . ship . group )
{
// don't want escorts running off early
for ( i = 0 ; i < this . ship . group . leader . group . ships . length ; i ++ )
{
ship = this . ship . group . leader . group . ships [ i ] ;
if ( ship != this . ship && ship . position . distanceTo ( this . ship . target ) < this . ship . scannerRange )
{
us += this . threatAssessment ( ship , true ) ;
}
}
}
}
if ( this . ship . escortGroup && this . ship . escortGroup != this . ship . group )
{
for ( i = 0 ; i < this . ship . escortGroup . ships . length ; i ++ )
{
ship = this . ship . escortGroup . ships [ i ] ;
if ( ship != this . ship && ship . position . distanceTo ( this . ship . target ) < this . ship . scannerRange )
{
us += this . threatAssessment ( ship , true ) ;
}
}
}
them += this . threatAssessment ( this . ship . target , false )
if ( this . ship . target . group )
{
for ( i = 0 ; i < this . ship . target . group . ships . length ; i ++ )
{
ship = this . ship . target . group . ships [ i ]
if ( ship != this . ship . target && ship . position . distanceTo ( this . ship ) < this . ship . scannerRange )
{
them += this . threatAssessment ( ship , false ) ;
}
}
}
if ( this . ship . target . escortGroup && this . ship . target . escortGroup != this . ship . target . group )
{
for ( i = 0 ; i < this . ship . target . escortGroup . ships . length ; i ++ )
{
ship = this . ship . target . escortGroup . ships [ i ]
if ( ship != this . ship . target && ship . position . distanceTo ( this . ship ) < this . ship . scannerRange )
{
them += this . threatAssessment ( ship , false ) ;
}
}
}
return us / them ;
}
2013-08-04 12:29:44 +01:00
/* Be very careful with 'passon' parameter to avoid infinite loops */
AILib . prototype . respondToThargoids = function ( whom , passon )
{
if ( this . getParameter ( "oolite_flag_noSpecialThargoidReaction" ) != null )
{
return false ;
}
// non-thargoid being attacked by thargoid
if ( this . ship . target && this . ship . target . scanClass != "CLASS_THARGOID" )
{
if ( passon )
{
this . noteDistraction ( whom ) ;
}
this . ship . target = whom ; // thargoid gets priority
if ( passon )
{
this . ship . requestHelpFromGroup ( ) ; // tell the rest!
this . communicate ( "oolite_thargoidAttack" , this . entityCommsParams ( whom ) , 2 ) ;
}
}
var dts = this . ship . defenseTargets ;
for ( var i = 0 ; i < dts . length ; i ++ )
{
if ( dts [ i ] . scanClass != "CLASS_THARGOID" && dts [ i ] . scanClass != "CLASS_MISSILE" && dts [ i ] . scanClass != "CLASS_MINE" )
{
// safe: dts is a copy of the real data
this . ship . removeDefenseTarget ( dts [ i ] ) ;
}
}
return true ;
}
2013-08-05 14:14:50 +01:00
AILib . prototype . threatAssessment = function ( ship , full )
2013-08-04 12:29:44 +01:00
{
2013-08-05 14:14:50 +01:00
return worldScripts [ "oolite-libPriorityAI" ] . _threatAssessment ( ship , full ) ;
2013-08-04 12:29:44 +01:00
}
2013-07-26 18:52:11 +01:00
/* ****************** Condition functions ************** */
/ * C o n d i t i o n s . A n y f u n c t i o n w h i c h r e t u r n s t r u e o r f a l s e c a n b e u s e d a s
* a condition . They do not have to be part of the AI library , but
* several common conditions are provided here . * /
/*** Combat-related conditions ***/
2013-07-27 15:39:23 +01:00
AILib . prototype . conditionCascadeDetected = function ( )
{
2013-08-01 22:40:23 +01:00
var cpos = this . getParameter ( "oolite_cascadeDetected" ) ;
if ( cpos != null )
{
if ( cpos . distanceTo ( this . ship ) < this . ship . scannerRange )
2013-07-27 15:39:23 +01:00
{
2013-08-01 22:40:23 +01:00
return true ;
2013-07-27 15:39:23 +01:00
}
2013-08-01 22:40:23 +01:00
this . setParameter ( "oolite_cascadeDetected" , null ) ;
}
return false ;
2013-07-27 15:39:23 +01:00
}
2013-08-05 14:14:50 +01:00
AILib . prototype . conditionCombatOddsTerrible = function ( )
{
2013-08-05 15:42:26 +01:00
if ( this . getParameter ( "oolite_flag_surrendersEarly" ) )
{
return this . oddsAssessment ( ) < 0.75 ;
}
else
{
return this . oddsAssessment ( ) < 0.375 ;
}
2013-08-05 14:14:50 +01:00
}
AILib . prototype . conditionCombatOddsBad = function ( )
{
2013-08-05 15:42:26 +01:00
if ( this . getParameter ( "oolite_flag_surrendersLate" ) )
{
return this . oddsAssessment ( ) < 0.375 ;
}
else
{
return this . oddsAssessment ( ) < 0.75 ;
}
2013-08-05 14:14:50 +01:00
}
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionCombatOddsGood = function ( )
{
2013-08-05 14:14:50 +01:00
return this . oddsAssessment ( ) >= 1.5 ;
}
2013-08-01 22:40:23 +01:00
2013-08-05 14:14:50 +01:00
AILib . prototype . conditionCombatOddsExcellent = function ( )
{
return this . oddsAssessment ( ) >= 3.0 ;
2013-07-26 18:52:11 +01:00
}
2013-07-13 23:18:05 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionInCombat = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . isFighting ( this . ship ) )
{
return true ;
}
var dts = this . ship . defenseTargets ;
for ( var i = 0 ; i < dts . length ; i ++ )
{
2013-08-04 12:29:44 +01:00
if ( dts [ i ] . position . squaredDistanceTo ( this . ship ) < this . ship . scannerRange * this . ship . scannerRange && this . isFighting ( dts [ i ] ) )
2013-07-15 23:14:47 +01:00
{
2013-08-01 22:40:23 +01:00
return true ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
if ( this . ship . group != null )
{
for ( var i = 0 ; i < this . ship . group . count ; i ++ )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . isFighting ( this . ship . group . ships [ i ] ) )
{
return true ;
}
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
if ( this . ship . escortGroup != null )
{
for ( var i = 0 ; i < this . ship . escortGroup . count ; i ++ )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . isFighting ( this . ship . escortGroup . ships [ i ] ) )
{
return true ;
}
2013-07-15 23:14:47 +01:00
}
2013-08-01 22:40:23 +01:00
}
delete this . ship . AIScript . oolite _intership . cargodemandpaid ;
return false ;
2013-07-26 18:52:11 +01:00
}
2013-07-15 23:14:47 +01:00
2013-07-26 18:52:11 +01:00
/* Ships being attacked are firing back */
AILib . prototype . conditionInCombatWithHostiles = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . isFighting ( this . ship ) && this . isAggressive ( this . ship . target ) )
{
return true ;
}
var dts = this . ship . defenseTargets ;
for ( var i = 0 ; i < dts . length ; i ++ )
{
if ( this . isAggressive ( dts [ i ] ) && dts [ i ] . position . squaredDistanceTo ( this . ship ) < this . ship . scannerRange * this . ship . scannerRange )
2013-07-15 23:14:47 +01:00
{
2013-08-01 22:40:23 +01:00
return true ;
2013-07-15 23:14:47 +01:00
}
2013-08-01 22:40:23 +01:00
else
2013-07-20 09:14:58 +01:00
{
2013-08-01 22:40:23 +01:00
// this is safe to do mid-loop as dts is a copy of the
// actual defense target list
this . ship . removeDefenseTarget ( dts [ i ] ) ;
2013-07-20 09:14:58 +01:00
}
2013-08-01 22:40:23 +01:00
}
if ( this . ship . group != null )
{
for ( var i = 0 ; i < this . ship . group . count ; i ++ )
2013-07-12 19:44:14 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . isFighting ( this . ship . group . ships [ i ] ) && this . isAggressive ( this . ship . group . ships [ i ] . target ) )
{
return true ;
}
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
if ( this . ship . escortGroup != null )
{
for ( var i = 0 ; i < this . ship . escortGroup . count ; i ++ )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . isFighting ( this . ship . escortGroup . ships [ i ] ) && this . isAggressive ( this . ship . escortGroup . ships [ i ] . target ) )
{
return true ;
}
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
delete this . ship . AIScript . oolite _intership . cargodemandpaid ;
return false ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionLosingCombat = function ( )
{
2013-08-01 22:40:23 +01:00
var cascade = this . getParameter ( "oolite_cascadeDetected" ) ;
if ( cascade != null )
{
if ( cascade . distanceTo ( this . ship ) < 25600 )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
return true ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
else
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . setParameter ( "oolite_cascadeDetected" , null ) ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
if ( this . ship . energy == this . ship . maxEnergy )
{
// forget previous defeats
2013-08-05 14:14:50 +01:00
if ( ! this . conditionCombatOddsTerrible ( ) )
{
this . setParameter ( "oolite_lastFleeing" , null ) ;
}
2013-08-01 22:40:23 +01:00
}
if ( ! this . conditionInCombat ( ) )
{
return false ;
}
var lastThreat = this . getParameter ( "oolite_lastFleeing" ) ;
if ( lastThreat != null && this . ship . position . distanceTo ( lastThreat ) < 25600 )
{
// the thing that attacked us is still nearby
return true ;
}
if ( this . ship . energy * 4 < this . ship . maxEnergy )
{
// TODO: adjust threshold based on group odds
return true ; // losing if less than 1/4 energy
}
var dts = this . ship . defenseTargets ;
for ( var i = 0 ; i < dts . length ; i ++ )
{
if ( dts [ i ] . scanClass == "CLASS_MISSILE" && dts [ i ] . target == this . ship )
{
return true ;
}
if ( dts [ i ] . scanClass == "CLASS_MINE" )
{
return true ;
}
}
// if we've dumped cargo or the group leader has, then we're losing
if ( this . ship . AIScript . oolite _intership . cargodemandpaid )
{
return true ;
}
if ( this . ship . group && this . ship . group . leader && this . ship . group . leader . AIScript . oolite _intership && this . ship . group . leader . AIScript . oolite _intership . cargodemandpaid )
{
return true ;
}
2013-08-05 14:14:50 +01:00
if ( this . ship . energy < this . ship . maxEnergy / 2 )
{
if ( this . conditionCombatOddsBad ( ) )
{
// outnumbered; losing earlier
return true ;
}
}
if ( this . conditionCombatOddsTerrible ( ) )
{
if ( ! this . ship . isFleeing )
{
if ( this . ship . group && this . ship . group . leader && this . ship . group . leader == this . ship )
{
this . communicate ( "oolite_groupIsOutnumbered" , { } , 2 ) ;
}
else
{
this . communicate ( "oolite_groupIsOutnumbered" , { } , 4 ) ;
}
}
// badly outnumbered; losing
return true ;
}
2013-08-01 22:40:23 +01:00
return false ; // not losing yet
}
AILib . prototype . conditionMothershipInCombat = function ( )
{
if ( this . ship . group && this . ship . group . leader && this . ship . group . leader != this . ship )
{
var leader = this . ship . group . leader ;
if ( leader . position . distanceTo ( this . ship ) > this . ship . scannerRange )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
return false ; // can't tell
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
if ( this . isFighting ( leader ) )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
return true ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
if ( leader . target && leader . target . target == leader && leader . target . hasHostileTarget )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
return true ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
var dts = leader . defenseTargets ;
2013-07-26 18:52:11 +01:00
for ( var i = 0 ; i < dts . length ; i ++ )
{
2013-08-01 22:40:23 +01:00
if ( dts [ i ] . target == leader && dts [ i ] . hasHostileTarget )
{
2013-07-26 18:52:11 +01:00
return true ;
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
return false ;
}
else
{
// no mothership
return false ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-12 19:44:14 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionMothershipIsAttacking = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group && this . ship . group . leader != this . ship )
{
var leader = this . ship . group . leader ;
if ( leader . target && this . isFighting ( leader ) && leader . target . position . distanceTo ( this . ship ) < this . ship . scannerRange )
2013-07-17 22:44:26 +01:00
{
2013-08-01 22:40:23 +01:00
return true ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
return false ;
2013-07-26 18:52:11 +01:00
}
// as MothershipIsAttacking, but leader.target must be aggressive
AILib . prototype . conditionMothershipIsAttackingHostileTarget = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group && this . ship . group . leader != this . ship )
{
var leader = this . ship . group . leader ;
if ( leader . target && this . isFighting ( leader ) && this . isAggressive ( leader . target ) && leader . target . position . distanceTo ( this . ship ) < this . ship . scannerRange )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
return true ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
return false ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionMothershipUnderAttack = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group && this . ship . group . leader != this . ship )
{
var leader = this . ship . group . leader ;
if ( leader . target && leader . target . target == leader && leader . target . hasHostileTarget && leader . target . position . distanceTo ( this . ship ) < this . ship . scannerRange )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
return true ;
2013-07-17 22:44:26 +01:00
}
2013-08-01 22:40:23 +01:00
var dts = leader . defenseTargets ;
for ( var i = 0 ; i < dts . length ; i ++ )
2013-07-24 21:56:50 +01:00
{
2013-08-01 22:40:23 +01:00
if ( dts [ i ] . target == leader && dts [ i ] . hasHostileTarget && dts [ i ] . position . distanceTo ( this . ship ) < this . ship . scannerRange )
{
return true ;
}
2013-07-24 21:56:50 +01:00
}
2013-08-01 22:40:23 +01:00
return false ;
}
else
{
return false ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-24 21:56:50 +01:00
2013-07-26 18:52:11 +01:00
/*** Navigation-related conditions ***/
AILib . prototype . conditionCanWitchspaceOut = function ( )
{
2013-08-01 22:40:23 +01:00
if ( ! this . ship . hasHyperspaceMotor )
{
return false ;
}
return ( system . info . systemsInRange ( this . ship . fuel ) . length > 0 ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-24 21:56:50 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionFriendlyStationExists = function ( )
{
2013-08-01 22:40:23 +01:00
var stations = system . stations ;
for ( var i = 0 ; i < stations . length ; i ++ )
{
var station = stations [ i ] ;
if ( this . friendlyStation ( station ) )
2013-07-13 23:18:05 +01:00
{
2013-08-01 22:40:23 +01:00
// this is not a very good check for friendliness, but
// it will have to do for now
return true ;
2013-07-13 23:18:05 +01:00
}
2013-08-01 22:40:23 +01:00
}
return false ;
2013-07-26 18:52:11 +01:00
}
2013-07-13 23:18:05 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionFriendlyStationNearby = function ( )
{
2013-08-01 22:40:23 +01:00
var stations = system . stations ;
for ( var i = 0 ; i < stations . length ; i ++ )
{
var station = stations [ i ] ;
if ( this . friendlyStation ( station ) )
2013-07-13 23:18:05 +01:00
{
2013-08-01 22:40:23 +01:00
// this is not a very good check for friendliness, but
// it will have to do for now
if ( station . position . distanceTo ( this . ship ) < this . ship . scannerRange )
{
return true ;
}
2013-07-13 23:18:05 +01:00
}
2013-08-01 22:40:23 +01:00
}
return false ;
2013-07-26 18:52:11 +01:00
}
2013-07-13 23:18:05 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionGroupIsSeparated = function ( )
{
2013-08-01 22:40:23 +01:00
if ( ! this . ship . group || ! this . ship . group . leader )
{
return false ;
}
if ( this . ship . group . leader . isStation )
{
// can get 2x as far from station
return ( this . ship . position . distanceTo ( this . ship . group . leader ) > this . ship . scannerRange * 2 ) ;
}
else
{
return ( this . ship . position . distanceTo ( this . ship . group . leader ) > this . ship . scannerRange ) ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-22 23:15:17 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionHasSelectedPlanet = function ( )
{
2013-08-01 22:40:23 +01:00
var planet = this . getParameter ( "oolite_selectedPlanet" ) ;
if ( planet && ( ! planet . isValid || ! planet . isPlanet ) )
{
this . setParameter ( "oolite_selectedPlanet" , null ) ;
return false ;
}
return planet != null ;
2013-07-26 18:52:11 +01:00
}
2013-07-12 19:44:14 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionHasSelectedStation = function ( )
{
2013-08-01 22:40:23 +01:00
var station = this . getParameter ( "oolite_selectedStation" ) ;
if ( station && ( ! station . isValid || ! station . isStation ) )
{
this . setParameter ( "oolite_selectedStation" , null ) ;
return false ;
}
return station != null ;
2013-07-26 18:52:11 +01:00
}
2013-07-13 19:13:30 +01:00
2013-07-14 23:11:25 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionHomeStationExists = function ( )
{
2013-08-01 22:40:23 +01:00
return ( this . homeStation ( ) != null ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionHomeStationNearby = function ( )
{
2013-08-01 22:40:23 +01:00
var home = this . homeStation ( ) ;
if ( home == null )
{
return false ;
}
return this . ship . position . distanceTo ( home ) < this . ship . scannerRange ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionInInterstellarSpace = function ( )
{
2013-08-01 22:40:23 +01:00
return system . isInterstellarSpace ;
2013-07-26 18:52:11 +01:00
}
2013-07-18 23:00:23 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionMainPlanetNearby = function ( )
{
2013-08-01 22:40:23 +01:00
if ( ! system . mainPlanet )
{
2013-07-26 18:52:11 +01:00
return false ;
2013-08-01 22:40:23 +01:00
}
if ( this . ship . position . distanceTo ( system . mainPlanet ) < system . mainPlanet . radius * 4 )
{
return true ;
}
return false ;
2013-07-26 18:52:11 +01:00
}
2013-07-21 11:10:47 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionNearDestination = function ( )
{
2013-08-01 22:40:23 +01:00
return ( this . ship . destination . squaredDistanceTo ( this . ship ) < this . ship . desiredRange * this . ship . desiredRange ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionPlayerNearby = function ( )
{
2013-08-01 22:40:23 +01:00
return this . ship . position . distanceTo ( player . ship ) < this . ship . scannerRange ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionReadyToSunskim = function ( )
{
2013-08-01 22:40:23 +01:00
return ( system . sun && this . ship . position . distanceTo ( system . sun ) < system . sun . radius * 1.15 ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionSelectedStationNearby = function ( )
{
2013-08-01 22:40:23 +01:00
var station = this . getParameter ( "oolite_selectedStation" ) ;
if ( station && station . position . distanceTo ( this . ship ) < this . ship . scannerRange )
{
return true ;
}
return false ;
2013-07-26 18:52:11 +01:00
}
2013-07-13 19:13:30 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionSelectedStationNearMainPlanet = function ( )
{
2013-08-01 22:40:23 +01:00
if ( ! system . mainPlanet )
{
2013-07-26 18:52:11 +01:00
return false ;
2013-08-01 22:40:23 +01:00
}
var station = this . getParameter ( "oolite_selectedStation" ) ;
if ( station && station . position . distanceTo ( system . mainPlanet ) < system . mainPlanet . radius * 4 )
{
return true ;
}
return false ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionSunskimPossible = function ( )
{
2013-08-01 22:40:23 +01:00
return ( system . sun &&
! system . sun . hasGoneNova &&
! system . sun . isGoingNova &&
this . ship . fuel < 7 &&
this . ship . equipmentStatus ( "EQ_FUEL_SCOOPS" ) == "EQUIPMENT_OK" &&
( this . ship . heatInsulation > 1000 / this . ship . maxSpeed || this . ship . heatInsulation >= 12 ) ) ;
2013-07-26 18:52:11 +01:00
}
/*** Pirate conditions ***/
2013-07-22 21:24:27 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionCargoDemandsMet = function ( )
{
2013-08-01 22:40:23 +01:00
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 recorder = null ;
var demand = 0 ;
if ( this . ship . group )
2013-07-13 19:13:30 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group . leader && this . ship . group . leader . AIScript . oolite _intership && this . ship . group . leader . AIScript . oolite _intership . cargodemanded > 0 )
{
if ( this . ship . group . leader . AIScript . oolite _intership . cargodemandmet )
2013-07-15 23:14:47 +01:00
{
2013-08-01 22:40:23 +01:00
return true ;
2013-07-13 19:13:30 +01:00
}
2013-08-01 22:40:23 +01:00
recorder = this . ship . group . leader ;
demand = this . ship . group . leader . AIScript . oolite _intership . cargodemanded ;
}
else if ( this . ship . group . ships [ 0 ] . AIScript . oolite _intership && this . ship . group . ships [ 0 ] . AIScript . oolite _intership . cargodemanded > 0 )
2013-07-13 19:13:30 +01:00
2013-08-01 22:40:23 +01:00
{
demand = this . ship . group . ships [ 0 ] . AIScript . oolite _intership . cargodemanded ;
if ( this . ship . group . ships [ 0 ] . AIScript . oolite _intership . cargodemandmet )
2013-07-23 21:21:49 +01:00
{
2013-08-01 22:40:23 +01:00
return true ;
2013-07-13 19:13:30 +01:00
}
2013-08-01 22:40:23 +01:00
recorder = this . ship . group . ships [ 0 ] ;
}
2013-07-13 19:13:30 +01:00
}
2013-08-01 22:40:23 +01:00
else
2013-07-14 19:02:16 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . AIScript . oolite _intership . cargodemanded > 0 )
{
if ( this . ship . AIScript . oolite _intership . cargodemandmet )
2013-07-14 19:02:16 +01:00
{
2013-08-01 22:40:23 +01:00
return true ;
2013-07-14 19:02:16 +01:00
}
2013-08-01 22:40:23 +01:00
demand = this . ship . AIScript . oolite _intership . cargodemanded ;
recorder = this . ship ;
}
2013-07-14 19:02:16 +01:00
}
2013-08-01 22:40:23 +01:00
if ( demand == 0 )
2013-07-21 23:25:41 +01:00
{
2013-08-01 22:40:23 +01:00
return true ; // no demand made
2013-07-21 23:25:41 +01:00
}
2013-08-01 22:40:23 +01:00
if ( demand <= seen )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
recorder . AIScript . oolite _intership . cargodemandmet = true ;
return true ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
return false ;
2013-07-26 18:52:11 +01:00
}
2013-07-21 23:25:41 +01:00
2013-08-01 22:40:23 +01:00
AILib . prototype . conditionGroupHasEnoughLoot = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
var used = 0 ;
var available = 0 ;
if ( ! this . ship . group )
{
used = this . ship . cargoSpaceUsed ;
if ( this . ship . equipmentStatus ( "EQ_FUEL_SCOOPS" ) == "EQUIPMENT_OK" )
{
available = this . ship . cargoSpaceAvailable ;
}
}
else
{
for ( var i = 0 ; i < this . ship . group . ships . length ; i ++ )
{
used += this . ship . group . ships [ i ] . cargoSpaceUsed ;
if ( this . ship . equipmentStatus ( "EQ_FUEL_SCOOPS" ) == "EQUIPMENT_OK" )
{
available += this . ship . group . ships [ i ] . cargoSpaceAvailable ;
}
}
}
if ( available < used || available == 0 )
{
/ * O v e r h a l f - f u l l . T h i s w i l l d o f o r n o w . T O D O : c u t t i n g
* losses if group is taking damage , losing ships , running
* low on consumables , etc . * /
2013-07-26 18:52:11 +01:00
return true ;
2013-08-01 22:40:23 +01:00
}
return false ;
}
AILib . prototype . conditionPiratesCanBePaidOff = function ( )
{
if ( this . ship . AIScript . oolite _intership . cargodemandpaid )
{
return false ;
}
// TODO: need some way for the player to set this
if ( ! this . ship . AIScript . oolite _intership . cargodemand )
{
return false ;
}
if ( this . ship . cargoSpaceUsed < this . ship . AIScript . oolite _intership . cargodemand )
{
return false ;
}
return true ;
2013-07-26 18:52:11 +01:00
}
/*** Scanner conditions ***/
AILib . prototype . conditionScannerContainsEscapePods = function ( )
{
2013-08-01 22:40:23 +01:00
return this . checkScannerWithPredicate ( function ( s ) {
return s . primaryRole == "escape-capsule" && s . isInSpace && s . scanClass == "CLASS_CARGO" && s . velocity . magnitude ( ) < this . ship . maxSpeed && this . conditionCanScoopCargo ( ) ;
} ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-14 19:02:16 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionScannerContainsFineableOffender = function ( )
{
2013-08-01 22:40:23 +01:00
return this . checkScannerWithPredicate ( function ( s ) {
var threshold = this . fineThreshold ( ) ;
return s . isInSpace && s . bounty <= threshold && s . bounty > 0 && ! s . markedForFines && ( s . scanClass == "CLASS_NEUTRAL" || s . isPlayer ) && ! s . isDerelict ;
} ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionScannerContainsFugitive = function ( )
{
2013-08-01 22:40:23 +01:00
return this . checkScannerWithPredicate ( function ( s ) {
return s . isInSpace && s . bounty > 50 && s . scanClass != "CLASS_CARGO" && s . scanClass != "CLASS_ROCK" ;
} ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionScannerContainsHuntableOffender = function ( )
2013-08-05 15:42:26 +01:00
{
return this . checkScannerWithPredicate ( function ( s ) {
var threshold = this . fineThreshold ( ) / 2 ;
return s . isInSpace && s . bounty > threshold && s . scanClass != "CLASS_CARGO" && s . scanClass != "CLASS_ROCK" ;
} ) ;
}
AILib . prototype . conditionScannerContainsSeriousOffender = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
return this . checkScannerWithPredicate ( function ( s ) {
var threshold = this . fineThreshold ( ) ;
return s . isInSpace && s . bounty > threshold && s . scanClass != "CLASS_CARGO" && s . scanClass != "CLASS_ROCK" ;
} ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionScannerContainsHunters = function ( )
{
2013-08-01 22:40:23 +01:00
return this . checkScannerWithPredicate ( function ( s ) {
return s . primaryRole == "hunter" || s . scanClass == "CLASS_POLICE" || ( s . isStation && s . isMainStation ) ;
} ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionScannerContainsMiningOpportunity = function ( )
{
2013-08-01 22:40:23 +01:00
// if hold full, no
if ( ! this . conditionCanScoopCargo ( ) )
{
return false ;
}
// need a mining laser, and for now a forward one
if ( ! this . ship . forwardWeapon == "EQ_WEAPON_MINING_LASER" )
{
return false ;
}
return this . conditionScannerContainsRocks ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-14 19:02:16 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionScannerContainsNonThargoid = function ( )
{
2013-08-01 22:40:23 +01:00
var prioritytargets = this . checkScannerWithPredicate ( function ( s ) {
return s . scanClass != "CLASS_THARGOID" && s . scanClass != "CLASS_ROCK" && s . scanClass != "CLASS_CARGO" ;
} ) ;
if ( prioritytargets )
{
return true ;
}
return this . checkScannerWithPredicate ( function ( s ) {
return s . scanClass != "CLASS_THARGOID" ;
} ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-22 21:24:27 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionScannerContainsPirateVictims = function ( )
{
2013-08-01 22:40:23 +01:00
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 || ! s . AIScript . oolite _intership || ! s . AIScript . oolite _intership . cargodemandpaid ) ;
} ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionScannerContainsReadyThargoidMothership = function ( )
{
2013-08-01 22:40:23 +01:00
return this . checkScannerWithPredicate ( function ( s ) {
return s . hasRole ( "thargoid-mothership" ) && ( ! s . escortGroup || s . escortGroup . count <= 16 ) ;
} ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-22 21:24:27 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionScannerContainsRocks = function ( )
{
2013-08-01 22:40:23 +01:00
var scan1 = this . checkScannerWithPredicate ( function ( s ) {
return s . isInSpace && s . isBoulder ;
} ) ;
if ( scan1 )
{
return true ;
}
// no boulders, what about asteroids?
return this . checkScannerWithPredicate ( function ( s ) {
return s . isInSpace && s . hasRole ( "asteroid" ) ;
} ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionScannerContainsSalvage = function ( )
{
2013-08-01 22:40:23 +01:00
return this . checkScannerWithPredicate ( function ( s ) {
return s . isInSpace && s . scanClass == "CLASS_CARGO" ;
} ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-25 20:09:53 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionScannerContainsSalvageForGroup = function ( )
{
2013-08-01 22:40:23 +01:00
var maxspeed = 0 ;
if ( this . conditionCanScoopCargo ( ) )
{
maxspeed = this . ship . maxSpeed ;
}
if ( this . ship . group )
{
for ( var i = 0 ; i < this . ship . group . ships . length ; i ++ )
2013-07-13 23:18:05 +01:00
{
2013-08-01 22:40:23 +01:00
var ship = this . ship . group . ships [ i ] ;
if ( ship . cargoSpaceAvailable > 0 && ship . equipmentStatus ( "EQ_FUEL_SCOOPS" ) == "EQUIPMENT_OK" && ship . maxSpeed > maxspeed )
{
maxspeed = ship . maxSpeed ;
}
2013-07-13 23:18:05 +01:00
}
2013-08-01 22:40:23 +01:00
}
return this . checkScannerWithPredicate ( function ( s ) {
return s . isInSpace && s . scanClass == "CLASS_CARGO" && s . velocity . magnitude ( ) < maxspeed ;
} ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-13 23:18:05 +01:00
2013-07-22 21:24:27 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionScannerContainsSalvageForMe = function ( )
{
2013-08-01 22:40:23 +01:00
if ( ! this . conditionCanScoopCargo ( ) )
{
return false ;
}
return this . checkScannerWithPredicate ( function ( s ) {
return s . isInSpace && s . scanClass == "CLASS_CARGO" && s . velocity . magnitude ( ) < this . ship . maxSpeed ;
} ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-13 23:18:05 +01:00
2013-07-21 11:10:47 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionScannerContainsShipNeedingEscort = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . ship . bounty == 0 )
{
return this . checkScannerWithPredicate ( function ( s ) {
return s . scanClass == this . ship . scanClass && s . bounty == 0 && ( ! s . escortGroup || s . escortGroup . count <= s . maxEscorts ) ;
} ) ;
}
else
{
return this . checkScannerWithPredicate ( function ( s ) {
return s . scanClass == this . ship . scanClass && s . bounty > 0 && ( ! s . escortGroup || s . escortGroup . count <= s . maxEscorts ) ;
} ) ;
}
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionScannerContainsThargoidMothership = function ( )
{
2013-08-01 22:40:23 +01:00
return this . checkScannerWithPredicate ( function ( s ) {
return s . hasRole ( "thargoid-mothership" ) ;
} ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-21 11:10:47 +01:00
2013-07-26 18:52:11 +01:00
/*** State conditions ***/
AILib . prototype . conditionAllEscortsInFlight = function ( )
{
2013-08-01 22:40:23 +01:00
if ( ! this . ship . escortGroup )
{
return true ; // there are no escorts not in flight
}
for ( var i = 0 ; i < this . ship . escortGroup . ships . length ; i ++ )
{
if ( this . ship . escortGroup . ships [ i ] . status != "STATUS_IN_FLIGHT" )
2013-07-13 23:18:05 +01:00
{
2013-08-01 22:40:23 +01:00
return false ;
2013-07-27 21:40:07 +01:00
}
2013-08-01 22:40:23 +01:00
}
// if just exited witchspace, escorts might not have rejoined escort
// group yet.
if ( ! this . ship . group )
{
return true ;
}
for ( var i = 0 ; i < this . ship . group . ships . length ; i ++ )
{
if ( this . ship . group . ships [ i ] . status != "STATUS_IN_FLIGHT" )
2013-07-27 21:40:07 +01:00
{
2013-08-01 22:40:23 +01:00
return false ;
2013-07-27 21:40:07 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-27 21:40:07 +01:00
2013-08-01 22:40:23 +01:00
return true ;
2013-07-26 18:52:11 +01:00
}
2013-07-13 23:18:05 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionCanScoopCargo = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . ship . cargoSpaceAvailable == 0 || this . ship . equipmentStatus ( "EQ_FUEL_SCOOPS" ) != "EQUIPMENT_OK" )
{
return false ;
}
return true ;
2013-07-26 18:52:11 +01:00
}
2013-07-14 19:02:16 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionCargoIsProfitableHere = function ( )
{
2013-08-06 18:40:15 +01:00
/ * T O D O : t h i s s h o u l d b e h a n d l e d i n t h e t r a d e r A I i t s e l f r a t h e r
* than using this condition . This is a temporary hack to test
* other bits * /
if ( this . ship . AIScript . oolite _intership . dest _system && this . ship . AIScript . oolite _intership . dest _system == system . ID )
{
return true ;
}
if ( this . ship . AIScript . oolite _intership . source _system && this . ship . AIScript . oolite _intership . source _system == system . ID )
{
return false ;
}
2013-08-01 22:40:23 +01:00
/ * T O D O : i n t h e M a i n l y X s y s t e m s , i t ' s n o t i m p o s s i b l e f o r
* PLENTIFUL _GOODS to generate a hold which is profitable in that
* system , and SCARCE _GOODS not to do so . Cargo should never be
* profitable in its origin system . * /
2013-07-27 21:40:07 +01:00
2013-08-01 22:40:23 +01:00
if ( ! system . mainStation )
{
return false ;
}
if ( this . ship . cargoSpaceUsed == 0 )
{
return false ;
}
var cargo = this . ship . cargoList ;
var profit = 0 ;
var multiplier = ( system . info . economy <= 3 ) ? - 1 : 1 ;
for ( var i = 0 ; i < cargo . length ; i ++ )
{
var commodity = cargo [ i ] . commodity ;
var quantity = cargo [ i ] . quantity ;
var adjust = system . mainStation . market [ commodity ] . marketEcoAdjustPrice * multiplier * quantity / system . mainStation . market [ commodity ] . marketMaskPrice ;
profit += adjust ;
}
return ( profit >= 0 ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionGroupLeaderIsStation = function ( )
{
2013-08-01 22:40:23 +01:00
return ( this . ship . group && this . ship . group . leader && this . ship . group . leader . isStation ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionHasInterceptCoordinates = function ( )
{
2013-08-01 22:40:23 +01:00
return ( this . getParameter ( "oolite_interceptCoordinates" ) != null ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionHasMothership = function ( )
{
2013-08-01 22:40:23 +01:00
return ( this . ship . group && this . ship . group . leader && this . ship . group . leader != this . ship ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionHasNonThargoidTarget = function ( )
{
2013-08-01 22:40:23 +01:00
return ( this . ship . target && this . ship . target . scanClass != "CLASS_THARGOID" ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-14 19:02:16 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionHasReceivedDistressCall = function ( )
{
2013-08-01 22:40:23 +01:00
var aggressor = this . getParameter ( "oolite_distressAggressor" ) ;
var sender = this . getParameter ( "oolite_distressSender" ) ;
var ts = this . getParameter ( "oolite_distressTimestamp" ) ;
2013-07-26 18:52:11 +01:00
2013-08-01 22:40:23 +01:00
if ( aggressor == null || ! aggressor . isInSpace || sender == null || ! sender . isInSpace || sender . position . distanceTo ( this . ship ) > this . ship . scannerRange || ts + 30 < clock . adjustedSeconds )
{
// no, or it has expired
this . setParameter ( "oolite_distressAggressor" , null ) ;
this . setParameter ( "oolite_distressSender" , null ) ;
this . setParameter ( "oolite_distressTimestamp" , null ) ;
return false ;
}
return true ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionHasTarget = function ( )
{
2013-08-01 22:40:23 +01:00
return this . ship . target != null ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionHasWaypoint = function ( )
{
2013-08-01 22:40:23 +01:00
return this . getParameter ( "oolite_waypoint" ) != null ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionIsActiveThargon = function ( )
{
2013-08-01 22:40:23 +01:00
return this . ship . scanClass == "CLASS_THARGOID" && this . ship . hasRole ( "EQ_THARGON" ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-15 23:14:47 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionIsEscorting = function ( )
{
2013-08-01 22:40:23 +01:00
if ( ! this . ship . group || ! this . ship . group . leader || this . ship . group . leader == this . ship )
{
return false ;
}
if ( this . ship . group . leader . escortGroup && this . ship . group . leader . escortGroup . containsShip ( this . ship ) )
{
if ( this . ship . group . leader . status == "STATUS_ENTERING_WITCHSPACE" )
{
var hole = this . getParameter ( "oolite_witchspaceWormhole" ) ;
if ( hole == null || hole . expiryTime < clock . seconds )
{
// has been left behind
this . configurationLeaveEscortGroup ( ) ;
this . setParameter ( "oolite_witchspaceWormhole" , false ) ;
2013-07-18 23:00:23 +01:00
return false ;
2013-08-01 22:40:23 +01:00
}
2013-07-18 23:00:23 +01:00
}
2013-08-01 22:40:23 +01:00
return true ;
}
return false ;
2013-07-26 18:52:11 +01:00
}
2013-07-14 19:02:16 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . conditionIsGroupLeader = function ( )
{
2013-08-01 22:40:23 +01:00
if ( ! this . ship . group )
{
return true ;
}
return ( this . ship . group . leader == this . ship ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionMissileOutOfFuel = function ( )
{
2013-08-01 22:40:23 +01:00
var range = 30000 ; // 30 km default
if ( this . ship . scriptInfo . oolite _missile _range )
{
range = this . ship . scriptInfo . oolite _missile _range ;
}
return range < this . ship . distanceTravelled ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . conditionWitchspaceEntryRequested = function ( )
{
2013-08-01 22:40:23 +01:00
return ( this . getParameter ( "oolite_witchspaceWormhole" ) != null ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-15 23:14:47 +01:00
2013-07-26 18:52:11 +01:00
/* ****************** Behaviour functions ************** */
/ * B e h a v i o u r s . B e h a v i o u r s a r e e f f e c t i v e l y a s t a t e d e f i n i t i o n ,
* defining a set of events and responses . They are aided in this
* by the 'responses' , which mean that the event handlers for the
* behaviour within the definition can itself be templated . * /
AILib . prototype . behaviourApproachDestination = function ( )
{
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
2013-07-26 18:52:11 +01:00
2013-08-01 22:40:23 +01:00
handlers . shipAchievedDesiredRange = function ( )
{
var waypoints = this . getParameter ( "oolite_waypoints" ) ;
if ( waypoints != null )
2013-07-14 23:11:25 +01:00
{
2013-08-01 22:40:23 +01:00
if ( waypoints . length > 0 )
{
waypoints . pop ( ) ;
if ( waypoints . length == 0 )
2013-07-14 23:11:25 +01:00
{
2013-08-01 22:40:23 +01:00
waypoints = null ;
2013-07-14 23:11:25 +01:00
}
2013-08-01 22:40:23 +01:00
this . setParameter ( "oolite_waypoints" , waypoints ) ;
}
}
else
{
var patrol = this . getParameter ( "oolite_waypoint" ) ;
if ( patrol != null && this . ship . destination . distanceTo ( patrol ) < 1000 + this . getParameter ( "oolite_waypointRange" ) )
{
// finished patrol to waypoint
// clear route
this . communicate ( "oolite_waypointReached" , { } , 3 ) ;
this . setParameter ( "oolite_waypoint" , null ) ;
this . setParameter ( "oolite_waypointRange" , null ) ;
if ( this . getParameter ( "oolite_flag_patrolStation" ) )
2013-07-14 23:11:25 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group )
{
var station = this . ship . group . leader ;
if ( station != null && station . isStation )
2013-07-14 23:11:25 +01:00
{
2013-08-01 22:40:23 +01:00
this . communicate ( "oolite_patrolReportIn" , this . entityCommsParams ( station ) , 4 ) ;
this . ship . patrolReportIn ( station ) ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
this . reconsiderNow ( ) ;
} ;
var waypoints = this . getParameter ( "oolite_waypoints" ) ;
if ( waypoints != null )
{
this . ship . destination = waypoints [ waypoints . length - 1 ] ;
this . ship . desiredRange = 1000 ;
}
var blocker = this . ship . checkCourseToDestination ( ) ;
if ( blocker )
{
if ( blocker . isPlanet || blocker . isSun )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
// the selected planet can't block
if ( blocker . isSun || this . getParameter ( "oolite_selectedPlanet" ) != blocker )
{
if ( this . ship . position . distanceTo ( blocker ) < blocker . radius * 3 )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( waypoints == null )
{
waypoints = [ ] ;
}
waypoints . push ( this . ship . getSafeCourseToDestination ( ) ) ;
this . ship . destination = waypoints [ waypoints . length - 1 ] ;
this . ship . desiredRange = 1000 ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
}
else if ( blocker . isShip )
{
if ( this . ship . position . distanceTo ( blocker ) < 25600 )
{
if ( ! blocker . group || ! blocker . group . leader == this . ship )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
// our own escorts are not a blocker!
if ( waypoints == null )
{
waypoints = [ ] ;
}
waypoints . push ( this . ship . getSafeCourseToDestination ( ) ) ;
this . ship . destination = waypoints [ waypoints . length - 1 ] ;
this . ship . desiredRange = 1000 ;
2013-07-14 23:11:25 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-14 23:11:25 +01:00
}
2013-08-01 22:40:23 +01:00
}
this . setParameter ( "oolite_waypoints" , waypoints ) ;
this . applyHandlers ( handlers ) ;
this . ship . performFlyToRangeFromDestination ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-14 23:11:25 +01:00
2013-07-26 18:52:11 +01:00
2013-07-27 15:39:23 +01:00
AILib . prototype . behaviourAvoidCascadeExplosion = function ( )
{
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
this . applyHandlers ( handlers ) ;
2013-07-27 15:39:23 +01:00
2013-08-01 22:40:23 +01:00
var cascade = this . getParameter ( "oolite_cascadeDetected" ) ;
if ( cascade != null )
{
if ( cascade . distanceTo ( this . ship ) < 25600 )
2013-07-27 15:39:23 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . defenseTargets . length > 0 && this . ship . defenseTargets [ 0 ] . scanClass == "CLASS_MINE" )
{
// if the mine is still visible, conventional fleeing works
2013-08-02 22:16:54 +01:00
this . communicate ( "oolite_quiriumCascade" , { } , 3 ) ;
2013-08-01 22:40:23 +01:00
this . ship . target = this . ship . defenseTargets [ 0 ] ;
this . ship . desiredRange = 30000 ;
this . ship . performFlee ( ) ;
return ;
}
else
{
if ( this . ship . destination != cascade )
2013-07-27 15:39:23 +01:00
{
2013-08-01 22:40:23 +01:00
this . communicate ( "oolite_quiriumCascade" , { } , 3 ) ;
2013-07-27 15:39:23 +01:00
}
2013-08-01 22:40:23 +01:00
this . ship . destination = cascade ;
this . ship . desiredRange = 30000 ;
this . ship . desiredSpeed = 10 * this . ship . maxSpeed ;
this . ship . performFlyToRangeFromDestination ( ) ;
return ;
}
2013-07-27 15:39:23 +01:00
}
2013-08-01 22:40:23 +01:00
else
{
this . setParameter ( "oolite_cascadeDetected" , null ) ;
}
}
2013-07-27 15:39:23 +01:00
}
2013-07-26 18:52:11 +01:00
AILib . prototype . behaviourBecomeInactiveThargon = function ( )
{
2013-08-01 22:40:23 +01:00
this . applyHandlers ( { } ) ;
this . ship . scanClass = "CLASS_CARGO" ;
this . ship . target = null ;
this . ship . clearDefenseTargets ( ) ;
if ( this . ship . group )
{
this . ship . group . removeShip ( this . ship ) ;
this . ship . group = null ;
}
if ( this . ship . escortGroup )
{
this . ship . escortGroup . removeShip ( this . ship ) ;
}
this . ship . desiredSpeed = 0 ;
this . ship . performStop ( ) ;
2013-08-04 12:29:44 +01:00
var nearby = this . ship . checkScanner ( true ) ;
2013-08-01 22:40:23 +01:00
for ( var i = 0 ; i < nearby . length ; i ++ )
{
var ship = nearby [ i ] ;
if ( ship . target == this . ship && ! ship . isPlayer && ship . hasHostileTarget )
{
ship . target = null ;
}
ship . removeDefenseTarget ( this . ship ) ;
}
2013-07-26 18:52:11 +01:00
}
AILib . prototype . behaviourCollectSalvage = function ( )
{
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
handlers . shipScoopedOther = function ( other )
{
2013-08-03 14:55:48 +01:00
this . communicate ( "oolite_scoopedCargo" , { "oolite_goodsDescription" : displayNameForCommodity ( other . commodity ) } , 4 ) ;
2013-08-01 22:40:23 +01:00
this . setParameter ( "oolite_cargoDropped" , null ) ;
this . reconsiderNow ( ) ;
}
this . applyHandlers ( handlers ) ;
this . ship . performCollect ( ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . behaviourDestroyCurrentTarget = function ( )
{
2013-08-01 22:40:23 +01:00
this . setParameter ( "oolite_witchspaceEntry" , null ) ;
2013-07-14 23:11:25 +01:00
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
this . applyHandlers ( handlers ) ;
2013-08-04 12:29:44 +01:00
if ( this . getParameter ( "oolite_flag_noSpecialThargoidReaction" ) != null )
{
if ( this . ship . scanClass != "CLASS_THARGOID" && this . ship . target . scanClass != "CLASS_THARGOID" && this . ship . target . target . scanClass == "CLASS_THARGOID" )
{
2013-08-04 14:20:20 +01:00
this . respondToThargoids ( this . ship . target . target , true ) ;
2013-08-04 12:29:44 +01:00
this . ship . performAttack ( ) ;
return ;
}
}
2013-08-01 22:40:23 +01:00
if ( this . ship . target && ! this . ship . hasHostileTarget )
{
// entering attack mode
2013-08-02 22:16:54 +01:00
this . communicate ( "oolite_beginningAttack" , this . entityCommsParams ( this . ship . target ) , 3 ) ;
2013-08-01 22:40:23 +01:00
}
2013-08-04 12:29:44 +01:00
else if ( this . ship . target )
2013-08-01 22:40:23 +01:00
{
this . communicate ( "oolite_continuingAttack" , this . entityCommsParams ( this . ship . target ) , 4 ) ;
}
this . ship . performAttack ( ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . behaviourDockWithStation = function ( )
{
2013-08-01 22:40:23 +01:00
// may need to release escorts
if ( this . ship . escortGroup && this . ship . escortGroup . count > 1 )
{
this . ship . dockEscorts ( ) ;
}
var station = this . getParameter ( "oolite_dockingStation" ) ;
this . ship . target = station ;
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
this . responsesAddDocking ( handlers ) ;
this . ship . requestDockingInstructions ( ) ;
if ( ! this . ship . dockingInstructions )
{
this . ship . performIdle ( ) ;
this . reconsiderNow ( ) ;
return ;
}
switch ( this . ship . dockingInstructions . ai _message )
{
case "TOO_BIG_TO_DOCK" :
case "DOCKING_REFUSED" :
this . ship . setParameter ( "oolite_dockingStation" , null ) ;
this . ship . target = null ;
this . reconsiderNow ( ) ;
break ;
case "TRY_AGAIN_LATER" :
if ( this . ship . target . position . distanceTo ( this . ship ) < 10000 )
{
this . ship . destination = this . ship . target . position ;
this . ship . desiredRange = 12500 ;
this . ship . desiredSpeed = this . cruiseSpeed ( ) ;
this . ship . performFlyToRangeFromDestination ( ) ;
break ;
}
// else fall through
case "HOLD_POSITION" :
2013-08-02 22:16:54 +01:00
this . communicate ( "oolite_dockingWait" , { } , 4 ) ;
2013-08-01 22:40:23 +01:00
this . ship . destination = this . ship . target . position ;
this . ship . performFaceDestination ( ) ;
// and will reconsider in a little bit
break ;
case "APPROACH" :
case "APPROACH_COORDINATES" :
case "BACK_OFF" :
this . ship . performFlyToRangeFromDestination ( ) ;
break ;
}
this . applyHandlers ( handlers ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-14 23:11:25 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . behaviourEnterWitchspace = function ( )
{
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
var wormhole = this . getParameter ( "oolite_witchspaceWormhole" ) ;
if ( wormhole && wormhole . expiryTime < clock . adjustedSeconds )
{
// the wormhole we were trying for has expired
this . setParameter ( "oolite_witchspaceWormhole" , null ) ;
}
else if ( wormhole )
{
handlers . playerWillEnterWitchspace = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
var wormhole = this . getParameter ( "oolite_witchspaceWormhole" ) ;
if ( wormhole != null )
{
this . ship . enterWormhole ( wormhole ) ;
}
else
{
this . ship . enterWormhole ( ) ;
}
}
this . ship . destination = wormhole . position ;
this . ship . desiredRange = 0 ;
this . ship . desiredSpeed = this . ship . maxSpeed ;
this . ship . performFlyToRangeFromDestination ( ) ;
this . applyHandlers ( handlers ) ;
return ;
}
var destID = this . getParameter ( "oolite_witchspaceDestination" ) ;
if ( destID == null )
{
// look for wormholes out of here
// no systems in range
handlers . wormholeSuggested = function ( hole )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . destination = hole . position ;
this . ship . desiredRange = 0 ;
this . ship . desiredSpeed = this . ship . maxSpeed ;
this . ship . performFlyToRangeFromDestination ( ) ;
this . setParameter ( "oolite_witchspaceWormhole" , hole ) ;
// don't reconsider
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
handlers . playerWillEnterWitchspace = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
var wormhole = this . getParameter ( "oolite_witchspaceWormhole" ) ;
if ( wormhole != null )
{
this . ship . enterWormhole ( wormhole ) ;
}
else
{
this . ship . enterWormhole ( ) ;
}
}
this . applyHandlers ( handlers ) ;
return ;
}
else
{
handlers . shipWitchspaceBlocked = function ( blocker )
{
2013-08-02 22:16:54 +01:00
this . communicate ( "oolite_witchspaceBlocked" , this . entityCommsParams ( blocker ) , 3 ) ;
2013-08-01 22:40:23 +01:00
this . ship . setDestination = blocker . position ;
this . ship . setDesiredRange = 30000 ;
this . ship . setDesiredSpeed = this . cruiseSpeed ( ) ;
this . ship . performFlyToRangeFromDestination ( ) ;
this . setParameter ( "oolite_witchspaceEntry" , null ) ;
// no reconsidering yet
}
// set up the handlers before trying it
this . applyHandlers ( handlers ) ;
var entry = this . getParameter ( "oolite_witchspaceEntry" ) ;
// wait for escorts to launch
if ( ! this . conditionAllEscortsInFlight ( ) )
{
this . ship . destination = this . ship . position ;
this . ship . desiredRange = 10000 ;
this . ship . desiredSpeed = this . cruiseSpeed ( ) ;
if ( this . ship . checkCourseToDestination ( ) )
{
this . ship . destination = this . ship . getSafeCourseToDestination ( ) ;
}
this . ship . performFlyToRangeFromDestination ( ) ;
}
else if ( entry != null && entry < clock . seconds )
{
// this should work
var result = this . ship . exitSystem ( destID ) ;
// if it doesn't, we'll get blocked
if ( result )
{
this . setParameter ( "oolite_witchspaceEntry" , null ) ;
}
2013-07-14 23:11:25 +01:00
}
2013-07-26 18:52:11 +01:00
else
2013-07-14 23:11:25 +01:00
{
2013-08-01 22:40:23 +01:00
if ( entry == null )
{
this . communicate ( "oolite_engageWitchspaceDrive" , { } , 4 ) ;
this . setParameter ( "oolite_witchspaceEntry" , clock . seconds + 15 ) ;
}
this . ship . destination = this . ship . position ;
this . ship . desiredRange = 10000 ;
this . ship . desiredSpeed = this . cruiseSpeed ( ) ;
if ( this . ship . checkCourseToDestination ( ) )
{
this . ship . destination = this . ship . getSafeCourseToDestination ( ) ;
}
this . ship . performFlyToRangeFromDestination ( ) ;
2013-07-14 23:11:25 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
AILib . prototype . behaviourEscortMothership = function ( )
{
2013-08-01 22:40:23 +01:00
var handlers = { } ;
2013-08-02 22:16:54 +01:00
if ( this . ship . group . leader )
{
this . communicate ( "oolite_escortFormation" , this . entityCommsParams ( this . ship . group . leader ) , 4 ) ;
}
2013-08-01 22:40:23 +01:00
this . responsesAddStandard ( handlers ) ;
this . responsesAddEscort ( handlers ) ;
this . applyHandlers ( handlers ) ;
this . ship . desiredRange = 0 ;
this . ship . performEscort ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-14 23:11:25 +01:00
2013-07-21 11:10:47 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . behaviourFineCurrentTarget = function ( )
{
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
this . applyHandlers ( handlers ) ;
if ( this . ship . scanClass == "CLASS_POLICE" && this . ship . target )
{
this . communicate ( "oolite_markForFines" , this . entityCommsParams ( this . ship . target ) , 1 ) ;
2013-07-26 18:52:11 +01:00
2013-08-01 22:40:23 +01:00
this . ship . markTargetForFines ( ) ;
}
2013-07-24 21:56:50 +01:00
2013-08-01 22:40:23 +01:00
this . ship . performIdle ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-12 19:44:14 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . behaviourFleeCombat = function ( )
{
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
this . applyHandlers ( handlers ) ;
2013-07-13 11:36:28 +01:00
2013-08-01 22:40:23 +01:00
var cascade = this . getParameter ( "oolite_cascadeDetected" ) ;
if ( cascade != null )
{
if ( cascade . distanceTo ( this . ship ) < 25600 )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . defenseTargets . length > 0 && this . ship . defenseTargets [ 0 ] . scanClass == "CLASS_MINE" )
{
// if the mine is still visible, conventional fleeing works
this . ship . target = this . ship . defenseTargets [ 0 ] ;
this . ship . desiredRange = 30000 ;
this . ship . performFlee ( ) ;
return ;
}
else
{
if ( this . ship . destination != cascade )
2013-07-13 11:36:28 +01:00
{
2013-08-01 22:40:23 +01:00
this . communicate ( "oolite_quiriumCascade" , { } , 4 ) ;
2013-07-13 11:36:28 +01:00
}
2013-08-01 22:40:23 +01:00
this . ship . destination = cascade ;
this . ship . desiredRange = 30000 ;
this . ship . desiredSpeed = 10 * this . ship . maxSpeed ;
this . ship . performFlyToRangeFromDestination ( ) ;
return ;
}
2013-07-12 19:44:14 +01:00
}
2013-08-01 22:40:23 +01:00
else
2013-07-12 19:44:14 +01:00
{
2013-08-01 22:40:23 +01:00
this . setParameter ( "oolite_cascadeDetected" , null ) ;
2013-07-12 19:44:14 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-08-05 14:14:50 +01:00
if ( this . ship . AIPrimaryAggressor && this . ship . AIPrimaryAggressor . isInSpace && this . ship . AIPrimaryAggressor . position . distanceTo ( this . ship ) < this . ship . scannerRange )
{
this . ship . target = this . ship . AIPrimaryAggressor ;
}
2013-08-01 22:40:23 +01:00
if ( ! this . ship . target || this . ship . position . distanceTo ( this . ship . target ) > 25600 )
{
var dts = this . ship . defenseTargets ;
for ( var i = 0 ; i < dts . length ; i ++ )
{
2013-08-02 22:16:54 +01:00
if ( this . ship . position . distanceTo ( dts [ i ] ) < 25600 && this . isFighting ( dts [ i ] ) )
{
this . ship . target = dts [ i ] ;
break ;
}
2013-08-01 22:40:23 +01:00
}
}
2013-08-02 22:16:54 +01:00
if ( this . getParameter ( "oolite_lastFleeing" ) != null )
{
this . communicate ( "oolite_continueFleeing" , this . entityCommsParams ( this . ship . target ) , 4 ) ;
}
2013-08-05 14:14:50 +01:00
else if ( this . ship . energy < this . ship . maxEnergy / 4 )
2013-08-02 22:16:54 +01:00
{
this . communicate ( "oolite_startFleeing" , this . entityCommsParams ( this . ship . target ) , 3 ) ;
}
2013-08-05 14:14:50 +01:00
if ( this . ship . target )
{
this . setParameter ( "oolite_lastFleeing" , this . ship . target ) ;
}
2013-08-01 22:40:23 +01:00
this . ship . desiredRange = this . ship . scannerRange ;
this . ship . performFlee ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-12 19:44:14 +01:00
2013-07-26 18:52:11 +01:00
/* Follow the group leader in a less organised way than escorting them */
AILib . prototype . behaviourFollowGroupLeader = function ( )
{
2013-08-01 22:40:23 +01:00
if ( ! this . ship . group || ! this . ship . group . leader )
{
2013-08-05 14:14:50 +01:00
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
this . applyHandlers ( handlers ) ;
2013-08-01 22:40:23 +01:00
this . ship . performIdle ( ) ;
}
else
{
this . ship . destination = this . ship . group . leader . position ;
this . ship . desiredRange = 2000 + Math . random ( ) * 2000 ;
this . ship . desiredSpeed = this . ship . maxSpeed ;
2013-08-05 14:14:50 +01:00
this . behaviourApproachDestination ( ) ;
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-07-13 23:18:05 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . behaviourGuardTarget = function ( )
{
2013-08-01 22:40:23 +01:00
if ( ! this . ship . target )
{
this . ship . destination = this . ship . position ;
}
else
{
this . ship . destination = this . ship . target . position ;
}
this . ship . desiredSpeed = this . cruiseSpeed ( ) ;
this . ship . desiredRange = 2500 ;
2013-08-05 14:14:50 +01:00
this . behaviourApproachDestination ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-18 23:00:23 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . behaviourLandOnPlanet = function ( )
{
2013-08-01 22:40:23 +01:00
this . ship . desiredSpeed = this . ship . maxSpeed / 4 ;
this . ship . performLandOnPlanet ( ) ;
this . ship . AIScriptWakeTime = 0 ; // cancel reconsiderations
this . applyHandlers ( { } ) ; // cancel interruptions
this . communicate ( "oolite_landingOnPlanet" , { } , 4 ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . behaviourLeaveVicinityOfTarget = function ( )
{
2013-08-01 22:40:23 +01:00
if ( ! this . ship . target )
{
this . reconsiderNow ( ) ;
return ;
}
this . ship . destination = this . ship . target . position ;
this . ship . desiredRange = 27500 ;
this . ship . desiredSpeed = this . ship . maxSpeed ;
2013-08-02 22:16:54 +01:00
this . communicate ( "oolite_leaveVicinity" , this . entityCommsParams ( this . ship . target ) , 3 ) ;
2013-08-05 14:14:50 +01:00
this . behaviourApproachDestination ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-22 21:24:27 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . behaviourMineTarget = function ( )
{
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
this . applyHandlers ( handlers ) ;
2013-08-02 22:16:54 +01:00
this . communicate ( "oolite_mining" , { } , 4 ) ;
2013-08-01 22:40:23 +01:00
this . ship . performMining ( ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . behaviourOfferToEscort = function ( )
{
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
this . applyHandlers ( handlers ) ;
var possible = this . getParameter ( "oolite_scanResultSpecific" ) ;
if ( possible == null )
{
this . reconsiderNow ( ) ;
}
else
{
if ( this . ship . offerToEscort ( possible ) )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
// accepted
this . reconsiderNow ( ) ;
2013-07-13 19:13:30 +01:00
}
2013-08-01 22:40:23 +01:00
// if rejected, wait for next scheduled reconsideration
}
2013-07-26 18:52:11 +01:00
}
2013-07-13 19:13:30 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . behaviourPayOffPirates = function ( )
{
2013-08-01 22:40:23 +01:00
this . ship . dumpCargo ( this . ship . AIScript . oolite _intership . cargodemand ) ;
2013-08-02 22:16:54 +01:00
this . communicate ( "oolite_agreeingToDumpCargo" , { "oolite_demandSize" : this . ship . AIScript . oolite _intership . cargodemand } , 1 ) ;
2013-08-01 22:40:23 +01:00
delete this . ship . AIScript . oolite _intership . cargodemand ;
this . ship . AIScript . oolite _intership . cargodemandpaid = true ;
this . behaviourFleeCombat ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-13 09:53:53 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . behaviourReconsider = function ( )
{
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
this . applyHandlers ( handlers ) ;
this . reconsiderNow ( ) ;
2013-07-26 18:52:11 +01:00
}
// Separate behaviour to EscortMothership in case we want to change it later
// This is the one to catch up with a distant mothership
AILib . prototype . behaviourRejoinMothership = function ( )
{
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
this . responsesAddEscort ( handlers ) ;
this . applyHandlers ( handlers ) ;
// to consider: should this behaviour use injectors if
// possible? so few escorts have them that it's probably not
// worth it.
this . ship . desiredRange = 0 ;
this . ship . performEscort ( ) ;
2013-07-26 18:52:11 +01:00
}
AILib . prototype . behaviourRepelCurrentTarget = function ( )
{
2013-08-01 22:40:23 +01:00
this . setParameter ( "oolite_witchspaceEntry" , null ) ;
2013-07-26 18:52:11 +01:00
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
this . applyHandlers ( handlers ) ;
if ( ! this . ship . target || ! this . ship . target . isValid || ! this . ship . target . isShip )
{
this . reconsiderNow ( ) ;
return ;
}
2013-08-04 12:29:44 +01:00
if ( this . getParameter ( "oolite_flag_noSpecialThargoidReaction" ) != null )
{
if ( this . ship . scanClass != "CLASS_THARGOID" && this . ship . target . scanClass != "CLASS_THARGOID" && this . ship . target . target . scanClass == "CLASS_THARGOID" )
{
2013-08-04 14:20:20 +01:00
this . respondToThargoids ( this . ship . target . target , true ) ;
2013-08-04 12:29:44 +01:00
this . ship . performAttack ( ) ;
return ;
}
}
2013-08-01 22:40:23 +01:00
if ( ! this . isAggressive ( this . ship . target ) )
{
var target = this . ship . target ;
// repelling succeeded
if ( this . ship . escortGroup )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
// also tell escorts to stop attacking it
for ( var i = 0 ; i < this . ship . escortGroup . ships . length ; i ++ )
{
this . ship . escortGroup . ships [ i ] . removeDefenseTarget ( target ) ;
if ( this . ship . escortGroup . ships [ i ] . target == target )
2013-07-13 09:53:53 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . escortGroup . ships [ i ] . target = null ;
2013-07-13 09:53:53 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-12 19:44:14 +01:00
}
2013-08-01 22:40:23 +01:00
this . ship . removeDefenseTarget ( target ) ;
this . ship . target = null ;
}
else
{
if ( ! this . ship . hasHostileTarget )
2013-07-12 23:00:39 +01:00
{
2013-08-01 22:40:23 +01:00
// entering attack mode
this . communicate ( "oolite_beginningAttack" , this . entityCommsParams ( this . ship . target ) , 3 ) ;
2013-07-12 23:00:39 +01:00
}
2013-08-02 22:16:54 +01:00
else if ( this . ship . target )
{
this . communicate ( "oolite_continuingAttack" , this . entityCommsParams ( this . ship . target ) , 4 ) ;
}
2013-08-01 22:40:23 +01:00
this . ship . performAttack ( ) ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-12 19:44:14 +01:00
2013-07-26 18:52:11 +01:00
/ * S t a n d a r d " h e l p t h e i n n o c e n t " d i s t r e s s c a l l r e s p o n s e . P e r h a p s
* there should be a 'blood in the water' response available
* too ... * /
AILib . prototype . behaviourRespondToDistressCall = function ( )
{
2013-08-01 22:40:23 +01:00
var aggressor = this . getParameter ( "oolite_distressAggressor" ) ;
var sender = this . getParameter ( "oolite_distressSender" ) ;
if ( aggressor && aggressor . isShip && sender && sender . isShip )
{
if ( sender . bounty > aggressor . bounty )
2013-07-13 19:13:30 +01:00
{
2013-08-01 22:40:23 +01:00
var tmp = sender ;
sender = aggressor ;
aggressor = tmp ;
2013-07-13 19:13:30 +01:00
}
2013-08-01 22:40:23 +01:00
if ( aggressor . position . distanceTo ( this . ship ) < this . ship . scannerRange )
{
this . ship . target = aggressor ;
this . ship . performAttack ( ) ;
this . reconsiderNow ( ) ;
this . communicate ( "oolite_distressResponseAggressor" , this . entityCommsParams ( aggressor ) , 2 ) ;
}
else
{ // we can't actually see what's attacking the sender yet
this . ship . destination = sender . position ;
this . ship . desiredRange = 1000 + sender . collisionRadius + this . ship . collisionRadius ;
this . ship . desiredSpeed = 7 * this . ship . maxSpeed ; // use injectors if possible
this . ship . performFlyToRangeFromDestination ( ) ;
// and when we next reconsider, hopefully the aggressor will be on the scanner
this . communicate ( "oolite_distressResponseSender" , this . entityCommsParams ( sender ) , 2 ) ;
}
}
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
this . applyHandlers ( handlers ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-13 19:13:30 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . behaviourRobTarget = function ( )
{
2013-08-01 22:40:23 +01:00
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 )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
demand = this . ship . group . leader . AIScript . oolite _intership . cargodemanded ;
2013-07-13 19:13:30 +01:00
}
2013-08-01 22:40:23 +01:00
}
else
{
if ( this . ship . AIScript . oolite _intership . cargodemanded )
2013-07-13 23:18:05 +01:00
{
2013-08-01 22:40:23 +01:00
demand = this . ship . AIScript . oolite _intership . cargodemanded ;
}
}
if ( demand == null )
{
var hascargo = this . ship . target . cargoSpaceUsed + this . ship . target . cargoSpaceAvailable ;
// blowing them up probably gets ~10%, so how much we feel
// confident in demanding depends on how likely patrols
// are to come along and interfere.
demand = ( hascargo / 20 ) ;
demand = demand * ( 1 + Math . random ( ) + ( 8 - system . info . government ) / 8 ) ;
// between 5% and 15% of cargo
demand = Math . ceil ( demand ) ; // round it up so there's always at least 1
var maxdemand = 0 ;
var gc = 1 ;
if ( ! this . ship . group )
{
if ( this . ship . equipmentStatus ( "EQ_FUEL_SCOOPS" ) == "EQUIPMENT_OK" )
{
maxdemand = this . ship . cargoSpaceAvailable ;
}
2013-07-13 23:18:05 +01:00
}
2013-08-01 22:40:23 +01:00
else
2013-07-13 23:18:05 +01:00
{
2013-08-01 22:40:23 +01:00
gc = this . ship . group . ships . length ;
for ( var i = 0 ; i < gc ; i ++ )
{
var ship = this . ship . group . ships [ i ] ;
if ( ship . equipmentStatus ( "EQ_FUEL_SCOOPS" ) == "EQUIPMENT_OK" )
2013-07-15 23:14:47 +01:00
{
2013-08-01 22:40:23 +01:00
maxdemand += ship . cargoSpaceAvailable ;
2013-07-13 23:18:05 +01:00
}
else
{
2013-08-01 22:40:23 +01:00
gc -- ; // this ship can't help scoop
2013-07-15 23:14:47 +01:00
}
2013-08-01 22:40:23 +01:00
}
}
if ( demand > maxdemand )
{
demand = maxdemand ; // don't ask for more than we can carry
}
while ( demand > gc * 5 )
{
// asking for more than 5TC each probably means there
// won't be time to pick it all up anyway
demand = Math . ceil ( demand / 2 ) ;
}
if ( demand < 2 )
{
demand = 2 ;
}
2013-07-15 23:14:47 +01:00
2013-08-01 22:40:23 +01:00
/* Record our demand with the group leader */
if ( this . ship . group && this . ship . group . leader )
{
this . ship . group . leader . AIScript . oolite _intership . cargodemanded = demand ;
2013-07-21 11:10:47 +01:00
}
2013-08-01 22:40:23 +01:00
else
{
this . ship . AIScript . oolite _intership . cargodemanded = demand ;
}
/* Inform the victim of the demand, if possible */
if ( this . ship . target . AIScript && this . ship . target . AIScript . oolite _intership )
{
this . ship . target . AIScript . oolite _intership . cargodemand = demand ;
}
var commsparams = this . entityCommsParams ( this . ship . target ) ;
commsparams [ "oolite_demandSize" ] = demand ;
this . communicate ( "oolite_makePirateDemand" , commsparams , 1 ) ;
2013-07-28 12:07:39 +01:00
this . ship . requestHelpFromGroup ( ) ;
2013-08-01 22:40:23 +01:00
/ * }
else
{
log ( this . ship . displayName , "Already asked for " + demand ) ; * /
}
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
this . applyHandlers ( handlers ) ;
this . ship . performAttack ( ) ;
this . ship . requestHelpFromGroup ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-21 11:10:47 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . behaviourSunskim = function ( )
{
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddStandard ( handlers ) ;
this . responsesAddScooping ( handlers ) ;
this . applyHandlers ( handlers ) ;
this . ship . performFlyToRangeFromDestination ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-21 23:25:41 +01:00
2013-07-23 21:21:49 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . behaviourTumble = function ( )
{
2013-08-01 22:40:23 +01:00
this . applyHandlers ( { } ) ;
this . ship . performTumble ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-27 12:29:59 +01:00
/* Missile behaviours: have different standard handler sets */
2013-07-26 18:52:11 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . behaviourMissileInterceptTarget = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddMissile ( handlers ) ;
this . applyHandlers ( handlers ) ;
if ( this . ship . scriptInfo . oolite _missile _proximity )
{
this . ship . desiredRange = this . ship . scriptInfo . oolite _missile _proximity ;
}
else
{
this . ship . desiredRange = 25 ;
}
2013-07-27 12:29:59 +01:00
2013-08-01 22:40:23 +01:00
this . ship . performIntercept ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-23 21:21:49 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . behaviourMissileInterceptCoordinates = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddMissile ( handlers ) ;
this . applyHandlers ( handlers ) ;
if ( this . ship . scriptInfo . oolite _missile _proximity )
{
this . ship . desiredRange = this . ship . scriptInfo . oolite _missile _proximity ;
}
else
{
this . ship . desiredRange = 25 ;
}
var dest = this . getParameter ( "oolite_interceptCoordinates" ) ;
if ( dest == null )
{
return ;
}
this . ship . destination = dest
this . ship . desiredSpeed = this . ship . maxSpeed ;
this . ship . performFlyToRangeFromDestination ( ) ;
// if we have an intercept target, try to restore it
var oldtarget = this . getParameter ( "oolite_interceptTarget" ) ;
if ( oldtarget && ! oldtarget . isCloaked && oldtarget . isInSpace )
{
this . ship . target = oldtarget ;
}
2013-07-27 12:29:59 +01:00
}
2013-07-26 18:52:11 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . behaviourMissileSelfDestruct = function ( ) {
2013-08-01 22:40:23 +01:00
this . ship . explode ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-27 12:29:59 +01:00
/* Station behaviours: have different standard handler sets */
AILib . prototype . behaviourStationLaunchDefenseShips = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . target && this . isAggressive ( this . ship . target ) )
{
this . alertCondition = 3 ;
this . ship . launchDefenseShip ( ) ;
2013-08-02 22:16:54 +01:00
this . communicate ( "oolite_launchDefenseShips" , this . entityCommsParams ( this . ship . target ) , 3 ) ;
2013-08-01 22:40:23 +01:00
this . ship . requestHelpFromGroup ( ) ;
}
var handlers = { } ;
this . responsesAddStation ( handlers ) ;
this . applyHandlers ( handlers ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-23 22:37:07 +01:00
2013-07-27 12:29:59 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . behaviourStationLaunchMiner = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . alertCondition > 1 )
{
this . alertCondition -- ;
}
var handlers = { } ;
this . responsesAddStation ( handlers ) ;
this . applyHandlers ( handlers ) ;
if ( this . ship . group )
{
for ( var i = 0 ; i < this . ship . group . ships . length ; i ++ )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group . ships [ i ] . primaryRole == "miner" )
{
// only one in flight at once
return ;
}
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-08-02 22:16:54 +01:00
this . communicate ( "oolite_launchMiner" , this . entityCommsParams ( this . ship . target ) , 3 ) ;
2013-08-01 22:40:23 +01:00
this . ship . launchMiner ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-23 22:37:07 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . behaviourStationLaunchPatrol = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . alertCondition > 1 )
{
this . alertCondition -- ;
}
var handlers = { } ;
this . responsesAddStation ( handlers ) ;
this . applyHandlers ( handlers ) ;
2013-07-23 21:21:49 +01:00
2013-08-01 22:40:23 +01:00
if ( this . ship . group )
{
for ( var i = 0 ; i < this . ship . group . ships . length ; i ++ )
2013-07-23 21:21:49 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group . ships [ i ] . primaryRole == this . getParameter ( "oolite_stationPatrolRole" ) )
{
// only one in flight at once
return ;
}
2013-07-23 21:21:49 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-08-02 22:16:54 +01:00
this . communicate ( "oolite_launchPatrol" , this . entityCommsParams ( this . ship . target ) , 3 ) ;
2013-08-01 22:40:23 +01:00
this . ship . launchPatrol ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-24 21:56:50 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . behaviourStationLaunchSalvager = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . alertCondition > 1 )
{
this . alertCondition -- ;
}
2013-08-02 22:16:54 +01:00
this . communicate ( "oolite_launchSalvager" , this . entityCommsParams ( this . ship . target ) , 3 ) ;
2013-08-01 22:40:23 +01:00
this . ship . launchScavenger ( ) ;
var handlers = { } ;
this . responsesAddStation ( handlers ) ;
this . applyHandlers ( handlers ) ;
2013-07-27 12:29:59 +01:00
}
2013-07-26 18:52:11 +01:00
AILib . prototype . behaviourStationManageTraffic = function ( )
{
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddStation ( handlers ) ;
this . applyHandlers ( handlers ) ;
2013-08-02 22:16:54 +01:00
if ( this . ship . hasNPCTraffic && ( ! system . sun || ! system . sun . isGoingNova ) )
2013-08-01 22:40:23 +01:00
{
if ( Math . random ( ) < 0.3 )
2013-07-24 21:56:50 +01:00
{
2013-08-01 22:40:23 +01:00
var trader = this . ship . launchShipWithRole ( "trader" ) ;
trader . setCargoType ( "PLENTIFUL_GOODS" ) ;
}
if ( Math . random ( ) < 0.1 )
{
this . ship . launchShuttle ( ) ;
2013-07-24 21:56:50 +01:00
}
2013-08-01 22:40:23 +01:00
// TODO: integrate with system repopulator rather than just
// launching ships at random
}
2013-07-26 18:52:11 +01:00
}
2013-07-24 21:56:50 +01:00
2013-07-26 18:52:11 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . behaviourStationRespondToDistressCall = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
var aggressor = this . getParameter ( "oolite_distressAggressor" ) ;
var sender = this . getParameter ( "oolite_distressSender" ) ;
if ( sender . bounty > aggressor . bounty )
{
var tmp = sender ;
sender = aggressor ;
aggressor = tmp ;
}
if ( aggressor . position . distanceTo ( this . ship ) < this . ship . scannerRange )
{
this . ship . target = aggressor ;
this . ship . alertCondition = 3 ;
this . ship . launchDefenseShip ( ) ;
this . communicate ( "oolite_distressResponseAggressor" , this . entityCommsParams ( aggressor ) , 2 ) ;
this . ship . requestHelpFromGroup ( ) ;
}
else
{
this . communicate ( "oolite_distressResponseSender" , this . entityCommsParams ( sender ) , 3 ) ;
}
2013-07-26 18:52:11 +01:00
2013-08-01 22:40:23 +01:00
var handlers = { } ;
this . responsesAddStation ( handlers ) ;
this . applyHandlers ( handlers ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-24 21:56:50 +01:00
2013-07-23 21:21:49 +01:00
2013-07-26 18:52:11 +01:00
/* ****************** Configuration functions ************** */
2013-07-12 19:44:14 +01:00
2013-07-26 18:52:11 +01:00
/ * C o n f i g u r a t i o n s . C o n f i g u r a t i o n s a r e s e t u p a c t i o n s f o r a b e h a v i o u r
* or behaviours . They can also be used on a fall - through conditional
* to set parameters for later tests * /
2013-07-12 19:44:14 +01:00
2013-07-27 12:29:59 +01:00
/*** Target acquisition configuration ***/
2013-07-26 18:52:11 +01:00
AILib . prototype . configurationAcquireCombatTarget = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . ship . target && this . allied ( this . ship , this . ship . target ) )
{
// don't shoot at allies even if they have ended up as a target...
this . ship . removeDefenseTarget ( this . ship . target ) ;
this . ship . target = null ;
}
if ( this . ship . target && this . ship . target . scanClass == "CLASS_CARGO" )
{
this . ship . target = null ;
}
/ * I f f t h e s h i p d o e s n o t c u r r e n t l y h a v e a t a r g e t , s e l e c t a n e w o n e
* from the defense target list . * /
if ( this . ship . target )
{
if ( this . ship . target . isInSpace )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
return ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
this . ship . removeDefenseTarget ( this . ship . target ) ;
this . ship . target = null ;
}
var dts = this . ship . defenseTargets
for ( var i = 0 ; i < dts . length ; i ++ )
{
if ( dts [ i ] . position . distanceTo ( this . ship ) < this . ship . scannerRange )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . target = dts [ 0 ] ;
return ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
if ( this . ship . group != null )
{
for ( var i = 0 ; i < this . ship . group . count ; i ++ )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group . ships [ i ] != this . ship )
{
if ( this . ship . group . ships [ i ] . target && this . isFighting ( this . ship . group . ships [ i ] ) && this . ship . group . ships [ i ] . target . position . distanceTo ( this . ship ) < this . ship . scannerRange )
2013-07-13 11:36:28 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . target = this . ship . group . ships [ i ] . target ;
return ;
2013-07-13 11:36:28 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
if ( this . ship . escortGroup != null )
{
for ( var i = 0 ; i < this . ship . escortGroup . count ; i ++ )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . escortGroup . ships [ i ] != this . ship )
{
if ( this . ship . escortGroup . ships [ i ] . target && this . isFighting ( this . ship . escortGroup . ships [ i ] ) && this . ship . escortGroup . ships [ i ] . target . position . distanceTo ( this . ship ) < this . ship . scannerRange )
2013-07-13 11:36:28 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . target = this . ship . escortGroup . ships [ i ] . target ;
return ;
2013-07-12 19:44:14 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-12 19:44:14 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-07-12 19:44:14 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationAcquireDefensiveEscortTarget = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group && this . ship . group . leader )
{
var leader = this . ship . group . leader ;
if ( leader . target && leader . target . target == leader && this . isFighting ( leader ) && leader . target . position . distanceTo ( this . ship ) < this . ship . scannerRange )
2013-07-27 12:29:59 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . target = leader . target ;
}
else
{
var dts = leader . defenseTargets ;
for ( var i = 0 ; i < dts . length ; i ++ )
{
if ( dts [ i ] . target == leader && this . isAggressive ( dts [ i ] ) && dts [ i ] . position . distanceTo ( this . ship ) < this . ship . scannerRange )
2013-07-27 12:29:59 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . target = dts [ i ] ;
2013-07-27 12:29:59 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-27 12:29:59 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-27 12:29:59 +01:00
}
// TODO: reuse code from AcquireCombatTarget better
AILib . prototype . configurationAcquireHostileCombatTarget = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . ship . target && this . allied ( this . ship , this . ship . target ) )
{
// don't shoot at allies even if they have ended up as a target...
this . ship . removeDefenseTarget ( this . ship . target ) ;
this . ship . target = null ;
}
/ * I f f t h e s h i p d o e s n o t c u r r e n t l y h a v e a t a r g e t , s e l e c t a n e w o n e
* from the defense target list . * /
if ( this . ship . target )
{
if ( this . ship . target . isInSpace && this . isAggressive ( this . ship . target ) )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
return ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
this . ship . removeDefenseTarget ( this . ship . target ) ;
this . ship . target = null ;
}
var dts = this . ship . defenseTargets
for ( var i = 0 ; i < dts . length ; i ++ )
{
if ( dts [ i ] . position . distanceTo ( this . ship ) < this . ship . scannerRange && this . isAggressive ( dts [ i ] ) )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . target = dts [ 0 ] ;
return ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
if ( this . ship . group != null )
{
for ( var i = 0 ; i < this . ship . group . count ; i ++ )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group . ships [ i ] != this . ship )
{
if ( this . ship . group . ships [ i ] . target && this . isFighting ( this . ship . group . ships [ i ] ) && this . ship . group . ships [ i ] . target . position . distanceTo ( this . ship ) < this . ship . scannerRange && this . isAggressive ( this . ship . group . ships [ i ] . target ) )
2013-07-15 23:14:47 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . target = this . ship . group . ships [ i ] . target ;
return ;
2013-07-15 23:14:47 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
if ( this . ship . escortGroup != null )
{
for ( var i = 0 ; i < this . ship . escortGroup . count ; i ++ )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . escortGroup . ships [ i ] != this . ship )
{
if ( this . ship . escortGroup . ships [ i ] . target && this . isFighting ( this . ship . escortGroup . ships [ i ] ) && this . ship . escortGroup . ships [ i ] . target . position . distanceTo ( this . ship ) < this . ship . scannerRange && this . isAggressive ( this . ship . escortGroup . ships [ i ] . target ) )
2013-07-15 23:14:47 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . target = this . ship . escortGroup . ships [ i ] . target ;
return ;
2013-07-15 23:14:47 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-15 23:14:47 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-07-15 23:14:47 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . configurationAcquireOffensiveEscortTarget = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group && this . ship . group . leader && this . ship . group . leader . target && this . ship . group . leader . hasHostileTarget )
{
if ( this . ship . position . distanceTo ( this . ship . group . leader . target ) < this . ship . scannerRange )
2013-07-13 23:18:05 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . target = this . ship . group . leader . target ;
this . ship . addDefenseTarget ( this . ship . target ) ;
2013-07-13 23:18:05 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-07-13 23:18:05 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationAcquirePlayerAsTarget = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . target = player . ship ;
2013-07-26 18:52:11 +01:00
}
2013-07-13 23:18:05 +01:00
2013-07-25 20:09:53 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationAcquireScannedTarget = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . target = this . getParameter ( "oolite_scanResultSpecific" ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-25 20:09:53 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . configurationCheckScanner = function ( )
{
2013-08-04 12:29:44 +01:00
if ( this . getParameter ( "oolite_flag_scanIgnoresUnpowered" ) != null )
{
this . setParameter ( "oolite_scanResults" , this . ship . checkScanner ( true ) ) ;
}
else
{
this . setParameter ( "oolite_scanResults" , this . ship . checkScanner ( ) ) ;
}
2013-08-01 22:40:23 +01:00
this . setParameter ( "oolite_scanResultSpecific" , null ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-13 19:13:30 +01:00
2013-07-25 20:09:53 +01:00
2013-07-27 12:29:59 +01:00
/*** Navigation configuration ***/
2013-08-05 14:14:50 +01:00
AILib . prototype . configurationSelectPlanet = function ( )
{
var possibles = system . planets ;
this . setParameter ( "oolite_selectedPlanet" , possibles [ Math . floor ( Math . random ( ) * possibles . length ) ] ) ;
}
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationSelectRandomTradeStation = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
var stations = system . stations ;
var threshold = 1E16 ;
var chosenStation = null ;
if ( this . ship . bounty == 0 )
{
if ( Math . random ( ) < 0.9 && this . friendlyStation ( system . mainStation ) )
2013-07-27 12:29:59 +01:00
{
2013-08-01 22:40:23 +01:00
this . setParameter ( "oolite_selectedStation" , system . mainStation ) ;
return ;
}
}
else if ( this . ship . bounty <= this . fineThreshold ( ) )
{
if ( Math . random ( ) < 0.5 && this . friendlyStation ( system . mainStation ) )
2013-07-27 12:29:59 +01:00
{
2013-08-01 22:40:23 +01:00
this . setParameter ( "oolite_selectedStation" , system . mainStation ) ;
return ;
2013-07-27 12:29:59 +01:00
}
2013-08-01 22:40:23 +01:00
}
var friendlies = 0 ;
for ( var i = 0 ; i < stations . length ; i ++ )
{
var station = stations [ i ] ;
if ( this . friendlyStation ( station ) )
2013-07-27 12:29:59 +01:00
{
2013-08-01 22:40:23 +01:00
friendlies ++ ;
// equivalent to filtering the list to only contain
// friendlies, then picking a random element.
if ( Math . random ( ) < 1 / friendlies )
{
chosenStation = station ;
}
2013-07-27 12:29:59 +01:00
}
2013-08-01 22:40:23 +01:00
}
this . setParameter ( "oolite_selectedStation" , chosenStation ) ;
this . communicate ( "oolite_selectedStation" , this . entityCommsParams ( chosenStation ) , 4 ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-13 19:13:30 +01:00
2013-07-27 12:29:59 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . configurationSelectShuttleDestination = function ( )
{
2013-08-01 22:40:23 +01:00
var possibles = system . planets . concat ( system . stations ) ;
var destinations1 = [ ] ;
var destinations2 = [ ] ;
for ( var i = 0 ; i < possibles . length ; i ++ )
{
var possible = possibles [ i ] ;
// travel at least a little way
var distance = possible . position . distanceTo ( this . ship ) ;
if ( distance > possible . collisionRadius + 10000 )
2013-07-21 23:25:41 +01:00
{
2013-08-01 22:40:23 +01:00
// must be friendly destination and not moving too fast
if ( possible . isPlanet || this . friendlyStation ( possible ) || possible . maxSpeed > this . ship . maxSpeed / 5 )
{
if ( distance > system . mainPlanet . radius * 5 )
2013-07-21 23:25:41 +01:00
{
2013-08-01 22:40:23 +01:00
destinations2 . push ( possible ) ;
2013-07-21 23:25:41 +01:00
}
2013-08-01 22:40:23 +01:00
else
{
destinations1 . push ( possible ) ;
}
}
}
}
// no nearby destinations
if ( destinations1 . length == 0 )
{
destinations1 = destinations2 ;
}
// no destinations
if ( destinations1 . length == 0 )
{
return ;
}
var destination = destinations1 [ Math . floor ( Math . random ( ) * destinations1 . length ) ] ;
if ( destination . isPlanet )
{
this . setParameter ( "oolite_selectedPlanet" , destination ) ;
this . setParameter ( "oolite_selectedStation" , null ) ;
}
else
{
this . setParameter ( "oolite_selectedStation" , destination ) ;
this . setParameter ( "oolite_selectedPlanet" , null ) ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationSelectWitchspaceDestination = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( ! this . ship . hasHyperspaceMotor )
{
this . setParameter ( "oolite_witchspaceDestination" , null ) ;
return ;
}
var preselected = this . getParameter ( "oolite_witchspaceDestination" ) ;
if ( preselected != system . ID && system . info . distanceToSystem ( System . infoForSystem ( galaxyNumber , preselected ) ) <= this . ship . fuel )
{
// we've already got a destination
return ;
}
var possible = system . info . systemsInRange ( this . ship . fuel ) ;
var selected = possible [ Math . floor ( Math . random ( ) * possible . length ) ] ;
this . setParameter ( "oolite_witchspaceDestination" , selected . systemID ) ;
this . communicate ( "oolite_selectedWitchspaceDestination" , { "oolite_witchspaceDestination" : selected . name } , 4 ) ;
2013-07-27 12:29:59 +01:00
}
/*** Destination configuration ***/
AILib . prototype . configurationSetDestinationToNearestFriendlyStation = function ( )
{
2013-08-01 22:40:23 +01:00
var stations = system . stations ;
var threshold = 1E16 ;
var chosenStation = null ;
for ( var i = 0 ; i < stations . length ; i ++ )
{
var station = stations [ i ] ;
if ( this . friendlyStation ( station ) )
{
var distance = station . position . distanceTo ( this . ship ) ;
if ( distance < threshold )
{
threshold = distance ;
chosenStation = station ;
}
}
}
if ( chosenStation == null )
{
this . ship . destination = this . ship . position ;
this . ship . desiredRange = 0 ;
}
else
{
this . ship . destination = chosenStation . position ;
this . ship . desiredRange = 15000 ;
this . ship . desiredSpeed = this . cruiseSpeed ( ) ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationSetDestinationToHomeStation = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
var home = this . homeStation ( ) ;
if ( home != null )
{
this . ship . destination = home . position ;
this . ship . desiredRange = 15000 ;
this . ship . desiredSpeed = this . cruiseSpeed ( ) ;
}
else
{
this . ship . destination = this . ship . position ;
this . ship . desiredRange = 0 ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationSetDestinationToGroupLeader = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
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 ;
2013-07-26 18:52:11 +01:00
}
2013-07-14 19:02:16 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationSetDestinationToMainPlanet = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( system . mainPlanet )
{
this . ship . destination = system . mainPlanet . position ;
this . ship . desiredRange = system . mainPlanet . radius * 3 ;
this . ship . desiredSpeed = this . cruiseSpeed ( ) ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-13 23:18:05 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationSetDestinationToMainStation = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . destination = system . mainStation . position ;
this . ship . desiredRange = 15000 ;
2013-07-27 12:29:59 +01:00
2013-08-01 22:40:23 +01:00
this . ship . desiredSpeed = this . cruiseSpeed ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-14 19:02:16 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . configurationSetDestinationToPirateLurk = function ( )
{
2013-08-01 22:40:23 +01:00
var lurk = this . getParameter ( "oolite_pirateLurk" ) ;
if ( lurk != null )
{
this . ship . destination = lurk ;
}
else
{
var code = "WITCHPOINT" ;
var p = this . ship . position ;
// if already on a lane, stay on it
if ( p . z < system . mainPlanet . position . z && ( ( p . x * p . x ) + ( p . y * p . y ) ) < this . scannerRange * this . scannerRange * 4 )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
lurk = p ;
2013-07-14 19:02:16 +01:00
}
2013-08-01 22:40:23 +01:00
else if ( p . subtract ( system . mainPlanet ) . dot ( p . subtract ( system . sun ) ) < - 0.9 )
2013-07-14 19:02:16 +01:00
{
2013-08-01 22:40:23 +01:00
lurk = p ;
2013-07-15 23:14:47 +01:00
}
2013-08-01 22:40:23 +01:00
else if ( p . dot ( system . sun . position ) > 0.9 )
{
lurk = p ;
}
else // not on a lane, so pick somewhere at random
{
var choice = Math . random ( ) ;
if ( choice < 0.8 )
{
code = "LANE_WP" ;
}
else
{
code = "LANE_PS" ;
}
// code = "LANE_WS"? "WITCHPOINT"?
// what about other locations in less policed systems?
lurk = system . locationFromCode ( code ) ;
}
this . setParameter ( "oolite_pirateLurk" , lurk ) ;
}
this . ship . desiredRange = 1000 ;
this . ship . desiredSpeed = this . cruiseSpeed ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-15 23:14:47 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationSetDestinationToSelectedPlanet = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
var planet = this . getParameter ( "oolite_selectedPlanet" ) ;
if ( planet )
{
this . ship . destination = planet . position ;
this . ship . desiredRange = planet . radius + 100 ;
this . ship . desiredSpeed = this . cruiseSpeed ( ) ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-14 19:02:16 +01:00
2013-07-26 18:52:11 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationSetDestinationToSelectedStation = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
var station = this . getParameter ( "oolite_selectedStation" ) ;
if ( station )
{
this . ship . destination = station . position ;
this . ship . desiredRange = 15000 ;
this . ship . desiredSpeed = this . cruiseSpeed ( ) ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-13 23:18:05 +01:00
2013-07-22 21:24:27 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationSetDestinationToSunskimEnd = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( system . sun )
{
var direction = Vector3D . random ( ) . cross ( this . ship . position . subtract ( system . sun . position ) ) ;
// 2km parallel to local sun surface for every LY of fuel
this . ship . destination = this . ship . position . add ( direction . multiply ( 2000 * ( 7 - this . ship . fuel ) ) ) ;
// max sunskim height is sqrt(4/3) radius
this . ship . desiredRange = 0 ;
this . ship . desiredSpeed = this . ship . maxSpeed ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-22 21:24:27 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationSetDestinationToSunskimStart = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( system . sun )
{
this . ship . destination = system . sun . position ;
// max sunskim height is sqrt(4/3) radius
this . ship . desiredRange = system . sun . radius * 1.125 ;
this . ship . desiredSpeed = this . cruiseSpeed ( ) ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-13 19:13:30 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationSetDestinationToWaypoint = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . getParameter ( "oolite_waypoint" ) != null && this . getParameter ( "oolite_waypointRange" ) != null )
{
this . ship . destination = this . getParameter ( "oolite_waypoint" ) ;
this . ship . desiredRange = this . getParameter ( "oolite_waypointRange" ) ;
this . ship . desiredSpeed = this . cruiseSpeed ( ) ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationSetDestinationToWitchpoint = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . destination = new Vector3D ( 0 , 0 , 0 ) ;
this . ship . desiredRange = 10000 ;
this . ship . desiredSpeed = this . cruiseSpeed ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-13 19:13:30 +01:00
2013-07-27 12:29:59 +01:00
AILib . prototype . configurationSetWaypoint = function ( )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
var gen = this . getWaypointGenerator ( ) ;
if ( gen != null )
{
gen . call ( this ) ;
this . configurationSetDestinationToWaypoint ( ) ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-12 19:44:14 +01:00
2013-07-27 12:29:59 +01:00
/*** Docking configurations ***/
2013-07-26 18:52:11 +01:00
AILib . prototype . configurationSetNearbyFriendlyStationForDocking = function ( )
{
2013-08-01 22:40:23 +01:00
var stations = system . stations ;
for ( var i = 0 ; i < stations . length ; i ++ )
{
var station = stations [ i ] ;
if ( this . friendlyStation ( station ) )
{
// this is not a very good check for friendliness, but
// it will have to do for now
if ( station . position . distanceTo ( this . ship ) < this . ship . scannerRange )
{
this . setParameter ( "oolite_dockingStation" , station )
return ;
}
2013-07-13 23:18:05 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-07-13 23:18:05 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . configurationSetHomeStationForDocking = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . ship . owner && this . ship . owner . isStation && this . friendlyStation ( this . ship . owner ) )
{
this . setParameter ( "oolite_dockingStation" , this . ship . owner )
return ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-22 21:24:27 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . configurationSetSelectedStationForDocking = function ( )
{
2013-08-01 22:40:23 +01:00
this . setParameter ( "oolite_dockingStation" , this . getParameter ( "oolite_selectedStation" ) ) ;
2013-07-26 18:52:11 +01:00
}
2013-07-14 19:02:16 +01:00
2013-07-27 12:29:59 +01:00
/*** Miscellaneous configuration ***/
2013-07-26 18:52:11 +01:00
AILib . prototype . configurationAppointGroupLeader = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group && ! this . ship . group . leader )
{
this . ship . group . leader = this . ship . group . ships [ 0 ] ;
for ( var i = 0 ; i < this . ship . group . ships . length ; i ++ )
2013-07-14 23:11:25 +01:00
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group . ships [ i ] . hasHyperspaceMotor )
{
// bias towards jump-capable ships
this . ship . group . leader = this . ship . group . ships [ i ] ;
break ;
}
}
var leadrole = this . getParameter ( "oolite_leaderRole" )
if ( leadrole != null )
{
this . ship . group . leader . primaryRole = leadrole ;
2013-07-14 23:11:25 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-07-14 23:11:25 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . configurationEscortGroupLeader = function ( )
{
2013-08-01 22:40:23 +01:00
if ( ! this . ship . group || ! this . ship . group . leader || this . ship . group . leader == this . ship )
{
return ;
}
if ( this . ship . group . leader . escortGroup && this . ship . group . leader . escortGroup . containsShip ( this . ship ) )
{
return ;
}
var escrole = this . getParameter ( "oolite_escortRole" )
if ( escrole != null )
{
var oldrole = this . ship . primaryRole ;
this . ship . primaryRole = escrole ;
var accepted = this . ship . offerToEscort ( this . ship . group . leader ) ;
if ( ! accepted )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . primaryRole = oldrole ;
2013-07-18 23:00:23 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-07-18 23:00:23 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . configurationForgetCargoDemand = function ( )
{
2013-08-01 22:40:23 +01:00
/ * i f ( t h i s . s h i p . g r o u p & & t h i s . s h i p . g r o u p . l e a d e r & & t h i s . s h i p . g r o u p . l e a d e r . A I S c r i p t . o o l i t e _ i n t e r s h i p . c a r g o d e m a n d e d )
{
delete this . ship . group . leader . AIScript . oolite _intership . cargodemanded ;
} * / / / not sure about this , maybe not needed
2013-07-15 23:14:47 +01:00
2013-08-01 22:40:23 +01:00
if ( this . ship . AIScript . oolite _intership . cargodemanded )
{
delete this . ship . AIScript . oolite _intership . cargodemanded ;
delete this . ship . AIScript . oolite _intership . cargodemandmet ;
// and make the group lose the cargo count from the last demand
if ( this . ship . group )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
for ( var i = 0 ; i < this . ship . group . ships . length ; i ++ )
{
var ship = this . ship . group . ships [ i ] ;
if ( ship . AIScript && ship . AIScript . oolite _priorityai )
2013-07-15 23:14:47 +01:00
{
2013-08-01 22:40:23 +01:00
ship . AIScript . oolite _priorityai . setParameter ( "oolite_cargoDropped" , 0 ) ;
2013-07-15 23:14:47 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-15 23:14:47 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-07-15 23:14:47 +01:00
2013-07-27 15:39:23 +01:00
AILib . prototype . configurationLeaveEscortGroup = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . ship . group && this . ship . group . leader && this . ship . group . leader != this . ship && this . ship . group . leader . escortGroup && this . ship . group . leader . escortGroup . containsShip ( this . ship ) )
{
this . ship . group . leader . escortGroup . removeShip ( this . ship ) ;
if ( this . ship . group )
2013-07-27 15:39:23 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . group . removeShip ( this . ship ) ;
this . ship . group = null ;
2013-07-27 15:39:23 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-27 15:39:23 +01:00
}
2013-07-27 12:29:59 +01:00
/*** Station configuration ***/
2013-07-26 18:52:11 +01:00
AILib . prototype . configurationStationReduceAlertLevel = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . ship . alertCondition > 1 )
{
this . ship . alertCondition -- ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-23 21:21:49 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . configurationStationValidateTarget = function ( )
{
2013-08-01 22:40:23 +01:00
if ( this . ship . target )
{
if ( this . ship . position . distanceTo ( this . ship . target ) > this . ship . scannerRange )
2013-07-23 21:21:49 +01:00
{
2013-08-01 22:40:23 +01:00
// station behaviour does not generally validate target
this . ship . target = null ;
2013-07-23 21:21:49 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-07-21 11:10:47 +01:00
2013-07-26 18:52:11 +01:00
/* ****************** Response definition functions ************** */
2013-07-12 19:44:14 +01:00
2013-07-26 18:52:11 +01:00
/ * S t a n d a r d s t a t e - m a c h i n e r e s p o n s e s . T h e s e s e t u p a s e t o f s t a n d a r d
* state machine responses where incoming events will cause reasonable
* default behaviour and often force a reconsideration of
* priorities . Many behaviours will need to supplement the standard
* responses with additional definitions . * /
AILib . prototype . responsesAddStandard = function ( handlers )
{
2013-08-01 22:40:23 +01:00
handlers . commsMessageReceived = function ( message )
{
this . noteCommsHeard ( ) ;
}
handlers . cascadeWeaponDetected = function ( weapon )
{
this . ship . clearDefenseTargets ( ) ;
this . ship . addDefenseTarget ( weapon ) ;
this . setParameter ( "oolite_cascadeDetected" , weapon . position ) ;
this . ship . target = weapon ;
this . ship . performFlee ( ) ;
this . reconsiderNow ( ) ;
} ;
2013-07-12 19:44:14 +01:00
2013-08-01 22:40:23 +01:00
handlers . shipAttackedWithMissile = function ( missile , whom )
{
if ( ! this . ship . hasHostileTarget && this . getParameter ( "oolite_flag_sendsDistressCalls" ) )
2013-07-13 23:18:05 +01:00
{
2013-08-01 22:40:23 +01:00
this . broadcastDistressMessage ( ) ;
}
if ( this . ship . equipmentStatus ( "EQ_ECM" ) == "EQUIPMENT_OK" )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . fireECM ( ) ;
this . ship . addDefenseTarget ( missile ) ;
this . ship . addDefenseTarget ( whom ) ;
2013-08-02 22:16:54 +01:00
// but don't reconsider immediately, because the ECM will
// probably get it
2013-08-01 22:40:23 +01:00
}
else
2013-07-26 18:52:11 +01:00
{
2013-08-02 22:16:54 +01:00
this . communicate ( "oolite_incomingMissile" , this . entityCommsParams ( whom ) , 3 ) ;
2013-08-01 22:40:23 +01:00
this . ship . addDefenseTarget ( missile ) ;
this . ship . addDefenseTarget ( whom ) ;
var tmp = this . ship . target ;
this . ship . target = whom ;
this . ship . requestHelpFromGroup ( ) ;
this . ship . target = tmp ;
this . reconsiderNow ( ) ;
}
} ;
2013-08-04 12:29:44 +01:00
handlers . shipAttackerDistracted = function ( whom )
{
if ( this . ship . scanClass != "CLASS_THARGOID" && whom . scanClass == "CLASS_THARGOID" && ( ! this . ship . target || this . ship . target . scanClass != "CLASS_THARGOID" ) )
{
// frying pan, fire
if ( this . respondToThargoids ( whom , false ) )
{
this . reconsiderNow ( ) ;
return ;
}
}
var last = this . getParameter ( "oolite_lastAssist" ) ;
if ( last != whom )
{
this . communicate ( "oolite_thanksForHelp" , this . entityCommsParams ( whom ) , 1 ) ;
if ( this . ship . scanClass == "CLASS_POLICE" )
{
if ( whom . scanClass != "CLASS_POLICE" && whom . scanClass != "CLASS_THARGOID" && whom . bounty > 0 )
{
whom . setBounty ( whom . bounty * 4 / 5 , "assisting police" ) ;
}
}
this . setParameter ( "oolite_lastAssist" , whom ) ;
}
this . reconsiderNow ( ) ;
} ;
2013-08-01 22:40:23 +01:00
handlers . shipBeingAttacked = function ( whom )
{
if ( whom . target != this . ship && whom != player . ship )
{
// was accidental
if ( this . allied ( whom , this . ship ) )
{
this . communicate ( "oolite_friendlyFire" , this . entityCommsParams ( whom ) , 3 ) ;
// ignore it
return ;
}
if ( Math . random ( ) > 0.1 )
{
// usually ignore it anyway
return ;
}
}
if ( ! this . ship . hasHostileTarget && this . getParameter ( "oolite_flag_sendsDistressCalls" ) )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . broadcastDistressMessage ( ) ;
}
2013-08-05 14:14:50 +01:00
if ( this . ship . isFleeing )
{
this . communicate ( "oolite_surrender" , { } , 3 ) ;
}
2013-08-04 12:29:44 +01:00
if ( ( whom . scanClass == "CLASS_THARGOID" ) && ( this . ship . scanClass != "CLASS_THARGOID" ) && ( ! this . ship . target || this . ship . target . scanClass != "CLASS_THARGOID" ) )
{
if ( this . respondToThargoids ( whom , true ) )
{
this . reconsiderNow ( ) ;
return ;
}
}
if ( whom . scanClass != "CLASS_THARGOID" && this . ship . target && this . ship . target . scanClass == "CLASS_THARGOID" )
{
// now is not a good time. Everything is friendly fire right now...
return ;
}
2013-08-01 22:40:23 +01:00
if ( this . ship . defenseTargets . indexOf ( whom ) < 0 )
{
2013-08-02 22:16:54 +01:00
this . communicate ( "oolite_newAssailiant" , this . entityCommsParams ( whom ) , 3 ) ;
2013-08-01 22:40:23 +01:00
this . ship . addDefenseTarget ( whom ) ;
}
else
{
// else we know about this attacker already
if ( this . ship . energy * 4 < this . ship . maxEnergy )
{
2013-08-02 22:16:54 +01:00
this . communicate ( "oolite_attackLowEnergy" , this . entityCommsParams ( whom ) , 2 ) ;
2013-08-01 22:40:23 +01:00
// but at low energy still reconsider
this . ship . requestHelpFromGroup ( ) ;
}
}
if ( this . ship . hasHostileTarget )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( ! this . isAggressive ( this . ship . target ) )
{
// if our current target is running away, switch targets
2013-08-04 12:29:44 +01:00
this . noteDistraction ( whom ) ;
2013-08-01 22:40:23 +01:00
this . ship . target = whom ;
}
else if ( this . ship . target . target != this . ship )
{
// if our current target isn't aiming at us
if ( Math . random ( ) < 0.2 )
2013-07-12 23:00:39 +01:00
{
2013-08-01 22:40:23 +01:00
// occasionally switch
2013-08-04 12:29:44 +01:00
this . noteDistraction ( whom ) ;
this . ship . target = whom ;
}
}
else
{
2013-08-05 14:14:50 +01:00
// tend to switch to the more dangerous one
if ( this . threatAssessment ( whom , true ) > this . threatAssessment ( this . ship . target , true ) * ( 1 + Math . random ( ) ) )
2013-08-04 12:29:44 +01:00
{
this . noteDistraction ( whom ) ;
2013-08-01 22:40:23 +01:00
this . ship . target = whom ;
2013-07-13 11:36:28 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
if ( this . ship . escortGroup != null )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . requestHelpFromGroup ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-08-04 12:29:44 +01:00
this . reconsiderNow ( ) ;
2013-08-01 22:40:23 +01:00
} ;
handlers . shipBeingAttackedUnsuccessfully = function ( whom )
{
if ( ! this . ship . hasHostileTarget && this . getParameter ( "oolite_flag_sendsDistressCalls" ) )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . broadcastDistressMessage ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
if ( this . ship . defenseTargets . indexOf ( whom ) < 0 )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . addDefenseTarget ( whom ) ;
this . reconsiderNow ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
} ;
handlers . shipTargetLost = function ( target )
{
this . reconsiderNow ( ) ;
} ;
// overridden for escorts
handlers . helpRequestReceived = function ( ally , enemy )
{
2013-08-04 12:29:44 +01:00
if ( this . allied ( this . ship , enemy ) )
{
return ;
}
2013-08-01 22:40:23 +01:00
this . ship . addDefenseTarget ( enemy ) ;
if ( enemy . scanClass == "CLASS_MISSILE" && enemy . position . distanceTo ( this . ship ) < this . ship . scannerRange && this . ship . equipmentStatus ( "EQ_ECM" ) == "EQUIPMENT_OK" )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . fireECM ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-08-04 12:29:44 +01:00
if ( enemy . scanClass == "CLASS_THARGOID" && this . ship . scanClass != "CLASS_THARGOID" && ( ! this . ship . target || this . ship . target . scanClass != "CLASS_THARGOID" ) )
{
if ( this . respondToThargoids ( enemy , false ) )
{
this . reconsiderNow ( ) ;
return ; // not in a combat mode
}
}
2013-08-01 22:40:23 +01:00
if ( ! this . ship . hasHostileTarget )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . reconsiderNow ( ) ;
return ; // not in a combat mode
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
if ( ally . energy / ally . maxEnergy < this . ship . energy / this . ship . maxEnergy )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
// not in worse shape than ally
if ( this . ship . target . target != ally && this . ship . target != ally . target )
{
// not already helping, go for it...
2013-08-02 22:16:54 +01:00
this . communicate ( "oolite_startHelping" , this . entityCommsParams ( enemy ) , 4 ) ;
2013-08-01 22:40:23 +01:00
this . ship . target = enemy ;
2013-07-26 18:52:11 +01:00
this . reconsiderNow ( ) ;
2013-08-01 22:40:23 +01:00
}
}
}
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 ( )
{
if ( this . getParameter ( "oolite_flag_allowPlanetaryLanding" ) )
{
this . ship . desiredSpeed = this . ship . maxSpeed / 4 ;
this . ship . performLandOnPlanet ( ) ;
this . ship . AIScriptWakeTime = 0 ; // cancel reconsiderations
this . applyHandlers ( { } ) ; // cancel interruptions
this . communicate ( "oolite_landingOnPlanet" , { } , 4 ) ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
else
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . reconsiderNow ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
handlers . shipLaunchedFromStation = function ( station )
{
// clear the station
this . ship . destination = station . position ;
this . ship . desiredSpeed = this . cruiseSpeed ( ) ;
this . ship . desiredRange = 15000 ;
this . ship . performFlyToRangeFromDestination ( ) ;
}
handlers . shipWillEnterWormhole = function ( )
{
this . setParameter ( "oolite_witchspaceWormhole" , null ) ;
this . applyHandlers ( { } ) ;
}
handlers . shipExitedWormhole = function ( )
{
this . ship . AIScript . oolite _intership = { } ;
// this.reconsiderNow();
}
handlers . distressMessageReceived = function ( aggressor , sender )
{
if ( this . getParameter ( "oolite_flag_listenForDistressCall" ) != true )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
return ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
this . setParameter ( "oolite_distressAggressor" , aggressor ) ;
this . setParameter ( "oolite_distressSender" , sender ) ;
this . setParameter ( "oolite_distressTimestamp" , clock . adjustedSeconds ) ;
this . reconsiderNow ( ) ;
}
handlers . offenceCommittedNearby = function ( attacker , victim )
{
if ( this . getParameter ( "oolite_flag_markOffenders" ) )
{
2013-08-06 18:40:15 +01:00
if ( attacker . bounty & 7 != 7 )
2013-08-02 22:16:54 +01:00
{
2013-08-06 18:40:15 +01:00
this . communicate ( "oolite_offenceDetected" , this . entityCommsParams ( attacker ) , 3 ) ;
2013-08-02 22:16:54 +01:00
}
else
{
this . communicate ( "oolite_offenceDetected" , this . entityCommsParams ( attacker ) , 4 ) ;
}
2013-08-01 22:40:23 +01:00
attacker . setBounty ( attacker . bounty | 7 , "seen by police" ) ;
this . ship . addDefenseTarget ( attacker ) ;
this . reconsiderNow ( ) ;
}
}
handlers . playerWillEnterWitchspace = function ( )
{
var wormhole = this . getParameter ( "oolite_witchspaceWormhole" ) ;
if ( wormhole != null && wormhole . isWormhole )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . enterWormhole ( wormhole ) ;
}
}
handlers . wormholeSuggested = function ( hole )
{
this . setParameter ( "oolite_witchspaceWormhole" , hole ) ;
this . reconsiderNow ( ) ;
}
2013-08-02 22:16:54 +01:00
handlers . shipAttackedOther = function ( other )
{
this . communicate ( "oolite_hitTarget" , this . entityCommsParams ( other ) , 4 ) ;
}
handlers . shipFiredMissile = function ( missile , target )
{
this . communicate ( "oolite_firedMissile" , this . entityCommsParams ( target ) , 4 ) ;
}
handlers . shipKilledOther = function ( other )
{
this . communicate ( "oolite_killedTarget" , this . entityCommsParams ( other ) , 3 ) ;
}
handlers . shipLaunchedEscapePod = function ( )
{
this . communicate ( "oolite_eject" , { } , 1 ) ;
}
handlers . escortAccepted = function ( escort )
{
this . communicate ( "oolite_escortAccepted" , this . entityCommsParams ( escort ) , 2 ) ;
}
handlers . shipAcceptedEscort = function ( mother )
{
this . communicate ( "oolite_escortMotherAccepted" , this . entityCommsParams ( mother ) , 2 ) ;
}
2013-08-01 22:40:23 +01:00
// TODO: more event handlers
2013-07-26 18:52:11 +01:00
}
/* Additional handlers for use while docking */
AILib . prototype . responsesAddDocking = function ( handlers )
{
2013-08-01 22:40:23 +01:00
handlers . stationWithdrewDockingClearance = function ( )
{
this . setParameter ( "oolite_dockingStation" , null ) ;
this . reconsiderNow ( ) ;
} ;
handlers . shipAchievedDesiredRange = function ( )
{
var message = this . ship . dockingInstructions . ai _message ;
if ( message == "APPROACH" || message == "BACK_OFF" || message == "APPROACH_COORDINATES" )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . reconsiderNow ( ) ;
}
} ;
2013-07-26 18:52:11 +01:00
}
/* Override of standard handlers for use while escorting */
AILib . prototype . responsesAddEscort = function ( handlers )
{
2013-08-01 22:40:23 +01:00
handlers . helpRequestReceived = function ( ally , enemy )
{
2013-08-04 12:29:44 +01:00
if ( this . allied ( this . ship , enemy ) )
{
return ;
}
this . ship . addDefenseTarget ( enemy ) ;
if ( enemy . scanClass == "CLASS_MISSILE" && enemy . position . distanceTo ( this . ship ) < this . ship . scannerRange && this . ship . equipmentStatus ( "EQ_ECM" ) == "EQUIPMENT_OK" )
{
this . ship . fireECM ( ) ;
}
if ( enemy . scanClass == "CLASS_THARGOID" && this . ship . scanClass != "CLASS_THARGOID" && ( ! this . ship . target || this . ship . target . scanClass != "CLASS_THARGOID" ) )
{
if ( this . respondToThargoids ( enemy , false ) )
{
this . reconsiderNow ( ) ;
return ;
}
}
2013-08-01 22:40:23 +01:00
// always help the leader
if ( ally == this . ship . group . leader )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( ! this . ship . target || this . ship . target . target != ally )
{
this . ship . target = enemy ;
this . reconsiderNow ( ) ;
return ;
}
}
this . ship . addDefenseTarget ( enemy ) ;
2013-08-02 22:16:54 +01:00
if ( enemy . scanClass == "CLASS_MISSILE" && enemy . position . distanceTo ( this . ship ) < this . ship . scannerRange && this . ship . equipmentStatus ( "EQ_ECM" ) == "EQUIPMENT_OK" )
{
this . ship . fireECM ( ) ;
return ;
}
2013-08-01 22:40:23 +01:00
if ( ! this . ship . hasHostileTarget )
{
this . reconsiderNow ( ) ;
return ; // not in a combat mode
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
if ( ally . energy / ally . maxEnergy < this . ship . energy / this . ship . maxEnergy )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
// not in worse shape than ally
if ( this . ship . target . target != ally && this . ship . target != ally . target )
{
// not already helping, go for it...
this . ship . target = enemy ;
2013-07-26 18:52:11 +01:00
this . reconsiderNow ( ) ;
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
handlers . escortDock = function ( )
{
this . reconsiderNow ( ) ;
}
2013-07-26 18:52:11 +01:00
}
/* Additional handlers for scooping */
AILib . prototype . responsesAddScooping = function ( handlers )
{
2013-08-01 22:40:23 +01:00
handlers . shipAchievedDesiredRange = function ( )
{
this . reconsiderNow ( ) ;
}
handlers . shipScoopedFuel = function ( )
{
if ( this . ship . fuel == 7 )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . reconsiderNow ( ) ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
// shorter list than before
AILib . prototype . responsesAddStation = function ( handlers )
{
2013-08-01 22:40:23 +01:00
handlers . commsMessageReceived = function ( message )
{
this . noteCommsHeard ( ) ;
}
handlers . cascadeWeaponDetected = function ( weapon )
{
this . ship . alertCondition = 3 ;
this . reconsiderNow ( ) ;
} ;
2013-07-26 18:52:11 +01:00
2013-08-01 22:40:23 +01:00
handlers . shipAttackedWithMissile = function ( missile , whom )
{
this . ship . alertCondition = 3 ;
if ( this . ship . equipmentStatus ( "EQ_ECM" ) == "EQUIPMENT_OK" )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . fireECM ( ) ;
this . ship . addDefenseTarget ( missile ) ;
this . ship . addDefenseTarget ( whom ) ;
// but don't reconsider immediately
}
else
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . addDefenseTarget ( missile ) ;
this . ship . addDefenseTarget ( whom ) ;
var tmp = this . ship . target ;
this . ship . target = whom ;
this . ship . requestHelpFromGroup ( ) ;
this . ship . target = tmp ;
this . reconsiderNow ( ) ;
}
} ;
handlers . shipBeingAttacked = function ( whom )
{
if ( ! whom )
{
this . reconsiderNow ( ) ;
return ;
}
if ( whom . target != this . ship && whom != player . ship )
{
// was accidental
if ( this . allied ( whom , this . ship ) )
{
this . communicate ( "oolite_friendlyFire" , this . entityCommsParams ( whom ) , 4 ) ;
// ignore it
return ;
}
if ( Math . random ( ) > 0.1 )
{
// usually ignore it anyway
return ;
}
}
this . ship . alertCondition = 3 ;
if ( this . ship . defenseTargets . indexOf ( whom ) < 0 )
{
this . ship . addDefenseTarget ( whom ) ;
this . reconsiderNow ( ) ;
}
else
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
// else we know about this attacker already
if ( this . ship . energy * 4 < this . ship . maxEnergy )
{
// but at low energy still reconsider
2013-07-26 18:52:11 +01:00
this . reconsiderNow ( ) ;
2013-08-01 22:40:23 +01:00
this . ship . requestHelpFromGroup ( ) ;
}
}
if ( this . ship . hasHostileTarget )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
if ( ! this . isAggressive ( this . ship . target ) )
{
// if our current target is running away, switch targets
this . ship . target = whom ;
}
else if ( this . ship . target . target != this . ship )
{
// if our current target isn't aiming at us
if ( Math . random ( ) < 0.2 )
2013-07-13 23:18:05 +01:00
{
2013-08-01 22:40:23 +01:00
// occasionally switch
this . ship . target = whom ;
2013-07-13 23:18:05 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-12 23:00:39 +01:00
}
2013-08-02 22:16:54 +01:00
handlers . shipAttackedOther = function ( other )
{
this . communicate ( "oolite_hitTarget" , this . entityCommsParams ( other ) , 4 ) ;
}
handlers . shipFiredMissile = function ( missile , target )
{
this . communicate ( "oolite_firedMissile" , this . entityCommsParams ( target ) , 4 ) ;
}
handlers . shipKilledOther = function ( other )
{
this . communicate ( "oolite_killedTarget" , this . entityCommsParams ( other ) , 3 ) ;
}
2013-08-01 22:40:23 +01:00
} ;
handlers . shipTargetLost = function ( target )
{
this . reconsiderNow ( ) ;
} ;
handlers . helpRequestReceived = function ( ally , enemy )
{
this . ship . addDefenseTarget ( enemy ) ;
2013-08-02 22:16:54 +01:00
if ( enemy . scanClass == "CLASS_MISSILE" && enemy . position . distanceTo ( this . ship ) < this . ship . scannerRange && this . ship . equipmentStatus ( "EQ_ECM" ) == "EQUIPMENT_OK" )
{
this . ship . fireECM ( ) ;
return ;
}
2013-08-01 22:40:23 +01:00
if ( ! this . ship . alertCondition == 3 )
2013-07-14 19:02:16 +01:00
{
2013-08-01 22:40:23 +01:00
this . ship . target = enemy ;
this . reconsiderNow ( ) ;
return ; // not in a combat mode
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
this . ship . target = enemy ;
}
handlers . distressMessageReceived = function ( aggressor , sender )
{
if ( this . getParameter ( "oolite_flag_listenForDistressCall" ) != true )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
return ;
}
this . setParameter ( "oolite_distressAggressor" , aggressor ) ;
this . setParameter ( "oolite_distressSender" , sender ) ;
this . setParameter ( "oolite_distressTimestamp" , clock . adjustedSeconds ) ;
this . reconsiderNow ( ) ;
}
handlers . offenceCommittedNearby = function ( attacker , victim )
{
if ( this . getParameter ( "oolite_flag_markOffenders" ) )
{
attacker . setBounty ( attacker . bounty | 7 , "seen by police" ) ;
this . ship . addDefenseTarget ( attacker ) ;
if ( this . ship . alertCondition < 3 )
{
this . ship . alertCondition = 3 ;
this . ship . target = attacker ;
}
this . reconsiderNow ( ) ;
2013-07-14 19:02:16 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-07-26 18:52:11 +01:00
}
2013-07-12 19:44:14 +01:00
2013-07-23 21:21:49 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . responsesAddMissile = function ( handlers ) {
2013-08-01 22:40:23 +01:00
handlers . commsMessageReceived = function ( message )
{
this . noteCommsHeard ( ) ;
}
handlers . shipHitByECM = function ( )
{
if ( this . ship . scriptInfo . oolite _missile _ecmResponse )
{
var fn = this . ship . scriptInfo . oolite _missile _ecmResponse ;
if ( this . ship . AIScript [ fn ] )
{
this . ship . AIScript [ fn ] ( ) ;
this . reconsiderNow ( ) ;
return ;
}
if ( this . ship . script [ fn ] )
{
this . ship . script [ fn ] ( ) ;
this . reconsiderNow ( ) ;
return ;
}
}
2013-07-26 18:52:11 +01:00
2013-08-01 22:40:23 +01:00
/ * T h i s s e c t i o n f o r t h e h a r d h e a d s s h o u l d b e a n E C M
* response function , and that is used in the default
* shipdata . plist , but for compatibility with older OXPs
* it ' s also hardcoded here for now .
*
* OXPs wanting to overrule this for hardheads can set a
* response function to do so .
* /
if ( this . ship . primaryRole == "EQ_HARDENED_MISSILE" )
{
if ( Math . random ( ) < 0.1 ) //10% chance per pulse
{
if ( Math . random ( ) < 0.5 )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
// 50% chance responds by detonation
this . ship . AIScript . shipAchievedDesiredRange ( ) ;
return ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
// otherwise explode as normal below
}
else // 90% chance unaffected
{
return ;
}
}
2013-07-23 21:21:49 +01:00
2013-08-01 22:40:23 +01:00
this . ship . explode ( ) ;
}
handlers . shipTargetCloaked = function ( )
{
this . setParameter ( "oolite_interceptCoordinates" , this . ship . target . position ) ;
this . setParameter ( "oolite_interceptTarget" , this . ship . target ) ;
// stops performIntercept sending AchievedDesiredRange
this . ship . performIdle ( ) ;
}
handlers . shipTargetLost = function ( )
{
this . reconsiderNow ( ) ;
}
handlers . shipAchievedDesiredRange = function ( )
{
if ( this . ship . scriptInfo . oolite _missile _detonation )
{
var fn = this . ship . scriptInfo . oolite _missile _detonation ;
if ( this . ship . AIScript [ fn ] )
{
this . ship . AIScript [ fn ] ( ) ;
this . reconsiderNow ( ) ;
return ;
}
if ( this . ship . script [ fn ] )
{
this . ship . script [ fn ] ( ) ;
this . reconsiderNow ( ) ;
return ;
}
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
/ * D e f a u l t s t o s t a n d a r d m i s s i l e s e t t i n g s , i n c a s e t h e y ' r e
* not specified in scriptInfo * /
var blastpower = 170 ;
var blastradius = 32.5 ;
var blastshaping = 0.25 ;
if ( this . ship . scriptInfo . oolite _missile _blastPower )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
blastpower = this . ship . scriptInfo . oolite _missile _blastPower ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
if ( this . ship . scriptInfo . oolite _missile _blastRadius )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
blastradius = this . ship . scriptInfo . oolite _missile _blastRadius ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
if ( this . ship . scriptInfo . oolite _missile _blastShaping )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
blastshaping = this . ship . scriptInfo . oolite _missile _blastShaping ;
2013-07-23 21:21:49 +01:00
}
2013-08-01 22:40:23 +01:00
this . ship . dealEnergyDamage ( blastpower , blastradius , blastshaping ) ;
this . ship . explode ( ) ;
}
2013-07-26 18:52:11 +01:00
}
2013-07-23 21:21:49 +01:00
2013-07-24 21:56:50 +01:00
2013-07-26 18:52:11 +01:00
/* ******************* Waypoint generators *********************** */
/ * W a y p o i n t g e n e r a t o r s . W h e n t h e s e a r e c a l l e d , t h e y s h o u l d s e t u p
* the next waypoint for the ship . Ideally ships should either
* reach that waypoint or formally give up on it before asking for
* the next one , but the generator shouldn ' t assume that unless
* it ' s one written specifically for a particular AI . * /
2013-07-24 21:56:50 +01:00
2013-07-26 18:52:11 +01:00
AILib . prototype . waypointsSpacelanePatrol = function ( )
{
2013-08-01 22:40:23 +01:00
var p = this . ship . position ;
var choice = "" ;
if ( p . magnitude ( ) < 10000 )
{
// near witchpoint
if ( Math . random ( ) < 0.9 )
{
// mostly return to planet
choice = "PLANET" ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
else
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
choice = "SUN" ;
2013-07-24 21:56:50 +01:00
}
2013-08-01 22:40:23 +01:00
}
else if ( p . distanceTo ( system . mainPlanet ) < system . mainPlanet . radius * 2 )
{
// near planet
if ( Math . random ( ) < 0.75 )
{
// mostly go to witchpoint
choice = "WITCHPOINT" ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
else
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
choice = "SUN" ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
else if ( p . distanceTo ( system . sun ) < system . sun . radius * 3 )
{
// near sun
if ( Math . random ( ) < 0.9 )
{
// mostly return to planet
choice = "PLANET" ;
}
else
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
choice = "SUN" ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
else if ( p . z < system . mainPlanet . position . z && ( ( p . x * p . x ) + ( p . y * p . y ) ) < this . ship . scannerRange * this . ship . scannerRange * 4 )
{
// on lane 1
if ( Math . random ( ) < 0.5 )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
choice = "PLANET" ;
2013-07-26 18:52:11 +01:00
}
else
{
2013-08-01 22:40:23 +01:00
choice = "WITCHPOINT" ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
else if ( p . subtract ( system . mainPlanet ) . dot ( p . subtract ( system . sun ) ) < - 0.9 )
{
// on lane 2
if ( Math . random ( ) < 0.5 )
2013-07-19 20:18:53 +01:00
{
2013-08-01 22:40:23 +01:00
choice = "PLANET" ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
else
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
choice = "SUN" ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
}
else if ( p . dot ( system . sun . position ) > 0.9 )
{
// on lane 3
if ( Math . random ( ) < 0.5 )
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
choice = "WITCHPOINT" ;
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
else
2013-07-26 18:52:11 +01:00
{
2013-08-01 22:40:23 +01:00
choice = "SUN" ;
}
}
else
{
// we're not on any lane. Return to the planet
choice = "PLANET" ;
}
// having chosen, now set up the next stop on the patrol
switch ( choice ) {
case "WITCHPOINT" :
this . setParameter ( "oolite_waypoint" , new Vector3D ( 0 , 0 , 0 ) ) ;
this . setParameter ( "oolite_waypointRange" , 7500 ) ;
break ;
case "PLANET" :
this . setParameter ( "oolite_waypoint" , system . mainPlanet . position ) ;
this . setParameter ( "oolite_waypointRange" , system . mainPlanet . radius * 2 ) ;
break ;
case "SUN" :
this . setParameter ( "oolite_waypoint" , system . sun . position ) ;
this . setParameter ( "oolite_waypointRange" , system . sun . radius * 2.5 ) ;
break ;
}
2013-07-26 18:52:11 +01:00
}
2013-08-01 22:40:23 +01:00
AILib . prototype . waypointsStationPatrol = function ( )
{
var station = null ;
if ( this . ship . group )
{
station = this . ship . group . leader ;
}
if ( ! station )
{
station = system . mainStation ;
if ( ! station )
{
this . setParameter ( "oolite_waypoint" , new Vector3D ( 0 , 0 , 0 ) ) ;
this . setParameter ( "oolite_waypointRange" , 7500 ) ;
return ;
}
}
var z = station . vectorForward ;
var tmp = new Vector3D ( 0 , 1 , 0 ) ;
if ( system . sun )
{
tmp = z . cross ( system . sun . position . direction ( ) ) ;
}
var x = z . cross ( tmp ) ;
var y = z . cross ( x ) ;
// x and y now consistent vectors relative to a rotating station
var waypoints = [
station . position . add ( x . multiply ( 25000 ) ) ,
station . position . add ( y . multiply ( 25000 ) ) ,
station . position . add ( x . multiply ( - 25000 ) ) ,
station . position . add ( y . multiply ( - 25000 ) )
] ;
var waypoint = waypoints [ 0 ] ;
for ( var i = 0 ; i <= 3 ; i ++ )
{
if ( this . ship . position . distanceTo ( waypoints [ i ] ) < 500 )
{
waypoint = waypoints [ ( i + 1 ) % 4 ] ;
break ;
}
}
this . setParameter ( "oolite_waypoint" , waypoint ) ;
this . setParameter ( "oolite_waypointRange" , 100 ) ;
}
/* ********** Communications data ****************/
/ * W a r n i n g : O X P s s h o u l d o n l y i n t e r a c t w i t h t h i s t h r o u g h t h e p r o v i d e d
* API functions . The internals of data storage may be changed at any
* time . This data is global . * /
this . startUp = function ( )
{
// initial definition is just essential communications for now
2013-08-03 17:47:43 +01:00
this . $commsSettings = { } ;
this . _setCommunications ( {
2013-08-04 12:29:44 +01:00
generic : {
generic : {
2013-08-05 14:14:50 +01:00
oolite _thanksForHelp : "[oolite-comms-thanksForHelp]" ,
oolite _surrender : "[oolite-comms-surrender]"
2013-08-04 12:29:44 +01:00
}
} ,
2013-08-02 22:16:54 +01:00
trader : {
2013-08-01 22:40:23 +01:00
generic : {
oolite _acceptPirateDemand : "[oolite-comms-acceptPirateDemand]" ,
oolite _makeDistressCall : "[oolite-comms-makeDistressCall]"
}
} ,
police : {
generic : {
2013-08-04 12:29:44 +01:00
oolite _thanksForHelp : "[oolite-comms-police-thanksForHelp]" ,
2013-08-01 22:40:23 +01:00
oolite _markForFines : "[oolite-comms-markForFines]" ,
oolite _distressResponseAggressor : "[oolite-comms-distressResponseAggressor]" ,
oolite _offenceDetected : "[oolite-comms-offenceDetected]" ,
}
2013-08-02 22:16:54 +01:00
} ,
pirate : {
generic : {
oolite _makePirateDemand : "[oolite-comms-makePirateDemand]" ,
}
} ,
2013-08-03 14:55:48 +01:00
_thargoid : {
thargoid : {
oolite _continuingAttack : "[thargoid_curses]"
2013-08-02 22:16:54 +01:00
}
2013-08-01 22:40:23 +01:00
}
2013-08-03 17:47:43 +01:00
} ) ;
2013-08-01 22:40:23 +01:00
/* These are temporary for testing. Remove before release... */
this . $commsSettings . generic . generic . oolite _continuingAttack = "I've got the [oolite_entityClass]" ;
this . $commsSettings . generic . generic . oolite _beginningAttack = "Die, [oolite_entityClass]!" ;
2013-08-02 22:16:54 +01:00
this . $commsSettings . generic . generic . oolite _hitTarget = "Take that, scum." ;
this . $commsSettings . generic . generic . oolite _killedTarget = "[oolite_entityClass] down!" ;
this . $commsSettings . pirate . generic . oolite _hitTarget = "Where's the cargo, [oolite_entityClass]?" ;
this . $commsSettings . generic . generic . oolite _friendlyFire = "Watch where you're shooting, [oolite_entityClass]!" ;
this . $commsSettings . generic . generic . oolite _eject = "Condition critical! I'm bailing out..." ;
2013-08-04 12:29:44 +01:00
this . $commsSettings . generic . generic . oolite _thargoidAttack = "%N! A thargoid warship!" ;
2013-08-02 22:16:54 +01:00
this . $commsSettings . generic . generic . oolite _firedMissile = "Dodge this for a bit, [oolite_entityClass]." ;
this . $commsSettings . generic . generic . oolite _incomingMissile = "Help! Help! Missile!" ;
this . $commsSettings . generic . generic . oolite _startHelping = "Hold on! I'm on them." ;
this . $commsSettings . generic . generic . oolite _switchTarget = "I'll get the [oolite_entityClass]." ;
this . $commsSettings . generic . generic . oolite _newAssailant = "Where did that [oolite_entityClass] come from?" ;
this . $commsSettings . generic . generic . oolite _startFleeing = "I can't take this much longer! I'm getting out of here." ;
2013-08-05 14:14:50 +01:00
this . $commsSettings . generic . generic . oolite _continueFleeing = "I'm still not clear. Someone please help!" ;
this . $commsSettings . generic . generic . oolite _groupIsOutnumbered = "Please, let us go!" ;
this . $commsSettings . pirate . generic . oolite _groupIsOutnumbered = "Argh! They're tougher than they looked. Break off the attack!" ;
2013-08-02 22:16:54 +01:00
this . $commsSettings . generic . generic . oolite _dockingWait = "Bored now." ;
this . $commsSettings . generic . generic . oolite _quiriumCascade = "Cascade! %N! Get out of here!" ;
this . $commsSettings . pirate . generic . oolite _scoopedCargo = "Ah, [oolite_goodsDescription]. We should have shaken them down for more." ;
2013-08-01 22:40:23 +01:00
}
/ * S e a r c h t h r o u g h c o m m u n i c a t i o n s f r o m m o s t s p e c i f i c t o l e a s t s p e c i f i c .
* role + personality
* "generic" + personality
* role + "generic"
* "generic" + "generic"
* A return value of "" means no communication is set .
2013-08-02 22:16:54 +01:00
*
2013-08-03 14:55:48 +01:00
* Roles or personalities starting with _ do not fall back to generic
2013-08-01 22:40:23 +01:00
* /
this . _getCommunication = function ( role , personality , key )
{
if ( this . $commsSettings [ role ] && this . $commsSettings [ role ] [ personality ] && this . $commsSettings [ role ] [ personality ] [ key ] && this . $commsSettings [ role ] [ personality ] [ key ] != "" )
{
return this . $commsSettings [ role ] [ personality ] [ key ] ;
}
2013-08-03 14:55:48 +01:00
if ( role . charAt ( 0 ) != "_" )
2013-08-01 22:40:23 +01:00
{
2013-08-03 14:55:48 +01:00
if ( this . $commsSettings [ "generic" ] && this . $commsSettings [ "generic" ] [ personality ] && this . $commsSettings [ "generic" ] [ personality ] [ key ] && this . $commsSettings [ "generic" ] [ personality ] [ key ] != "" )
{
return this . $commsSettings [ "generic" ] [ personality ] [ key ] ;
}
2013-08-01 22:40:23 +01:00
}
2013-08-03 14:55:48 +01:00
if ( personality . charAt ( 0 ) != "_" )
2013-08-01 22:40:23 +01:00
{
2013-08-03 14:55:48 +01:00
if ( this . $commsSettings [ role ] && this . $commsSettings [ role ] [ "generic" ] && this . $commsSettings [ role ] [ "generic" ] [ key ] && this . $commsSettings [ role ] [ "generic" ] [ key ] != "" )
{
return this . $commsSettings [ role ] [ "generic" ] [ key ] ;
}
2013-08-01 22:40:23 +01:00
}
2013-08-03 14:55:48 +01:00
if ( role . charAt ( 0 ) != "_" && personality . charAt ( 0 ) != "_" )
2013-08-01 22:40:23 +01:00
{
2013-08-03 14:55:48 +01:00
if ( this . $commsSettings [ "generic" ] && this . $commsSettings [ "generic" ] [ "generic" ] && this . $commsSettings [ "generic" ] [ "generic" ] [ key ] && this . $commsSettings [ "generic" ] [ "generic" ] [ key ] != "" )
{
return this . $commsSettings [ "generic" ] [ "generic" ] [ key ] ;
}
2013-08-01 22:40:23 +01:00
}
return "" ;
2013-08-03 17:47:43 +01:00
}
/* Returns the available personalities for a particular role */
this . _getCommunicationPersonalities = function ( role )
{
if ( ! this . $commsSettings [ role ] )
{
return [ ] ;
}
else
{
return Object . keys ( this . $commsSettings [ role ] ) ;
}
}
/ * S e t a c o m m u n i c a t i o n f o r t h e s p e c i f i e d r o l e , p e r s o n a l i t y a n d c o m m s
* key . "generic" is used as a fallback role and personality . * /
this . _setCommunication = function ( role , personality , key , value )
{
if ( ! this . $commsSettings [ role ] )
{
this . $commsSettings [ role ] = { } ;
}
if ( ! this . $commsSettings [ role ] [ personality ] )
{
this . $commsSettings [ role ] [ personality ] = { } ;
}
this . $commsSettings [ role ] [ personality ] [ key ] = value ;
}
/* Bulk setting of communications */
this . _setCommunications = function ( obj )
{
var roles = Object . keys ( obj ) ;
for ( var i = 0 ; i < roles . length ; i ++ )
{
var personalities = Object . keys ( obj [ roles [ i ] ] ) ;
for ( var j = 0 ; j < personalities . length ; j ++ )
{
var keys = Object . keys ( obj [ roles [ i ] ] [ personalities [ j ] ] ) ;
for ( var k = 0 ; k < keys . length ; k ++ )
{
var val = obj [ roles [ i ] ] [ personalities [ j ] ] [ keys [ k ] ] ;
this . _setCommunication ( roles [ i ] , personalities [ j ] , keys [ k ] , val ) ;
}
}
}
2013-08-05 14:14:50 +01:00
}
this . _threatAssessment = function ( ship , full )
{
// there's a ship
var assessment = 1 ;
// slightly tune for speed (+/- 0.1 for most ships)
assessment += ( ship . maxSpeed - 300 ) / 1000 ;
if ( ship . maxSpeed < 200 )
{
assessment += ( ship . maxSpeed - 200 ) / 500 ;
}
// tune for max energy
/ * F I X M E : a t t h e m o m e n t t h i s m e a n s N P C s c a n d e t e c t o t h e r N P C s s h i e l d
* boosters , since they ' re implemented as extra energy * /
assessment += ( ship . maxEnergy - 200 ) / 1000 ;
// a bit extra for missiles
if ( ship . missileCapacity > 2 )
{
assessment += 0.5 ;
}
else
{
assessment += ship . missileCapacity / 5 ;
}
// if the ship is in our group or currently fighting, we can
// determine more about it. (simplified to avoid having to track a
// *lot* of visibility data)
if ( full || ship . hasHostileTarget || ( ship . isPlayer && player . alertCondition == 3 ) )
{
// pilot skill
if ( ship . isPlayer )
{
assessment += Math . pow ( Math . min ( player . score , 6400 ) , 0.33 ) / 10 ;
}
else
{
assessment += ship . accuracy / 10 ;
}
// weapons
if ( ! ship . forwardWeapon )
{
// if ship has single subent forward laser, this -1 gets
// cancelled out
assessment -= 1 ;
}
else
{
assessment += this . _threatSubAssessmentWeapon ( ship . forwardWeapon . equipmentKey ) ;
}
if ( ship . aftWeapon )
{
assessment += 1 + this . _threatSubAssessmentWeapon ( ship . aftWeapon . equipmentKey ) ;
}
if ( ship . portWeapon )
{
assessment += 0.2 + this . _threatSubAssessmentWeapon ( ship . portWeapon . equipmentKey ) ;
}
if ( ship . starboardWeapon )
{
assessment += 0.2 + this . _threatSubAssessmentWeapon ( ship . starboardWeapon . equipmentKey ) ;
}
if ( ship . subEntities )
{
for ( var i = 0 ; i < ship . subEntities . length ; i ++ )
{
if ( ship . subEntities [ i ] . forwardWeapon )
{
assessment += 1 + this . _threatSubAssessmentWeapon ( ship . subEntities [ i ] . forwardWeapon ) ;
}
else if ( ship . subEntities [ i ] . isTurret )
{
/ * T O D O : c o n s i d e r m a k i n g s h i p c o m b a t b e h a v i o u r t r y t o
* stay at long range from enemies with turrets . Then
* we could perhaps reduce this bonus a bit . * /
assessment ++ ;
}
}
}
if ( ship . equipmentStatus ( "EQ_ECM" ) == "EQUIPMENT_OK" )
{
assessment += 0.5 ;
}
if ( ship . equipmentStatus ( "EQ_FUEL_INJECTION" ) == "EQUIPMENT_OK" )
{
assessment += 0.5 ;
}
} // end if full stats available
else
{
// thargoid ships should be counted as tougher
if ( ship . scanClass == "CLASS_THARGOID" )
{
assessment *= 1.5 ;
2013-08-05 20:23:13 +01:00
if ( ship . hasRole ( "thargoid-mothership" ) )
{
assessment += 2.5 ;
}
2013-08-05 14:14:50 +01:00
}
else
{
2013-08-05 20:23:13 +01:00
if ( ship . weaponFacings > 0 )
{
// safety factor for ships we don't know the capabilities of
assessment += 1 ;
}
2013-08-05 14:14:50 +01:00
}
}
// (mostly) ignore fleeing ships
if ( ship . isFleeing )
{
assessment /= 5 ;
}
if ( assessment < 0.1 )
{
assessment = 0.1 ;
}
return assessment ;
}
this . _threatSubAssessmentWeapon = function ( weapon )
{
if ( weapon == "EQ_WEAPON_PULSE_LASER" )
{
return 0 ;
}
else if ( weapon == "EQ_WEAPON_BEAM_LASER" )
{
return 0.33 ;
}
else if ( weapon == "EQ_WEAPON_MILITARY_LASER" )
{
return 1 ;
}
else if ( weapon == "EQ_WEAPON_THARGOID_LASER" )
{
return 1 ;
}
else if ( weapon == "EQ_WEAPON_MINING_LASER" )
{
return - 0.5 ;
}
else // not known
{
return 0 ;
}
}