First go at pirateFreighter/Fighter AIs

Fix bug with launching ships with escorts, tidy up functions
New: ship.notifyGroupOfWormhole()
Introduce AI template blocks to compact AI writing even further.
This commit is contained in:
cim 2013-08-09 13:09:54 +01:00
parent bcc93120d7
commit b00b1bb550
11 changed files with 629 additions and 237 deletions

View File

@ -33,7 +33,7 @@ this.aiStarted = function() {
this.ai = new worldScripts["oolite-libPriorityAI"].AILib(this.ship);
ai.setParameter("oolite_flag_watchForCargo",true);
/* This communication is necessary but needs more variety and moving to descriptions.plist so it can be translated */
ai.setCommunicationsRole("pirate");
@ -107,45 +107,7 @@ this.aiStarted = function() {
truebranch: [
{
condition: ai.conditionIsGroupLeader,
truebranch: [
{
condition: ai.conditionFriendlyStationNearby,
configuration: ai.configurationSetNearbyFriendlyStationForDocking,
behaviour: ai.behaviourDockWithStation,
reconsider: 30
},
{
condition: ai.conditionFriendlyStationExists,
configuration: ai.configurationSetDestinationToNearestFriendlyStation,
behaviour: ai.behaviourApproachDestination,
reconsider: 30
},
{
condition: ai.conditionHasSelectedPlanet,
truebranch: [
{
preconfiguration: ai.configurationSetDestinationToSelectedPlanet,
condition: ai.conditionNearDestination,
behaviour: ai.behaviourLandOnPlanet
},
{
behaviour: ai.behaviourApproachDestination,
reconsider: 30
}
]
},
{
condition: ai.conditionPlanetExists,
configuration: ai.configurationSelectPlanet,
behaviour: ai.behaviourReconsider
},
{
condition: ai.conditionCanWitchspaceOut,
configuration: ai.configurationSelectWitchspaceDestination,
behaviour: ai.behaviourEnterWitchspace,
reconsider: 20
}
]
truebranch: ai.templateReturnToBaseOrPlanet()
},
/* Once the group leader has docked or landed, another one gets
* appointed, and they can decide what to do next */
@ -159,28 +121,7 @@ this.aiStarted = function() {
falsebranch: [
{
condition: ai.conditionIsGroupLeader,
truebranch: [
{
preconfiguration: ai.configurationForgetCargoDemand,
condition: ai.conditionScannerContainsPirateVictims,
configuration: ai.configurationAcquireScannedTarget,
truebranch: [
{
label: "Check odds",
condition: ai.conditionCombatOddsGood,
behaviour: ai.behaviourRobTarget,
reconsider: 5
}
]
},
{
/* move to a position on one of the space lanes, preferring lane 1 */
label: "Lurk",
configuration: ai.configurationSetDestinationToPirateLurk,
behaviour: ai.behaviourApproachDestination,
reconsider: 30
},
]
truebranch: ai.templateLeadPirateMission()
},
{
behaviour: ai.behaviourFollowGroupLeader,

View File

@ -0,0 +1,134 @@
/*
pirateFighterAI.js
Priority-based AI for pirate fighters (guards and extra capacity for
organised pirate gangs)
Oolite
Copyright © 2004-2013 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
"use strict";
this.name = "Oolite Pirate Fighter AI";
this.version = "1.79";
this.aiStarted = function() {
this.ai = new worldScripts["oolite-libPriorityAI"].AILib(this.ship);
ai.setParameter("oolite_flag_watchForCargo",true);
ai.setCommunicationsRole("pirate");
ai.setPriorities([
/* Combat */
{
condition: ai.conditionLosingCombat,
behaviour: ai.behaviourFleeCombat,
reconsider: 5
},
{
label: "Cargo demands met?",
condition: ai.conditionCargoDemandsMet,
/* Let them go if they've dropped enough cargo and stop firing back */
truebranch: [
{
condition: ai.conditionInCombatWithHostiles,
configuration: ai.configurationAcquireHostileCombatTarget,
behaviour: ai.behaviourRepelCurrentTarget,
reconsider: 5
}
],
falsebranch: [
{
condition: ai.conditionInCombat,
configuration: ai.configurationAcquireCombatTarget,
behaviour: ai.behaviourDestroyCurrentTarget,
reconsider: 5
}
]
},
{
condition: ai.conditionWitchspaceEntryRequested,
behaviour: ai.behaviourEnterWitchspace,
reconsider: 15
},
{
preconfiguration: ai.configurationCheckScanner,
condition: ai.conditionScannerContainsSalvageForGroup,
truebranch: [
{
condition: ai.conditionScannerContainsSalvageForMe,
configuration: ai.configurationAcquireScannedTarget,
behaviour: ai.behaviourCollectSalvage,
reconsider: 20
},
// if can't scoop, hang around waiting for the others,
// unless the entire group has enough cargo
{
notcondition: ai.conditionGroupHasEnoughLoot,
configuration: ai.configurationSetDestinationToGroupLeader,
behaviour: ai.behaviourApproachDestination,
reconsider: 15
}
]
},
/* Stay out of the way of hunters */
{
condition: ai.conditionScannerContainsHunters,
configuration: ai.configurationAcquireScannedTarget,
truebranch: [
{
condition: ai.conditionCombatOddsExcellent,
behaviour: ai.behaviourDestroyCurrentTarget,
reconsider: 10
},
{
behaviour: ai.behaviourLeaveVicinityOfTarget,
reconsider: 20
}
]
},
/* Follow leader */
{
condition: ai.conditionHasMothership,
configuration: ai.configurationSetDestinationToGroupLeader,
behaviour: ai.behaviourApproachDestination,
reconsider: 15
},
/* Find a new leader, or return to base */
{
condition: ai.conditionScannerContainsPirateLeader,
configuration: ai.configurationAcquireScannedTarget,
behaviour: ai.behaviourJoinTargetGroup,
reconsider: 10,
falsebranch: ai.templateReturnToBaseOrPlanet()
},
{
// full of loot, but stuck in system and no friendly stations
configuration: ai.configurationSetDestinationToWitchpoint,
// TODO: behaviour search for wormholes
behaviour: ai.behaviourApproachDestination,
reconsider: 30
}
]);
}

View File

@ -0,0 +1,155 @@
/*
pirateFreighterAI.js
Priority-based AI for pirate freighters leading large pirate groups
Oolite
Copyright © 2004-2013 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
"use strict";
this.name = "Oolite Pirate Freighter AI";
this.version = "1.79";
this.aiStarted = function() {
var ai = new worldScripts["oolite-libPriorityAI"].AILib(this.ship);
ai.setParameter("oolite_flag_watchForCargo",true);
ai.setCommunicationsRole("pirate");
// combat and looting behaviour same at all stages
var common = [
/* Combat */
{
condition: ai.conditionLosingCombat,
behaviour: ai.behaviourFleeCombat,
reconsider: 5
},
{
label: "Cargo demands met?",
condition: ai.conditionCargoDemandsMet,
/* Let them go if they've dropped enough cargo and stop firing back */
truebranch: [
{
condition: ai.conditionInCombatWithHostiles,
configuration: ai.configurationAcquireHostileCombatTarget,
behaviour: ai.behaviourRepelCurrentTarget,
reconsider: 5
}
],
falsebranch: [
{
condition: ai.conditionInCombat,
configuration: ai.configurationAcquireCombatTarget,
behaviour: ai.behaviourDestroyCurrentTarget,
reconsider: 5
}
]
},
/* Collect loot. Don't worry too much about other group
* members. If they pick up stuff too that's good, but this is
* the ship with the hold... */
{
preconfiguration: ai.configurationCheckScanner,
condition: ai.conditionScannerContainsSalvageForMe,
configuration: ai.configurationAcquireScannedTarget,
behaviour: ai.behaviourCollectSalvage,
reconsider: 20
},
/* Are there hunters about? Avoid, or destroy if safe to do so. */
{
condition: ai.conditionScannerContainsHunters,
configuration: ai.configurationAcquireScannedTarget,
truebranch: [
{
condition: ai.conditionCombatOddsExcellent,
behaviour: ai.behaviourDestroyCurrentTarget,
reconsider: 10
},
{
behaviour: ai.behaviourLeaveVicinityOfTarget,
reconsider: 20
}
]
}
];
var specific;
if (this.ship.homeSystem == this.ship.destinationSystem)
{
// local piracy
specific = [
{
label: "Enough loot?",
condition: ai.conditionGroupHasEnoughLoot,
truebranch: ai.templateReturnToBaseOrPlanet(),
falsebranch: ai.templateLeadPirateMission()
}
];
}
else if (this.ship.homeSystem == system.ID && this.ship.fuel == 7)
{
// jump to destination system, taking group
specific = ai.templateWitchspaceJumpOutbound().concat(ai.templateReturnToBaseOrPlanet);
}
else if (this.ship.homeSystem == system.ID)
{
// if not at full fuel, we're probably returning home. Or
// something went wrong when trying to enter witchspace that
// needed injectors to fix.
specific = ai.templateReturnToBaseOrPlanet();
}
else
{
// pirate work, until enough cargo gathered or lose too many
// fighters, then jump home if we're in witchspace, just jump
// home immediately!
specific = [
{
condition: ai.conditionGroupHasEnoughLoot,
truebranch: ai.templateWitchspaceJumpOutbound()
},
{
condition: ai.conditionGroupAttritionReached,
truebranch: ai.templateWitchspaceJumpOutbound()
},
{
condition: ai.conditionInInterstellarSpace,
truebranch: ai.templateWitchspaceJumpOutbound(),
falsebranch: ai.templateLeadPirateMission()
}
];
}
var fallback = [
{
// stuck in system and no friendly stations
configuration: ai.configurationSetDestinationToWitchpoint,
// TODO: behaviour search for wormholes
behaviour: ai.behaviourApproachDestination,
reconsider: 30
}
];
var priorities = common.concat(specific).concat(fallback);
ai.setPriorities(priorities);
}

View File

@ -73,63 +73,9 @@ this.aiStarted = function() {
{
condition: ai.conditionCargoIsProfitableHere,
// branch to head for station
truebranch: [
{
condition: ai.conditionHasSelectedStation,
truebranch: [
{
condition: ai.conditionSelectedStationNearby,
configuration: ai.configurationSetSelectedStationForDocking,
behaviour: ai.behaviourDockWithStation,
reconsider: 30
},
{
condition: ai.conditionSelectedStationNearMainPlanet,
truebranch: [
{
notcondition: ai.conditionMainPlanetNearby,
configuration: ai.configurationSetDestinationToMainPlanet,
behaviour: ai.behaviourApproachDestination,
reconsider: 30
}
]
},
// either the station isn't near the planet, or we are
{
configuration: ai.configurationSetDestinationToSelectedStation,
behaviour: ai.behaviourApproachDestination,
reconsider: 30
}
],
falsebranch: [
{
configuration: ai.configurationSelectRandomTradeStation,
behaviour: ai.behaviourReconsider
}
]
}
],
truebranch: ai.templateReturnToBase(),
// jump to another system if possible, sunskim if not
falsebranch: [
{
preconfiguration: ai.configurationSelectWitchspaceDestinationOutbound,
condition: ai.conditionCanWitchspaceOnRoute,
behaviour: ai.behaviourEnterWitchspace,
reconsider: 20
},
{
condition: ai.conditionReadyToSunskim,
configuration: ai.configurationSetDestinationToSunskimEnd,
behaviour: ai.behaviourSunskim,
reconsider: 20
},
{
condition: ai.conditionSunskimPossible,
configuration: ai.configurationSetDestinationToSunskimStart,
behaviour: ai.behaviourApproachDestination,
reconsider: 30
}
]
falsebranch: ai.templateWitchspaceJumpOutbound()
}, // end of cargoprofitable true/false branches
{
// if we're here, the cargo isn't profitable, and we can't

View File

@ -65,7 +65,6 @@ this.AILib = function(ship)
/* Cache variables used by utility functions */
var condmet = true;
var logging = false;
/* Private utility functions. Cannot be called from external code */
@ -89,7 +88,7 @@ this.AILib = function(ship)
/* Considers a priority list, potentially recursively */
function _reconsiderList(priorities) {
logging = this.getParameter("oolite_flag_behaviourLogging")
var logging = this.getParameter("oolite_flag_behaviourLogging");
var pl = priorities.length;
if (logging)
{
@ -311,6 +310,20 @@ this.AILib = function(ship)
}
this.clearHandlers = function()
{
// delete all handlers to allow rebinding
activeHandlers = Object.keys(handlerCache);
for (var i=0; i < activeHandlers.length ; i++)
{
delete this.ship.AIScript[activeHandlers[i]];
}
handlerCache = {};
delete this.ship.AIScript.aiAwoken;
delete this.ship.AIScript.shipDied;
}
this.communicate = function(key,params,priority)
{
if (priority > 1)
@ -413,6 +426,7 @@ this.AILib = function(ship)
this.setPriorities = function(priorities)
{
priorityList = priorities;
this.clearHandlers();
this.applyHandlers({});
_resetReconsideration.call(this,Math.random());
}
@ -928,6 +942,29 @@ AILib.prototype.conditionCombatOddsExcellent = function()
}
// group has taken too many losses
AILib.prototype.conditionGroupAttritionReached = function()
{
var group;
if (!(group = this.ship.group))
{
return false;
}
var cgp = this.getParameter("oolite_groupPower");
if (!cgp)
{
this.setParameter("oolite_groupPower",group.count);
return false;
}
if (group.count > cgp)
{
this.setParameter("oolite_groupPower",group.count);
return false;
}
return group.count < cgp*0.75;
}
AILib.prototype.conditionInCombat = function()
{
if (this.__cache.conditionInCombat !== undefined)
@ -1615,6 +1652,14 @@ AILib.prototype.conditionScannerContainsNonThargoid = function()
}
AILib.prototype.conditionScannerContainsPirateLeader = function()
{
return this.checkScannerWithPredicate(function(s) {
return s.group && s.group.leader == s && (s.primaryRole.match(/pirate-.*-freighter/));
});
}
AILib.prototype.conditionScannerContainsPirateVictims = function()
{
return this.checkScannerWithPredicate(function(s) {
@ -1776,23 +1821,27 @@ AILib.prototype.conditionCanScoopCargo = function()
AILib.prototype.conditionCargoIsProfitableHere = function()
{
// cargo is always considered profitable in the designated
// destination system (assume they have a prepared buyer)
if (this.ship.destinationSystem && this.ship.destinationSystem == system.ID)
// only consider these values if the ship has a route defined
if (this.ship.homeSystem != this.ship.destinationSystem)
{
return true;
}
// cargo is never considered profitable in the designated source
// system (or you could get ships launching and immediately
// redocking)
if (this.ship.homeSystem && this.ship.homeSystem == system.ID)
{
return false;
}
// and allow ships to be given multi-system trips if wanted
if (this.getParameter("oolite_flag_noDockingUntilDestination"))
{
return false;
// cargo is always considered profitable in the designated
// destination system (assume they have a prepared buyer)
if (this.ship.destinationSystem && this.ship.destinationSystem == system.ID)
{
return true;
}
// cargo is never considered profitable in the designated source
// system (or you could get ships launching and immediately
// redocking)
if (this.ship.homeSystem && this.ship.homeSystem == system.ID)
{
return false;
}
// and allow ships to be given multi-system trips if wanted
if (this.getParameter("oolite_flag_noDockingUntilDestination"))
{
return false;
}
}
if (!system.mainStation)
@ -2192,6 +2241,12 @@ AILib.prototype.behaviourEnterWitchspace = function()
{
// the wormhole we were trying for has expired
this.setParameter("oolite_witchspaceWormhole",null);
if (this.ship.group && this.ship.group.leader && this.ship.group.leader.status == "STATUS_ENTERING_WITCHSPACE")
{
// left behind, so leave group
this.ship.group.removeShip(this.ship);
this.ship.group = null;
}
}
else if (wormhole)
{
@ -2241,6 +2296,7 @@ AILib.prototype.behaviourEnterWitchspace = function()
// if it doesn't, we'll get blocked
if (result)
{
this.ship.notifyGroupOfWormhole();
this.setParameter("oolite_witchspaceEntry",null);
}
}
@ -2404,6 +2460,17 @@ AILib.prototype.behaviourGuardTarget = function()
}
AILib.prototype.behaviourJoinTargetGroup = function()
{
if (this.ship.target && this.ship.target.group)
{
this.ship.target.group.addShip(this.ship);
this.ship.group = this.ship.target.group;
}
this.ship.performIdle();
}
AILib.prototype.behaviourLandOnPlanet = function()
{
this.ship.desiredSpeed = this.ship.maxSpeed / 4;
@ -3235,23 +3302,36 @@ AILib.prototype.configurationSelectWitchspaceDestination = function()
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);
if (possible.length > 0)
{
var selected = possible[Math.floor(Math.random()*possible.length)];
this.setParameter("oolite_witchspaceDestination",selected.systemID);
this.communicate("oolite_selectedWitchspaceDestination",{"oolite_witchspaceDestination":selected.name},4);
}
else
{
this.setParameter("oolite_witchspaceDestination",null);
}
}
AILib.prototype.configurationSelectWitchspaceDestinationInbound = function()
{
var dest = this.ship.homeSystem;
this.setWitchspaceRouteTo(dest);
if (this.ship.homeSystem == this.ship.destinationSystem)
{
return this.configurationSelectWitchspaceDestination();
}
this.setWitchspaceRouteTo(this.ship.homeSystem);
}
AILib.prototype.configurationSelectWitchspaceDestinationOutbound = function()
{
var dest = this.ship.destinationSystem;
this.setWitchspaceRouteTo(dest);
if (this.ship.homeSystem == this.ship.destinationSystem)
{
return this.configurationSelectWitchspaceDestination();
}
this.setWitchspaceRouteTo(this.ship.destinationSystem);
}
@ -4526,10 +4606,175 @@ AILib.prototype.responseComponent_trackPlayer_playerWillEnterWitchspace = functi
/* ******************* Templates *************************** */
/* Templates. Common AI priority list fragments which may be useful to
* multiple AIs. These functions take no parameters and return a
* list. This can either be used straightforwardly as a truebranch or
* falsebranch value, or appended to a list using Array.concat() */
AILib.prototype.templateLeadPirateMission = function()
{
return [
{
preconfiguration: this.configurationForgetCargoDemand,
condition: this.conditionScannerContainsPirateVictims,
configuration: this.configurationAcquireScannedTarget,
truebranch: [
{
label: "Check odds",
condition: this.conditionCombatOddsGood,
behaviour: this.behaviourRobTarget,
reconsider: 5
}
]
},
{
/* move to a position on one of the space lanes, preferring lane 1 */
label: "Lurk",
configuration: this.configurationSetDestinationToPirateLurk,
behaviour: this.behaviourApproachDestination,
reconsider: 30
},
];
}
AILib.prototype.templateReturnToBase = function()
{
return [
{
condition: this.conditionHasSelectedStation,
truebranch: [
{
condition: this.conditionSelectedStationNearby,
configuration: this.configurationSetSelectedStationForDocking,
behaviour: this.behaviourDockWithStation,
reconsider: 30
},
{
condition: this.conditionSelectedStationNearMainPlanet,
truebranch: [
{
notcondition: this.conditionMainPlanetNearby,
configuration: this.configurationSetDestinationToMainPlanet,
behaviour: this.behaviourApproachDestination,
reconsider: 30
}
]
},
// either the station isn't near the planet, or we are
{
configuration: this.configurationSetDestinationToSelectedStation,
behaviour: this.behaviourApproachDestination,
reconsider: 30
}
],
falsebranch: [
{
configuration: this.configurationSelectRandomTradeStation,
behaviour: this.behaviourReconsider
}
]
}
];
}
AILib.prototype.templateReturnToBaseOrPlanet = function()
{
return [
{
condition: this.conditionFriendlyStationNearby,
configuration: this.configurationSetNearbyFriendlyStationForDocking,
behaviour: this.behaviourDockWithStation,
reconsider: 30
},
{
condition: this.conditionFriendlyStationExists,
configuration: this.configurationSetDestinationToNearestFriendlyStation,
behaviour: this.behaviourApproachDestination,
reconsider: 30
},
{
condition: this.conditionHasSelectedPlanet,
truebranch: [
{
preconfiguration: this.configurationSetDestinationToSelectedPlanet,
condition: this.conditionNearDestination,
behaviour: this.behaviourLandOnPlanet
},
{
behaviour: this.behaviourApproachDestination,
reconsider: 30
}
]
},
{
condition: this.conditionPlanetExists,
configuration: this.configurationSelectPlanet,
behaviour: this.behaviourReconsider
},
{
condition: this.conditionCanWitchspaceOut,
configuration: this.configurationSelectWitchspaceDestination,
behaviour: this.behaviourEnterWitchspace,
reconsider: 20
}
];
}
AILib.prototype.templateWitchspaceJumpInbound = function()
{
return [
{
preconfiguration: this.configurationSelectWitchspaceDestinationInbound,
condition: this.conditionCanWitchspaceOnRoute,
behaviour: this.behaviourEnterWitchspace,
reconsider: 20
},
{
condition: this.conditionReadyToSunskim,
configuration: this.configurationSetDestinationToSunskimEnd,
behaviour: this.behaviourSunskim,
reconsider: 20
},
{
condition: this.conditionSunskimPossible,
configuration: this.configurationSetDestinationToSunskimStart,
behaviour: this.behaviourApproachDestination,
reconsider: 30
}
];
}
AILib.prototype.templateWitchspaceJumpOutbound = function()
{
return [
{
preconfiguration: this.configurationSelectWitchspaceDestinationOutbound,
condition: this.conditionCanWitchspaceOnRoute,
behaviour: this.behaviourEnterWitchspace,
reconsider: 20
},
{
condition: this.conditionReadyToSunskim,
configuration: this.configurationSetDestinationToSunskimEnd,
behaviour: this.behaviourSunskim,
reconsider: 20
},
{
condition: this.conditionSunskimPossible,
configuration: this.configurationSetDestinationToSunskimStart,
behaviour: this.behaviourApproachDestination,
reconsider: 30
}
];
}
/* ******************* Waypoint generators *********************** */
@ -4537,7 +4782,7 @@ AILib.prototype.responseComponent_trackPlayer_playerWillEnterWitchspace = functi
* 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. */
* it's one written specifically for a particular AI . */
AILib.prototype.waypointsSpacelanePatrol = function()
{

View File

@ -1123,14 +1123,14 @@ this._addPirateAssistant = function(role,lead)
asst[0].setBounty(20+system.government+Math.floor(Math.random()*12),"setup actions");
asst[0].group = lead.group;
lead.group.addShip(asst[0]);
if (role == "pirate-interceptor")
/* if (role == "pirate-interceptor")
{
// asst[0].switchAI("pirateInterceptorAI.js");
asst[0].switchAI("pirateInterceptorAI.js");
}
else
{
// asst[0].switchAI("pirateFighterAI.js");
}
{ */
asst[0].switchAI("pirateFighterAI.js");
// }
}
@ -1148,7 +1148,6 @@ this._addPiratePack = function(pos,leader,lf,mf,hf,thug)
lead[0].setBounty(60+system.government+Math.floor(Math.random()*8),"setup actions");
var group = new ShipGroup("pirate pack",lead[0]);
lead[0].group = group;
// lead[0].switchAI("pirateFreighterAI.js"); // TODO: write AI
for (var i = Math.floor(lf+(0.5+Math.random()-Math.random())); i > 0; i--)
{
this._addPirateAssistant("pirate-light-fighter",lead[0]);
@ -1185,6 +1184,7 @@ this._addLightPirateLocal = function(pos)
var lead = this._addPiratePack(pos,"pirate-light-freighter",2,1,-1,0);
lead.homeSystem = system.ID;
lead.destinationSystem = system.ID;
lead.switchAI("pirateFreighterAI.js");
}
@ -1195,6 +1195,17 @@ this._addLightPirateRemote = function(pos)
lead.homeSystem = this._nearbyDangerousSystem(system.info.government-1);
this._setFuel(lead);
lead.destinationSystem = system.ID;
lead.switchAI("pirateFreighterAI.js");
}
this._addLightPirateOutbound = function(pos)
{
var lead = this._addPiratePack(pos,"pirate-light-freighter",2,1,-1,0);
lead.destinationSystem = this._nearbySafeSystem(system.info.government+1);
lead.fuel = 7
lead.homeSystem = system.ID;
lead.switchAI("pirateFreighterAI.js");
}
@ -1203,6 +1214,7 @@ this._addMediumPirateLocal = function(pos)
var lead = this._addPiratePack(pos,"pirate-medium-freighter",3,2,0,1);
lead.homeSystem = system.ID;
lead.destinationSystem = system.ID;
lead.switchAI("pirateFreighterAI.js");
}
@ -1213,6 +1225,7 @@ this._addMediumPirateRemote = function(pos)
lead.homeSystem = this._nearbyDangerousSystem(system.info.government-1);
this._setFuel(lead);
lead.destinationSystem = system.ID;
lead.switchAI("pirateFreighterAI.js");
}
@ -1221,6 +1234,7 @@ this._addHeavyPirateLocal = function(pos)
var lead = this._addPiratePack(pos,"pirate-heavy-freighter",4,4,2,2);
lead.homeSystem = system.ID;
lead.destinationSystem = system.ID;
lead.switchAI("pirateFreighterAI.js");
}
@ -1231,6 +1245,7 @@ this._addHeavyPirateRemote = function(pos)
lead.homeSystem = this._nearbyDangerousSystem(system.info.government-1);
this._setFuel(lead);
lead.destinationSystem = system.ID;
lead.switchAI("pirateFreighterAI.js");
}

View File

@ -1719,18 +1719,26 @@ static ShipEntity *doOctreesCollide(ShipEntity *prime, ShipEntity *other);
{
[escorter switchAITo:autoAI];
}
[UNIVERSE addEntity:escorter]; // STATUS_IN_FLIGHT, AI state GLOBAL
[escorter setGroup:escortGroup];
[escorter setOwner:self]; // mark self as group leader
if ([self status] == STATUS_DOCKED)
{
[[self owner] addShipToLaunchQueue:escorter withPriority:NO];
}
else
{
[UNIVERSE addEntity:escorter]; // STATUS_IN_FLIGHT, AI state GLOBAL
[escortAI setState:@"FLYING_ESCORT"]; // Begin escort flight. (If the AI doesn't define FLYING_ESCORT, this has no effect.)
[escorter doScriptEvent:OOJSID("spawnedAsEscort") withArgument:self];
}
if([escorter heatInsulation] < [self heatInsulation]) [escorter setHeatInsulation:[self heatInsulation]]; // give escorts same protection as mother.
if(([escorter maxFlightSpeed] < cruiseSpeed) && ([escorter maxFlightSpeed] > cruiseSpeed * 0.3))
cruiseSpeed = [escorter maxFlightSpeed] * 0.99; // adapt patrolSpeed to the slowest escort but ignore the very slow ones.
[escortAI setState:@"FLYING_ESCORT"]; // Begin escort flight. (If the AI doesn't define FLYING_ESCORT, this has no effect.)
[escorter doScriptEvent:OOJSID("spawnedAsEscort") withArgument:self];
if (bounty)
{
@ -12085,16 +12093,7 @@ Vector positionOffsetForShipInRotationToAlignment(ShipEntity* ship, Quaternion q
{
return; // has already entered a different wormhole
}
if (replacing && ![[UNIVERSE sun] willGoNova] && [UNIVERSE sun] != nil)
{
/* Add a new ship to maintain quantities of standard ships, unless
there's a nova in the works, the AI asked us not to, or we're in
interstellar space.
*/
// now handled by system repopulator
// [UNIVERSE witchspaceShipWithPrimaryRole:[self primaryRole]];
}
// Replacement ships now handled by system repopulator
// MKW 2011.02.27 - Moved here from ShipEntityAI so escorts reliably follow
// mother in all wormhole cases, not just when the ship

View File

@ -45,6 +45,7 @@ MA 02110-1301, USA.
- (void) enterPlayerWormhole;
- (void) wormholeEscorts;
- (void) wormholeEntireGroup;
- (BOOL) suggestEscortTo:(ShipEntity *)mother;

View File

@ -147,7 +147,6 @@
- (void) performHyperSpaceExit;
- (void) performHyperSpaceExitWithoutReplacing;
- (void) wormholeGroup;
- (void) wormholeEntireGroup;
- (void) commsMessage:(NSString *)valueString;
- (void) commsMessageByUnpiloted:(NSString *)valueString;
@ -696,6 +695,13 @@
}
- (void) wormholeEntireGroup
{
[self wormholeGroup];
[self wormholeEscorts];
}
- (BOOL) suggestEscortTo:(ShipEntity *)mother
{
if (mother)
@ -1635,13 +1641,6 @@
}
- (void) wormholeEntireGroup
{
[self wormholeGroup];
[self wormholeEscorts];
}
- (void) commsMessage:(NSString *)valueString
{
[self commsMessage:valueString withUnpilotedOverride:NO];

View File

@ -1358,10 +1358,6 @@ NSDictionary *OOMakeDockingInstructions(StationEntity *station, HPVector coords,
BOOL trader = [role isEqualToString:@"trader"];
BOOL sunskimmer = ([role isEqualToString:@"sunskim-trader"]);
ShipEntity *ship = nil;
NSString *defaultRole = @"escort";
NSString *escortRole = nil;
NSString *escortShipKey = nil;
NSDictionary *traderDict = nil;
if((trader && (randf() < 0.1)) || sunskimmer)
{
@ -1383,7 +1379,6 @@ NSDictionary *OOMakeDockingInstructions(StationEntity *station, HPVector coords,
if (ship)
{
traderDict = [ship shipInfoDictionary];
if (![ship crew])
[ship setCrew:[NSArray arrayWithObject:
[OOCharacter randomCharacterWithRole: role
@ -1422,64 +1417,9 @@ NSDictionary *OOMakeDockingInstructions(StationEntity *station, HPVector coords,
unsigned escorts = [ship pendingEscortCount];
if(escorts > 0)
{
escortRole = [traderDict oo_stringForKey:@"escort_role" defaultValue:nil];
if (escortRole == nil)
escortRole = [traderDict oo_stringForKey:@"escort-role" defaultValue:defaultRole];
if (![escortRole isEqualToString: defaultRole])
{
if (![[UNIVERSE newShipWithRole:escortRole] autorelease])
{
escortRole = defaultRole;
}
}
escortShipKey = [traderDict oo_stringForKey:@"escort_ship" defaultValue:nil];
if (escortShipKey == nil)
escortShipKey = [traderDict oo_stringForKey:@"escort-ship"];
if (escortShipKey != nil)
{
if (![[UNIVERSE newShipWithName:escortShipKey] autorelease])
{
escortShipKey = nil;
}
}
while (escorts--)
{
ShipEntity *escort_ship;
if (escortShipKey)
{
escort_ship = [UNIVERSE newShipWithName:escortShipKey]; // retained
}
else
{
escort_ship = [UNIVERSE newShipWithRole:escortRole]; // retained
}
if (escort_ship && [self fitsInDock:escort_ship])
{
if (![escort_ship crew] && ![escort_ship isUnpiloted])
[escort_ship setCrew:[NSArray arrayWithObject:
[OOCharacter randomCharacterWithRole: @"hunter"
andOriginalSystem: [UNIVERSE systemSeed]]]];
[escort_ship setScanClass: [ship scanClass]];
[escort_ship setCargoFlag: CARGO_FLAG_FULL_PLENTIFUL];
[escort_ship setPrimaryRole:@"escort"];
if ((sunskimmer || trader) && [escort_ship heatInsulation] < [ship heatInsulation])
[escort_ship setHeatInsulation:[ship heatInsulation]];
[escort_ship setGroup:escortGroup];
[escort_ship setOwner:ship];
[escort_ship switchAITo:@"escortAI.js"];
[self addShipToLaunchQueue:escort_ship withPriority:NO];
}
[escort_ship release];
}
[ship setOwner:self]; // makes escorts get added to station launch queue
[ship setUpEscorts];
[ship setOwner:ship];
}
[ship setPendingEscortCount:0];

View File

@ -102,6 +102,7 @@ static JSBool ShipBroadcastDistressMessage(JSContext *context, uintN argc, jsval
static JSBool ShipOfferToEscort(JSContext *context, uintN argc, jsval *vp);
static JSBool ShipMarkTargetForFines(JSContext *context, uintN argc, jsval *vp);
static JSBool ShipEnterWormhole(JSContext *context, uintN argc, jsval *vp);
static JSBool ShipNotifyGroupOfWormhole(JSContext *context, uintN argc, jsval *vp);
static JSBool ShipThrowSpark(JSContext *context, uintN argc, jsval *vp);
static JSBool ShipPerformAttack(JSContext *context, uintN argc, jsval *vp);
@ -450,6 +451,7 @@ static JSFunctionSpec sShipMethods[] =
{ "getShaders", ShipGetShaders, 0 },
{ "hasRole", ShipHasRole, 1 },
{ "markTargetForFines", ShipMarkTargetForFines, 0 },
{ "notifyGroupOfWormhole", ShipNotifyGroupOfWormhole, 0 },
{ "offerToEscort", ShipOfferToEscort, 1 },
{ "patrolReportIn", ShipPatrolReportIn, 1},
{ "performAttack", ShipPerformAttack, 0 },
@ -3024,6 +3026,21 @@ static JSBool ShipEnterWormhole(JSContext *context, uintN argc, jsval *vp)
}
static JSBool ShipNotifyGroupOfWormhole(JSContext *context, uintN argc, jsval *vp)
{
OOJS_PROFILE_ENTER
ShipEntity *thisEnt = nil;
GET_THIS_SHIP(thisEnt);
[thisEnt wormholeEntireGroup];
OOJS_RETURN_VOID;
OOJS_PROFILE_EXIT
}
static JSBool ShipThrowSpark(JSContext *context, uintN argc, jsval *vp)
{
OOJS_PROFILE_ENTER