| Scripts/ficc_equipment.js | "use strict";
this.name = "FuelInjectionCruiseControl";
this.author = "phkb";
this.copyright = "2018 phkb";
this.description = "Mode/Activation code for the FICC equipment";
this.licence = "CC BY-NC-SA 4.0";
this._bgs = false; // is BGS installed?
this._qcharger = false; // is the Q-Charger OXP installed?
this._surjectors = false; // is the Surjectors OXP installed?
this._engaged = false;
this._autoEngage = true;
this._disableAutoOnRedAlert = false;
this._disableAutoOnDocking = false;
this._passive = false;
this._trueValues = ["yes", "1", 1, "true", true];
/*
	TODO:
		Can we get the space dust effect to work the same as it does for normal injectors?
		Chase mode - match speed to target, or match speed when within 10km of target?
*/
// Library config
this._libraryConfig = {
	Name: this.name,
	Alias: expandDescription("[ficc_config_alias]"),
	Display: expandDescription("[ficc_config_display]"),
	Alive: "_libraryConfig",
	Notify: "$onChange",
	Bool: {
		B0: { Name: "_autoEngage", Def: true, Desc: expandDescription("[ficc_auto_engage]") },
		B1: { Name: "_disableAutoOnRedAlert", Def: false, Desc: expandDescription("[ficc_red_alert]") },
		B2: { Name: "_disableAutoOnDocking", Def: false, Desc: expandDescription("[ficc_dock_disable]") },
		B3: { Name: "_passive", Def: false, Desc: expandDescription("[ficc_passive]") },
		Info: expandDescription("[ficc_config_bool_info]")
	},
};
//-------------------------------------------------------------------------------------------------------------
this.$onChange = function () {
	var p = player.ship;
	var w = worldScripts.FuelInjectionCruiseControl;
	if (w._passive == true) {
		if (p.equipmentStatus("EQ_FUEL_INJECTION_CRUISECONTROL") == "EQUIPMENT_OK") {
			p.removeEquipment("EQ_FUEL_INJECTION_CRUISECONTROL");
			p.awardEquipment("EQ_FUEL_INJECTION_CRUISECONTROL_PASSIVE");
		}
		w._autoEngage = true; // must be on for passive mode
		return;
	}
	if (w._passive == true) {
		if (p.equipmentStatus("EQ_FUEL_INJECTION_CRUISECONTROL") == "EQUIPMENT_DAMAGED") {
			p.removeEquipment("EQ_FUEL_INJECTION_CRUISECONTROL");
			p.awardEquipment("EQ_FUEL_INJECTION_CRUISECONTROL_PASSIVE");
			p.setEquipmentStatus("EQ_FUEL_INJECTION_CRUISECONTROL_PASSIVE", "EQUIPMENT_DAMAGED");
		}
		w._autoEngage = true; // must be on for passive mode
		return;
	}
	if (w._passive == false && p.equipmentStatus("EQ_FUEL_INJECTION_CRUISECONTROL_PASSIVE") == "EQUIPMENT_OK") {
		p.removeEquipment("EQ_FUEL_INJECTION_CRUISECONTROL_PASSIVE");
		p.awardEquipment("EQ_FUEL_INJECTION_CRUISECONTROL");
		return;
	}
	if (w._passive == true && p.equipmentStatus("EQ_FUEL_INJECTION_CRUISECONTROL_PASSIVE") == "EQUIPMENT_DAMAGED") {
		p.removeEquipment("EQ_FUEL_INJECTION_CRUISECONTROL_PASSIVE");
		p.awardEquipment("EQ_FUEL_INJECTION_CRUISECONTROL");
		p.setEquipmentStatus("EQ_FUEL_INJECTION_CRUISECONTROL", "EQUIPMENT_DAMAGED");
		return;
	}
}
//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
	if (worldScripts.BGS) this._bgs = true;
	if (worldScripts["Q-Charger"]) this._qcharger = true;
	if (worldScripts.Surjectors) this._surjectors = true;
	if (missionVariables.FICC_AutoEngage) this._autoEngage = (this._trueValues.indexOf(missionVariables.FICC_AutoEngage) >= 0 ? true : false);
	// register our settings, if Lib_Config is present
	if (worldScripts.Lib_Config) {
		worldScripts.Lib_Config._registerSet(this._libraryConfig);
		// only restore settings if library config is present - otherwise, we'll keep the values set in the script (in case of manual override)
		if (missionVariables.FICC_AutoEngageRedAlertDisable) this._disableAutoOnRedAlert = (this._trueValues.indexOf(missionVariables.FICC_AutoEngageRedAlertDisable) >= 0 ? true : false);
		if (missionVariables.FICC_AutoEngageDockingDisable) this._disableAutoOnDocking = (this._trueValues.indexOf(missionVariables.FICC_AutoEngageDockingDisable) >= 0 ? true : false);
	}
	// make sure the passive flag is set correctly
	if (["EQUIPMENT_OK", "EQUIPMENT_DAMAGED"].indexOf(player.ship.equipmentStatus("EQ_FUEL_INJECTION_CRUISECONTROL_PASSIVE")) >= 0) {
		this._passive = true;
	}
}
//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
	missionVariables.FICC_AutoEngage = this._autoEngage;
	if (worldScripts.Lib_Config) {
		missionVariables.FICC_AutoEngageRedAlertDisable = this._disableAutoOnRedAlert;
		missionVariables.FICC_AutoEngageDockingDisable = this._disableAutoOnDocking;
	} else {
		delete missionVariables.FICC_AutoEngageRedAlertDisable;
		delete missionVariables.FICC_AutoEngageDockingDisable;
	}
}
//-------------------------------------------------------------------------------------------------------------
this.shipWillLaunchFromStation = function (station) {
	var p = player.ship, ps = p.script;
	ps._fuelDeduct = 0;
	ps._qcharger_toggled = false;
	ps._surjectorsApplied = false;
	ps._surjectors = (this._surjectors === true);
}
//-------------------------------------------------------------------------------------------------------------
this.shipLaunchedFromStation = this.shipExitedWitchspace = function () {
	var p = player.ship, ps = p.script;
	ps._qchargerCheck = (this._qcharger === true && p.equipmentStatus("EQ_Q-CHARGER") === "EQUIPMENT_OK" && ((p.equipmentStatus("EQ_ENERGY_UNIT") === "EQUIPMENT_OK") || (p.equipmentStatus("EQ_NAVAL_ENERGY_UNIT") === "EQUIPMENT_OK")));
	ps._ficc_ok = (p.hasEquipmentProviding("EQ_FUEL_INJECTION") === true && p.hasEquipmentProviding("EQ_FICC") == true);
	if (this._autoEngage === true) {
		this.$startCheckSpeedFCB(true);
	}
}
//-------------------------------------------------------------------------------------------------------------
this.shipDied = this.shipWillDockWithStation = this.shipWillEnterWitchspace = function () {
	//if (isValidFrameCallback(this._cbID)) removeFrameCallback(this._cbID);
	this.$stopFCB();
	// turn the speed check fcb off as well, although if this is the shipWillEnterWitchspace event, it will be turned back on again when we exit witchspace
	if (isValidFrameCallback(this._speedFCB)) removeFrameCallback(this._speedFCB);
}
//-------------------------------------------------------------------------------------------------------------
this.playerRequestedDockingClearance = function (message) {
	var ps = player.ship.script;
	if (message === "DOCKING_CLEARANCE_GRANTED" || message === "DOCKING_CLEARANCE_EXTENDED" || message === "DOCKING_CLEARANCE_DENIED_TRAFFIC_INBOUND" || message === "DOCKING_CLEARANCE_DENIED_TRAFFIC_OUTBOUND") {
		ps._ficc_playerIsDocking = true;
	} else {
		ps._ficc_playerIsDocking = false;
	}
}
//-------------------------------------------------------------------------------------------------------------
// for oolite 1.85++
this.playerDockingClearanceCancelled = function () {
	var ps = player.ship.script;
	ps._ficc_playerIsDocking = false;
}
//-------------------------------------------------------------------------------------------------------------
// for oolite 1.83++
this.playerDockingClearanceExpired = function () {
	this.playerDockingClearanceCancelled();
}
//-------------------------------------------------------------------------------------------------------------
this.equipmentAdded = function (equipmentKey) {
	// make sure we don't end up with a mismatch on the passive flag
	// if the player has previously set passive mode via library config, maintain that config with the right equipment
	if (equipmentKey == "EQ_FUEL_INJECTION_CRUISECONTROL" && this._passive == true) {
		var p = player.ship;
		p.removeEquipment(equipmentKey);
		p.awardEquipment("EQ_FUEL_INJECTION_CRUISECONTROL_PASSIVE");
	}
}
//-------------------------------------------------------------------------------------------------------------
this.activated = function () {
	var ficc = worldScripts.FuelInjectionCruiseControl;
	// if it's already on, turn it off
	if (isValidFrameCallback(ficc._cbID)) {
		player.consoleMessage(expandDescription("[ficc_disengaged]"));
		ficc.$playSound("engineDown");
		ficc.$stopFCB();
		return;
	}
	var p = player.ship;
	if (p.torusEngaged) {
		player.consoleMessage(expandDescription("[ficc_torus]"));
		return;
	}
	// no injectors
	if (p.hasEquipmentProviding("EQ_FUEL_INJECTION") === false) {
		player.consoleMessage(expandDescription("[ficc_unavailable]"));
		return;
	}
	// no fuel
	if (p.fuel <= 0) {
		player.consoleMessage(expandDescription("[fuel-out]"));
		return;
	}
	// not at max speed
	if (p.speed < p.maxSpeed) {
		player.consoleMessage(expandDescription("[ficc_accelerate]"));
		return;
	}
	// qcharger only operates if fuel is >= 0.1 and there is a working energy unit (normal or naval)
	/*if (p.equipmentStatus("EQ_Q-CHARGER") === "EQUIPMENT_OK" &&
		(worldScripts["Q-Charger"].qchargercondition === "ON" || (p.fuel >= 0.1 && (p.equipmentStatus("EQ_ENERGY_UNIT") == "EQUIPMENT_DAMAGED" || p.equipmentStatus("EQ_NAVAL_ENERGY_UNIT") === "EQUIPMENT_DAMAGED")))) {
		player.consoleMessage(expandDescription("[ficc_qcharger]"));
		return;
	}*/
	player.consoleMessage(expandDescription("[ficc_engaged]"));
	ficc.$startFCB();
}
//-------------------------------------------------------------------------------------------------------------
this.mode = function () {
	var ficc = worldScripts.FuelInjectionCruiseControl;
	if (isValidFrameCallback(ficc._speedFCB)) {
		ficc._autoEngage = false;
		removeFrameCallback(ficc._speedFCB);
		player.consoleMessage(expandDescription("[ficc_autoEngageOff]"));
		if (ficc._engaged) this.activated();
	} else {
		// start a fcb to monitor speed and auto-engage CC when max speed reached
		ficc._autoEngage = true;
		ficc.$startCheckSpeedFCB(false)
	}
}
//-------------------------------------------------------------------------------------------------------------
this.equipmentDamaged = function (equipmentKey) {
	var p = player.ship, ps = p.script;
	if (!ps) return; // script is undefined when player ship is destroyed
	if (p.hasEquipmentProviding("EQ_FICC") == false || p.hasEquipmentProviding("EQ_FUEL_INJECTION") === false) {
		ps._ficc_ok = false;
		if (isValidFrameCallback(this._cbID)) {
			player.consoleMessage(expandDescription("[ficc_disengaged]"));
			this.$playSound("engineDown");
			this.$stopFCB();
		}
		if (isValidFrameCallback(this._speedFCB)) removeFrameCallback(this._speedFCB);
	}
	// monitor when the qcharger state changes (needs either EQ_ENERGY_UNIT or EQ_NAVAL_ENERGY_UNIT in addition to its own equipment)
	if (this._qcharger === true && ps._qchargerCheck === true &&
		(p.equipmentStatus("EQ_Q-CHARGER") !== "EQUIPMENT_OK" || (p.equipmentStatus("EQ_ENERGY_UNIT") !== "EQUIPMENT_OK" && p.equipmentStatus("EQ_NAVAL_ENERGY_UNIT") !== "EQUIPMENT_OK"))) {
		ps._qchargerCheck = false;
	}
}
//-------------------------------------------------------------------------------------------------------------
this.equipmentRepaired = function (equipmentKey) { // if in-flight repairs restored functionality
	if (player.ship.status === "STATUS_IN_FLIGHT") this.shipLaunchedFromStation(); // check FICC and Q-Charger equipment and re-activate FICC auto-engage monitoring FCB if configured to do so
}
//-------------------------------------------------------------------------------------------------------------
this.$startFCB = function $startFCB() {
	var p = player.ship;
	p.script._soundCounter = 0;
	p.script._accel = (p.speed / (p.maxSpeed * p.injectorSpeedFactor));
	if (p.script._accel > 1) p.script._accel = 1; // in case we're slowing from torus speed
	// turn on some flags so other OXP's have an easier way of determining that the ficc is engaged
	this._engaged = true;
	p.script._ficcEngaged = true;
	// only play the engine up sound if we are actually speeding up
	if (p.speed < (p.maxSpeed * p.injectorSpeedFactor)) this.$playSound("engineUp");
	this.$playSound("afterburner");
	// set up the frame callback
	this._cbID = addFrameCallback(function _fcbID(delta) {
		var p = (_fcbID.p = _fcbID.p || player.ship), ps = (_fcbID.ps = _fcbID.ps || p.script); // cache the player.ship and player.ship.script references
		var ficc = (_fcbID.ficc = _fcbID.ficc || worldScripts.FuelInjectionCruiseControl); // cache the worldScript reference
		var wsq = (_fcbID.wsq = _fcbID.wsq || worldScripts["Q-Charger"]); // cache the worldScript reference
		if (!p || !p.isValid || !ps._ficc_ok) {
			ficc.$stopFCB();
			return;
		}
		if (p.torusEngaged || p.injectorsEngaged) {
			player.consoleMessage(expandDescription("[ficc_disengaged]"));
			ficc.$stopFCB();
			return;
		}
		if (p.speed < p.maxSpeed) {
			player.consoleMessage(expandDescription("[ficc_disengaged]"));
			ficc.$playSound("engineDown");
			ficc.$stopFCB();
			return;
		}
		if (p.fuel <= 0) {
			player.consoleMessage(expandDescription("[fuel-out]"));
			ficc.$playSound("engineDown");
			ficc.$stopFCB();
			return;
		}
		if (ps._qchargerCheck === true && wsq) { // this is a do-once
			if (wsq.qchargerenabled) {
				// FICC takes over from Q-Charger at 100% speed (Q-Charger handles 95-99% speed)
				ps._qcharger_toggled = true;
				wsq.qchargerCheckTimer.stop(); // stop Q-Charger's timer while FICC is running (expectation, currently true: if the player toggles Q-Charger manually, it doesn't restart the timer)
				//ficc.$playSound("engineDown");
				//ficc.$stopFCB();
				//return;
			}
			ps._qchargerCheck = false; // don't need to check Q-Charger again while FICC is running
		}
		ps._soundCounter += delta;
		if (ps._soundCounter > 1) {
			ps._soundCounter = 0;
			ficc.$playSound("afterburner");
		}
		// note: this method will not change the actual speed of the ship, 
		// so I've included the requirement that the ship must be at full speed before engaging
		// velocity adjustment method
		p.velocity = p.vectorForward.multiply(parseFloat(p.maxSpeed * p.injectorSpeedFactor) * ps._accel);
		if (ps._accel < 1) ps._accel += 0.01 + (p.maxThrust / 15000);
		else if (ps._accel > 1) ps._accel = 1;
		// deduct fuel
		var br = p.injectorBurnRate;
		// surjectors changes the way injectors work at the base level, rather than at an equipment level
		// so, if the OXP is installed, and weapons are offline, the code kicks in
		// because we are simulating injectors here, not actually doing injectors, we need to implement the surjector code
		if (ps._surjectors && !p.weaponsOnline) {
			ps._surjectorsApplied = true;
			br = 0.01;
			p.aftShield -= 0.1;
			p.forwardShield -= 0.1;
			p.aftShieldRechargeRate = 0;
			p.forwardShieldRechargeRate = 0;
			p.energyRechargeRate = 0;
		}
		ps._fuelDeduct += ((br / 10) * delta);
		if (ps._fuelDeduct > 0.1) {
			p.fuel -= 0.1;
			ps._fuelDeduct -= 0.1;
			if (p.fuel < 0) p.fuel = 0;
		}
	});
}
//-------------------------------------------------------------------------------------------------------------
this.$stopFCB = function $stopFCB() {
	if (isValidFrameCallback(this._cbID)) {
		removeFrameCallback(this._cbID);
		var p = player.ship, ps = p.script;
		ps._ficcEngaged = false;
		this._engaged = false;
		// restore surjector shield/energy recharge rates	
		if (ps._surjectorsApplied) {
			var s = worldScripts.Surjectors;
			if (!p.injectorsEngaged) { // don't reset anything if the injectors are engaged, otherwise we might override surjectors
				p.aftShieldRechargeRate = s.$savedAftRate;
				p.forwardShieldRechargeRate = s.$savedFwdRate;
				p.energyRechargeRate = s.$savedERate;
			}
			ps._surjectorsApplied = false;
		}
		// reset velocity
		// we have to set this back to the current speed value, otherwise we'll get the "sliding" issue
		p.velocity = p.vectorForward.multiply(p.speed);
		// resume Q-Charger if it was active before FICC took over
		if (ps._qcharger_toggled === true) {
			ps._qcharger_toggled = false;
			worldScripts["Q-Charger"].qchargerCheckTimer.start();
		}
		ps._qchargerCheck = true; // need to check Q-Charger again next time FICC starts		
	}
}
//-------------------------------------------------------------------------------------------------------------
this.$startCheckSpeedFCB = function $startCheckSpeedFCB(silent) {
	var p = player.ship, ps = p.script;
	if (ps._ficc_ok) {
		if (silent === false) player.consoleMessage(expandDescription("[ficc_autoEngageOn]"));
		if (!isValidFrameCallback(this._speedFCB)) {
			this._speedFCB = addFrameCallback(function _checkSpeedFCB(delta) {
				var p = (_checkSpeedFCB.p = _checkSpeedFCB.p || player.ship); // cache the player.ship reference
				if (p && p.isInSpace && p.isValid) { // removed && p.script._qchargerCheck === false
					var ficc = (_checkSpeedFCB.ficc = _checkSpeedFCB.ficc || worldScripts.FuelInjectionCruiseControl); // cache the worldScript reference
					if (p.speed == p.maxSpeed && p.fuel > 0 && p.torusEngaged === false && p.injectorsEngaged === false && !isValidFrameCallback(ficc._cbID)) {
						var _disableAutoOnRedAlert = (_checkSpeedFCB._disableAutoOnRedAlert = _checkSpeedFCB._disableAutoOnRedAlert || ficc._disableAutoOnRedAlert); // cache the config value
						var _disableAutoOnDocking = (_checkSpeedFCB._disableAutoOnDocking = _checkSpeedFCB._disableAutoOnDocking || ficc._disableAutoOnDocking); // cache the config value
						if (_disableAutoOnRedAlert && p.alertCondition === 3) return;
						if (_disableAutoOnDocking && p.script._ficc_playerIsDocking) return;
						ficc.activated();
					}
				}
			});
		}
	}
}
//-------------------------------------------------------------------------------------------------------------
// player the buy/sell sound effects
this.$playSound = function $playSound(soundtype) {
	var mySound;
	switch (soundtype) {
		case "afterburner":
			mySound = new SoundSource;
			mySound.sound = "afterburner1.ogg";
			break;
		case "engineUp":
			if (this._bgs) {
				mySound = new SoundSource;
				mySound.sound = worldScripts.BGS.$BGS.def["engineUp"];
			} else return;
			break;
		case "engineDown":
			if (this._bgs) {
				mySound = new SoundSource;
				mySound.sound = worldScripts.BGS.$BGS.def["engineDown"];
			} else return;
			break;
	}
	mySound.loop = false;
	mySound.play();
} |