Scripts/reversecontrol.js |
this.name = "reversecontrol";
this.author = "Norby, cag";
this.copyright = "2014 Norbert Nagy";
this.licence = "CC BY-NC-SA 3.0";
this.description = "Reverse up-down control in the aft view.";
(function(){
"use strict";
//customizable properties
this.$ReverseControlOn = true; //can turn off without uninstall
this.$InertiaOn = true; // "
//internal properties, should not touch
this.$AxesWritable = oolite.compareVersion("1.87") <= 0;
this.$ReverseControlFCB = null; //FrameCallBack
this.$revCtrlCfg = { Name: this.name,
Display: "Reverse Control",
Alive: "$revCtrlCfg",
Notify: "$versionCheck",
Bool: { B0: { Name: "$ReverseControlOn",
Def: true,
Desc: "Toggle reverse ctrls"
},
B1: { Name: "$InertiaOn",
Def: true,
Desc: "Toggle inertia"
},
Info: "Inertia for keyboard only (sticks already have it). Settings are independent; you can set either, both or none. NB: Inertia requires a minimum Oolite version of 1.87"
}
};
this.$versionCheck = function versionCheck() {
var that = versionCheck;
var wr = (that.wr = that.wr || worldScripts.reversecontrol);
var writable = oolite.compareVersion("1.87") <= 0;
if( wr.$InertiaOn && !writable ) {
wr.$ReverseControl_FCB.inertiaOn = wr.$InertiaOn = false;
log(this.name, 'this version of Oolite (' + oolite.version + ') cannot support Inertia (need at least 1.87). Inertia has been disabled.');
}
}
//world script events
this.startUpComplete = function () {
var wLC = worldScripts.Lib_Config;
if( wLC ) {
let error = wLC._registerSet( this.$revCtrlCfg );
if( error !== 0 )
log(this.name, 'startUpComplete, _registerSet returned error = ' + error );
}
this.$versionCheck();
}
this.shipWillLaunchFromStation = function shipWillLaunchFromStation() {
var that = shipWillLaunchFromStation;
var wr = (that.wr = that.wr || worldScripts.reversecontrol);
if( wr.$InertiaOn ) { // fcb always on to catch axis reversal
if( !isValidFrameCallback( wr.$ReverseControlFCB ) ) {
wr.$ReverseControlFCB = addFrameCallback( wr.$ReverseControl_FCB.bind(wr) );
}
}
}
this.step1Up = function() {
}
this.shipWillDockWithStation = function(/*station*/) {
this.$Stop( true );
}
this.$nearZero = 1e-6; // some floating point numbers never === 0
this.viewDirectionChanged = function viewDirectionChanged( view ) {
var that = viewDirectionChanged;
var wr = (that.wr = that.wr || worldScripts.reversecontrol);
var transferFn = (that.transferFn = that.transferFn || wr._transferInertia);// fn ref
var inertiaFn = (that.inertiaFn = that.inertiaFn || wr._inInertialMotion); // fn ref
var nearZero = (that.nearZero = that.nearZero || wr.$nearZero); // static var
var revCtrlOn = wr.$ReverseControlOn;
var inertiaOn = wr.$InertiaOn;
if( inertiaOn ) {
var ps = player.ship, pitch = ps.pitch, roll = ps.roll;
var fcbFn = wr.$ReverseControl_FCB;
var abs_pitch = pitch < 0 ? -pitch : pitch;
var abs_roll = roll < 0 ? -roll : roll;
if( abs_pitch > nearZero || abs_roll > nearZero ) { // have inertia to bleed off (doesn't apply to yaw as same in all views)
let inertiaType = 0, PITCH = 1, ROLL = 2; // bitflags for axes
if( revCtrlOn ) // set pitch/roll in inertiaFn w/ cross axis values
transferFn( view, ps, fcbFn.prevPitch, fcbFn.prevRoll ); // use fcb's prev values so 1st call to inertiaFn can calc diffs
if( abs_pitch > nearZero ){ // only save significant pitch
inertiaType |= PITCH;
if( !revCtrlOn ) // transfer prevPitch value
inertiaFn.pitch = fcbFn.prevPitch;
}
if( abs_roll > nearZero ){ // only save significant roll
inertiaType |= ROLL;
if( !revCtrlOn ) // transfer prevRoll value
inertiaFn.roll = fcbFn.prevRoll;
}
fcbFn.isInertial = inertiaType; // bleed off inertia before overriding input
inertiaFn.keyboardInput = false;
} else {
transferFn.lastView = view;
fcbFn.isInertial = 0; // no inertia
}
}
switch( view ) {
case "VIEW_AFT":
case "VIEW_PORT":
case "VIEW_STARBOARD":
case "VIEW_FORWARD": // added for inertia
if( view === 'VIEW_FORWARD' && !inertiaOn ) {
wr.$Stop( true );
} else if( (revCtrlOn || inertiaOn) &&
!isValidFrameCallback( wr.$ReverseControlFCB ) ) {
wr.$ReverseControlFCB = addFrameCallback( wr.$ReverseControl_FCB.bind(wr) );
}
break;
default:
wr.$Stop( !inertiaOn );
}
}
// moved code in from core so this oxp no longer generates any garbage to be collected (speed is comparable)
this._rotate_about_axis = function _rotate_about_axis( quat, vector, angle, result ) {
var that = _rotate_about_axis;
var cos = (that.cos = that.cos || Math.cos);
var sin = (that.sin = that.sin || Math.sin);
var rotn = (that.rotn = that.rotn || [0, 0, 0, 0]);
var a = angle / 2;
var c = cos(a);
var s = sin(a);
// rotation quaternion
rotn[0] = c;
rotn[1] = vector[0] * s;
rotn[2] = vector[1] * s;
rotn[3] = vector[2] * s;
// multiply quaternions
result[0] = quat[0] * rotn[0] - quat[1] * rotn[1] - quat[2] * rotn[2] - quat[3] * rotn[3];
result[1] = quat[0] * rotn[1] + quat[1] * rotn[0] + quat[2] * rotn[3] - quat[3] * rotn[2];
result[2] = quat[0] * rotn[2] + quat[2] * rotn[0] + quat[3] * rotn[1] - quat[1] * rotn[3];
result[3] = quat[0] * rotn[3] + quat[3] * rotn[0] + quat[1] * rotn[2] - quat[2] * rotn[1];
}
this._transferInertia = function _transferInertia( view, ps, pitch, roll ) {
var that = _transferInertia;
var wr = (that.wr = that.wr || worldScripts.reversecontrol);
var inertiaFn = (that.inertiaFn = that.inertiaFn || wr._inInertialMotion); // fn ref
var nearZero = (that.nearZero = that.nearZero || wr.$nearZero); // static var
var lastView = (that.lastView = that.lastView || 'VIEW_FORWARD'); // store viewDirection to know where changed from
var rpRatio = wr.$ReverseControl_FCB.rpRatio;
var abs_pitch = pitch < 0 ? -pitch : pitch;
var abs_roll = roll < 0 ? -roll : roll;
var newPitch, newRoll;
switch( lastView ) {
case "VIEW_FORWARD":
switch( view ) {
case "VIEW_AFT":
newPitch = abs_pitch > nearZero ? -pitch : 0;
newRoll = abs_roll > nearZero ? -roll : 0;
break;
case "VIEW_PORT":
newPitch = abs_roll > nearZero ? roll / rpRatio : 0;
newRoll = abs_pitch > nearZero ? -pitch * rpRatio : 0;
break;
case "VIEW_STARBOARD":
newPitch = abs_roll > nearZero ? -roll / rpRatio : 0;
newRoll = abs_pitch > nearZero ? pitch * rpRatio : 0;
break;
default: // needed in case hit F5 <d'Oh>
return;
}
break;
case "VIEW_AFT":
switch( view ) {
case "VIEW_FORWARD":
newPitch = abs_pitch > nearZero ? -pitch : 0;
newRoll = abs_roll > nearZero ? -roll : 0;
break;
case "VIEW_PORT":
newPitch = abs_roll > nearZero ? -roll / rpRatio : 0;
newRoll = abs_pitch > nearZero ? pitch * rpRatio : 0;
break;
case "VIEW_STARBOARD":
newPitch = abs_roll > nearZero ? roll / rpRatio : 0;
newRoll = abs_pitch > nearZero ? -pitch * rpRatio : 0;
break;
default:
return;
}
break;
case "VIEW_PORT":
switch( view ) {
case "VIEW_FORWARD":
newPitch = abs_roll > nearZero ? -roll /rpRatio : 0;
newRoll = abs_pitch > nearZero ? pitch * rpRatio : 0;
break;
case "VIEW_AFT":
newPitch = abs_roll > nearZero ? roll / rpRatio : 0;
newRoll = abs_pitch > nearZero ? -pitch * rpRatio : 0;
break;
case "VIEW_STARBOARD":
newPitch = abs_pitch > nearZero ? -pitch : 0;
newRoll = abs_roll > nearZero ? -roll : 0;
break;
default:
return;
}
break;
case "VIEW_STARBOARD":
switch( view ) {
case "VIEW_FORWARD":
newPitch = abs_roll > nearZero ? roll / rpRatio : 0;
newRoll = abs_pitch > nearZero ? -pitch * rpRatio : 0;
break;
case "VIEW_AFT":
newPitch = abs_roll > nearZero ? -roll / rpRatio : 0;
newRoll = abs_pitch > nearZero ? pitch * rpRatio : 0;
break;
case "VIEW_PORT":
newPitch = abs_pitch > nearZero ? -pitch : 0;
newRoll = abs_roll > nearZero ? -roll : 0;
break;
default:
return;
}
break;
}
inertiaFn.pitch = ps.pitch = newPitch; // save inertia values
inertiaFn.roll = ps.roll = newRoll;
that.lastView = view;
}
this._inInertialMotion = function _inInertialMotion( delta, ps, view, isInertial, pitch, roll, yaw ) {
/* core game differs in how it handles input from joystick/gamepad vs keyboard
- when you reverse an axis, say pitch down from an up pitch, the stick handlers reverse from the
current value through zero and beyond to the opposite maxPitch. When using the keyboard, a reverse
immediately sets the rate to zero and proceeds to the maxPitch. This has the unfortunate consequence
of cancelling any inertia. This function attempts to detect when a keyboard is being used and
simulate the inertial effects that one sees when using a stick. Stick users should not be
impacted in any way (hopefully) :)
*/
var that = _inInertialMotion;
var wr = (that.wr = that.wr || worldScripts.reversecontrol);
var nearZero = (that.nearZero = that.nearZero || wr.$nearZero);
var maxPitch = that.maxPitch = that.maxPitch === undefined ? player.ship.maxPitch : that.maxPitch; // ship values that may chang
var maxRoll = that.maxRoll = that.maxRoll === undefined ? player.ship.maxRoll : that.maxRoll; // - updated 1/sec in fcb
var maxYaw = that.maxYaw = that.maxYaw === undefined ? player.ship.maxYaw : that.maxYaw;
var keyboardInput = that.keyboardInput = that.keyboardInput === undefined ? 0 : that.keyboardInput;
/* for keyboard: (src: PlayerEntityControls.m)
[self increase_flight_roll:isCtrlDown ? flightArrowKeyPrecisionFactor*roll_dampner*roll_delta : delta_t*roll_delta];
...
if (!rolling) ... [self decrease_flight_roll:roll_dampner];
where #define ROLL_DAMPING_FACTOR 1.0f (src: PlayerEntity.h)
roll_delta = 2.0f * max_flight_roll; (src: PlayerEntity.m)
roll_dampner = ROLL_DAMPING_FACTOR * delta_t;
- same goes for pitch & yaw
- so change in axis is scaled to full +- range (thus 0 to full motion is thus 0.5 sec for all axes)
- thus, max change frame to frame, maxDelta, is: delta * 2 * max<axis>
*/
function adjustKeyInput( axis, prev, curr, abs_curr, max, flag ) {
if( prev !== 0 ) { // prev is set == 0 when it becomes insignificant
var finished = false;
let abs_prev = prev < 0 ? -prev : prev;
if( abs_curr > nearZero ) {
let diff = abs_prev - abs_curr;
let abs_diff = diff < 0 ? -diff : diff;
let maxDelta = delta * 2 * max + nearZero;
if( curr * prev < 0 // change direction
|| (abs_diff > maxDelta && // or keyboard input (core setting to zero!)
maxDelta + nearZero >= abs_curr) ) {
keyboardInput = that.keyboardInput |= flag;
let newValue = abs_curr > maxDelta ? maxDelta : abs_curr; // use core's value except on change of view
curr = that[axis] = ps[axis] = prev + newValue * (prev > 0 ? -1 : 1);
finished = curr * prev < 0; // exit when inertia has been bled off, ie. cross zero
} else { // input agrees w/ inertial direction
finished = true;
}
} else if( curr === 0 ) { // zero => no input or both axis keys pressed, so dampen
keyboardInput = that.keyboardInput |= flag;
curr = that[axis] = ps[axis] = prev + delta * (prev > 0 ? -1 : 1);
finished = abs_prev < delta;
}
if( finished ) {
prev = that[axis] = 0;
keyboardInput = that.keyboardInput &= ~flag;
}
}
}
var PITCH = 1, ROLL = 2, YAW = 4, // bitflags for axes
prevPitch, abs_pitch, prevRoll, abs_roll, prevYaw, abs_yaw;
abs_pitch = pitch < 0 ? -pitch : pitch;
abs_roll = roll < 0 ? -roll : roll;
abs_yaw = yaw < 0 ? -yaw : yaw;
prevPitch = that.pitch === undefined ? 0 : that.pitch; // load saved values from previous frame
prevRoll = that.roll === undefined ? 0 : that.roll;
prevYaw = that.yaw === undefined ? 0 : that.yaw;
that.pitch = abs_pitch > nearZero ? pitch : 0; // only save significant pitch
that.roll = abs_roll > nearZero ? roll : 0;
that.yaw = abs_yaw > nearZero ? yaw : 0;
adjustKeyInput( 'pitch', prevPitch, pitch, abs_pitch, maxPitch, PITCH );
adjustKeyInput( 'roll', prevRoll, roll, abs_roll, maxRoll, ROLL );
adjustKeyInput( 'yaw', prevYaw, yaw, abs_yaw, maxYaw, YAW );
if( !keyboardInput || (prevPitch === 0 && prevRoll === 0 && prevYaw === 0) ) {
keyboardInput = that.keyboardInput = 0;
that.pitch = that.roll = that.yaw = 0;
return false;
}
return keyboardInput;
}
/* (function() { // IIFE for neither
var wr = worldScripts.reversecontrol;
wr.$Stop( true );
wr.$ReverseControl_FCB.revCtrlOn = wr.$ReverseControlOn = false;
wr.$ReverseControl_FCB.inertiaOn = wr.$InertiaOn = false;
console.clearConsole();
log('ReverseControlOn = ' + wr.$ReverseControlOn + ', InertiaOn = ' + wr.$InertiaOn +
', FCB ' + (isValidFrameCallback(wr.$ReverseControlFCB) ? 'is running' : 'not registered') );
})() //*/
/* (function() { // IIFE for ReverseControl only
var wr = worldScripts.reversecontrol;
wr.$Stop( true );
wr.$ReverseControl_FCB.revCtrlOn = wr.$ReverseControlOn = true;
wr.$ReverseControl_FCB.inertiaOn = wr.$InertiaOn = false;
if( player.ship.viewDirection !== 'VIEW_FORWARD' )
wr.$ReverseControlFCB = addFrameCallback( wr.$ReverseControl_FCB.bind(wr) );
console.clearConsole();
log('ReverseControlOn = ' + wr.$ReverseControlOn + ', InertiaOn = ' + wr.$InertiaOn +
', FCB ' + (isValidFrameCallback(wr.$ReverseControlFCB) ? 'is running' : 'not registered') );
})() //*/
/* (function() { // IIFE for Inertia only
var wr = worldScripts.reversecontrol;
wr.$Stop( true );
wr.$ReverseControl_FCB.revCtrlOn = wr.$ReverseControlOn = false;
wr.$ReverseControl_FCB.inertiaOn = wr.$InertiaOn = true;
wr.$versionCheck();
if( wr.$InertiaOn )
wr.$ReverseControlFCB = addFrameCallback( wr.$ReverseControl_FCB.bind(wr) );
console.clearConsole();
log('ReverseControlOn = ' + wr.$ReverseControlOn + ', InertiaOn = ' + wr.$InertiaOn +
', FCB ' + (isValidFrameCallback(wr.$ReverseControlFCB) ? 'is running' : 'not registered') );
})() //*/
/* (function() { // IIFE for both
var wr = worldScripts.reversecontrol;
wr.$Stop( true );
wr.$ReverseControl_FCB.revCtrlOn = wr.$ReverseControlOn = true;
wr.$ReverseControl_FCB.inertiaOn = wr.$InertiaOn = true;
wr.$versionCheck();
if( wr.$InertiaOn )
wr.$ReverseControlFCB = addFrameCallback( wr.$ReverseControl_FCB.bind(wr) );
console.clearConsole();
log('ReverseControlOn = ' + wr.$ReverseControlOn + ', InertiaOn = ' + wr.$InertiaOn +
', FCB ' + (isValidFrameCallback(wr.$ReverseControlFCB) ? 'is running' : 'not registered') );
})() //*/
// console.script.wr = worldScripts.reversecontrol;
// console.script.ps = player.ship;
// wr.$ReverseControlOn = true;
// wr.$ReverseControlOn = false;
// wr.$InertiaOn = true;
// wr.$InertiaOn = false;
// wr.$ReverseControlFCB
// :time wr.$ReverseControl_FCB( 0.02 )
// log('ReverseControlOn = ' + wr.$ReverseControlOn + ', InertiaOn = ' + wr.$InertiaOn );
/* (function() { // IIFE for debug console
var wr = worldScripts.reversecontrol;
wr.$Stop( true );
if( wr.$InertiaOn || player.ship.viewDirection !== 'VIEW_FORWARD' )
wr.$ReverseControlFCB = addFrameCallback( wr.$ReverseControl_FCB.bind(wr) );
console.clearConsole();
log('ReverseControlOn = ' + wr.$ReverseControlOn + ', InertiaOn = ' + wr.$InertiaOn +
', FCB ' + (isValidFrameCallback(wr.$ReverseControlFCB) ? 'is running' : 'not registered') );
})() //*/
//ReverseControl methods
this.$ReverseControl_FCB = function ReverseControl_FCB( delta ) { //FrameCallBack
var that = ReverseControl_FCB;
var wr = (that.wr = that.wr || worldScripts.reversecontrol);
var _rotate_about_axis = (that._rotate_about_axis = that._rotate_about_axis || wr._rotate_about_axis); // fn ref
var _inInertialMotion = (that._inInertialMotion = that._inInertialMotion || wr._inInertialMotion); // "
var nearZero = (that.nearZero = that.nearZero || wr.$nearZero); // static var
var revCtrlOn = (that.revCtrlOn = that.revCtrlOn || wr.$ReverseControlOn); // static var's (can change in stn but new fcb created)
var inertiaOn = (that.inertiaOn = that.inertiaOn || wr.$InertiaOn); // "
var orient = (that.orient = that.orient || []); // working quaternion
var step1 = (that.step1 = that.step1 || []); // working vector
var step2 = (that.step2 = that.step2 || []); // "
var prevVR = (that.prevVR = that.prevVR || []); // vectorRight from previous frame
var prevVF = (that.prevVF = that.prevVF || []); // vectorForward "
var prevVU = (that.prevVU = that.prevVU || []); // vectorUp "
var prevPitch = that.prevPitch = that.prevPitch === undefined ? 0 : that.prevPitch; // pitch from previous frame
var prevRoll = that.prevRoll = that.prevRoll === undefined ? 0 : that.prevRoll; // roll "
var prevYaw = that.prevYaw = that.prevYaw === undefined ? 0 : that.prevYaw; // yaw "
var isInertial = that.isInertial = that.isInertial === undefined ? 0 : that.isInertial; // flag for input override
var sumDelta = that.sumDelta = that.sumDelta === undefined ? null : that.sumDelta; // counter for per second updates (null as used as flag for 1st time)
var lastView = that.lastView = that.lastView === lastView ? 'VIEW_FORWARD' : that.lastView;// to separate view chg's from reversals
var axesWritable = that.axesWritable = that.axesWritable === undefined ? wr.$AxesWritable : that.axesWritable;
// - flag for compatible version (axis writability only supported starting in 1.87)
var rpRatio = (that.rpRatio = that.rpRatio || 2); // relative difference in angular speed of roll vs pitch
// - makes motion consistent (closed loophole where you could pitch faster than maxPitch by switching view to port/starboard and rolling)
function copy_vector( a, b ) { // a -> b
b[0] = a[0];
b[1] = a[1];
b[2] = a[2];
}
function copy_quaternion( a, b ) { // a -> b
b[0] = a[0];
b[1] = a[1];
b[2] = a[2];
b[3] = a[3];
}
function checkReversal( axis, prev, curr, abs_curr, flag ) {
let abs_prev = prev < 0 ? -prev : prev;
if( abs_prev < nearZero ) return;
if( prev * curr < 0 || curr === 0 ) { // different signs or zero => no input or both axis keys pressed
_inInertialMotion[axis] = prev; // save inertia values
_inInertialMotion.keyboardInput &= ~flag; // reset flag (determined in _inInertialMotion)
isInertial = that.isInertial |= flag; // set flag for axis
}
}
var PITCH = 1, ROLL = 2, YAW = 4, // bitflags for axes
angle, ps = player.ship;
if( ps && ps.isValid && !ps.docked && (revCtrlOn || inertiaOn) && ps.AI != "dockingAI.plist" ) {
var origPitch, pitch, abs_pitch, origRoll, roll, abs_roll, yaw, abs_yaw,
view = ps.viewDirection;
origPitch = pitch = ps.pitch;
origRoll = roll = ps.roll;
yaw = ps.yaw;
if( sumDelta === null ) { // 1st time thru, ie. 1st frame in this view
that.sumDelta = 1; // force update of max values
copy_vector( ps.vectorForward, prevVF )
copy_vector( ps.vectorRight, prevVR )
copy_vector( ps.vectorUp, prevVU )
prevPitch = pitch;
prevRoll = roll;
prevYaw = yaw;
}
if( sumDelta >= 1 ) { // limit refreshing values to 1/second
that.sumDelta = 0; // - are periodically updated to detect change from equipment, battle damage or oxp's
let maxRoll = ps.maxRoll;
let maxPitch = ps.maxPitch;
rpRatio = that.rpRatio = maxRoll / maxPitch;
_inInertialMotion.maxRoll = maxRoll;
_inInertialMotion.maxPitch = maxPitch;
_inInertialMotion.maxYaw = ps.maxYaw;
}
that.sumDelta += delta;
abs_pitch = pitch < 0 ? -pitch : pitch;
abs_roll = roll < 0 ? -roll : roll;
abs_yaw = yaw < 0 ? -yaw : yaw;
if( inertiaOn && axesWritable ) {
if( view === that.lastView ) { // must separate view chgs & reversals else they could cancel each other
checkReversal( 'pitch', prevPitch, pitch, abs_pitch, PITCH );
checkReversal( 'roll', prevRoll, roll, abs_roll, ROLL );
checkReversal( 'yaw', prevYaw, yaw, abs_yaw, YAW );
}
that.lastView = view;
}
if( isInertial !== 0 && axesWritable ) { // delay correcting until inertia bleeds off
let result = _inInertialMotion( delta, ps, view, isInertial, pitch, roll, yaw );
if( result === false ) {
isInertial = that.isInertial = 0; // ending inertial phase
} else {
if( result & PITCH ) {
pitch = _inInertialMotion.pitch;
abs_pitch = pitch < 0 ? -pitch : pitch;
}
if( result & ROLL ) {
roll = _inInertialMotion.roll;
abs_roll = roll < 0 ? -roll : roll;
}
if( result & YAW ) {
yaw = _inInertialMotion.yaw;
abs_yaw = yaw < 0 ? -yaw : yaw;
}
}
}
if( abs_pitch > nearZero || abs_roll > nearZero || (isInertial & YAW && abs_yaw > nearZero) ) {
// yaw treated only on reversal, as it doesn't change from one view to another
copy_quaternion( ps.orientation, orient );
let setOrientation = false,
lastQuat = orient; // each view alternates step1, step2, so _rotate_about_axis always gets diff quats
switch( view ) {
case "VIEW_FORWARD": // aligns w/ input, so only concerned w/ inertia
if( abs_pitch > nearZero && isInertial & PITCH ) {
angle = -pitch * delta;
_rotate_about_axis(lastQuat, prevVR, angle, lastQuat = step1);
setOrientation = true;
}
if( abs_roll > nearZero && isInertial & ROLL) {
angle = -roll * delta;
_rotate_about_axis(lastQuat, prevVF, angle, lastQuat = step2);
setOrientation = true;
}
break;
case "VIEW_AFT":
if( abs_pitch > nearZero && (revCtrlOn || isInertial & PITCH) ) {
angle = pitch * delta * (revCtrlOn ? 1 : -1);
angle *= revCtrlOn ? 2 : 1; // 2 * incl's correcting for cmd that got us here
_rotate_about_axis(lastQuat, prevVR, angle, lastQuat = step1);
setOrientation = true;
}
if( abs_roll > nearZero && (revCtrlOn || isInertial & ROLL) ) {
angle = roll * delta * (revCtrlOn ? 1 : -1);
angle *= revCtrlOn ? 2 : 1; // 2 * incl's correcting for cmd that got us here
_rotate_about_axis(lastQuat, prevVF, angle, lastQuat = step2);
setOrientation = true;
}
break;
case "VIEW_PORT":
// important to alternate prevVR, prevVF,...; doing prevVR, prevVF, prevVF, prevVR as before
// will cause center to be off vertically; but this way, only set ps.orientation once, not 4 times
if( abs_pitch > nearZero && (revCtrlOn || isInertial & PITCH) ) { //pitch -> -roll
angle = (revCtrlOn ? origPitch : -pitch) * delta;
_rotate_about_axis(lastQuat, prevVR, angle, lastQuat = step1); // - undo this frame's pitch
if( revCtrlOn ) {
angle = -pitch * delta * rpRatio;
_rotate_about_axis(lastQuat, prevVF, angle, lastQuat = step2); // - apply pitch cmd as roll
}
setOrientation = true;
/* for dampening (no input) (src: PlayerEntityControls.m)
self decrease_flight_roll:roll_dampner]
roll_dampner = ROLL_DAMPING_FACTOR * delta_t;
#define ROLL_DAMPING_FACTOR 1.0f (src: PlayerEntity.h)
- so time to decay to zero varies with each axis
- as we're swapping axes, an adjustment is needed to correct the decay time
*/
let abs_prevP = prevPitch < 0 ? -prevPitch : prevPitch;
if( abs_prevP > abs_pitch && axesWritable ) { // dampening
let adjustment = delta - delta / rpRatio; // +delta (undo pitch time) - delta/rpRatio (add roll time)
pitch = ps.pitch = ps.pitch + adjustment * (pitch < 0 ? -1 : 1); // rolling ship, lengthen dampening time
}
}
if( abs_roll > nearZero && (revCtrlOn || isInertial & ROLL) ) { //roll -> pitch
if( revCtrlOn ) {
angle = roll * delta / rpRatio;
_rotate_about_axis(lastQuat, prevVR, angle, lastQuat = step1); // - apply roll cmd as pitch
}
angle = (revCtrlOn ? origRoll : -roll) * delta;
_rotate_about_axis(lastQuat, prevVF, angle, lastQuat = step2); // - undo this frame's roll
setOrientation = true;
let abs_prevR = prevRoll < 0 ? -prevRoll : prevRoll;
if( abs_prevR > abs_roll && axesWritable ) { // dampening
let adjustment = delta - delta * rpRatio; // +delta (undo roll time) - delta * rpRatio (add pitch time)
roll = ps.roll = ps.roll + adjustment * (roll < 0 ? -1 : 1); // rolling ship, shorten dampening time
}
}
break;
case "VIEW_STARBOARD":
// important to alternate prevVR, prevVF,...; doing prevVR, prevVF, prevVF, prevVR as before
// will cause center to be off vertically; but this way, only set ps.orientation once, not 4 times
if( abs_pitch > nearZero && (revCtrlOn || isInertial & PITCH) ) { //pitch -> -roll
angle = (revCtrlOn ? origPitch : -pitch) * delta;
_rotate_about_axis(lastQuat, prevVR, angle, lastQuat = step1); // - undo this frame's pitch
if( revCtrlOn ) {
angle = pitch * delta * rpRatio;
_rotate_about_axis(lastQuat, prevVF, angle, lastQuat = step2); // - apply pitch cmd as roll
}
setOrientation = true;
let abs_prevP = prevPitch < 0 ? -prevPitch : prevPitch;
if( abs_prevP > abs_pitch && axesWritable ) { // dampening
let adjustment = delta - delta / rpRatio; // +delta (undo pitch time) - delta/rpRatio (add roll time)
pitch = ps.pitch = ps.pitch + adjustment * (pitch < 0 ? -1 : 1); // rolling ship, lengthen dampening time
}
}
if( abs_roll > nearZero && (revCtrlOn || isInertial & ROLL) ) { //roll -> pitch
if( revCtrlOn ) {
angle = -roll * delta / rpRatio;
_rotate_about_axis(lastQuat, prevVR, angle, lastQuat = step1); // - apply roll cmd as pitch
}
angle = (revCtrlOn ? origRoll : -roll) * delta;
_rotate_about_axis(lastQuat, prevVF, angle, lastQuat = step2); // - undo this frame's roll
setOrientation = true;
let abs_prevR = prevRoll < 0 ? -prevRoll : prevRoll;
if( abs_prevR > abs_roll && axesWritable ) { // dampening
let adjustment = delta - delta * rpRatio; // +delta (undo roll time) - delta * rpRatio (add pitch time)
roll = ps.roll = ps.roll + adjustment * (roll < 0 ? -1 : 1); // pitching ship, shorten dampening time
}
}
break;
default:
wr.$Stop( !wr.$InertiaOn );
}
if( isInertial & YAW && abs_yaw > nearZero ) { // put result in orient to keep it simple
angle = -yaw * delta;
_rotate_about_axis(lastQuat, prevVU, angle, lastQuat = orient);
setOrientation = true;
}
if( setOrientation ) ps.orientation = lastQuat;
}
copy_vector( ps.vectorForward, prevVF )
copy_vector( ps.vectorRight, prevVR )
copy_vector( ps.vectorUp, prevVU )
that.prevPitch = pitch;
that.prevRoll = roll;
that.prevYaw = yaw;
} else {
wr.$Stop( true );
}
}
this.$Stop = function Stop( force ) {
var that = Stop;
var wr = (that.wr = that.wr || worldScripts.reversecontrol);
if( force || !wr.$InertiaOn ) {
var rcFCB = wr.$ReverseControlFCB;
if( rcFCB && isValidFrameCallback( rcFCB ) ) {
removeFrameCallback( rcFCB );
wr.$ReverseControlFCB = null;
}
wr.$ReverseControl_FCB.sumDelta = null; // internal flag to reload values
}
}
/*
this.$ReverseControl_FCB = function( delta ) { //FrameCallBack, improved by cag
var ps = player.ship;
var a, c = null;
if( ps && ps.isValid && !ps.docked && this.$ReverseControlOn && ps.AI != "dockingAI.plist" ) {
var prevori = this.$ReverseControlPrevOri;
if( prevori ) switch ( ps.viewDirection ) {
case "VIEW_AFT":
//reverse up-down
a = ps.pitch * delta;
c = prevori.vectorRight();
ps.orientation = ps.orientation.rotate( c, 2 * a ); // 2 * incl's correcting for pitch cmd that got us here
//reverse roll
a = ps.roll * delta;
c = prevori.vectorForward();
ps.orientation = ps.orientation.rotate( c, 2 * a ); // 2 * incl's correcting for roll cmd that got us here
break;
case "VIEW_PORT":
//pitch -> roll
a = ps.pitch * delta;
c = prevori.vectorRight();
ps.orientation = ps.orientation.rotate( c, 1 * a ); // - 1st undo this frame's pitch
c = prevori.vectorForward();
ps.orientation = ps.orientation.rotate( c, -1 * a ); // - now apply pitch cmd as roll
//roll -> -pitch
a = ps.roll * delta;
c = prevori.vectorForward();
ps.orientation = ps.orientation.rotate( c, 1 * a ); // - 1st undo this frame's roll
c = prevori.vectorRight();
ps.orientation = ps.orientation.rotate( c, 1 * a ); // - now apply roll cmd as pitch
break;
case "VIEW_STARBOARD":
//pitch -> roll
a = ps.pitch * delta;
c = prevori.vectorRight();
ps.orientation = ps.orientation.rotate( c, 1 * a ); // - 1st undo this frame's pitch
c = prevori.vectorForward();
ps.orientation = ps.orientation.rotate( c, 1 * a ); // - now apply pitch cmd as roll
//roll -> -pitch
a = ps.roll * delta;
c = prevori.vectorForward();
ps.orientation = ps.orientation.rotate( c, 1 * a ); // - 1st undo this frame's roll
c = prevori.vectorRight();
ps.orientation = ps.orientation.rotate( c, -1 * a ); // - now apply roll cmd as pitch
break;
default:
this.$Stop();
}
this.$ReverseControlPrevOri = ps.orientation;
} else {
this.$ReverseControlPrevOri = null;
}
}
this.$ReverseControl_FCB_old = function( delta ) { //FrameCallBack, old version
var ps = player.ship;
if( ps && ps.isValid && !ps.docked && this.$ReverseControlOn && ps.AI != "dockingAI.plist" ) {
var prevori = this.$ReverseControlPrevOri;
if( prevori ) switch ( ps.viewDirection ) {
case "VIEW_AFT":
//reverse up-down
//var a = ps.heading.angleTo( prevori.vectorUp() ) - Math.PI / 2; //buggy with sniperlock
var a = ps.pitch * delta;
var c = prevori.vectorRight();
ps.orientation = ps.orientation.rotate( c, 2 * a );
//reverse roll
//var a = ps.orientation.vectorRight().angleTo( prevori.vectorUp() ) - Math.PI / 2; //buggy with sniperlock
var a = ps.roll * delta;
var c = prevori.vectorForward();
ps.orientation = ps.orientation.rotate( c, 2 * a );
break;
case "VIEW_PORT":
//pitch -> roll
var a = ps.pitch * delta;
var c = prevori.vectorForward();
ps.orientation = ps.orientation.rotate( c, -2 * a );
//roll -> -pitch
var a = ps.roll * delta;
var c = prevori.vectorRight();
ps.orientation = ps.orientation.rotate( c, 2 * a );
break;
case "VIEW_STARBOARD":
//pitch -> -roll
var a = ps.pitch * delta;
var c = prevori.vectorForward();
ps.orientation = ps.orientation.rotate( c, 2 * a );
//roll -> pitch
var a = ps.roll * delta;
var c = prevori.vectorRight();
ps.orientation = ps.orientation.rotate( c, -2 * a );
break;
default:
this.$Stop();
}
this.$ReverseControlPrevOri = ps.orientation;
} else {
this.$ReverseControlPrevOri = null;
}
}
*/
}).call(this);
|