[WIP] Bounty system OXP

Discussion and information relevant to creating special missions, new ships, skins etc.

Moderators: another_commander, winston

User avatar
phkb
Impressively Grand Sub-Admiral
Impressively Grand Sub-Admiral
Posts: 4644
Joined: Tue Jan 21, 2014 10:37 pm
Location: Writing more OXPs, because the world needs more OXPs.

Re: [WIP] Bounty system OXP

Post by phkb »

Another bug squashed in v0.7, with thanks to dybal for the tip off.
User avatar
Milo
---- E L I T E ----
---- E L I T E ----
Posts: 466
Joined: Mon Sep 17, 2018 5:01 pm

Re: [WIP] Bounty system OXP

Post by Milo »

With auto-scanning mode, I targeted an offender from a distance of 20km, and nothing happened. When I approached within 15km and did the same, the scanner MFD appeared and scanning started. I think it would be better if when you target someone that otherwise qualifies to be scanned, the MFD would appear but indicate out of range.
User avatar
phkb
Impressively Grand Sub-Admiral
Impressively Grand Sub-Admiral
Posts: 4644
Joined: Tue Jan 21, 2014 10:37 pm
Location: Writing more OXPs, because the world needs more OXPs.

Re: [WIP] Bounty system OXP

Post by phkb »

Good idea. In for the next release.
dybal
---- E L I T E ----
---- E L I T E ----
Posts: 499
Joined: Mon Feb 10, 2020 12:47 pm

Re: [WIP] Bounty system OXP

Post by dybal »

I recently got myself a fugitive status to be able to find/dock in some of Anarchies features (Renegade Stations and Hacker Outposts).

I slowly worn down the bounty from 111 to 2 by helping police, then with bounty 2 that didn't work anymore (I think that's an intentional feature).

Several GalCop cops commed me to get to the station and pay the fines.

When I docked at secondary stations (I run Additional Planets and Stations for Additional Planets) there were no screens applying a fine and sending me to "attitude adjustment", but I found several emails about 500 Cr fines being applied, and I didn't notice any change in my credit balance (I think that's a potential bug).

I finaly docked at the Main Station and there was a screen applying a 200Cr fine and sending me to "attitude adjustment". My legal status on Status Screen showed Clean.

Then I launched, was scanned by a GalCop Viper and got back my bounty of 2. I reloaded the last savegame and looked at the Security Office and the entry for an offence was there, with fine of 50 Cr payable. I paid it and after that my clean status held.

I suppose the 200 Cr fine and "attitude adjustment" was done by the core game because a cop flagged me to pay my fines, but shouldn't that clear my Bounty System record without having to go to the Security Office and pay the fine again?
User avatar
phkb
Impressively Grand Sub-Admiral
Impressively Grand Sub-Admiral
Posts: 4644
Joined: Tue Jan 21, 2014 10:37 pm
Location: Writing more OXPs, because the world needs more OXPs.

Re: [WIP] Bounty system OXP

Post by phkb »

dybal wrote: Sun Jul 05, 2020 5:27 pm
I found several emails about 500 Cr fines being applied, and I didn't notice any change in my credit balance (I think that's a potential bug).
Bug in the Email System, fixed in next release.
dybal wrote: Sun Jul 05, 2020 5:27 pm
I suppose the 200 Cr fine and "attitude adjustment" was done by the core game because a cop flagged me to pay my fines, but shouldn't that clear my Bounty System record without having to go to the Security Office and pay the fine again?
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.

From the Bounty System wiki:
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.
User avatar
Milo
---- E L I T E ----
---- E L I T E ----
Posts: 466
Joined: Mon Sep 17, 2018 5:01 pm

Re: [WIP] Bounty system OXP

Post by Milo »

I agree with dybal, that behavior is non-intuitive. You would think the local cops would have noted your compliance after fining you. I think it is fine for the bounty to return if you go to a different system and are scanned there (so until you pay off the bounty in the original system or at an HQ, you are at risk of being an offender wherever you go), but as long as you are in the same system after paying the local offender tax, I think you should get to keep your clean status even if the local cops scan you again.
dybal
---- E L I T E ----
---- E L I T E ----
Posts: 499
Joined: Mon Feb 10, 2020 12:47 pm

Re: [WIP] Bounty system OXP

Post by dybal »

The original offence was in another system, yes, but I went to Security Offices several times in that system to monitor the fine as the bounty decreased by helping police and there never was a local offence listed.
User avatar
Milo
---- E L I T E ----
---- E L I T E ----
Posts: 466
Joined: Mon Sep 17, 2018 5:01 pm

Re: [WIP] Bounty system OXP

Post by Milo »

The 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.
User avatar
phkb
Impressively Grand Sub-Admiral
Impressively Grand Sub-Admiral
Posts: 4644
Joined: Tue Jan 21, 2014 10:37 pm
Location: Writing more OXPs, because the world needs more OXPs.

Re: [WIP] Bounty system OXP

Post by phkb »

Milo wrote: Tue Jul 07, 2020 12:22 pm
The 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.
That is probably a more intuitive way of dealing with it. I'll add it to the list.
User avatar
Milo
---- E L I T E ----
---- E L I T E ----
Posts: 466
Joined: Mon Sep 17, 2018 5:01 pm

Re: [WIP] Bounty system OXP

Post by Milo »

Here is a suggested modification replacing this.$scanTarget (relevant section is in the middle but I optimized[?] the whole function):

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();
	}
}
Since the npcscan script is both a world script and a ship script, I think the don't apply out-of-system-bounties-again variable needs to be kept somewhere centralized, which is why I put it as worldScripts.BountySystem_Core._alreadyScannedPlayerInThisSystem. We don't have to worry about the case when the player leaves the system because any ships in the previous system are removed.

Below code is all for bountysystem_core.js:

Initialize:

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
Load (add to this.startUpComplete):

Code: Select all

	if (missionVariables.BountySystem_alreadyScannedPlayerInThisSystem) {
		this._alreadyScannedPlayerInThisSystem = missionVariables.BountySystem_alreadyScannedPlayerInThisSystem;
		delete missionVariables.BountySystem_alreadyScannedPlayerInThisSystem;
	}
Save (add to this.playerWillSaveGame):

Code: Select all

	missionVariables.BountySystem_alreadyScannedPlayerInThisSystem = this._alreadyScannedPlayerInThisSystem;
Clear (add to this.shipWillEnterWitchspace):

Code: Select all

this._alreadyScannedPlayerInThisSystem = false;
Docking (just showing the top lines of the function):

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;
	}
I considered that the player might pay a fine before having been scanned. That would imply that they were fined for a local offence only, so I think we should allow a scan to happen after that, which could apply a separate "out-of-system" bounty to them.

TODO: As an enhancement (scope creep?) to Bounty System, the warrant scan could include a manifest scan; then, you could check if any of the goods on the player's public manifest are illegal imports in the current system, and if so demand the player dump them or apply a fine or whatever... (making smuggling OXPs more important!) ... although slaves would have to be handled carefully, since escape pods count as them too. In this case, you'd want the ignore player check to only prevent re-applying out-of-system bounties, but still allow the manifest checks to happen repeatedly while the player is in-system. Since I've enclosed only the out-of-system bounty code in the ignore condition, you could add the manifest check outside that.

Incidentally, here is an unrelated minor optimization (string comparisons after booleans, instead of before):

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");
				}
			}
		}
	}
}
Last edited by Milo on Tue Jul 14, 2020 10:54 pm, edited 2 times in total.
User avatar
Milo
---- E L I T E ----
---- E L I T E ----
Posts: 466
Joined: Mon Sep 17, 2018 5:01 pm

Re: [WIP] Bounty system OXP

Post by Milo »

worldScript caching for bountysystem_scanner.js:

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;
	}
}
dybal
---- E L I T E ----
---- E L I T E ----
Posts: 499
Joined: Mon Feb 10, 2020 12:47 pm

Re: [WIP] Bounty system OXP

Post by dybal »

I noticed that the time necessary to be possible to pay a fine for a given offence doesn't start counting from the offence date, but from the date the player leaves the system where the offence was commited.

I think that's a huge in-universe story inconsistency: since it's GalCop that keeps tab of those dates and when the fine becomes payable, I ask: how does GalCop _knows_ when an offender leaves a given system? The offender knows, but how could anyone know for sure, always?

The only date GalCop knows, for sure, is the date the offence was commited... why not start counting the days till the fine is payable from that?
Last edited by dybal on Sun Jul 19, 2020 9:44 pm, edited 1 time in total.
User avatar
phkb
Impressively Grand Sub-Admiral
Impressively Grand Sub-Admiral
Posts: 4644
Joined: Tue Jan 21, 2014 10:37 pm
Location: Writing more OXPs, because the world needs more OXPs.

Re: [WIP] Bounty system OXP

Post by phkb »

Thanks Dybal, that does make more sense. I’ll add something along these lines into the pending release.
User avatar
phkb
Impressively Grand Sub-Admiral
Impressively Grand Sub-Admiral
Posts: 4644
Joined: Tue Jan 21, 2014 10:37 pm
Location: Writing more OXPs, because the world needs more OXPs.

Re: [WIP] Bounty system OXP

Post by phkb »

Version 0.9 has been released. Changes since 0.7:
  • Date applied to any offences is the date of the first offence (was previously the date the player left the system).
  • Bug fixes for appeal processing.
  • Warrant Scanner in auto-scan mode will now open MFD even if target is out of range.
  • Performance improvements for the scanning process.
  • If player is scanned in a system and bounties uncovered, and they are forced to deal with those bounties at the station, subsequent scans will not reapply bounty.
  • Code refactoring.
User avatar
Milo
---- E L I T E ----
---- E L I T E ----
Posts: 466
Joined: Mon Sep 17, 2018 5:01 pm

Re: [WIP] Bounty system OXP

Post by Milo »

Did you not include the Broadcast Comms integration or just left it out of the change log?
Post Reply