Back to Index Page generated: May 8, 2024, 6:16:03 AM

Expansion Telescope

Content

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description Extended targeting and scanning features, masslock borders and sniper ring. Extended targeting and scanning features, masslock borders and sniper ring.
Identifier oolite.oxp.Norby.Telescope oolite.oxp.Norby.Telescope
Title Telescope Telescope
Category Equipment Equipment
Author Norby Norby
Version 1.15 1.15
Tags
Required Oolite Version
Maximum Oolite Version
Required Expansions
Optional Expansions
  • oolite.oxp.Norby.Telescope_Extender:1.0
  • oolite.oxp.Norby.CombatMFD:1.9
  • oolite.oxp.Norby.Telescope_Extender:1.0
  • oolite.oxp.Norby.CombatMFD:1.9
  • Conflict Expansions
    Information URL http://wiki.alioth.net/index.php/Telescope n/a
    Download URL https://wiki.alioth.net/img_auth.php/7/77/Telescope_1.15.oxz n/a
    License CC BY-NC-SA 4 CC BY-NC-SA 4
    File Size n/a
    Upload date 1610873341

    Documentation

    Also read http://wiki.alioth.net/index.php/Telescope

    Telescope readme.txt

    Telescope forum topic: http://bb.aegidian.org/viewtopic.php?f=4&t=14961
    
    Quick start as a Hunter
    
    * Copy the .oxp folder to the AddOns directory, hold down the Shift when starting the game the first time after copying.
    * Buy Telescope with Extender equipment or load the included savegame ("Telescope demo.oolite-save"). Injectors, ECM System, Fuel Scoop, Military Laser and Scanner Targeting Enhancement are used also in this guide.
    * After undock (F1) you can see the magnified target in top center position.
    * If you bought Gravity Scanner then stop near the station, turn off your weapons with underscore (_) and wait for the scan results.
    * Turn your ship where you see lollipops at the edge of your IFF scanner. The Panorama targeting will countinually change the box to the most centered target.
    * Choose your enemy for the strength of your ship: start with a small ship flying alone.
    * Use Injectors (i) to fly into scanner range where you can see a Red ball which means visible ship with bounty. Do not hunt Yellow traders and Purple police ships if you want to remain in clean status.
    * Use Ctrl+arrow keys to put the target into the middle of the crosshairs to turn on the sniper ring which magnify the difference from the correct line-up.
    * Turn on your weapons (_) and fire (a). You can try to hit with Military laser over normal scanner range but within 30km and a stopped target will turn when successful, but if your target is too small then fly inside 25.6km where the red target box indicate the correct line-up.
    * Press ident (r) to lock the most centered target if there are more, press again to start auto steering or to lock another if it's more centered than the current target.
    * Telescope can be primed (Shift+N) then activated (n) to lock the nearest target, mode (b) change the function of the activate key.
    * If a missile is coming then a cyan ball warns you: start ECM (e), target it (t), try to avoid (i) or shoot it (a).
    * If you win and the pilot ejected then shot down the derelict ship to get the bounty, then fly after the small White balls and scoop the Escape Pod, metal fragments and cargo pods.
    * Fly back to the station and dock to get the reward for the pilot and sell the cargo (F8). Congratulations, you are richer. :)
    
    
    Telescope Equipment
    
    This OXP realizes some requests for an extended scanner described in this topic:
    http://bb.aegidian.org/viewtopic.php?f=6&t=13274
    Can determine the direction, distance, orientation and legal status of all visible ships.
    
    The detection range is equal with the scanner range if you use it without the Telescope Extender (still or willfully), but you can use the following features:
    
    Visual targeting
    
    The virtual model of the target ship is displayed and a console message shows the name, range and direction.
    The direction is marked by <^ or v> symbols to read faster than the Port Up or Down Starboard words.
    The ranges and directions are calculated continually to help turn in line and see the decreasing range while traveling to the target.
    The orientation of the model is equal with the target if you centered it and you can spin the model if turn around your ship.
    You can set the size of the model with the $TelescopeVSize variable in the Scripts/telescope.js.
    
    Lightballs
    
    Far targets get coloured lightball markers in the view and lollipops with darker colours near the edge of the scanner.
    
    Red: Hostile ship with bounty (pirates and thargoids),
    Pink: Tharglet until active and Drones with bounty in HardShips OXP,
    Yellow: Neutral ship with clean status (traders and co),
    Purple: Police,
    Cyan: Missile or Mine,
    Green: Buoy or Station,
    Blue: Derelict (Wormhole also from Oolite v1.79),
    White: Cargo, Escape Pod or Sun,
    Gray: last known position of a lost target (moved out of telescope range),
    Lightgray: Planet or Moon (a bit darker than Planet),
    Orange: Gravity Scanner detected ship too far to be visible,
    Brown: Gravity Scanner detected ship under 130t mass (less dangerous, especially with ShipVersion OXP).
    Black: lollipops in red alert over 30km to help focus on near targets.
    
    Beacons are identified in the whole system due to the transmitted radio signals.
    If a ship transmits a beaconcode then it's detected anywhere but coloured as a ship and turns to orange if too far to be visible.
    
    The current target is also marked with a gray "shadow" lollipop at the edge of the IFF scanner to help determine the direction, especially at close range, to avoid zooming the scanner. 
    If the target is closer than 1000m, the lightball will be removed as the ship almost covers the ball and cargo pods are large enough to detect with your eyes, but the shadow lollipop still remain.
    You can set the minimum distance with the $TelescopeVMarkMinDist variable in the file Scripts/telescope.js.
    
    Targets within the range of the Military Laser (30km) are marked with a larger ball and a normal (not darker) coloured lollipop.
    You can use this feature to determine exactly where you can open fire on the target so it's large enough to stand a chance of being hit.
    
    You can disable the lightballs of the ships by setting $TelescopeLightBalls to false, but non-ships with Blue, Cyan, Gray, Green and White colours will remain to help find these. Auto targeting functions can still lock far targets; there seems to be an empty box but the ships are in there.
    
    You can restrict lightballs in Red Alert to 10-30km if you set $TelescopeRedAlertLimiter to true to save a few CPU cycles but it is worth it on slow computers only.
    
    Sniper ring
    
    If you have almost lined up with your target who is between 10 and 30km then a ring will appear.
    The movement of this ring magnifies the variance to the correct line-up, to help fine tune your aim.
    You can hit if the target box of the Scanner Targeting Enhancement switches to red.
    
    Snipers can use the distances between 25.6 and 30km to fire before the enemy can see the attacker on its scanner.
    This was possible without Telescope, but the size of the ball gives another aid to knowing when the target runs out of range; and the ring can guide your aim without red box, which only works in the normal scanner range.
    
    If you think this is a cheat then fire only when the target can also see you (red box) or set the $TelescopeSniperRange variable to 25600 which will shorten the appearance of the ring and the largest ball (but there's no way to shorten the range of the Military Laser).
    
    Auto steering
    
    With the Telescope primed (Shift+N) the activate button ("n") can steer to the nearest lightball-marked target, which is useful in picking up cargo from the near field, rather than zooming the scanner to find them. Now you can just press activate instead of hitting zoom, locate, turn and unzoom.
    
    Auto-steering starts only if your ship flys in a line (not turning) and stops instantly if you touch the controls. It will stop steering before a perfect line-up so as not to aim for you.
    
    In Red Alert the activate button steers to the nearest attacker. You can disable this by turning off the weapons, in which case you can lock and steer to any near target regardless of the alert condition.
    
    Auto scanning
    
    Telescope checks for new targets every second and performs an autoscan if it finds one: simple light sensors can see new dots in the whole sky without using energy but must zoom with the main scope to determine the ship type which needs 2 energy points.
    The autoscan can be turned off if you set $TelescopeAutoScan false in the telescope.js file but it's usually worth the cost to get new information sooner.
    
    Far target locking
    
    You can lock far targets also which is not in the normal scanner (beacons and planets are always lockable). The distance is shown before the name of the ship with Scanner Targeting Enhancement, due to the fact that the second line shows the range of the virtual marker (which is always near the edge of the IFF Scanner).
    We must use this workaround because the core game does not allow locking onto a target outside of normal scanner range. So the locked marker coinsides with the lock of the target (can only fix this in the core game).
    
    If you cannot see a ship then it may have been destroyed, jumped out or flew farther than the visible range; or it has a Military Scanner Jammer and you do not have a Jammer Filter so you cannot get target lock.
    
    When a target flys too far and is lost, the telescope renames it to 'Lost target' until the next scan.
    
    MFD with the nearest targets
    
    The built-in MFD shows the 10 nearest targets, ships first then others.
    A "*" mean "red" targets (pirates and thargoids).
    Hostiles (who targeting you) are flagged with "!".
    Your current target is bracketed with [].
    If [[CombatMFD|Combat MFD]] is installed then the newest detected target is displayed in the last line in Combat MFD.
    
    Masslock borders
    
    In green alert you will see circles around detected targets where these ships can block your Torus Drive.
    Without a Telescope Extender, these are shown around planets and stations only, but if you buy one, it allows for detection of ships that you can see with your Mark-1 eyeball (escort ships at 2x, traders near 4x scanner range) and you will then get circles around all the ships before you.
    
    Until you enter these circles you will stay in condition green, but cloaked ships can cause surprises.
    
    The colour of a masslock border is the same as the lightball in the middle (see above).
    
    To get the shortest route, you can safely go forward near the largest circle without touching it or going inside. Imagine this is the border of a sphere so if you fly direct to the center then you will be masslocked sooner than if you flew to a point of the circle. If you steer just a little outside the circle, you will fly forward as near as possible without masslock.
    
    These are also a good indicator of the distance to the ships because the radius of the circles represent exactly one scanner range for the ship at its center.
    Planets have rings that are double its radius as they can masslock you within this range.
    The sun also has a large masslock field but this is not displayed by rings - it's less important and mainly a distraction.
    
    You can also turn on masslock borders in non-green alerts if you turn off your weapons.
    You can turn off all circles completely with the Telescope primed to the Lightballs menu (see below).
    
    Keypress functions
    
    The ident button ("r") gets a new feature from Telescope: it can lock the most centered target even if it's not shown on the screen.
    The next press will start auto steering to the target if the most centered target is the same, otherwise it will lock onto the new target; a third ident press will unlock it.
    In Red Alert it will not narrow the locking to the attackers; it can lock any target.
    
    Works only if there was a locked target beforehand, due to the triggering of the shipTargetLost event, so if there's no target it won't get called. Press "r" again or get something into the crosshair or use equipment buttons to lock a target. (Usually only an issue when leaving a station, exitting witchspace or Torus drive.)
    
    If you turn off the weapons with the underscore button ("_") then a scan happens and you enter into "Navigation Mode", where autolock helps you see through targets (called Panorama targeting): continually relocks to the most centered target.
    This button is choosed to avoid unwanted fire if you have turrets.
    In Red Alert you can lock any target and see far targets if you turn this mode on.
    The Ident button presses can turn Panorama targeting on and off when in Navigation Mode.
    
    With the Telescope primed (Shift+N), the mode button ("b") cycles through the functions of the activate ("n") button:
    
     Nearest target
     Rescan
     Step forward in the target list
     Step back in the target list
     Lightballs: off / navigation only / ships / masslock borders / large
     Sniper ring km: off / 5-25.6 / 10-25.6 / 15-25.6 / 5-30 / 10-30 / 15-30
     Steering: off / nearest target only / both nearest and step in the list
     Targets: 20 and limitation in red alert / 50 / 100 / 200
     Visual target: off / weapons off / no ring / no station / no question mark / all
     Visual target size: 1-8
    
    The first 4 functions are commands which happen instantly when activated.
    The "Step forward" and "Step back" will rescan if you step over the end of the target list.
    The Target list contains hostiles first, if any, then all ships in normal scanner (25.6km), followed by Cargo and Escape Pods, then ending with ships which are not in the normal scanner.
    
    The last 6 functions are some of customizable properties in the telescope.js file, which you can change during flight.
    Your settings are stored into missionVariables and saved when you save your game after docking.
    
    [When the core game provides more equipment buttons, a back button could step back to the previous function (maybe Ctrl+"b").
    With a second equipment button (maybe Ctrl+"n") settings could be separated from the commands.]
    
     Cost: 500.0 Cr.
     Techlevel: 5
    
    
    Telescope Extender
    
    You must install "Telescope Extender and Gravity Scanner" OXZ package and buy this equipment separatedly to confirm you want step over the rules of the standard game.
    This will increase the detection range based on the size of the target so you can lock onto it when the core game sets it to isVisible and shows at least a dot in the sky.
    For example you can see:
    -Adder at 32 km,
    -Viper and escort ships around 50 km (2x scanner range),
    -Anaconda, Boa, Cobra MkIII and Python about 100 km (4x scanner range),
    -Rock Hermit in almost 500 km (if not visible from the Main Station then fly around),
    -Coriolis Station at 1000 km (right from the witchpoint).
    
     Cost: 500.0 Cr.
     Techlevel: 5
    
    
    Gravity Scanner Equipment
    
    You must install the separated "Telescope Extender and Gravity Scanner" OXZ package to use this and the following uber-ranged equipments.
    
    If the mass of your ship more than 130t (Cobra MkIII and above) and there is a station within 5km, you can then extend the detection range of Telescope using a mass detector, which scales by third power of distance:
    the mass of the target in kg must be larger than d2*d2*d2/100 where d2 = distance*2 in km.
    
    Detection of the player ships and the Hard versions in HardShips OXP:
                     t    km   Hard t  km
    Adder           11    52    23     66
    Moray           40    79    81     100
    Cobra Mk I      47    84    94     106
    Fer-de-Lance    51    86    102    108
    Asp             59    90    118    114
    Cobra Mk III    186   132   371    167
    Boa             192   134   385    169
    Phyton          222   141   445    177
    Anaconda        430   175   1289   253
    
    Beacons are detected in the whole system and Rock Hermits usually as well (from 900km).
    
    A station needs to be near as the largest parts of the gravity scanner system is fitted into the stations, which broadcast some important data and it needs a large mass (at least 10.000t, the station itself) nearby as a reference to refine the results.
    Mobile bases can scan anywhere.
    
    It takes 4 minutes from undock or hyperjump to reach the maximal detection range when your ship is on the move.
    The detection progresses 4 times faster when your ship is stopped, needing only 60 seconds to finish.
    
    The mass is scaled with the elapsed time: an Anaconda is detected at half time as its half mass (215t) would, which means 140km, and needs more time to detect it from the maximal 175km.
    
    When the gravity detection is done and you remain at maximum speed until docking or jumping, the computer only needs to calculate in the new area coming into range from travel and not the whole sphere.
    
    The Gravity scanner works only when you turn off your weapons with underscore ("_") button, otherwise only visible targets are displayed.
    You can define a more comfortable key in the Oolite/oolite.app/Resources/Config/keyconfig.plist file.
    
    The auto-relock feature will continually change your target to the most centered one, so the box will jump during your turn; your ship helps browse the many targets. The ident ("r") button can lock the same target only in this mode, it cannot step to the second centered target.
    
    Gravity scan consume 8 energy points (visual scan uses only 2).
    You can hear the sound of the Gravity Scanner at work, which is to remind you of the energy usage.
    
    Orange and Brown lollipops and lightballs mark the detected targets out of the visible range.
    Brown if the ship is under 130t mass which means small ships, usually escorts - avoid Orange ones and target single Browns while your ship is not very strong.
    The mass usually cannot tell if a ship is pirate or not, coloured identification need visual contact.
    
    If you see a lighter ball then there are two or more ship in the same place.
    The smallest orange and brown ball means the target is farther than 150km, these get dark orange and dark brown lollipops.
    
    The Gravity Scanner cannot determine the orientation. If the target is not visible then the view position of the virtual model will be a fixed view from the top.
    
    There are no passive gravity sensors so AutoScan will happen only if a new target arrives into the visible range.
    When you undock or arrive at a new system, the telescope scan is performed automatically but if you want to scan the final frontier then you must turn off your weapons.
    
    Beware: aliens can sometimes detect the signal of a Gravity Scan, so if your ship is not strong then do not use it often and pay for the full repair if it's damaged.
    
     Cost: 10000.0 Cr.
     Techlevel: 5
    
    
    Secondary Gravity Scanner Equipment
    
    Cuts in half the time to get the full detection range and works as a single scanner if the primary one is damaged.
    Can only fit into ships over 400t mass (Anaconda and heavy OXP ships).
    
     Cost: 20000.0 Cr.
     Techlevel: 5
    
    
    Small Dish Equipment
    
    Detect ships from one third farther out (for example an Asp at 120km) but can only fit onto ships over 130t mass (from Cobra MkIII) due to the size.
    
    Needs Gravity Scanner, it is just a piece of metal.
    
    Fast CPUs can draw 200 targets without relevant FPS drop, slow systems draw it also but with some drop (tested on Intel Atom netbook). In this case scan for Gravity targets only when needed and turn it off when Telescope range is reached.
    
    This is not a primable equipment (there are no buttons on the pure alloy) and a passive extension so will not increase the energy usage of the scan.
    
     Cost: 5000.0 Cr.
     Techlevel: 5
    
    
    Large Dish Equipment
    
    Detect ships from double the range (an Asp at 180km) but can only fit onto ships over 400t mass (Anaconda and over) due to the size.
    
    Small Dish will not increase this range further so you can refund it when you buy a Large Dish.
    
    Ships over 1000t (Hard Anaconda and big OXP ships) can use the hull mass to refine the gravity signals to reach 4 times range than without Large Dish.
    
    Will only show the nearest 200 targets to save CPU and avoid serious FPS drops in systems with many ships.
    
     Cost: 10000.0 Cr.
     Techlevel: 5
    
    
    
    Cheap Repairs
    
    You can buy small fixes for 1/10 the cost of the new equipment instead of the normal 1/2 price but the following drawbacks will be applied:
    
    Telescope: get back the lightballs but will not fix the virtual model display, auto steering and sniper ring.
    Gravity Scanner: the cheap spare parts can interfere with the hyperdrive and often cause misjumps.
    Small and Large Dish: uses less durable alloys and can break during hyperjump.
    
    After a cheap fix you can buy the full repair which costs the difference between cheap and normal repair: 2/5 of the full price.
    You can only refund fully repaired equipment.
    
    
    Dependencies:
    Oolite v1.77 or later. No shaders needed.
    
    Instructions:
    Unzip the file, and then move the folder name ending in ".oxp" into the AddOns directory of your Oolite installation.
    Savegame included: put the "Telescope demo.oolite-save" file into the oolite-saves directory to load it.
    
    
    Settings in Scripts/telescope.js:
    
    $TelescopeAutoScan = true; //check continually for new isVisible isPiloted target and scan if found
    Scanning use a very little energy if a new target arrived but you strongly need this to avoid often scan manually.
    
    $TelescopeAutoScanMaxRange = 1000000; //meters, how far targets will be reported
    
    $TelescopeAutoLock = 1; //if no target and something in crosshairs with max. this degree diff., 1=lightball size, 0=off
    AutoLock is set to 1 degree (size of a lightball) and lock only if no current target to make it similar with the original ident function plus can lock far targets also. Disabled if set to 0 but in this case you can lock targets in 25.6km only.
    
    $TelescopeFarStatus = false; //red ball reveal pirates over normal scanner if true
    If false then bounty detected within 25.6km only which is more real and exciting but Thargoids get red ball at any range and a red ship will not change back to yellow if fly over the normal scanner range.
    
    $TelescopeGravLock = 20; //gravity scanner relock in this degree cone (0-180, 20=about the screen)
    Panorama targeting offer auto-relock to the most centered target during manual turning and can be switched on or off in-game frist with the weapons and second with the ident button when you see gravity targets. Disabled if set it to 0 but it is not recommended due to in this case you can not lock far targets, so set it to 1 degree at least, or simply use the ident button in-game to reduce it to the level of the AutoLock and leave the possibility to turn it on again.
    
    $TelescopeIdentLock = 180; //if ident pressed or target lost then lock in this degree (0-180, 180=the whole sphere)
    Can lock the most centered target even if behind you if set it to 180 and the second press will unlock it so you can clear the virtual model when not needed. If set it to 1 then the locking radius reduced to the size of a lightball so can do unlock only. Note the AutoLock will relock in 0.25 second if the target centered exactly, in this case turn a bit before unlock.
    
    Red alert is an exception where the IdentLock target hostiles only (who target you) to help find attackers. Can be override with AutoLock and GravLock, so if you unlock the current target and point exactly to another then the AutoLock will target it regardless of the hostile's status, or if you point it and turn weapons quickly off and on again then lock it regardless from you has lock on another target (but not too quickly, the timed function need max. 0.25 second to lock).
    
    $TelescopeLargeLightBalls = false; //lightballs are increasing depending on the distance or remains small
    
    $TelescopeLightBalls = true; //turn on or off all lightballs, but marks on the scanner will remain
    
    $TelescopeMassLockBorders = true; //coloured circles around ships and planets in green alert
    
    $TelescopeRedAlertDist = 30000; //show lollipops in red alert within this distance only
    
    $TelescopeRedAlertLimiter = false; //a bit more FPS in dogfight if true but show all marks with weapons off only
    
    $TelescopeRing = true; //show a ring around the visual target
    
    $TelescopeShipLightBalls = true; //turn on or off the lightballs with scanner markers of the ships, but cargo, etc. remain
    Non-ships with Blue, Cyan, Gray, Green and White colours will remain to help find these. Auto targeting functions still can lock far targets, the targeted ship are in an empty box.
    
    $TelescopeShowVisualQuestionMark = false; //if a ship has no virtual model in effecdata.plist show a big "?" model
    
    $TelescopeShowVisualStation = true; //show or not show the 3D model of the targeted station
    
    $TelescopeShowVisualTarget = true; //show the 3D model of the target if weapons are on (except stations if VisualStation is false)
    
    Set it to false if you want to use the weapon toggle button to show and hide virtual model also.
    
    $TelescopeSniperMinRange = 10000; //meters, show sniper ring if the target is over this distance
    
    $TelescopeSniperRange = 30000; //meters, if the target is inside then show sniper ring and large lightball
    Set it to to 25600 if you want to shorten the appearance of the sniper ring in the name of the fair play.
    
    $TelescopeSniperRingSize = 2; //size of the sniper ring (between 1 and 5, default: 2)
    
    $TelescopeSteering = 2; //auto steering if lock nearest or step in the target list with activate, 1:nearest only
    
    $TelescopeTargets = 200; //limitable to reduce FPS drop in systems with many ships, min. 10, max. 200
    
    $TelescopeThargoids = false; //you will get aliens right after undock to test Telescope
    
    $TelescopeVMarkMinDist = 1000; //meters, if target is inside then remove the lightball marker
    
    $TelescopeVMarkShipMinDist = 5000; //meters, if target is ship and inside then remove the lightball marker
    
    $TelescopeVPosHUD = [0, 0, 0]; //position shift for your HUD's built-in visual target screen if any
    
    $TelescopeVSize = 4; //size of the visual target with online weapons (between 0 and 10, default: 4)
    
    $TelescopeVZoomSize = 6; //zoomed size of the visual target with offline weapons (between 0 and 10, default: 6)
    
    If size is 0 then the visual target is not shown at all.
    
    
    Script_info support:
    
    OXP makers can alter the detection of any objects in shipdata.plist to avoid revealing mission secrets.
    
    Stations with non-standard roles and ships with the word "stealth" within their dataKey or role are detected in normal scanner range only. Must specify "telescope" script_info key to detect it farther to stay compatible with the existing OXPs, for example Rescue Stations.oxp, Stealth.oxp and Vector.oxp.
    
    Standard Station roles: "station", "coriolis", "dodo", "dodec", "dodecahedron", "ico", "icosa", "icosahedron" and "rockhermit".
    
    Hiding ships need "stealth" role or set telescope = 0; in script_info.
    
     script_info = {
     	telescope = 0;
     };
    
    * 0: detected within normal scanner only as without telescope.
    * 1: detected in visible range only (due to gravity scanner can see a ship with 1kg mass from 2km only).
    * Positive integer: give new mass to the ship in kg which can increase the gravity detection.
    * Negative integer: will be substracted from the ship.mass in kg to reduce gravity detection.
    
    If this key is not placed at all then get the normal detection.
    Objects with disabled detection (0) are not detected when arrive into visible range (need scanner range), but once detected then tracked over scanner range while in visible range until next scan (small help and save performance).
    
    
    Problems:
    If you do not see the model of a ship (or see a question mark if enabled) then you probably installed a custom ship OXP.
    The core game currently does not support making visual effects from ships directly, but there is a workaround: copy the Config/shipdata.plist files in your OXPs to effectdata.plist or insert into the full contents if exists to avoid overwrite.
    
    If the model still not appear and the definition of the ship using like_ship then copy the model = "filename.dat"; from the original ship into the section of this ship. Original cobras arrived after I do this only.
    
    If a custom ship use shaders with uniforms then you may see errors in the log but the visual target usually appear with less detail (need several core improvements to fix properly). To avoid the errors you can try commenting out the referred lines with // from the effectdata.plist or replace the inputs with fix numbers based on the wiki. For example the Griff Boa ( http://wiki.alioth.net/index.php/Griff_Boa ) is included in the effectdata.plist file of the Telescope OXP.
    
    If you ride a custom ship and the visual target is misaligned (not in the top center position) then copy the view_position values from your shipdata.plist into the $ShipLibViewPosition array in Scripts/shiplib.js .
    From Oolite v1.79 the player.ship.viewPositionForward property solve this.
    
    Opened wormholes are not auto targetable. You can put a box around with the standard ident but the script receive an empty target only. Can not get a pointer from the list of allShips nor allVisualEffects in v1.77, but from v1.79 the isWormhole flag will solve this.
    
    If a ship flys over a station then the lightball hides behind the station. To solve this need resizeable flashers which will be available from Oolite v1.79 only.
    
    Right after undock or hyperjump the first ident press can not target the most centered ship, only the second press. The first does not call any event, so you must press "r" again.
    
    Distant targets writes double message when locked, sometimes the first shows the name of the previous target. This seems to be a problem in the core game (tried to log it but only one log line created). Simply ignore the first message.
    
    
    License:
    This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License version 4.0.
    If you are re-using any piece of this OXP, please let me know by sending an e-mail to norbylite@gmail.com.
    ScanSound source: http://soundbible.com/878-Martian-Scanner.html
    
    Changelog:
     2017.06.24. v1.15 Further speed improvements by cag.
     2017.06.20. v1.14 Serious speed improvements and a fix for targeting boulders by cag.
     2015.05.25. v1.13 Asteroids are lockable with ident press not in the crosshairs also.
                       Selectable bright masslock borders.
     2014.10.28. v1.12 Masslock borders with very bright textures for fluxxx.
     2014.10.28. v1.11 Masslock border brightness is adjusted.
                       Default lightball size is smaller, except for large ships over 400t mass.
                       Very far targets over 1000km show a dot only.
                       Show hostile/offender/derelict flag in CombatMFD.
     2014.10.12. v1.10 A fix to prevent sudden target changes reported by Bogatyr.
     2014.10.11. v1.9  Show the list of the nearest 10 targets in a MFD.
                       The newest detected target is displayed in the CombatMFD if installed.
                       A small fix if Visual target:off and size is changed, thanks to Anthony.
     2014.07.06. v1.8  Fixed the restore of lightball settings after load game, thanks to Anthony.
                       Dark far lollipops in yellow alert except if weapons are offline.
                       Virtual target model is within a ring by default with offline weapons also.
                       Range extenders are separated into Telescope Extender and Gravity Scanner OXZ.
     2014.01.28. v1.7  Targets marked with dots from far distances for FarPlanets OXP.
                       Lock the nearest from overlapping targets within 0.5 degree.
                       $TelescopeRedAlertDist change lollipops over 30km to black in red alert.
                       Reduced chanche of timeLimit.
     2014.01.16. v1.6  Fixed reorientation during Asteroid hunting, thanks to Duggan.
                       Improvements for FarPlanets OXP.
                       Sun is added into the target list and got orange lollipop.
                       Planet lollipop color is changed to lightgray, masslockborder also.
                       Targets over $TelescopeSniperRange (30km) get tiny lightballs.
     2014.01.04. v1.5  Planets are targetable within scanner range also.
     2014.01.02. v1.4  Polished masslock borders and planet targets.
     2013.12.29. v1.3  Masslock borders: coloured circles around ships and planets.
                       Planets are included into the target list.
                       In Oolite 1.79 show visual target models of new ship graphics.
                       Thinner ring around visual target, smaller sniper ring.
     2013.11.02. v1.2  Gravity Scanner can fit from 130t mass again but restricted to use near stations.
                       Small Dish introduced, Large Dish need at least an Anaconda.
                       Ship lightballs minimal distance raised to 10km, cargo lightballs stay at 1km.
                       Lightball size of ships under 30t mass reduced to tiny.
                       Fixed infinite lost messages caused by piloted rocks in Lave.oxp.
     2013.10.22. v1.1  Double ident press will start auto steering to the target.
                       Need Telescope Extender to see over scanner range.
                       Gravity Scanner need ship with at least 400t mass.
                       Custom stations are lockable from 4x scanner range.
     2013.08.06. v1.0  Telescope configurable during fly: press mode key to cycle, activate to choose.
                       Settings stored into missionVariables and restored with load game.
                       Gravity Scanner is not primable anymore due to functions merged into Telescope.
     2013.08.03. v0.92 FCB speed almost doubled by Svengali, many thanks to him!
                       Maximal handled Telescope targets increased to 200.
                       False $TelescopeFarStatus to do not reveal pirates over normal scanner range.
                       Gravity scan need some time to calculate.
                       Added Secondary Gravity Scanner Equipment for faster scan in large ships.
                       $TelescopeLargeLightBalls set to false by default for small balls.
                       $TelescopeLightBalls can turn off lightballs but leave lollipops on the scanner.
                       $TelescopeShipLightBalls turn off ship lightballs only and show non-ships only.
     2013.07.23. v0.91 Cheap repairs get nice drawbacks.
                       Gravity scan sometimes detected by aliens.
                       FCB speed improvements to prevent Timelimit in trunk.
                       Fixed rescan bug if target has Military Jammer awarded by ShipVersion OXP.
                       Changed $TelescopeShowVisualTarget to always show the model in weapons off mode.
     2013.07.20. v0.90 Activate steer to the nearest target or to the nearest attacker in Red Alert.
                       Mode lock and steer to the most centered target or attacker in Red Alert.
                       Large lightballs added, show within $TelescopeSniperMinRange (10km).
                       Added telescope script_info key and hide custom Stations, thanks to Svengali.
                       Added $TelescopeThargoids if you want a test in instant action.
                       Fixed $TelescopeRedAlertLimiter, thanks to Solonar.
     2013.07.17. v0.86 Gravity Scanner cost increased, repair discounted.
                       Tharglet get small pink lightball until active.
     2013.07.16. v0.85 Debug version to Duggan and Solonar with many Tharglets and without crash.
     2013.07.15. v0.84 Internal updates to avoid timeLimit.
     2013.07.15. v0.83 Gravity Scanner range reduced and made another performance improvements.
     2013.07.14. v0.82 Added $TelescopeGravLock and $TelescopeIdentLock sensitivity in degree.
                       Added $TelescopeShowVisualStation and $TelescopeShowVisualQuestionMark.
                       Added is_external_dependency = yes; to griff boa, thanks to Svengali.
                       Bugfixes and preformance improvements.
                       Can lock asteroids in crosshairs with ident press.
     2013.07.11. v0.81 Added $TelescopeAutoLock, if 0 then must manually lock targets as before.
     2013.06.17. v0.8  Lightballs and shadow lollipop added.
     2013.06.10. v0.7  Visual targeting and Auto steering added.
     2013.05.05. v0.6  Minor fixes.
     2013.04.08. v0.5  First working version.
     2013.03.31. v0.1  First test files.
    

    cag/Norby.readme.again.cag.txt

    After posting in 'OXP Performance tips' about the speed gains of the function
    _index_in_list, I realized I forgot to back-port that little gem into the
    version I made for you.  So it's included in "telescope -after final.js", so
    you should see a few more fps.
    
    I also fixed a few minor bugs (HA! not) that I introduced (you didn't test it
    much, did you) and excluded docked escorts and the tow ship from Nearest,
    Steering and stepping through the List. No doubt Murphy will find a way to
    make me regret it!
    
    Unless someone (else) finds a bug(s), I'm going to stop here and move onto
    other things.
    =============================================================================
    Enclosed are the following:  ('*' indicate new or modified)
    
      telescope.orig.1.13.js            - copy of your last published version
      telescope -stage1.js             - after stage1 changes
      orig-stage1.diffs.txt            - output from DOS's FC (file compare)
      telescope -stage2.js             - after stage2 changes
      stage1-stage2.diffs.txt          - output from DOS's FC (file compare)
      telescope -final.js              - after final changes
      stage2-final.diffs.txt           - output from DOS's FC (file compare)
    * telescope -after final.js        - after more 'final' changes & bug fixes
    * after-final.diffs.txt            -  output from DOS's FC (file compare)
      Telescope 1.13 readme.cag.txt    - updated readme (grammar)
    * Norby.readme.txt                 - this file
    
    Let me preface this by saying I wanted to avoid changing anything in the logic
    or calculations in your code - that's how bugs are born!  Most edits were just
    house keeping and should not produce any behavioral changes.  I say 'most'
    because a) I did rewrite Telescope_MostCentered2 and b) some of the closures
    benefitted by moving functions inside.  I don't see any differences but, being
    much more familiar, you may.  
    
    stage1:
     - eliminate JSLint errors (220+)
       - if you're not using one, JSLint is a syntax checker that also reports
         strict usage violations and some basic speed improvements
       - mine is a plug-in to NotePad++
     - remove all 'delete' statements
       - a poorly named statement, delete actually removes a property from its object.
         This is an expensive op, esp. if you add it back on.  Setting the ref to null
    	 or some other value, is sufficient for garbage collection. eg.
    	 	delete ws.$TelescopeList; 	// not necessary
    		ws.$TelescopeList = [];		// reduces # references to previous array to 0
     - dynamic FCBs to replace 50 static ones
       - I discovered by accident that the 50 hard coded FCBs to ws.$TelescopeVFCBM 
         were costing me a few fps, so I replaced them w/ a dynamic scheme.  This 
    	 turned out to be tricky, as changes can take a few frames (5 on my PC) to
    	 take effect.  Adding a callback was fine, as this was done before processing
    	 the VFCBMarks, and I always had more than 16 in the List, doing 4/frame.  But
    	 removing a callback kept triggering my error checks until I realized the delay.
     - rewrote Telescope_MostCentered2
       - I usually equip my ship w/ a mining laser to port and was having trouble 
         targetting boulders. The problem is in the ordering of the subtracted entity 
    	 positons.  It seems backward to me but
    		ps.vectorForward.angleTo( psp.subtract( t.position ) )
    	 counts down from 180, whereas
    		ps.vectorForward.angleTo( t.subtract( psp.position ) )
    	 counts up from 0!  So, if you always .subtract( psp ) instead of psp.subtract(...,
    	 you get what you'd expect from angleTo and it makes coding easier.
    	 
    stage2:
      - here I placed 3 FCB ( _VFCB, _VFCB2, _VFCBVisualTarget ) inside closures to reduces
        the # of WorldScriptsGetProperty, ShipGetProperty & EntityGetProperty executed, as
    	they were usually the top time expenditures.  Function addresses and some global
    	variables do not change or the course of a game.  Those are stored in the closure's
    	scope and are only evaluated once, from the initiaton call in startUp.  In a few
    	cases, I was able to move global variables into a closure, as they were not 
    	referenced elsewhere.
    	I avoided doing _SteerFCB and _VFCBMarks, as I'm a chicken at heart! (I have not
    	dealt with _SteerFCB at all so far and you had already done _VFCBMarks).
    
    final:
      - here I placed _TimedS in a closure along with 3 functions, as they were called no
        where else: _IsPilotedVisible, _IsNonHostileStaion, _MFDTarget
    	I also folded _IsScannerTarget into _IsPilotedVisible to eliminate repeated property 
    	checks. 
      - I added a few more locals to _TimedVMC() and cut a third off its execution time
    
    I'm sure _List3 could also benefit but I failed (twice) trying to put it in a closure 
    (I introduced bugs) and so reverted to its original form.
    
    frame rates (fps):
      - with no Addons, telescope demo varied
        from 58 - 85, using ver.1.13
          to 74 -112, using final
      - with 156 Addons, telescope demo varied
        from 18 - 30, using ver.1.13
          to 23 - 36, using final
      - with 156 Addons, telescope demo, my re-write varies between 35 - 48 fps
      
    Given the numbers above, I think it best for me to halt optimizing current telescope code
    and continue with my re-write.  Unless you find a bug (or 2) in my edits but you wouldn't,
    would you? ;->  
    

    cag/Norby.readme.cag.txt

    Enclosed are the following:
    
    telescope.orig.1.13.js			- copy of your last published version
    telescope -stage1.js			- after stage1 changes
    orig-stage1.diffs.txt			- output from DOS's FC (file compare)
    telescope -stage2.js			- after stage2 changes
    stage1-stage2.diffs.txt			- output from DOS's FC (file compare)
    telescope -final.js				- after final changes
    stage2-final.diffs.txt			- output from DOS's FC (file compare)
    Telescope 1.13 readme.cag.txt	- updated readme (grammar)
    Norby.readme.txt				- this file
    
    Let me preface this by saying I wanted to avoid changing anything in the logic
    or calculations in your code - that's how bugs are born!  Most edits were just
    house keeping and should not produce any behavioral changes.  I say 'most'
    because a) I did rewrite Telescope_MostCentered2 and b) some of the closures
    benefitted by moving functions inside.  I don't see any differences but, being
    much more familiar, you may.  
    
    stage1:
     - eliminate JSLint errors (220+)
       - if you're not using one, JSLint is a syntax checker that also reports
         strict usage violations and some basic speed improvements
       - mine is a plug-in to NotePad++
     - remove all 'delete' statements
       - a poorly named statement, delete actually removes a property from its object.
         This is an expensive op, esp. if you add it back on.  Setting the ref to null
    	 or some other value, is sufficient for garbage collection. eg.
    	 	delete ws.$TelescopeList; 	// not necessary
    		ws.$TelescopeList = [];		// reduces # references to previous array to 0
     - dynamic FCBs to replace 50 static ones
       - I discovered by accident that the 50 hard coded FCBs to ws.$TelescopeVFCBM 
         were costing me a few fps, so I replaced them w/ a dynamic scheme.  This 
    	 turned out to be tricky, as changes can take a few frames (5 on my PC) to
    	 take effect.  Adding a callback was fine, as this was done before processing
    	 the VFCBMarks, and I always had more than 16 in the List, doing 4/frame.  But
    	 removing a callback kept triggering my error checks until I realized the delay.
     - rewrote Telescope_MostCentered2
       - I usually equip my ship w/ a mining laser to port and was having trouble 
         targetting boulders. The problem is in the ordering of the subtracted entity 
    	 positons.  It seems backward to me but
    		ps.vectorForward.angleTo( psp.subtract( t.position ) )
    	 counts down from 180, whereas
    		ps.vectorForward.angleTo( t.subtract( psp.position ) )
    	 counts up from 0!  So, if you always .subtract( psp ) instead of psp.subtract(...,
    	 you get what you'd expect from angleTo and it makes coding easier.
    	 
    stage2:
      - here I placed 3 FCB ( _VFCB, _VFCB2, _VFCBVisualTarget ) inside closures to reduces
        the # of WorldScriptsGetProperty, ShipGetProperty & EntityGetProperty executed, as
    	they were usually the top time expenditures.  Function addresses and some global
    	variables do not change or the course of a game.  Those are stored in the closure's
    	scope and are only evaluated once, from the initiaton call in startUp.  In a few
    	cases, I was able to move global variables into a closure, as they were not 
    	referenced elsewhere.
    	I avoided doing _SteerFCB and _VFCBMarks, as I'm a chicken at heart! (I have not
    	dealt with _SteerFCB at all so far and you had already done _VFCBMarks).
    
    final:
      - here I placed _TimedS in a closure along with 3 functions, as they were called no
        where else: _IsPilotedVisible, _IsNonHostileStaion, _MFDTarget
    	I also folded _IsScannerTarget into _IsPilotedVisible to eliminate repeated property 
    	checks. 
      - I added a few more locals to _TimedVMC() and cut a third off its execution time
    
    I'm sure _List3 could also benefit but I failed (twice) trying to put it in a closure 
    (I introduced bugs) and so reverted to its original form.
    
    frame rates (fps):
      - with no Addons, telescope demo varied
        from 58 - 85, using ver.1.13
          to 74 -112, using final
      - with 156 Addons, telescope demo varied
        from 18 - 30, using ver.1.13
          to 23 - 36, using final
      - with 156 Addons, telescope demo, my re-write varies between 35 - 48 fps
      
    Given the numbers above, I think it best for me to halt optimizing current telescope code
    and continue with my re-write.  Unless you find a bug (or 2) in my edits but you wouldn't,
    would you? ;->  
    

    cag/Telescope 1.13 readme.cag.txt

    Telescope forum topic: http://bb.aegidian.org/viewtopic.php?f=4&t=14961
    
    Quick start as a Hunter
    
    * Copy the .oxp folder to the AddOns directory, hold down the Shift when starting the game the first time after copying.
    * Buy Telescope with Extender equipment or load the included savegame ("Telescope demo.oolite-save"). Injectors, ECM System, Fuel Scoop, Military Laser and Scanner Targeting Enhancement are used also in this guide.
    * After undock (F1) you can see the magnified target in top center position.
    * If you bought Gravity Scanner then stop near the station, turn off your weapons with underscore (_) and wait for the scan results.
    * Turn your ship where you see lollipops at the edge of your IFF scanner. The Panorama targeting will countinually change the box to the most centered target.
    * Choose your enemy for the strength of your ship: start with a small ship flying alone.
    * Use Injectors (i) to fly into scanner range where you can see a Red ball which means visible ship with bounty. Do not hunt Yellow traders and Purple police ships if you want to remain in clean status.
    * Use Ctrl+arrow keys to put the target into the middle of the crosshairs to turn on the sniper ring which magnify the difference from the correct line-up.
    * Turn on your weapons (_) and fire (a). You can try to hit with Military laser over normal scanner range but within 30km and a stopped target will turn when successful, but if your target is too small then fly inside 25.6km where the red target box indicate the correct line-up.
    * Press ident (r) to lock the most centered target if there are more, press again to start auto steering or to lock another if it's more centered than the current target.
    * Telescope can be primed (Shift+N) then activated (n) to lock the nearest target, mode (b) change the function of the activate key.
    * If a missile is coming then a cyan ball warns you: start ECM (e), target it (t), try to avoid (i) or shoot it (a).
    * If you win and the pilot ejected then shot down the derelict ship to get the bounty, then fly after the small White balls and scoop the Escape Pod, metal fragments and cargo pods.
    * Fly back to the station and dock to get the reward for the pilot and sell the cargo (F8). Congratulations, you are richer. :)
    
    
    Telescope Equipment
    
    This OXP realizes some requests for an extended scanner described in this topic:
    http://bb.aegidian.org/viewtopic.php?f=6&t=13274
    Can determine the direction, distance, orientation and legal status of all visible ships.
    
    The detection range is equal with the scanner range if you use it without the Telescope Extender (still or willfully), but you can use the following features:
    
    Visual targeting
    
    The virtual model of the target ship is displayed and a console message shows the name, range and direction.
    The direction is marked by <^ or v> symbols to read faster than the Port Up or Down Starboard words.
    The ranges and directions are calculated continually to help turn in line and see the decreasing range while traveling to the target.
    The orientation of the model is equal with the target if you centered it and you can spin the model if turn around your ship.
    You can set the size of the model with the $TelescopeVSize variable in the Scripts/telescope.js.
    
    Lightballs
    
    Far targets get coloured lightball markers in the view and lollipops with darker colours near the edge of the scanner.
    
    Red: Hostile ship with bounty (pirates and thargoids),
    Pink: Tharglet until active and Drones with bounty in HardShips OXP,
    Yellow: Neutral ship with clean status (traders and co),
    Purple: Police,
    Cyan: Missile or Mine,
    Green: Buoy or Station,
    Blue: Derelict (Wormhole also from Oolite v1.79),
    White: Cargo, Escape Pod or Sun,
    Gray: last known position of a lost target (moved out of telescope range),
    Lightgray: Planet or Moon (a bit darker than Planet),
    Orange: Gravity Scanner detected ship too far to be visible,
    Brown: Gravity Scanner detected ship under 130t mass (less dangerous, especially with ShipVersion OXP).
    Black: lollipops in red alert over 30km to help focus on near targets.
    
    Beacons are identified in the whole system due to the transmitted radio signals.
    If a ship transmits a beaconcode then it's detected anywhere but coloured as a ship and turns to orange if too far to be visible.
    
    The current target is also marked with a gray "shadow" lollipop at the edge of the IFF scanner to help determine the direction, especially at close range, to avoid zooming the scanner. 
    If the target is closer than 1000m, the lightball will be removed as the ship almost covers the ball and cargo pods are large enough large to detect with your eyes, but the shadow lollipop still remain.
    You can set the minimum distance with the $TelescopeVMarkMinDist variable in the file Scripts/telescope.js.
    
    Targets within the range of the Military Laser (30km) are marked with a larger ball and a normal (not darker) coloured lollipop.
    You can use this feature to determine exactly where you can open fire on the target so it's large enough to stand a chance of being hit.
    
    You can disable the lighballs of the ships by setting $TelescopeLightBalls to false, but non-ships with Blue, Cyan, Gray, Green and White colours will remain to help find these. Auto targeting functions can still lock far targets; there seems to be an empty box but the ships are in there.
    
    You can restrict lighballs in Red Alert to 10-30km if you set $TelescopeRedAlertLimiter to true to save a few CPU cycles but it is worth it on slow computers only.
    
    Sniper ring
    
    If you have almost lined up with your target who is between 10 and 30km then a ring will appear.
    The movement of this ring magnifies the variance to the correct line-up, to help fine tune your aim.
    You can hit if the target box of the Scanner Targeting Enhancement switches to red.
    
    Snipers can use the distances between 25.6 and 30km to fire before the enemy can see the attacker on its scanner.
    This was possible without Telescope, but the size of the ball gives another aid to knowing when the target runs out of range; and the ring can guide your aim without red box, which only works in the normal scanner range.
    
    If you think this is a cheat then fire only when the target can also see you (red box) or set the $TelescopeSniperRange variable to 25600 which will shorten the appearance of the ring and the largest ball (but there's no way to shorten the range of the Military Laser).
    
    Auto steering
    
    With the Telescope primed (Shift+N) the activate button ("n") can steer to the nearest lightball-marked target, which is useful in picking up cargo from the near field, rather than zooming the scanner to find them. Now you can just press activate instead of hitting zoom, locate, turn and unzoom.
    
    Auto-steering starts only if your ship flys in a line (not turning) and stops instantly if you touch the controls. It will stop steering before a perfect line-up so as not to aim for you.
    
    In Red Alert the activate button steers to the nearest attacker. You can disable this by turning off the weapons, in which case you can lock and steer to any near target regardless of the alert condition.
    
    Auto scanning
    
    Telescope checks for new targets every second and performs an autoscan if it finds one: simple light sensors can see new dots in the whole sky without using energy but must zoom with the main scope to determine the ship type which needs 2 energy points.
    The autoscan can be turned off if you set $TelescopeAutoScan false in the telescope.js file but it's usually worth the cost to get new information sooner.
    
    Far target locking
    
    You can lock far targets also which is not in the normal scanner (beacons and planets are always lockable). The distance is shown before the name of the ship with Scanner Targeting Enhancement, due to the fact that the second line shows the range of the virtual marker (which is always near the edge of the IFF Scanner).
    We must use this workaround because the core game does not allow locking onto a target outside of normal scanner range. So the locked marker coinsides with the lock of the target (can only fix this in the core game).
    
    If you cannot see a ship then it may have been destroyed, jumped out or flew farther than the visible range; or it has a Military Scanner Jammer and you do not have a Jammer Filter so you cannot get target lock.
    
    When a target flys too far and is lost, the telescope renames it to 'Lost target' until the next scan.
    
    Masslock borders
    
    In green alert you will see circles around detected targets where these ships can block your Torus Drive.
    Without a Telescope Extender, these are shown around planets and stations only, but if you buy one, it allows for detection of ships that you can  see with your Mark-1 eyeball (escort ships at 2x, traders near 4x scanner range) and you will then get circles around all the ships before you.
    
    Until you enter these circles you will stay in condition green, but cloaked ships can cause surprises.
    
    The colour of a masslock border is the same as the lightball in the middle (see above).
    
    To get the shortest route, you can safely go forward near the largest circle without touching it or going inside. Imagine this is the border of a sphere so if you fly direct to the center then you will be masslocked sooner than if you flew to a point of the circle. If you steer just a little outside the circle, you will fly forward as near as possible without masslock.
    
    These are also a good indicator of the distance to the ships because the radius of the circles represent exactly one scanner range for the ship at its center.
    Planets have rings that are double its radius as they can masslock you within this range.
    The sun also has a large masslock field but this is not displayed by rings - it's less important and mainly a distraction.
    
    You can also turn on masslock borders in non-green alerts if you turn off your weapons.
    You can turn off all circles completely with the Telescope primed to the Lightballs menu (see below).
    
    Keypress functions
    
    The ident button ("r") gets a new feature from Telescope: it can lock the most centered target even if it's not shown on the screen.
    The next press will start auto steering to the target if the most centered target is the same, otherwise it will lock onto the new target; a third ident press will unlock it.
    In Red Alert it will not narrow the locking to the attackers; it can lock any target.
    
    Works only if there was a locked target beforehand, due to the triggering of the shipTargetLost event, so if there's no target it won't get called. Press "r" again or get something into the crosshair or use equipment buttons to lock a target. (Usually only an issue when leaving a station, exitting witchspace or Torus drive.)
    
    If you turn off the weapons with the underscore button ("_") then a scan happens and you enter into "Navigation Mode", where autolock helps you see through targets (called Panorama targeting): continually relocks to the most centered target.
    This button is choosed to avoid unwanted fire if you have turrets.
    In Red Alert you can lock any target and see far targets if you turn this mode on.
    The Ident button presses can turn Panorama targeting on and off when in Navigation Mode.
    
    With the Telescope primed (Shift+N), the mode button ("b") cycles through the functions of the activate ("n") button:
    
     Nearest target
     Rescan
     Step forward in the target list
     Step back in the target list
     Lightballs: off / navigation only / ships / masslock borders / large
     Sniper ring km: off / 5-25.6 / 10-25.6 / 15-25.6 / 5-30 / 10-30 / 15-30
     Steering: off / nearest target only / both nearest and step in the list
     Targets: 20 and limitation in red alert / 50 / 100 / 200
     Visual target: off / weapons off / no ring / no station / no question mark / all
     Visual target size: 1-8
    
    The first 4 functions are commands which happen instantly when activated.
    The "Step forward" and "Step back" will rescan if you step over the end of the target list.
    The Target list contains hostiles first, if any, then all ships in normal scanner (25.6km), followed by Cargo and Escape Pods, then ending with ships which are not in the normal scanner.
    
    The last 6 functions are some of customizable properties in the telescope.js file, which you can during flight.
    Your settings are stored into missionVariables and saved when you save your game after docking.
    
    [When the core game provides more equipment buttons, a back button could step back to the previous function (maybe Ctrl+"b").
    With a second equipment button (maybe Ctrl+"n") settings could be separated from the commands.]
    
     Cost: 500.0 Cr.
     Techlevel: 5
    
    
    Telescope Extender
    
    You must install "Telescope Extender and Gravity Scanner" OXZ package and buy this equipment separatedly to confirm you want step over the rules of the standard game.
    This will increase the detection range based on the size of the target so you can lock onto it when the core game sets it to isVisible and shows at least a dot in the sky.
    For example you can see:
    -Adder at 32 km,
    -Viper and escort ships around 50 km (2x scanner range),
    -Anaconda, Boa, Cobra MkIII and Python about 100 km (4x scanner range),
    -Rock Hermit in almost 500 km (if not visible from the Main Station then fly around),
    -Coriolis Station at 1000 km (right from the witchpoint).
    
     Cost: 500.0 Cr.
     Techlevel: 5
    
    
    Gravity Scanner Equipment
    
    You must install the separated "Telescope Extender and Gravity Scanner" OXZ package to use this and the following uber-ranged equipments.
    
    If the mass of your ship more than 130t (Cobra MkIII and above) and there is a station within 5km, you can then extend the detection range of Telescope using a mass detector, which scales by third power of distance:
    the mass of the target in kg must be larger than d2*d2*d2/100 where d2 = distance*2 in km.
    
    Detection of the player ships and the Hard versions in HardShips OXP:
                     t    km   Hard t  km
    Adder           11    52    23     66
    Moray           40    79    81     100
    Cobra Mk I      47    84    94     106
    Fer-de-Lance    51    86    102    108
    Asp             59    90    118    114
    Cobra Mk III    186   132   371    167
    Boa             192   134   385    169
    Phyton          222   141   445    177
    Anaconda        430   175   1289   253
    
    Beacons are detected in the whole system and Rock Hermits usually as well (from 900km).
    
    A station needs to be near as the largest parts of the gravity scanner system is fitted into the stations, which broadcast some important data and it needs a large mass (at least 10.000t, the station itself) nearby as a reference to refine the results.
    Mobile bases can scan anywhere.
    
    It takes 4 minutes from undock or hyperjump to reach the maximal detection range when your ship is on the move.
    The detection progresses 4 times faster when your ship is stopped, needing only 60 seconds to finish.
    
    The mass is scaled with the elapsed time: an Anaconda is detected at half time as its half mass (215t) would, which means 140km, and needs more time to detect it from the maximal 175km.
    
    When the gravity detection is done and you remain at maximum speed until docking or jumping, the computer only needs to calculate in the new area coming into range from travel and not the whole sphere.
    
    The Gravity scanner works only when you turn off your weapons with underscore ("_") button, otherwise only visible targets are displayed.
    You can define a more comfortable key in the Oolite/oolite.app/Resources/Config/keyconfig.plist file.
    
    The auto-relock feature will contiunually change your target to the most centered one, so the box will jump during your turn; your ship helps browse the many targets. The ident ("r") button can lock the same target only in this mode, it cannot step to the second centered target.
    
    Gravity scan consume 8 energy points (visual scan uses only 2).
    You can hear the sound of the Gravity Scanner at work, which is to remind you of the energy usage.
    
    Orange and Brown lollipops and lightballs mark the detected targets out of the visible range.
    Brown if the ship is under 130t mass which means small ships, usually escorts - avoid Orange ones and target single Browns while your ship is not very strong.
    The mass usually cannot tell if a ship is pirate or not, coloured identification need visual contact.
    
    If you see a lighter ball then there are two or more ship in the same place.
    The smallest orange and brown ball means the target is farther than 150km, these get dark orange and dark brown lollipops.
    
    The Gravity Scanner cannot determine the orientation. If the target is not visible then the view position of the virtual model will be a fixed view from the top.
    
    There are no passive gravity sensors so AutoScan will happen only if a new target arrives into the visible range.
    When you undock or arrive at a new system, the telescope scan is performed automatically but if you want to scan the final frontier then you must turn off your weapons.
    
    Beware: aliens can sometimes detect the signal of a Gravity Scan, so if your ship is not strong then do not use it often and pay for the full repair if it's damaged.
    
     Cost: 10000.0 Cr.
     Techlevel: 5
    
    
    Secondary Gravity Scanner Equipment
    
    Cuts in half the time to get the full detection range and works as a single scanner if the primary one is damaged.
    Can only fit into ships over 400t mass (Anaconda and heavy OXP ships).
    
     Cost: 20000.0 Cr.
     Techlevel: 5
    
    
    Small Dish Equipment
    
    Detect ships from one third farther out (for example an Asp at 120km) but can only fit onto ships over 130t mass (from Cobra MkIII) due to the size.
    
    Needs Gravity Scanner, it is a just piece of metal.
    
    Fast CPUs can draw 200 targets without relevant FPS drop, slow systems draw it also but with some drop (tested on Intel Atom netbook). In this case scan for Gravity targets only when needed and turn it off when Telescope range is reached.
    
    This is not a primable equipment (there are no buttons on the pure alloy) and a passive extension so will not increase the energy usage of the scan.
    
     Cost: 5000.0 Cr.
     Techlevel: 5
    
    
    Large Dish Equipment
    
    Detect ships from double the range (an Asp at 180km) but can only fit onto ships over 400t mass (Anaconda and over) due to the size.
    
    Small Dish will not increase this range further so you can refund it when you buy a Large Dish.
    
    Ships over 1000t (Hard Anaconda and big OXP ships) can use the hull mass to refine the gravity signals to reach 4 times range than without Large Dish.
    
    Will only show the nearest 200 targets to save CPU and avoid serious FPS drops in systems with many ships.
    
     Cost: 10000.0 Cr.
     Techlevel: 5
    
    
    
    Cheap Repairs
    
    You can buy small fixes for 1/10 the cost of the new equipment instead of the normal 1/2 price but the following drawbacks will be applied:
    
    Telescope: get back the lightballs but will not fix the virtual model display, auto steering and sniper ring.
    Gravity Scanner: the cheap spare parts can interfere with the hyperdrive and often cause misjumps.
    Small and Large Dish: usees less durable alloys and can break during hyperjump.
    
    After a cheap fix you can buy the full repair which costs the difference between cheap and normal repair: 2/5 of the full price.
    You can only refund fully repaired equipment.
    
    
    Dependencies:
    Oolite v1.77 or later. No shaders needed.
    
    Instructions:
    Unzip the file, and then move the folder name ending in ".oxp" into the AddOns directory of your Oolite installation.
    Savegame included: put the "Telescope demo.oolite-save" file into the oolite-saves directory to load it.
    
    
    Settings in Scripts/telescope.js:
    
    $TelescopeAutoScan = true; //check continually for new isVisible isPiloted target and scan if found
    Scanning use a very little energy if a new target arrived but you strongly need this to avoid often scan manually.
    
    $TelescopeAutoScanMaxRange = 1000000; //meters, how far targets will be reported
    
    $TelescopeAutoLock = 1; //if no target and something in crosshairs with max. this degree diff., 1=lightball size, 0=off
    AutoLock is set to 1 degree (size of a lightball) and lock only if no current target to make it similar with the original ident function plus can lock far targets also. Disabled if set to 0 but in this case you can lock targets in 25.6km only.
    
    $TelescopeFarStatus = false; //red ball reveal pirates over normal scanner if true
    If false then bounty detected within 25.6km only which is more real and exciting but Thargoids get red ball at any range and a red ship will not change back to yellow if fly over the normal scanner range.
    
    $TelescopeGravLock = 20; //gravity scanner relock in this degree cone (0-180, 20=about the screen)
    Panorama targeting offer auto-relock to the most centered target during manual turning and can be switched on or off in-game frist with the weapons and second with the ident button when you see gravity targets. Disabled if set it to 0 but it is not recommended due to in this case you can not lock far targets, so set it to 1 degree at least, or simply use the ident button in-game to reduce it to the level of the AutoLock and leave the possibility to turn it on again.
    
    $TelescopeIdentLock = 180; //if ident pressed or target lost then lock in this degree (0-180, 180=the whole sphere)
    Can lock the most centered target even if behind you if set it to 180 and the second press will unlock it so you can clear the virtual model when not needed. If set it to 1 then the locking radius reduced to the size of a lightball so can do unlock only. Note the AutoLock will relock in 0.25 second if the target centered exactly, in this case turn a bit before unlock.
    
    Red alert is an exception where the IdentLock target hostiles only (who target you) to help find attackers. Can be override with AutoLock and GravLock, so if you unlock the current target and point exactly to another then the AutoLock will target it regardless of the hostile's status, or if you point it and turn weapons quickly off and on again then lock it regardless from you has lock on another target (but not too quickly, the timed function need max. 0.25 second to lock).
    
    $TelescopeLargeLightBalls = false; //lightballs are increasing depending on the distance or remains small
    
    $TelescopeLightBalls = true; //turn on or off all lightballs, but marks on the scanner will remain
    
    $TelescopeMassLockBorders = true; //coloured circles around ships and planets in green alert
    
    $TelescopeRedAlertDist = 30000; //show lollipops in red alert within this distance only
    
    $TelescopeRedAlertLimiter = false; //a bit more FPS in dogfight if true but show all marks with weapons off only
    
    $TelescopeRing = true; //show a ring around the visual target
    
    $TelescopeShipLightBalls = true; //turn on or off the lightballs with scanner markers of the ships, but cargo, etc. remain
    Non-ships with Blue, Cyan, Gray, Green and White colours will remain to help find these. Auto targeting functions still can lock far targets, the targeted ship are in an empty box.
    
    $TelescopeShowVisualQuestionMark = false; //if a ship has no virtual model in effecdata.plist show a big "?" model
    
    $TelescopeShowVisualStation = true; //show or not show the 3D model of the targeted station
    
    $TelescopeShowVisualTarget = true; //show the 3D model of the target if weapons are on (except stations if VisualStation is false)
    
    Set it to false if you want to use the weapon toggle button to show and hide virtual model also.
    
    $TelescopeSniperMinRange = 10000; //meters, show sniper ring if the target is over this distance
    
    $TelescopeSniperRange = 30000; //meters, if the target is inside then show sniper ring and large lightball
    Set it to to 25600 if you want to shorten the appearance of the sniper ring in the name of the fair play.
    
    $TelescopeSniperRingSize = 2; //size of the sniper ring (between 1 and 5, default: 2)
    
    $TelescopeSteering = 2; //auto steering if lock nearest or step in the target list with activate, 1:nearest only
    
    $TelescopeTargets = 200; //limitable to reduce FPS drop in systems with many ships, min. 10, max. 200
    
    $TelescopeThargoids = false; //you will get aliens right after undock to test Telescope
    
    $TelescopeVMarkMinDist = 1000; //meters, if target is inside then remove the lightball marker
    
    $TelescopeVMarkShipMinDist = 5000; //meters, if target is ship and inside then remove the lightball marker
    
    $TelescopeVPosHUD = [0, 0, 0]; //position shift for your HUD's built-in visual target screen if any
    
    $TelescopeVSize = 4; //size of the visual target with online weapons (between 0 and 10, default: 4)
    
    $TelescopeVZoomSize = 6; //zoomed size of the visual target with offline weapons (between 0 and 10, default: 6)
    
    If size is 0 then the visual target is not shown at all.
    
    
    Script_info support:
    
    OXP makers can alter the detection of any objects in shipdata.plist to avoid revealing mission secrets.
    
    Stations with non-standard roles and ships with the word "stealth" within their dataKey or role are detected in normal scanner range only. Must specify "telescope" script_info key to detect it farther to stay compatible with the existing OXPs, for example Rescue Stations.oxp, Stealth.oxp and Vector.oxp.
    
    Standard Station roles: "station", "coriolis", "dodo", "dodec", "dodecahedron", "ico", "icosa", "icosahedron" and "rockhermit".
    
    Hiding ships need "stealth" role or set telescope = 0; in script_info.
    
     script_info = {
     	telescope = 0;
     };
    
    * 0: detected within normal scanner only as without telescope.
    * 1: detected in visible range only (due to gravity scanner can see a ship with 1kg mass from 2km only).
    * Positive integer: give new mass to the ship in kg which can increase the gravity detection.
    * Negative integer: will be substracted from the ship.mass in kg to reduce gravity detection.
    
    If this key is not placed at all then get the normal detection.
    Objects with disabled detection (0) are not detected when arrive into visible range (need scanner range), but once detected then tracked over scanner range while in visible range until next scan (small help and save performance).
    
    
    Problems:
    If you do not see the model of a ship (or see a question mark if enabled) then you probably installed a custom ship OXP.
    The core game currently does not support making visual effects from ships directly, but there is a workaround: copy the Config/shipdata.plist files in your OXPs to effectdata.plist or insert into the full contents if exists to avoid overwrite.
    
    If the model still not appear and the definition of the ship using like_ship then copy the model = "filename.dat"; from the original ship into the section of this ship. Original cobras arrived after I do this only.
    
    If a custom ship use shaders with uniforms then you may see errors in the log but the visual target usually appear with less detail (need several core improvements to fix properly). To avoid the errors you can try commenting out the referred lines with // from the effectdata.plist or replace the inputs with fix numbers based on the wiki. For example the Griff Boa ( http://wiki.alioth.net/index.php/Griff_Boa ) is included in the effectdata.plist file of the Telescope OXP.
    
    If you ride a custom ship and the visual target is misaligned (not in the top center position) then copy the view_position values from your shipdata.plist into the $ShipLibViewPosition array in Scripts/shiplib.js .
    From Oolite v1.79 the player.ship.viewPositionForward property solve this.
    
    Opened wormholes are not auto targetable. You can put a box around with the standard ident but the script receive an empty target only. Can not get a pointer from the list of allShips nor allVisualEffects in v1.77, but from v1.79 the isWormhole flag will solve this.
    
    If a ship flys over a station then the lightball hides behind the station. To solve this need resizeable flashers which will be available from Oolite v1.79 only.
    
    Right after undock or hyperjump the first ident press can not target the most centered ship, only the second press. The first does not call any event, so you must press "r" again.
    
    Distant targets writes double message when locked, sometimes the first shows the name of the previous target. This seems to be a problem in the core game (tried to log it but only one log line created). Simply ignore the first message.
    
    
    License:
    This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License version 4.0.
    If you are re-using any piece of this OXP, please let me know by sending an e-mail to norbylite@gmail.com.
    ScanSound source: http://soundbible.com/878-Martian-Scanner.html
    
    Changelog:
     2015.05.25. v1.13 Asteroids are lockable with ident press not in the crosshairs also.
                       Selectable bright masslock borders.
     2014.10.28. v1.12 Masslock borders with very bright textures for fluxxx.
     2014.10.28. v1.11 Masslock border brightness is adjusted.
                       Default lightball size is smaller, except for large ships over 400t mass.
                       Very far targets over 1000km show a dot only.
                       Show hostile/offender/derelict flag in CombatMFD.
     2014.10.12. v1.10 A fix to prevent sudden target changes reported by Bogatyr.
     2014.10.11. v1.9  Show the list of the nearest 10 targets in a MFD.
                       The newest detected target is displayed in the CombatMFD if installed.
                       A small fix if Visual target:off and size is changed, thanks to Anthony.
     2014.07.06. v1.8  Fixed the restore of lightball settings after load game, thanks to Anthony.
                       Dark far lollipops in yellow alert except if weapons are offline.
                       Virtual target model is within a ring by default with offline weapons also.
                       Range extenders are separated into Telescope Extender and Gravity Scanner OXZ.
     2014.01.28. v1.7  Targets marked with dots from far distances for FarPlanets OXP.
                       Lock the nearest from overlapping targets within 0.5 degree.
                       $TelescopeRedAlertDist change lollipops over 30km to black in red alert.
                       Reduced chanche of timeLimit.
     2014.01.16. v1.6  Fixed reorientation during Asteroid hunting, thanks to Duggan.
                       Improvements for FarPlanets OXP.
                       Sun is added into the target list and got orange lollipop.
                       Planet lollipop color is changed to lightgray, masslockborder also.
                       Targets over $TelescopeSniperRange (30km) get tiny lightballs.
     2014.01.04. v1.5  Planets are targetable within scanner range also.
     2014.01.02. v1.4  Polished masslock borders and planet targets.
     2013.12.29. v1.3  Masslock borders: coloured circles around ships and planets.
                       Planets are included into the target list.
                       In Oolite 1.79 show visual target models of new ship graphics.
                       Thinner ring around visual target, smaller sniper ring.
     2013.11.02. v1.2  Gravity Scanner can fit from 130t mass again but restricted to use near stations.
                       Small Dish introduced, Large Dish need at least an Anaconda.
                       Ship lightballs minimal distance raised to 10km, cargo lightballs stay at 1km.
                       Lightball size of ships under 30t mass reduced to tiny.
                       Fixed infinite lost messages caused by piloted rocks in Lave.oxp.
     2013.10.22. v1.1  Double ident press will start auto steering to the target.
                       Need Telescope Extender to see over scanner range.
                       Gravity Scanner need ship with at least 400t mass.
                       Custom stations are lockable from 4x scanner range.
     2013.08.06. v1.0  Telescope configurable during fly: press mode key to cycle, activate to choose.
                       Settings stored into missionVariables and restored with load game.
                       Gravity Scanner is not primable anymore due to functions merged into Telescope.
     2013.08.03. v0.92 FCB speed almost doubled by Svengali, many thanks to him!
                       Maximal handled Telescope targets increased to 200.
                       False $TelescopeFarStatus to do not reveal pirates over normal scanner range.
                       Gravity scan need some time to calculate.
                       Added Secondary Gravity Scanner Equipment for faster scan in large ships.
                       $TelescopeLargeLightBalls set to false by default for small balls.
                       $TelescopeLightBalls can turn off lightballs but leave lollipops on the scanner.
                       $TelescopeShipLightBalls turn off ship lightballs only and show non-ships only.
     2013.07.23. v0.91 Cheap repairs get nice drawbacks.
                       Gravity scan sometimes detected by aliens.
                       FCB speed improvements to prevent Timelimit in trunk.
                       Fixed rescan bug if target has Military Jammer awarded by ShipVersion OXP.
                       Changed $TelescopeShowVisualTarget to always show the model in weapons off mode.
     2013.07.20. v0.90 Activate steer to the nearest target or to the nearest attacker in Red Alert.
                       Mode lock and steer to the most centered target or attacker in Red Alert.
                       Large lightballs added, show within $TelescopeSniperMinRange (10km).
                       Added telescope script_info key and hide custom Stations, thanks to Svengali.
                       Added $TelescopeThargoids if you want a test in instant action.
                       Fixed $TelescopeRedAlertLimiter, thanks to Solonar.
     2013.07.17. v0.86 Gravity Scanner cost increased, repair discounted.
                       Tharglet get small pink lightball until active.
     2013.07.16. v0.85 Debug version to Duggan and Solonar with many Tharglets and without crash.
     2013.07.15. v0.84 Internal updates to avoid timeLimit.
     2013.07.15. v0.83 Gravity Scanner range reduced and made another performance improvements.
     2013.07.14. v0.82 Added $TelescopeGravLock and $TelescopeIdentLock sensitivity in degree.
                       Added $TelescopeShowVisualStation and $TelescopeShowVisualQuestionMark.
                       Added is_external_dependency = yes; to griff boa, thanks to Svengali.
                       Bugfixes and preformance improvements.
                       Can lock asteroids in crosshairs with ident press.
     2013.07.11. v0.81 Added $TelescopeAutoLock, if 0 then must manually lock targets as before.
     2013.06.17. v0.8  Lightballs and shadow lollipop added.
     2013.06.10. v0.7  Visual targeting and Auto steering added.
     2013.05.05. v0.6  Minor fixes.
     2013.04.08. v0.5  First working version.
     2013.03.31. v0.1  First test files.
    

    Equipment

    Name Visible Cost [deci-credits] Tech-Level
    Telescope yes 5000 5+
    Repair Telescope yes 2000 5+
    Refund Telescope yes 0 5+
    Repair Telescope targeting only yes 500 5+

    Ships

    Name
    Telescope marker

    Models

    This expansion declares no models.

    Scripts

    Path
    Scripts/largeonly.js
    "use strict";
    this.name        = "largeonly";
    this.author      = "Norby";
    this.copyright   = "2013 Norbert Nagy";
    this.licence     = "CC BY-NC-SA 3.0";
    this.description = "This equipment is usable only for ships over 130t like the Cobra III or Rocket Miner.";
    this.version     = "1.0";
    
    this.allowAwardEquipment = function(eqKey, ship, context)
    {
    //	player.consoleMessage( eqKey+" "+ship+" "+context );//debug
    	if( ship.mass >= 130000 ) return true;
    	else return false;
    }
    
    Scripts/notforsmall.js
    "use strict";
    this.name        = "notforsmall";
    this.author      = "Norby";
    this.copyright   = "2013 Norbert Nagy";
    this.licence     = "CC BY-NC-SA 3.0";
    this.description = "This equipment can not fit into small ships like the Adder or Rocket Fighter.";
    this.version     = "1.0";
    
    this.allowAwardEquipment = function(eqKey, ship, context)
    {
    //bugfix for eqs with conditions scripts: need double replaceShip() to work!
    //player.replaceShip(shipname, pers); player.replaceShip(shipname, pers);
    //call twice! the first always got false for allow without run this script.
    //Proof: if you uncomment the following line, only the 2nd call will write into the log.
    //log("notforsmall", eqKey+" "+ship.name+" "+ship.mass+" "+context );
    
    	if( ship.mass >= 30000 ) return true;
    	else return false;
    }
    
    Scripts/shiplib.js
    "use strict";
    this.name        = "shiplib";
    this.author      = "Norby";
    this.copyright   = "2013 Norbert Nagy";
    this.licence     = "CC BY-NC-SA 3.0";
    this.description = "Contains data about player ships.";
    this.version     = "1.0";
    
    //please configure these to your custom ship if not in the list below
    this.$ShipLibViewPosition = { //must be equal with view_position_* of your ship in the shipdata.plist
    	aft:[0.0, 7.5, -32.5], forward:[0.0, 7.25, 16.25], port:[-30.0, 4.75, 0.0], starboard:[30.0, 4.75, 0.0]};
    	
    //internal data, should not touch
    this.$ShipLibVP = [
    	{k:"default", a:this.$ShipLibViewPosition.aft, f:this.$ShipLibViewPosition.forward,
    		p:this.$ShipLibViewPosition.port, s:this.$ShipLibViewPosition.starboard},
    	//original player ships
    	{k:"adder", a:[0.0, 2.5, -22.5], f:[0.0, 2.5, 14.0], p:[-12.0, 1.5, -1.0], s:[12.0, 1.5, -1.0]},
    	{k:"anaconda", a:[0.0, -4.0, -80.0], f:[0.0, -0.5, 70.0], p:[-2.15, -0.5, 70.0], s:[2.15, -0.5, 70.0]},
    	{k:"asp", a:[0.0, 5.0, -35.0], f:[0.0, 5.31, 17.5], p:[-27.0, 5.0, 0.0], s:[27.0, 5.0, 0.0]},
    	{k:"boa", a:[0.0, 20.5, -50.75], f:[0.0, 16.25, 23.9], p:[-7.3, 16.25, -23.9], s:[7.3, 16.25, -23.9]},
    	{k:"boa-mk2", a:[0.0, 22.875, -47.375], f:[0.0, 12.5, 19.4375], p:[-20.67, 11.17, -14.67], s:[20.67, 11.17, -14.67]},
    	{k:"cobra", a:[0.0, 7.5, -32.5], f:[0.0, 7.25, 16.25], p:[-30.0, 4.75, 0.0], s:[30.0, 4.75, 0.0]},
    	{k:"cobra3", a:[0.0, 7.5, -32.5], f:[0.0, 7.25, 16.25], p:[-30.0, 4.75, 0.0], s:[30.0, 4.75, 0.0]},
    	{k:"cobramk1", a:[0.0, 3.75, -27.5], f:[0.0, 2.925, 15.125], p:[-13.0, 3.75, 0.0], s:[13.0, 3.75, 0.0]},
    	{k:"ferdelance", a:[0.0, 5.0, -32.5], f:[0.0, 0.0, 9.0], p:[-16.875, 2.0, 3.5], s:[16.875, 2.0, 3.5]},
    	{k:"moray", a:[0.0, 8.875, 5.5], f:[0.0, 4.4, 22.5], p:[-22.5, 5.25, 11.875], s:[22.5, 5.25, 11.875]},
    	{k:"morayMED", a:[0.0, 8.875, 5.5], f:[0.0, 4.4, 22.5], p:[-22.5, 5.25, 11.875], s:[22.5, 5.25, 11.875]},
    	{k:"python", a:[0.0, 15.0, -49.5], f:[0.0, 10.0, 31.5], p:[-16.0, 4.0, 15.2], s:[16.0, 4.0, 15.2]},
    	//custom ships
    	{k:"base", a:[0.0, 500.0, -400.0], f:[0.0, 3.0, 320.0], p:[-60.0, 3.0, 0.0], s:[60.0, 3.0, 0.0]},
    	{k:"corvette", a:[0.0, 6.0, -16.5], f:[0.0, 3.0, 16.5], p:[-5.0, 2.0, 0.0], s:[5.0, 2.0, 0.0]},
    	{k:"cruiser", a:[0.0, 3.0, -120.0], f:[0.0, 3.0, 120.0], p:[-60.0, 3.0, 0.0], s:[60.0, 3.0, 0.0]},
    	{k:"drone", a:[0.0, 5.0, -6.5], f:[0.0, 3.0, 9.5], p:[-2.5, 2.0, 0.0], s:[2.5, 2.0, 0.0]},
    	{k:"freighter", a:[0.0, 3.0, -120.0], f:[0.0, 3.0, 120.0], p:[-60.0, 3.0, 0.0], s:[60.0, 3.0, 0.0]},
    	{k:"frigate", a:[0.0, 30.0, -50.0], f:[0.0, 3.0, 50.0], p:[-30.0, 0.0, 0.0], s:[30.0, 0.0, 0.0]},
    	{k:"fighter", a:[0.0, 6.0, -16.5], f:[0.0, 3.0, 16.5], p:[-5.0, 2.0, 0.0], s:[5.0, 2.0, 0.0]},
    	{k:"gunship", a:[0.0, 6.0, -16.5], f:[0.0, 3.0, 16.5], p:[-5.0, 2.0, 0.0], s:[5.0, 2.0, 0.0]},
    	{k:"miner", a:[0.0, 30.0, -50.0], f:[0.0, 3.0, 50.0], p:[-30.0, 0.0, 0.0], s:[30.0, 0.0, 0.0]},
    	{k:"runner", a:[0.0, 6.0, -16.5], f:[0.0, 3.0, 16.5], p:[-5.0, 2.0, 0.0], s:[5.0, 2.0, 0.0]},
    	];
    
    Scripts/telescope.js
    "use strict";
    this.name        = "telescope";
    this.author      = "Norby";
    this.copyright   = "2013 Norbert Nagy";
    this.licence     = "CC BY-NC-SA 4.0";
    this.description = "Telescope mark all visible ships, show vitrual model, sniper ring and more.";
    
    //customizable properties
    this.$TelescopeAutoScan = true; //check continually for new isVisible isPiloted target and scan if found
    this.$TelescopeAutoScanMaxRange = 1000000; //meters, how far targets will be reported
    this.$TelescopeAutoLock = 1; //if no target and something in crosshairs with max. this degree diff., 1=lightball size, 0=off
    this.$TelescopeFarStatus = false; //red ball reveal pirates over normal scanner if true
    this.$TelescopeGravLock = 20; //gravity scanner relock in this degree cone (0-180, 20=about the screen)
    this.$TelescopeIdentLock = 180; //if ident pressed or target lost then lock in this degree (0-180, 180=the whole sphere)
    this.$TelescopeLargeLightBalls = false; //lightballs are increasing depending on the distance or remains small
    this.$TelescopeLightBalls = true; //turn on or off all lightballs, but markes on the scanner will be remain
    this.$TelescopeMassLockBorders = true; //coloured circles around ships and planets in green alert
    this.$TelescopeBrightMassLockBorders = false; //brighter circles around ships and planets in green alert
    this.$TelescopeRedAlertDist = 30000; //show lollipops in red alert within this distance only
    this.$TelescopeRedAlertLimiter = false; //a bit more FPS in dogfight if true but show all marks with weapons off only
    this.$TelescopeRing = true; //show a ring around the visual target
    this.$TelescopeShipLightBalls = true; //turn on or off the lightballs with scanner markers of the ships, but cargo, etc. remain
    this.$TelescopeShowVisualQuestionMark = false; //if a ship has no visual model in effecdata.plist show a big "?" model
    this.$TelescopeShowVisualStation = true; //show or not show the 3D model of the targeted station
    this.$TelescopeShowVisualTarget = true; //show 3D model of target if weapons are on (except stations if VisualStation false)
    this.$TelescopeSniperMinRange = 10000; //meters, show sniper ring if the target is over this distance
    this.$TelescopeSniperRange = 30000; //meters, if the target is inside then show sniper ring and large lightball
    this.$TelescopeSniperRingSize = 2; //size of the sniper ring (between 1 and 5, default: 2)
    this.$TelescopeSteering = 2; //auto steering if lock nearest or step in the target list with activate, 1:nearest only
    this.$TelescopeTargets = 200; //limitable to reduce FPS drop in systems with many ships, min. 4, max. 200 due to VFCBM0-VFCBM49
    this.$TelescopeThargoids = false; //you will get some aliens right after undock to test Telescope
    this.$TelescopeVMarkMinDist = 3000; //meters, if target is inside then remove the lightball marker
    this.$TelescopeVMarkShipMinDist = 5000; //meters, if target is ship and inside this range then remove the lightball marker
    this.$TelescopeVPosHUD = [0, 0, 0]; //position shift for your HUD's built-in visual target screen if any
    this.$TelescopeVSize = 4; //size of the visual target with online weapons (between 0 and 10, default: 4)
    this.$TelescopeVZoomSize = 6; //zoomed size of the visual target with offline weapons (between 0 and 10, default: 6)
    
    //internal properties, should not touch
    this.$TelescopeAttackers = []; //ships attacked player regardless of success
    this.$TelescopeAttackeri = 0; //store the sequential number of the last attacker in the target list
    this.$TelescopeBuyMsg = true; //flag to show the buy message once
    this.$TelescopeCMFD = null; //store the worldScripts pointer of Combart MFD
    this.$TelescopeDataKeys77 = ["adder", //dataKeys with 77 suffix in effectdata.plist for Oolite 1.77
    	"alloy", "anaconda", "arc-detail", "asp", "asp-cloaked", "asteroid", "asteroid-alternative",
    	"oolite-cinder", "oolite-cinder-alternative", "oolite-cinder-small", "oolite-cinder-small-alternative",
    	"barrel","boa", "boa-mk2", "boulder", "boulder-alternative", "buoy", "buoy-witchpoint",
    	"cloaking-device", "cobra3-trader", "cobra3-alternate", "cobra3-pirate", "cobramk1",
    	"cobramk1-alt", "cobramk1-miner", "constrictor", "coriolis-station", "dock",
    	"dock-flat", "oolite-dock-virtual", "dodecahedron-station", "ecm-proof-missile",
    	"escape-capsule", "ferdelance", "gecko", "hermit-docking-slit", "hermitage", "icosahedron-station",
    	"krait", "mamba", "mamba-escort", "missile", "moray", "morayMED", "oolite-unknown-ship",
    	"python", "python-blackdog", "python-trader", "qbomb", "rock-hermit", "scarred-alloy",
    	"shuttle", "sidewinder", "sidewinder-escort", "splinter", "splinter-alternative", "strut",
    	"tharglet", "thargoid", "transporter", "transporter-miner", "viper", "viper-interceptor",
    	"viper-pursuit", "worm", "worm-miner", "wreckage-component", "more-wreckage2",
    	"more-wreckage3", "more-wreckage4", "more-wreckage5", "ballturret"];
    this.$TelescopeFixedTel = 0; //cheaply fixed Telescope with drawbacks
    this.$TelescopeFixedGS = 0; //cheaply fixed Gravity Scanner with drawbacks
    this.$TelescopeFixedSD = 0; //cheaply fixed Small Dish with drawbacks
    this.$TelescopeFixedLD = 0; //cheaply fixed Large Dish with drawbacks
    this.$TelescopeGSC = 0; //Gravity Scan counter to call aliens
    this.$TelescopeGSP = 0; //Gravity Scan percent counter store the progress of the gravity scan
    this.$TelescopeIdentUnLock = false; //flag to steering started by ident, next press will unlock
    this.$TelescopeList = []; //list of the scanned ships
    this.$TelescopeListFar = []; //list of the scanned far ships
    this.$TelescopeListFarPos = []; //positions of the scanned far ships at the moment of the scan
    this.$TelescopeListGD = []; //gravity scanner maximal detection distances of the scanned ships
    this.$TelescopeListPos = []; //positions of the scanned ships at the moment of the scan
    this.$TelescopeListi = 0; //sequential number of the currently selected ship in the list
    this.$TelescopeMaxRange = 1000000000000000; //10^15m, usable part of double precision for filteredEntities
    this.$TelescopeMMarks = []; //visual effects to mark the masslocking fields around NPC ships
    this.$TelescopeMRings = []; //mark the border of masslock fields
    this.$TelescopeNearestd = this.$TelescopeMaxRange; //store the distance of the nearest ship in the list (updated in TimedVMC)
    this.$TelescopeNearesti = 0; //store the sequential number of the nearest ship in the list (updated in TimedVMC)
    this.$TelescopePlanetNames = []; //cache of names
    this.$TelescopePrevHeading = []; //heading vector of the player ship in the previous frame to detect manual steering
    this.$TelescopePrevNewTarget = null; //bugfix against repeated scan if a ship can not get into the list by any reason
    this.$TelescopePrevMFDTarget = null; //support for Combat MFD
    this.$TelescopePrevTargetAcq = null; //bugfix against repeated scan if a ship can not get into the list by any reason
    this.$TelescopePrevWho = null; //bugfix against non-lockable Military Jammer awarded by ShipVersion to avoid repeated scan
    this.$TelescopePrevPlanet = null; //bugfix against target switch but name remain old if new planet is within scanner range
    this.$TelescopeReds = []; //store red ships detected earlier
    this.$TelescopeSoundScan = null; //scan soundsource
    this.$TelescopeStaionNearby = true; //store a station is nearby for gravity scanner
    this.$TelescopeSteerFCB = null; //auto steering framecallback
    //this.$TelescopeSniperRing = null; //if this ring guided around the crosshair then the far target is lined up correctly
    this.$TelescopeSVSync = false; //flag to synhronize the visual effect during auto steering
    this.$TelescopeTarget = null; //the targeted entity, needed when target is far and player target set to the markership
    this.$TelescopeTargetSet = false; //flag scanning to avoid double scan
    this.$TelescopeGravLock2 = this.$TelescopeGravLock; //ident press will switch this between 1 and GravLock
    this.$TelescopeTimerA = null; //set player target a bit later to avoid relock the ship in crosshair (bugfix)
    this.$TelescopeTimerF = null; //delayed launch of FCBs, must after FarPlanets FCB
    this.$TelescopeTimerS = null; //AutoScan timer get targets from normal scanner and do scan if new far target in the front view
    this.$TelescopeTWS = null; //store the worldScripts pointer of Towbar
    //this.$TelescopeV = null; //visual effect to show the selected target
    //this.$TelescopeVDataKey = null; //key of the visual effect
    this.$TelescopeVFCB = null; //visual model and ring positions updated in these framecallbacks
    this.$TelescopeVFCB2 = null; //visual model and ring positions updated in these framecallbacks part 2 to avoid timelimit
    this.$TelescopeVFCB3 = null; //visual model and ring positions updated in these framecallbacks part 3 to avoid timelimit
    this.$TelescopeVFCBM = []; //visual marker positions updated in these framecallbacks
    this.$TelescopeVMark = null; //visual effect to mark the target
    this.$TelescopeVMarks = []; //visual effects to mark the non-current targets
    //this.$TelescopeVMCC = 0; //counter to make colour of the visual marks, used to do once in a second within a 0.25s timer
    this.$TelescopeVMCs = []; //colour of the visual marks
    this.$TelescopeVP = { //default view_positions if shiplib.js is missing
    	aft:[0.0, 7.5, -32.5], forward:[0.0, 7.25, 16.25], port:[-30.0, 4.75, 0.0], starboard:[30.0, 4.75, 0.0]};
    this.$TelescopeVPos = null; //position of the visual effect
    //this.$TelescopeVRing = null; //a ring around the visual effect target
    //this.$TelescopeVShrinkC = 0; //visual effect shrink counter - shrink code is not ready, leave these at 0
    //this.$TelescopeVShrinkDelay = 0; //in sec after the larger visual target start shrinkig to normal size (0: instant)
    //this.$TelescopeVShrinkLength = 0; //in sec, shrinking faster if smaller (0: instant)
    //this.$TelescopeVUp = 0; //visual effect align to top modifier
    //this.$TelescopeWps = true; //the previous state of the player weapons
    //this.$TelescopeZoom = 1; //store the original maximal zoom level of the visual effect
    
    //world script events
    this.startUp = function() {
    	var h = worldScripts.hudselector;
    	if( h && h.$HUDSelectorAddMFD ) h.$HUDSelectorAddMFD(this.name);
    	var ps = player.ship;
    	if( ps.setMultiFunctionText )
    		ps.setMultiFunctionText(this.name, "No Telescope Target", false);
    
    	this.$TelescopeSoundScan = new SoundSource();
    	this.$TelescopeSoundScan.sound = "ScanSound.ogg"; //sound of the Gravity Scanner
    	this.$TelescopeSoundScan.loop = false;
    	this.$TelescopeSoundScan.repeatCount = 1;
    	this.$TelescopeBuyMsg = true; //show again after reload
    	this.$TelescopePlanetNames = []; //cache of names
    	this.$TelescopeReds = []; //store red ships detected earlier
    	this.$TelescopeCMFD = worldScripts.combat_MFD; //store the worldScripts pointer of Combart MFD
    	this.$TelescopeTWS = worldScripts.towbar; //store the worldScripts pointer of Towbar
    	
    	var subitem = missionVariables.$TelescopeMenuLightballs;
    	if( subitem > 0 ) this.$Telescope_SetLightballs( subitem );
    	subitem = missionVariables.$TelescopeMenuSniper;
    	if( subitem > 0 ) this.$Telescope_SetSniper( subitem );
    	subitem = missionVariables.$TelescopeMenuSteering;
    	if( subitem > 0 ) this.$Telescope_SetSteering( subitem );
    	subitem = missionVariables.$TelescopeMenuTargets;
    	if( subitem > 0 ) this.$Telescope_SetTargets( subitem );
    	subitem = missionVariables.$TelescopeMenuVisual;
    	if( subitem > 0 ) this.$Telescope_SetVisual( subitem );
    	subitem = missionVariables.$TelescopeMenuVisualSize;
    	if( subitem > 0 ) this.$Telescope_SetVisualSize( subitem );
    	
    	var ws = worldScripts.telescope;
    	ws._FBC_closures();
     	if( ws.$FBC_closures )								// once done
    		delete ws._FBC_closures;						// remove ability to create a 2nd ones!
    	else
    		log('telescope', 'startUp, failed to create $FBC_closures!' );
    }
    
    this.$FBC_closures = false;
    this._FBC_closures = function() {	// create closure & expose functions
    	var ws = worldScripts.telescope;
    	ws.$Telescope_TimedS = ws._TimedS_closure();
    	ws.$Telescope_VFCBVisualTarget = ws._VFCBVisualTarget_closure();
    	
    	var vc = ws._VFCB_closure();
    	ws.$Telescope_VClearS = vc.clear;
    	ws.$Telescope_VShow = vc.show;
    	ws.$Telescope_VFCB = vc.fcb;
    
    	vc = ws._VFCB2_closure();
    	ws._clearSniperRing = vc.clear;
    	ws.$Telescope_VFCB2 = vc.fcb;
    
    	ws.$FBC_closures = true;
    }
    
    this.equipmentDamaged = this.equipmentDestroyed = function(equipment) {
    	if( equipment === "EQ_TELESCOPE" || equipment === "EQ_GRAVSCANNER"
    		|| equipment === "EQ_GRAVSCANNER2" || equipment === "EQ_SMALLDISH" || equipment === "EQ_LARGEDISH" )
    		worldScripts.telescope.$Telescope_NewList(); //remove lost targets, autoscan will scan again
    }
    
    this.equipmentRepaired = function(equipment) {
    	//Do not use "false" in missionVariables! Will return true! Use 0 and 1 instead!
    	if(equipment === ("EQ_TELESCOPE"))
    		this.$TelescopeFixedTel = missionVariables.$TelescopeFixedTel = 0;
    	else if(equipment === ("EQ_GRAVSCANNER"))
    		this.$TelescopeFixedGS = missionVariables.$TelescopeFixedGS = 0;
    	else if(equipment === ("EQ_GRAVSCANNER2"))
    		this.$TelescopeFixedGS = missionVariables.$TelescopeFixedGS = 0;
    	else if(equipment === ("EQ_SMALLDISH"))
    		this.$TelescopeFixedLD = missionVariables.$TelescopeFixedSD = 0;
    	else if(equipment === ("EQ_LARGEDISH"))
    		this.$TelescopeFixedLD = missionVariables.$TelescopeFixedLD = 0;
    }
    
    this.playerBoughtEquipment = function(equipmentKey) {
    	var ps = player.ship;
    	if(equipmentKey === ("EQ_TELESCOPE"))
    		this.$TelescopeFixedTel = missionVariables.$TelescopeFixedTel = 0;
    	else if(equipmentKey === ("EQ_GRAVSCANNER"))
    		this.$TelescopeFixedGS = missionVariables.$TelescopeFixedGS = 0;
    	else if(equipmentKey === ("EQ_GRAVSCANNER2"))
    		this.$TelescopeFixedGS = missionVariables.$TelescopeFixedGS = 0;
    	else if(equipmentKey === ("EQ_SMALLDISH"))
    		this.$TelescopeFixedSD = missionVariables.$TelescopeFixedSD = 0;
    	else if(equipmentKey === ("EQ_LARGEDISH"))
    		this.$TelescopeFixedLD = missionVariables.$TelescopeFixedLD = 0;
    	else if(equipmentKey === ("EQ_TELESCOPE_REPAIR"))      {
    		ps.setEquipmentStatus("EQ_TELESCOPE", "EQUIPMENT_OK");
    		ps.removeEquipment("EQ_TELESCOPE_REPAIR");
    		clock.addSeconds("3600");
    		this.$TelescopeFixedTel = missionVariables.$TelescopeFixedTel = 1; //with drawback (lightballs only)
    	}
    	else if(equipmentKey === ("EQ_TELESCOPE_FULLREPAIR"))      {
    		ps.removeEquipment("EQ_TELESCOPE_FULLREPAIR");
    		clock.addSeconds("3600");
    		this.$TelescopeFixedTel = missionVariables.$TelescopeFixedTel = 0;
    	}
    	else if(equipmentKey === ("EQ_GRAVSCANNER_REPAIR"))      {
    		ps.setEquipmentStatus("EQ_GRAVSCANNER", "EQUIPMENT_OK");
    		ps.removeEquipment("EQ_GRAVSCANNER_REPAIR");
    		clock.addSeconds("3600");
    		this.$TelescopeFixedGS = missionVariables.$TelescopeFixedGS = 1; //with drawback (misjump)
    	}
    	else if(equipmentKey === ("EQ_GRAVSCANNER2_REPAIR"))      {
    		ps.setEquipmentStatus("EQ_GRAVSCANNER2", "EQUIPMENT_OK");
    		ps.removeEquipment("EQ_GRAVSCANNER2_REPAIR");
    		clock.addSeconds("3600");
    		this.$TelescopeFixedGS = missionVariables.$TelescopeFixedGS = 1; //with drawback (misjump)
    	}
    	else if(equipmentKey === ("EQ_GRAVSCANNER_FULLREPAIR") || equipmentKey === ("EQ_GRAVSCANNER2_FULLREPAIR") ) {
    		ps.removeEquipment(equipmentKey);
    		clock.addSeconds("3600");
    		this.$TelescopeFixedGS = missionVariables.$TelescopeFixedGS = 0;
    	}
    	else if(equipmentKey === ("EQ_SMALLDISH_REPAIR"))      {
    		ps.setEquipmentStatus("EQ_SMALLDISH", "EQUIPMENT_OK");
    		ps.removeEquipment("EQ_SMALLDISH_REPAIR");
    		clock.addSeconds("3600");
    		this.$TelescopeFixedSD = missionVariables.$TelescopeFixedSD = 1; //with drawback (break during jump)
    	}
    	else if(equipmentKey === ("EQ_LARGEDISH_REPAIR"))      {
    		ps.setEquipmentStatus("EQ_LARGEDISH", "EQUIPMENT_OK");
    		ps.removeEquipment("EQ_LARGEDISH_REPAIR");
    		clock.addSeconds("3600");
    		this.$TelescopeFixedLD = missionVariables.$TelescopeFixedLD = 1; //with drawback (break during jump)
    	}
    	else if(equipmentKey === ("EQ_SMALLDISH_FULLREPAIR"))      {
    		ps.removeEquipment("EQ_SMALLDISH_FULLREPAIR");
    		clock.addSeconds("3600");
    		this.$TelescopeFixedSD = missionVariables.$TelescopeFixedSD = 0;
    	}
    	else if(equipmentKey === ("EQ_LARGEDISH_FULLREPAIR"))      {
    		ps.removeEquipment("EQ_LARGEDISH_FULLREPAIR");
    		clock.addSeconds("3600");
    		this.$TelescopeFixedLD = missionVariables.$TelescopeFixedLD = 0;
    	}
    	else if( equipmentKey.substr(-7,7) === "_REFUND" ) { 
    		//any eq which end with _REFUND means full refund the eq named before
    		ps.removeEquipment( equipmentKey ); //remove the "bought" refund eq
    		var eq = equipmentKey.substr( 0, equipmentKey.length-7 );//the real eq
    		if( ps.equipmentStatus( eq ) === "EQUIPMENT_OK" )
    			worldScripts.telescope.$Telescope_RefundEQ( eq );
    		else if( ps.equipmentStatus( eq ) === "EQUIPMENT_DAMAGED" ) //need check to avoid message at refund
    			player.consoleMessage("Need repair first"); //do not get full price back for a damaged eq
    		return;
    	}
    }
    
    this.shipBeingAttacked = function(whom) {
    	if( whom && whom.isValid ) {
    		var ws = worldScripts.telescope;
    		if( ws._index_in_list( whom, ws.$TelescopeAttackers ) === -1 )
    			ws.$TelescopeAttackers.push(whom); //add
    		var i = ws._index_in_list( whom, ws.$TelescopeList );
    		if( i > -1 ) ws.$TelescopeAttackeri = i + 1; //save last attacker
    	}
    }
    
    this.shipBeingAttackedUnsuccessfully = function(whom) {
    	if( whom && whom.isValid ) {
    		var ws = worldScripts.telescope;
    		if( ws._index_in_list( whom, ws.$TelescopeAttackers ) === -1 )
    			ws.$TelescopeAttackers.push(whom); //add
    		var ai = ws.$TelescopeAttackeri;
    		if( ai > 0 ) {
    			var a = ws.$TelescopeList[ ai - 1 ]; //if previous attacker is destroyed or too far
    			if( !a || !a.isValid || !a.isVisible ) ai = ws.$TelescopeAttackeri = 0;
    		}
    		if( ai === 0 ) {
    			var i = ws._index_in_list( whom, ws.$TelescopeList );
    			if( i > -1 ) ws.$TelescopeAttackeri = i + 1; //save attacker
    		}
    	}
    }
    
    this.shipKilledOther = function(whom /*,damageType*/) {
    	var ws = worldScripts.telescope;
    	var i = ws._index_in_list( whom, ws.$TelescopeList );
    	if( i > -1 ) ws.$Telescope_Scan();//forced rescan to remove from the list
    }
    
    this.shipLaunchedFromStation = function() {
    	var ps = player.ship;
    	this.$TelescopeFixedTel = missionVariables.$TelescopeFixedTel;
    //	if(this.$TelescopeFixedTel) log("Telescope", "FixedTel "+this.$TelescopeFixedTel);
    	this.$TelescopeFixedGS = missionVariables.$TelescopeFixedGS;
    //	if(this.$TelescopeFixedGS) log("Telescope", "FixedGS "+this.$TelescopeFixedGS);
    	this.$TelescopeFixedSD = missionVariables.$TelescopeFixedSD;
    //	if(this.$TelescopeFixedSD) log("Telescope", "FixedSD "+this.$TelescopeFixedSD);
    	this.$TelescopeFixedLD = missionVariables.$TelescopeFixedLD;
    //	if(this.$TelescopeFixedLD) log("Telescope", "FixedLD "+this.$TelescopeFixedLD);
    	this.$TelescopeGSC = missionVariables.$TelescopeGSC; //Gravity Scan counter to call aliens
    	if( !this.$TelescopeGSC ) this.$TelescopeGSC = 0; //start to count gravity scans
    	this.$TelescopeGSP = 0; //begin new gravity detection process
    	var ws = worldScripts.telescope;
    	ws.$Telescope_NewList();
    	ws.$TelescopeAttackers = [];
    //	if( ps.equipmentStatus("EQ_TELESCOPE") == "EQUIPMENT_OK" ) {
    //		if( ps && ps.weaponsOnline 
    //			&& ps.equipmentStatus("EQ_GRAVSCANNER") == "EQUIPMENT_OK" )
    //			player.consoleMessage("Turn off weapons for Gravity Scan.",10);
    ////		ws.$Telescope_Scan();//forced rescan
    //	}
    
    //	if( ws.$TelescopeV && ws.$TelescopeV.isValid ) {
    //;		log("Telescope", "shipLaunchedFromStation V:"+ws.$TelescopeV);//debug
    		ws.$Telescope_VClear();
    //		ws.$TelescopeV.remove();
    //		var i = system.allVisualEffects.indexOf( this.$TelescopeV );
    //		if( i > -1 ) system.allVisualEffects[i].remove();
    //	}
    	if( ps.viewPositionForward ) { //from oolite v1.79
    		ws.$TelescopeVPos = ps.viewPositionForward;
    	} else  {
    		var p = new Vector3D(0,0,0);
    		if( worldScripts.shiplib && worldScripts.shiplib.$ShipLibVP ) {
    			var l = worldScripts.shiplib.$ShipLibVP; //view_position
    			var d = 0; //0. line contains the default data
    			for( var i = 1; i < l.length; i++ )
    				if( ps.dataKey && l && l[i]
    					&& ws._index_in_list( l[i].k, ps.dataKey ) > -1 ) {
    					d = i; //found
    					i = l.length; //end of for
    				}
    //			player.consoleMessage(l[d].f[0]+" "+l[d].f[1]+" "+l[d].f[2]);//debug
    			p = new Vector3D( l[d].f[0], l[d].f[1], l[d].f[2] );
    		} else {
    			var p1 = ws.$TelescopeVP;
    			p = new Vector3D( p1.forward[0], p1.forward[1], p1.forward[2] );
    		}
    //		player.consoleMessage(p,10);//debug
    		ws.$TelescopeVPos = p; //save corrected position
    	}
    //	if( worldScripts["genericHUDswitch - MilHUD.js"] ) { //was in MilHUD 0.9, removed in 0.92
    //		ws.$TelescopeVPos = ws.$TelescopeVPos.add([20,-25,70]);
    //		ws.$TelescopeRing = false; //do not show the ring around visual target
    //		ws.$TelescopeVSize = 1; //zoomed size of the visual target
    //		ws.$TelescopeVZoomSize = 1; //zoomed size of the visual target
    //	} else
    	ws.$TelescopeVPos = ws.$TelescopeVPos.add(ws.$TelescopeVPosHUD); //custom HUD position
    	ws.$Telescope_Scan(); //need to avoid list all ship name as newship
    	ws.$Telescope_StartTimer( 0 ); //no delay
    }
    
    this.shipScoopedOther = function(whom) {
    //      var w = worldScripts["telescope"].$TelescopeList [worldScripts["telescope"].$TelescopeListi - 1 ];
    //      log("Telescope", "Scooped "+w.isValid+w.name+" VMark:"+worldScripts["telescope"].$TelescopeVMark); //debug
    	var ws = worldScripts.telescope;
    	if( ws.$TelescopeVMark && whom === ws.$TelescopeList [ ws.$TelescopeListi - 1 ] ) { //remove shadowmarker if targeted
    		ws.$TelescopeVMark.remove();
    		ws.$TelescopeVMark = null;
    	}
    }
    
    this.shipSpawned = function(ship) { //detect missile launch immediately
    //	ship.setScript("oolite-default-ship-script.js");//debug
    	var ps = player.ship;
    	if( !ps || !ps.isValid || ps.docked //player died or docked
    		|| !worldScripts.telescope.$TelescopeTimerS //no timer means in witchspace
    		|| !ship || !ship.isValid || ship.isVisualEffect //invalid or effect
    		|| !ship.dataKey || ship.dataKey === "telescopemarker" //planet or marker
    		|| !ship.isWeapon ) //missile and mine only
    		return; //means no scan needed
    	var distance = ps.position.distanceTo( ship.position );
    	if( distance < ps.scannerRange ) //ship in range
    		worldScripts.telescope.$Telescope_Scan();//forced rescan to see launched missiles
    }
    
    this.shipTargetAcquired = function(target) { //if locked target by hand then set as the actual item in the list
    //		player.ship.targetSystem = 147;
    
    //	player.consoleMessage(target.name+" "+target.isBeacon);//debug
    //	var a = system.allShips; var s = "";//debug
    //	for( var i = 0; i < a.length; i++) s+=a[i]+"\n";//debug
    //;	log("Telescope", a.length+" Ships "+s);//debug
    //	a = system.allVisualEffects; s = "";//debug
    //	for( var i = 0; i < a.length; i++) s+=a[i]+"\n";//debug
    //	log("Telescope", a.length+" VEffs "+s);//debug
    //	a = system.filteredEntities(this, this.$Telescope_True, player.ship, player.ship.scannerRange); s = "";//debug
    //	for( var i = 0; i < a.length; i++) { s+=a[i]+"\n"; if(!a[i].isPlayer && !a[i].isStation) a[i].remove(); }//debug
    //	log("Telescope", a.length+" Nears: "+s);//debug
    	if(!target || !target.isValid || this.$TelescopeTargetSet === true ) return;
    		//no target or just set in $Telescope_Show() or list is empty
    //	log("Telescope", "TargetAcq: "+target);//debug
    	var i = -1;
    	var ws = worldScripts.telescope;
    	if( target && target.dataKey && target.dataKey === "telescopemarker" ) { //planet marker?
    		i = ws.$TelescopeListi;
    		ws.$TelescopeTarget = ws.$TelescopeList [ i - 1 ];
    	} else ws.$TelescopeTarget = target; //save the real target
    	if( i < 0 && ws.$TelescopeList && ws.$TelescopeList.length > 0 )
    		i = ws._index_in_list( target, ws.$TelescopeList ); //find target in list
    	if( i < 0 && target && target.isValid //not found, refresh the list (usually the target spawned after the last scan)
    		&& target !== ws.$TelescopePrevTargetAcq ) { //Military Jammer bugfix
    //;		log("Telescope", "ScanTA: "+target+" - "+ws.$TelescopePrevTargetAcq);//debug
    		ws.$TelescopePrevTargetAcq = target; //store to avoid repeated scan
    		ws.$Telescope_Scan(); //rescan
    //		ws.$Telescope_List3(0, true, true); //free refresh from the normal scanner - need fix, removed
    		i = ws._index_in_list( target, ws.$TelescopeList ); //find target in list
    		if( i < 0 ) ws.$TelescopeListi = 0; //target not in list
    	}
    	if( ( ws.$TelescopeShowVisualTarget || !player.ship.weaponsOnline )
    		&& ws.$TelescopeFixedTel !== 1 ) {
    		//player.consoleMessage(i);//debug
    		if( i >= 0) { //found
    			ws.$TelescopeListi = i + 1;
    			ws.$Telescope_Show2( false );
    		} else {
    //;			log("Telescope","shipTargetAcquired i:"+i+" ti:"+ws.$TelescopeListi);
    			ws.$Telescope_VShow();
    		}
    	}
    }
    
    this.shipTargetLost = function(losttarget) {
    //;	log("Telescope", "Losttarget: "+losttarget);//debug
    	var ws = worldScripts.telescope;
    	ws.$TelescopeTarget = null; //must to clear
    	if( ws.$TelescopeTargetSet === true //set by script or disabled
    		|| ws.$TelescopeIdentLock === 0 ) return;
    	var lostvalid = false; //target destroyed
    	if( losttarget && losttarget.isValid ) lostvalid = true; //target not destroyed but lost by ident keypress
    	ws.$Telescope_MostCentered( null, "ident", lostvalid ); //lock-unlock target
    }
    
    this.shipWillEnterWitchspace = function() {
    	var ps = player.ship;
    	this.$TelescopeReds = []; //store red ships detected earlier
    	if( this.$TelescopeFixedGS === 1 && Math.random() > 0.5 ) {
    		ps.scriptedMisjump = true; //meet Thargoids due to the cheap Grav.Sc. repair
    		player.consoleMessage("Gravity Scanner caused misjump!");
    	}
    	if( this.$TelescopeFixedSD === 1 && Math.random() > 0.2 ) {
    		ps.setEquipmentStatus("EQ_SMALLDISH", "EQUIPMENT_DAMAGED");
    		player.consoleMessage("Small Dish damaged during hyperjump!", 10);
    	}
    	if( this.$TelescopeFixedLD === 1 && Math.random() > 0.2 ) {
    		ps.setEquipmentStatus("EQ_LARGEDISH", "EQUIPMENT_DAMAGED");
    		player.consoleMessage("Large Dish damaged during hyperjump!", 10);
    	}
    	this.shipWillDockWithStation();
    }
    
    this.shipWillDockWithStation = function() {
    	var ws = worldScripts.telescope;
    	ws.$Telescope_NewList();
    	ws.$TelescopeAttackers = [];
    	ws.$Telescope_VClear();
    	if( isValidFrameCallback( ws.$TelescopeVFCB ) )
    		removeFrameCallback( ws.$TelescopeVFCB );
    	if( isValidFrameCallback( ws.$TelescopeVFCB2 ) )
    		removeFrameCallback( ws.$TelescopeVFCB2 );
    	if( isValidFrameCallback( ws.$TelescopeVFCB3 ) )
    		removeFrameCallback( ws.$TelescopeVFCB3 );
    	for(var i = 0; i < ws.$TelescopeVFCBM.length; i++) {
    		if( isValidFrameCallback( ws.$TelescopeVFCBM[i] ) )
    			removeFrameCallback( ws.$TelescopeVFCBM[i] );
    		ws.$TelescopeVFCBM[i] = null; //cag: converting to static arrays
    	}
    	if( ws.$TelescopeTimerS ) {
    		ws.$TelescopeTimerS.stop();
    		ws.$TelescopeTimerS = null;
    	}
    }
    
    this.shipWillExitWitchspace = function() { //use this event due to shipExitedWitchspace is not working in v1.77
    	var ws = worldScripts.telescope;
    	ws.$TelescopePlanetNames = []; //cache of names
    	ws.$TelescopeGSP = 0; //begin new gravity detection process
    	ws.$Telescope_NewList();
    	ws.$Telescope_AddShips(); //do not call shipLaunchedFromStation() to aovid a bug
    	this.$TelescopeTimerF = new Timer(this, ws.$Telescope_TimedF, 1, 0); //1s delay to launch FCBs after FarPlanets FCB!
    }
    
    this.shipWillLaunchFromStation = function( /*station*/ ) {
    //	player.consoleMessage(player.ship.targetSystem+" "+player.ship.galaxyCoordinatesInLY.x+" "+player.ship.galaxyCoordinatesInLY.y);//debug
    //	player.ship.targetSystem = 147;//debug
    	var ws = worldScripts.telescope;
    	ws.$TelescopeStaionNearby = false; //to send gravscanner message after launch
    	ws.$Telescope_AddShips();
    }
    
    this._index_in_list = function( item, list ) { // for arrays only; faster than indexOf 
    	var k = list.length;
    	while( k-- ) {
    		if( list[ k ] === item ) return k;
    	}
    	return -1;
    }
    
    //Telescope methods
    this.$Telescope_AddShips = function() { 
    	if( !system.isInterstellarSpace ) { //need check due to called from shipWillExitWitchspace also
    		system.addShips("shuttle", 1, system.mainStation.position, 50000);//demo visible target
    		system.addShips("trader", 1, system.mainStation.position, 20000);//demo near target
    		
    //		system.addShips("asteroid", 10, system.mainStation.position, 20000);//test rock target
    //		system.addShips("rescue_station", 1, system.mainStation.position, 20000);//test custom station
    //		system.addShips("rescue_blackbox", 1, system.mainStation.position, 10000);//test custom ship
    //		system.addShips("rescue_blackbox_generic", 1, system.mainStation.position, 10000);//test custom ship
    //		system.addShips("stealth_base", 1, system.mainStation.position, 20000);//test custom station
    //		system.addShips("stealth_barracuda", 1, system.mainStation.position, 10000);//test stealth ship
    //		system.addShips("stealth_mine", 1, system.mainStation.position, 20000);//test stealth mine
    //		system.addShips("vector_areidisAlpha", 1, system.mainStation.position, 20000);//test custom station
    //		system.addShips("vector_arn", 1, system.mainStation.position, 10000);//test custom ship
    //		system.addShips("griff_NPC_prototype_boa_decals_from_red_channel",
    //			1, system.mainStation.position, 10000);//test visual effect shader uniforms, need Griff Boa OXP
    		
    		if( worldScripts.telescope.$TelescopeThargoids ) { //test Telescope in instant action
    			system.addShips("tharglet", 4, system.mainStation.position, 30000);
    			system.addShips("thargoid", 4, system.mainStation.position, 30000);
    //			system.addShips("police", 4, system.mainStation.position, 30000);
    			player.ship.scriptedMisjump = true; //meet Thargoids in the next hyperjump also
    		}
    	}
    }
    
    this.$Telescope_Angle = function( angle ) { //show the side and up viewing angle of the target
    	var c = 90;
    	if( angle < 0 ) c = -90;
    	return( Math.round( angle * 180 / Math.PI - c ) + "°" );
    }
    
    this.$Telescope_Attacker = function(lock) { //lock on the last attacker
    	var ws = worldScripts.telescope;
    	var ai = ws.$TelescopeAttackeri;
    	var who = ws.$TelescopeList[ ai - 1 ];
    //;	log("Telescope", "Attacker ai:"+ai+" who:"+who);//debug
    	if( who && who.isValid && who.isVisible ) { //remove if destroyed or flyed too far
    		ws.$TelescopeListi = ai;
    		if( lock ) ws.$Telescope_Show(); //change player target to the nearest
    		return( who );
    	} else {
    		ws.$TelescopeAttackeri = 0; //clear to get another attacker
    		return false;
    	}
    }
    
    this.$Telescope_From = function(ship, whomposition) { //direction mark
    //	log("Telescope_From1", "ship:"+ship+" wp:"+whomposition);//debug
    	if( !ship || !ship.isValid || !whomposition || !whomposition.dot ) return ("");
    	var v = ship.position.subtract(whomposition);
    	var fw = ship.vectorForward.angleTo(v);
    	var ri = ship.vectorRight.angleTo(v);
    	var up = ship.vectorUp.angleTo(v);
    	var s = ""; // refine if out of front 1 degree cone
    	if( ri < 1.56 ) s += "<";
    	if( up > 1.58 ) s += "^";
    	else if( up < 1.56 ) s += "v";
    	if( ri > 1.58 ) s += ">";
    //	log("Telescope_From2", "v:"+v+" fw:"+fw+" ri:"+ri+" up:"+up+" s:"+s);//debug
    	if( s.length > 0 || fw < 1 ) //do not exclude the aft 1 degree cone
    		s = Math.round( 180 * ( 1 - fw / Math.PI ) ) + "° " + s;
    	return( s ); //show forward angle in degrees
    }
    
    this.$Telescope_GetDetect = function(who) { //read detection from the script_info telescope entry
    	var good = true; var d;
    	if( who.scriptInfo || who.isStation ) {//can give custom detection distance to the target ship
    		if( who.scriptInfo ) d = parseInt( who.scriptInfo.telescope );
    		if( d === 0 ) good = false;
    		else if( d > 0 || d < 0 ) good = true; //show custom station
    		else if( who.isStation && who.primaryRole !== "station" //d is NaN due to undefined scriptInfo key
    			&& who.primaryRole !== "coriolis"
    			&& who.primaryRole !== "dodo"
    			&& who.primaryRole !== "dodec"
    			&& who.primaryRole !== "dodecahedron"
    			&& who.primaryRole !== "ico"
    			&& who.primaryRole !== "icosa"
    			&& who.primaryRole !== "icosahedron"
    			&& who.primaryRole !== "rockhermit" ) {
    			var ps = player.ship;
    			if(ps && ps.isValid) { //hide custom station over 4x scanner range
    				var distance = ps.position.distanceTo(who.position);
    				if( distance > 4 * ps.scannerRange ) good = false;
    			}
    		}
    	}
    	//stealth ships and non-standard stations detected in normal scanner range only, requested by Svengali
    	if( good && ( who.dataKey && ( who.dataKey.indexOf("stealth") > -1 
    		|| who.dataKey === "vector_arn" ) //mission ship in Vector OXP
    		|| who.primaryRole && ( who.primaryRole.indexOf("stealth") > -1
    		|| who.primaryRole.indexOf("rescue_blackbox") > -1 ) ) ) //mission ships in Rescue Stations OXP
    		good = false;
    	return( good );
    }
    
    this.$Telescope_GetMass = function(entity) { //read mass from the script_info telescope_mass entry
    	var em = entity.mass;
    	if( entity.scriptInfo ) { //can give custom mass to the ship
    		var e = parseInt(entity.scriptInfo.telescope);
    		if( e >= 0 ) em = e;
    		else if( e < 0 ) em += e; //substract the mass instead of overwrite it
    		//else telescope key not present, return the original mass
    //; 		log("Telescope", "scriptInfo "+entity.name+":"+entity.scriptInfo.telescope+" e:"+e+" mass:"+em);//debug
    	}
    	return( em * Math.min( 1, worldScripts.telescope.$TelescopeGSP ) ); //reduce if GS not in full detection yet
    }
    
    this.$Telescope_GravOK = function() { //in the range of the Gravity Scanner if installed
    	if( player.ship && player.ship.equipmentStatus("EQ_GRAVSCANNER") == "EQUIPMENT_OK"
    		&& worldScripts.telescope.$TelescopeStaionNearby //near a station or baseship
    		&& !player.ship.weaponsOnline ) return true; //grav scan without weapons only to need force it
    	return false;
    }
    
    this.$Telescope_InRange = function(entity) { //Visible or in normal Scanner means in Telescope range
    	if( player.ship && player.ship.equipmentStatus("EQ_TELESCOPE") === "EQUIPMENT_OK" &&
    		( entity.isPlanet || entity.isSun || !entity.isCloaked && !entity.isVisualEffect
    		&& ( entity.dataKey //sun and planets has no dataKey
    			&& entity.dataKey !== "telescopemarker" ) //virtual target of far targets
    		&& ( !entity.isRock || entity.isStation || entity.isPiloted ) && //there are piloted "rocks" in lave.oxp
    		( ( entity.isVisible && player.ship.equipmentStatus("EQ_TELESCOPEEXT") === "EQUIPMENT_OK" )
    		|| entity.isBeacon || entity.beaconCode //beacons in the whole system
    		|| worldScripts.telescope.$Telescope_IsScannerTarget(entity)
    		|| worldScripts.telescope.$Telescope_InGravRange(entity) ) ) ) //or in grav.scanner
    		return true;
    	else return false;
    }
    
    this.$Telescope_InRangeFast = function(entity, listi) {
    	//less check than InRange due to checked before and speed is important
    	if( entity.isCloaked ) return false;
    	var ps = player.ship;
    	if( entity.isVisible && ps.equipmentStatus("EQ_TELESCOPEEXT") === "EQUIPMENT_OK"
    		|| entity.isBeacon || entity.beaconCode || entity.isPlanet || entity.isSun )
    		return true;//beacons in the whole system
    	var distance = ps.position.distanceTo(entity.position);
    	if( distance < ps.scannerRange ) return true; //target in the normal scanner
    	var ws = worldScripts.telescope;
    	if( !ws.$TelescopeListGD[ listi ] ) { //store max. grav. distance to next time check faster
    		var em = ws.$Telescope_GetMass( entity );
    		var gd = Math.pow( 100 * em, 1/3 );//reverse calculation of InGravRange
    		if( ps.equipmentStatus("EQ_LARGEDISH") === "EQUIPMENT_OK" ) {
    			gd = gd * 2; //large dish double range
    			var pm = ws.$Telescope_GetMass( ps );
    			if( pm > 1000000 ) gd = gd * 2; //huge player ship double range another time
    			if( pm > 100000000 ) gd = gd * 2; //baseship scan double range third time
    		} else if( ps.equipmentStatus("EQ_SMALLDISH") === "EQUIPMENT_OK" )
    			gd = gd * 1.33; //small dish 1.33x range
    		gd = gd * 500; //max. grav. distance of this ship in meters
    		ws.$TelescopeListGD[ listi ] = gd; //store
    //;		log("Telescope", "InRangeFast ship:"+entity.name+" em:"+Math.round(em)+"kg gd:"+Math.round(gd)+"m");//debug
    	}
    	if( distance < ws.$TelescopeListGD[ listi ] ) return true; //compare with the stored value
    	return false;
    }
    
    this.$Telescope_InGravRange = function(entity) { //in the range of the Gravity Scanner if installed
    	var ws = worldScripts.telescope;
    	if( ws.$Telescope_GravOK()
    		&& entity.isValid && !entity.isCloaked //can not detect if cloaked
    		&& entity.dataKey && entity.dataKey !== "telescopemarker" //virtual target of far targets
    		&& ( entity.isPiloted || entity.forwardWeapon ) //need to detect hostile drones from HardShips OXP
    		&& ( !entity.isRock || entity.isStation )
    		&& !entity.isVisualEffect ) {
    		var ps = player.ship;
    		var distance = ps.position.distanceTo(entity.position);
    		if( distance < ps.scannerRange ) return false; //near ships listed before
    		if( entity.isBeacon || entity.beaconCode || entity.isPlanet || entity.isSun )
    			return true; //beacons in the whole system
    		var d2 = distance / 500; //in half km, = 2 * distance / 1000, keep it in sync with InRangeFast
    		if( ps.equipmentStatus("EQ_LARGEDISH") === "EQUIPMENT_OK" ) {
    			var pm = ws.$Telescope_GetMass( ps );
    			if( pm.mass > 100000000 ) d2 = d2 / 2; //baseship scan double range
    			if( pm.mass > 1000000 ) d2 = d2 / 2; //huge player ship
    			d2 = d2 / 2; //others detected at double range (with huge 4x)
    		} else if( ps.equipmentStatus("EQ_SMALLDISH") === "EQUIPMENT_OK" )
    			d2 = d2 / 1.33; //small dish 1.33x range
    		var em = ws.$Telescope_GetMass( entity );
    		if( em > d2*d2*d2/100 ) return true; //mass scaled to third power of distance
    	}
    	return false;
    }
    
    this.$Telescope_IsCargo = function(entity) { //Cargo, Escape Pod, Station in visible range and Beacon anywhere
    	if( entity && entity.isValid && !entity.isVisualEffect ) {
    		if( entity.isBeacon || entity.beaconCode ) return true; //beacons identifyed by active radio broadcast
    		if( !entity.isPiloted && entity.isCargo || entity.isStation
    			|| entity.primaryRole && entity.primaryRole === "escape-capsule" ) {
    			if( worldScripts.telescope.$Telescope_IsVisible( entity ) )
    				return true; //visible
    		}
    	}
    	return false;
    }
    
    this.$Telescope_IsHostile = function(entity) {
    	var ps = player.ship;
    	var ws = worldScripts.telescope;
    	if( entity && entity.isValid && !entity.isVisualEffect
    		&& ( entity.isPiloted || entity.forwardWeapon ) //need to detect hostile drones from HardShips OXP
    		&& ws.$Telescope_InRange( entity )
    		&& !entity.isStation && !entity.isCloaked &&
    		( entity.target && entity.target === ps //target is hostile if targeting back
    		   //or in the defenseTargets
    		|| ps.defenseTargets && ws._index_in_list( entity, ps.defenseTargets ) > -1
    		   //or this ship in the defenseTargets of the other
    		|| entity.defenseTargets && ws._index_in_list( ps, entity.defenseTargets ) > -1 )
    		|| ws._index_in_list( entity, ws.$TelescopeReds ) > -1 ) //pirate or thargoid
    		return true;
    	else return false;
    }
    
    /* this.$Telescope_IsNonHostileStaion = function(entity) {
    	if( entity && entity.isValid && entity.isStation && !entity.isVisualEffect && !entity.isCloaked
    		&& entity.mass > 10000000 //skip ships with docking port (except baseships), rockhermit with 53508t must fit in
    		&& entity.target !== player.ship //target is hostile if targeting back
    		   //or player is in the defenseTargets
    		&& ( !entity.defenseTargets || entity.defenseTargets.indexOf(player.ship) === -1 ) )
    		return true;
    	else return false;
    }
     */
    
     this.$Telescope_IsNotInTList = function(entity) { //asteroids, etc.
    	var ws = worldScripts.telescope;
    	if( entity && entity.isValid && !entity.isVisualEffect && !entity.isCloaked //else excluded from this list also
    		&& !entity.isPiloted && !entity.forwardWeapon && !entity.isCargo //approximately not in list for speedup
    		&& ws._index_in_list( entity, ws.$TelescopeList ) === -1 ) //surely not in list but slow alone
    		return( true );
    	else return( false );
    }
    
    this.$Telescope_IsPilotedShip = function(entity) { //non-hostile piloted ships
    	return( entity.isValid && !entity.isVisualEffect && !entity.isStation && !entity.isPlanet && !entity.isSun
    		&& ( entity.isPiloted || entity.forwardWeapon ) //need to detect drones from HardShips OXP
    		&& worldScripts.telescope.$Telescope_InRange( entity )
    		&& !entity.isCloaked && !this.$Telescope_IsHostile(entity) );
    }
    
    /* this.$Telescope_IsPilotedVisible = function(entity) {
    	if( entity && entity.isValid && !entity.isVisualEffect && !entity.isCloaked
    		&& ( entity.isPiloted || entity.forwardWeapon ) //need to detect drones from HardShips OXP
    //		&& ( !entity.isRock || entity.isStation ) //too much asteroids, lock only in scannerRange
    		&& (( worldScripts.telescope.$Telescope_GetDetect( entity )
    			&& entity.isVisible && player.ship.equipmentStatus("EQ_TELESCOPEEXT") === "EQUIPMENT_OK" )
    			|| worldScripts.telescope.$Telescope_IsScannerTarget( entity ))
    	  	&& ( !entity.dataKey || entity.dataKey !== "telescopemarker" ) )
    		return true;
    	return false;
    }
     */
    
    // there is now a copy of this inside _TimedS_closure
    this.$Telescope_IsScannerTarget = function(entity) { //call to surely detect all small targets in scanner
    	var ps = player.ship;
    	if( entity && entity.isValid && !entity.isVisualEffect && entity.dataKey //sun and planets has no dataKey
    		&& !entity.isCloaked && ps && ps.isValid ) {
    		var distance = ps.position.distanceTo(entity.position);
    		if( distance < ps.scannerRange ) return true; //target in the normal scanner
    	}
    	return false;
    }
    
    this.$Telescope_IsValid = function(entity) { //help to find most centered
    	if( !entity || !entity.isValid || ( !entity.dataKey && !entity.isPlanet && !entity.isSun ) )
    		return false; //Sun and Planets has no dataKey
    //	var angle = player.ship.vectorForward.angleTo(player.ship.position.subtract(entity.position));
    //	log("Telescope","angle:"+angle+" entity:"+entity.name);//debug
    //	if( angle < worldScripts.telescope.$TelescopeAutoLockRad ) { //in auto degree
    		if( !entity.isVisualEffect || entity.dataKey && entity.dataKey.indexOf("telescope-") > -1 )
    			return true;
    //	}
    	return false;
    }
    
    this.$Telescope_IsVisible = function(entity) {
    	if( !entity.isVisualEffect && ( entity.dataKey || entity.isPlanet || entity.isSun ) //sun and planets has no dataKey
    		&& ( !entity.isRock || entity.isStation ) //too much asteroids, lock only in scannerRange
    		&& !entity.isCloaked
    		&& ( ( entity.isVisible && player.ship.equipmentStatus("EQ_TELESCOPEEXT") === "EQUIPMENT_OK" )
    			|| worldScripts.telescope.$Telescope_IsScannerTarget(entity) ) ) return true;
    	return false;
    }
    
    
    this.$Telescope_List = function(step) {
    	//no scan if there are a list already, show again the message only
    	worldScripts.telescope.$Telescope_List2(step, false);
    }
    
    this.$Telescope_List2 = function(step, forcedscan) {
    	worldScripts.telescope.$Telescope_List3(step, forcedscan, false);
    }
    
    this.$Telescope_List3 = function(step, forcedscan, free) {
    	var ps = player.ship;
    	if( !ps || !ps.isValid ) return; //if player died
    	var ws = worldScripts.telescope;
    	var list = ws.$TelescopeList;
    	var len = list.length;
    	var ti = ws.$TelescopeListi;
    	if(ps.equipmentStatus("EQ_TELESCOPE") === "EQUIPMENT_DAMAGED") {
    		player.consoleMessage("Telescope damaged");
    		ws.$Telescope_NewList();
    	} else if(ps.equipmentStatus("EQ_TELESCOPE") !== "EQUIPMENT_OK") {
    		if( ws.$TelescopeBuyMsg ) {
    			player.consoleMessage("Buy Telescope! (x)");
    			ws.$TelescopeBuyMsg = false;
    		}
    		ws.$Telescope_NewList(); //needed when destroyed
    		//else silent exit (no Telescope when called from shipTargetLost)
    	} else {
    		ws.$TelescopeTargetSet = true;
    		var forcedship = null;
    		if( ti > 0 )
    			forcedship = list[ti - 1];
    //		var who = null;
    		if(step < 0) ws.$TelescopeListi--; //step backward in list
    		else if(step > 0) ws.$TelescopeListi++; //step forward in list
    		ti = ws.$TelescopeListi;
    //		player.consoleMessage(step+" "+ws.$TelescopeListi);//debug
    		if( forcedscan || !list
    			|| ti < 1
    			|| len < ti ) {
    			//if forced to scan or no list yet or listed all items then rescan
    			if(ps.energy < 64)
    				player.consoleMessage("Not enough energy for Telescope");
    			else {
    				ws.$Telescope_NewList();
    				//detect hostile ships in normal scanner
    				var st = system.filteredEntities(this, this.$Telescope_IsHostile,
    								 ps, ps.scannerRange);
    //				var st1 = st.length;
    				if(st && st[0]) ws.$Telescope_ListAdd(st); //add to the list if any
    				//detect non-hostile piloted ships in normal scanner
    				st = system.filteredEntities(this, this.$Telescope_IsPilotedShip,
    								 ps, ps.scannerRange);
    //				var st2 = st.length;
    				if(st && st[0]) ws.$Telescope_ListAdd(st); //add to the list if any
    				//detect visible cargoes, stations and all beacons
    				st = system.filteredEntities(this, this.$Telescope_IsCargo,
    								 ps, 2*ps.scannerRange);
    //				var st3 = st.length;
    				if(st && st[0]) ws.$Telescope_ListAdd(st); //add to the list
    				var st4 = 0;
    				if( free ) { //add the previously scanned far ships
    					ws.$Telescope_VFCBShrinkVMarks();//new far marks needed
    					ws.$Telescope_ListAdd2(
    						ws.$TelescopeListFar,
    						ws.$TelescopeListFarPos ); //old positions
    				} else {
    					ps.energy -= 2; //use a little energy to scan the whole sky
    					var sc = "Telescope";
    					if( ws.$Telescope_GravOK() ) {
    						ps.energy -= 6; //need 4x energy with Gravity Scanner
    						ws.$TelescopeSoundScan.play();//GS scan sound
    						sc = "Gravity scan";
    						if( ws.$TelescopeGSP < 1 )
    							sc += " "+Math.round(ws.$TelescopeGSP*100)
    								+"% done,";
    						//Gravity Scan counter to bring aliens
    						missionVariables.$TelescopeGSC = ++ws.$TelescopeGSC;
    						var p = 200; //at every 200. scan
    						if( ws.$TelescopeFixedGS === 1 ) p = 100; //every 100. scan
    						var g = ws.$TelescopeGSC / p;
    						if( g === Math.floor( g ) ) {
    							var n = Math.ceil( Math.pow( player.score, 0.5 ) / 10 );
    							if( n > 0 ) { //with 0 score do not get any
    								player.consoleMessage("Aliens detected your Gravity Scan!",10);
    								system.addShips("thargoid", n, ps.position, 50000);
    							}
    						}
    					}
    					ws.$Telescope_NewFarList();
    					//detect ships NOT in normal scanner
    					st = system.filteredEntities(this, this.$Telescope_InRange, ps,
    								     ws.$TelescopeMaxRange);//to avoid bugs
    					st4 = st.length;
    					if(st && st[0]) ws.$Telescope_ListFarAdd(st); //add to the list
    					//finally planets
    					st = system.planets;
    					if(st && st[0]) ws.$Telescope_ListFarAdd(st); //add to the list
    					if( !system.isInterstellarSpace && system.sun && system.sun.isValid )
    						ws.$Telescope_ListFarAdd(system.sun); //add sun to the end of the list
    					//send message about scan
    					len = ws.$TelescopeList.length;
    					if( !forcedscan ) {
    						var msg = "No";
    						if( len > 0 ) msg = sc+" found "+len;
    						var s = "";
    						if( len > 1 ) s = "s";
    						player.consoleMessage(msg+" target"+s, 5);
    					}
    				}
    //;		 		log("Telescope", "filteredEntities: "+st1+", "+st2+", "+st3+", "+st4+" list:"+ws.$TelescopeList.length+" allShips:"+system.allShips.length);  //debug
    				ws.$Telescope_TimedVMC(); //make colours to the new list
    			}
    		}
    		if( ps.target && ps.target.isValid ) {
    			ti = 0;
    			list = ws.$TelescopeList;
    			len = list.length;
    			if( forcedship && ps.target.dataKey
    				&& ps.target.dataKey === "telescopemarker" ) //far target
    				ti  = ws._index_in_list( forcedship, list ) + 1;
    			else ti = ws._index_in_list( ps.target, list ) + 1;
    			if( ti > 0 ) {
    				if( step < 0 ) {
    					ti--; //step backward in list
    					if( ti <= 0 ) ti = len;
    				} else if( step > 0 ) {
    					ti++; //step forward in list
    					if( ti > len ) ti = 1;
    				}
    			} else ti = 0;
    			if( ti > 0 ) {
    				var who = list[ ti - 1 ];
    				var limit = len;
    				while( ws._is_ignored_ship( ws, who ) && limit > 0 ) {
    					ti += step;
    					limit--;
    					if( ti <= 0 ) ti = len;
    					if( ti > len ) ti = 1;
    					who = list[ ti - 1 ];
    				}
    				if( limit === 0 ) ti = 0;
    			}
    			ws.$TelescopeListi = ti;
    //			if( forcedscan ) ws.$Telescope_Show2( false ); //do not show name - removed due to a target changer bug reported by Bogatyr
    			if( !forcedscan ) ws.$Telescope_Show(); //target name with list number
    		} //else leave listi on 0 to do not nock anything, show lightballs only
    //; 		log("Telescope", "List step:"+step+" forcedscan:"+forcedscan+" listi:"+ws.$TelescopeListi+" ship:"+ws.$TelescopeList[ws.$TelescopeListi-1]);//debug
    		ws.$TelescopeTargetSet = false;
    	}
    }
    
    this.$Telescope_ListAdd = function(newitems) { //add to the main list
    	worldScripts.telescope.$Telescope_ListAdd2(newitems, null); //with fresh positions
    }
    
    this.$Telescope_ListAdd2 = function(newitems, pos) { //can append the old scanned far list to the new free list
    	var ws = worldScripts.telescope;
    	for(var i = 0; i < newitems.length && ws.$TelescopeList.length <
    		ws.$TelescopeTargets; i++) { //limited number of targets to save CPU
    		var who = newitems[i]; var p;
    		if( who && who.isValid &&
    			ws._index_in_list( who, ws.$TelescopeList ) === -1 ) {
    			if( ws.$Telescope_GetDetect( who )
    				|| ws.$Telescope_IsScannerTarget( who ) ) {
    				if( pos ) p = pos[i]; //old saved position
    				else p = who.position; //fresh position
    				ws.$TelescopeListPos.push( p );
    				ws.$TelescopeList.push(who);
    			}
    		}
    	}
    }
    
    this.$Telescope_ListFarAdd = function(newitems) { //add to the far and main list also
    	var ws = worldScripts.telescope;
    	for(var i = 0; i < newitems.length; i++) {
    		var who = newitems[i];
    		if( who && who.isValid &&
    			ws._index_in_list( who, ws.$TelescopeListFar ) === -1 ) {
    			ws.$TelescopeListFarPos.push(who.position);
    			ws.$TelescopeListFar.push(who);
    		}
    	}
    	ws.$Telescope_ListAdd(newitems); //add to the main list also
    }
    
    /* this.$Telescope_MFDTarget = function(ws, ps, who) { //helper function for building MFD in TimedS
    	if( who && who.isValid ) {
    		var v = ws.$Telescope_ShowName2(who, who.position);
    		if( v && v.length > 0 && v.indexOf("(Lost ") === -1 ) {
    			if( who === ps.target || //current target
    				ps.target && ps.target.dataKey
    				&& ps.target.dataKey === "telescopemarker" //get the original target
    				&& who === ws.$TelescopeList[ ws.$TelescopeListi - 1 ] )
    				v = "[ "+v+" ]"; //mark the current target
    			return( v+"\n" );
    		}
    	}
    	return false;
    }
     */
    this.$Telescope_MostCentered = function( skiptarget, mode, lostvalid ) {
    	var ws = worldScripts.telescope;
    //;	log("Telescope", "MostCentered "+skiptarget+" "+mode+" "+lostvalid+" "+ws.$TelescopeIdentUnLock);//debug
    	var mct = null;
    	var ps = player.ship;
    	//in red alert find most centered hostile first if not supressed with offline weapons
    	if( player.alertCondition > 2 && ps.weaponsOnline && mode === "mode" ) {
    //		mct = ws.$Telescope_Attacker(false); //fallback to last attacker - old method
    //;		if(mct) log("Telescope", "Ident lock on attacker: "+mct.name);//debug
    		mct = ws.$Telescope_MostCentered2( skiptarget, mode, true, true );//ships attacking player
    //		log("Telescope", "mct1:"+mct);  //debug
    		if(!mct) {
    			mct = ws.$Telescope_MostCentered2( skiptarget, mode, true, false );//ships targeting player
    //			log("Telescope", "mct2:"+mct);  //debug
    		}
    	}
    	//if no hostile then fallback to normal check
    	if(!mct) mct = ws.$Telescope_MostCentered2( skiptarget, mode, false, false );
    	if( mct ) {
    		if( mct.dataKey && mct.dataKey === "telescopemarker" ) {//markership
    			mct = ws.$TelescopeTarget; //the real target behind the markership
    		}
    		var mi = ws._index_in_list( mct, ws.$TelescopeList );
    		if( mi > -1 ) {
    			//ident mode do unlock if pressed second time with the same most centered target
    			if( mode !== "ident" || mi + 1 !== ws.$TelescopeListi ) {
    				
    //;				log("Telescope", "MostCenteredTarget: "+mct.name+" mi:"+mi);//debug
    				if( !ps.weaponsOnline && mode === "ident" ) {//restore GravLock if ident lock target
    					ws.$TelescopeGravLock2 = ws.$TelescopeGravLock;
    					player.consoleMessage("Panorama targeting turned ON.");
    				}
    				if(  ws.$TelescopeListi !== mi + 1 ) {
    					ws.$TelescopeListi = mi + 1;
    					ws.$Telescope_Show2( false ); //lock target
    				}
    			} else {
    				if( mode === "ident" && ws.$TelescopeIdentUnLock ) { //unlock target after steer (3. ident press)
    					ws.$TelescopeIdentUnLock = false;
    //;					log("Telescope", "MCT ident unlock");//debug
    					if( !ps.weaponsOnline && mode === "ident" && lostvalid ) {
    						//shrink GravLock if ident unlock target by keypress and not by target destroyed
    						ws.$TelescopeGravLock2 = ws.$TelescopeAutoLock;
    						player.consoleMessage("Panorama targeting turned OFF.");
    					}
    					ws.$TelescopeListi = 0;
    //the following lines cause crash to desktop in 1.77 after some fight when a targeted Tharglet is exploding
    //					ws.$Telescope_VClear();
    					if( ps.target ) {
    //						log("Telescope", "MostCenteredTarget unlock: "+mct.name+" mi:"+mi);//debug
    						ws.$TelescopeTargetSet = true;//to avoid doubled message
    						ps.target = null;
    						ws.$TelescopeTargetSet = false;
    					}
    					ws.$TelescopeTarget = null; //clear after(!) player target cleared
    				} else if( mode === "ident") { //second ident press start steering to the target
    					ws.$TelescopeIdentUnLock = true; //next time will do unlock
    					ws.$TelescopeListi = mi + 1;
    					ws.$Telescope_Show2( false ); //lock target
     					if( ws.$TelescopeSteering > 0 )
    						if( ps.velocity.magnitude() < ps.maxSpeed * 1.1 )
    							//prevent unwanted steer when lost marker at high speeds
    							ws.$Telescope_Steer();//turn to the target
    				}
    			}
    		}
    	}
    }
    
    this.$Telescope_MostCentered2 = function( skiptarget, mode, red, attacker ) {
    	var ps = player && player.ship;
    	if( !ps || !ps.isValid ) return false;
    	var psp = ps.position;
    	var ws = worldScripts.telescope;
    
    	function closest_to( target_vector ) {
    		var min_a = rad;
    		var best = -1; 		// ie. not init'd
    		var best_d = ws.$TelescopeMaxRange;
    		var angle, distance, i;
    		for( i = 0; i < list.length; i++ ) { //search target near the center
    			var test = list[ i ];
    			if( !test || !test.isValid ) continue; // brought $Telescope_MostCenteredCheck inline
    			if( test === skiptarget ) continue;
    			if( red && mode === 'ident' && test.target !== ps ) continue;
    				// in red alert w/ mode 'ident', check only hostiles
    			angle = target_vector.angleTo( test.position.subtract( psp ) );
    			if( angle > min_a ) continue;
    			distance = psp.distanceTo( test.position );
    			if( min_a === rad ) { // 1st target found
    				best_d = distance;
    				min_a = angle;
    				best = i;
    				continue;
    			}
    			if( Math.abs( angle - min_a ) < Math.PI / 360 ) { // for ships within a half degree, pick the closer one
    				if( distance > best_d ) continue;
    			}
    			best_d = distance;
    			min_a = angle;
    			best = i;
    		}
    		return best;
    	}
    	function find_target() {
    		var dir = ps.viewDirection;
    			 if( dir === "VIEW_FORWARD" ) 	return closest_to( ps.vectorForward );
    		else if( dir === "VIEW_AFT" )		return closest_to( ps.vectorForward.multiply(-1) );
    		else if( dir === "VIEW_STARBOARD" ) return closest_to( ps.vectorRight );
    		else if( dir === "VIEW_PORT" ) 		return closest_to( ps.vectorRight.multiply(-1) );
    		return -1;
    	}
    	var list = ws.$TelescopeList; //search in the list only (stable)
    	var rad = ws.$TelescopeIdentLock * Math.PI / 180; ///angle in radians
    	var result = -1;
    	if( attacker ) { 
    		list = ws.$TelescopeAttackers; 
    		red = false; 
    	} else if( player.alertCondition > 2 && ps.weaponsOnline ) {//in red alert find most centered hostile if not supressed with offline weapons
    		rad = Math.PI; //in Red Alert lock from the whole sphere who target you
    		red = true;
    	}
    	if( mode === "ident" ) {									//button "r" pressed or target lost
    		if( player.alertCondition < 3 ) { //do not target asteroids before ships in red alert
    //	log("Telescope", "MCT calling filteredEntities");  //debug
    //	var list = system.filteredEntities(this, this.$Telescope_IsValid, ps, ps.scannerRange); - cause bug
    			list = system.filteredEntities(ws, ws.$Telescope_IsNotInTList, ps, ps.scannerRange);
    //	log("Telescope", "MCT filteredEntities returned "+list.length);  //debug
    			rad = ws.$TelescopeIdentLock * Math.PI / 180; ///angle in radians
    			result = find_target();
    			if( result > -1 ) { //found, target it
    				return( list[ result ] ); //priority to targets not in list but in crosshairs for asteroid hunting
    			}
    		} //if no asteroid in crosshairs then do normal ident to a ship in telescope list
    		list = ws.$TelescopeList; 
    		red = false;
    	} else 	if( mode === "auto" ) { //lock in the crosshairs only
    		if( ws.$TelescopeAutoLock <= 0 ) return false;	//if disabled
    		rad = ws.$TelescopeAutoLock * Math.PI / 180; ///angle in radians
    	} else 	if( mode === "grav" ) { //panorama targeting or lock in the crosshairs
    		rad = ws.$TelescopeGravLock2 * Math.PI / 180; ///angle in radians
    	} else 	if( mode === "mode" ) { //button "b" pressed after primed Telescope equipment with Shift+N
    		if( red ) rad = Math.PI;//in Red Alert lock from the whole sphere who target you
    	}
    //;	log("Telescope", "MostCentered2 "+mode+" "+l);//debug
    //	if( ps && ps.isValid && ps.target ) skiptarget = ps.target; //search another target
    	result = find_target();
    	if( result > -1 ) { //found, target it
    		return( list[ result ] );
    	}
    	return false;
    }
    
    /* this.$Telescope_Nearest = function() { //lock the nearest target
    //	var attacker = false;
    	var ws = worldScripts.telescope;
    	var ps = player.ship;
    	var i, nd, nai, who, ni = 0, d = null;
    	//in Red Alert lock the last attacker if any and weapons are online
    	if( player.alertCondition > 2 && ps.weaponsOnline ) {
    //		attacker = ws.$Telescope_Attacker( true ); //last attacker - old method
    		nd = ws.$TelescopeMaxRange; //reset to max.
    		nai = -1;
    		for( i = 0; i < ws.$TelescopeAttackers.length; i++ ) {
    			who = ws.$TelescopeAttackers[ i ];
    			ni = ws._index_in_list( who, ws.$TelescopeList );
    			if( who && who.isValid && ni > -1 && !who.isDerelict ) {
    				d = ps.position.distanceTo( who.position ); //distance to the attacker
    				if( d < nd ) { //nearer than the last nearest
    					nd = d; //save the new nearest distance
    					nai = ni; //save the index in the main list
    				}
    			} else { //remove if destroyed, derelict or not in TelescopeList
    				ws.$TelescopeAttackers.splice( i, 1 ); //remove from the array
    				i--; //stay on this position due to the array is shorter
    			}
    		}
    		if( nai > -1 ) { //change player target to the nearest attacker
    			ws.$TelescopeListi = nai + 1;
    			ws.$Telescope_Show();
    		}
    	} else { //lock the nearest target
    //	if( !attacker ) {
    		ni = ws.$TelescopeNearesti;
    		who = ws.$TelescopeList[ ni - 1 ];
    		if( ps && ( !who || !who.isValid ) ) {
    			ws.$Telescope_Scan(); //forced scan
    			ni = ws.$TelescopeNearesti;
    			who = ws.$TelescopeList[ ni - 1 ];
    		}
    //;		log("Telescope", "Nearest ni:"+ni+" who:"+who);//debug
    		if( ps && who && who.isValid ) {
    			ws.$TelescopeListi = ni;
    			ws.$Telescope_Show(); //change player target to the nearest
    		}
    	}
    }
     */
    
     this.$Telescope_Nearest = function() { //lock the nearest target
    //	var attacker = false;
    	var ws = worldScripts.telescope;
    	var ps = player.ship;
    	var i, nd, nai, who, ni = 0, d = null;
    	function nearest( list ) {
    		nd = ws.$TelescopeMaxRange; //reset to max.
    		let near = -1;
    		let len = list.length;
    		for( i = 0; i < len; i++ ) {
    			who = list[ i ];
    			if( ws._is_ignored_ship( ws, who ) ) continue;
    			ni = list === ws.$TelescopeList ? i : ws._index_in_list( who, ws.$TelescopeList );
    			if( who && who.isValid && ni > -1 && !who.isDerelict ) {
    				d = ps.position.distanceTo( who.position ); //distance to the attacker
    				if( d < nd ) { //nearer than the last nearest
    					nd = d; //save the new nearest distance
    					near = ni; //save the index in the main list
    				}
    			} else { //remove if destroyed, derelict or not in TelescopeList
    				ws.$TelescopeAttackers.splice( i, 1 ); //remove from the array
    				i--; //stay on this position due to the array is shorter
    			}
    		}
    		return near;
    	}
    	//in Red Alert lock the last attacker if any and weapons are online
    	if( player.alertCondition > 2 && ps.weaponsOnline ) {
    //		attacker = ws.$Telescope_Attacker( true ); //last attacker - old method
    		nai = nearest( ws.$TelescopeAttackers );
    		if( nai > -1 ) { //change player target to the nearest attacker
    			ws.$TelescopeListi = nai + 1;
    			ws.$Telescope_Show();
    			return;
    		}
    	}
    	nai = nearest( ws.$TelescopeList );
    	if( nai > -1 ) { //change player target to the nearest attacker
    		ws.$TelescopeListi = nai + 1;
    		ws.$Telescope_Show();
    	}
    }
    
    this.$Telescope_NewFarList = function() {
    //	log("Telescope","NewFarList");
    	var ws = worldScripts.telescope;
    	ws.$TelescopeListFar = [];
    	ws.$TelescopeListFarPos = [];
    }
    
    this.$Telescope_NewList = function() {
    //;	log("Telescope","NewList");
    	var ws = worldScripts.telescope;
    	ws.$Telescope_NewFarList();
    	ws.$TelescopeAttackeri = 0;
    	ws.$TelescopeList = [];
    	ws.$TelescopeListGD = [];
    	ws.$TelescopeListPos = [];
    	ws.$TelescopeListi = 0;
    	ws.$TelescopeNearestd = ws.$TelescopeMaxRange;
    	ws.$TelescopeNearesti = 0;
    	var m = ws.$TelescopeMMarks;
    	var i = 0;
    	for( i = 0; i < m.length; i++ ) {
    		if( m[i] ) m[i].remove();
    		ws.$TelescopeMMarks[i] = null;
    	}
    	ws.$TelescopeMMarks = [];
    	m = ws.$TelescopeMRings;
    	for( i = 0; i < m.length; i++ ) {
    		if( m[i] ) m[i].remove();
    		ws.$TelescopeMRings[i] = null;
    	}
    	ws.$TelescopeMRings = [];
    	m = ws.$TelescopeVMarks;
    	for( i = 0; i < m.length; i++ ) {
    		if( m[i] ) m[i].remove();
    		ws.$TelescopeVMarks[i] = null;
    	}
    	ws.$TelescopeVMarks = [];
    	ws.$TelescopeVMCs = [];
    }
    
    this.$Telescope_PlanetName = function(who) { //cached names
    	if( !system || !system.planets || system.planets.length < 1 || !who || (!who.isPlanet && !who.isSun ))
    		return("");
    	var ws = worldScripts.telescope;
    	var name = '';
    	if( who.isSun ) {
    		name = ws.$TelescopePlanetNames.sun;
    		if( !name || name.length < 1 ) {
    			name = ws.$Telescope_PlanetName2(who);
    			ws.$TelescopePlanetNames.sun = name;
    		}
    	} else {
    		var i = ws._index_in_list( who, system.planets );
    		if( i === -1 ) return("");
    		name = ws.$TelescopePlanetNames[i];
    		if( !name || name.length < 1 ) {
    			name = ws.$Telescope_PlanetName2(who);
    			ws.$TelescopePlanetNames[i] = name;
    		}
    	}
    	return( name );
    }
    
    this._isNotPlanet = function (entity) {return(!entity.isPlanet);}
    this._allPlanets  = function (entity) {return entity.isPlanet;}
    this.$Telescope_PlanetName2 = function(who) {
    	var ws = worldScripts.telescope;
    	var name = "Planet";
    	var p = null;
    	if( system && who && who.isValid ) {
    		if( who.isSun ) return("Sun of "+system.name);
    		if( !who.isPlanet ) return("Non-Planet");
    
    		var wn = worldScripts.planetnames;
    		if( wn ) {
    			name = wn.$PlanetNames_GetPlanetName( who );
    			if( name && name.length > 0 ) return( name );
    		}
    
    		if( worldScripts["planetaryCompass_worldScript.js"] ) {
    			p = system.filteredEntities(this, this._isNotPlanet, who, 10);
    //;			log("Telescope","PlanetName2 "+p);
    			if( p && p[0] && p[0].isValid && p[0].isVisualEffect && p[0].displayName )
    				return( p[0].displayName );
    		}
    		name = system.name;
    		if( who.isMainPlanet ) name += " Prime (Planet)";
    		else {
    			p = system.filteredEntities(this, this._allPlanets, system.sun); //order by distance from sun
    			if( who.hasAtmosphere ) {
    				var n = [ "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", 
    					"XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX" ];
    				var no = 0;
    				var i = ws._index_in_list( who, p );
    				if( i < n.length ) no = n[i];
    				else no = i;
    				name += " "+no+" (Planet)";
    			} else name = "Moon "+(ws._index_in_list( who, p )+1);
    		}
    	}
    	return( name );
    }
    
    this.$Telescope_RefundEQ = function( eq ) {
    	var ws = worldScripts.telescope;
    	var ps = player.ship;
    	if( ws._index_in_list( eq, ps.equipment ) ) {
    		if( ps.equipmentStatus( eq ) === "EQUIPMENT_DAMAGED" )
    			player.consoleMessage("Need repair first"); //do not get back full price for a damaged eq
    		else if( ps.equipmentStatus( eq ) === "EQUIPMENT_OK" ) {
    			ps.removeEquipment( eq );
    			clock.addSeconds ("3600");
    			var refund = EquipmentInfo.infoForKey( eq ).price / 10; ///
    			player.credits += refund;
    			player.consoleMessage("Refunded "+refund+" credits for "+EquipmentInfo.infoForKey( eq ).name);
    			return( refund );
    		}
    	}
    	return( 0 );
    }
    
    this.$Telescope_Scan = function() {
    	worldScripts.telescope.$Telescope_List2( 0, true ); //forced to scan
    }
    
    this.$Telescope_SetLightballs = function( subitem ) { //set config variables from telescopeeq.js also
    	var ws = worldScripts.telescope;
    //	if( subitem == 1 ) { //off
    	ws.$TelescopeLightBalls = false;
    	ws.$TelescopeShipLightBalls = false;
    	ws.$TelescopeMassLockBorders = false;
        ws.$TelescopeBrightMassLockBorders = false;
    	ws.$TelescopeLargeLightBalls = false;
    	if( subitem >= 2 ) ws.$TelescopeLightBalls = true; //ship off
    	if( subitem >= 3 ) ws.$TelescopeShipLightBalls = true; //small
    	if( subitem >= 4 ) ws.$TelescopeMassLockBorders = true; //25.6km but in green alert only
    	if( subitem >= 5 ) ws.$TelescopeBrightMassLockBorders = true; //use brighter borders
    	if( subitem >= 6 ) ws.$TelescopeLargeLightBalls = true;//large
    }
    
    this.$Telescope_SetSniper = function( subitem ) {
    	var ws = worldScripts.telescope;
    	if( subitem === 1 ) { //off
    		ws.$TelescopeSniperRange = 10000;
    		ws.$TelescopeSniperMinRange = 10000;
    	} else {
    		var minitem = subitem;
    		if( subitem < 5 ) ws.$TelescopeSniperRange = 25600;
    		else { minitem = subitem - 3; ws.$TelescopeSniperRange = 30000;}
    		ws.$TelescopeSniperMinRange = 5000 * (minitem-1); //5, 10 or 15km
    	}
    }
    
    this.$Telescope_SetSteering = function( subitem ) {
    	worldScripts.telescope.$TelescopeSteering = subitem - 1;
    }
    
    this.$Telescope_SetTargets = function( subitem ) {
    	var ws = worldScripts.telescope;
    	if( subitem === 1 ) { //20 and limitation in red alert
    		ws.$TelescopeRedAlertLimiter = true;
    		ws.$TelescopeTargets = 20;
    	} else {
    		ws.$TelescopeRedAlertLimiter = false;
    		if( subitem === 2 ) ws.$TelescopeTargets = 50;
    		else if( subitem === 3 ) ws.$TelescopeTargets = 100;
    		else ws.$TelescopeTargets = 200;
    	}
    }
    
    this.$Telescope_SetVisual = function( subitem ) {
    	var ws = worldScripts.telescope;
    	if( subitem === 1 ) {
    		ws.$TelescopeShowVisualTarget = false; //off
    		ws.$TelescopeVZoomSize = 0; //off without weapons also
    	}
    	if( subitem <= 2 ) ws.$TelescopeShowVisualTarget = false; //weapons off
    		else ws.$TelescopeShowVisualTarget = true;
    	if( subitem <= 3 ) {
    		if( subitem > 1 && ws.$TelescopeRing ) {
    			ws.$TelescopeRing = false; //no ring
    			ws.$TelescopeVSize += 3; //due to ring turned off
    			ws.$TelescopeVZoomSize = ws.$TelescopeVSize;
    		}
    	} else {
    		if( subitem > 1 && !ws.$TelescopeRing ) {
    			ws.$TelescopeRing = true;
    			ws.$TelescopeVSize -= 3; //due to ring turned on
    			ws.$TelescopeVZoomSize = ws.$TelescopeVSize + 3;
    		}
    	}
    	if( subitem <= 4 ) ws.$TelescopeShowVisualStation = false; //no station
    	else ws.$TelescopeShowVisualStation = true;
    	if( subitem <= 5 ) ws.$TelescopeShowVisualQuestionMark = false; //no "?"
    	else ws.$TelescopeShowVisualQuestionMark = true;
    }
    
    this.$Telescope_SetVisualSize = function( subitem ) {
    	var ws = worldScripts.telescope;
    	if( ws.$TelescopeRing ) ws.$TelescopeVSize = subitem; //1-8
    	else ws.$TelescopeVSize = subitem + 3; //without ring is equal with zoomed
    	ws.$TelescopeVZoomSize = subitem + 3; //3-10
    }
    
    this.$Telescope_Show = function() { //inputs: $TelescopeList array, $TelescopeListi item index to show
    	worldScripts.telescope.$Telescope_Show2( true );
    }
    
    this.$Telescope_Show2 = function( showname ) { //call with showname=true if eq step pressed
    	var ws = worldScripts.telescope;
    	//must use ws.$Telescope* and not this.$Telescope* to do not crash the game
    	var ti = ws.$TelescopeListi;
    	if( ws.$TelescopeList && ti > 0
    		&& ws.$TelescopeList.length >= ti ) {
    		var who = ws.$TelescopeList[ ti - 1 ];
    		if( who ) {
    			if( worldScripts.detectors && who.isValid && who.script && who.script.$Detectors_Origname )
    				who.displayName = who.script.$Detectors_Origname;
    				//show short name in automatic lock message
    //			log("Telescope", "Show pt:"+player.ship.target+" who:"+who+" tt:"+ws.$TelescopeTarget);//debug
    			if( player.ship.target !== who //to avoid repeated ident lock sound and message
    				&& ws.$TelescopeTarget !== who) { //far target
    				if( ws.$TelescopeTimerA ) { //restart timer if already running
    					ws.$TelescopeTimerA.stop();
    					ws.$TelescopeTimerA = null;
    				}
    				if( showname )
    					ws.$TelescopeTimerA = new Timer(this, ws.$Telescope_TimedA, 0.05, 0);
    				//return; //if the wait time of the timer set to 0.01 then buggy on Intel Atom netbook
    				//but good on i3: if see coriolis then can not step to the next target (relock the base)
    				//without timer the same bug happen on desktop i3
    				else ws.$Telescope_TimedA();
    				//if called from shipTargetLost then must to lock new target instantly to avoid
    				//ident system active message
    			}
    		}
    		if( showname ) ws.$Telescope_ShowName(ti, who,
    			ws.$TelescopeListPos[ ti - 1 ] ); //fallback pos if lost target
    		if( ( ws.$TelescopeShowVisualTarget || !player.ship.weaponsOnline )
    			&& ws.$TelescopeFixedTel !== 1 ) {//no visual mode if cheaply fixed
    //;			log("Telescope","Show2 ti:"+ti+" who:"+who);
    			ws.$Telescope_VShow();
    		}
    	}
    }
    
    this.$Telescope_ShowName = function(ti, who, position) {
    	var ws = worldScripts.telescope;
    	var msg = ws.$Telescope_ShowName2(who, position);
    	if( !msg || msg.length <= 0 ) return;
    	
    	var idlast = "";
    	if( ti > 0 && ws.$TelescopeList.length <= ti ) idlast = " last";
    	var tis = "";
    	if( ti ) tis = " ("+ti+"."+idlast+")";
    	
    //	msg+=" ("+Math.round( ws.$TelescopeZoom )+"x)"; //debug
    	if( ws.$TelescopeCMFD ) { //show in Combat MFD instead of console
    		ws.$TelescopePrevMFDTarget = who; //store for MFD
    		if(msg && msg.length > 0) ws.$TelescopeCMFD.$TelescopeLine = msg;
    	} else player.consoleMessage( msg+tis, 4.5 ); //fallback to console, showtime matched to ident message
    //;	log("Telescope", msg+tis); //debug
    }
    
    this.$Telescope_ShowName2 = function(who, position) {
    	var ws = worldScripts.telescope;
    	var name = "";
    	if( who && who.isValid ) {
    		if( who.name && ( who.name === "Railgun Projectile"
                            || who.name === "Debris" //do not show launched bullets
    			|| who.name.indexOf("customshields") > -1 ) ) return; //nor customshields parts
    		if( ws.$Telescope_InRange( who ) )
    			position = who.position; //got fresh data from the normal scanner or the telescope
    		if( who.script && who.script.$Detectors_Origname )
    			name = who.script.$Detectors_Origname;
    		else if( who.displayName && who.displayName.length > 0 )
    			name = who.displayName;
    		else if( who.isPlanet || who.isSun ) name = ws.$Telescope_PlanetName(who);
    		else name = who.name;
    	}
    	if( name.indexOf("Exhibition]") > -1 ) return; //do not show ships in exhibition of Gallery OXP
    	if( !name || name.length === 0 ) name = "(Lost target)";
    	else if( !who || !who.isPlanet && !who.isSun && ( !who.isValid || !ws.$Telescope_InRange( who ) ) )
    //		name = "(Lost "+name+")";
    		return;
    	var direction = ws.$Telescope_From(player.ship, position);
    //	log(ws.name, name+" d:"+direction+" p:"+position); //debug
    	var cr = 0;
    	if( who.collisionRadius > 0) cr = who.collisionRadius;
    	var rng = Math.floor((player.ship.position.distanceTo(position)-cr) / 1000);
    	if( rng >= 1000000 ) rng = Math.floor(rng/1000000)+"M";
    
    	if( who.isDerelict ) {
    		if(ws.$TelescopeTWS && who.script) { //Towbar status
    			if(who.script.$TowbarUsableShip) name = "Usable "+name;
    			else if(who.script.$TowbarMinedShip) name = "Mined "+name;
    			if(who.script.$TowbarEmptyShip) name = "Empty "+name;
    			else name = "Derelict "+name;
    		} else name = "Derelict "+name;
    	} else if( who.target === player.ship ) rng = "! "+rng; //hostile
    	else if( ws._index_in_list( who, ws.$TelescopeReds ) > -1 ) rng = "* "+rng; //pirate
    	return( rng+"km "+name+" "+direction );
    }
    
    this.$Telescope_ShowFoundTargetNumber = function() {
    	var ws = worldScripts.telescope;
    	var len = ws.$TelescopeList.length;
    	var s = "";
    	if( len > 1 ) s = "s";
    	var msg = " found "+len+" target"+s;
    	if( ws.$TelescopeStaionNearby ) {
    		var p = "";
    		if( ws.$TelescopeGSP < 1 ) p = Math.round(ws.$TelescopeGSP*100)+"% ";
    		msg = "Gravity scan"+msg+", "+p+"done"; //need online gravity scanner
    	} else msg = "Telescope"+msg;
    	player.consoleMessage( msg, 5 );
    }
    
    this.$Telescope_StartTimer = function( delay ) {
    	if( player.ship.equipmentStatus("EQ_TELESCOPE") === "EQUIPMENT_OK" ) {
    		var ws = worldScripts.telescope;
    		if( !isValidFrameCallback( ws.$TelescopeVFCB ) )
    			ws.$TelescopeVFCB = addFrameCallback( this.$Telescope_VFCB.bind(this) );
    		if( !isValidFrameCallback( ws.$TelescopeVFCB2 ) )
    			ws.$TelescopeVFCB2 = addFrameCallback( this.$Telescope_VFCB2.bind(this) );
    		if( !isValidFrameCallback( ws.$TelescopeVFCB3 ) )
    			ws.$TelescopeVFCB3 = addFrameCallback( this.$Telescope_VFCBVisualTarget.bind(this) );
    		for(var i = 0; i < ws.$TelescopeVFCBM.length; i++) {
    			if( isValidFrameCallback( ws.$TelescopeVFCBM[i] ) )
    				removeFrameCallback( ws.$TelescopeVFCBM[i] );
    			ws.$TelescopeVFCBM[i] = null; //cag: converting to static arrays
    		}
    //		ws.$TelescopeVFCBM = [];
    		ws.$TelescopeVFCBM[0] = addFrameCallback( ws.$Telescope_VFCBM0.bind(ws) );
    //cag: deleted 49 static FCBs to replace with dynamic ones
    		//AutoScan timer get targets from normal scanner and do scan if a new target is visible
    		//need at least 1 sec delay when called from shipWillExitWitchspace to avoid many gray balls
    		this.$TelescopeTimerS = new Timer(this, ws.$Telescope_TimedS, delay, 0.25);
    	}
    }
    
    this._is_ignored_ship = function( ws, who ) {
    	var we = worldScripts.escortdeck;
    	if( who.status === 'STATUS_BEING_SCOOPED' || who.status === 'STATUS_IN_HOLD' ) 
    		return true;											// a problem when mining!
    	if( we ) { 
    		let i = ws._index_in_list( who, we.$EscortDeckShip );	//escortdeck oxp is present
    		if( i >= 0 && we.$EscortDeckShipPos[i] )
    			return true; 										//who is on deck so exclude this who from target list
    	}
    	var wt = worldScripts.towbar;
    		if( wt && who === wt.$TowbarShip )
    			return true;										//skip the towed ship
    	return false;
    }
    
    this.$Telescope_Steer = function() {//turn to the target
    	var ws = worldScripts.telescope;
    	var ti = ws.$TelescopeListi;
    	var who = ws.$TelescopeList[ ti - 1 ];
    	if( ws._is_ignored_ship( ws, who ) ) return;
    	if( player.ship && who && who.isValid && player.ship.viewDirection === "VIEW_FORWARD" //working in forward view only
    		&& ws.$TelescopeFixedTel !== 1 //no steering if cheaply fixed
    		&& !isValidFrameCallback( ws.$TelescopeSteerFCB ) ) {
    		ws.$TelescopePrevHeading = player.ship.heading;
    		ws.$TelescopeSteerFCB = addFrameCallback( ws.$Telescope_SteerFCB.bind(ws) );
    	}
    }
    
    /*
    (function() {
    	var ws = worldScripts.telescope;
    	if( isValidFrameCallback( ws.$TelescopeSteerFCB ) )
    		removeFrameCallback( ws.$TelescopeSteerFCB );
    	ws.$TelescopeSteerFCB = addFrameCallback( ws.$Telescope_SteerFCB.bind(ws) );
    	console.clearConsole();
    })()
    */
    
    this.$Telescope_SteerFCB = function( delta ) {
    	var ws = worldScripts.telescope;
    	var ps = player.ship;
    //	sum += delta;	log("Telescope", "Time elapsed: " + sum + " (delta: " + delta + ")");
    	var ti = ws.$TelescopeListi;
    	var who = ws.$TelescopeList[ ti - 1 ];
    	if( ps && ps.isValid && who && who.isValid && !ws._is_ignored_ship( ws, who ) ) {
    		var position = who.position; //got fresh data from the normal scanner or the telescope
    		if( !ws.$Telescope_InRangeFast( who, ti ) )
    			position = ws.$TelescopeListPos[ ti - 1 ];
    		if( !position ) {
    			if( isValidFrameCallback( ws.$TelescopeSteerFCB ) )
    				removeFrameCallback( ws.$TelescopeSteerFCB );
    		} else {
    			var v = position.subtract( ps.position ).direction();
    			var angle = ps.heading.angleTo(v);
                    	var ato = ps.heading.angleTo(ws.$TelescopePrevHeading);
    			if( ato < 0.005 && angle > 0.015 ) { //steer if no manual steering and not in 1 degree
    				//if the above ato value lower then can not start steering in my intel atom netbook
    //				player.consoleMessage(ws.$TelescopePrevHeading +" vs "+ ps.heading);
    				var a = Math.min(ps.maxPitch*delta, angle/12);//half max turn/step and not too accutate
    				var wstow = ws.$TelescopeTWS;  //slowed steering from towbar oxp if there are towed ship
    				if( wstow && wstow.$TowbarShip && wstow.$TowbarShip.isValid ) {
    					var ma = Math.min( 2, ps.mass / wstow.$TowbarShip.mass ); ///small ship max. 2x
    					a = a * ma / 3; // 1/3 of the original rate with same mass, min. 1/5 max. 2/3
    //					player.consoleMessage("Telescope slow steering with towed ship "+ato);//debug
    				}
    				var c = ps.heading.cross(v).direction(); //set the plane where we should rotate in
    				var q = ps.orientation.rotate( c, -a );
    				ps.orientation = q;
    			} else { //end of steering
    //      	                player.consoleMessage(ato+" a"+angle+" p"+position);//debug
    				if( isValidFrameCallback( ws.$TelescopeSteerFCB ) )
    					removeFrameCallback( ws.$TelescopeSteerFCB );
    			}
    		}
    		ws.$TelescopePrevHeading = ps.heading;
    	} else { //end of steering
    		if( isValidFrameCallback( ws.$TelescopeSteerFCB ) )
    			removeFrameCallback( ws.$TelescopeSteerFCB );
    	}
    	if( isValidFrameCallback( ws.$TelescopeVFCB ) ) {
    		ws.$TelescopeSVSync = true;
    		ws.$Telescope_VFCB( delta );//must be run surely after the SteerFCB
    		ws.$TelescopeSVSync = false;
    	}
    }
    
    this.$Telescope_TimedA = function() { //to avoid backstep if a target in the crosshairs
    //	log("Telescope", "TimedA start");//debug
    	var ws = worldScripts.telescope;
    	var ti = ws.$TelescopeListi;
    	var who = ws.$TelescopeList[ ti - 1 ];
    	var ps = player.ship;
    //	log("Telescope", "TimedA ti:"+ti+" who:"+who);//debug
    	if( ps && ps.isValid && who && who.isValid ) {
    		var d = ps.position.distanceTo( who.position );
    		if( d < ps.scannerRange && !who.isVisualEffect && !who.isPlanet && !who.isSun ) {
    			if( who && who.isValid && ps.target !== who ) {
    				ws.$TelescopeTargetSet = true;//to avoid doubled list item message
    //				log("Telescope", "New player target: "+who.displayName+" who:"+who);//debug
    				if( !who.isPlanet && !who.isSun ) ps.target = who;
    				if( ps.target !== who && //yes, it is possible! Hohoho showed when a ship jumped!
    				//bugfix against non-lockable Military Jammer awarded by ShipVersion to avoid repeated scan
    					ws.$TelescopePrevWho !== who ) {
    					ws.$TelescopePrevWho = who; //store to scan only once
    //					player.consoleMessage("Hohoho!");//debug
    //;				log("Telescope", "ScanTW: "+who+" - "+ws.$TelescopePrevWho);//debug
    					ws.$Telescope_Scan(); //forced scan to remove the invalid ball
    				}
    				ws.$TelescopeTarget = ps.target; //save real target after(!) set
    //				log("Telescope", "New player target set to "+who.displayName);//debug
    				ws.$TelescopeTargetSet = false;
    			}
    		} else { //far target or planet, lock the marker
    			var vm = ws.$TelescopeVMark;
    //			log("Telescope", "ti:"+ti+" pt:"+ps.target+" VMark:"+vm);//debug
    			if( !ps.target || ps.target !== vm ) {
    //				log("Telescope", "TimedA pt:"+ps.target+" vm:"+vm);//debug
    				ws.$Telescope_VFCBVisualTarget(); //get new name
    				vm = ws.$TelescopeVMark;
    //				log("Telescope", "TimedA got new target name for vm:"+vm);//debug
    				if( vm && vm.isValid && !vm.isVisualEffect && ps.target !== vm ) {
    //					log("Telescope", "TimedA will set displayName:"+vm.displayName);//debug
    					var vd = vm.displayName;
    					if( who.isPlanet || who.isSun ) vm.displayName = ws.$Telescope_PlanetName(who);
    					else vm.displayName = who.displayName; //remove km from ident message
    					ws.$TelescopeTargetSet = true;//avoid doubled list item message
    //					log("Telescope", "New player target: "+vm.displayName+" vm:"+vm+" who:"+who);//debug
    					ps.target = vm;
    					ws.$TelescopeTarget = who; //save the real target after(!) set
    //					log("Telescope", "New player target set to "+vm.displayName);//debug
    					ws.$TelescopeTargetSet = false;
    					vm.displayName = vd; //restore km
    				}
    			}
    		}
    //		log("Telescope", "Player target: "+ps.target+" VMark:"+vm);//debug
    //		ws.$Telescope_ShowName();//removed, show in target box
    	}
    	if( ws.$TelescopeTimerA ) {
    		ws.$TelescopeTimerA.stop();
    		ws.$TelescopeTimerA = null;
    	}
    //	log("Telescope", "TimedA end");//debug
    }
    
    this.$Telescope_TimedF = function() { //delayed launch of FCBs, must after FarPlanets FCB
    	var ws = worldScripts.telescope;
    	ws.$Telescope_StartTimer( 1 ); //1 sec delay to avoid unwanted gray balls
    	if( ws.$TelescopeTimerF ) {
    		ws.$TelescopeTimerF.stop();
    		ws.$TelescopeTimerF = null;
    	}
    }
    
    // before w/ TelescopeVMCC=0, Total time: 48.901 ms JavaScript: 10.732 ms, native: 38.155 ms
    //  after w/ TelescopeVMCC=0, Total time: 34.331 ms JavaScript: 9.057 ms, native: 25.257 ms
    this._TimedS_closure = function() {
    	// 'constant' variables
    	var ws = worldScripts.telescope;
    	var telescopeAutoScan = ws.$TelescopeAutoScan;
    	var telescopeAutoScanMaxRange = ws.$TelescopeAutoScanMaxRange;
    	var telescopeRedAlertLimiter = ws.$TelescopeRedAlertLimiter;
    	var telescopeAutoLock = ws.$TelescopeAutoLock;
    	var telescopeGravLock = ws.$TelescopeGravLock;
    	var telescopeCMFD = ws.$TelescopeCMFD;
    	// function references
    	var player_consoleMessage = player.consoleMessage;
    	var telescope_Scan = ws.$Telescope_Scan;
    	var telescope_ShowFoundTargetNumber = ws.$Telescope_ShowFoundTargetNumber;
    	var system_filteredEntities = system.filteredEntities;
    	var telescope_ShowName = ws.$Telescope_ShowName;
    	var telescope_ShowName2 = ws.$Telescope_ShowName2;
    	var telescope_MostCentered = ws.$Telescope_MostCentered;
    	var telescope_GetDetect = ws.$Telescope_GetDetect;
    	var telescope_VClear = ws.$Telescope_VClear;
    	var telescope_TimedVMC = ws.$Telescope_TimedVMC;
    	var index_in_list = ws._index_in_list;
    	// local variables
    	var telescopeVMCC = 0; //counter to make colour of the visual marks, used to do once in a second within a 0.25s timer
    	var ps, pst, psp, scannerRange, telescopeGSP, telescopeList, telescopeListi, telescopeStaionNearby;
    
    	function _IsPilotedVisible(entity) {
    		if( entity && entity.isValid && !entity.isVisualEffect && !entity.isCloaked
    			&& ( entity.isPiloted || entity.forwardWeapon ) //need to detect drones from HardShips OXP
    	//		&& ( !entity.isRock || entity.isStation ) //too much asteroids, lock only in scannerRange
    			&& (( telescope_GetDetect( entity )
    				&& entity.isVisible && ps.equipmentStatus("EQ_TELESCOPEEXT") === "EQUIPMENT_OK" )
    				|| // brought code inline // ws.$Telescope_IsScannerTarget( entity )
    				( entity.dataKey && ps && ps.isValid && psp.distanceTo(entity.position) < scannerRange )
    				)
    			&& ( !entity.dataKey || entity.dataKey !== "telescopemarker" ) )
    			return true;
    		return false;
    	}
    	function _IsNonHostileStaion(entity) {
    		if( entity && entity.isValid && entity.isStation && !entity.isVisualEffect && !entity.isCloaked
    			&& entity.mass > 10000000 //skip ships with docking port (except baseships), rockhermit with 53508t must fit in
    			&& entity.target !== ps //target is hostile if targeting back
    			   //or player is in the defenseTargets
    			&& ( !entity.defenseTargets || ws._index_in_list( ps, entity.defenseTargets ) === -1 ) )
    			return true;
    		else return false;
    	}
    	function _MFDTarget(ws, ps, who) { //helper function for building MFD in TimedS
    		if( who && who.isValid ) {
    			var v = telescope_ShowName2(who, who.position);
    			if( v && v.length > 0 && v.indexOf("(Lost ") === -1 ) {
    				if( who === pst || //current target
    					pst && pst.dataKey
    					&& pst.dataKey === "telescopemarker" //get the original target
    					&& who === telescopeList[ telescopeListi - 1 ] )
    					v = "[ "+v+" ]"; //mark the current target
    				return( v+"\n" );
    			}
    		}
    		return false;
    	}
    
    	function _TimedS() { //check for most centered 4 times/second, new targets and colour update once/second
    	//	log("Telescope", "TimedS start"); //debug
    		ps = player.ship;
    		pst = ps.target;
    		psp = ps.position;
    		scannerRange = ps.scannerRange;
    		telescopeStaionNearby = ws.$TelescopeStaionNearby;
    	//;	if( ps.target === null ) log("Telescope","TimedS notarget1");
    		if( !ps || !ps.isValid || ps.equipmentStatus("EQ_TELESCOPE") !== "EQUIPMENT_OK" ) {
    			if(ps.setMultiFunctionText) //clear MFD if Telescope damaged
    				ps.setMultiFunctionText(this.name, "No Telescope Target", false);
    			return; //stop scan when damaged
    		}
    	//	var gravdone = false;
    		var gravok = false;
    		if( ps.equipmentStatus("EQ_GRAVSCANNER") === "EQUIPMENT_OK" ) gravok = true;
    		telescopeGSP = ws.$TelescopeGSP;
    		if( gravok && telescopeStaionNearby ) {
    			if( telescopeGSP < 1 ) {
    				var gsm = 1;//gravity scanner multiplyer
    				if( ps.speed === 0 ) gsm = 4; //4 times faster if stopped
    				if( ps.equipmentStatus("EQ_GRAVSCANNER2") === "EQUIPMENT_OK" )
    					gsm *= 2; //half time with 2 working grav.scanner
    				ws.$TelescopeGSP = telescopeGSP = telescopeGSP + gsm * 1/960; //normal gravity scan need 4 minutes
    				if( telescopeGSP >= 1 ) {
    					ws.$TelescopeGSP = telescopeGSP = 1;
    					if( ps.weaponsOnline )
    						player_consoleMessage("Gravity scan done, turn off weapons to see results", 5);
    					else {
    						telescope_Scan(); //forced scan to insert new gravity targets
    						telescope_ShowFoundTargetNumber();
    					}
    				}
    			}
    		} else if( telescopeGSP > 0 ) 
    			ws.$TelescopeGSP = telescopeGSP = telescopeGSP - 1/240; //degrading from 100% to 0% in 2 minute if away from stations
    		var newtarget = null;
    		var st = [];
    		var ascan = false;
    		var i = 0, j = 0, who = 0, s = '';
    		telescopeList = ws.$TelescopeList;
    		telescopeListi = ws.$TelescopeListi;
    		if( telescopeAutoScan //enabled
    			&& telescopeVMCC < 1 //and every 4. call
    			&& telescopeList.length < ws.$TelescopeTargets ) { //and list is not full
    			ascan = true;
    			st = system_filteredEntities(this._TimedS_closure, _IsPilotedVisible, ps,
    							 telescopeAutoScanMaxRange); //maybe avoid bugs if maxed
    	// 		log("Telescope", "TimedS filteredEntities returned "+st.length);  //debug
    
    			var mfd = ""; //build Telescope MFD
    
    			for( i = 0; i < st.length && j < 10; i++ ) { //list the nearest ships first
    				s = _MFDTarget(ws, ps, st[i]);
    				if( s ) { mfd += s; j++; }
    			}
    			for( i = 0; i < telescopeList.length && j < 10; i++ ) { //then other targets
    				who = telescopeList[i];
    				if( who && ws._index_in_list( who, st ) === -1 ) { //not in the filtered ships array
    					s = _MFDTarget(ws, ps, who);
    					if( s ) { mfd += s; j++; }
    				}
    			}
    			if( j < 1 ) mfd = "No Telescope Target";
    			if(ps.setMultiFunctionText) ps.setMultiFunctionText(this.name, mfd, false);
    
    			//check a station is nearby for gravity scanner
    			if( gravok ) {
    				if( ps.mass < 100000000 ) { //baseships can perform gravity scan anywhere
    					var sg = system_filteredEntities(this._TimedS_closure, _IsNonHostileStaion, ps, 5000);
    					if( !telescopeStaionNearby && sg && sg.length > 0 ) {
    	//					var w = "";
    						telescopeStaionNearby = true; //store the result
    						if( ps.weaponsOnline )  //show when arrived near a station
    							player_consoleMessage("Gravity scan need offline weapons", 5);
    						else {
    							telescope_Scan();//forced rescan
    							telescope_ShowFoundTargetNumber();
    						}
    					} else {
    						if( telescopeStaionNearby && ( !sg || sg.length === 0 ) ) { //too far or become hostile
    							player_consoleMessage("Gravity scan need friendly station in 5km", 5);
    							telescopeStaionNearby = false;
    							telescope_Scan();//forced rescan
    						}
    					}
    				} else telescopeStaionNearby = true;//baseship
    			} else telescopeStaionNearby = false;
    		}
    		for( i = 0; i < st.length; i++ ) { //search new target
    			if( ws._index_in_list( st[i], telescopeList ) === -1 &&
    				!st[i].isPlayer ) { //must, bugfix
    				var p = st[i].position;
    				telescope_ShowName("", st[i], p);
    	//			var m = Math.round(ps.position.distanceTo( p ));
    	//			var dir = ws.$Telescope_From(ps, p);
    	//			log("Telescope", "New visible: " + m+"m "+dir+" "+st[i]);//debug
    	//			system.addVisualEffect("telescope-whitemarker", p);//debug
    				newtarget = st[i]; //but do not jump out of the cycle, keep to print all new name
    			}
    		}
    		if( !newtarget && telescopeCMFD ) { //CombatMFD support
    			var pt = ws.$TelescopePrevMFDTarget;
    			if( !pt || !pt.isValid || ascan && index_in_list( pt, telescopeList ) === -1 ) {
    				//need ascan check also, else cause blinking names in CombatMFD
    	//			player.consoleMessage(pt);//debug
    				ws.$TelescopePrevMFDTarget = null;
    				telescopeCMFD.$TelescopeLine = ""; //clear the line in MFD
    			} else telescope_ShowName("", pt, pt.position); //update the direction
    		}
    	//;	if( ps.target == null ) log("Telescope","TimedS notarget2");
    		if( newtarget && newtarget !== ws.$TelescopePrevNewTarget
    			|| pst && !pst.isValid //target jumped or so
    			&& !pst.isPlanet && !pst.isSun
    			&& ( !pst.dataKey || pst.dataKey !== "telescopemarker" )) {
    	//;		log("Telescope", "ScanNT: "+newtarget+" - "+ws.$TelescopePrevNewTarget);//debug
    			ws.$TelescopePrevNewTarget = newtarget; //store to scan only once
    			telescope_Scan(); //forced scan to insert new one(s)
    		}
    	//	else ws.$Telescope_List3(0, true, true); //forced free scan to refresh from the normal scanner - need fix, removed
    
    	//;	if( ps.target == null ) log("Telescope","TimedS notarget3");
    		if( !isValidFrameCallback( ws.$TelescopeSteerFCB ) ) { //no retarget during autosteering
    			if( ps.weaponsOnline ) {//in auto mode
    				if( pst === null //no target and autolock not disabled
    					&& telescopeAutoLock !== 0 )
    					telescope_MostCentered( null, "auto", false );
    			} else if( telescopeGravLock !== 0 ) //in grav mode and not disabled
    				telescope_MostCentered( null, "grav", false );
    		}
    		
    		if( pst === null ) {
    	//;		log("Telescope","TimedS notarget");
    			var ti = telescopeListi;
    			who = telescopeList[ ti - 1 ];
    			if( !who || ( !who.isPlanet && !who.isSun ) ) {
    	//;			log("Telescope","TimedS VClear");
    				telescope_VClear(); //cleanup needed in some cases
    			}
    		}
    		
    		if( !telescopeRedAlertLimiter //small help to slow computers:
    			|| telescopeVMCC < 1 ) //update in every 4. call only
    			telescope_TimedVMC(); //update the colours of the lightball markers
    		if( telescopeVMCC < 1 ) { //check colour making counter
    			telescopeVMCC = 3; //do once in a second when reach 0
    		} else telescopeVMCC--;
    	//	log("Telescope", "TimedS end"); //debug
    		ws.$TelescopeStaionNearby = telescopeStaionNearby;
    	}
    
    	return _TimedS;
    }
    
    this.$Telescope_TimedVMC = function() { //make colours to visual markers, check targets are flyed out of range
    	if( !player.ship || !player.ship.isValid ) return;
    	var ws = worldScripts.telescope;
    	var ps = player.ship;
    	var ball = null;
    	var col = null;
    	var dt = null;
    //	var dir = null;
    //	var scr = ps.scannerRange;
    	var tcr = 0;
    	var tl = ws.$TelescopeList;
    	var tfs = ws.$TelescopeFarStatus;
    	var t = null;
    //	var v = null;
    	ws.$TelescopeNearestd = ws.$TelescopeMaxRange; //reset to max.
    	var prepa = ws.$TelescopeRedAlertLimiter; //run much faster with local variables
    	var prepb = ws.$TelescopeLightBalls;
    	var prepc = ws.$TelescopeVMCs;
    	var prepd = ws.$TelescopeNearestd;
    	var prepi = ws.$TelescopeNearesti;
    	var prepl = ws.$TelescopeLargeLightBalls;
    	var prepm = ws.$TelescopeSniperMinRange;
    	var prepp = ws.$TelescopeListPos;
    	var prepr = ws.$TelescopeSniperRange;
    	var preps = ws.$TelescopeShipLightBalls;
    	var prept = null; if( ws.$TelescopeTWS ) prept = ws.$TelescopeTWS.$TowbarShip; //skip the towed ship
    	var prepv = ws.$TelescopeVMarkMinDist;
    	var prepvs = ws.$TelescopeVMarkShipMinDist;
    	var reds = ws.$TelescopeReds; //pirate detected earlier
    	var telescope_InRangeFast = ws.$Telescope_InRangeFast;
    	var telescopeListPos = ws.$TelescopeListPos;
    	var psp = ps.position;
    	var scannerRange = ps.scannerRange;
    	var tpos, dataKey, isPlanet, isThargoid, isSun, mass;
    	//following part is very CPU consuming, with 100 ships need 400 cycle/sec, so code wisely
    	for( var i = 0; i < tl.length; i++ ) {
    		t = tl[i];
    		if( !t || !t.isValid ) col = null; //invalid target
    		else if( !telescope_InRangeFast( t, i ) ) {
    			//less check than InRange due to checked before and speed is important
    			col = "gray"; //last known position only
    			dt = psp.distanceTo( prepp[ i ] );
    			//distance to the last known position
    		} else {
    			tpos = t.position;
    			dataKey = t.dataKey;
    			isPlanet = t.isPlanet;
    			isThargoid = t.isThargoid;
    			isSun = t.isSun;
    			mass = t.mass;
    			dt = psp.distanceTo( tpos ); //distance to the target
    			telescopeListPos[ i ] = tpos; //update until InRange
    			if( t.isStation || ( dataKey && dataKey.indexOf("buoy") > -1 ) )
    				col = "green"; //base or buoy, do not use isBeacon to exclude ships with beaconCode
    			else if( isPlanet ) col = "lightgray"; //planets
    			else if( isThargoid && dataKey !== "tharglet" //warship is red but tharglet is pink or white
    				|| !t.isDerelict && prepc[i] && prepc[i].indexOf("red") > -1 )
    				col = "red"; //pirate detected earlier
    			else if( t.isVisible || dt < scannerRange ) {
    				if( t.isPolice ) col = "purple"; //police
    				else if( t.isWormhole || t.isDerelict ) col = "blue"; //wormhole from Oolite v1.79
    				else if( !t.isPiloted && t.forwardWeapon && t.bounty > 0 //active tharglet or drone
    //					&& ( ( t.owner && t.owner.isValid || t.speed > 0 ) //can fire after owner gone until moving
    						//&& !t.owner.isDerelict - still fire
    					&& ( isThargoid //will be like a rock when inactive
    						|| dataKey !== "tharglet" ) ) col = "pink";
    				else if( t.isCargo || t.primaryRole === "escape-capsule" //cargo, esc.pod, rock or sun
    					|| isSun || t.isRock || dataKey === "tharglet" ) col = "white";
    				else if( t.bounty > 0 && ( tfs || dt < scannerRange || isThargoid ) ) {
    					col = "red"; //pirate in scanner or FarStatus=true or thargoid
    					if( ws._index_in_list( t, reds ) === -1 ) reds.push(t); //save
    				}
    				else if( t.isWeapon ) col = "cyan"; //missile or mine
    				else if( ws._index_in_list( t, reds ) > -1 ) col = "red"; //pirate detected earlier
    				else col = "yellow"; //other ship with clean status
    			} else if( mass >= 130000 ) col = "orange"; //large ship over the visible range
    			else col = "brown"; //small ship over the visible range
    		}
    		if( col ) { //check to avoid invalid target
    			if( dt < prepd && t !== prept ) { //find the nearest target but skip the towed ship
    				prepd = dt;
    				prepi = i + 1; //store the nearest
    			}
    			tcr = 0;
    			if( t && t.isValid && !isPlanet && !isSun ) tcr = t.collisionRadius;
    			if( prepb && prepl ) {
    				if( isPlanet && !t.hasAtmosphere ) ball = "moon";
    				else if( dt < prepm + tcr ) ball = "largeball"; //xl size
    				else if( dt < prepr + tcr ) ball = "ball"; //large size
    				else ball = "marker"; //average size
    			}
    			if( dt < prepv + tcr //too near
    				|| ( ( dt < prepvs + tcr ) && col !== "cyan" && col !== "white" && col !== "pink" )
    				//in red alert show ball marked targets only to save CPU and clean scanner
    				|| prepa && player.alertCondition > 2 && ps.weaponsOnline && ball !== "ball" )
    				col = null;
    			else {
    				if( !prepb || !preps //if ship lightballs are disabled then show others only
    					&& col !== "blue" && col !== "cyan" && col !== "green" && col !== "white" )
    					col = "telescope-"+col+"_flag";//lollipop without lightball
    				else if( isPlanet || isSun ) {
    					if( t.hasAtmosphere ) { //only lightrgray has moon in effectdata.plsit
    						if( t.isVisible && dt < 10000000 )
    							col = "telescope-lightgray_moonflag"; //no dot
    						else col = "telescope-lightgray_moon";
    					} else if( t.isVisible && dt < 10000000 )
    						col = "telescope-"+col+"_flag"; //no dot
    					else col = "telescope-"+col+"_dotmarker";
    				} else if( prepl ) { //large balls
    					if( dt > 1000000 ) col += "_tiny"; //over 1000km show tiny ball
    					else if( dt > 100000 ) col += "_small"; //over 100km show smaller ball
    					col = "telescope-"+col+"_"+ball;
    				} else if( dt > 1000000 ) col = "telescope-"+col+"_dotmarker"; //over 1000km
    				else if( mass < 400000 ) col = "telescope-"+col+"_tinymarker"; //escort ship
    				else col = "telescope-"+col+"_smallmarker"; //large ship (Cobra3 and over)
    			}
    		}
    		ws.$TelescopeVMCs[ i ] = col;
    	}
    	ws.$TelescopeNearestd = prepd;
    	ws.$TelescopeNearesti = prepi;
    }
    
    this.$Telescope_True = function(/*target*/) { //system.filteredEntities can get all ship with this
    	return true;
    }
    
    this.$Telescope_VClear = function() { //Clear Visual Effect Ship Model and Visual Marker also
    	var ws = worldScripts.telescope;
    //;	log("Telescope","VClear");
    	ws.$Telescope_VClearM();
    	ws.$Telescope_VClearS();
    }
    
    /*
    (function() {
    	var ws = worldScripts.telescope;
    	if( isValidFrameCallback( ws.$TelescopeVFCB ) ) removeFrameCallback( ws.$TelescopeVFCB );
    	var vc = ws._VFCB_closure();
    	ws.$Telescope_VClearS = vc.clear;
    	ws.$Telescope_VShow = vc.show;
    	ws.$Telescope_VFCB = vc.fcb;
    	if( !isValidFrameCallback( ws.$TelescopeVFCB ) )
    		ws.$TelescopeVFCB = addFrameCallback( ws.$Telescope_VFCB.bind(ws) );
    })()
    */
    
    this._VFCB_closure = function() {
    	// 'constant' variables
    	var ws = worldScripts.telescope;
    	var w_shiplib = worldScripts.shiplib;
    	var gameWindow = oolite.gameSettings.gameWindow;
    	// function references
    	var system_addVisualEffect = system.addVisualEffect;
    	var telescope_Scan = ws.$Telescope_Scan;
    	var telescope_ShowFoundTargetNumber = ws.$Telescope_ShowFoundTargetNumber;
    	var telescope_InRange = ws.$Telescope_InRange;
    	var telescope_VClear = ws.$Telescope_VClear;
    	var index_in_list = ws._index_in_list;
    	// local variables
    	var weaps = true; //the previous state of the player weapons
    	var vRing = null; //a ring around the visual effect target
    	var vShip = null; //visual effect to show the selected target
    	var vDataKey = null; //key of the visual effect
    	var vShrinkC = 0; //visual effect shrink counter - shrink code is not ready, leave these at 0
    	var vShrinkDelay = 0; //in sec after the larger visual target start shrinkig to normal size (0: instant)
    	var vShrinkLength = 0; //in sec, shrinking faster if smaller (0: instant)
    	var vAlighUp = 0; //visual effect align to top modifier
    	var vZoom = 1; //store the original maximal zoom level of the visual effect
    	var ps, wide;
    
    //	function getVShip() { return vShip; }
    	function setVShip( ship, up, delay, length ) {
    		vShip = ship;
    		vAlighUp = up;
    		vShrinkDelay = delay;
    		vShrinkLength = length;
    		vShrinkC = vShrinkDelay + vShrinkLength; //shrink after delay
    		return ship;
    	}
    	function clearVShip() { //Clear Visual Effect Ship Model and large visual ring
    		if( vShip ) {
    	//; 		log("Telescope", "Remove VModel:"+ws.$TelescopeV); //debug
    			vShip.remove();
    	// 		log("Telescope", "Removed VModel."); //debug
    			vShip = null;
    			vShrinkC = 0;
    			vAlighUp = 0;
    			vDataKey = null;//need to show again when reident
    		}
    		if( vRing ) {//remove large visual ring
    	//; 		log("Telescope", "Remove VRing:"+vRing); //debug
    			vRing.remove();
    	// 		log("Telescope", "Removed VRing."); //debug
    			vRing = null;
    		}
    		return null;
    	}
    	function showVShip() { //Show Visual Effect
    		ps = player.ship;
    		if( !ws.$TelescopeShowVisualTarget ) return;
    	//cag: added to fix bug where loading game or altering its size caused effect to re-appear when turned off
    		if( !ws.$TelescopeAutoLock && !ps.target ) return;
    		if( ps.weaponsOnline ) {
    			if( ws.$TelescopeVSize <= 0 ) return;
    		} else if( ws.$TelescopeVZoomSize <= 0 ) return;
    		var ti = ws.$TelescopeListi;
    		var who = ws.$TelescopeList[ ti - 1 ];
    		if( !who || !who.isValid && !who.isPlanet && !who.isSun || !telescope_InRange( who ) ) {
    	//;		log("Telescope","VShow ti:"+ti+" who:"+who);
    			telescope_VClear();
    			return;
    		}
    		var dk = who.dataKey;
    		var v = vShip;
    	//	log("Telescope","VShow "+dk+" "+v+" "+who.isStation+" "+ws.$TelescopeShowVisualStation);//debug
    		if( who.isPlanet || who.isSun || who.isStation && !ws.$TelescopeShowVisualStation )
    			clearVShip();
    		else if( !v || vDataKey !== dk ) {
    			vDataKey = dk;
    			if( v ) v.remove();
    			v = null;
    					
    			if( 0 < oolite.compareVersion("1.79") ) { //before Oolite v1.79
    				if( index_in_list( dk, ws.$TelescopeDataKeys77 ) > -1 ) {
    					dk += "77"; //use dataKeys with 77 suffix in effectdata.plist
    				}
    			}
    			var p = ps.position;
    			if( dk ) v = system_addVisualEffect( dk, p );
    	//		log("Telescope","VShow2 "+dk+" "+v);//debug
    			if( !v ) { //maybe the dataKey contains partially a name from the shiplib
    				if( w_shiplib && w_shiplib.$ShipLibVP ) {
    					var l = w_shiplib.$ShipLibVP;
    					var d = 0; //0. line contains the default data
    					for( var i = l.length; i >= 0 ; i-- ) //search backward (from orig.ships)
    						if( dk && l && l[i] && dk.indexOf( l[i].k ) > -1 ) {
    							d = i; //found
    							i = -1; //exit from for
    						}
    					v = system_addVisualEffect( l[d].k, p );
    				}
    				if( !v && ws.$TelescopeShowVisualQuestionMark )
    					v = system_addVisualEffect("oolite-unknown-ship", p); //fallback
    				else clearVShip(); //silent fallback
    			}
    	//		player.consoleMessage(v+" "+dk);//debug
    			if( v ) { //must to check it (bugfix)
    	//			log("Telescope","VShow3 "+dk+" "+v);//debug
    					var z = v.collisionRadius / 6; ///
    				vZoom = z; //save zoom value
    				if( z > 0 ) v.scale( 1 / z ); //shrink to the largest size
    				v.scannerDisplayColor1 = null; //hide from the scanner
    				v.scannerDisplayColor2 = null; //hide from the scanner
    	//			ws.$TelescopeV = v; //save
    	//			ws.$TelescopeVUp = 0;
    	//			ws.$TelescopeVShrinkC = ws.$TelescopeVShrinkDelay + ws.$TelescopeVShrinkLength; //shrink after delay
    				setVShip( v, 0, 0, 0 ); // vAlighUp, vShrinkDelay, vShrinkLength
    				if( !isValidFrameCallback( ws.$TelescopeVFCB ) )
    					ws.$TelescopeVFCB = addFrameCallback( ws.$Telescope_VFCB.bind( ws ) );
    				if( !isValidFrameCallback( ws.$TelescopeVFCB2 ) )
    					ws.$TelescopeVFCB2 = addFrameCallback( ws.$Telescope_VFCB2.bind( ws ) );
    				if( !isValidFrameCallback( ws.$TelescopeVFCB3 ) )
    					ws.$TelescopeVFCB3 = addFrameCallback( ws.$Telescope_VFCBVisualTarget.bind( ws ) );
    			}
    		}
    	}
    	function _VFCB( delta ) { //Visual FrameCallBack
    	//	log("Telescope", "VFCB start"); //debug
    		ps = player.ship
    		if( !ps || !ps.isValid ) return; //if player died
    		if( isValidFrameCallback( ws.$TelescopeSteerFCB )
    			&& !ws.$TelescopeSVSync ) return; //call after Steering
    
    		wide = gameWindow.height / gameWindow.width; ///widescreen correction
    		//full size virtual target (and ring also) if weapons offline
    		var vsize = ws.$TelescopeVZoomSize / 10; ///
    		var vsizechanged = false;
    		if( ps.weaponsOnline ) { //gravity scan if weapons turned off
    			if( !ws.$TelescopeShowVisualTarget //remove model if weapons changed back to on
    				&& vShip ) { //and model showing is disabled
    				vShip.remove();
    				vShip = null;
    			}
    			vsize = ws.$TelescopeVSize / 10; ///
    			if( !weaps ) { //state changed to on
    				vsizechanged = true;
    				vShrinkC = 0; //restart counter to rescale
    				weaps = true; //save state
    				telescope_Scan(); //perform visible scan
    			}
    		} else if( weaps ) { //state changed to off
    			vsizechanged = true;
    			vShrinkC = 0; //restart counter to rescale
    			weaps = false; //save state
    			telescope_Scan(); //perform gravity scan
    			telescope_ShowFoundTargetNumber();
    			if( ps.target //repaint visual target and ring if show in weapons off mode only
    				&& ws.$TelescopeFixedTel !== 1 ) {//no visual mode if cheaply fixed
    				clearVShip();
    				showVShip();
    			}
    	//		player.consoleMessage(ps.target);//debug
    		}
    
    		//visual target zoom
    		var vp = ws.$TelescopeVPos;
    		var pos = ps.position.add( ps.vectorForward.multiply( 50 + vp.z ) ); //check effectdata.plist if this line give TypeError: vp is null
    		pos = pos.add( ps.vectorRight.multiply( vp.x ) );
    	//	var srp = pos; //sniper ring position
    		if( !vShip || !vShip.isValid ) {
    			if( vShip || vRing ) {
    	//;			log("Telescope","VFCB remove V:"+ws.$TelescopeV);
    				clearVShip();
    			}
    		} else {
    			var slen = vShrinkLength; //do shrink after delay
    			if( ( vShrinkC >= 0 || vsizechanged )
    				&& vShrinkC <= slen ) {
    				vShrinkC -= delta;
    				var d = 1;
    				if( slen > 0 ) d = ( slen - vShrinkC ) / slen;
    				var z = ( 1 - d * ( 1 - vsize ) ) / vZoom;
    				if( z > 0 ) vShip.scale( z ); //shrink
    				vAlighUp = 3 * d * ( 1 - vsize ) * 1.5; //align to top
    	//			player.consoleMessage(vShrinkC+" "+z);//debug
    			}
    			var up = 12.5;
    			if( !ws.$TelescopeRing )
    				//|| vsize == ws.$TelescopeVZoomSize / 10 ) ///no ring if zoomed - removed since the ring is narrow
    				up += 1; //a bit more higher if no ring
    			else up += 1.25 - 2.5 * vsize; //correction with ring
    	//		player.consoleMessage(up);//debug
    			pos = pos.add( ps.vectorUp.multiply( up - 24 * ( 0.75 - wide ) + vp.y
    				+ vAlighUp ) );
    			vShip.position = pos;
    		}
    
    		//orient vship model
    		var ti = ws.$TelescopeListi;
    		var who = ws.$TelescopeList[ ti - 1 ];
    		var wp = null;
    		if( who && who.isValid && ( who.isPlanet || who.isSun || ws.$Telescope_InRangeFast( who, ti ) ) ) {
    			wp = who.position;
    		} else 
    			wp = ws.$TelescopeListPos[ ti - 1 ]; //last known position
    		if( wp ) {
    			var vd = wp.subtract( ps.position ).direction(); // unit vector towards wp
    			if( vShip && vShip.isValid && !vShip.isPlanet && !vShip.isSun ) {
    				if( who.isVisible ) { //orientation is known only if visible, Grav.Scanner can give position only
    					let ps_heading = ps.heading;
    					var a = ps_heading.angleTo(vd);
    					var c = ps_heading.cross(vd).direction();
    					var o = who.orientation.rotate( c, -a );
    	//	var v2 = ps.position.subtract(wp);
    	//	var fw = ship.vectorForward.angleTo(v2);
    	//	var ri = ship.vectorRight.angleTo(v2);
    	//	var up = ship.vectorUp.angleTo(v2);
    	//	var o = who.orientation;
    	//	var o = who.orientation.multiply( ps.orientation ); //depends on player ori only
    	//	o = o.rotateZ(fw);
    	//	o = o.rotateY(up);
    	//	o = o.rotateX(ri);
    					vShip.orientation = o;
    	// 		( who.orientation.rotate( ps.position.subtract( who.position ) )
    	//			).multiply( ps.orientation );
    			//who.orientation.multiply( ps.orientation );
    				} else { //nonvisible, fixed view only
    					var r = Math.PI / 2 + 0.22; ///ships viewed from top (90 degree plus a bit)
    					let who_isValid = who.isValid;
    					if( who_isValid && who.isStation ) r = Math.PI + 0.22; //stations facing
    					vShip.orientation = ps.orientation.rotate( ps.vectorRight, r );
    					if( who_isValid && who.isMainStation ) //rotate to horizontal dock position
    						vShip.orientation = vShip.orientation.rotate( vShip.vectorForward, Math.PI / 2 );
    	//				var cr = who.collisionRadius;
    	//				log("Telescope", "VFCB who:"+who.name+" cr:"+cr);//debug
    	//				if( cr > 0 ) vShip.scale( 2.5 / cr ); //smaller
    				}
    			}
    		}
    		
    		//large visual target ring
    		if( vShip && vShip.isValid && ws.$TelescopeRing //if not disabled
    			&& ws.$TelescopeFixedTel !== 1 //no ring (nor target model) if cheaply fixed
    			//&& ps.weaponsOnline //and not in zoomed size - removed since the ring is narrow
    			&& vShrinkC <= 0 ) { //and not zooming
    			if( !vRing ) {
    				vRing = system_addVisualEffect("telescope-sniper", pos);
    				vRing.scale( 0.166 * vsize ); //shrink
    	//			vRing.scaleZ = vRing.scaleZ * 0.1; //make flatter
    			} else {
    				vRing.position = pos;
    				if( vsizechanged ) { //if weapons switched on/off set new size (small/large)
    					vRing.scale( 0.166 * vsize ); //shrink
    	//				vRing.scaleZ = vRing.scaleZ * 0.1; //make flatter
    				}
    			}
    			vRing.orientation = ps.orientation;//.rotate( ps.vectorRight, 0 );
    		} else if( vRing ) {
    			vRing.remove();
    			vRing = null;
    		}
    
    	//	ws.$Telescope_VFCBVisualTarget(); //called from separated FCB to avoid timeLimit
    
    	//	the following can reach timeLimit if placed here so must be called from separatd FCB
    	//	for(var i = 0; i < ws.$TelescopeList.length; i+=10; )
    	//		ws.$Telescope_VFCBMarks(Math.floor(i/10)); 
    	}
    	return { clear: clearVShip,
    			  show: showVShip,
    			   fcb: _VFCB };
    }
    
    this.$Telescope_VClearM = function() { //Clear Visual Marker and small sniper ring
    	var ws = worldScripts.telescope;
    	if( ws.$TelescopeVMark ) {
    //; 		log("Telescope", "Remove VMark:"+ws.$TelescopeVMark); //debug
    		ws.$TelescopeVMark.remove();
    // 		log("Telescope", "Removed VMark."); //debug
    		ws.$TelescopeVMark = null;
    	}
    	ws._clearSniperRing();
    }
    /*
    (function() {
    	var ws = worldScripts.telescope;
    	if( isValidFrameCallback( ws.$TelescopeVFCB2 ) ) removeFrameCallback( ws.$TelescopeVFCB2 );
    	var vc = ws._VFCB2_closure();
    	ws._clearSniperRing = vc.clear;
    	ws.$Telescope_VFCB2 = vc.fcb;
    	if( !isValidFrameCallback( ws.$TelescopeVFCB2 ) )
    		ws.$TelescopeVFCB2 = addFrameCallback( ws.$Telescope_VFCB2.bind(ws) );
    })()
    */
    
    this._VFCB2_closure = function() {
    	// 'constant' variables
    	var ws = worldScripts.telescope;
    	var gameWindow = oolite.gameSettings.gameWindow;
    	var TelescopeSniperRingSize = ws.$TelescopeSniperRingSize;
    	// function references
    	var system_addVisualEffect = system.addVisualEffect;
    	// local variables
    	var sniperRing = null; //if this ring guided around the crosshair then the far target is lined up correctly
    	var ps, wide;
    
    	function clearVRing() { //Clear small sniper ring
    		if( sniperRing ) {//remove the small sniper ring
    	//; 		log("Telescope", "Remove SniperRing:"+ws.$TelescopeSniperRing); //debug
    			sniperRing.remove();
    	// 		log("Telescope", "Removed SniperRing."); //debug
    			sniperRing = null;
    		}
    	}
    	function _VFCB2( /*delta*/ ) { //Visual FrameCallBack for the small sniper ring
    		ps = player.ship;
    		var d = 0;
    		var snipertarget = false;
    		var psp, psVF, psVR, psVU;
    		var pst = ps.target;
    		if( ps && pst && pst.isValid
    			&& ws.$TelescopeFixedTel !== 1 ) { //no sniper ring if cheaply fixed
    			if( pst.dataKey && pst.dataKey === "telescopemarker" ) { //get the original target
    				var ti = ws.$TelescopeListi;
    				pst = ws.$TelescopeList[ ti - 1 ];
    			}
    			if( pst && pst.isValid && !pst.isPlanet && !pst.isSun ) {
    				psp = ps.position;
    				d = psp.distanceTo( pst.position );
    				var tcr = pst.collisionRadius;
    				if( d - tcr < ws.$TelescopeSniperRange
    					&& d - tcr > ws.$TelescopeSniperMinRange ) {
    					var m = psp.subtract( pst.position );
    					psVF = ps.vectorForward;
    					if( psVF.angleTo( m ) > 3 ) { //to exclude aft line-up
    	//					player.consoleMessage(ps.vectorForward.angleTo( m ));//debug
    						var vp = ws.$TelescopeVPos;
    						var srp = psp.add( psVF.multiply( 50 + vp.z ) ); //check effectdata.plist if this line give TypeError: vp is null
    						psVR = ps.vectorRight;
    						psVU = ps.vectorUp;
    						srp = srp.add( psVR.multiply( vp.x ) );
    						wide = gameWindow.height / gameWindow.width; ///widescreen correction
    						//distant and smaller target tracked with larger ring movement
    						var ax = -2 * d / tcr * ( psVR.angleTo( m ) - Math.PI / 2 );
    						var ay = -2 * d / tcr * ( psVU.angleTo( m ) - Math.PI / 2 );
    	//					player.consoleMessage(Math.round(ax*100)/100+" "+Math.round(ay*100)/100);//debug
    						if( Math.abs(ax) < 5 &&  Math.abs(ay) < 6 * wide ) { //ay<4 if 4:3, <3.4 if 16:9
    							srp = srp.add( psVU.multiply( vp.y ) );//center of the view_position
    							srp = srp.add( psVR.multiply( ax ) );//show misalignment
    							srp = srp.add( psVU.multiply( ay ) );
    							if( sniperRing ) sniperRing.position = srp;
    							else {
    								sniperRing = system_addVisualEffect("telescope-ring", srp);
    								sniperRing.scale( 0.01 * TelescopeSniperRingSize ); //shrink
    							}
    							sniperRing.orientation = ps.orientation;
    							snipertarget = true;
    						}
    					}
    				}
    			}
    		}
    		if( !snipertarget && sniperRing ) {
    			sniperRing.remove();
    			sniperRing = null;
    		}
    	}
    	return { clear: clearVRing,
    			   fcb: _VFCB2 };
    }
    
    //call these from separated FCBs to avoid timeLimit
    //this.$Telescope_VFCBM0 = function( delta ) { this.$Telescope_VFCBMarks(0); }
    //...
    this.$Telescope_VFCBM0 = function() { this.$Telescope_VFCBMarks('zero'); }
    //cag:  deleted 49 static FCB fns to replace with dynamic FCBs
    
    /*
    (function() {
    	var ws = worldScripts.telescope;
    	for(var i = 1; i < ws.$TelescopeVFCBM.length; i++) {
    		if( isValidFrameCallback( ws.$TelescopeVFCBM[i] ) )
    			removeFrameCallback( ws.$TelescopeVFCBM[i] );
    		ws.$TelescopeVFCBM[i] = null; //cag: converting to static arrays
    	}
    	console.clearConsole();
    })()
    */
    
    this.$Telescope_fcb_Startfrom = 0;
    this.$Telescope_VFCBMarks = function(starting) { //create and update target marker lightballs - new fast code
    //cag: implement dynamic FCBs, as unused ones impact framerate significantly.  
    // 	var $num_fcbs = 1;  			// we initiate w/ FCB in [ 0 ] // for *debug msgs* only
    	var $fcbs_removed = 0;
    
    	function _SetUpFCBs() { // count # FCBs needed and adjust list
    	//cag: implement dynamic FCBs, as unused ones impact framerate significantly
    		var ws = worldScripts.telescope;
    		var TelescopeVFCBM = ws.$TelescopeVFCBM;
    		var Telescope_VFCBMarks = ws.$Telescope_VFCBMarks;
    		var list = ws.$TelescopeList;
    		var i = 4;		// start checking on 2nd group of 4 from TelescopeList
    		var fnum = 1;	// $Telescope_VFCBMarks() is started w/ an FCB in [ 0 ]
    		var fcb;
    //var chg = false; // to reduce # degug msgs
    		do {
    			fcb = TelescopeVFCBM[ fnum ]; // the next FCB in the lists
    			if( list[ i ] ) { // have at least one more, will need next FCB slot
    //				if( !isValidFrameCallback( fcb ) ) {
    // not good enough, as it can take a number of frames for a callback to become registered (ie. isValidFrameCallback to become true)!!!
    				if( fnum > TelescopeVFCBM.length - 1 || fcb === null ) {
    					TelescopeVFCBM[ fnum ] = addFrameCallback( Telescope_VFCBMarks.bind(ws) );
    //				log('telescope', '_SetUpFCBs adding FCB in slot ' + fnum + ', list.len is ' + list.length ); $num_fcbs++; chg = true;
    				}
    			} else if( isValidFrameCallback( fcb ) ) { // have run out of items in TelescopeList, remove any remaining FCBs
    					removeFrameCallback( fcb );
    					TelescopeVFCBM[ fnum ] = null;
    					$fcbs_removed = 5;	// takes 5 frames (on my pc) to complete removal
    //				log('telescope', '_SetUpFCBs removing FCB in slot ' + fnum + ', list.len is ' + list.length ); $num_fcbs--; chg = true;
    			}
    			i += 4;	// each call of $Telescope_VFCBMarks handles 4 items from list
    			fnum++; // the # of the next FCB in the array
    		} while( i < list.length || isValidFrameCallback( fcb ) ); // end of list or have extra FCB to delete
    //	if( chg ) log('telescope', "# of FCBs: " + $num_fcbs + ', List.len is ' + ws.$TelescopeList.length );
    	}
    
    //	log("Telescope", "VFCBMarks start from "+startfrom); //debug
    	//need call from FrameCallBack to compensate player movement
    	var ws = worldScripts.telescope;
    	var startfrom = starting;	// never modify function args
    	var prept = ws.$TelescopeList; // local
    // - insert - ////////////////////////////////////////////////////////
    	if( startfrom === 'zero' ) { // is 'zero' on 1st call, delta on the rest
    		_SetUpFCBs();
    		ws.$Telescope_fcb_Startfrom = 0;
    	} else { 
    		ws.$Telescope_fcb_Startfrom++;
    	}
    	startfrom = ws.$Telescope_fcb_Startfrom;
    // - end - ///////////////////////////////////////////////////////////
    	var co = 4*startfrom, cl = prept.length; // set loop conditions from local instead
    //	if( co > cl ) return; //early exit over the end of TelescopeList
    // - insert - ////////////////////////////////////////////////////////
    	if( cl && co > cl ) { // happens when decr # of FCBs, as changes are not applied until next frame (or later?) so don't panic
    		if( $fcbs_removed > 0 ) {
    			$fcbs_removed--;
    		} else if( prept.length > ws.$TelescopeVFCBM * 4 ) {
    			log('telescope', 'do not have enough FCBs!!!!!!  List.length: ' + prept.length + ' TelescopeVFCBM: ' + ws.$TelescopeVFCBM );
    		} else {										// ok, now you can panic
    			log('telescope', 'should never happen anymore!!!!!!  co > cl: ' + co + ' > ' + cl );
    		}
    		return; //early exit over the end of TelescopeList
    	}
    // - end - ///////////////////////////////////////////////////////////
    	var ce = co+4; //end of the loop, 4 item only in one FCB to avoid timelimit
    	var ps = player.ship;
    	if( !ps || !ps.isValid ) return; //exit if player died
    	var dir = null, dt = null, md = null, pos = null, tpos = null;
    
    	var prepc = ws.$TelescopeVMCs; // local, colour calculated in timer to save CPU
    // 	var prepd = ws.$TelescopeRedAlertDist; //show lollipops in red alert within this distance only
    //	var prepm = ws.$TelescopeMMarks; // removed
    	var prepp = ws.$TelescopeListPos; // local
    	var prepr = ws.$TelescopeMRings; // local
    	var prepv = ws.$TelescopeVMarks; // local
    	var pla = player.alertCondition;
    	var psf = ps.vectorForward; // local
    	var pso = ps.orientation; //local
    	var psp = ps.position; //local
    	var scr = ps.scannerRange; //local
    
    	var angle = 0; //thin masslock border outside this angle
    	var bl = false; //black lollipops in red alert
    	if( pla > 1 && ps.weaponsOnline ) bl = true;
    	var bla = [0,0,0]; //the color of black lollipops is black in red alert
    	if( pla === 2 ) bla = [0.1, 0.1, 0.1]; //and very dark grey in yellow alert
    	var cm = null, cmf = null, cmt = null; //dataKey holders of normal, far and thin masslock borders
    	var fardist = 300000; //far masslock border over this distance (station or gravity scanner target)
    	var farplanet = 30; //far masslock border distance multiplier (an average 5000km planet over 1500km distance)
    	var mb = ws.$TelescopeMassLockBorders && ( pla === 1 || !ps.weaponsOnline );
    	var mb2 = "";
    	if( ws.$TelescopeBrightMassLockBorders ) mb2 = "2";
    	var mrs = 41.8; //masslock ring scale, used in var rsc and below for planets also
    	var rsc = scr / mrs; //masslock ring size
    	var scr2 = 2 * scr; //maximal distance of thin border
    	var ti = ws.$TelescopeListi - 1; //exclude current target from black lollipops
    	
    	//following part is very CPU consuming, with 100 ships 60fps need 6000 cycle/sec, so code wisely
    	for(var i=co; i<cl && i<ce; i++){ //process 5 marker only at once to avoid timeLimit
    		var t = prept[i], c = prepc[i], r = prepr[i], v = prepv[i]; // local //m = prepm[i],
    		if( !c || !t || !t.isValid ) {
    			tpos = null;
    			if( v ) {
    				v.remove(); //without this red ball remain after destroyed pirates
    				prepv[i] = null;
    			}
    			if( r ) {
    				r.remove();
    				prepr[i] = null;
    			}
    //			if( m ) {
    //				m.remove();
    //				ws.$TelescopeMMarks[i] = null;
    //			}
    		} else if( c.indexOf("gray") > -1 ) tpos = prepp[i]; //last known position only
    		else tpos = t.position;
    		if( tpos ) { //check to avoid invalid target and if ListPos[i] is null
    			dir = tpos.subtract( psp ).direction();
    			dt = psp.distanceTo( tpos ); //distance to the target (or to last known pos)
    			if( dt > scr ) { //target is out of scanner range
    				md = scr - 600; //marker distance
    				//do not set closer to the range to do not left behind aft markers during torus travel
    				if( mb && !t.isSun ) { //masslock borders allowed, except for the sun
    					cm = c.substr(0, c.indexOf("_"))+mb2+"_ml"; //dataKey with colour
    					cmf = cm+"f"; //dataKey of coloured far masslock border
    					cmt = cm+"t"; //dataKey of coloured thin masslock border
    //					log("Telescope", cm );//debug
    //					var ml = false;  //masslock field - removed
    					if( r && r.dataKey !== cm && r.dataKey !== cmf && r.dataKey !== cmt ) {
    						r.remove(); //colour changed, forced to recreate
    						prepr[i] = r = null;
    					}
    					//following conditions determine the thickness of ring, need thinner from close
    					//if( t.isSun ) cm = cmt; //set thin masslock border, the sun always get thin border
    					//else 
    					if( dt > fardist ) { //change to far
    						if( t.isPlanet ) {
    							if( dt > farplanet * t.radius ) {
    								cm = cmf; //set far masslock border
    								if( r && r.dataKey.substr(-1) !== "f" ) {
    									r.remove();
    									prepr[i] = r = null;
    								}
    							} else if( dt < 3 * t.radius ) {
    								cm = cmt; //set thin masslock border
    								if( r && r.dataKey.substr(-1) !== "t" ) {
    									r.remove();
    									prepr[i] = r = null;
    								}
    							} else {
    //								cm = cm; //set normal masslock border (default)
    								if( r && r.dataKey.substr(-1) !== "l" ) {
    									r.remove();
    									prepr[i] = r = null;
    								}
    							}
    						} else { //is not planet but far
    							cm = cmf; //set far masslock border
    							if( r && r.dataKey.substr(-1) !== "f" ) {
    								r.remove();
    								prepr[i] = r = null;
    							}
    						}
    					} else { //check for thin border within 2x scanner range
    						//and over 45 degree mean ship is near and out of sight
    						//so if ring is in screen then can be close so need thin border
    						if( dt < scr2 //45 degree = 2.9 radian
    							&& (angle = psf.angleTo(psp.subtract(tpos))) < 2.9
    							//and crosshairs points out of ring which is closer
    							&& Math.sin( angle ) > scr / dt ) {
    							cm = cmt; //set thin masslock border
    							if( r && r.dataKey.substr(-1) !== "t" ) {
    								r.remove();
    								prepr[i] = r = null;
    							}
    						} else {
    //							cm = cm; //set normal masslock border (default)
    							if( r && r.dataKey.substr(-1) !== "l" ) {
    								r.remove();
    								prepr[i] = r = null;
    							}
    						}
    					}
    					if( r ) { //move masslock ring
    						r.position = tpos;
    //						if( cm == cmt ) {//stop further rotating if crosshairs points outside of ring
    //							r.orientation = (psp.subtract(tpos)).rotationTo([0, 0, 1]).normalize();
    //							r.orientation = psp.subtract(tpos).rotationTo(psf, angle).normalize();
    //							var psu = ps.vectorUp;
    //							var ua = psu.angleTo(psp.subtract(tpos));//up angle
    							//var borderangle = Math.asin( scr / dt );
    							//r.orientation = pso.rotate([1, 1, 0], Math.asin(scr / dt) );
    //						} else 
    							r.orientation = pso;
    					} else { //create masslock ring
    						if( t.isPlanet  ) { //|| t.isSun ) {
    							r = system.addVisualEffect( cm, tpos );
    							if( r ) r.scale( ( t.radius + Math.max( t.radius, scr ) ) / mrs ); ///
    //							log("Telescope", "Planet radius: "+t.radius );
    						} else if( !t.isRock && !t.isDerelict
    							&& t.scanClass !== "CLASS_BUOY" && t.scanClass !== "CLASS_CARGO"
    							&& !t.isWormhole && !t.isCloaked ) {
    //							ml = true;  //target
    							r = system.addVisualEffect( cm, tpos ); //cm is set above
    							if( r ) r.scale( rsc ); //ring radius=scanner range
    						}
    						if( r ) {
    							r.orientation = pso;
    							prepr[i] = r;
    						}
    					}
    //					if( m ) m.position = tpos; //huge lightball - replaced with MRings
    //					else {
    //						if( ml ) {
    //							ws.$TelescopeMMarks[i] = //show masslockfield
    //								system.addVisualEffect( "telescope-masslockfield", tpos );
    //						} else ml = false;
    //					}
    				} else { //masslock borders turned off with primable menu or weapons on and alert is not green
    					if( r ) {
    						r.remove();
    						prepr[i] = null;
    					}
    //					if( m ) {
    //						m.remove();
    //						ws.$TelescopeMMarks[i] = null;
    //					}
    				}
    			} else { //target is within scanner range (circle is surely out of view)
    				md = scr + 300; //show the visual marker only without shadow lolipop
    				if( r ) {
    					r.remove();
    					prepr[i] = null;
    				}
    //				if( m ) { //remove masslockfield
    //					m.remove();
    //					ws.$TelescopeMMarks[i] = null;
    //				}
    			}
    			pos = psp.add( dir.multiply( md - i ) ) //anti-flicker difference
    				.add( ps.heading.direction().multiply( ps.speed / 25 ) ); ///torus speed need correction
    			if( !v ) {
    				ws.$TelescopeVMarks[i] = system.addVisualEffect( c, pos );
    //				log("Telescope", "VMarks["+i+"]:"+ws.$TelescopeVMarks[i]+" c:"+c+" pos:"+pos); //debug
    			} else {
    				if( v.dataKey !== c ) { //colour or size changed
    					v.remove();
    					prepv[i] = system.addVisualEffect( c, pos );
    				} else {
    					v.position = pos;
    				}
    				if( bl && i !== ti ) //black lollipops exccept the current target
    					v.scannerDisplayColor1 = bla;
    				else v.scannerDisplayColor1 = null; //original color from effectdata.plist
    			}
    		}
    	}	
    //	log("Telescope", "VFCBMarks end"); //debug
    }
    
    this.$Telescope_VFCBMarksOld = function() { //create and update target marker lightballs - old slow code
    	var ps = player.ship;
    	if( !ps || !ps.isValid ) return; //exit if player died
    	var ws = worldScripts.telescope;
    	var tl = ws.$TelescopeList, i, md, v = 0, ball = '';
    	//following part is very CPU cunsuming, with 100 ships 60fps need 6000 cycle/sec, so code wisely
    	for( i = 0; i < tl.length; i++ ) {
    		var col = "pink"; //invalid target
    		var tpos = null;
    		var t = tl[i];
    		if( !t || !t.isValid || !t.dataKey ) { //sun and planets has no dataKey
    			v = ws.$TelescopeVMarks[i];
    			if( v ) {
    				v.remove(); //without this red ball remain after destroyed pirates
    				ws.$TelescopeVMarks[i] = null;
    			}
    		} else if( !t.isBeacon && //buoy in the whole system, others in range
    			!ws.$Telescope_InRange( t ) ) {
    			tpos = ws.$TelescopeListPos[ i ];
    			col = "gray"; //last known position only
    		} else {
    			ws.$TelescopeListPos[ i ] = t.position; //update until InRange
    			if( t.isStation || t.dataKey.indexOf("buoy") > -1 )
    				col = "green"; //base or buoy, do not use isBeacon to exclude ships with beaconCode
    			else if( t.isVisible || ps.position.distanceTo( t.position ) < ps.scannerRange ) {
    				if( t.isPolice ) col = "purple"; //police
    				else if( t.isCargo || t.primaryRole === "escape-capsule" //cargo, esc.pod,
    					|| t.isRock ) col = "white"; //or rock
    				else if( t.isWormhole || t.isDerelict ) col = "blue"; //wormhole from Oolite v1.79
    				else if( t.bounty > 0 ) col = "red"; //pirate or thargoid with bounty
    				else if( t.isWeapon ) col = "cyan"; //missile or mine
    				else col = "yellow"; //other ship with clean status
    			} else if( t.mass >= 130000 ) col = "orange"; //large ship over the visible range
    			else col = "brown"; //small ship over the visible range
    			if( ws.$TelescopeLightBalls //if ship lightballs are disabled
    				|| col === "blue" || col === "cyan" || col === "green" || col === "white" )
    				tpos = t.position; //then show others only
    		}
    		if( tpos ) { //check to avoid invalid target and if ListPos[i] is null
    			var dir = tpos.subtract( ps.position ).direction();
    			var dt = ps.position.distanceTo( tpos ); //distance to the target (or to last known pos)
    			if( dt > ps.scannerRange ) md = ps.scannerRange - 100; //marker distance
    			//do not set closer to the range to do not left behind aft markers during torus travel
    			else md = ps.scannerRange + 100; //show the visual marker only without shadow lolipop
    			var pos = ps.position.add( dir.multiply( md + 50 * Math.random() ) ) //anti-flicker randomness
    				.add( ps.heading.direction().multiply( ps.speed / 60 ) ); ///torus speed need correction
    			var tcr = 0;
    			if( t && t.isValid ) tcr = t.collisionRadius;
    			if( dt < ws.$TelescopeSniperRange + tcr ) ball = "ball"; //large size
    			else ball = "marker"; //average size
    			if( dt < ws.$TelescopeVMarkMinDist + tcr
    				//in red alert show nearest large ball marked targets only to save CPU and clean scanner
    				|| player.alertCondition > 2 && ps.weaponsOnline && ball !== "ball" ) {
    				var vmi = ws.$TelescopeVMarks[i];
    				if( vmi ) vmi.remove();
    				ws.$TelescopeVMarks[i] = null;
    			} else {
    				if( dt > 150000 ) col += "small"; //over 150km show smaller ball
    				v = ws.$TelescopeVMarks[i];
    				if( !v ) {
    					v = ws.$TelescopeVMarks[i] =
    						system.addVisualEffect("telescope-"+col+ball, pos);
    				} else if( v.dataKey !== "telescope-"+col+ball ) { //colour or size changed
    					v.remove();
    					v = ws.$TelescopeVMarks[i] =
    						system.addVisualEffect("telescope-"+col+ball, pos);
    				} else v.position = pos;
    			}
    		}
    	}
    //	ws.$Telescope_VFCBShrinkVMarks();//should not needed
    }
    
    this.$Telescope_VFCBShrinkVMarks = function() {
    	var ws = worldScripts.telescope;
    	var i = ws.$TelescopeList.length;
    	while( i < ws.$TelescopeVMarks.length) { //shrink VMarks to match List
    		if( ws.$TelescopeVMarks[i] ) ws.$TelescopeVMarks[i].remove();
    		ws.$TelescopeVMarks[i] = null;
    		i++;
    	}
    	i = ws.$TelescopeList.length;
    	while( i < ws.$TelescopeMMarks.length) { //shrink VMarks to match List
    		if( ws.$TelescopeMMarks[i] ) ws.$TelescopeMMarks[i].remove();
    		ws.$TelescopeMMarks[i] = null;
    		i++;
    	}
    	i = ws.$TelescopeList.length;
    	while( i < ws.$TelescopeMRings.length) { //shrink VMarks to match List
    		if( ws.$TelescopeMRings[i] ) ws.$TelescopeMRings[i].remove();
    		ws.$TelescopeMRings[i] = null;
    		i++;
    	}
    }
    
    this._VFCBVisualTarget_closure = function() { //orient vship model, make and update shadow and vmarkship
    	// 'constant' variables
    	var ws = worldScripts.telescope;
    	var ps, ps_scannerRange, ps_target, ps_heading, ps_position, who_isPlanet, who_isSun, who_isValid;
    	var vm, mp, wdn, who;
    	// function references
    	var system_addShips = system.addShips;
    	var system_addVisualEffect = system.addVisualEffect;
    	var telescope_VClear = ws.$Telescope_VClear;
    	var telescope_VClearM = ws.$Telescope_VClearM;
    	var telescope_InRangeFast = ws.$Telescope_InRangeFast;
    	var telescope_PlanetName = ws.$Telescope_PlanetName;
    	var telescope_Scan = ws.$Telescope_Scan;
    		
    	function _VFCBVisualTargetMarker() {
    		if( vm ) vm.remove();
    		vm = system_addShips("telescopemarker", 1, mp, 1)[0];
    		ws.$TelescopeVMark = vm;
    		if( wdn && vm && vm.isValid ) vm.displayName = wdn;
    	//;	log("Telescope", "VMarkShip:"+vm);//debug
    		if( vm && vm.isValid && (ps_target === who || who_isPlanet || who_isSun) && ps_target !== vm ) {
    	//		var v = ws.$TelescopeVMarks[ws.$TelescopeListi-1];
    	//		if( v && v.isValid ) 
    			vm.scannerDisplayColor1 = vm.scannerDisplayColor2 = [0.2, 0.2, 0.2];
    			//change the lock to the markership when step over normal scanner range
    			ws.$TelescopeTargetSet = true;//to avoid doubled list item message
    	//;		log("Telescope", "Vtarget change n:"+n+"km from:"+vm+" to:"+who);//debug
    			ps.target = ps_target = vm;
    			ws.$TelescopeTarget = who; //save the real target after(!) set
    	//;		log("Telescope", "Vtarget changed n:"+n+"km from:"+vm+" to:"+who);//debug
    			ws.$TelescopeTargetSet = false;
    		}
    		return(vm);
    	}
    	function _VFCBVisualTarget() { // make and update shadow and vmarkship
    		ps = player.ship;	// need this here unless we export a fn to reset value when player changes ship
    		if( !ps || !ps.isValid ) return; //if player died
    		ps_scannerRange = ps.scannerRange; // needed in case scannerRange can chg over course of a game (new equipment?)
    		ps_target = ps.target; 
    		if( !ws.$TelescopeAutoLock && !ps_target ) return;
    	//	ws.$TelescopeV.orientation = ps.orientation;
    		var ti = ws.$TelescopeListi;
    		who = ws.$TelescopeList[ ti - 1 ];
    		var wp = null;
    		if( !who ) {
    	//;		log("Telescope","VFCBVisualTarget ti:"+ti+" who:"+who);
    			telescope_VClear();
    		} else {
    			who_isPlanet = who.isPlanet;
    			who_isSun = who.isSun;
    			who_isValid = who.isValid;
    			wdn = "";
    			if( who_isValid && ( who_isPlanet || who_isSun || telescope_InRangeFast( who, ti ) ) ) {
    				wp = who.position;
    				if( who_isPlanet || who_isSun ) wdn = telescope_PlanetName(who);
    				else wdn = who.displayName; //remove km from ident message
    			} else wp = ws.$TelescopeListPos[ ti - 1 ]; //last known position
    			if( !wp ) return; //bugfix after jump
    			ps_heading = ps.heading; 
    			ps_position = ps.position;
    			var vd = wp.subtract( ps_position ).direction(); //used below for target marker also		
    			
    			//current target marker
    			var md = ps_scannerRange - 499.6; //shadow lolipop nearer than others to reduce flickering
    				//do not set nearer to do not left behind aft markers during torus travel
    			mp = ps_position.add(vd.multiply(md))
    				.add(ps_heading.direction().multiply(ps.speed/25));
    			vm = ws.$TelescopeVMark;
    			var n = ps_position.distanceTo( wp );
    			if( who_isPlanet || who_isSun ) n -= who.radius;
    			else n += who.collisionRadius;
    			if( n < ps_scannerRange ) { //too near, replace the markership with visuall effect shadow lollipop
    				if( who_isPlanet || who_isSun ) { //stay with marker
    					mp = ps_position.add(vd.multiply(Math.min(md, n-299.6)
    						)).add(ps_heading.direction().multiply(ps.speed/25));
    					if( !vm || !vm.isValid || ws.$TelescopePrevPlanet !== who ) {
    						ws.$TelescopePrevPlanet = who;
    						vm = _VFCBVisualTargetMarker();
    	//;					log("Telescope", "Markwho: "+who+" "+n+" m target:"+ps.target+" "+vm);
    					} else {
    						vm.position = mp;
    					}
    				} else {
    					if( vm && !ps_target ) //must check player target to remove shadow marker after scooped
    						telescope_VClearM();
    					else if( who && who_isValid && vm && ps_target === vm && ps_target !== who ) {
    						//change the lock to the real target when reached normal scanner range
    						ws.$TelescopeTargetSet = true;//to avoid doubled list item message
    	//;					log("Telescope", "Vtarget change n:"+n+"km from:"+vm+" to:"+who);//debug
    						if( !who_isPlanet && !who_isSun ) ps.target = ps_target = who;
    						if( ps.target !== who && //yes, it is possible! Hohoho showed when a ship jumped!
    	//						player.consoleMessage("Hohoho!");//debug
    					//bugfix against non-lockable Military Jammer awarded by ShipVersion to avoid repeated scan
    							ws.$TelescopePrevWho !== who ) {
    	//						player.consoleMessage("Hohoho!");//debug
    	//;					log("Telescope", "ScanV: "+who+" - "+ws.$TelescopePrevWho);//debug
    							ws.$TelescopePrevWho = who; //store to scan only once
    							telescope_Scan(); //forced scan to remove the invalid ball
    						}
    						ws.$TelescopeTarget = ps_target; //save the real target after(!) set
    	//;					log("Telescope", "Vtarget changed n:"+n+"km from:"+vm+" to:"+who);//debug
    						ws.$TelescopeTargetSet = false;
    					}
    					if( vm && !vm.isVisualEffect ) { vm.remove(); vm = null; } //remove markership
    					if( !vm && ps_target ) { //check player target to remove shadow marker after scooped
    						vm = system_addVisualEffect("telescope-shadow", mp);
    						ws.$TelescopeVMark = vm;
    					} else if( vm ) vm.position = mp;
    				}
    			} else if( !vm || vm.isVisualEffect ) { //markership to can virtually target far ships
    				vm = _VFCBVisualTargetMarker();
    			} else vm.position = mp;
    			if( vm && vm.isValid && !vm.isVisualEffect ) {
    				if( n >= ps_scannerRange ) {
    					n -= ps.collisionRadius;
    					if( !who_isPlanet && !who_isSun ) n -= 2 * who.collisionRadius;
    					var km = Math.floor( n / 1000 );
    					if( km < 100 ) {
    						var m = Math.floor( n - km * 1000 ); //rest after km
    						var lz = ""; //leading zeros
    						if( m < 10 ) lz = "00";
    						else if( m < 100 ) lz = "0";
    						//ws.$TelescopeVMark.name =
    						ws.$TelescopeVMark.displayName =
    	//						formatInteger( Math.round( n ) ) + //missing a dot after km
    							km + "." + lz + m + " km " + wdn;
    					} else {
    						if( km < 1000000 ) ws.$TelescopeVMark.displayName = km + " km " + wdn;
    						else ws.$TelescopeVMark.displayName = Math.floor( km / 1000000 ) + " Mkm " + wdn;
    					}
    				} else ws.$TelescopeVMark.displayName = wdn; //clear previous km
    				ws.$TelescopeVMark.velocity = ps.velocity;//keep target over Torus speeds in FarPlanets OXP
    			}
    		}
    	//;	log("Telescope", "VFCB end, target:"+ps.target); //debug
    	}
    	return _VFCBVisualTarget;
    }
    
    Scripts/telescope_refundeq.js
    "use strict";
    this.name        = "telescope_refundeq";
    this.author      = "Norby";
    this.copyright   = "2013 Norbert Nagy";
    this.licence     = "CC BY-NC-SA 3.0";
    this.description = "Refund an equipment if undamaged and not cheaply repaired only.";
    this.version     = "1.0";
    
    this.allowAwardEquipment = function(eqKey, ship, context)
    {
    	if(eqKey==="EQ_TELESCOPE_REFUND" && ship === player.ship && context === "purchase"
    		&& player.ship.equipmentStatus("EQ_TELESCOPE") === "EQUIPMENT_OK"
    		&& missionVariables.$TelescopeFixedTel != 1 )
    		return true;
    	if(eqKey==="EQ_TELESCOPEEXT_REFUND" && ship === player.ship && context === "purchase"
    		&& player.ship.equipmentStatus("EQ_TELESCOPEEXT") === "EQUIPMENT_OK"
    		&& missionVariables.$TelescopeFixedTel != 1 )
    		return true;
    	if(eqKey==="EQ_GRAVSCANNER_REFUND" && ship === player.ship && context === "purchase"
    		&& player.ship.equipmentStatus("EQ_GRAVSCANNER") === "EQUIPMENT_OK"
    		&& missionVariables.$TelescopeFixedGS != 1 )
    		return true;
    	if(eqKey==="EQ_GRAVSCANNER2_REFUND" && ship === player.ship && context === "purchase"
    		&& player.ship.equipmentStatus("EQ_GRAVSCANNER2") === "EQUIPMENT_OK"
    		&& missionVariables.$TelescopeFixedGS != 1 )
    		return true;
    	if(eqKey==="EQ_SMALLDISH_REFUND" && ship === player.ship && context === "purchase"
    		&& player.ship.equipmentStatus("EQ_SMALLDISH") === "EQUIPMENT_OK"
    		&& missionVariables.$TelescopeFixedSD != 1)
    		return true;
    	if(eqKey==="EQ_LARGEDISH_REFUND" && ship === player.ship && context === "purchase"
    		&& player.ship.equipmentStatus("EQ_LARGEDISH") === "EQUIPMENT_OK"
    		&& missionVariables.$TelescopeFixedLD != 1)
    		return true;
    	return false;
    }
    
    Scripts/telescope_repaireq.js
    "use strict";
    this.name        = "telescope_repaireq";
    this.author      = "Norby";
    this.copyright   = "2013 Norbert Nagy";
    this.licence     = "CC BY-NC-SA 3.0";
    this.description = "Repair an equipment if damaged only, full repairs if cheaply fixed.";
    this.version     = "1.0";
    
    this.allowAwardEquipment = function(eqKey, ship, context)
    {
    	if(eqKey==="EQ_TELESCOPE_REPAIR" && ship === player.ship && context === "purchase"
    		&& player.ship.equipmentStatus("EQ_TELESCOPE") === "EQUIPMENT_DAMAGED")
    		return true;
    	if(eqKey==="EQ_GRAVSCANNER_REPAIR" && ship === player.ship && context === "purchase"
    		&& player.ship.equipmentStatus("EQ_GRAVSCANNER") === "EQUIPMENT_DAMAGED")
    		return true;
    	if(eqKey==="EQ_GRAVSCANNER2_REPAIR" && ship === player.ship && context === "purchase"
    		&& player.ship.equipmentStatus("EQ_GRAVSCANNER2") === "EQUIPMENT_DAMAGED")
    		return true;
    	if(eqKey==="EQ_SMALLDISH_REPAIR" && ship === player.ship && context === "purchase"
    		&& player.ship.equipmentStatus("EQ_SMALLDISH") === "EQUIPMENT_DAMAGED")
    		return true;
    	if(eqKey==="EQ_LARGEDISH_REPAIR" && ship === player.ship && context === "purchase"
    		&& player.ship.equipmentStatus("EQ_LARGEDISH") === "EQUIPMENT_DAMAGED")
    		return true;
    	if(eqKey==="EQ_TELESCOPE_FULLREPAIR" && ship === player.ship && context === "purchase"
    		&& missionVariables.$TelescopeFixedTel == 1 )
    		return true;
    	if(eqKey==="EQ_GRAVSCANNER_FULLREPAIR" && ship === player.ship && context === "purchase"
    		&& missionVariables.$TelescopeFixedGS == 1 )
    		return true;
    	if(eqKey==="EQ_GRAVSCANNER2_FULLREPAIR" && ship === player.ship && context === "purchase"
    		&& missionVariables.$TelescopeFixedGS == 1 )
    		return true;
    	if(eqKey==="EQ_SMALLDISH_FULLREPAIR" && ship === player.ship && context === "purchase"
    		&& missionVariables.$TelescopeFixedLD == 1)
    		return true;
    	if(eqKey==="EQ_LARGEDISH_FULLREPAIR" && ship === player.ship && context === "purchase"
    		&& missionVariables.$TelescopeFixedLD == 1)
    		return true;
    	return false;
    }
    
    Scripts/telescopeeq.js
    "use strict";
    this.name        = "telescopeeq";
    this.author      = "Norby";
    this.copyright   = "2013 Norbert Nagy";
    this.licence     = "CC BY-NC-SA 4.0";
    this.description = "Commands and settings, press mode to cycle, activate to accept or change.";
    this.version     = "1.9";
    
    this.$Telescopeeq_Menu = [ [ "Nearest target" ],
    	[ "Rescan" ],
    	[ "Step forward in the target list" ],
    	[ "Step back in the target list" ],
    	[ "Lightballs:", "off", "navigation only", "ships", "masslock borders", "bright masslock borders", "large" ],
    	[ "Sniper ring km:", "off", "5-25.6", "10-25.6", "15-25.6", "5-30", "10-30", "15-30"], //off=10-10
    	[ "Steering:", "off", "nearest target only", "both nearest and step in the list"],
    	[ "Targets:", "20 and limitation in red alert", 50, 100, 200 ],
    	[ "Visual target:", "off", "weapons off", "no ring", "no station", "no question mark", "all" ],
    	[ "Visual target size:", 1, 2, 3, 4, 5, 6, 7, 8 ] ];
    this.$Telescopeeq_MenuItem = 0; //current item in the commands or settings menu
    
    //equipment events
    this.activated = function ()
    {
    	var w = worldScripts["telescope"];
    	var menu = this.$Telescopeeq_Menu;
    	var item = this.$Telescopeeq_MenuItem;
    	if( item < 4 ) { //commands
    		switch( item ) {
    			case 0: //Nearest target
    				w.$Telescope_Nearest(); //lock the nearest target
    				if( w.$TelescopeSteering > 0 )
    					w.$Telescope_Steer();//turn to the target
    				break;
    			case 1: //Rescan
    				w.$TelescopeListi = w.$TelescopeList.length;
    				w.$Telescope_List( 1 ); //rescan and show found due to the end of list
    				break;
    			case 2: //Step forward in the target list
    				w.$Telescope_List( 1 );//step forward, scan after the end of the list
    				if( w.$TelescopeSteering > 1 )
    					w.$Telescope_Steer();//turn to the target
    				break;
    			case 3: //Step back in the target list
    				w.$Telescope_List( -1 );//step forward, scan after the end of the list
    				if( w.$TelescopeSteering > 1 )
    					w.$Telescope_Steer();//turn to the target
    				break;
    		}
    //		player.consoleMessage( menu[ this.$Telescopeeq_MenuItem ][ 0 ], 5 );//debug
    	} else { //settings
    		var subitem = this.$Telescopeeq_GetSubItem( item ) + 1; //step to the next subitem in the current settings
    		var submenu = menu[ item ];
    		if( subitem >= submenu.length ) subitem = 1; //back to the first subitem (0. is the menuitem)
    		switch( item ) {
    			case 4: //Lightballs
    				missionVariables.$TelescopeMenuLightballs = subitem;
    				w.$Telescope_SetLightballs( subitem );
    				break;
    			case 5: //Sniper ring km
    				missionVariables.$TelescopeMenuSniper = subitem;
    				w.$Telescope_SetSniper( subitem );
    				break;
    			case 6: //Steering
    				missionVariables.$TelescopeMenuSteering = subitem;
    				w.$Telescope_SetSteering( subitem );
    				break;
    			case 7: //Targets
    				missionVariables.$TelescopeMenuTargets = subitem;
    				w.$Telescope_SetTargets( subitem );
    				break;
    			case 8: //Visual target
    				missionVariables.$TelescopeMenuVisual = subitem;
    				w.$Telescope_SetVisual( subitem );
    				w.$Telescope_VClearS();
    				if( w.$TelescopeFixedTel != 1 )
    					w.$Telescope_VShow(); //repaint visual target and ring
    				else player.consoleMessage("Your cheaply fixed Telescope can not show visual target, buy full repair.",5);
    //				player.consoleMessage(w.$TelescopeVSize+" "+w.$TelescopeVZoomSize ); //debug
    				break;
    			case 9: //Visual target size
    				missionVariables.$TelescopeMenuVisualSize = subitem;
    				if(!(missionVariables.$TelescopeMenuVisual > 0)
    					|| missionVariables.$TelescopeMenuVisual == 1 ) {
    					missionVariables.$TelescopeMenuVisual = 2; //must at least wp off
    					w.$Telescope_SetVisual( 2 );
    				}
    				w.$Telescope_SetVisualSize( subitem );
    				w.$Telescope_VClearS();
    				if( w.$TelescopeFixedTel != 1 )
    					w.$Telescope_VShow(); //repaint visual target and ring
    				else player.consoleMessage("Your cheaply fixed Telescope can not show visual target, buy full repair.",5);
    //				player.consoleMessage(subitem+" "+w.$TelescopeVSize+" "+w.$TelescopeVZoomSize ); //debug
    				break;
    		}
    		//show subitem in settings
    		player.consoleMessage( menu[ this.$Telescopeeq_MenuItem ][ 0 ]
    			+" "+menu[ this.$Telescopeeq_MenuItem ][ subitem ], 5 );
    	}
    }
    
    this.mode = function ()
    {
    	this.$Telescopeeq_MenuItem++;//step forward in the current menu
    	var menu = this.$Telescopeeq_Menu;
    	if( this.$Telescopeeq_MenuItem >= menu.length ) this.$Telescopeeq_MenuItem = 0; //back to the first menu item
    	var sub = "";
    	var item = this.$Telescopeeq_MenuItem;
    	var subitem = this.$Telescopeeq_GetSubItem( item );
    	if( menu[ item ][ subitem ] ) sub = " " + menu[ item ][ subitem ]; //no subitem with commands
    	player.consoleMessage( menu[ item ][ 0 ] + sub, 5 );
    }
    
    
    //Telescope primable equipment method
    this.$Telescopeeq_GetSubItem = function ( item ) {
    	var w = worldScripts["telescope"];
    	switch( item ) {
    		case 4: //Lightballs
    			var m = missionVariables.$TelescopeMenuLightballs;
    			if( m > 0 ) return( m );
    			if( !w.$TelescopeLightBalls ) return( 1 ); //off
    			if( !w.$TelescopeShipLightBalls ) return( 2 ); //ship off
    			if( !w.$TelescopeMassLockBorders ) return( 3 ); //masslockborders off
    			if( !w.$TelescopeLargeLightBalls ) return( 4 ); //small
    			return( 4 ); //large
    		case 5: //Sniper ring km
    			var m = missionVariables.$TelescopeMenuSniper;
    			if( m > 0 ) return( m );
    			var max = w.$TelescopeSniperRange;
    			var min = w.$TelescopeSniperMinRange;
    			if( max == min ) return( 1 ); //off
    			var p = 0; //max = 25.6km
    			if( max > 25600 ) p = 3; //add this to reach subitems with max = 30km
    			if( min <= 5000 ) return( 2 + p ); // "5-25.6" or "5-30"
    			if( min >= 15000 ) return( 4 + p ); // "15-25.6" or "15-30"
    			return( 3 + p ); // "10-25.6" or "10-30"
    		case 6: //Steering
    			var m = missionVariables.$TelescopeMenuSteering;
    			if( m > 0 ) return( m );
    			return( w.$TelescopeSteering + 1 );
    		case 7: //Targets
    			var m = missionVariables.$TelescopeMenuTargets;
    			if( m > 0 ) return( m );
    			if( w.$TelescopeRedAlertLimiter ) return( 1 ); //20 and limitation in red alert
    			if( w.$TelescopeTargets <= 50 ) return( 2 ); //50
    			if( w.$TelescopeTargets >= 200 ) return( 4 ); //200
    			return( 3 ); //100
    		case 8: //Visual target
    			var m = missionVariables.$TelescopeMenuVisual;
    			if( m > 0 ) return( m );
    			if( w.$TelescopeVZoomSize <= 0 ) return( 1 ); //off
    			if( !w.$TelescopeShowVisualTarget ) return( 2 ); //weapons off
    			if( !w.$TelescopeRing ) return( 3 ); //no ring
    			if( !w.$TelescopeShowVisualStation ) return( 4 ); //no station
    			if( !w.$TelescopeShowVisualQuestionMark ) return( 5 ); //no "?"
    			return( 6 ); //all
    		case 9: //Visual target size (VZoomSize=0 if full off)
    			var m = missionVariables.$TelescopeMenuVisualSize;
    			if( m > 0 ) return( m );
    			var size = w.$TelescopeVSize;
    			if( !w.$TelescopeRing ) size -= 2;
    			if( !size ) return( 5 ); //default
    			if( size < 2 ) return( 1 );
    			if( size > 7 ) return( 8 );
    			return( size );
    	}
    	return( -2 ); //no subitem with commands
    }
    
    
    /* //old events
    this.activated = function ()
    {	//lock the nearest target
    	w.$Telescope_Nearest();
    	w.$Telescope_Steer();//turn to the target
    }
    
    this.mode = function ()
    {	//lock the most centered target
    	w.$Telescope_MostCentered( null, "mode", false );
    	w.$Telescope_Steer();//turn to the current target
    //	w.$Telescope_List( 0 );//show target name
    }
    */
    
    //this.___ = function () { //step forward - need new button
    //	w.$Telescope_List( 1 );
    //}
    
    //this.___ = function () { //step backward - need new button
    //	w.$Telescope_List( -1 );
    //}
    
    //this.___ = function () { //turn on and off visual target - need new button
    //	if( w.$TelescopeShowVisualTarget )
    //		w.$TelescopeShowVisualTarget = false;
    //	else w.$TelescopeShowVisualTarget = true;
    //}
    Scripts/xlonly.js
    "use strict";
    this.name        = "xlonly";
    this.author      = "Norby";
    this.copyright   = "2013 Norbert Nagy";
    this.licence     = "CC BY-NC-SA 3.0";
    this.description = "This equipment is usable only for ships from 400t like Anaconda or Hard Python.";
    this.version     = "1.0";
    
    this.allowAwardEquipment = function(eqKey, ship, context)
    {
    //	player.consoleMessage( eqKey+" "+ship+" "+context );//debug
    	if( ship.mass >= 400000 ) return true;
    	else return false;
    }