| Scripts/npb-player.js |
"use strict";
this.name = "NPB Neutralizer";
this.author = "Reval";
this.version = "1.3";
this.license = "CC-BY-NC-SA 4.0";
this.description = "Galcop's Neutral Particle Beam weapon causes catastrophic internal damage while throwing off little energy as surface heat. A direct hit leaves the target a powerless drifting hulk without triggering fatal explosions of fuel or ordinance.";
// test beam timings with Transporters?
this.$npbTest = false;
// fully disable the target?
this.$npbDisabling = true;
// cripple target on first hit?
this.$npbCrippling = true;
// OPTION: adjust Neutralizing threshold (must be < C-thresh)
this.$npbNeutralizingThreshold = 0.90;
// OPTION: adjust Crippling threshold (must be > N-thresh)
this.$npbCripplingThreshold = 0.99;
this.$log = true;
/* NOTE: there are two options for where the Neutralizer logic fires from:
1. This world Script's shipAttackedOther ($npbNPCScript=false);
2. The individual NPC NPB ship script's shipBeingAttacked ($npbNPCScript=true);
Both work equally well, with a possible efficiency gain using (1).
The main difference in behaviour is in the number of log entries: (2) has more.
But then the logging can be toggled too ($log=true/false).
There is also the option not to disable the victim, with $npbDisabling=false.
Option to cripple (or not) the victim on first NPB hit ($npbCrippling=true/false).
*/
/*
Version 1.3
OPTION: Adjust Neutralizing threshold, after which victim is definitively NPB'd.
OPTION: Adjust Crippling threshold, after which victim's systems and armament suffer.
Remove fuel injectors from a crippled ship, if fitted, to simulate disabling them.
Several cosmetic enhancements and logical tightenings.
Version 1.2a
Not a true update: remedied omission to reset the $npbTesting option on last release.
Version 1.2
OPTION: On first NPB hit, cripple victim ship's performance and armament.
On neutralization, disable (remove) victim's lasers and missiles.
Crippling also disables the victim's hyperdrive, preventing escape.
Victim crew's voice-comm reactions to being hit by NPB.
Ship's sensors' recording of NPB weapon's effects on victim vessel.
Pevent double score after killing a neutralized ship.
Code refactoring, optimizations, log & comment additions and amendments.
Version 1.1
Fully implemented Galcop License-granting conditions.
OXP now distinguishes when the NPB Neutralizer is causing the damage to a victim.
NPB can be effected either from the world script or ship script (see NOTE).
Changed the 'kill message' and several strings in the log entries.
*/
/*
To Do
*/
// Conditionally, give every system-spawned NPC ship (except SDD) a NPB script
this.shipSpawned = function(ship) {
if ((ship.primaryRole != "police") && (ship.dataKey != "sdd-mambita") && (ship.name!="Asteroid")) {
// Conditionally assign system NPCs their own neutralizer-reaction script
if (this.$npbNPCScript) {
ship.setScript("npc-npb.js");
this._npbCheckScripts()
}
}
}
this.shipWillLaunchFromStation = function() {
var ps = player.ship;
this.$npbHasEQ = (ps.equipmentStatus ("EQ_WEAPON_NEUTRALIZER") === "EQUIPMENT_OK");
}
this.startUpComplete = function() {
var ps = player.ship;
this.$npbHasEQ = (ps.equipmentStatus ("EQ_WEAPON_NEUTRALIZER") === "EQUIPMENT_OK");
}
// spawn some dollies to test Neutralizer beam timings
this.shipExitedStationAegis = function(station) {
if (this.$npbTest) {
var ps = player.ship;
// spawn some transporters
var dummy = system.addShips("[transporter]", 10, ps.position);
// make them distinctive
for (var i = 0; i < dummy.length; i++) {
// set its scanner blip colours
dummy[i].scannerDisplayColor1 = "orangeColor";
dummy[i].scannerDisplayColor2 = "whiteColor";
// make it legal to shoot at
dummy[i].bounty = 1;
// conditionally, assign NPB reaction script
if (this.$npbNPCScript) dummy[i].setScript("npc-npb.js");
}
}
}
this.shipExitedWitchspace = function() {
if (this.$npbTest) {
var ps = player.ship;
// spawn some transporters
var dummy = system.addShips("[transporter]", 10, ps.position);
for (var i=0; i<dummy.length; i++) {
// set its scanner blip colours
dummy[i].scannerDisplayColor1 = "orangeColor";
dummy[i].scannerDisplayColor2 = "whiteColor";
// make it legal to shoot at
dummy[i].bounty = 1;
// conditionally, assign NPB reaction script
if (this.$npbNPCScript) dummy[i].setScript("npc-npb.js");
}
}
}
// Neutralize! (or not)
this.shipAttackedOther = function(other) {
// exit if neutralizing from an NPC script
if (this.$npbNPCScript) return;
// exit if we don't have a Neutralizer
if (!this.$npbHasEQ) return;
// no neutralizing of invalid or disabled ships, debris, rocks, stations or buoys...
if ((!other.isValid) || (!other.isShip) || (other.isStation || other.isDisabled)) return;
// don't proceed if victim is already neutralized
if (other.$neutralized) return;
// establish if we're firing NPB
var currentWeapon = player.ship.currentWeapon;
var usedNPB = (currentWeapon && currentWeapon.equipmentKey == "EQ_WEAPON_NEUTRALIZER");
// In combat, a victim is likely to be damaged already...
// abbreviate thresholds
var cthresh = this.$npbCripplingThreshold;
var nthresh = this.$npbNeutralizingThreshold;
var mE = other.maxEnergy, E = other.energy;
// Conditions for Crippling: victim must not be below Neutralizing threshold.
var cte = (E<=nthresh);
var cripg = this.$npbCrippling;
// sanity check
if (cte) {
player.consoleMessage("Crippling threshold below Neutralizing threshold!",9);
if (this.$log) this._log("Crippling threshold <= Neutralizing threshold!");
}
// First hit: optionally cripple weapons, speed and manoevrability
if ((usedNPB) && (!cte) && (cripg) && (!other.$crippled) && (E/mE < cthresh)) {
// make victim's armament a Pulse Laser, simulating crippled weaponry
this._npbGivePulse(other);
// remove victim's missiles and mines to simulate disabling
this._npbRemoveMissilesMines(other);
// cripple speed and manoevrability
other.$crippled = this._npbCripple(other,0.25);
// disable hyperdrive
other.hyperspaceSpinTime = -1;
// disable fuel injector, if fitted, by removing it
if (this._npbShipHasEquipment(other, "EQ_FUEL_INJECTION"))
other.removeEquipment("EQ_FUEL_INJECTION");
// make victim's scanner blip blink
var osdc = other.scannerDisplayColor1;
other.scannerDisplayColor1 = osdc;
other.scannerDisplayColor2 = "blackColor";
// amend display name to 'crippled' + ship name
other.displayName = 'Crippled '+other.name;
// victim crew reaction to NPB effects
this._npbExclaim(other);
// sensors advise on NPB effects
this._npbAdvisory(other);
// Notify victim and SDD-Net of crippling
var name = other.name;
var dname = other.displayName;
var bount = other.bounty;
var ps = player.ship;
var pc = player.consoleMessage;
var oc = ps.messageGuiTextColor;
// set the 'police comms' colour - a light purple
ps.messageGuiTextColor = "0.7 0.3 0.8";
// communicate crippling and potential bounty on subspace comm
pc(" ", 5); // line break
pc(dname+" awaits further action. (+"+bount+" cr).", 9);
ps.messageGuiTextColor = oc;
if (this.$log) this._log(dname+" awaits further action. (Possible +"+bount+").");
}
// Neutralize if NPB is the weapon and quarry's energy is below threshold
if ((usedNPB) && (!other.$neutralized) && (E / mE < nthresh)) {
// victim crew reaction to NPB
this._npbExclaim(other);
// remove victim's lasers to simulate weapons disabled
this._npbRemoveLasers(other);
// remove victim's missiles and mines to simulate disabling
this._npbRemoveMissilesMines(other);
// Apply disabling effects:
// victim's engine is disabled, so make it drift
other.maxSpeed = 0;
other.setAI("dumbAI.plist");
// Conditionally, disable the ship, making it a drifting hulk
if (this.$npbDisabling) other.isDisabled = true;
// set its scanner blip to reflect its derelict status
other.scannerDisplayColor1 = "grayColor";
other.scannerDisplayColor2 = "whiteColor";
// amend display name to 'neutralized' + ship name
other.displayName = 'Neutralized '+other.name;
// custom property to flag victim's neutralized state
other.$neutralized = true;
// neutralization counts as a valid 'kill'
player.score ++;
// collect bounty
var bounty = other.bounty;
player.credits += bounty;
// nullify ex-ship's bounty
// keeping it a nominal 1 for missions.
other.bounty = 1; // was 0
// but remember previous bounty!
other.$oldBounty = bounty;
// Notify victim and SDD-Net
var name = other.name;
var ps = player.ship;
var pc = player.consoleMessage;
var oc = ps.messageGuiTextColor;
// set the 'police comms' colour - a light purple
ps.messageGuiTextColor = "0.7 0.3 0.8";
// communicate neutralization on subspace comm
pc(" ", 5); // line break
pc(name+" neutralized. Awaits tow.", 9);
pc("Bounty: "+bounty+" cr transferred via SDD-Net.", 9);
ps.messageGuiTextColor = oc;
if (this.$log) this._log(name+" neutralized. Bounty: "+bounty+".");
}
};
this.shipKilledOther = function(whom, damageType) {
// prevent double scoring on a ship already neutralized
if (whom.$neutralized) {
player.credits -= whom.$oldBounty;
player.score --;
player.consoleMessage("Well done. You killed a dead ship.", 9);
if (this.$log) this._log("Killed neutralized "+whom.name+". No extra score.");
}
}
// verify that all system NPC ships have an NPB script
this._npbCheckScripts = function() {
var count = 0;
var sas = system.allShips.length;
// Loop through all ships in the system
for (var i = 0; i < sas; i++) {
var ship = system.allShips[i];
// Skip if it's not a valid ship (e.g., debris, buoys)
if (!ship.isShip || !ship.isValid) continue;
// Check if it has the npc-npb.js script
if (ship.script && ship.script.name == "NPC NPB") count++;
}
this._log(count + " / " + sas + " entities are ships with NPC NPB script.");
};
// Reduce a ship to a desired level of performance
this._npbCripple = function(other, level) {
// reduce their speed, turn rate, acceleration
other.maxSpeed = other.maxSpeed * level;
other.maxThrust = other.maxThrust * level;
other.maxPitch = other.maxPitch * level;
other.maxRoll = other.maxRoll * level;
other.maxYaw = other.maxYaw * level;
// they are crippled
return true;
}
// remove any and all lasers from NPC ship
this._npbRemoveLasers = function(ship) {
var laserKeys = [
"EQ_WEAPON_PULSE_LASER",
"EQ_WEAPON_BEAM_LASER",
"EQ_WEAPON_MINING_LASER",
"EQ_WEAPON_MILITARY_LASER"
];
var lasersRemoved = false;
for (var i = 0; i < laserKeys.length; i++) {
var key = laserKeys[i];
if (ship.equipmentStatus(key) !== "EQUIPMENT_NONE") {
ship.removeEquipment(key);
lasersRemoved = true;
}
}
return lasersRemoved;
};
// remove any and all missiles and mines from a NPC ship
this._npbRemoveMissilesMines = function(ship) {
var missileMineKeys = [
"EQ_MISSILE", "EQ_HARDENED_MISSILE", "EQ_QC_MINE"
];
var itemsRemoved = false;
for (var i = 0; i < missileMineKeys.length; i++) {
var key = missileMineKeys[i];
// Check status once, then remove
if (ship.equipmentStatus(key) !== "EQUIPMENT_NONE") {
ship.removeEquipment(key);
itemsRemoved = true;
}
}
return itemsRemoved;
};
// Give victim a Pulse Laser as its only armament
this._npbGivePulse = function(ship) {
// already has a Pulse Laser?
if (ship.equipmentStatus("EQ_WEAPON_PULSE_LASER") !== "EQUIPMENT_NONE") {
return false;
}
// List of other lasers to remove
var otherWeapons = [
"EQ_WEAPON_BEAM_LASER",
"EQ_WEAPON_MINING_LASER",
"EQ_WEAPON_MILITARY_LASER"
];
// Remove any other lasers
for (var i = 0; i < otherWeapons.length; i++) {
if (ship.equipmentStatus(otherWeapons[i]) !== "EQUIPMENT_NONE") {
ship.removeEquipment(otherWeapons[i]);
}
}
// Award the Pulse Laser
ship.awardEquipment("EQ_WEAPON_PULSE_LASER");
return true;
};
this._npbShipHasEquipment = function(ship, equip) {
return (ship.equipmentStatus(equip) !== "EQUIPMENT_NONE");
};
this._npbExclaim = function(ship) {
var exclamations = [
"Our scanners are dead!",
"S.O.S. Electronics fried!",
"Weapons and engines failing!",
"Systems are down!",
"We're blind and powerless!",
"Critical systems overload!",
"All power failing!",
"Hull integrity failing!",
"Anyone... Notify Station!",
"S.O.S. Ship dying... need tow...",
"S.O.S. Uknown weapon hit us!",
"What the... what's happened to our ship?!",
"S.O.S. Our controls are dead...",
"Something's turning our ship into salvage!",
"S.O.S. We're helpless, drifting!",
"S.O.S. Our life-support failing!",
"S.O.S. Ship's computers offline!",
"Mercy! Drive inoperative, cannot flee!",
"Please! You're firing on a helpless vessel!",
"S.O.S! Our airlocks inoperative!",
"Hyperspace engine unresponsive!"
];
var randomIndex = Math.floor(Math.random() * exclamations.length);
ship.commsMessage(exclamations[randomIndex]);
};
this._npbAdvisory = function(ship) {
var n = ship.name;
var advisories = [
n+"'s voice comms unclear.",
n+"'s transponder ceased sending.",
n+"'s course erratic.",
n+"'s drive wake in massive decline.",
n+"'s power output falling.",
n+"'s scan indicator intermittent.",
n+"'s EM signature fluctuating."
];
var randomIndex = Math.floor(Math.random() * advisories.length);
var ps = player.ship;
var pc = player.consoleMessage;
var oc = ps.messageGuiTextColor;
// set the 'police comms' colour - a light purple
ps.messageGuiTextColor = "0.7 0.3 0.8";
// communicate advisories on subspace comm
pc(" ", 5); // line break
pc(advisories[randomIndex], 9);
ps.messageGuiTextColor = oc;
};
this._npbLogVictimData = function(vdata) {
this._log("Victim Data:");
this._log(" Unique ID: " + vdata.ID);
this._log(" spawnTime: " + vdata.spawnTime);
this._log(" position: " + vdata.position);
this._log(" dataKey: " + vdata.dataKey);
this._log(" speed: " + vdata.speed);
this._log(" bounty: " + vdata.bounty);
this._log(" scanClass: " + vdata.scanClass);
this._log(" name: " + vdata.name);
this._log(" displayName: " + vdata.displayName);
this._log(" primaryRole: " + vdata.primaryRole);
this._log(" scannerDisplayColor1: " + vdata.scannerDisplayColor1);
this._log(" scannerDisplayColor2: " + vdata.scannerDisplayColor2);
};
this.startUp = function() {
// maintain a list of neutralized ships
this.$npbNeutralizedShips = {};
// for ship-resurrection experiments
this.$npbCloning = false;
// Neutralizer code inside ship-scripts?
this.$npbNPCScript = false;
};
// echo to Oolite log for this script only
this._log = function(msg) {
log(this.name+".debug", msg);
}
|
| Scripts/npc-npb.js |
"use strict";
this.name = "NPC NPB";
this.author = "Reval";
this.license = "CC-BY-NC-SA 4.0";
this.version = "1.3";
this.description = "This is the replacement script for Oolite populator-spawned NPCs. Used to register damage taken, from whom and from which weapon.";
/*
Version 1.3
Fix: Removed redundant shipDied event.
*/
this.$log = true;
this.shipSpawned = function() {
// runs as soon as the ship is spawned (or should!)
if (this.$log) this._log(this.ship.name+" NPC spawned as "+this.ship.primaryRole+".");
}
this.startUp = function() {
// runs as soon as the script is assigned by world (or should!)
this._log(this.ship.name+" NPC signing on as "+this.ship.primaryRole+".");
}
// echo to Oolite log for this script only
this._log = function(msg) {
log(this.name+".debug", msg);
}
this.shipDied = function(whom, why) {
// prevent double scoring on a ship already neutralized
if (this.ship.$neutralized) {
player.credits -= this.ship.$oldBounty;
player.score --;
player.consoleMessage("Well done. You killed a dead ship.", 9);
if (this.$log) this._log("Killed neutralized "+this.ship.name+". No extra score.", 9);
}
};
// Get Neutralized! (or not)
this.shipBeingAttacked = function(whom) {
// optimize
var s = this.ship;
// Exit if no attacker
if (!whom) return;
if (s.isDisabled) return;
// Array of weapon facings to check
var facings = ["forwardWeapon", "portWeapon", "starboardWeapon", "aftWeapon"];
var neutralizerFound = false;
var wName = "";
// Check for NPB if the attacker is the player
if (whom == player.ship) {
var currentWeapon = player.ship.currentWeapon;
if (currentWeapon && currentWeapon.equipmentKey == "EQ_WEAPON_NEUTRALIZER") {
neutralizerFound = true;
wName = currentWeapon.name;
}
}
// Check for NPB if attacker is an NPC
else {
for (var i = 0; i < facings.length; i++) {
var weapon = whom[facings[i]];
if (weapon && weapon.equipmentKey == "EQ_WEAPON_NEUTRALIZER") {
neutralizerFound = true;
wName = weapon.name;
break;
}
}
}
// NPB Neutralizer used - proceed with Neutralizer logic
if (neutralizerFound) {
//this._log("Attacked by " + whom.name + " with " + wName);
// call functions and vars from world script (declare once)
var ws = ws || worldScripts["NPB Neutralizer"];
// first hit: optionally disable weapons, cripple speed and manoevrability
if ((ws.$npbCrippling) && (!s.$crippled)) {
// make my armament a Pulse Laser, simulating crippled weaponry
ws._npbGivePulse(s);
// remove my missiles and mines to simulate disabling
ws._npbRemoveMissilesMines(s);
// cripple my speed and manoevrability
ws.$npbCrippled = ws._npbCripple(s,0.25);
// disable hyperdrive
s.hyperspaceSpinTime = -1;
// disable fuel injector, if fitted, by removing it
if (ws._npbShipHasEquipment(s,"EQ_FUEL_INJECTION"))
s.removeEquipment("EQ_FUEL_INJECTION");
// make victim's scanner blip blink
var osdc = s.scannerDisplayColor1;
s.scannerDisplayColor1 = osdc;
s.scannerDisplayColor2 = "blackColor";
// amend display name to 'crippled' + ship name
s.ship.displayName = 'Crippled '+s.displayName;
// victim crew reaction to NPB effects
ws._npbExclaim(s);
// sensors advise on NPB effects
ws._npbAdvisory(s);
}
// Neutralize me if my energy is below 90%
if ((!this.ship.$neutralized) && (s.energy / s.maxEnergy < 0.9) {
// victim crew reaction to NPB
ws._npbExclaim(s);
// Apply disabling effects:
// engine disabled, so make me drift
s.maxSpeed = 0;
s.setAI("dumbAI.plist");
// remove my lasers to simulate weapons disabled
ws._npbRemoveLasers(s);
// remove my missiles and mines to simulate disabling
ws._npbRemoveMissilesMines(s);
// fully disable me if world script says so
if (ws && ws.$npbDisabling)
s.isDisabled = true;
// set my scanner blip colour to reflect derelict status
s.scannerDisplayColor1 = "grayColor";
s.scannerDisplayColor2 = "whiteColor";
player.score++;
// collect bounty on me
var bounty = s.bounty;
player.credits += bounty;
// nullify my existing bounty
// keeping it a nominal 1 for player missions.
s.bounty = 1; // was 0
// but remember previous bounty!
s.$oldBounty = bounty;
// Notify SDD-Net
var ps = player.ship;
var pc = player.consoleMessage;
var oc = ps.messageGuiTextColor;
ps.messageGuiTextColor = "0.7 0.3 0.8";
pc(s.name + " neutralized. Await tow.", 9);
pc("Bounty: "+bounty+" cr transferred via SDD-Net.", 9);
ps.messageGuiTextColor = oc;
}
} else {
// No Neutralizer - general catch-all case
var weaponInfo = EquipmentInfo.infoForKey(whom.forwardWeapon.equipmentKey);
if (weaponInfo) {
var wName = weaponInfo.name; // eg. "Military Laser"
if (this.$log) this._log(s.name+" <-* "+whom.displayName+" ("+wName+")");
}
}
}
// Test for docking
this.shipDockedWithStation = function(station) {
if (this.$log) this._log(this.ship.displayName + " docked with " + station.name);
};
|