Add escortAI, update and debug AI library, system.stations property

This commit is contained in:
cim 2013-07-13 23:18:05 +01:00
parent eea8c6843d
commit 0e69dae8f4
6 changed files with 582 additions and 15 deletions

View File

@ -30,7 +30,7 @@ this.name = "Oolite Bounty Hunter AI";
this.version = "1.79"; this.version = "1.79";
this.aiStarted = function() { this.aiStarted = function() {
this.ai = new worldScripts["oolite-libPriorityAI"].AILib(this.ship); var ai = new worldScripts["oolite-libPriorityAI"].AILib(this.ship);
ai.setParameter("oolite_flag_listenForDistressCall",true); ai.setParameter("oolite_flag_listenForDistressCall",true);

115
Resources/AIs/escortAI.js Normal file
View File

@ -0,0 +1,115 @@
/*
escortAI.js
Priority-based AI for escorts
Oolite
Copyright © 2004-2013 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
"use strict";
this.name = "Oolite Escort AI";
this.version = "1.79";
this.aiStarted = function() {
var ai = new worldScripts["oolite-libPriorityAI"].AILib(this.ship);
ai.setPriorities([
{
condition: ai.conditionLosingCombat,
behaviour: ai.behaviourFleeCombat,
reconsider: 5
},
{
condition: ai.conditionMothershipInCombat,
truebranch: [
{
condition: ai.conditionMothershipUnderAttack,
configuration: ai.configurationAcquireDefensiveEscortTarget,
behaviour: ai.behaviourRepelCurrentTarget,
reconsider: 5
},
{
condition: ai.conditionMothershipIsAttacking,
configuration: ai.configurationAcquireOffensiveEscortTarget,
behaviour: ai.behaviourDestroyCurrentTarget,
reconsider: 5
},
{
behaviour: ai.behaviourRejoinMothership,
reconsider: 5
}
]
},
{
// if we're in combat but mothership isn't, then we need
// to finish this fight off and get back to them
condition: ai.conditionInCombat,
configuration: ai.configurationAcquireCombatTarget,
behaviour: ai.behaviourRepelCurrentTarget,
reconsider: 5
},
{
condition: ai.conditionHasMothership,
behaviour: ai.behaviourEscortMothership,
reconsider: 60
},
/* Don't have a mothership */
{
condition: ai.conditionWitchspaceEntryRequested,
behaviour: ai.behaviourEnterWitchspace,
reconsider: 15
},
/* And it's not because they've recently jumped out */
{
condition: ai.conditionFriendlyStationNearby,
configuration: ai.configurationSetNearbyFriendlyStationForDocking,
behaviour: ai.behaviourDockWithStation,
reconsider: 30
},
/* And it's not because they just docked either */
{
preconfiguration: ai.configurationCheckScanner,
condition: ai.conditionScannerContainsShipNeedingEscort,
behaviour: ai.behaviourOfferToEscort,
reconsider: 15
},
{
condition: ai.conditionFriendlyStationExists,
configuration: ai.configurationSetDestinationToNearestFriendlyStation,
behaviour: ai.behaviourApproachDestination,
reconsider: 30
},
/* No friendly stations and no nearby ships needing escort */
{
condition: ai.conditionCanWitchspaceOut,
configuration: ai.configurationSelectWitchspaceDestination,
behaviour: ai.behaviourEnterWitchspace,
reconsider: 20
},
/* And we're stuck here */
{
configuration: ai.configurationSetDestinationToWitchpoint,
behaviour: ai.approachDestination,
reconsider: 30
}
]);
}

View File

@ -230,6 +230,37 @@ this.AILib = function(ship)
} }
} }
this.friendlyStation = function(station)
{
return (station.target != this.ship || !station.hasHostileTarget);
}
this.cruiseSpeed = function()
{
var cruise = this.ship.maxSpeed * 0.8;
if (this.ship.group)
{
for (var i = 0 ; i < this.ship.group.ships.length ; i++)
{
if (cruise > this.ship.group.ships[i].maxSpeed)
{
cruise = this.ship.group.ships[i].maxSpeed;
}
}
}
if (this.ship.escortGroup)
{
for (var i = 0 ; i < this.ship.escortGroup.ships.length ; i++)
{
if (cruise > this.ship.escortGroup.ships[i].maxSpeed)
{
cruise = this.ship.escortGroup.ships[i].maxSpeed;
}
}
}
return cruise;
}
/* ****************** Condition functions ************** */ /* ****************** Condition functions ************** */
@ -324,6 +355,82 @@ this.AILib = function(ship)
return false; return false;
} }
this.conditionHasMothership = function()
{
return (this.ship.group && this.ship.group.leader != this.ship && this.ship.group.leader.escortGroup.containsShip(this.ship));
}
this.conditionMothershipInCombat = function()
{
if (this.ship.group && this.ship.group.leader != this.ship && this.ship.group.leader.escortGroup.containsShip(this.ship))
{
var leader = this.ship.group.leader;
if (leader.position.distanceTo(this.ship) > this.ship.scannerRange)
{
return false; // can't tell
}
if (leader.hasHostileTarget)
{
return true;
}
if (leader.target && leader.target.target == leader && leader.target.hasHostileTarget)
{
return true;
}
var dts = leader.defenseTargets;
for (var i = 0 ; i < dts.length ; i++)
{
if (dts[i].target == leader && dts[i].hasHostileTarget)
{
return true;
}
}
return false;
}
else
{
// no mothership
return false;
}
}
this.conditionMothershipUnderAttack = function()
{
if (this.ship.group && this.ship.group.leader != this.ship && this.ship.group.leader.escortGroup.containsShip(this.ship))
{
var leader = this.ship.group.leader;
if (leader.target.target == leader && leader.target.hasHostileTarget && leader.target.position.distanceTo(this.ship) < this.ship.scannerRange)
{
return true;
}
var dts = leader.defenseTargets;
for (var i = 0 ; i < dts.length ; i++)
{
if (dts[i].target == leader && dts[i].hasHostileTarget && dts[i].position.distanceTo(this.ship) < this.ship.scannerRange)
{
return true;
}
}
return false;
}
else
{
return false;
}
}
this.conditionMothershipIsAttacking = function()
{
if (this.ship.group && this.ship.group.leader != this.ship && this.ship.group.leader.escortGroup.containsShip(this.ship))
{
var leader = this.ship.group.leader;
if (leader.target && leader.hasHostileTarget && leader.target.position.distanceTo(this.ship) < this.ship.scannerRange)
{
return true;
}
}
return false;
}
this.conditionNearDestination = function() this.conditionNearDestination = function()
{ {
@ -348,7 +455,7 @@ this.AILib = function(ship)
this.conditionScannerContainsSalvage = function() this.conditionScannerContainsSalvage = function()
{ {
if (this.ship.cargoSpaceAvailable == 0) if (this.ship.cargoSpaceAvailable == 0 || this.ship.equipmentStatus("EQ_FUEL_SCOOPS") != "EQUIPMENT_OK")
{ {
return false; return false;
} }
@ -381,6 +488,71 @@ this.AILib = function(ship)
return system.isInterstellarSpace; return system.isInterstellarSpace;
} }
this.conditionWitchspaceEntryRequested = function()
{
return (this.getParameter("oolite_witchspaceWormhole") != null);
}
this.conditionFriendlyStationNearby = function()
{
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)
{
return true;
}
}
}
return false;
}
this.conditionFriendlyStationExists = function()
{
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
return true;
}
}
return false;
}
this.conditionScannerContainsShipNeedingEscort = function()
{
if (this.ship.bounty == 0)
{
return this.checkScannerWithPredicate(function(s) {
return s.bounty == 0 && (!s.escortGroup || s.escortGroup.count <= s.maxEscorts);
});
}
else
{
return this.checkScannerWithPredicate(function(s) {
return s.bounty > 0 && (!s.escortGroup || s.escortGroup.count <= s.maxEscorts);
});
}
}
this.conditionCanWitchspaceOut = function()
{
if (!this.ship.hasHyperspaceMotor)
{
return false;
}
return (system.info.systemsInRange(this.ship.fuel).length > 0);
}
/* ****************** Behaviour functions ************** */ /* ****************** Behaviour functions ************** */
/* Behaviours. Behaviours are effectively a state definition, /* Behaviours. Behaviours are effectively a state definition,
@ -444,6 +616,30 @@ this.AILib = function(ship)
} }
this.behaviourRepelCurrentTarget = function()
{
var handlers = {};
this.responsesAddStandard(handlers);
this.setUpHandlers(handlers);
if (this.ship.target.isFleeing || this.ship.target.isDerelict)
{
// repelling succeeded
this.ship.removeDefenseTarget(this.ship.target);
this.ship.target = null;
this.reconsiderNow();
}
else
{
if (!this.ship.hasHostileTarget)
{
// entering attack mode
this.communicate("oolite_beginningAttack",this.ship.target.displayName);
}
this.ship.performAttack();
}
}
this.behaviourCollectSalvage = function() this.behaviourCollectSalvage = function()
{ {
var handlers = {}; var handlers = {};
@ -609,6 +805,15 @@ this.AILib = function(ship)
// the wormhole we were trying for has expired // the wormhole we were trying for has expired
this.setParameter("oolite_witchspaceWormhole",null); this.setParameter("oolite_witchspaceWormhole",null);
} }
else if (wormhole)
{
this.ship.destination = wormhole.position;
this.ship.desiredRange = 0;
this.ship.desiredSpeed = this.ship.maxSpeed;
this.ship.performFlyToRangeFromDestination();
this.setUpHandlers(handlers);
return;
}
var destID = this.getParameter("oolite_witchspaceDestination"); var destID = this.getParameter("oolite_witchspaceDestination");
if (destID == null) if (destID == null)
@ -645,7 +850,7 @@ this.AILib = function(ship)
{ {
this.ship.setDestination = blocker.position; this.ship.setDestination = blocker.position;
this.ship.setDesiredRange = 30000; this.ship.setDesiredRange = 30000;
this.ship.setDesiredSpeed = this.ship.maxSpeed; this.ship.setDesiredSpeed = this.cruiseSpeed();
this.ship.performFlyToRangeFromDestination(); this.ship.performFlyToRangeFromDestination();
// no reconsidering yet // no reconsidering yet
} }
@ -661,6 +866,56 @@ this.AILib = function(ship)
} }
} }
this.behaviourEscortMothership = function()
{
var handlers = {};
this.responsesAddStandard(handlers);
this.responsesAddEscort(handlers);
this.setUpHandlers(handlers);
this.ship.desiredRange = 0;
this.ship.performEscort();
}
// Separate behaviour just in case we want to change it later
// This is the one to catch up with a distant mothership
this.behaviourRejoinMothership = function()
{
var handlers = {};
this.responsesAddStandard(handlers);
this.responsesAddEscort(handlers);
this.setUpHandlers(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();
}
this.behaviourOfferToEscort = function()
{
var handlers = {};
this.responsesAddStandard(handlers);
this.setUpHandlers(handlers);
var possible = this.getParameter("oolite_scanResultSpecific");
if (possible == null)
{
this.reconsiderNow();
}
else
{
if (this.ship.offerToEscort(possible))
{
// accepted
this.reconsiderNow();
}
// if rejected, wait for next scheduled reconsideration
}
}
/* ****************** Configuration functions ************** */ /* ****************** Configuration functions ************** */
/* Configurations. Configurations are set up actions for a behaviour /* Configurations. Configurations are set up actions for a behaviour
@ -676,18 +931,21 @@ this.AILib = function(ship)
return; return;
} }
var dts = this.ship.defenseTargets var dts = this.ship.defenseTargets
if (dts.length > 0) for (var i = 0; i < dts.length ; i++)
{
if (dts[i].position.distanceTo(this.ship) < this.ship.scannerRange)
{ {
this.ship.target = dts[0]; this.ship.target = dts[0];
return; return;
} }
}
if (this.ship.group != null) if (this.ship.group != null)
{ {
for (var i = 0 ; i < this.ship.group.length ; i++) for (var i = 0 ; i < this.ship.group.length ; i++)
{ {
if (this.ship.group.ships[i] != this.ship) if (this.ship.group.ships[i] != this.ship)
{ {
if (this.ship.group.ships[i].target && this.ship.group.ships[i].hasHostileTarget) if (this.ship.group.ships[i].target && this.ship.group.ships[i].hasHostileTarget && this.ship.group.ships[i].target.position.distanceTo(this.ship) < this.ship.scannerRange)
{ {
this.ship.target = this.ship.group.ships[i].target; this.ship.target = this.ship.group.ships[i].target;
return; return;
@ -701,7 +959,7 @@ this.AILib = function(ship)
{ {
if (this.ship.escortGroup.ships[i] != this.ship) if (this.ship.escortGroup.ships[i] != this.ship)
{ {
if (this.ship.escortGroup.ships[i].target && this.ship.escortGroup.ships[i].hasHostileTarget) if (this.ship.escortGroup.ships[i].target && this.ship.escortGroup.ships[i].hasHostileTarget && this.ship.escortGroup.ships[i].target.position.distanceTo(this.ship) < this.ship.scannerRange)
{ {
this.ship.target = this.ship.escortGroup.ships[i].target; this.ship.target = this.ship.escortGroup.ships[i].target;
return; return;
@ -711,6 +969,53 @@ this.AILib = function(ship)
} }
} }
this.configurationAcquireOffensiveEscortTarget = function()
{
if (this.ship.group && this.ship.group.leader && this.ship.group.leader.hasHostileTarget)
{
if (this.ship.distanceTo(this.ship.group.leader.target) < this.ship.scannerRange)
{
this.ship.target = this.ship.group.leader.target;
this.ship.addDefenseTarget(this.ship.target);
}
}
}
this.configurationAcquireDefensiveEscortTarget = function()
{
if (this.ship.group && this.ship.group.leader)
{
var leader = this.ship.group.leader;
if (leader.target.target == leader && leader.hasHostileTarget && leader.target.position.distanceTo(this.ship) < this.ship.scannerRange)
{
this.ship.target = leader.target;
}
else
{
var dts = leader.defenseTargets;
for (var i = 0 ; i < dts.length ; i++)
{
if (dts[i].target == leader && dts[i].hasHostileTarget && !dts[i].isFleeing && dts[i].position.distanceTo(this.ship) < this.ship.scannerRange)
{
this.ship.target = dts[i];
}
}
}
}
}
this.configurationAcquireOffensiveEscortTarget = function()
{
if (this.ship.group && this.ship.group.leader)
{
var leader = this.ship.group.leader;
if (leader.hasHostileTarget)
{
this.ship.target = leader.target;
}
}
}
this.configurationCheckScanner = function() this.configurationCheckScanner = function()
{ {
this.setParameter("oolite_scanResults",this.ship.checkScanner()); this.setParameter("oolite_scanResults",this.ship.checkScanner());
@ -722,11 +1027,49 @@ this.AILib = function(ship)
this.ship.target = this.getParameter("oolite_scanResultSpecific"); this.ship.target = this.getParameter("oolite_scanResultSpecific");
} }
this.configurationSetDestinationToWitchpoint = function()
{
this.ship.destination = new Vector3D(0,0,0);
this.ship.desiredRange = 10000;
this.ship.desiredSpeed = this.cruiseSpeed();
}
this.configurationSetDestinationToNearestFriendlyStation = function()
{
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();
}
}
this.configurationSetDestinationFromPatrolRoute = function() this.configurationSetDestinationFromPatrolRoute = function()
{ {
this.ship.destination = this.getParameter("oolite_patrolRoute"); this.ship.destination = this.getParameter("oolite_patrolRoute");
this.ship.desiredRange = this.getParameter("oolite_patrolRouteRange"); this.ship.desiredRange = this.getParameter("oolite_patrolRouteRange");
this.ship.desiredSpeed = this.ship.maxSpeed; this.ship.desiredSpeed = this.cruiseSpeed();
} }
this.configurationMakeSpacelanePatrolRoute = function() this.configurationMakeSpacelanePatrolRoute = function()
@ -845,6 +1188,26 @@ this.AILib = function(ship)
this.setParameter("oolite_witchspaceDestination",possible[Math.floor(Math.random()*possible.length)].systemID); this.setParameter("oolite_witchspaceDestination",possible[Math.floor(Math.random()*possible.length)].systemID);
} }
this.configurationSetNearbyFriendlyStationForDocking = function()
{
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;
}
}
}
}
/* ****************** Response definition functions ************** */ /* ****************** Response definition functions ************** */
/* Standard state-machine responses. These set up a set of standard /* Standard state-machine responses. These set up a set of standard
@ -853,7 +1216,8 @@ this.AILib = function(ship)
* priorities. Many behaviours will need to supplement the standard * priorities. Many behaviours will need to supplement the standard
* responses with additional definitions. */ * responses with additional definitions. */
this.responsesAddStandard = function(handlers) { this.responsesAddStandard = function(handlers)
{
handlers.cascadeWeaponDetected = function(weapon) handlers.cascadeWeaponDetected = function(weapon)
{ {
this.ship.clearDefenseTargets(); this.ship.clearDefenseTargets();
@ -957,6 +1321,15 @@ this.AILib = function(ship)
{ {
this.reconsiderNow(); this.reconsiderNow();
} }
handlers.shipLaunchedFromStation = function()
{
this.reconsiderNow();
}
handlers.shipExitedWormhole = function()
{
this.reconsiderNow();
}
handlers.distressMessageReceived = function(aggressor, sender) handlers.distressMessageReceived = function(aggressor, sender)
{ {
if (this.getParameter("oolite_flag_listenForDistressCall") != true) if (this.getParameter("oolite_flag_listenForDistressCall") != true)
@ -967,10 +1340,25 @@ this.AILib = function(ship)
this.setParameter("oolite_distressSender",sender); this.setParameter("oolite_distressSender",sender);
this.reconsiderNow(); this.reconsiderNow();
} }
handlers.playerWillEnterWitchspace = function()
{
var wormhole = this.getParameter("oolite_witchspaceWormhole");
if (wormhole != null)
{
this.ship.enterWormhole(wormhole);
}
}
handlers.wormholeSuggested = function(hole)
{
this.setParameter("oolite_witchspaceWormhole",hole);
this.reconsiderNow();
}
// TODO: more event handlers // TODO: more event handlers
} }
this.responsesAddDocking = function(handlers) { /* Additional handlers for use while docking */
this.responsesAddDocking = function(handlers)
{
handlers.stationWithdrewDockingClearance = function() handlers.stationWithdrewDockingClearance = function()
{ {
this.setParameter("oolite_dockingStation",null); this.setParameter("oolite_dockingStation",null);
@ -985,10 +1373,49 @@ this.AILib = function(ship)
this.reconsiderNow(); this.reconsiderNow();
} }
}; };
}
/* Override of standard handlers for use while escorting */
this.responsesAddEscort = function(handlers)
{
handlers.helpRequestReceived = function(ally, enemy)
{
// always help the leader
if (ally == this.ship.group.leader)
{
if (!this.ship.target || this.ship.target.target != ally)
{
this.ship.target = enemy;
this.reconsiderNow();
return;
}
}
this.ship.addDefenseTarget(enemy);
if (!this.ship.hasHostileTarget)
{
this.reconsiderNow();
return; // not in a combat mode
}
if (ally.energy / ally.maxEnergy < this.ship.energy / this.ship.maxEnergy)
{
// 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;
this.reconsiderNow();
}
}
}
handlers.escortDock = function()
{
this.reconsiderNow();
}
} }
}; // end object constructor }; // end object constructor

View File

@ -2199,6 +2199,8 @@ ShipEntity* doOctreesCollide(ShipEntity* prime, ShipEntity* other)
{ {
StationEntity *stationLaunchedFrom = [UNIVERSE nearestEntityMatchingPredicate:IsStationPredicate parameter:NULL relativeToEntity:self]; StationEntity *stationLaunchedFrom = [UNIVERSE nearestEntityMatchingPredicate:IsStationPredicate parameter:NULL relativeToEntity:self];
[self setStatus:STATUS_IN_FLIGHT]; [self setStatus:STATUS_IN_FLIGHT];
// awaken JS-based AIs
[self doScriptEvent:OOJSID("aiStarted")];
[self doScriptEvent:OOJSID("shipLaunchedFromStation") withArgument:stationLaunchedFrom]; [self doScriptEvent:OOJSID("shipLaunchedFromStation") withArgument:stationLaunchedFrom];
[shipAI reactToMessage:@"LAUNCHED OKAY" context:@"launched"]; [shipAI reactToMessage:@"LAUNCHED OKAY" context:@"launched"];
} }
@ -12178,10 +12180,15 @@ Vector positionOffsetForShipInRotationToAlignment(ShipEntity* ship, Quaternion q
idleEscorts = [NSMutableSet set]; idleEscorts = [NSMutableSet set];
for (escortEnum = [self escortEnumerator]; (escort = [escortEnum nextObject]); ) for (escortEnum = [self escortEnumerator]; (escort = [escortEnum nextObject]); )
{ {
if (![[[escort getAI] name] isEqualToString:@"interceptAI.plist"]) if (![[[escort getAI] name] isEqualToString:@"interceptAI.plist"] && ![[[escort getAI] name] isEqualToString:@"nullAI.plist"])
{ {
[idleEscorts addObject:escort]; [idleEscorts addObject:escort];
} }
else if ([[[escort getAI] name] isEqualToString:@"nullAI.plist"])
{
// JS-based escorts get a help request
[escort doScriptEvent:OOJSID("helpRequestReceived") withArgument:self andArgument:[self primaryTarget]];
}
} }
escortCount = [idleEscorts count]; escortCount = [idleEscorts count];
@ -12225,7 +12232,10 @@ Vector positionOffsetForShipInRotationToAlignment(ShipEntity* ship, Quaternion q
if ([escort owner] == self) [escort setOwner:escort]; if ([escort owner] == self) [escort setOwner:escort];
if(target && [target isStation]) [escort setTargetStation:target]; if(target && [target isStation]) [escort setTargetStation:target];
// JSAI: needs slightly different implementation of delay // JSAI: needs slightly different implementation of delay
if (![[[escort getAI] name] isEqualToString:@"nullAI.plist"])
{
[escort setAITo:@"dockingAI.plist"]; [escort setAITo:@"dockingAI.plist"];
}
[ai setState:@"ABORT" afterDelay:delay + 0.25]; [ai setState:@"ABORT" afterDelay:delay + 0.25];
[escort doScriptEvent:OOJSID("escortDock") withArgument:[NSNumber numberWithFloat:delay]]; [escort doScriptEvent:OOJSID("escortDock") withArgument:[NSNumber numberWithFloat:delay]];
} }

View File

@ -433,8 +433,11 @@ static void DrawWormholeCorona(GLfloat inner_radius, GLfloat outer_radius, int s
} }
[ship setSpeed:[self exitSpeed]]; // all ships from this wormhole have same velocity [ship setSpeed:[self exitSpeed]]; // all ships from this wormhole have same velocity
// awaken JS-based AIs
[ship doScriptEvent:OOJSID("aiStarted")];
// Should probably pass the wormhole, but they have no JS representation // Wormholes now have a JS representation, so we could provide it
// but is it worth it for the exit wormhole?
[ship doScriptEvent:OOJSID("shipExitedWormhole") andReactToAIMessage:@"EXITED WITCHSPACE"]; [ship doScriptEvent:OOJSID("shipExitedWormhole") andReactToAIMessage:@"EXITED WITCHSPACE"];
// update the ships's position // update the ships's position

View File

@ -130,6 +130,7 @@ enum
kSystem_pseudoRandom256, // constant-per-system pseudorandom number in [0..256), integer, read-only kSystem_pseudoRandom256, // constant-per-system pseudorandom number in [0..256), integer, read-only
kSystem_pseudoRandomNumber, // constant-per-system pseudorandom number in [0..1), double, read-only kSystem_pseudoRandomNumber, // constant-per-system pseudorandom number in [0..1), double, read-only
kSystem_sun, // system's sun, Planet, read-only kSystem_sun, // system's sun, Planet, read-only
kSystem_stations, // list of dockable entities, read-only
kSystem_techLevel, // tech level ID, integer, read/write kSystem_techLevel, // tech level ID, integer, read/write
}; };
@ -159,6 +160,7 @@ static JSPropertySpec sSystemProperties[] =
{ "pseudoRandom100", kSystem_pseudoRandom100, OOJS_PROP_READONLY_CB }, { "pseudoRandom100", kSystem_pseudoRandom100, OOJS_PROP_READONLY_CB },
{ "pseudoRandom256", kSystem_pseudoRandom256, OOJS_PROP_READONLY_CB }, { "pseudoRandom256", kSystem_pseudoRandom256, OOJS_PROP_READONLY_CB },
{ "pseudoRandomNumber", kSystem_pseudoRandomNumber, OOJS_PROP_READONLY_CB }, { "pseudoRandomNumber", kSystem_pseudoRandomNumber, OOJS_PROP_READONLY_CB },
{ "stations", kSystem_stations, OOJS_PROP_READONLY_CB },
{ "sun", kSystem_sun, OOJS_PROP_READONLY_CB }, { "sun", kSystem_sun, OOJS_PROP_READONLY_CB },
{ "techLevel", kSystem_techLevel, OOJS_PROP_READWRITE_CB }, { "techLevel", kSystem_techLevel, OOJS_PROP_READWRITE_CB },
{ 0 } { 0 }
@ -259,6 +261,16 @@ static JSBool SystemGetProperty(JSContext *context, JSObject *this, jsid propID,
handled = YES; handled = YES;
break; break;
case kSystem_stations:
/* Optimise? This may be called enough times that it's worth
* having a list of stations cached and edited on station
* creation/destruction, as we do for planets - CIM 13/7/2013 */
OOJS_BEGIN_FULL_NATIVE(context)
result = [UNIVERSE findShipsMatchingPredicate:IsStationPredicate parameter:NULL inRange:-1 ofEntity:nil];
OOJS_END_FULL_NATIVE
handled = YES;
break;
case kSystem_allShips: case kSystem_allShips:
OOJS_BEGIN_FULL_NATIVE(context) OOJS_BEGIN_FULL_NATIVE(context)
result = [UNIVERSE findShipsMatchingPredicate:JSEntityIsJavaScriptSearchablePredicate parameter:NULL inRange:-1 ofEntity:nil]; result = [UNIVERSE findShipsMatchingPredicate:JSEntityIsJavaScriptSearchablePredicate parameter:NULL inRange:-1 ofEntity:nil];