Scripts/fasttargetselector.js |
"use strict";
this.name = "FastTargetSelector";
this.author = "phkb";
this.copyright = "2016 phkb";
this.description = "Allows quick targeting of ships or other entities visible on the scanner. Also highlights current target on the scanner.";
this.license = "CC BY-NC-SA 4.0";
/* Borrows scanner highlight code from Thargoids Target Autolock OXP */
/* Also borrows "sort by distance calc" from Timer's Targeter OXP */
/* Bg thanks to cag for code optimisations */
this._normalColorsDefault = {color1:"blueColor", color2:"yellowColor"};
this._normalColorsStation = {color1:"blueColor", color2:"greenColor"};
this._normalColorsPolice = {color1:"greenColor", color2:"0.5 0.0 1.0"};
this._normalColorsRock = {color1:"blueColor", color2:"whiteColor"};
this._normalColorsThargoid = {color1:"blueColor", color2:"redColor"};
this._normalColorsMissile = {color1:"cyanColor", color2:"whiteColor"};
this._normalColorsBuoy = {color1:"greenColor", color2:"whiteColor"};
this._battleColorsDefault = {color1:"redColor", color2:"yellowColor"};
this._battleColorsMissile = {color1:"redColor", color2:"cyanColor"};
this._battleColorsPolice = {color1:"redColor", color2:"1.0 0.0 0.5"};
this._npc = []; // current scan data array
this._index = -1; // position of players selected target in array
this._dataAge = 0; // age of current scan data
this._targetTimer = null; // timer used to update the colors of the currently selected target, so it will stand out on the scanner
this._trueCondition = 0; // current alert condition of the player. Adjusted slightly so that red alert (3) is limited to when there are hostile ships
this._oldCondition = 0; // last state of the players alert condition
this._oldTarget = null; // reference to the players last selected target
this._ignoreRoles = ["btd_dummy_entity"]; // ship roles that will be ignored (ie they won't be selected)
//cag: increasing the length of _ignoreRoles requires chg's to $findNPCShips()
this._active = false; // flag to indicate the FTS is in operation
this._scanMode = 0; // mode 0 = normal scan operations, mode 1 = include all objects, even under condition red. Default to 0.
this._removePrimableEquipment = false; // flag to indicate whether the mode change primable equipment will be removed from the list. Default to false.
this._trueValues = ["yes", "1", 1, "true", true];
this._tapInstalled = false;
this._ED = null;
// configuration settings for use in Lib_Config
this._ftsConfig = {Name:this.name, Alias:"Fast Target Selector", Display:"Config", Alive:"_ftsConfig",
Bool:{
B0:{Name:"_removePrimableEquipment", Def:false, Desc:"Removes the FTS Mode Changer"},
Info:"0 - Removes the FTS Mode Changer from primable equipment."},
EInt:{
E0:{Name:"_scanMode", Def:0, Desc:["Def. scan mode","Def. scan mode"], Min:0, Max:1},
Info:"Default scan mode: 0=Standard (red alert limited). 1=All entities (even in red alert)"}
};
//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function() {
// register our settings, if Lib_Config is present
if (worldScripts.Lib_Config) worldScripts.Lib_Config._registerSet(this._ftsConfig);
if (missionVariables.FTS_RemoveModeChanger) this._removePrimableEquipment = (this._trueValues.indexOf(missionVariables.FTS_RemoveModeChanger) >= 0 ? true : false);
if (missionVariables.FTS_ScanMode) this._scanMode = parseInt(missionVariables.FTS_ScanMode);
if (worldScripts.targetAutolock) {
if (missionVariables.targetAutolock == "TRUE") this._tapInstalled = true;
}
if (worldScripts.escortdeck) {
this._ED = worldScripts.escortdeck;
this.$findNPCShips = this.$findNPCShips_withEscortDeck;
} else {
this.$findNPCShips = this.$findNPCShips_normal;
}
// make sure the mode change equipment isn't on board - we only push it into the equip list on launch
player.ship.removeEquipment("EQ_TARGETSELECTOR_MODECHANGER");
if (player.ship.equipmentStatus("EQ_TARGETSELECTOR") === "EQUIPMENT_OK") this._active = true;
}
//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function() {
missionVariables.FTS_RemoveModeChanger = this._removePrimableEquipment;
missionVariables.FTS_ScanMode = this._scanMode;
}
//-------------------------------------------------------------------------------------------------------------
this.shipTargetAcquired = function(newTarget) {
if (this._active) this.$startTimer();
}
//-------------------------------------------------------------------------------------------------------------
this.alertConditionChanged = function(newCondition, oldCondition) {
if (this._active === false) return;
if (this._trueCondition !== newCondition && ((newCondition === 3 && player.alertHostiles === true) || newCondition < 3)) {
this._trueCondition = newCondition;
this.$startTimer();
}
}
//-------------------------------------------------------------------------------------------------------------
this.shipTargetLost = this.shipTargetDestroyed = function(target) {
if (this._active === false) return;
this.$resetOldTarget();
}
//-------------------------------------------------------------------------------------------------------------
this.shipDockedWithStation = function(station) {
this.$resetOldTarget();
// remove the mode changer
player.ship.removeEquipment("EQ_TARGETSELECTOR_MODECHANGER");
}
//-------------------------------------------------------------------------------------------------------------
this.shipLaunchedFromStation = function(station) {
// make sure the mode changer is installed, but only if (a) its not disabled, and the fts is not broken
if (this._removePrimableEquipment === false && player.ship.equipmentStatus("EQ_TARGETSELECTOR") === "EQUIPMENT_OK") {
player.ship.awardEquipment("EQ_TARGETSELECTOR_MODECHANGER");
}
}
//-------------------------------------------------------------------------------------------------------------
this.shipDied = this.shipWillEnterWitchspace = function() {
if (this._active === false) return;
this.$resetOldTarget();
}
//-------------------------------------------------------------------------------------------------------------
this.playerBoughtEquipment = function(equipmentKey) {
if (equipmentKey === "EQ_TARGETSELECTOR_REMOVAL") {
player.ship.removeEquipment(equipmentKey);
player.ship.removeEquipment("EQ_TARGETSELECTOR_MODECHANGER");
player.ship.removeEquipment("EQ_TARGETSELECTOR");
this._active = false;
}
if (equipmentKey === "EQ_TARGETSELECTOR") this._active = true;
// monitor for when TAP is installed so we don't conflict with it
if (equipmentKey === "EQ_TARGET_AUTOLOCK") this._tapInstalled = true;
}
//-------------------------------------------------------------------------------------------------------------
this.equipmentDamaged = function(equipmentKey) {
if (equipmentKey === "EQ_TARGETSELECTOR") this._active = false;
if (equipmentKey === "EQ_SCANNER_SHOW_MISSILE_TARGET") this._tapInstalled = false;
}
//-------------------------------------------------------------------------------------------------------------
this.equipmentRepaired = function(equipmentKey) {
if (equipmentKey === "EQ_TARGETSELECTOR") this._active = true;
// if the fts gets repaired in flight, add back the mode changer (if we haven't disabled it)
if (player.ship.isInSpace && equipmentKey === "EQ_TARGETSELECTOR" && this._removePrimableEquipment === false) player.ship.awardEquipment("EQ_TARGETSELECTOR_MODECHANGER");
if (equipmentKey === "EQ_TARGET_AUTOLOCK") this._tapInstalled = true;
}
//-------------------------------------------------------------------------------------------------------------
this.alertConditionChanged = function(newCondition, oldCondition) {
// update our "trueCondition" variable
this._trueCondition = newCondition;
// if this new condition is "red alert", see if we're actually under attack
if (newCondition === 3) {
if (player.alertHostiles === false) {
// if not, it's really only yellow alert from this OXP's point of view
this._trueCondition = 2;
}
}
}
//-------------------------------------------------------------------------------------------------------------
// "n" key
this.activated = function () {
// next target
var w = worldScripts.FastTargetSelector;
w.$lookForTarget(1);
}
//-------------------------------------------------------------------------------------------------------------
// "b" key
this.mode = function() {
// previous target
var w = worldScripts.FastTargetSelector;
w.$lookForTarget(-1);
}
//-------------------------------------------------------------------------------------------------------------
// starts the lockon timer, if it isn't already
this.$startTimer = function() {
if (this._targetTimer && this._targetTimer.isRunning) return;
this._targetTimer = new Timer(this, this.$lockOn, 0.25, 0);
}
//-------------------------------------------------------------------------------------------------------------
// reset the scanner colors of the previous target
this.$resetOldTarget = function() {
if (this._targetTimer && this._targetTimer.isRunning) this._targetTimer.stop();
if (this._oldTarget) {
if (this._tapInstalled === false) {
this._oldTarget.scannerDisplayColor1 = null;
this._oldTarget.scannerDisplayColor2 = null;
}
this._oldTarget = null;
}
}
//-------------------------------------------------------------------------------------------------------------
// updates the scanner colors of the current target to make it more visible
this.$lockOn = function $lockOn() {
//this.$resetOldTarget();
var p = player.ship;
// let's not lock onto ourselves shall we?
if(p.target === p) p.target = null;
// do we have a new target
if (p.target) {
// no changes, so don't do anything
if (p.target === this._oldTarget && this._trueCondition === this._oldCondition) return;
this.$resetOldTarget();
this._oldTarget = p.target;
this._oldCondition = this._trueCondition;
var battle = false;
if (player.alertCondition === 3 && player.alertHostiles === true) {
// only switch to battle colors on ships that are actually trying to kill us
if (this._oldTarget.isShip && this._oldTarget.target === p) battle = true;
}
var currentColors = (battle === false ? this._normalColorsDefault : this._battleColorsDefault);
if (this._oldTarget.isValid) {
if (this._oldTarget.isMissile || this._oldTarget.isMine || this._oldTarget.isWeapon) {
currentColors = (battle === false ? this._normalColorsMissile : this._battleColorsMissile);
} else if (this._oldTarget.isPolice && (this._oldTarget.isShip && this._oldTarget.hasRole("RSsatellite") === false)) {
currentColors = (battle === false ? this._normalColorsPolice : this._battleColorsPolice);
} else if (this._oldTarget.isThargoid) {
currentColors = this._normalColorsThargoid;
} else if (this._oldTarget.isStation) {
currentColors = this._normalColorsStation;
} else if (this._oldTarget.isRock || this._oldTarget.isDerelict || this._oldTarget.isCargo || (this._oldTarget.isShip && this._oldTarget.hasRole("RSsatellite") === true)) {
currentColors = this._normalColorsRock;
} else if (this._oldTarget.scanClass === "CLASS_BUOY") {
currentColors = this._normalColorsBuoy;
}
}
if (this._tapInstalled === false) {
p.target.scannerDisplayColor1 = currentColors.color1;
p.target.scannerDisplayColor2 = currentColors.color2;
}
}
}
//-------------------------------------------------------------------------------------------------------------
this.$lookForTarget = function(direction) {
var currNPC = null;
var ps = player.ship;
var valid = false;
this.$refreshArray();
var npc = this._npc;
if (npc && npc.length > 0) {
var lc = 0;
do {
this._index += direction;
// select the next/previous index value, based on the direction
if (this._index < 0)
// if we get to the beginning of the array, go back to the end
this._index = npc.length - 1;
else if (this._index > npc.length - 1)
// if we get to the end of the array, go back to the beginning
this._index = 0;
currNPC = npc[this._index];
valid = currNPC && currNPC.isValid === true && currNPC.isJamming === false
&& currNPC.isCloaked === false;
if (valid && ps.position.distanceTo(currNPC) < ps.scannerRange) {
// if the item is valid, make it the player's target
ps.target = currNPC;
break;
}
lc += 1;
} while ( valid && lc < npc.length );
// if we didn't get a target, keep looping until we do
}
}
//-------------------------------------------------------------------------------------------------------------
this.$refreshArray = function() {
// get the array of ships
if ((global.clock.adjustedSeconds - this._dataAge) > 1) { // limit to once/sec
this._npc = this.$findNPCShips();
this._dataAge = global.clock.adjustedSeconds;
}
}
//-------------------------------------------------------------------------------------------------------------
// gives every ship a sort order as it spawns
this.shipSpawned = function(ship) {
ship.fts_sort = 5;
if (ship.scanClass == "CLASS_NEUTRAL" || ship.isStation) ship.fts_sort = 3;
if (ship.isThargoid) ship.fts_sort = 0;
if (ship.bounty > 0 && ship.fts_sort == 5) ship.fts_sort = 2;
if (ship.bounty >= 50 && ship.fts_sort == 5) ship.fts_sort = 1;
if (ship.isPolice) ship.fts_sort = 4;
if (ship.isCargo) ship.fts_sort = 6;
if (ship.isRock && !ship.isStation) ship.fts_sort = 7;
}
//-------------------------------------------------------------------------------------------------------------
// find all npc ships in range
this.$findNPCShips_normal = function() {
function _isNPC(entity) {
//var wF = worldScripts.FastTargetSelector; // shouldn't actually need this, as context should always be "this"
if (!entity.isShip) return false;
if (entity.isPlayer) return false;
if (entity.displayName === "Wreckage") return false;
if (this._trueCondition === 3 && this._scanMode === 0) {
// during red alert, only target thargoids, missiles and enemy ships
if (entity.isThargoid === true && entity.isCargo === false) return true;
if (entity.target === player.ship && entity.hasHostileTarget) return true;
if (entity.isMine) return true;
if (entity.isWeapon) return true;
return false;
}
if (entity.isVisualEffect) return false ;
if (this._ignoreRoles.length === 1) {
if (entity.primaryRole === this._ignoreRoles[0]) return false;
} else {
// only do indexof if there is more than one entry in the ignoreroles list
if (this._ignoreRoles.indexOf(entity.primaryRole) === -1) return false;
}
return true;
}
return this.$sortList(system.filteredEntities(this, _isNPC, player.ship, player.ship.scannerRange));
}
//-------------------------------------------------------------------------------------------------------------
// find all npc ships in range
this.$findNPCShips_withEscortDeck = function() {
function _isNPC(entity) {
//var wF = worldScripts.FastTargetSelector; // shouldn't actually need this, as context should always be "this"
if (!entity.isShip) return false;
if (entity.isPlayer) return false;
if (entity.displayName === "Wreckage") return false;
if (this._trueCondition === 3 && this._scanMode === 0) {
// during red alert, only target thargoids, missiles and enemy ships
if (entity.isThargoid === true && entity.isCargo === false) return true;
if (entity.target === player.ship && entity.hasHostileTarget) return true;
if (entity.isMine) return true;
if (entity.isWeapon) return true;
return false;
}
if (entity.isVisualEffect) return false ;
// thanks to Norby for this escortdeck exclusion code
var i = this._ED.$EscortDeckShip.indexOf(entity);
if (i >= 0 && this._ED.$EscortDeckShipPos[i]) //entity is on deck
return false; //so exclude this entity from target list
if (this._ignoreRoles.length === 1) {
if (entity.primaryRole === this._ignoreRoles[0]) return false;
} else {
// only do indexof if there is more than one entry in the ignoreroles list
if (this._ignoreRoles.indexOf(entity.primaryRole) === -1) return false;
}
return true;
}
return this.$sortList(system.filteredEntities(this, _isNPC, player.ship, player.ship.scannerRange));
}
//-------------------------------------------------------------------------------------------------------------
this.$sortList = function(targets) {
return targets.sort( function sortByID(a,b) {
return (a.fts_sort < b.fts_sort) ? -1 : (a.fts_sort > b.fts_sort) ? 1 : 0;
});
}
//-------------------------------------------------------------------------------------------------------------
// removes any wreckage items from the array, then calls the sorting algorithm
/*this.$sortArray = function() {
// remove any wreckage items
for (var i = this._npc.length - 1; i >= 0; i--) {
if (this._npc[i].displayName === "Wreckage") this._npc.splice(i, 1);
}
// then sort
this.$sortByDistance(this._npc);
}*/
//-------------------------------------------------------------------------------------------------------------
// sort by distance function courtesy of Thargoid
/*this.$sortByDistance = function(targets) {
var cache = {};
// sort
targets.sort( function sortByDist(a,b) {
// cache distance
var ai = a.entityPersonality, bi = b.entityPersonality; // use some "uniq" ident
cache[ai] = cache[ai] || (a.isValid && a.position.distanceTo(player.ship.position));
cache[bi] = cache[bi] || (b.isValid && b.position.distanceTo(player.ship.position));
return ((cache[ai] < cache[bi]) ? -1 : ((cache[ai] > cache[bi]) ? 1 : 0));
});
// turn off the flag that indicates the data isn't sorted
this._sortRequired = false;
}
*/ |