Re: [WIP] Bounty system OXP
Posted: Tue Jun 30, 2020 5:20 am
Another bug squashed in v0.7, with thanks to dybal for the tip off.
For information and discussion about Oolite.
https://bb.oolite.space/
Bug in the Email System, fixed in next release.
It would clear the bounty of any local fines, but not necessarily of fines collected in other systems, which the police scan after you launched would have uncovered.
Special note: If a police vessel flags your ship to pay fines upon docking, the fine you pay will be the full amount and will not be reduced by a degrading component. This will only happen if your bounty is small, however. If you have a large bounty, police ships are more likely to attack you. If, after paying your fine, you are scanned again, the bounty may reappear in the current system.
That is probably a more intuitive way of dealing with it. I'll add it to the list.Milo wrote: ↑Tue Jul 07, 2020 12:22 pmThe scanning isn't about local offences, it's an import of offences from elsewhere. I'm just suggesting that the import of external bounties should be limited once-per-exit-witchspace so if you pay the locals, they don't bother you again until you leave. Of course, they don't actually resolve your bounty elsewhere, they just take your money as a toll for not being shot.
Code: Select all
this.$scanTarget = function $scanTarget() {
var that = $scanTarget; // pointer to this function
var core = (that.core = that.core || worldScripts.BountySystem_Core); // cache worldScript reference in local property on this function
var w = (that.w = that.w || worldScripts.BountySystem_WarrantScanner); // cache worldScript reference in local property on this function
var npcscan = (that.npcscan = that.npcscan || worldScripts.BountySystem_NPCScan); // cache worldScript reference in local property on this function
var broadcastcomms = (that.broadcastcomms = that.broadcastcomms || worldScripts.BroadcastCommsMFD); // cache worldScript reference in local property on this function
var myShip = this.ship, myScript = myShip.script, myTarget = myScript._checkTarget;
if (myScript._debugNPCScan && myScript._loggingLevel > 1) log(this.name, "running scanTarget :" + myShip);
// make sure ship is valid
if (!myShip.isValid) {
myScript._warrantScannerTimer.stop();
return;
}
if (myShip.target != myTarget || !myTarget.isValid) {
// somethings happened and their target has switched
if (myScript._debugNPCScan && myScript._loggingLevel > 1) log(this.name, "target has shifted - cancelling scan :" + myShip);
myScript._warrantScannerTimer.stop();
myTarget = null;
myScript.$initialiseTimer();
return;
}
// is target still valid
var result = w.$targetIsValid(myShip);
if (result) {
// are we in range?
if (w.$targetInRange(myShip)) {
myScript._rangeCheck = 0;
if (myScript._debugNPCScan && myScript._loggingLevel > 1) log(this.name, "scanning ship (" + myTarget + ") :" + myShip);
// ok, do a pass
myScript._scanPass += 1;
if (myScript._debugNPCScan && myScript._loggingLevel > 1) log(this.name, "pass " + this._scanPass + " :" + myShip);
if (myScript._scanPass === 1 && myTarget.isPlayer) player.consoleMessage(expandDescription("[warrant-scan-detected]"), 5);
if (myScript._scanPass === 2 && myTarget.isPlayer === false) w.$npcReaction(myTarget, myShip);
// are we done?
if (myScript._scanPass >= myScript._warrantScannerTime) {
myScript._warrantScannerTimer.stop();
if (!myTarget.isPlayer || !core._alreadyScannedPlayerInThisSystem) { // only apply out-of-system bounties to the player once per system visit
if (myTarget.isPlayer) core._alreadyScannedPlayerInThisSystem = true; // remember that we did this
// ok, get the hidden bounty;
if (myScript._debugNPCScan && myScript._loggingLevel > 1) log(this.name, "scan complete :" + myShip);
myScript._scannedList.push(myTarget);
var initial = myTarget.bounty;
var check = w.$checkBounty(myTarget, true, myShip); // returns sum of out-of-system bounties and imports any transferable bounties to the current system (this affects the calculation in shipWillDockWithStation that sets player bounty)
if (check > 0) { // an out-of-system bounty was found
if (myTarget.isPlayer && broadcastcomms && broadcastcomms._surrenderBounty > 0) { // player has a hidden bounty after surrendering to police with Broadcast Comms
broadcastcomms._surrenderBounty += check; // add bounty amount to Broadcast Comms' "hidden" value to be restored if the player commits another crime or doesn't go to the station
if (myScript._debugNPCScan && myScript._loggingLevel >= 1) log(this.name, "bounty of " + check + " added to Broadcast Comms' hidden bounty value for surrendered " + myTarget);
} else { // apply the bounty directly
myTarget.bounty += check;
if (myScript._debugNPCScan && myScript._loggingLevel >= 1) log(this.name, "bounty change of " + (myTarget.bounty - initial) + " for " + myTarget);
}
}
}
// add this target to all vessels in range who have the same scan class
npcscan.$addTargetToScannedList(myShip, myTarget); // call to worldScript function from ship-attached script
myScript._lastTarget = myTarget;
myTarget = null;
myScript.$initialiseTimer();
}
} else {
myScript._rangeCheck += 1;
// try to move in range of target so we can continue scanning
if (myShip.position.distanceTo(myTarget) < (myShip.scannerRange * 1.3) && myScript._rangeCheck < 30) {
if (myScript._debugNPCScan && myScript._loggingLevel > 1) log(this.name, "attempting to get in range :" + myShip);
myShip.destination = myTarget.position;
myShip.desiredRange = myScript._warrantScannerRange - parseInt(Math.random * 1000) + 200;
myShip.desiredSpeed = myShip.maxSpeed;
} else {
// give up if the distance is too great, or the range check counter times out
if (myScript._debugNPCScan && myScript._loggingLevel > 1) log(this.name, "unable to get in range of target (timeout or distance too far) :" + myShip);
myScript._warrantScannerTimer.stop();
myShip.target = null;
myTarget = null;
myScript.$initialiseTimer();
}
}
} else {
if (myScript._debugNPCScan && myScript._loggingLevel > 1) log(this.name, "target no longer valid :" + myShip);
myScript._warrantScannerTimer.stop();
myShip.target = null;
myTarget = null;
myScript.$initialiseTimer();
}
}
Code: Select all
this._alreadyScannedPlayerInThisSystem = false; // do-once-per-system (not cleared when docked, only when jumping out) for importing out-of-system bounties so if player pays off the locals, they don't get a bounty again from a subsequent scan
Code: Select all
if (missionVariables.BountySystem_alreadyScannedPlayerInThisSystem) {
this._alreadyScannedPlayerInThisSystem = missionVariables.BountySystem_alreadyScannedPlayerInThisSystem;
delete missionVariables.BountySystem_alreadyScannedPlayerInThisSystem;
}
Code: Select all
missionVariables.BountySystem_alreadyScannedPlayerInThisSystem = this._alreadyScannedPlayerInThisSystem;
Code: Select all
this._alreadyScannedPlayerInThisSystem = false;
Code: Select all
this.shipWillDockWithStation = function (station) {
if (this._escapePod === true) {
this._changing = true;
player.bounty += this.$calculateBounty(system.ID); // add instead of setting so other OXPs can update it (currently known:
Broadcast Comms restores hidden bounty for players who surrendered to police)
this._changing = false;
this._escapePod = false;
}
Code: Select all
this.$targetAttacked = function (whom, damageType, killed) {
// don't record anything if this is a simulator run
if (this.$simulatorRunning()) return;
var msgType = "attacked";
if (killed) msgType = "killed";
// player shot and killed other with lasers, q-bomb, or by ramming them
if (whom.isPiloted && !whom.isDerelict && !whom.isThargoid) {
if (damageType === "energy damage" || damageType === "cascade weapon" || damageType === "scrape damage") {
// who did we just kill?
if (whom.isPolice) {
this.$addOffence(msgType + " police");
} else {
// is there a police ship/station in range
// (check the holding array first though, so we can avoid doing "findLawVessels" with every laser shot)
if (whom.bounty === 0 && this._holding.indexOf(msgType + "innocent") === -1) {
var police = this.$findLawVessels(player.ship);
if (police.length > 0) this.$addOffence(msgType + " innocent");
}
}
}
}
}
Code: Select all
"use strict";
this.name = "BountySystem_WarrantScanner";
this.author = "phkb";
this.copyright = "2016 phkb";
this.description = "Routines for controlling the use of the Warrant scanner.";
this.licence = "CC BY-NC-SA 4.0";
this._debug = false; // turns on debug messages in the log
this._warrantScannerTimer = null; // timer object used when scanning NPC's
this._hideMFDTimer = null; // timer used to hide the MFD after a scan completes
//this._scannedShips = []; // list of ships scanned by the player
this._scanTimeMidPoint = 4; // point of time during a scan (in seconds) at which initial information is displayed.
this._lastTarget = null; // holding object of player's target when scanning started (so we can check if the player switches target and stops the scan)
this._mfdID = -1; // ID of the MFD that last held the Warrant Scanner
this._outputMode = 0; // mode = 0 standard MFD view, mode = 1, no MFD
this._scannerMode = 0; // 0 = manual scan, 1 = automatic scan (offenders only), 2 = auto scan everyone
this._overrideMode = false;
this._giveToNPC = true; // flag to control whether NPC's can get the warrant scanner
this._trueValues = ["yes", "1", 1, "true", true];
this._ficcInstalled = false;
//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
if (worldScripts.BountySystem_Core._disabled) {
delete this.shipSpawned;
delete this.playerBoughtEquipment;
delete this.shipWillLaunchFromStation;
delete this.startUpComplete;
return;
}
// ensure station dock control has the important script properties noted
var sdc = worldScripts.StationDockControl;
if (sdc && sdc._propertiesToKeep) {
sdc._propertiesToKeep.push("_hiddenBounty");
sdc._propertiesToKeep.push("_storedHiddenBounty");
}
if (missionVariables.BountySystem_WarrantScanAuto) {
this._scannerMode = parseInt(missionVariables.BountySystem_WarrantScanAuto);
worldScripts.BountySystem_Core._warrantScannerMode = this._scannerMode;
}
if (worldScripts.FuelInjectionCruiseControl) this._ficcInstalled = true;
}
//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
missionVariables.BountySystem_WarrantScanAuto = this._scannerMode;
}
//-------------------------------------------------------------------------------------------------------------
this.shipWillLaunchFromStation = function (station) {
var equip = "";
var p = player.ship;
if (p.equipmentStatus("EQ_WARRANT_SCANNER") === "EQUIPMENT_OK" || p.equipmentStatus("EQ_WARRANT_SCANNER_PASSIVE") === "EQUIPMENT_OK") equip = "EQ_WARRANT_SCANNER";
if (p.equipmentStatus("EQ_WARRANT_SCANNER_POLICE") === "EQUIPMENT_OK" || p.equipmentStatus("EQ_WARRANT_SCANNER_POLICE_PASSIVE") === "EQUIPMENT_OK") equip = "EQ_WARRANT_SCANNER_POLICE";
if (equip != "") {
var eq = EquipmentInfo.infoForKey(equip);
p.script._warrantScannerTime = eq.scriptInfo.scan_time;
p.script._warrantScannerRange = p.scannerRange * eq.scriptInfo.scan_range;
}
// if the player doesn't have a way of seeing the target bounty, put the scanner mode into override
this._overrideMode = false;
if (this._scannerMode === 1 && p.equipmentStatus("EQ_SCANNER_SHOW_MISSILE_TARGET") != "EQUIPMENT_OK") this._overrideMode = true;
}
//-------------------------------------------------------------------------------------------------------------
this.playerBoughtEquipment = function (equip) {
var p = player.ship;
// remove and refund warrant scanner (both versions)
if (equip === "EQ_WARRANT_SCANNER_REMOVAL") {
p.removeEquipment(equip);
var stn = p.dockedStation;
var eqSts = "";
var eqSts1 = p.equipmentStatus("EQ_WARRANT_SCANNER");
if (eqSts1 === "EQUIPMENT_OK" || eqSts1 === "EQUIPMENT_DAMAGED") {
var eq = EquipmentInfo.infoForKey("EQ_WARRANT_SCANNER");
p.removeEquipment("EQ_WARRANT_SCANNER");
eqSts = eqSts1;
}
eqSts1 = p.equipmentStatus("EQ_WARRANT_SCANNER_PASSIVE");
if (eqSts1 === "EQUIPMENT_OK" || eqSts1 === "EQUIPMENT_DAMAGED") {
var eq = EquipmentInfo.infoForKey("EQ_WARRANT_SCANNER_PASSIVE");
p.removeEquipment("EQ_WARRANT_SCANNER_PASSIVE");
eqSts = eqSts1;
}
var eqSts2 = p.equipmentStatus("EQ_WARRANT_SCANNER_POLICE");
if (eqSts2 === "EQUIPMENT_OK" || eqSts2 === "EQUIPMENT_DAMAGED") {
var eq = EquipmentInfo.infoForKey("EQ_WARRANT_SCANNER_POLICE");
p.removeEquipment("EQ_WARRANT_SCANNER_POLICE");
eqSts = eqSts2;
}
eqSts2 = p.equipmentStatus("EQ_WARRANT_SCANNER_POLICE_PASSIVE");
if (eqSts2 === "EQUIPMENT_OK" || eqSts2 === "EQUIPMENT_DAMAGED") {
var eq = EquipmentInfo.infoForKey("EQ_WARRANT_SCANNER_POLICE_PASSIVE");
p.removeEquipment("EQ_WARRANT_SCANNER_POLICE_PASSIVE");
eqSts = eqSts2;
}
// refund the cost of the equipment
if (eq) {
if (eqSts === "EQUIPMENT_OK") player.credits += (eq.price / 10) * stn.equipmentPriceFactor;
if (eqSts === "EQUIPMENT_DAMAGED") player.credits += ((eq.price / 10) * stn.equipmentPriceFactor) / 2;
}
}
}
//-------------------------------------------------------------------------------------------------------------
this.shipSpawned = function (newship) {
if (newship.isShip === false) return;
// ignore these types
if (newship.isPlayer || newship.isRock || newship.isThargoid || newship.isCargo || newship.isDerelict || newship.isBeacon ||
newship.isStation || newship.isBoulder || newship.isWeapon || newship.isWormhole || newship.isVisualEffect || !newship.isPiloted) return;
// for some standard ships that gets spawned we want to create a secondary bounty value that the player can uncover using the warrant scanner
// some assassins
// some traders
// rarely hunters
// most pirates
// no miners/scavengers
// most of these increases will only be marginal (most 10 points, some 20, very few 30 and above)
// if the ship already has the property, we can exit
// this is most likely a relaunch at a station via SDC
if (newship.script.hasOwnProperty("_hiddenBounty")) return;
newship.script._hiddenBounty = 0;
if (newship.isPolice === false) {
if (Ship.roleIsInCategory(newship.primaryRole, "oolite-pirate")) {
if (Math.random() > 0.2) {
var b = parseInt(Math.random() * 10) + 10;
// some larger bounties
if (Math.random() > 0.8) {
b = parseInt(Math.random() * 20) + 30
}
// and rarely, some even larger ones
if (Math.random() > 0.95) {
b = parseInt(Math.random() * 40) + 50
}
newship.script._hiddenBounty = b;
if (this._debug) log(this.name, "adding pirate bounty for " + newship);
}
} else if (Ship.roleIsInCategory(newship.primaryRole, "oolite-assassin")) {
if (Math.random() > 0.5) {
var b = parseInt(Math.random() * 10) + 20;
// rarely some larger bounties
if (Math.random() > 0.92) {
b = parseInt(Math.random() * 20) + 40
}
// and even rarely, some even larger ones
if (Math.random() > 0.99) {
b = parseInt(Math.random() * 40) + 50
}
newship.script._hiddenBounty = b;
if (this._debug) log(this.name, "adding assassin bounty for " + newship);
}
} else if (Ship.roleIsInCategory(newship.primaryRole, "oolite-trader")) {
if (Math.random() > 0.8 && newship.name.indexOf("Medical") === 0) {
newship.script._hiddenBounty = parseInt(Math.random() * 10) + 5;
if (this._debug) log(this.name, "adding trader bounty for " + newship);
}
} else if (Ship.roleIsInCategory(newship.primaryRole, "oolite-bounty-hunter")) {
if (Math.random() > 0.98) {
newship.script._hiddenBounty = parseInt(Math.random() * 10) + 5;
if (this._debug) log(this.name, "adding hunter bounty for " + newship);
}
// most hunters (60%) will have the warrant scanner
if (this._giveToNPC === true) {
if (Math.random() > 0.4) {
if (this._debug) log(this.name, "adding equipment to " + newship);
newship.awardEquipment("EQ_WARRANT_SCANNER");
this.$addScannerRoutines(newship);
}
}
}
}
// store what the hidden bounty is in a separate spot so we can reset back to it if the ship jumps to another system
// allows us to simulate the same rules the player gets
newship.script._storedHiddenBounty = newship.script._hiddenBounty;
// add our shipExitedWormhole, shipDied and shipRemoved events to the ship
// monkey patch if necessary
if (newship.script.shipExitedWormhole) newship.script.$bounty_ovr_shipExitedWormhole = newship.script.shipExitedWormhole;
newship.script.shipExitedWormhole = this.$bounty_shipExitedWormhole;
if (newship.script.shipDied) newship.script.$bounty_ovr_shipDied = newship.script.shipDied;
newship.script.shipDied = this.$bounty_shipDied;
if (newship.script.shipRemoved) newship.script.$bounty_ovr_shipRemoved = newship.script.shipRemoved;
newship.script.shipRemoved = this.$bounty_shipRemoved;
if (newship.script.shipWillEnterWormhole) newship.script.$bounty_ovr_shipWillEnterWormhole = newship.script.shipWillEnterWormhole;
newship.script.shipWillEnterWormhole = this.$bounty_shipWillEnterWormhole;
if (this._giveToNPC === true) {
// only give it to police ships that are piloted, can actually move (ie not stations), and are not satellites.
if (newship.isPolice && newship.isPiloted && newship.maxSpeed > 0 && newship.hasRole("RSsatellite") === false) {
// all police vessels have the scanner
if (this._debug) log(this.name, "adding equipment to " + newship);
newship.awardEquipment("EQ_WARRANT_SCANNER_POLICE");
this.$addScannerRoutines(newship);
}
}
}
//-------------------------------------------------------------------------------------------------------------
this.equipmentDamaged = function (equipmentKey) {
// switch to override mode if the STE is damaged
if (equipmentKey === "EQ_SCANNER_SHOW_MISSILE_TARGET" && this._scannerMode === 1) this._overrideMode = true;
}
//-------------------------------------------------------------------------------------------------------------
this.equipmentRepaired = function (equipmentKey) {
// switch off override mode if the STE is repaired
if (equipmentKey === "EQ_SCANNER_SHOW_MISSILE_TARGET" && this._scannerMode === 1) this._overrideMode = false;
}
//-------------------------------------------------------------------------------------------------------------
this.shipTargetAcquired = function (target) {
var ts = target.script;
if (!ts) return;
if (!player.ship.weaponsOnline && !target.hasHostileTarget && target.alertCondition !== 3) return; // don't auto-scan when weapons are offline, unless target won't react (has a hostile target or is in red alert)
// if the scanner mode is set to automatically scan, and we haven't started scanning this ship, start it now
if (((this._scannerMode === 1 && target.bounty > 0 && this._overrideMode === false) || (this._scannerMode === 1 && this._overrideMode === true) || this._scannerMode === 2) && ts.hasOwnProperty("_warrantScanPosition") === false) { // removed player.ship.script._warrantScannerRange restriction
this.$startScan();
return;
}
// if we're already partway through scanning the target, automatically continue now
if (ts.hasOwnProperty("_warrantScanPosition") === true && ts._warrantScanPosition >= 0 && ts._warrantScanPosition < player.ship.script._warrantScannerTime) {
this.$startScan();
}
}
//-------------------------------------------------------------------------------------------------------------
this.activated = function $activated() {
var that = $activated; // pointer to this function
var s = (that.s = that.s || worldScripts.BountySystem_WarrantScanner); // cache reference to worldScript in local property on this function
if (s._warrantScannerTimer && s._warrantScannerTimer.isRunning) {
s._warrantScannerTimer.stop();
if (s._outputMode === 0) s.$turnOffMFD();
player.consoleMessage("Warrant scanner disengaged.");
return;
}
// is the equipment OK?
// theoretically the player can only have the standard scanner, but just in case the rules change later...
if (player.ship.equipmentStatus("EQ_WARRANT_SCANNER") != "EQUIPMENT_OK" && player.ship.equipmentStatus("EQ_WARRANT_SCANNER_POLICE") != "EQUIPMENT_OK" &&
player.ship.equipmentStatus("EQ_WARRANT_SCANNER_PASSIVE") != "EQUIPMENT_OK" && player.ship.equipmentStatus("EQ_WARRANT_SCANNER_POLICE_PASSIVE") != "EQUIPMENT_OK") return;
// do we have a station target and is it a main station?
if (s.$targetIsValid(player.ship) === false) return;
// automatically switch to Non-MFD mode if there are no available slots
if (s._outputMode === 0 && s.$isMFDSlotAvailable() === false) s._outputMode = 1;
// TWEAK: automatically switch to MFD mode if there is an available slot (thanks to Nite Owl)
if (s._outputMode === 1 && s.$isMFDSlotAvailable() === true) s._outputMode = 0;
var p = player.ship;
// have we started scanning this ship already?
var checkScan = s.$existingShipScanPosition(p.target);
if (checkScan >= p.script._warrantScannerTime) {
if (s._outputMode === 1) player.consoleMessage("Ship already scanned.");
if (s._outputMode === 0) {
s.$updateMFD("Warrant Scanner:\nShip already scanned.");
if (s._hideMFDTimer && s._hideMFDTimer.isRunning) s._hideMFDTimer.stop();
s._hideMFDTimer = new Timer(s, s.$turnOffMFD, 5, 0);
}
return;
}
if (s._outputMode === 1) player.consoleMessage("Scanning started.");
s._warrantScannerTimer = new Timer(s, s.$scanProcess, 1, 1);
s._lastTarget = p.target;
if (s._hideMFDTimer && s._hideMFDTimer.isRunning) s._hideMFDTimer.stop();
if (s._outputMode === 0) s.$updateMFD("Warrant Scanner:\nInitialising...\n");
}
//-------------------------------------------------------------------------------------------------------------
this.mode = function $mode() {
var that = $mode; // pointer to this function
var s = (that.s = that.s || worldScripts.BountySystem_WarrantScanner); // cache reference to worldScript in local property on this function
if (s._outputMode === 0) {
s._outputMode = 1;
player.consoleMessage("Switched to Non-MFD mode.");
return;
}
if (s._outputMode === 1) {
s._outputMode = 0;
player.consoleMessage("Switched to MFD mode.");
return;
}
}
//-------------------------------------------------------------------------------------------------------------
this.$addScannerRoutines = function $addScannerRoutines(newship) {
var that = $addScannerRoutines; // pointer to this function
var w = (that.w = that.w || worldScripts.BountySystem_NPCScan); // cache reference to worldScript in local property on this function
newship.script.$initialiseTimer = w.$initialiseTimer;
newship.script.$checkScannerForTarget = w.$checkScannerForTarget;
newship.script.$scanTarget = w.$scanTarget;
newship.script.$initialiseTimer();
}
//-------------------------------------------------------------------------------------------------------------
this.$startScan = function () {
if (this._hideMFDTimer && this._hideMFDTimer.isRunning) this._hideMFDTimer.stop();
if (this._warrantScannerTimer && this._warrantScannerTimer.isRunning) this._warrantScannerTimer.stop();
this.activated();
}
//-------------------------------------------------------------------------------------------------------------
this.$scanProcess = function $scanProcess() {
var that = $scanProcess; // pointer to this function
var w = (that.w = that.w || worldScripts.BountySystem_WarrantScanner); // cache reference to worldScript in local property on this function
var p = player.ship;
// player switched target while scanning or target isn't valid anymore (jumped out/destroyed) - auto-off triggered
if (p.target != this._lastTarget || this.$targetIsValid(p) === false) {
player.consoleMessage("Warrant scanner disengaged.");
if (this._outputMode === 0) this.$turnOffMFD();
this._warrantScannerTimer.stop();
return;
}
// add this ship to the array
var scanPos = this.$existingShipScanPosition(p.target);
//if (scanPos === 0) this._scannedShips.push({ship:p.target, scanPosition:0});
var mfdText = "Warrant Scanner:\n";
var outOfRange = false;
// make sure target is still in range - but put scan on hold until it is
if (this.$targetInRange(p) === false) {
mfdText += "Ship out of range.\n";
outOfRange = true;
} else {
scanPos += 1;
}
if (this._outputMode === 1 && outOfRange === false) player.consoleMessage(((scanPos / p.script._warrantScannerTime) * 100).toFixed(0) + "% complete");
if (scanPos === p.script._warrantScannerTime) {
mfdText += "Scan complete.\n";
} else {
mfdText += "Scanning: " + ((scanPos / p.script._warrantScannerTime) * 100).toFixed(0) + "% complete\n";
}
if (scanPos >= this._scanTimeMidPoint) {
var viewrole = this.$translateRole(p.target.primaryRole);
mfdText += "\nPilot: " + this.$extractPilotName(p.target) + "\n" + (viewrole != "Unknown" ? viewrole + "\n" : "");
// only display the message once for consoleMessage mode
if (this._outputMode === 1 && scanPos === this._scanTimeMidPoint && outOfRange === false) {
player.consoleMessage("Pilot is " + this.$extractPilotName(p.target) + (viewrole != "Unknown" ? " (" + viewrole + ")" : ""));
}
}
p.target.script._warrantScanPosition = scanPos;
// update the scan position for this ship
/*for (var i = 0; i < this._scannedShips.length; i++) {
if (this._scannedShips[i].ship === p.target) this._scannedShips[i].scanPosition = scanPos;
}*/
// NPC reaction after 2 seconds
if (scanPos === 2) {
// decide if they will flee, attack or ignore
this.$npcReaction(p.target, p);
}
// finish the scan
if (scanPos >= p.script._warrantScannerTime) {
if (this._outputMode === 1) player.consoleMessage("Scan complete.");
this._warrantScannerTimer.stop();
// will this ship get a bounty?
var check = w.$checkBounty(p.target, true, p);
if (check > 0) {
mfdText += "\nAdditional warrants of " + formatCredits(check, false, true) + "\napplied to target.";
if (this._outputMode === 1) player.consoleMessage("Additional warrants of " + formatCredits(check, false, true) + " applied to target.");
p.target.bounty += check;
} else {
if (this._outputMode === 1) player.consoleMessage("No additional warrants found for target.");
mfdText += "\nNo additional warrants\nfound for target.";
}
if (this._outputMode === 0) {
if (this._hideMFDTimer && this._hideMFDTimer.isRunning) this._hideMFDTimer.stop();
this._hideMFDTimer = new Timer(this, this.$turnOffMFD, 10, 0);
}
}
if (this._outputMode === 0) this.$updateMFD(mfdText);
}
//-------------------------------------------------------------------------------------------------------------
// checks if we have an MFD slot available (a blank spot or a slot currently allocated to this OXP.
// Returns true if a spot is found, otherwise false
this.$isMFDSlotAvailable = function () {
var p = player.ship;
var result = false;
for (var i = 0; i < p.multiFunctionDisplayList.length; i++) {
if (!p.multiFunctionDisplayList[i] || (p.multiFunctionDisplayList[i] === "" || p.multiFunctionDisplayList[i] === this.name)) {
result = true;
}
}
return result;
}
//-------------------------------------------------------------------------------------------------------------
this.$updateMFD = function (text) {
var p = player.ship;
// set the text in the MFD
p.setMultiFunctionText(this.name, text, false);
// if the hud is hidden don't try an update - it will get confusing as to which mfd slot is open or not.
if (p.hudHidden === false) {
// find the slot currently set for this MFD
this.$findMFDID();
// if we haven't got a set slot (this._mfdID === -1) or the set slot we had is now unavailable...
if (this._mfdID === -1 ||
(p.multiFunctionDisplayList[this._mfdID] && p.multiFunctionDisplayList[this._mfdID] != "" && p.multiFunctionDisplayList[this._mfdID] != this.name)) {
// find a free slot
// first, make sure we reset our slot id marker (in the case where the previous one is in use)
this._mfdID = -1;
// search for a free slot
for (var i = 0; i < p.multiFunctionDisplayList.length; i++) {
if (!p.multiFunctionDisplayList[i] || p.multiFunctionDisplayList[i] === "") {
this._mfdID = i;
break;
}
}
}
// we have a free slot, so force the mfd to display
if (this._mfdID != -1) p.setMultiFunctionDisplay(this._mfdID, this.name);
}
}
//-------------------------------------------------------------------------------------------------------------
// records the index of the MFD that currently holds the warrant scanner mfd
this.$findMFDID = function () {
var p = player.ship;
if (p.isValid === false || !p.multiFunctionDisplayList) return;
for (var i = 0; i < p.multiFunctionDisplayList.length; i++) {
if (p.multiFunctionDisplayList[i] === this.name) this._mfdID = i;
}
}
//-------------------------------------------------------------------------------------------------------------
// hides all instances of the warrant scanner MFD
this.$turnOffMFD = function $turnOffMFD() {
var p = player.ship;
if (p.isValid === false || !p.multiFunctionDisplayList) return;
for (var i = 0; i < p.multiFunctionDisplayList.length; i++) {
if (p.multiFunctionDisplayList[i] === this.name) {
p.setMultiFunctionDisplay(i, "");
}
}
}
//-------------------------------------------------------------------------------------------------------------
// gets a readable form of the pilot's name
this.$extractPilotName = function (ship) {
var pilotName = "";
var pilot = ship.crew[0];
pilotName = pilot.name + (this._outputMode === 1 ? ", " : "\n") + pilot.description;
return pilotName;
}
//-------------------------------------------------------------------------------------------------------------
// returns the scan position (ie. how far through the scan we have gotten) for a particular ship
this.$existingShipScanPosition = function (ship) {
if (ship.script.hasOwnProperty("_warrantScanPosition") === false) ship.script._warrantScanPosition = 0;
return ship.script._warrantScanPosition;
/*
for (var i = 0; i < this._scannedShips.length; i++) {
if (this._scannedShips[i].ship === ship) return this._scannedShips[i].scanPosition;
}
return 0;
*/
}
//-------------------------------------------------------------------------------------------------------------
// checks the target of the source ship to ensure it is still valid. If so, returns true.
// when false, scanning process will stop
this.$targetIsValid = function (source) {
var ship = source.target;
var result = true;
// do we have a station target and is it a main station?
if (!ship || ship.isValid === false) {
if (source.isPlayer) player.consoleMessage("No target selected.");
result = false;
}
if (result === true && ship.isStation) {
if (source.isPlayer) player.consoleMessage("Scanner will not work on stations.");
result = false;
}
if (result === true && (ship.isPiloted === false || ship.isThargoid || ship.hasRole("escape-capsule"))) {
if (source.isPlayer) player.consoleMessage("No valid target selected.");
result = false;
}
if (result === true && (source.injectorsEngaged || (source.isPlayer && this._ficcInstalled && source.script._ficcEngaged === true) || source.torusEngaged === true)) {
if (source.isPlayer) player.consoleMessage("Unable to scan at injector or torus speed.");
result = false;
}
if (result === true && source.isCloaked) {
if (source.isPlayer) player.consoleMessage("Scanner will not work when cloaked.");
result = false;
}
if (result === true && ship.isCloaked) {
if (source.isPlayer) player.consoleMessage("Scanner unable to target cloaked ship.");
result = false;
}
return result;
}
//-------------------------------------------------------------------------------------------------------------
// checks range to source's target. when in range, returns true
// otherwise false. When false, scan will continue but be on hold.
this.$targetInRange = function (source) {
var dist = source.position.distanceTo(source.target);
if (source.script._debugNPCScan === true) log(this.name, "dist to target = " + dist + " for " + source);
// are we too far away for a good scan?
if (dist > source.script._warrantScannerRange) {
if (source.isPlayer) {
if (this._outputMode === 1) player.consoleMessage("Ship out of range.");
}
return false;
}
return true;
}
//-------------------------------------------------------------------------------------------------------------
// checks the hidden bounty for a particular ship. doRemove will do an "official" search, in which case the ship will be removed from the list
// basically, once a ship has been scanned with the warrant scanner we don't want the ship to get scanned again and increase their bounty multiple times.
// so we remove it for an official scan.
this.$checkBounty = function $checkBounty (checkship, doRemove, source) {
var bounty = 0;
if (checkship.isPlayer) {
// uncover the player's bounties
var that = $checkBounty; // pointer to this function
var w = (that.w = that.w || worldScripts.BountySystem_Core); // cache reference to worldScript in local property on this function
w.$uncoverBounties(source);
w.shipExitedWitchspace();
} else {
var idx = -1;
bounty = checkship.script._hiddenBounty;
// if this is an official scan, reset the ships hiddenBounty value so they don't get multiple bounty increases
if (doRemove === true && bounty > 0) checkship.script._hiddenBounty = 0;
}
return bounty;
}
//-------------------------------------------------------------------------------------------------------------
// updates the hidden bounty for a particular ship, or adds it if the ship isn't in the list already
this.$setBounty = function (checkship, newbounty) {
checkship.script._hiddenBounty = newbounty;
}
//-------------------------------------------------------------------------------------------------------------
this.$npcReaction = function (ship, source) {
// don't do anything if the ship is under attack or at condition red - they're too busy to notice
if (ship.hasHostileTarget || ship.alertCondition === 3) return;
// check if they've got something to hide
var check = this.$checkBounty(ship, false, source);
if (check === 0) return;
// count up the number of ships in range that might be doing the scan
var ships = ship.checkScanner(true);
var inRange = 0;
if (ships) {
for (var i = 0; i < ships.length; i++) {
var groupMbr = false;
// check if this ship is actually a member of the same group as the target
if (ships[i].group && ships[i].group.containsShip(ship)) groupMbr = true;
if (ships[i].isPiloted === true && ships[i].isStation === false && ships[i].isThargoid === false && groupMbr === false &&
ship.position.distanceTo(ships[i]) <= source.script._warrantScannerRange) inRange += 1;
}
}
if (ship.equipmentStatus("EQ_CLOAKING_DEVICE") === "EQUIPMENT_OK") {
// turn on the cloak to evade detection
if (this._debug) log(this.name, "cloaking " + ship);
ship.isCloaked = true;
}
// if there's only one ship in range, then it's obvious who the scanner is
if (inRange === 1) {
ship.target = source;
if (source.isPolice === false && ship.threatAssessment(false) > source.threatAssessment(false) && ship.withinStationAegis === false) {
if (this._debug) log(this.name, "attack mode(1) engaged for " + ship + " against " + source);
ship.performAttack();
return;
} else {
if (this._debug) log(this.name, "flee mode(1) engaged for " + ship + " against " + source);
ship.performFlee();
return;
}
}
// won't know who the perpetrator is if more than one ship in range
if (inRange > 1) return;
// otherwise, they'll know exactly who it is
// they're clear, but it's still not friendly
// police will ignore
// pirates will most likely attack
// traders will sometimes attack or sometimes flee
// assassins will attack
// hunters will sometimes attack
// if the one scanning is a police vessel, don't do anything.
if (source.isPolice) return;
if (Ship.roleIsInCategory(ship.primaryRole, "oolite-pirate") && Math.random() > 0.8) {
if (this._debug) log(this.name, "attack mode(2) engaged for " + ship + " against " + source);
ship.target = source;
ship.performAttack();
return;
}
if (Ship.roleIsInCategory(ship.primaryRole, "oolite-assassin") && ship.withinStationAegis === false && Math.random() > 0.8) {
if (this._debug) log(this.name, "attack mode(3) engaged for " + ship + " against " + source);
ship.target = source;
ship.performAttack();
return;
}
if (Ship.roleIsInCategory(ship.primaryRole, "oolite-trader") && ship.withinStationAegis === false && Math.random() > 0.9 && ship.threatAssessment(false) > source.threatAssessment(false)) {
if (this._debug) log(this.name, "attack mode(4) engaged for " + ship + " against " + source);
ship.target = source;
ship.performAttack();
return;
}
if (Ship.roleIsInCategory(ship.primaryRole, "oolite-trader") && ship.withinStationAegis === false && Math.random() > 0.3) {
if (this._debug) log(this.name, "flee mode(2) engaged for " + ship + " against " + source);
ship.target = source;
ship.performFlee();
return;
}
if (Ship.roleIsInCategory(ship.primaryRole, "oolite-bounty-hunter") && ship.withinStationAegis === false && Math.random() > 0.98) {
if (this._debug) log(this.name, "attack mode(5) engaged for " + ship + " against " + source);
ship.target = source;
ship.performAttack();
return;
}
}
//-------------------------------------------------------------------------------------------------------------
this.$translateRole = function (role) {
switch (role) {
case "hunter":
case "hunter-medium":
case "hunter-heavy":
return "Known bounty hunter";
case "pirate":
case "pirate-light-fighter":
case "pirate-medium-fighter":
case "pirate-heavy-fighter":
case "pirate-light-freighter":
case "pirate-medium-freighter":
case "pirate-heavy-freighter":
case "pirate-aegis-raider":
case "pirate-interceptor":
case "ftzpirate":
return "Known pirate";
case "trader":
case "trader-courier":
case "trader-courier+":
case "trader-smuggler":
return "Known trader";
case "assassin-light":
case "assassin-medium":
case "assassin-heavy":
return "Known assassin";
case "police":
case "police-station-patrol":
case "police-witchpoint-patrol":
return "Known police officer";
}
return "Unknown";
}
//-------------------------------------------------------------------------------------------------------------
this.$bounty_shipExitedWormhole = function () {
if (this.ship.script.$bounty_ovr_shipExitedWormhole) this.ship.script.$bounty_ovr_shipExitedWormhole;
if (this.ship.script.hasOwnProperty("_hiddenBounty") === false) return;
// if this ship got scanned and had its hidden bounty applied, remove and reset when we exit a wormhole
this.ship.script._warrantScanPosition = 0;
if (this.ship.script._storedHiddenBounty > 0 && this.ship.script._hiddenBounty === 0) {
this.ship.bounty -= this.ship.script._storedHiddenBounty;
if (this.ship.bounty > 0) {
// reset back to zero - assume any bounty in that system was local
this.ship.bounty = 0;
// then we need to determine if this ship has a local bounty in the new system as well
if (Math.random() > 0.4) {
var sys = System.infoForSystem(galaxyNumber, this.ship.destinationSystem);
switch (this.ship.primaryRole) {
case "trader":
if (Math.random() > 0.6) this.ship.setBounty(Math.ceil(Math.random() * 20), "setup actions");
break;
case "trader-smuggler":
case "trader-smuggler+":
this.ship.setBounty(Math.ceil(Math.random() * 20), "setup actions");
if (this.ship.bounty > this.ship.cargoSpaceCapacity * 2) {
this.ship.bounty = this.ship.cargoSpaceCapacity * 2;
}
break;
case "pirate":
this.ship.setBounty(20 + sys.government + (this.ship.group ? this.ship.group.count : 0) + Math.floor(Math.random() * 8), "setup actions");
break;
case "pirate-interceptor":
this.ship.setBounty(50 + sys.government + Math.floor(Math.random() * 36), "setup actions");
break;
case "pirate-light-fighter":
case "pirate-medium-fighter":
case "pirate-heavy-fighter":
this.ship.setBounty(20 + sys.government + Math.floor(Math.random() * 12), "setup actions");
break;
case "pirate-light-freighter":
case "pirate-medium-freighter":
case "pirate-heavy-freighter":
this.ship.setBounty(60 + sys.government + Math.floor(Math.random() * 8), "setup actions");
break;
case "pirate-aegis-raider":
this.ship.setBounty(50 + sys.government + Math.floor(Math.random() * 36), "setup actions");
break;
}
}
}
this.ship.script._hiddenBounty = this.ship.script._storedHiddenBounty;
}
}
//-------------------------------------------------------------------------------------------------------------
this.$bounty_shipDied = function (whom, why) {
if (this.ship.script.$bounty_ovr_shipDied) this.ship.script.$bounty_ovr_shipDied(whom, why);
// clean up timers
if (this.ship.script._checkTimer && this.ship.script._checkTimer.isRunning) {
this.ship.script._checkTimer.stop();
delete this.ship.script._checkTimer;
}
if (this.ship.script._warrantScannerTimer && this.ship.script._warrantScannerTimer.isRunning) {
this.ship.script._warrantScannerTimer.stop();
delete this.ship.script._warrantScannerTimer;
}
}
//-------------------------------------------------------------------------------------------------------------
this.$bounty_shipRemoved = function (suppressDeathEvent) {
if (this.ship.script.$bounty_ovr_shipRemoved) this.ship.script.$bounty_ovr_shipRemoved(suppressDeathEvent);
// clean up timers
if (this.ship.script._checkTimer && this.ship.script._checkTimer.isRunning) {
this.ship.script._checkTimer.stop();
delete this.ship.script._checkTimer;
}
if (this.ship.script._warrantScannerTimer && this.ship.script._warrantScannerTimer.isRunning) {
this.ship.script._warrantScannerTimer.stop();
delete this.ship.script._warrantScannerTimer;
}
}
//-------------------------------------------------------------------------------------------------------------
this.$bounty_shipWillEnterWormhole = function () {
if (this.ship.script.$bounty_ovr_shipWillEnterWormhole) this.ship.script.$bounty_ovr_shipWillEnterWormhole();
// clean up timers
if (this.ship.script._checkTimer && this.ship.script._checkTimer.isRunning) {
this.ship.script._checkTimer.stop();
delete this.ship.script._checkTimer;
}
if (this.ship.script._warrantScannerTimer && this.ship.script._warrantScannerTimer.isRunning) {
this.ship.script._warrantScannerTimer.stop();
delete this.ship.script._warrantScannerTimer;
}
}