Scripts/mwa_main.js |
"use strict";
this.name = "ManualWitchspaceAlignment";
this.author = "phkb";
this.copyright = "2017 phkb";
this.description = "Reimplements the DHI AHC but with a manual alignment procedure";
this.licence = "CC BY-NC-SA 3.0";
/*
TODO:
work out better occlusion calc for stations
*/
this._markers = []; // array of markers representing jump points to all local systems
this._running = false; // used to work out when the spawn routine is running
this._alignAccuracy = 0.18; // used to control how accurately the player needs to align their ship to the nav beacon
// higher number means less accuracy
this._heldTarget = null;
this._colorList = ["blue", "amber", "green", "pink", "purple", "white"];
this._color = 0;
this._userOverride = false;
this._nova = false;
this._cancelled = false;
this._fcb;
this._libSettings = {
Name: this.name,
Display: "UI Settings",
Alias: "Manual Witchspace Alignment",
Alive: "_libSettings",
Bool: {
B0: {
Name: "_userOverride",
Def: false,
Desc: "User Override"
},
Info: "Setting User override to true will prevent other OXP's from changing the UI color."
},
SInt: {
S0: {
Name: "_color",
Def: 0,
Min: 0,
Max: 5,
Desc: "Nav Frame Color"
},
Info: "0 = Blue, 1 = Amber/Orange, 2 = Green, 3 = Pink, 4 = Purple, 5 = White"
},
};
this._trueValues = ["yes", "1", 1, "true", true];
//-------------------------------------------------------------------------------------------------------------
this.startUp = function () {
if (worldScripts.Lib_Config) {
if (missionVariables.MWA_UserOverride) this._userOverride = (this._trueValues.indexOf(missionVariables.MWA_UserOverride) >= 0 ? true : false);
if (missionVariables.MWA_Color) this._color = parseInt(missionVariables.MWA_Color);
}
// make sure we don't get in the way of the ANC
if (worldScripts.Deep_Horizon_Adv_Nav_Comp) {
// turn off ws events in ANC and control them from here
var anc = worldScripts.Deep_Horizon_Adv_Nav_Comp;
anc.shipWillEnterWitchspace_hold = anc.shipWillEnterWitchspace;
delete anc.shipWillEnterWitchspace;
anc.shipWillExitWitchspace_hold = anc.shipWillExitWitchspace;
delete anc.shipWillExitWitchspace;
anc.playerStartedJumpCountdown_hold = anc.playerStartedJumpCountdown;
delete anc.playerStartedJumpCountdown;
}
if (worldScripts.BGS) {
player.ship.script._BGS = true;
// we're taking charge of BGS's playerStartedJumpCountdown routine so we can make sure
// it's only called when we actually start the countdown (ie when aligned);
worldScripts.BGS.$mwa_playerStartedJumpCountdown = worldScripts.BGS.playerStartedJumpCountdown;
delete worldScripts.BGS.playerStartedJumpCountdown;
}
}
//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
if (worldScripts.Lib_Config) worldScripts.Lib_Config._registerSet(this._libSettings);
this.$systemSetup();
// bug fix to prevent countdown to zero from having its timer garbage collected if the jump starts/stops frequently
if (worldScripts.countdown_to_zero) {
worldScripts.countdown_to_zero.playerStartedJumpCountdown = function(type, seconds) {
if (this.$hyperTimer && this.$hyperTimer.isRunning === true) {
this.$hyperTimer.stop();
}
if (type === "standard") {
missionVariables["countdown_to_zero"] = seconds;
this.$hyperTimer = new Timer(this,this._hyperDriveCountdown,0,1);
}
else if (type === "galactic") {
missionVariables["countdown_to_zero"] = seconds - 1;
this.$hyperTimer = new Timer(this,this._hyperDriveCountdown,1,1);
}
}
}
}
//-------------------------------------------------------------------------------------------------------------
this.shipWillDockWithStation = function (station) {
this.$stopNavFrameCallback(true);
}
//-------------------------------------------------------------------------------------------------------------
this.shipWillLaunchFromStation = function () {
this._running = false;
var ps = player.ship.script;
ps.$checkForAlignment = this.$checkForAlignment;
ps.$shipIsAligned = this.$shipIsAligned;
ps.$jumpIsOccluded = this.$jumpIsOccluded;
ps.$entityType = this.$entityType;
ps.$reorientVEToPlayer = this.$reorientVEToPlayer;
ps.$vectoredPositionToTarget = this.$vectoredPositionToTarget;
ps.$lookAtRotate = this.$lookAtRotate;
ps.$orthoNormalise = this.$orthoNormalise;
ps.$lookAtRotateEuler = this.$lookAtRotateEuler;
ps.$displayBasicPointerMessage = this.$displayBasicPointerMessage;
ps.$positionNavVisualEffects = this.$positionNavVisualEffects;
ps.$performCancel = this.$performCancel;
//ps._useCheckCourseFunction = player.ship.hasOwnProperty("checkCourseToPosition") === true ? true : false; //oolite.compareVersion("1.87") <= 0 ? true : false;
ps._checkingAlignment = false;
ps._jumpStarted = false;
ps._BGSStarted = false;
ps._jumpMarker = null;
ps._alignCount = 0;
ps._occludedCount = 0;
ps._alignWarning = false;
ps._occludedWarning = false;
ps._override = false;
ps._navFrameVE = null;
ps._navStarVE = null;
ps._basicCompassFrameCount = 0;
ps._alignAccuracy = this._alignAccuracy;
this._cancelled = false;
var anc = worldScripts.Deep_Horizon_Adv_Nav_Comp;
ps._usingANC = false;
if (anc) {
if (player.ship.equipmentStatus("EQ_ADV_NAV_COMP") === "EQUIPMENT_OK") {
// turn on anc, turn off mwa
anc.shipWillEnterWitchspace = anc.shipWillEnterWitchspace_hold;
anc.shipWillExitWitchspace = anc.shipWillExitWitchspace_hold;
anc.playerStartedJumpCountdown = anc.playerStartedJumpCountdown_hold;
ps._usingANC = true;
} else {
delete anc.shipWillEnterWitchspace;
delete anc.shipWillExitWitchspace;
delete anc.playerStartedJumpCountdown;
}
}
}
//-------------------------------------------------------------------------------------------------------------
this.shipDied = function(whom, why) {
this.$alternateStopNavFrameCallback(false);
}
//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
missionVariables.MWA_UserOverride = this._userOverride;
missionVariables.MWA_Color = this._color;
}
//-------------------------------------------------------------------------------------------------------------
this.guiScreenChanged = function (to, from) {
var p = player.ship;
if (p.isInSpace === false) return;
if ((to === "GUI_SCREEN_LONG_RANGE_CHART" || to === "GUI_SCREEN_SHORT_RANGE_CHART") && this._heldTarget == null) this._heldTarget = p.nextSystem;
if ((from === "GUI_SCREEN_LONG_RANGE_CHART" || from === "GUI_SCREEN_SHORT_RANGE_CHART") &&
(to != "GUI_SCREEN_LONG_RANGE_CHART" && to != "GUI_SCREEN_SHORT_RANGE_CHART") && this._heldTarget != p.nextSystem) {
// player changed destination system
if (p.script._navFrameCallbackID && isValidFrameCallback(p.script._navFrameCallbackID)) {
player.consoleMessage("Hyperspace destination changed - jump cancelled", 5);
p.cancelHyperspaceCountdown();
this.$stopNavFrameCallback(false);
this._cancelled = false;
}
}
}
//-------------------------------------------------------------------------------------------------------------
this.shipLaunchedEscapePod = function(escapePod) {
this.$alternateStopNavFrameCallback(false);
}
//-------------------------------------------------------------------------------------------------------------
this.shipExitedWitchspace = function () {
this.$systemSetup();
player.ship.script._checkingAlignment = false;
if (this._nova === true) {
player.ship.takeInternalDamage();
this._nova = false;
}
}
//-------------------------------------------------------------------------------------------------------------
this.shipWillEnterWitchspace = function (cause, destination) {
this._heldTarget = null;
delete this.compassTargetChanged;
delete this.playerStartedAutoPilot;
//delete this.playerCancelledJumpCountdown;
this._cancelled = false;
this.$stopNavFrameCallback(false);
}
//-------------------------------------------------------------------------------------------------------------
this.playerStartedJumpCountdown = function (type, seconds) {
if (this._cancelled == true) {
// jump was cancelled during a playerStartedJumpCountdown worldscript
this._cancelled = false;
return;
}
var ps = player.ship.script;
// check for galactic jump or if the anc is in play, or the system is going nova - don't do anything for these
if (type === "galactic" || ps._usingANC === true || (system.sun && system.sun.hasGoneNova)) {
if (ps._BGS) worldScripts.BGS.$mwa_playerStartedJumpCountdown(type, seconds);
if (system.sun && system.sun.hasGoneNova) {
this._nova = true;
player.consoleMessage("System going nova - Emergency alignment engaged");
}
return;
}
if (!ps._navFrameCallbackID) {
ps._basicCompass = false;
if (system.isInterstellarSpace === true || player.ship.hasEquipmentProviding("EQ_ADVANCED_COMPASS") === false) {
ps._basicCompass = true;
}
this.$spawnJBNavVEs();
} else if (ps._override === false) {
player.ship.cancelHyperspaceCountdown();
//this.$stopNavFrameCallback(true);
player.consoleMessage(expandDescription("[witch-user-abort]"));
}
}
//-------------------------------------------------------------------------------------------------------------
this.playerStartedAutoPilot_hide = function () {
player.consoleMessage(expandDescription("[witch-user-abort]"));
player.ship.cancelHyperspaceCountdown();
this.$stopNavFrameCallback(true);
this._cancelled = false;
}
//-------------------------------------------------------------------------------------------------------------
this.compassTargetChanged_hide = function (whom, mode) {
player.consoleMessage(expandDescription("[witch-user-abort]"));
delete player.ship.script._oldTarget;
this.$stopNavFrameCallback(true);
player.ship.cancelHyperspaceCountdown();
this._cancelled = false;
}
//-------------------------------------------------------------------------------------------------------------
this.playerCancelledJumpCountdown = function () {
var ps = player.ship.script;
// set cancelled = true if we got here before the playerStartedJumpCountdown function was executed
// usually because some other OXP cancelled the jump in it's own playerStartedJumpCountdown function.
if (ps._jumpMarker == null) this._cancelled = true;
ps._BGSStarted = false;
if (ps._override === true) return;
if (ps._usingANC === true) return;
this.$stopNavFrameCallback(true);
}
//-------------------------------------------------------------------------------------------------------------
this.playerJumpFailed = function (reason) {
this.$stopNavFrameCallback(true);
}
//-------------------------------------------------------------------------------------------------------------
this.$spawnJBNavVEs = function $spawnJBNavVEs() {
if (this._running === true) return;
this._running = true;
var p = player.ship;
var ps = p.script;
// Select marker that matches the destination system.
var playerTarget = p.nextSystem;
for (var i = 0; i < this._markers.length; i++) {
if (playerTarget === this._markers[i].systemID) {
ps._jumpMarker = this._markers[i];
break;
}
}
if (ps._jumpMarker == null) {
log(this.name, "!!ERROR: marker not found for " + System.systemNameForID(playerTarget) + "!");
this._running = false;
return;
}
player.consoleMessage(System.systemNameForID(playerTarget) + " system beacon acquired.");
ps._jumpMarker.beaconCode = "witch-destination-icon";
ps._jumpMarker.beaconLabel = "Hyperspace destination: " + ps._jumpMarker.name;
ps._jumpStarted = true;
// start the framecount at its recycle point so an alignment check will happen next frame
ps._frameCount = 0.4;
// Spawn Visual Effect and store reference to it
if (this._color > 5 || this._color < 0) this._color = 0; // default to blue if incorrectly set
ps._navFrameVE = system.addVisualEffect("jumpbeacon_navframe_" + this._colorList[this._color], ps.$vectoredPositionToTarget(ps._jumpMarker.position, p.collisionRadius + 5000));
ps._navStarVE = system.addVisualEffect("jumpbeacon_navstar", ps.$vectoredPositionToTarget(ps._jumpMarker.position, p.collisionRadius + 50000));
// Orient the VE toward the player ship
ps._navFrameVE.scale(0.66);
ps._navStarVE.scale(3.30 - ps._jumpMarker.distanceToSystem * 0.27);
var tex = ps._jumpMarker.systemID % 4;
switch (tex) {
case 1:
ps._navStarVE.setMaterials({
"jumpbeacon_navstar.png": {
"textures": ["jumpbeacon_navstar2.png"],
"fragment_shader": "jumpbeacon_jumpstar.fragment",
"emission_map": "jumpbeacon_navstar2.png",
"uniforms": {
"uColorMap": {
"type": "texture",
"value": "0"
},
"uSpecIntensity": "shaderFloat1",
"uSpecColor": "shaderVector1"
},
"vertex_shader": "jumpbeacon_jumpstar.vertex"
}
});
break;
case 2:
ps._navStarVE.setMaterials({
"jumpbeacon_navstar.png": {
"textures": ["jumpbeacon_navstar3.png"],
"fragment_shader": "jumpbeacon_jumpstar.fragment",
"emission_map": "jumpbeacon_navstar3.png",
"uniforms": {
"uColorMap": {
"type": "texture",
"value": "0"
},
"uSpecIntensity": "shaderFloat1",
"uSpecColor": "shaderVector1"
},
"vertex_shader": "jumpbeacon_jumpstar.vertex"
}
});
break;
case 3:
ps._navStarVE.setMaterials({
"jumpbeacon_navstar.png": {
"textures": ["jumpbeacon_navstar4.png"],
"fragment_shader": "jumpbeacon_jumpstar.fragment",
"emission_map": "jumpbeacon_navstar4.png",
"uniforms": {
"uColorMap": {
"type": "texture",
"value": "0"
},
"uSpecIntensity": "shaderFloat1",
"uSpecColor": "shaderVector1"
},
"vertex_shader": "jumpbeacon_jumpstar.vertex"
}
});
break;
}
ps._navFrameCallbackID = addFrameCallback(ps.$positionNavVisualEffects.bind(ps));
worldScripts.ManualWitchspaceAlignment._fcb = ps._navFrameCallbackID;
this._running = false;
}
//-------------------------------------------------------------------------------------------------------------
this.$stopNavFrameCallback = function $stopNavFrameCallback(cancelled) {
var p = player.ship;
var ps = p.script;
if (ps._navFrameCallbackID && isValidFrameCallback(ps._navFrameCallbackID)) {
removeFrameCallback(ps._navFrameCallbackID);
}
delete ps._navFrameCallbackID;
delete this.compassTargetChanged;
delete this.playerStartedAutoPilot;
//delete this.playerCancelledJumpCountdown;
if (ps._jumpMarker) {
ps._jumpMarker.beaconCode = "";
ps._jumpMarker.beaconLabel = "";
ps._jumpMarker = null;
if (ps._oldTarget && ps._oldTarget.isValid) {
p.compassTarget = ps._oldTarget;
}
delete ps._oldTarget;
}
if (ps._navFrameVE != null) {
ps._navFrameVE.remove();
ps._navFrameVE = null;
}
if (ps._navStarVE != null) {
ps._navStarVE.remove();
ps._navStarVE = null;
}
ps._jumpStarted = false;
ps._occludedCount = 0;
ps._occludedWarning = false;
ps._alignWarning = false;
ps._alignCount = 0;
ps._override = false;
ps._lastHeading = null;
if (ps._BGS && ps._usingANC === false && cancelled === true) {
ps._BGSStarted = false;
worldScripts.BGS.playerCancelledJumpCountdown();
worldScripts.BGS._clrTimer(1);
}
this._running = false;
}
//-------------------------------------------------------------------------------------------------------------
this.$alternateStopNavFrameCallback = function $alternateStopNavFrameCallback() {
if (this.fcb && isValidFrameCallback(this._fcb)) {
removeFrameCallback(this._fcb);
}
var ve = system.allVisualEffects;
for (var i = ve.count; i >= 0; i--) {
if (ve[i].dataKey.indexOf("jumpbeacon") >= 0) {
ve[i].remove();
}
}
}
//-------------------------------------------------------------------------------------------------------------
this.$systemSetup = function $systemSetup() {
this._markers.length = 0;
// Determine how many farpoint markers are needed
this._localSystems = System.infoForSystem(galaxyNumber, system.ID).systemsInRange(7);
this._spawnCount = this._localSystems.length;
// For each in-range system spawn a farpoint marker
this._markers = system.addShips("jump_marker", this._spawnCount, player.ship.position, 5000000);
//Assign a matching system.ID as an additional value to each buoy
for (var i = 0; i < this._markers.length; i++) {
var mkr = this._markers[i];
mkr.systemID = this._localSystems[i].systemID;
mkr.galCoordinates = this._localSystems[i].coordinates;
mkr.name = this._localSystems[i].name;
mkr.distanceToSystem = System.infoForSystem(galaxyNumber, system.ID).distanceToSystem(this._localSystems[i]);
mkr.uSpecColor = this.$selectColor(this._localSystems[i].systemID, i);
mkr.uSpecIntensity = 0.5;
mkr.position = this.$positionJumpBeaconMarker(mkr, 100000000);
}
}
//-------------------------------------------------------------------------------------------------------------
this.$positionJumpBeaconMarker = function $positionJumpBeaconMarker(marker, distance) {
if (marker.galCoordinates == null) return null;
var t = marker.galCoordinates;
var s = System.infoForSystem(galaxyNumber, system.ID).coordinates;
var tV = t.subtract(s);
var u = system.scrambledPseudoRandomNumber(34567346);
var v = system.scrambledPseudoRandomNumber(431976567);
var w = system.scrambledPseudoRandomNumber(9834674);
var theta = Math.acos(2 * u - 1);
var phi = 2 * Math.PI * v;
var psi = 2 * Math.PI * w;
var v1 = new Vector3D(Math.sin(theta) * Math.sin(phi), Math.sin(theta) * Math.cos(phi), Math.cos(theta));
var v2 = new Vector3D(0, 0, 1); //straight up
var v3 = v2.cross(v1);
v3 = v3.direction(); // normalize (should be normalized anyway)
var q1 = new Quaternion(1, 0, 0, 0);
q1 = q1.rotate(v3, theta).normalize();
var v4 = tV.rotateBy(q1).direction(); // rotate target vector tV so that the coordinate space has pole moved from straight up to v1 (theta, phi) (spherical coords)
var q2 = new Quaternion(1, 0, 0, 0);
q2 = q2.rotate(v1, psi).normalize();
v4 = v4.rotateBy(q2).direction(); // rotate about the new pole v1 by psi
return v4.multiply(distance).add(player.ship.position);
}
//-------------------------------------------------------------------------------------------------------------
this.$selectColor = function $selectColor(sysID, i) {
var color = System.infoForSystem(galaxyNumber, sysID).sun_color;
if (typeof (color) === 'undefined') {
var c = Math.floor(system.scrambledPseudoRandomNumber(785331 - i) * 7);
switch (c) {
case 0:
color = "magentaColor";
break;
case 1:
color = "redColor";
break;
case 2:
color = "orangeColor";
break;
case 3:
color = "yellowColor";
break;
case 4:
color = "whiteColor";
break;
case 5:
color = "cyanColor";
break;
default:
color = "blueColor";
break;
}
}
switch (color) {
case "magentaColor":
var colorVec = [1, 0, 1];
break;
case "redColor":
var colorVec = [1, 0, 0];
break;
case "orangeColor":
var colorVec = [1, 0.5, 0];
break;
case "yellowColor":
var colorVec = [1, 1, 0];
break;
case "whiteColor":
var colorVec = [1, 1, 1];
break;
case "cyanColor":
var colorVec = [0, 1, 1];
break;
case "blueColor":
var colorVec = [0, 0, 1];
break;
default:
var colorVec = [1, 1, 1];
}
return colorVec;
}
//-------------------------------------------------------------------------------------------------------------
// all the following functions are attached to player.ship.script to improve performance
//-------------------------------------------------------------------------------------------------------------
// main frame callback routine
this.$positionNavVisualEffects = function $positionNavVisualEffects(delta) {
if (delta === 0) return;
var p = this.ship;
// make sure we have a valid ship to work with
if (!p || !p.position || !p.vectorForward) {
worldScripts.ManualWitchspaceAlignment.$alternateStopNavFrameCallback(false);
return;
}
var ps = this;
// make sure we have valid entities to work with, otherwise cancel the fcb
if (ps._navFrameVE.isValid === false || ps._navStarVE.isValid === false) {
if (ps._navFrameCallbackID && isValidFrameCallback(ps._navFrameCallbackID)) {
removeFrameCallback(ps._navFrameCallbackID);
}
delete ps._navFrameCallbackID;
return;
}
ps.$reorientVEToPlayer(ps._navFrameVE);
ps.$reorientVEToPlayer(ps._navStarVE);
ps._navFrameVE.position = ps.$vectoredPositionToTarget(ps._jumpMarker.position, p.collisionRadius + 5000);
ps._navStarVE.position = ps.$vectoredPositionToTarget(ps._jumpMarker.position, p.collisionRadius + 10000);
ps._navStarVE.shaderVector1 = ps._jumpMarker.uSpecColor;
ps._navStarVE.shaderFloat1 = ps._jumpMarker.uSpecIntensity;
ps._frameCount += delta;
if (ps._frameCount > 0.5) {
ps._frameCount = 0;
ps.$checkForAlignment();
}
// for non-compass directions, we need to give a console message pointer to the beacon
if (ps._basicCompass === true) {
ps._basicCompassFrameCount += delta;
if (ps._basicCompassFrameCount > 2) {
ps._basicCompassFrameCount = 0;
ps.$displayBasicPointerMessage();
}
}
}
//-------------------------------------------------------------------------------------------------------------
this.$checkForAlignment = function $checkForAlignment() {
//var startDate = new Date();
// make sure we don't run over ourselves
var ps = this;
if (ps._checkingAlignment === true) return;
ps._checkingAlignment = true;
var p = this.ship;
if (p.maxPitch - Math.abs(p.pitch) < 0.0001) {
ps._checkingAlignment = false;
return;
}
var ws = worldScripts.ManualWitchspaceAlignment;
// set the compass to the nav jump marker, and kick in our functions to monitor when the
// compass target changes, and when the player cancels the jump
if (ps._basicCompass === false && ps._jumpMarker && p.compassTarget != ps._jumpMarker) {
ps._oldTarget = p.compassTarget;
p.compassTarget = ps._jumpMarker;
ws.compassTargetChanged = ws.compassTargetChanged_hide;
ws.playerStartedAutoPilot = ws.playerStartedAutoPilot_hide;
//ws.playerCancelledJumpCountdown = ws.playerCancelledJumpCountdown_hide;
}
if (ps.$shipIsAligned() === true) {
ps._alignWarning = false;
ps._lastHeading = p.heading;
if (ps._jumpStarted === false) {
ps._jumpStarted = true;
if (ps._cancelTimer && ps._cancelTimer.isRunning) ps._cancelTimer.stop();
ps._override = true;
p.beginHyperspaceCountdown(p.hyperspaceSpinTime);
//ws.playerCancelledJumpCountdown = ws.playerCancelledJumpCountdown_hide;
ps._override = false;
}
if (ps._BGS && ps._BGSStarted === false) {
ps._BGSStarted = true;
worldScripts.BGS.$mwa_playerStartedJumpCountdown("standard", p.hyperspaceSpinTime);
}
} else {
if (ps._jumpStarted === true) {
ps._jumpStarted = false;
//delete ws.playerCancelledJumpCountdown;
// do the actual cancellation through a separate timer, rather than the framecallback
// sometimes getting timeout errors when run through the fcb
if (!ps._cancelTimer || !ps._cancelTimer.isRunning) {
ps._cancelTimer = new Timer(ps, ps.$performCancel, 0.25, 0);
}
//p.cancelHyperspaceCountdown();
/*if (ps._BGS) {
worldScripts.BGS.playerCancelledJumpCountdown();
worldScripts.BGS._clrTimer(1);
}*/
}
ps._alignCount += 1;
if (ps._alignCount >= 15 && ps._occludedWarning === false) ps._alignWarning = false;
if (ps._alignWarning === false) {
ps._alignWarning = true;
ps._alignCount = 0;
player.consoleMessage(expandDescription("[witch-unaligned]"), 4);
}
}
ps._checkingAlignment = false;
//log(this.name, "checkForAlignment complete in ms: " + (new Date().getTime() - startDate.getTime()));
}
//-------------------------------------------------------------------------------------------------------------
this.$performCancel = function $performCancel() {
var p = player.ship;
p.script._override = true;
p.cancelHyperspaceCountdown();
p.script._override = false;
}
//-------------------------------------------------------------------------------------------------------------
this.$vectoredPositionToTarget = function $vectoredPositionToTarget(targetPosVector, distance) {
if (!this.ship || !this.ship.position) return null;
var v = targetPosVector.subtract(this.ship.position).direction();
v = v.multiply(distance);
v = v.add(this.ship.position);
return v;
}
//-------------------------------------------------------------------------------------------------------------
this.$reorientVEToPlayer = function $reorientVEToPlayer(navVE) {
if (!this.ship || !this.ship.position) return;
var orient = this.$lookAtRotate(this.ship.position.subtract(navVE.position), this.ship.orientation.vectorUp());
if (orient === null) orient = this.$lookAtRotate(this.ship.position.subtract(navVE.position), this.ship.orientation.vectorForward().multiply(-1));
navVE.orientation = orient;
}
//-------------------------------------------------------------------------------------------------------------
this.$lookAtRotate = function $lookAtRotate(forward, up) {
//returns an orientation quaternion given a Forward vector and an Up vector (the Forward and Up do not need to be
//orthonormal but they can not be parallel or anti-parallel.)
if (forward.direction().cross(up.direction()).magnitude() < 0.01) return null; // Return null if Forward and Up are parallel or anti-parallel or nearly so.
var f = forward.direction();
var u = this.$orthoNormalise(f, up);
var v = new Vector3D(0, 1, 0); //Uses Y axis at the basis axis for theta and the Z axis is used to measure phi from for finding the Euler angles.
var u2 = this.$orthoNormalise(f, v);
var sign = -u2.cross(u).dot(f);
var sign = sign && sign / Math.abs(sign);
var psi = sign * u2.angleTo(u);
var z = new Vector3D(0, 0, 1);
var h = new Vector3D(f.x, 0, f.z);
sign = -z.cross(h).y;
sign = sign && sign / Math.abs(sign);
var phi = sign * z.angleTo(h);
sign = f.y;
sign = sign && sign / Math.abs(sign);
var theta = (Math.PI / 2) - (sign * h.angleTo(f));
return this.$lookAtRotateEuler(theta, phi, psi);
}
//-------------------------------------------------------------------------------------------------------------
this.$orthoNormalise = function $orthoNormalise(a, b) {
//Returns a normalised vector that is in the plane of "ab" and is at 90� to "a" in the same half plane as "b".
var a2 = a.direction();
var b2 = b.direction();
var c = a.cross(b).cross(a);
return c.direction();
}
//-------------------------------------------------------------------------------------------------------------
this.$lookAtRotateEuler = function $lookAtRotateEuler(theta, phi, psi) {
//Returns a Quaternion that matches a rotational transformation that is described by the Euler angles.
//Uses Y axis at the basis axis for theta and the Z axis is used to measure phi from.
var q = new Quaternion(1, 0, 0, 0);
var theta2 = (Math.PI / 2) - theta;
q = q.rotateZ(psi);
q = q.rotateX(theta2);
q = q.rotateY(phi);
return q;
}
//-------------------------------------------------------------------------------------------------------------
this.$shipIsAligned = function $shipIsAligned() {
//var startDate = new Date();
var p = this.ship;
var target = this._jumpMarker;
if (!target) return false;
var deviation = p.vectorForward.angleTo(target.position.subtract(p.position));
if (deviation < this._alignAccuracy) {
// check for occlusion
/*if (p.script._useCheckCourseFunction === true) {
var ent = p.checkCourseToPosition(target.position);
if (ent && (ent.isPlanet || ent.isSun || ent.isStation)) {
this._occludedCount += 1;
if (this._occludedCount >= 15 && this._occludedWarning === true) this._occludedWarning = false;
if (this._occludedWarning === false) {
this._occludedCount = 0;
player.consoleMessage(expandDescription("[witch-occluded]", {
entity: this.$entityType(ent)
}), 4);
this._occludedWarning = true;
}
return false;
}
} else {*/
var entities = [].concat(system.planets).concat(system.sun).concat(system.stations);
var occluded = false;
if (entities.length > 0) {
for (var i = 0; i < entities.length; i++) {
if (entities[i] && this.$jumpIsOccluded(entities[i], target) === true) {
this._occludedCount += 1;
if (this._occludedCount >= 15 && this._occludedWarning === true) this._occludedWarning = false;
if (this._occludedWarning === false) {
this._occludedCount = 0;
player.consoleMessage(expandDescription("[witch-occluded]", {
entity: this.$entityType(entities[i])
}), 4);
this._occludedWarning = true;
}
//log(this.name, "align check complete in ms: " + (new Date().getTime() - startDate.getTime()));
return false;
}
}
}
//}
//log(this.name, "align check complete in ms: " + (new Date().getTime() - startDate.getTime()));
return true;
} else {
//log(this.name, "align check complete in ms: " + (new Date().getTime() - startDate.getTime()));
return false;
}
}
//-------------------------------------------------------------------------------------------------------------
// with thanks to spara for the calculation
this.$jumpIsOccluded_alt = function $jumpIsOccluded_alt(bodyEntity, targetEntity) {
//if (this._jumpStarted === true && this._lastHeading && this.ship.heading.dot(this._lastHeading) > 0.99) return false;
var vStellarBody = bodyEntity.position;
var rStellarBody = bodyEntity.radius;
// i'm reusing this routine for stations, but it's not ideal for non-spherical entities
if (bodyEntity.isStation) {
var box = bodyEntity.boundingBox;
rStellarBody = (box.x > box.y && box.x > box.z ? box.x :
(box.y > box.x && box.y > box.z ? box.y : box.z)) / 2;
}
if (isNaN(rStellarBody) === true) return false;
var vTarget = targetEntity.position;
var vPlayerShip = player.ship;
var vPlayerToStellar = vStellarBody.subtract(vPlayerShip);
var vPlayerToTarget = vTarget.subtract(vPlayerShip);
var dPlayerToStellar = vPlayerToStellar.magnitude();
var dPlayerToTarget = vPlayerToTarget.magnitude();
if (dPlayerToStellar < dPlayerToTarget) {
var aStellar = Math.asin(rStellarBody / dPlayerToStellar);
var aTarget = vPlayerToStellar.angleTo(vPlayerToTarget);
if (aStellar > aTarget) {
return true;
} else {
return false;
}
}
}
//-------------------------------------------------------------------------------------------------------------
// with thanks to spara for the calculation
this.$jumpIsOccluded = function $jumpIsOccluded(bodyEntity, targetEntity) {
//if (this._jumpStarted === true && this._lastHeading && this.ship.heading.dot(this._lastHeading) > 0.99) return false;
//var startDate = new Date();
//var vStellarBody = bodyEntity.position;
var rStellarBody = bodyEntity.radius;
// i'm reusing this routine for stations, but it's not ideal for non-spherical entities (I think)
if (bodyEntity.isStation) {
var box = bodyEntity.boundingBox;
rStellarBody = (box.x > box.y && box.x > box.z ? box.x :
(box.y > box.x && box.y > box.z ? box.y : box.z)) / 2;
}
if (isNaN(rStellarBody) === true) return false;
var vTarget = targetEntity.position;
var vPlayerShip = this.ship.position;
var vPlayerToTarget = vTarget.subtract(vPlayerShip);
var dPlayerToStellar = vPlayerShip.distanceTo(bodyEntity);
var dPlayerToTarget = vPlayerToTarget.magnitude();
if (dPlayerToStellar < dPlayerToTarget) {
var checkVal = Math.sqrt(Math.pow(dPlayerToStellar, 2) - Math.pow(rStellarBody, 2));
if (isNaN(checkVal) === false) {
var vTest = vPlayerShip.add(vPlayerToTarget.direction().multiply(checkVal));
var dTest = vTest.distanceTo(bodyEntity);
if (rStellarBody > dTest) {
//log(this.name, "occlusion check complete in ms: " + (new Date().getTime() - startDate.getTime()));
return true;
} else {
//log(this.name, "occlusion check complete in ms: " + (new Date().getTime() - startDate.getTime()));
return false;
}
} else {
return true;
}
} else {
//log(this.name, "occlusion check complete in ms: " + (new Date().getTime() - startDate.getTime()));
return false;
}
}
//-------------------------------------------------------------------------------------------------------------
this.$entityType = function $entityType(entity) {
if (entity.isPlanet) return "planet";
if (entity.isSun) return "sun";
return "station";
}
//-------------------------------------------------------------------------------------------------------------
this.$displayBasicPointerMessage = function $displayBasicPointerMessage() {
var p = this.ship;
var target = this._jumpMarker;
var output = "";
var f_dev = p.vectorForward.angleTo(target.position.subtract(p));
var r_dev = p.vectorRight.angleTo(target.position.subtract(p));
var u_dev = p.vectorUp.angleTo(target.position.subtract(p));
var s = "";
// > 1.56 means opposite side (3.12 exact opposite)
// so, f_dev < alignAccuracy -- aligned
if (f_dev < this._alignAccuracy) s = "X";
if (f_dev >= this._alignAccuracy) {
if (r_dev > 1.69) s += "left ";
if (u_dev < 1.45) {
s += (s === "" ? "" : "and ") + "up ";
} else if (u_dev > 1.69) {
s += (s === "" ? "" : "and ") + "down ";
}
if (r_dev < 1.45) s += (s === "" ? "" : "and ") + "right ";
if (f_dev > 1.57) s += " (aft)";
}
if (s !== "X") {
output += "Align: " + s;
player.consoleMessage(output, 2);
}
} |