// ==UserScript==
// @name GeoFS Mod Menu -cool-
// @namespace http://tampermonkey.net/
// @version 1.4
// @description Mod Menu for GeoFS flight model variables using console-modifiable input fields
// @author Jasp
// @match https://www.geo-fs.com/*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
const presets = {
"Default": {
maxRPM: 2500,
enginePower: 100000,
fuelFlow: 0.3,
dragFactor: 0.05,
liftFactor: 1.0
},
"High Power": {
maxRPM: 4000,
enginePower: 200000,
fuelFlow: 0.8,
dragFactor: 0.03,
liftFactor: 1.2
},
"Glider": {
maxRPM: 0,
enginePower: 0,
fuelFlow: 0,
dragFactor: 0.01,
liftFactor: 1.5
},
"Heavy Jet": {
maxRPM: 3200,
enginePower: 150000,
fuelFlow: 0.6,
dragFactor: 0.07,
liftFactor: 0.9
},
"Light Sport": {
maxRPM: 2800,
enginePower: 75000,
fuelFlow: 0.4,
dragFactor: 0.04,
liftFactor: 1.3
}
};
const variableSettings = {
maxRPM: { min: 1000, max: 4000 },
minRPM: { min: 500, max: 1500 },
starterRPM: { min: 300, max: 1000 },
idleThrottle: { min: 0.01, max: 0.2 },
fuelFlow: { min: 0.1, max: 1.0 },
enginePower: { min: 50000, max: 200000 },
brakeRPM: { min: 100, max: 1000 },
wingArea: { min: 5, max: 150 },
dragFactor: { min: 0.01, max: 0.2 },
liftFactor: { min: 0.5, max: 2.0 },
CD0: { min: 0.01, max: 0.1 },
CLmax: { min: 0.5, max: 2.0 },
elevatorFactor: { min: 0.1, max: 2.0 },
rudderFactor: { min: 0.1, max: 2.0 },
aileronFactor: { min: 0.1, max: 2.0 },
mass: { min: 500, max: 50000 },
emptyWeight: { min: 500, max: 30000 },
maxWeight: { min: 1000, max: 100000 },
inertia: { min: 100, max: 50000 },
pitchMoment: { min: 10, max: 5000 },
yawMoment: { min: 10, max: 5000 },
rollMoment: { min: 10, max: 5000 },
gearDrag: { min: 0.01, max: 0.5 },
gearCompression: { min: 0.01, max: 1.0 },
gearLength: { min: 0.5, max: 5.0 }
};
const explanations = {
maxRPM: "Maximum revolutions per minute of the engine. Higher = more top power, but more stress. Lower = less max thrust.",
minRPM: "Minimum engine RPM. Too low and the engine might stall. Too high and you lose idle control.",
starterRPM: "RPM during engine startup. Higher starts faster. Too high = unrealistic.",
idleThrottle: "Throttle at idle. Higher means more thrust at 0% throttle.",
fuelFlow: "How fast fuel is consumed. Higher = more realistic consumption, lower = longer flights.",
enginePower: "The base power of the engine. Higher = more thrust and acceleration.",
brakeRPM: "RPM at which braking effect is active. Higher = faster deceleration.",
wingArea: "Total wing surface. More = more lift. Too much = unrealistic flight.",
dragFactor: "How much drag affects the aircraft. Higher = slower top speed.",
liftFactor: "How efficiently the wings lift. Higher = easier takeoff & climb.",
CD0: "Parasitic drag coefficient. Higher = more drag.",
CLmax: "Max lift coefficient. Higher = more lift before stalling.",
elevatorFactor: "Effectiveness of pitch control. Higher = more sensitive.",
rudderFactor: "Effectiveness of yaw control. Higher = tighter turns.",
aileronFactor: "Effectiveness of roll control. Higher = faster rolls.",
mass: "Total aircraft mass. Higher = slower response, more stability.",
emptyWeight: "Weight with no payload. Affects takeoff performance.",
maxWeight: "Max takeoff weight. Higher allows more payload.",
inertia: "Aircraft rotational inertia. Higher = more sluggish pitch/yaw/roll.",
pitchMoment: "How much pitch resists change. Higher = smoother pitch.",
yawMoment: "Yaw stability. Higher = less yaw wiggle.",
rollMoment: "Roll stability. Higher = steadier rolls.",
gearDrag: "Drag caused by landing gear. Higher = slower when deployed.",
gearCompression: "Suspension compression. Higher = softer landings.",
gearLength: "Length of gear strut. Higher = taller aircraft stance."
};
const categories = {
"Engine": ["maxRPM", "minRPM", "starterRPM", "idleThrottle", "fuelFlow", "enginePower", "brakeRPM"],
"Aerodynamics": ["wingArea", "dragFactor", "liftFactor", "CD0", "CLmax", "elevatorFactor", "rudderFactor", "aileronFactor"],
"Flight Model": ["mass", "emptyWeight", "maxWeight", "inertia", "pitchMoment", "yawMoment", "rollMoment"],
"Landing Gear": ["gearDrag", "gearCompression", "gearLength"]
};
const menuStyle = `
#geofsModMenu {
position: fixed;
top: 50px;
right: 20px;
background: rgba(0,0,0,0.85);
color: white;
padding: 15px;
border-radius: 10px;
z-index: 9999;
max-height: 90vh;
overflow-y: auto;
font-family: sans-serif;
font-size: 14px;
display: none;
width: 300px;
}
#geofsModMenu h2 {
font-size: 16px;
margin-top: 10px;
border-bottom: 1px solid #ccc;
}
#geofsModMenu input[type="number"] {
width: 80px;
margin: 2px 0;
background: #222;
color: white;
border: 1px solid #555;
padding: 3px;
}
#geofsModMenu input[type="range"] {
width: 100%;
margin-bottom: 10px;
}
.preset-button {
background: #444;
color: white;
padding: 4px 10px;
margin: 2px;
border: 1px solid #777;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
}
.preset-button:hover {
background: #666;
}
#helpOverlay {
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0,0,0,0.95);
color: white;
z-index: 10000;
overflow-y: scroll;
padding: 30px;
display: none;
font-size: 15px;
}
#helpOverlay h1 {
font-size: 24px;
margin-bottom: 10px;
}
#closeHelp {
position: absolute;
top: 10px;
right: 20px;
font-size: 24px;
cursor: pointer;
color: white;
}
#settingsIcon {
position: absolute;
top: 10px;
right: 10px;
font-size: 18px;
cursor: pointer;
background: none;
border: none;
color: white;
}
`;
const styleTag = document.createElement("style");
styleTag.innerHTML = menuStyle;
document.head.appendChild(styleTag);
const menu = document.createElement("div");
menu.id = "geofsModMenu";
const helpOverlay = document.createElement("div");
helpOverlay.id = "helpOverlay";
const closeBtn = document.createElement("div");
closeBtn.id = "closeHelp";
closeBtn.innerHTML = "✕";
closeBtn.onclick = () => {
helpOverlay.style.display = "none";
menu.style.display = "block";
};
helpOverlay.appendChild(closeBtn);
const helpTitle = document.createElement("h1");
helpTitle.textContent = "GeoFS Mod Menu – Variable Help";
helpOverlay.appendChild(helpTitle);
for (const [key, description] of Object.entries(explanations)) {
const para = document.createElement("p");
para.innerHTML = `<strong>${key}</strong>: ${description}`;
helpOverlay.appendChild(para);
}
document.body.appendChild(helpOverlay);
const settingsBtn = document.createElement("button");
settingsBtn.id = "settingsIcon";
settingsBtn.textContent = "⚙️";
settingsBtn.onclick = () => {
menu.style.display = "none";
helpOverlay.style.display = "block";
};
menu.appendChild(settingsBtn);
const inputsMap = {};
const presetHeader = document.createElement("h2");
presetHeader.textContent = "Presets";
menu.appendChild(presetHeader);
for (const [name, values] of Object.entries(presets)) {
const btn = document.createElement("button");
btn.textContent = name;
btn.className = "preset-button";
btn.onclick = () => {
for (const [key, val] of Object.entries(values)) {
const input = inputsMap[key]?.input;
const slider = inputsMap[key]?.slider;
if (input && slider) {
input.value = val;
slider.value = val;
updateModel(key, val);
}
}
};
menu.appendChild(btn);
}
for (const [category, vars] of Object.entries(categories)) {
const header = document.createElement("h2");
header.textContent = category;
menu.appendChild(header);
vars.forEach(variable => {
const setting = variableSettings[variable] || { min: 0, max: 100 };
const label = document.createElement("label");
label.textContent = variable + ": ";
const input = document.createElement("input");
input.type = "number";
input.step = "any";
input.value = setting.min;
const slider = document.createElement("input");
slider.type = "range";
slider.min = setting.min;
slider.max = setting.max;
slider.step = (setting.max - setting.min) / 100;
slider.value = setting.min;
input.oninput = () => {
slider.value = input.value;
updateModel(variable, input.value);
};
slider.oninput = () => {
input.value = slider.value;
updateModel(variable, slider.value);
};
inputsMap[variable] = { input, slider };
label.appendChild(input);
menu.appendChild(label);
menu.appendChild(slider);
menu.appendChild(document.createElement("br"));
});
}
document.body.appendChild(menu);
function updateModel(variable, value) {
try {
if (geofs?.aircraft?.instance?.flightModel) {
const model = geofs.aircraft.instance.flightModel;
if (variable in model) {
model[variable] = parseFloat(value);
console.log(`[MOD MENU] Set ${variable} to ${value}`);
}
}
} catch (err) {
console.error("GeoFS mod menu error:", err);
}
}
document.addEventListener("keydown", function (e) {
if (e.key === "#") {
const visible = menu.style.display === "block";
menu.style.display = visible ? "none" : "block";
helpOverlay.style.display = "none";
}
});
console.log("[GeoFS Mod Menu] Loaded. Press '#' to toggle.");
})();