├── LICENSE.md ├── README.md ├── build.py ├── html_ui ├── FPS_Setting.json ├── Pages │ ├── FlightPlanning │ │ └── Aircraft │ │ │ ├── AircraftPanels.css │ │ │ ├── AircraftPanels.html │ │ │ └── TCasPanels.js │ └── VCockpit │ │ └── Instruments │ │ └── Shared │ │ └── BaseInstrument.js └── SCSS │ └── refresh.css ├── layout.json └── manifest.json /LICENSE.md: -------------------------------------------------------------------------------- 1 | This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 Unported License (CC-NC-SA) . To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FPS-Fix v 1.1.4a 2 | 3 | READ: Important as of V1.1.4a / Game Version v1.8.3 - Set Glass Cockpit Refresh Rate to HIGH. Setting this to MEDIUM or LOW will overwrite the mod's setting (and this is intentional). If you wish to use the mod, please set this to High. The game's inbuilt FPS reduction applies on a global setting, the mod allows you to set this value per aircraft in freeflight mode. 4 | 5 | ## Install 6 | 7 | Unzip and install the FPS-Fix directory in your Community folder. DO NOT REPLACE THE BASE GAME FILES. 8 | 9 | Your community directory by default is located in C:\Users\[YOUR USERNAME]\AppData\Roaming\Microsoft Flight Simulator\Packages\Community 10 | 11 | If you changed this location during installation, you will not see any files in ..\Packages and will have to find your Community folder. 12 | 13 | Open your game and enable Developer Mode (In Settings > Developer). This will enable the developer menu. 14 | 15 | From the developer menu at the top of the screen, go to Tools > Virtual File System. 16 | 17 | Expand the Watched Bases tab. This is your Community folder, where the mod must be installed to. 18 | 19 | ------------- 20 | 21 | ## Usage 22 | 23 | Important as of V1.1.4a / Game Version v1.8.3 - Set Glass Cockpit Refresh Rate to HIGH. Setting this to MEDIUM or LOW will overwrite the mod's setting (and this is intentional). If you wish to use the mod, please set this to High. The game's inbuilt FPS reduction applies on a global setting, the mod allows you to set this value per aircraft in freeflight mode. 24 | 25 | By default, the default refresh settings are as listed. 26 | 27 | You can change this per airplane in the Flight Settings > FPS > Display Refresh options menu before you begin your flight. (Currently this read-only, you can modify it with a text editor but saving settings in game is not supported yet) 28 | 29 | Note that Very Low is a demonstration mode to simply make it obvious that the mod is actually doing something. It's not recommendeed and the recommended setting for most users is HIGH. 30 | 31 | ULTRA will completely disable the refresh rate modification and revert to vanilla behaviour. 32 | 33 | Default Settings: 34 | 35 | Unlisted - Ultra 36 | 37 | A320neo - High 38 | 39 | 747-8 Intercontinental - Medium 40 | 41 | 787-10 Dreamliner - Medium 42 | 43 | Cessna Citation CJ4 - Medium 44 | 45 | Cessna Citation Longitude - Medium 46 | 47 | TBM 930 - High 48 | 49 | Beechcraft King Air 350i - High 50 | 51 | Cessna 172 Skyhawk (G1000) - High 52 | 53 | Cessna 208 B Grand Caravan EX - High 54 | 55 | SR22 - High 56 | 57 | X Cub - High 58 | 59 | DA40 NG - High 60 | 61 | DA40 TDI - High 62 | 63 | DA62 - High 64 | 65 | DV20 - High 66 | 67 | EXTRA 330LT - High 68 | 69 | Flight Design CTSL - High 70 | 71 | A5 - High 72 | 73 | VL-3 - High 74 | 75 | Virus SW121 - High 76 | 77 | Cap10 - High 78 | 79 | Beechcraft Baron G58 - High 80 | 81 | Beechcraft Bonanza G36 - High 82 | 83 | Shock Ultra - Ultra 84 | 85 | Pitts Special S2S - Ultra 86 | 87 | Cessna 152 - Ultra 88 | 89 | Cessna 152 Aerobat - Ultra 90 | 91 | Cessna 172 Skyhawk - Ultra 92 | 93 | Savage Cub - Ultra 94 | 95 | ---- 96 | 97 | ## License/Credit 98 | 99 | See License.MD. 100 | 101 | Credit to Asobo Studio and Microsoft for the base game files. 102 | 103 | See Credits page on [Nexusmod page](https://www.nexusmods.com/microsoftflightsimulator/mods/34) for full credits. 104 | -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import sys 4 | 5 | 6 | def check_prerequisites(): 7 | if sys.version_info[0] < 3 or sys.version_info[1] < 6: 8 | raise Exception("Must be using Python 3.6 or later") 9 | 10 | 11 | def build_layout(project_dir): 12 | layout_entries = [] 13 | for root, _, files in os.walk(project_dir): 14 | for filename in files: 15 | filepath = os.path.join(root, filename) 16 | 17 | if not "git" in filepath and not filepath.endswith(".txt") and not filepath.endswith(".md") and not filepath.endswith("layout.json") and not filepath.endswith("manifest.json") and not filepath.endswith(".py"): 18 | rel_dir = os.path.relpath(root) 19 | rel_file = str(os.path.join(rel_dir, filename)) 20 | if rel_file[0] == '.': 21 | rel_file = rel_file[2:] 22 | 23 | print(" -- Processing " + rel_file) 24 | entry = {} 25 | entry["path"] = rel_file.replace('\\', '/') 26 | entry["size"] = os.path.getsize(filepath) 27 | entry["date"] = "132402817714110148" 28 | layout_entries.append(entry) 29 | 30 | layout_entries.sort(key=lambda e: e["path"]) 31 | 32 | return layout_entries 33 | 34 | 35 | if __name__ == "__main__": 36 | check_prerequisites() 37 | 38 | cwd = os.getcwd() 39 | 40 | layout_content = build_layout(cwd) 41 | 42 | layout_json = { 43 | "content": layout_content 44 | } 45 | 46 | with open("layout.json", "w") as outfile: 47 | json.dump(layout_json, outfile, indent=4) 48 | -------------------------------------------------------------------------------- /html_ui/FPS_Setting.json: -------------------------------------------------------------------------------- 1 | { 2 | "default":"High", 3 | "planes":[ 4 | {"name":"A320neo ","fps":"Medium"}, 5 | {"name":"747-8 Intercontinental ","fps":"Medium"}, 6 | {"name":"787-10 Dreamliner ","fps":"Medium"}, 7 | {"name":"Cessna Citation CJ4 ","fps":"Medium"}, 8 | {"name":"Cessna Citation Longitude ","fps":"Medium"}, 9 | {"name":"TBM 930 ","fps":"High"}, 10 | {"name":"Beechcraft King Air 350i ","fps":"High"}, 11 | {"name":"Cessna 172 Skyhawk (G1000) ","fps":"High"}, 12 | {"name":"Cessna 208 B Grand Caravan EX ","fps":"High"}, 13 | {"name":"SR22 ","fps":"High"}, 14 | {"name":"X Cub ","fps":"High"}, 15 | {"name":"DA40 NG ","fps":"High"}, 16 | {"name":"DA40 TDI ","fps":"High"}, 17 | {"name":"DA62 ","fps":"High"}, 18 | {"name":"DV20 ","fps":"High"}, 19 | {"name":"EXTRA 330LT ","fps":"High"}, 20 | {"name":"Flight Design CTSL ","fps":"High"}, 21 | {"name":"A5 ","fps":"High"}, 22 | {"name":"VL-3 ","fps":"High"}, 23 | {"name":"Virus SW121 ","fps":"High"}, 24 | {"name":"Cap10 ","fps":"High"}, 25 | {"name":"Beechcraft Baron G58 ","fps":"High"}, 26 | {"name":"Beechcraft Bonanza G36 ","fps":"High"}, 27 | {"name":"Shock Ultra ","fps":"Ultra"}, 28 | {"name":"Pitts Special S2S ","fps":"Ultra"}, 29 | {"name":"Cessna 152 ","fps":"Ultra"}, 30 | {"name":"Cessna 152 Aerobat ","fps":"Ultra"}, 31 | {"name":"Cessna 172 Skyhawk ","fps":"Ultra"}, 32 | {"name":"Savage Cub ","fps":"Ultra"} 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /html_ui/Pages/FlightPlanning/Aircraft/AircraftPanels.css: -------------------------------------------------------------------------------- 1 | /** This mixin makes a single line element fit exactly on the Roboto font for easier integration. */ 2 | aircraft-selection { 3 | width: 100%; 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | /* &.ChildActive { 8 | border: var(--baseMargin) solid white; // HACK 9 | } 10 | 11 | &:not(.ChildActive) { 12 | border: var(--baseMargin) solid transparent; // HACK 13 | } 14 | */ } 15 | aircraft-selection .searchContainer { 16 | flex: 0 0 auto; 17 | display: flex; 18 | align-items: center; } 19 | aircraft-selection .searchContainer new-push-button { 20 | margin-right: var(--baseMargin); } 21 | aircraft-selection search-element { 22 | background-color: var(--backgroundColorPanel); 23 | margin-bottom: var(--halfMargin); 24 | flex-shrink: 0; } 25 | 26 | aircraft-liveries { 27 | display: block; 28 | width: 100%; 29 | height: 100%; } 30 | 31 | aircraft-panels { 32 | position: relative; 33 | display: flex; 34 | flex-direction: row; 35 | justify-content: flex-start; 36 | align-items: flex-start; } 37 | aircraft-panels > .Content { 38 | display: block; 39 | position: relative; 40 | width: 100%; 41 | height: 100%; 42 | min-width: 0; 43 | padding-left: var(--halfMargin); } 44 | aircraft-panels options-menu { 45 | width: calc(var(--unscaledScreenHeight) * (670px / 2160)); 46 | background-color: var(--backgroundColorPanel); 47 | flex-shrink: 0; } 48 | aircraft-panels search-element { 49 | background-color: var(--backgroundColorPanel); 50 | margin-bottom: var(--halfMargin); 51 | flex-shrink: 0; } 52 | 53 | aircraft-atc { 54 | position: relative; 55 | width: 100%; 56 | height: 100%; 57 | display: flex; 58 | /*&.ChildActive { 59 | border: var(--baseMargin) solid white; // HACK 60 | } 61 | 62 | &:not(.ChildActive) { 63 | border: var(--baseMargin) solid transparent; // HACK 64 | }*/ } 65 | aircraft-atc #AircraftATCOptions_Content { 66 | display: block; 67 | flex: 1 0 auto; 68 | position: relative; 69 | padding: calc(var(--screenHeight) * (24px / 2160)); 70 | background-color: var(--backgroundColorPanel); } 71 | aircraft-atc #AircraftATCOptions_Content > * { 72 | margin-bottom: var(--halfMargin); } 73 | aircraft-atc #AircraftATCOptions_Content:last-child { 74 | margin-bottom: 0; } 75 | aircraft-atc .TipsWrapper { 76 | display: flex; 77 | width: calc(var(--screenHeight) * (700px / 2160)); 78 | margin-left: var(--halfMargin); 79 | padding: calc(var(--screenHeight) * (24px / 2160)); 80 | background: var(--backgroundColorPanel); } 81 | aircraft-atc .TipsWrapper .Tips { 82 | flex: 1 0 auto; 83 | position: relative; 84 | text-align: left; } 85 | 86 | aircraft-tcas-framerate { 87 | position: relative; 88 | width: 100%; 89 | height: 100%; 90 | display: flex; 91 | /*&.ChildActive { 92 | border: var(--baseMargin) solid white; // HACK 93 | } 94 | 95 | &:not(.ChildActive) { 96 | border: var(--baseMargin) solid transparent; // HACK 97 | }*/ } 98 | aircraft-tcas-framerate #AircraftATCOptions_Content { 99 | display: block; 100 | flex: 1 0 auto; 101 | position: relative; 102 | padding: calc(var(--screenHeight) * (24px / 2160)); 103 | background-color: var(--backgroundColorPanel); } 104 | aircraft-tcas-framerate #AircraftATCOptions_Content > * { 105 | margin-bottom: var(--halfMargin); } 106 | aircraft-tcas-framerate #AircraftATCOptions_Content:last-child { 107 | margin-bottom: 0; } 108 | aircraft-tcas-framerate .TipsWrapper { 109 | display: flex; 110 | width: calc(var(--screenHeight) * (700px / 2160)); 111 | margin-left: var(--halfMargin); 112 | padding: calc(var(--screenHeight) * (24px / 2160)); 113 | background: var(--backgroundColorPanel); } 114 | aircraft-tcas-framerate .TipsWrapper .Tips { 115 | flex: 1 0 auto; 116 | position: relative; 117 | text-align: left; } 118 | 119 | aircraft-specs { 120 | display: flex; 121 | flex-direction: column; 122 | background: var(--backgroundColorContrastedPanel); 123 | padding: var(--halfMargin); } 124 | aircraft-specs virtual-scroll { 125 | background-color: var(--backgroundColorPanel); 126 | padding: var(--basePadding); } 127 | aircraft-specs .Description > h1, aircraft-specs .Specs > h1 { 128 | padding-bottom: calc(var(--screenHeight) * (8px / 1080)); 129 | border-bottom: var(--minimalBorderWidth) solid var(--textColor); 130 | margin-bottom: var(--baseMargin); 131 | text-transform: uppercase; 132 | margin-top: var(--baseMargin); 133 | font-size: var(--fontSizeDefault); 134 | font-weight: bold; } 135 | aircraft-specs .Description > .Content, aircraft-specs .Specs > .Content { 136 | font-weight: 500; } 137 | aircraft-specs .Description > h1 { 138 | margin-top: 0; } 139 | 140 | -------------------------------------------------------------------------------- /html_ui/Pages/FlightPlanning/Aircraft/AircraftPanels.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 20 | 21 | 22 | 35 | 36 | 37 | 40 | 41 | 42 | 43 | 55 | 56 | 57 | 66 | 67 | 68 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /html_ui/Pages/FlightPlanning/Aircraft/TCasPanels.js: -------------------------------------------------------------------------------- 1 | // -------------------------- 2 | // TCasPanels 3 | // v 1.1.3a 4 | // -------------------------- 5 | 6 | /* 7 | +++++++++++++++++++++++ 8 | Turbofans 9 | +++++++++++++++++++++++ 10 | A320neo 11 | 747-8 Intercontinental 12 | 787-10 Dreamliner 13 | Cessna Citation CJ4 14 | Cessna Citation Longitude 15 | +++++++++++++++++++++++ 16 | Turboprops 17 | +++++++++++++++++++++++ 18 | TBM 930 19 | Beechcraft King Air 350i 20 | Cessna 208 B Grand Caravan EX 21 | +++++++++++++++++++++++ 22 | Props 23 | +++++++++++++++++++++++ 24 | SR22 25 | X Cub 26 | DA40 NG 27 | DA40 TDI 28 | DA62 29 | DV20 30 | EXTRA 330LT 31 | Flight Design CTSL 32 | A5 33 | VL-3 34 | Virus SW121 35 | Cap10 36 | Beechcraft Baron G58 37 | Beechcraft Bonanza G36 38 | ++++++++++++++++++++++ 39 | */ 40 | 41 | Include.addImport("/Templates/PanelInfoLine/PanelInfoLine.html"); 42 | Include.addScript("/JS/Services/Aircraft.js"); 43 | Include.addImport("/templates/searchElement/searchElement.html"); 44 | Include.addImport("/templates/OptionsMenu/Item/OptionsMenuItem.html"); 45 | Include.addImport("/templates/OptionsMenu/OptionsMenu.html"); 46 | Include.addImport("/Pages/World/PlaneSelection/PlaneSelection.html"); 47 | Include.addImport("/templates/FuelPayload/WM_FuelPayload.html"); 48 | Include.addImport("/Pages/World/AircraftFailures/WM_AircraftFailures.html"); 49 | 50 | const TC_DEBUG = false; 51 | 52 | class TCasOptionsElement extends TemplateElement { 53 | constructor() { 54 | super(); 55 | if (TC_DEBUG && g_modDebugMgr){ 56 | Include.addScript("/JS/debug.js", function () { 57 | g_modDebugMgr.AddConsole(null); 58 | }); 59 | } 60 | var json_obj = null; 61 | this.current_aircraft = null; 62 | // Parsing JSON string into object 63 | loadJSON(function(response) { 64 | if (response) { 65 | json_obj = JSON.parse(response); 66 | } 67 | if (TC_DEBUG) { 68 | for (let i = 0; i < json_obj.length; i++) { 69 | console.log(json_obj[i].name + " = " + json_obj[i].fps); 70 | } 71 | } 72 | }); 73 | this.onListenerRegistered = () => { 74 | this.m_gameFlightListener.requestGameFlight(this.onGameFlightUpdated); 75 | }; 76 | this.onGameFlightUpdated = (flight) => { 77 | //Type: AircraftData 78 | if (TC_DEBUG) { 79 | console.log("aircraft ID: " + flight.aircraftData.aircraftId); 80 | console.log("name: [" + flight.aircraftData.name + "]"); 81 | console.log("variation: " + flight.aircraftData.variation); 82 | } 83 | let factor = null; 84 | this.current_aircraft = flight.aircraftData.name; 85 | // Fetch from JSON 86 | if (json_obj) { 87 | for (let i = 0; i < json_obj.planes.length; i++) { 88 | if (json_obj.planes[i].name === flight.aircraftData.name) { 89 | if (TC_DEBUG) { 90 | console.log("FPS Quality: " + json_obj.planes[i].fps); 91 | console.log("QF: " + quality_factor[json_obj.planes[i].fps]); 92 | } 93 | factor = quality_factor[json_obj.planes[i].fps]; 94 | break; 95 | } 96 | } 97 | // No aircraft found 98 | if (!factor && json_obj.default) { 99 | if (TC_DEBUG) console.log("Using default: "+ json_obj.default); 100 | factor = quality_factor[json_obj.default]; 101 | localStorage.setItem("FPS_r_factor", factor); 102 | if (TC_DEBUG) console.log(localStorage.getItem("FPS_r_factor")); 103 | } 104 | 105 | } 106 | // Load factor variable (menus + local settings) 107 | if (factor) { 108 | localStorage.setItem("FPS_r_factor", factor); 109 | let index = factor_disp[factor] ? factor_disp[factor] : 0; 110 | this.fRedInd.setCurrentValue(index); 111 | } else { 112 | // If no factor, revert to High 113 | localStorage.setItem("FPS_r_factor", "2"); 114 | this.fRedInd.setCurrentValue(1); 115 | if (json_obj) { json_obj.default = "High"; } 116 | } 117 | }; 118 | // On menu change... 119 | this.onRateChange = () => { 120 | let index = this.fRedInd.getCurrentValue(); 121 | let factor = "2"; 122 | // Get rFactor from index 123 | for (let key in factor_disp) { 124 | if (factor_disp[key] == index) { 125 | factor = key; 126 | break; 127 | } 128 | } 129 | // Get quality from rFactor 130 | let quality = "High"; // Set High if nothing found 131 | for (let key in quality_factor) { 132 | if (quality_factor[key] == factor) { 133 | quality = key; 134 | break; 135 | } 136 | } 137 | // Set rFactor -> BaseInstrument 138 | localStorage.setItem("FPS_r_factor", factor); 139 | if (TC_DEBUG) console.log(localStorage.getItem("FPS_r_factor")); 140 | // Set quality for existing plane -> JSON 141 | let found = false; 142 | if (json_obj) { 143 | for (let i = 0; i < json_obj.planes.length; i++) { 144 | if (!found && json_obj.planes[i].name === this.current_aircraft) { 145 | json_obj.planes[i].fps = quality; // Set quality for this plane 146 | found = true; 147 | if (TC_DEBUG) { console.log("Found " + json_obj.planes[i].name); } 148 | //console.log(json_obj.planes[i].name + " = " + json_obj.planes[i].fps); 149 | } 150 | } 151 | // Create new plane -> JSON 152 | if (!found) { 153 | if (TC_DEBUG) { console.log(">>> Creating new: " + this.current_aircraft); } 154 | let new_plane = { 155 | name: this.current_aircraft, 156 | fps: quality 157 | } 158 | if (TC_DEBUG) { "JSON: " +json_obj.planes.push(new_plane); } 159 | //console.log("Created" + new_plane.name + " = " + new_plane.fps); 160 | } 161 | console.log(JSON.stringify(json_obj)); 162 | //console.log("FPS_r_factor set to " + factor); 163 | } 164 | }; 165 | this.copyToClipboard = () => { 166 | console.log(">>>> To Clipboard"); 167 | const element = document.createElement('textarea'); 168 | element.value = JSON.stringify(json_obj); 169 | element.setAttribute('readonly', ''); 170 | element.style.position = 'absolute'; 171 | element.style.left = '-9999px'; 172 | document.body.appendChild(element); 173 | const selected = 174 | document.getSelection().rangeCount > 0 175 | ? document.getSelection().getRangeAt(0) 176 | : false; 177 | element.select(); 178 | document.execCommand('copy'); 179 | document.body.removeChild(element); 180 | if (selected) { 181 | document.getSelection().removeAllRanges(); 182 | document.getSelection().addRange(selected); 183 | } 184 | }; 185 | } 186 | get templateID() { return "TCasOptionsTemplate"; } 187 | ; 188 | connectedCallback() { 189 | super.connectedCallback(); 190 | this.fRedInd = this.querySelector(".WM_TCOPTIONS_DISPLAY_RATE"); 191 | this.fRedInd.addEventListener("OnValidate", this.onRateChange); 192 | this.fRedCopy = this.querySelector(".WM_TCOPTIONS_COPY"); 193 | this.fRedCopy.addEventListener("OnValidate", this.copyToClipboard); 194 | if (!this.m_gameFlightListener) 195 | this.m_gameFlightListener = RegisterGameFlightListener(this.onListenerRegistered); 196 | this.m_gameFlightListener.onGameFlightUpdated(this.onGameFlightUpdated); 197 | } 198 | get canBeSelected() { return true; } 199 | getKeyNavigationStayInside(keycode) { return true; } 200 | getKeyNavigationDirection() { return KeyNavigationDirection.KeyNavigation_Vertical; } 201 | getAllFocusableChildren() { 202 | return [ this.fRedInd, this.fRedCopy ]; 203 | } 204 | 205 | } 206 | 207 | function loadJSON(callback) { 208 | if (TC_DEBUG) console.log("Loading JSON"); 209 | let xobj = new XMLHttpRequest(); 210 | xobj.overrideMimeType("application/json"); 211 | xobj.open("GET", "/FPS_Setting.json", true); 212 | xobj.onreadystatechange = function () { 213 | if (xobj.readyState == 4 && xobj.status == "200") { 214 | callback(xobj.responseText); 215 | } 216 | }; 217 | xobj.send(null); 218 | } 219 | 220 | function saveJSON(json) { 221 | 222 | const filename = "FPS_Setting.json"; 223 | let text = JSON.stringify(json); 224 | if (TC_DEBUG) { 225 | console.log("Saved to : " + filename); 226 | console.log(text); 227 | } 228 | 229 | var element = document.createElement('a'); 230 | element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); 231 | element.setAttribute('download', filename); 232 | 233 | element.style.display = 'none'; 234 | document.body.appendChild(element); 235 | 236 | element.click(); 237 | 238 | document.body.removeChild(element); 239 | } 240 | 241 | /* 242 | // :( 243 | function saveJSON(filename, text) { 244 | console.log("Saved to : " + filename); 245 | console.log(text); 246 | var a = document.createElement("a"); 247 | var file = new Blob([text], {type: "text/plain"}); 248 | a.href = URL.createObjectURL(file); 249 | a.download = filename; 250 | document.body.appendChild(a); 251 | a.click(); 252 | document.body.removeChild(a); 253 | } 254 | */ 255 | 256 | var factor_disp = { 257 | "1" : 0, // Ultra 258 | "2" : 1, // High 259 | "3" : 2, // Medium 260 | "4" : 3, // Low 261 | "16" : 4 // Very Low 262 | }; 263 | var quality_factor = { 264 | "Ultra" : "1", 265 | "High" : "2", 266 | "Medium" : "3", 267 | "Low" : "4", 268 | "Very Low" : "16" 269 | }; 270 | 271 | window.customElements.define("aircraft-tcas-framerate", TCasOptionsElement); 272 | checkAutoload(); 273 | //# sourceMappingURL=AircraftPanels.js.map 274 | -------------------------------------------------------------------------------- /html_ui/Pages/VCockpit/Instruments/Shared/BaseInstrument.js: -------------------------------------------------------------------------------- 1 | // -------------------------- 2 | // BaseInstrument 3 | // v 1.1.4.1a 4 | // -------------------------- 5 | 6 | const TC_DEBUG = false; 7 | 8 | class BaseInstrument extends TemplateElement { 9 | 10 | constructor() { 11 | super(); 12 | this.urlConfig = new URLConfig(); 13 | this.xmlConfigLoading = false; 14 | this.frameCount = 0; 15 | this.highlightList = []; 16 | this.backgroundList = []; 17 | this._lastTime = 0; 18 | this._isConnected = false; 19 | this._isInitialized = false; 20 | this._quality = Quality.high; 21 | this._gameState = GameState.ingame; 22 | this._deltaTime = 0; 23 | this._alwaysUpdate = false; 24 | this._pendingCalls = []; 25 | // --------------------------- MOD ------------------------------------------ // 26 | this._refresh = Refresh.ultra; // Refresh at FPS by default 27 | this.frameReduce = 1; // Default reduction factor 28 | // --------------------------- MOD ------------------------------------------ // 29 | this.dataMetaManager = new DataReadMetaManager(); 30 | } 31 | get initialized() { return this._isInitialized; } 32 | get instrumentIdentifier() { return this._instrumentId; } 33 | get instrumentIndex() { return (this.urlConfig.index != null) ? this.urlConfig.index : 1; } 34 | get isInteractive() { return false; } 35 | get IsGlassCockpit() { return false; } 36 | get isPrimary() { return (this.urlConfig.index == null || this.urlConfig.index == 1); } 37 | get deltaTime() { return this._deltaTime; } 38 | get flightPlanManager() { return null; } 39 | get facilityLoader() { 40 | if (!this._facilityLoader) 41 | this._facilityLoader = new FacilityLoader(this); 42 | return this._facilityLoader; 43 | } 44 | connectedCallback() { 45 | super.connectedCallback(); 46 | if (TC_DEBUG && g_modDebugMgr){ 47 | Include.addScript("/JS/debug.js", function () { 48 | g_modDebugMgr.AddConsole(null); 49 | }); 50 | } 51 | this.electricity = this.getChildById("Electricity"); 52 | this.highlightSvg = this.getChildById("highlight"); 53 | this.loadDocumentAttributes(); 54 | this.loadURLAttributes(); 55 | this.loadXMLConfig(); 56 | window.document.addEventListener("OnVCockpitPanelAttributesChanged", this.loadDocumentAttributes.bind(this)); 57 | this.startTime = Date.now(); 58 | if (this.getGameState() != GameState.mainmenu) { 59 | this.createMainLoop(); 60 | } 61 | // --------------------------- MOD ------------------------------------------ // 62 | this.frameReduce = this.loadFrameRed(); 63 | // DEBUG 64 | if (TC_DEBUG) console.log(this.instrumentIdentifier); 65 | // --------------------------- MOD ------------------------------------------ // 66 | } 67 | disconnectedCallback() { 68 | super.disconnectedCallback(); 69 | this._isConnected = false; 70 | } 71 | Init() { 72 | this._isInitialized = true; 73 | this.initTransponder(); 74 | } 75 | setInstrumentIdentifier(_identifier) { 76 | if (_identifier && _identifier != "" && _identifier != this.instrumentIdentifier) { 77 | this._instrumentId = _identifier; 78 | var guid = this.getAttribute("Guid"); 79 | if (guid != undefined) { 80 | LaunchFlowEvent("ON_VCOCKPIT_INSTRUMENT_INITIALIZED", guid, this.instrumentIdentifier, this.isInteractive, this.IsGlassCockpit); 81 | } 82 | } 83 | } 84 | setConfigFile(_path) { 85 | this._xmlConfigPath = _path; 86 | } 87 | getChildById(_selector) { 88 | if (_selector == "") 89 | return null; 90 | if (!_selector.startsWith("#") && !_selector.startsWith(".")) 91 | _selector = "#" + _selector; 92 | var child = this.querySelector(_selector.toString()); 93 | return child; 94 | } 95 | getChildrenById(_selector) { 96 | if (_selector == "") 97 | return null; 98 | if (!_selector.startsWith("#") && !_selector.startsWith(".")) 99 | _selector = "#" + _selector; 100 | var children = this.querySelectorAll(_selector.toString()); 101 | return children; 102 | } 103 | getChildrenByClassName(_selector) { 104 | return this.getElementsByClassName(_selector); 105 | } 106 | startHighlight(_id) { 107 | let elem = this.getChildById(_id); 108 | if (elem) { 109 | let highlight = new HighlightedElement(); 110 | highlight.elem = elem; 111 | highlight.style = elem.style.cssText; 112 | this.highlightList.push(highlight); 113 | } 114 | let elems = this.getChildrenByClassName(_id); 115 | for (let i = 0; i < elems.length; i++) { 116 | let highlight = new HighlightedElement(); 117 | highlight.elem = elems[i]; 118 | highlight.style = elems[i].style.cssText; 119 | this.highlightList.push(highlight); 120 | } 121 | this.updateHighlightElements(); 122 | } 123 | stopHighlight(_id) { 124 | let elem = this.getChildById(_id); 125 | if (elem) { 126 | for (let i = 0; i < this.highlightList.length; i++) { 127 | if (this.highlightList[i].elem == elem) { 128 | elem.style.cssText = this.highlightList[i].style; 129 | this.highlightList.splice(i, 1); 130 | } 131 | } 132 | } 133 | let elems = this.getChildrenByClassName(_id); 134 | for (let i = 0; i < elems.length; i++) { 135 | for (let j = 0; j < this.highlightList.length; j++) { 136 | if (this.highlightList[j].elem == elems[i]) { 137 | elems[i].style.cssText = this.highlightList[j].style; 138 | this.highlightList.splice(j, 1); 139 | } 140 | } 141 | } 142 | this.updateHighlightElements(); 143 | } 144 | clearHighlights() { 145 | this.highlightList = []; 146 | this.updateHighlightElements(); 147 | } 148 | updateHighlightElements() { 149 | for (let i = 0; i < this.backgroundList.length; i++) { 150 | this.backgroundList[i].remove(); 151 | } 152 | this.backgroundList = []; 153 | if (this.highlightList.length > 0) { 154 | this.highlightSvg.setAttribute("active", "true"); 155 | let elems = ""; 156 | for (let i = 0; i < this.highlightList.length; i++) { 157 | let rect = this.highlightList[i].elem.getBoundingClientRect(); 158 | if (this.highlightList[i] instanceof HTMLElement) { 159 | let bg = document.createElement("div"); 160 | bg.style.backgroundColor = "rgba(0,0,0,0.9)"; 161 | bg.style.zIndex = "-1"; 162 | bg.style.left = this.highlightList[i].elem.offsetLeft.toString() + "px"; 163 | bg.style.top = this.highlightList[i].elem.offsetTop.toString() + "px"; 164 | bg.style.width = rect.width.toString() + "px"; 165 | bg.style.height = rect.height.toString() + "px"; 166 | bg.style.position = "absolute"; 167 | this.highlightList[i].elem.parentElement.appendChild(bg); 168 | this.backgroundList.push(bg); 169 | } 170 | if (i > 0) { 171 | elems += ";"; 172 | } 173 | elems += rect.left + " "; 174 | elems += rect.top + " "; 175 | elems += rect.right + " "; 176 | elems += rect.bottom; 177 | } 178 | this.highlightSvg.setAttribute("elements", elems); 179 | } 180 | else { 181 | this.highlightSvg.setAttribute("active", "false"); 182 | } 183 | } 184 | onInteractionEvent(_args) { 185 | } 186 | onSoundEnd(_event) { 187 | } 188 | getQuality() { 189 | if (this._alwaysUpdate && this._quality != Quality.disabled) { 190 | return Quality.high; 191 | } 192 | return this._quality; 193 | } 194 | getGameState() { 195 | return this._gameState; 196 | } 197 | reboot() { 198 | console.log("Rebooting Instrument..."); 199 | this.startTime = Date.now(); 200 | this.frameCount = 0; 201 | this.initTransponder(); 202 | } 203 | onFlightStart() { 204 | console.log("Flight Starting..."); 205 | SimVar.SetSimVarValue("L:HUD_AP_SELECTED_SPEED", "Number", 0); 206 | SimVar.SetSimVarValue("L:HUD_AP_SELECTED_ALTITUDE", "Number", 0); 207 | this.dispatchEvent(new Event('FlightStart')); 208 | } 209 | onQualityChanged(_quality) { 210 | this._quality = _quality; 211 | } 212 | onGameStateChanged(_oldState, _newState) { 213 | if (_newState != GameState.mainmenu) { 214 | this.createMainLoop(); 215 | if (_oldState == GameState.loading && (_newState == GameState.ingame || _newState == GameState.briefing)) { 216 | this.reboot(); 217 | } 218 | else if (_oldState == GameState.briefing && _newState == GameState.ingame) { 219 | this.onFlightStart(); 220 | } 221 | } 222 | else { 223 | this.killMainLoop(); 224 | } 225 | this._gameState = _newState; 226 | } 227 | loadDocumentAttributes() { 228 | var attr = undefined; 229 | if (document.body.hasAttribute("quality")) 230 | attr = document.body.getAttribute("quality"); 231 | else if (window.parent && window.parent.document.body.hasAttribute("quality")) 232 | attr = window.parent.document.body.getAttribute("quality"); 233 | if (attr != undefined) { 234 | var quality = Quality[attr]; 235 | if (quality != undefined && this._quality != quality) { 236 | this.onQualityChanged(quality); 237 | } 238 | } 239 | if (document.body.hasAttribute("gamestate")) 240 | attr = document.body.getAttribute("gamestate"); 241 | else if (window.parent && window.parent.document.body.hasAttribute("gamestate")) 242 | attr = window.parent.document.body.getAttribute("gamestate"); 243 | if (attr != undefined) { 244 | var state = GameState[attr]; 245 | if (state != undefined && this._gameState != state) { 246 | this.onGameStateChanged(this._gameState, state); 247 | } 248 | } 249 | } 250 | parseXMLConfig() { 251 | if (this.instrumentXmlConfig) { 252 | let electric = this.instrumentXmlConfig.getElementsByTagName("Electric"); 253 | if (electric.length > 0) { 254 | this.electricalLogic = new CompositeLogicXMLElement(this, electric[0]); 255 | } 256 | let alwaysUpdate = this.instrumentXmlConfig.getElementsByTagName("AlwaysUpdate"); 257 | if (alwaysUpdate.length > 0) { 258 | if (alwaysUpdate[0].textContent.toLowerCase() == "true") { 259 | this._alwaysUpdate = true; 260 | } 261 | } 262 | } 263 | } 264 | parseURLAttributes() { 265 | var instrumentID = this.templateID; 266 | if (this.urlConfig.index) 267 | instrumentID += "_" + this.urlConfig.index; 268 | this.setInstrumentIdentifier(instrumentID); 269 | if (this.urlConfig.style) 270 | this.setAttribute("instrumentstyle", this.urlConfig.style); 271 | } 272 | beforeUpdate() { 273 | var curTime = Date.now(); 274 | this._deltaTime = curTime - this._lastTime; 275 | this._lastTime = curTime; 276 | this.updatePendingCalls(); 277 | } 278 | Update() { 279 | this.dataMetaManager.UpdateAll(); 280 | this.updateHighlight(); 281 | if (this._facilityLoader) { 282 | this._facilityLoader.update(); 283 | } 284 | } 285 | afterUpdate() { 286 | this.frameCount++; 287 | if (this.frameCount >= Number.MAX_SAFE_INTEGER) 288 | this.frameCount = 0; 289 | } 290 | doUpdate() { 291 | this.beforeUpdate(); 292 | this.Update(); 293 | this.afterUpdate(); 294 | } 295 | // ------------------------------------ MOD ------------------------------------------- 296 | CanUpdate() { 297 | let frame_red = this.getFrameRed(); 298 | let quality = this.getQuality(); 299 | 300 | if (quality == Quality.ultra) { 301 | if ((this.frameCount % frame_red) != 0) { 302 | //console.log("Delta time updated - passed canupdate() " + this._deltaTime); 303 | return false; 304 | } 305 | return true; 306 | } 307 | else if (quality == Quality.high) { 308 | if ((this.frameCount % 2) != 0) { 309 | return false; 310 | } 311 | } 312 | else if (quality == Quality.medium) { 313 | if ((this.frameCount % 4) != 0) { 314 | return false; 315 | } 316 | } 317 | else if (quality == Quality.low) { 318 | if ((this.frameCount % 32) != 0) { 319 | return false; 320 | } 321 | } 322 | else if (quality == Quality.hidden) { 323 | if ((this.frameCount % 128) != 0) { 324 | return false; 325 | } 326 | } 327 | else if (quality == Quality.disabled) { 328 | return false; 329 | } 330 | return true; 331 | } 332 | // ------------------------------------ MOD ------------------------------------------- 333 | updateElectricity() { 334 | let powerOn = this.isElectricityAvailable(); 335 | if (this.electricity) { 336 | if (powerOn) 337 | this.electricity.setAttribute("state", "on"); 338 | else 339 | this.electricity.setAttribute("state", "off"); 340 | } 341 | return powerOn; 342 | } 343 | isElectricityAvailable() { 344 | if (this.electricalLogic) { 345 | return this.electricalLogic.getValue() != 0; 346 | } 347 | return SimVar.GetSimVarValue("CIRCUIT AVIONICS ON", "Bool"); 348 | } 349 | playInstrumentSound(soundId) { 350 | if (this.isElectricityAvailable()) { 351 | Coherent.call("PLAY_INSTRUMENT_SOUND", soundId); 352 | return true; 353 | } 354 | return false; 355 | } 356 | createMainLoop() { 357 | if (this._isConnected) 358 | return; 359 | this._lastTime = Date.now(); 360 | let updateLoop = () => { 361 | if (!this._isConnected) { 362 | console.log("Exiting MainLoop..."); 363 | return; 364 | } 365 | try { 366 | if (BaseInstrument.allInstrumentsLoaded && !this.xmlConfigLoading && SimVar.IsReady()) { 367 | if (!this._isInitialized) 368 | this.Init(); 369 | this.beforeUpdate(); 370 | if (this.CanUpdate()) 371 | this.Update(); 372 | this.afterUpdate(); 373 | } 374 | } 375 | catch (Error) { 376 | console.error(this.instrumentIdentifier + " : " + Error, Error.stack); 377 | } 378 | requestAnimationFrame(updateLoop); 379 | }; 380 | this._isConnected = true; 381 | console.log("MainLoop created"); 382 | requestAnimationFrame(updateLoop); 383 | } 384 | killMainLoop() { 385 | this._isConnected = false; 386 | } 387 | loadXMLConfig() { 388 | var xmlPath; 389 | if (this.urlConfig.config) { 390 | xmlPath = "/Pages/VCockpit/Instruments/Shared/Configs/" + this.urlConfig.config + ".xml"; 391 | } 392 | else if (this._xmlConfigPath) { 393 | xmlPath = "/VFS/" + this._xmlConfigPath.replace(/\\/g, "/"); 394 | } 395 | if (xmlPath) { 396 | this.xmlConfigLoading = true; 397 | var xmlRequest = new XMLHttpRequest(); 398 | xmlRequest.onreadystatechange = function (_instrument) { 399 | if (this.readyState === XMLHttpRequest.DONE && this.status === 200) { 400 | _instrument.onXMLConfigLoaded(this); 401 | } 402 | }.bind(xmlRequest, this); 403 | xmlRequest.open("GET", xmlPath, true); 404 | xmlRequest.send(); 405 | } 406 | } 407 | onXMLConfigLoaded(_xml) { 408 | this.xmlConfig = _xml.responseXML; 409 | if (this.xmlConfig) { 410 | let instruments = this.xmlConfig.getElementsByTagName("Instrument"); 411 | for (let i = 0; i < instruments.length; i++) { 412 | let name = instruments[i].getElementsByTagName("Name")[0].textContent; 413 | if (name == this.instrumentIdentifier) { 414 | this.instrumentXmlConfig = instruments[i]; 415 | } 416 | } 417 | this.parseXMLConfig(); 418 | } 419 | else { 420 | console.error("XML Config file is not well-formatted"); 421 | } 422 | this.xmlConfigLoading = false; 423 | } 424 | loadURLAttributes() { 425 | var parsedUrl = new URL(this.getAttribute("Url").toLowerCase()); 426 | this.urlConfig.style = parsedUrl.searchParams.get("style"); 427 | this.urlConfig.config = parsedUrl.searchParams.get("config"); 428 | let index = parsedUrl.searchParams.get("index"); 429 | this.urlConfig.index = index == null ? null : parseInt(index); 430 | this.urlConfig.wasmModule = parsedUrl.searchParams.get("wasm_module"); 431 | this.urlConfig.wasmGauge = parsedUrl.searchParams.get("wasm_gauge"); 432 | this.parseURLAttributes(); 433 | } 434 | getTimeSinceStart() { 435 | return Date.now() - this.startTime; 436 | } 437 | getAspectRatio() { 438 | var vpRect = this.getBoundingClientRect(); 439 | if (vpRect) { 440 | var vpWidth = vpRect.width; 441 | var vpHeight = vpRect.height; 442 | var aspectRatio = vpWidth / vpHeight; 443 | return aspectRatio; 444 | } 445 | return 1.0; 446 | } 447 | isComputingAspectRatio() { return false; } 448 | isAspectRatioForced() { return false; } 449 | getForcedScreenRatio() { return 1.0; } 450 | getForcedAspectRatio() { return 1.0; } 451 | updateHighlight() { 452 | } 453 | highlightGetState(_valueMin, _valueMax, _period) { 454 | let time = new Date().getTime(); 455 | let size = _valueMax - _valueMin; 456 | let middle = _valueMin + size / 2; 457 | return middle + (Math.sin((time % _period / _period * Math.PI * 2)) * (size / 2)); 458 | } 459 | wasTurnedOff() { 460 | return false; 461 | } 462 | initTransponder() { 463 | let transponderCode = ("0000" + SimVar.GetSimVarValue("TRANSPONDER CODE:1", "number")).slice(-4); 464 | if (transponderCode) { 465 | let currentCode = parseInt(transponderCode); 466 | if (currentCode == 0) { 467 | Simplane.setTransponderToRegion(); 468 | } 469 | } 470 | } 471 | requestCall(_func) { 472 | this._pendingCalls.push(_func); 473 | } 474 | updatePendingCalls() { 475 | let length = this._pendingCalls.length; 476 | for (let i = 0; i < length; i++) { 477 | this._pendingCalls[i](); 478 | } 479 | this._pendingCalls.splice(0, length); 480 | } 481 | 482 | // ------------------------------------ MOD ------------------------------------------- 483 | loadFrameRed() { 484 | let f_name = "FPS_r_factor"; 485 | let def_factor = 2; 486 | if (!localStorage.getItem(f_name)) { 487 | localStorage.setItem(f_name, def_factor) 488 | console.log("Refresh Factor " + def_factor); 489 | return def_factor; 490 | } else { 491 | let factor_str = localStorage.getItem(f_name); 492 | let factor = parseInt(factor_str); 493 | console.log("Refresh Factor " + factor); 494 | return factor; 495 | } 496 | } 497 | 498 | getFrameRed() { 499 | return this.frameReduce; 500 | } 501 | 502 | // Can this refresh? 503 | getRefresh() { 504 | return this._refresh; 505 | } 506 | // ------------------------------------ MOD ------------------------------------------- 507 | } 508 | BaseInstrument.allInstrumentsLoaded = false; 509 | BaseInstrument.useSvgImages = false; 510 | class URLConfig { 511 | } 512 | var Quality; 513 | (function (Quality) { 514 | Quality[Quality["ultra"] = 0] = "ultra"; 515 | Quality[Quality["high"] = 1] = "high"; 516 | Quality[Quality["medium"] = 2] = "medium"; 517 | Quality[Quality["low"] = 3] = "low"; 518 | Quality[Quality["hidden"] = 4] = "hidden"; 519 | Quality[Quality["disabled"] = 5] = "disabled"; 520 | })(Quality || (Quality = {})); 521 | var GameState; 522 | (function (GameState) { 523 | GameState[GameState["mainmenu"] = 0] = "mainmenu"; 524 | GameState[GameState["loading"] = 1] = "loading"; 525 | GameState[GameState["briefing"] = 2] = "briefing"; 526 | GameState[GameState["ingame"] = 3] = "ingame"; 527 | })(GameState || (GameState = {})); 528 | 529 | // ----------------------------- MOD ------------------------------------ 530 | var Refresh; 531 | (function (Refresh) { 532 | Refresh[Refresh["ultra"] = 0] = "ultra"; 533 | Refresh[Refresh["high"] = 1] = "high"; 534 | Refresh[Refresh["medium"] = 2] = "medium"; 535 | Refresh[Refresh["low"] = 3] = "low"; 536 | Refresh[Refresh["hidden"] = 4] = "hidden"; 537 | Refresh[Refresh["disable"] = 5] = "disable"; 538 | })(Refresh || (Refresh = {})); 539 | // ----------------------------- MOD ------------------------------------ 540 | class HighlightedElement { 541 | } 542 | customElements.define("base-instrument", BaseInstrument); 543 | checkAutoload(); 544 | //# sourceMappingURL=BaseInstrument.js.map 545 | -------------------------------------------------------------------------------- /html_ui/SCSS/refresh.css: -------------------------------------------------------------------------------- 1 | #rPanel { 2 | z-index: 10000; 3 | transform: rotateX(0); 4 | font-family: 'Segoe UI Symbol'; 5 | } 6 | 7 | /* Links */ 8 | a { 9 | padding: 2px 35px 2px 35px; 10 | text-decoration: none; 11 | color: #818181; 12 | display: block; 13 | transition: 0.3s; 14 | background: none; 15 | border: none; 16 | } 17 | 18 | a:hover { 19 | color: #f1f1f1; 20 | } 21 | 22 | /* Sliding Panel */ 23 | .refreshPanel { 24 | height: 285px; 25 | width: 0%; /* JS */ 26 | position: fixed; 27 | z-index: 1; /* Top */ 28 | top: 0; 29 | left: 0; 30 | background-color: #111; 31 | overflow-x: hidden; /* Disable horizontal scroll */ 32 | padding-top: 60px; /* Place content from the top */ 33 | transition: 0.2s; /* 0.2 transition */ 34 | font-size: 25px; 35 | } 36 | -------------------------------------------------------------------------------- /layout.json: -------------------------------------------------------------------------------- 1 | { 2 | "content": [ 3 | { 4 | "path": "html_ui/FPS_Setting.json", 5 | "size": 1496, 6 | "date": "132402817714110148" 7 | }, 8 | { 9 | "path": "html_ui/Pages/FlightPlanning/Aircraft/AircraftPanels.css", 10 | "size": 4523, 11 | "date": "132402817714110148" 12 | }, 13 | { 14 | "path": "html_ui/Pages/FlightPlanning/Aircraft/AircraftPanels.html", 15 | "size": 5811, 16 | "date": "132402817714110148" 17 | }, 18 | { 19 | "path": "html_ui/Pages/FlightPlanning/Aircraft/TCasPanels.js", 20 | "size": 9886, 21 | "date": "132402817714110148" 22 | }, 23 | { 24 | "path": "html_ui/Pages/VCockpit/Instruments/Shared/BaseInstrument.js", 25 | "size": 20180, 26 | "date": "132402817714110148" 27 | }, 28 | { 29 | "path": "html_ui/SCSS/refresh.css", 30 | "size": 657, 31 | "date": "132402817714110148" 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | { 4 | "name": "asobo-vcockpits-core", 5 | "package_version": "0.1.12" 6 | } 7 | ], 8 | "content_type": "CORE", 9 | "title": "", 10 | "manufacturer": "", 11 | "creator": "2Cas", 12 | "package_version": "0.1.40", 13 | "minimum_game_version": "1.7.12", 14 | "release_notes": { 15 | "neutral": { 16 | "LastUpdate": "", 17 | "OlderHistory": "" 18 | } 19 | }, 20 | "total_package_size": "0000000000000015657" 21 | } 22 | --------------------------------------------------------------------------------