NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name OG-CoPilot // @description Your ogame co-pilot // @include *.ogame*gameforge.com/game/index.php* // @author DraymIncognito // @copyright 2019, Draym (draymlab.fr) // @license MIT // @version 1.3.0.1 // @updateURL https://openuserjs.org/meta/Draym/OG-CoPilot.meta.js // @downloadURL https://openuserjs.org/install/Draym/OG-CoPilot.user.js // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // ==/UserScript== /* global fleetDispatcher, playerId, LocalizationStrings */ // ==OpenUserJS== // @author Draym // ==/OpenUserJS== (function () { 'use strict'; const currentUni = window.location.href.substring( window.location.href.indexOf("://") + 3, window.location.href.lastIndexOf(".ogame") ) /* **************************************************************/ /* ******************** PARAMETERS ******************************/ /* **************************************************************/ const alertSound = 'https://assets.mixkit.co/sfx/preview/mixkit-bell-notification-933.mp3'; const configTime = 10; // something superior to 1. A random will be done between 0 and 'minutes', 5 will be added to it. const gm_keys = { raidAlert: `${currentUni}_raidAlertActivated`, autoExpedition: `${currentUni}_autoExpeditionActivated`, rescue: `${currentUni}_rescueActivated`, expeditionOnlyGT: `${currentUni}_expeditionOnlyGT`, expeditionOnlyPT: `${currentUni}_expeditionOnlyPT`, expeditionSwitchSystem: `${currentUni}_expeditionSwitchSystem`, expeditionSystem: `${currentUni}_expeditionSystem` } const menu_btn = { home: 'a[href$="page=ingame&component=overview"]:first', fleet: 'a[href$="page=ingame&component=fleetdispatch"]:first', fleet_continue: '#continueToFleet2', fleet_send: '#sendFleet', mission_exploration: "#missionButton15" } const ogame_class = { PLAYER_CLASS_EXPLORER : 3, PLAYER_CLASS_WARRIOR : 2, PLAYER_CLASS_MINER :1, PLAYER_CLASS_NONE : 0 } console.log(currentUni) let _refreshTimer = null let _nextRefresh = null const _wait = 3 const _wait_long = 7 const _refreshTime = Math.floor(Math.random() * 10) + configTime; const $ = unsafeWindow.$; const alertSoundAudio = document.createElement('audio'); alertSoundAudio.src = alertSound; alertSoundAudio.preload = 'auto'; const browserAudio = new AudioContext(); const ui_notificationDetails = { title: 'Ogame Alert', text: 'You are under attack on Ogame', timeout: 15000, image: 'https://is4-ssl.mzstatic.com/image/thumb/Purple91/v4/31/2d/98/312d98f7-b935-69b3-596e-2159d13c0865/mzl.kxpdlefq.png/246x0w.jpg', onclick: function () { window.focus(); } }; let player = {} /* **************************************************************/ /* ********************* BROWSER ********************************/ /* **************************************************************/ function log(data) { console.log(`[OG-CoPilot]> ${data}`) } function log_info(data) { console.log(`[OG-CoPilot]%c ${data}`, 'color: #1E90FF') } function log_success(data) { console.log(`[OG-CoPilot]%c ${data}`, 'color: #7CFC00') } function log_error(data) { console.log(`[OG-CoPilot]%c ${data}`, 'color: #B22222') } function clickOn(value) { const btn = $(value) if (btn.length != 0) { btn[0].click() } else { log_error(`btn not found for '${value}'`) } } function fillInput(search, value) { const input = $(search) if (input.length != 0) { input.click() input.focus() //input.attr('value', value) input.val(value).trigger('change') } } function notifyAlert() { browserAudio.resume().then(() => { alertSoundAudio.play(); GM_notification(ui_notificationDetails); log('Playback resumed successfully'); }); } function browser_allow_notifications() { if (typeof GM_notification === "function") { log("notifications ON"); return; } window.GM_notification = function (ntcOptions) { checkPermission(); function askPermission() { Notification.requestPermission(function (permission) { log("New permission: ", permission); checkPermission(); }); } function checkPermission() { if (Notification.permission === "granted") { fireNotice(); } else if (Notification.permission === "denied") { log("User has denied notifications for this page/site!"); askPermission(); } else { askPermission(); } } function fireNotice() { if (!ntcOptions.title) { log("Title is required for notification"); return; } if (ntcOptions.text && !ntcOptions.body) { ntcOptions.body = ntcOptions.text; } var ntfctn = new Notification(ntcOptions.title, ntcOptions); if (ntcOptions.onclick) { ntfctn.onclick = ntcOptions.onclick; } if (ntcOptions.timeout) { setTimeout(function () { ntfctn.close(); }, ntcOptions.timeout); } } } } function removeNumSeparator(str) { return str.replace(new RegExp(`\\${LocalizationStrings["thousandSeperator"]}`, "g"), "") } /* **************************************************************/ /* ********************** OGAME *********************************/ /* **************************************************************/ const ogameHelper = function () { var requestId = 0; function expedition(message) { let rid = requestId++; return new Promise((function (resolve, reject) { var listener = function (evt) { if (evt.detail.requestId == rid) { window.removeEventListener("ogi-expedition-rep", listener); resolve(evt.detail.type) } }; window.addEventListener("ogi-expedition-rep", listener); var payload = {requestId: rid, message: message}; window.dispatchEvent(new CustomEvent("ogi-expedition", {detail: payload})) })) } function Get(id) { let rid = requestId++; return new Promise((function (resolve, reject) { var listener = function (evt) { if (evt.detail.requestId == rid) { window.removeEventListener("ogi-players-rep", listener); resolve(evt.detail.player) } }; window.addEventListener("ogi-players-rep", listener); var payload = {requestId: rid, id: id}; window.dispatchEvent(new CustomEvent("ogi-players", {detail: payload})) })) } function filter(name, alliance) { let rid = requestId++; return new Promise((function (resolve, reject) { var listener = function (evt) { if (evt.detail.requestId == rid) { window.removeEventListener("ogi-filter-rep", listener); resolve(evt.detail.players) } }; window.addEventListener("ogi-filter-rep", listener); var payload = {requestId: rid, name: name, alliance: alliance}; window.dispatchEvent(new CustomEvent("ogi-filter", {detail: payload})) })) } return {getExpeditionType: expedition, getPlayer: Get, filter: filter} }(); /* **************************************************************/ /* ********************** DATA **********************************/ /* **************************************************************/ function activateRaidAlert(status) { GM_setValue(gm_keys.raidAlert, status); launch() } function isRaidAlertActivated(){ return GM_getValue(gm_keys.raidAlert) === true; } function activateAutoExpedition(status) { GM_setValue(gm_keys.autoExpedition, status); launch() } function isAutoExpeditionActivated(){ return GM_getValue(gm_keys.autoExpedition) === true; } function activateRescue(status) { GM_setValue(gm_keys.rescue, status); launch() } function isRescueActivated(){ return GM_getValue(gm_keys.rescue) === true; } function setExpeditionOnlyGT(status) { GM_setValue(gm_keys.expeditionOnlyPT, false); GM_setValue(gm_keys.expeditionOnlyGT, status); } function isExpeditionOnlyGT(){ return GM_getValue(gm_keys.expeditionOnlyGT) === true; } function setExpeditionOnlyPT(status) { GM_setValue(gm_keys.expeditionOnlyGT, false); GM_setValue(gm_keys.expeditionOnlyPT, status); } function isExpeditionOnlyPT(){ return GM_getValue(gm_keys.expeditionOnlyPT) === true; } function setExpeditionSwitchSystem(status) { GM_setValue(gm_keys.expeditionSwitchSystem, status); } function isExpeditionSwitchSystem(){ return GM_getValue(gm_keys.expeditionSwitchSystem) === true; } function setExpeditionSystem(system) { if (isNaN(system)) { GM_setValue(gm_keys.expeditionSystem, null); } else { GM_setValue(gm_keys.expeditionSystem, system); } } function getExpeditionSystem(){ const value = GM_getValue(gm_keys.expeditionSystem); if (value === undefined || value === null) { return 0; } return value; } /* **************************************************************/ /* ********************** FLEET *********************************/ /* **************************************************************/ const menu_fleet = { pt : ".transporterSmall", gt: ".transporterLarge", explorer:".explorer", cruiser: ".cruiser", spy:".espionageProbe", battleship:".battleship" } const fleet_id = { pt : 202, gt: 203, cle: 204, clo: 205, cruiser: 206, battleship:207, colon: 208, recycler: 209, spy:210, bomber: 211, sat: 212, destroyer: 213, rip: 214, interceptor:215, reaper: 218, explorer: 219, } const fleet = { pickShip: (shipID, amount) => { fleetDispatcher.shipsOnPlanet.forEach((ship => { if (ship.id == shipID) { if (amount > ship.number) amount = ship.number; fleetDispatcher.selectShip(shipID, amount); fleetDispatcher.refresh() } })) return amount }, setTargetPos: (x, y, z) => { fleetDispatcher.targetPlanet.type = 1; if (x != null) { fleetDispatcher.targetPlanet.galaxy = x; } if (y != null) { fleetDispatcher.targetPlanet.system = y; } if (z != null) { fleetDispatcher.targetPlanet.position = z; } fleetDispatcher.refreshTarget(); fleetDispatcher.updateTarget(); fleetDispatcher.refresh(); } } function calcNeededShips(options) { options = options || {}; let resources = [removeNumSeparator(document.querySelector("#resources_metal").textContent), removeNumSeparator(document.querySelector("#resources_crystal").textContent), removeNumSeparator(document.querySelector("#resources_deuterium").textContent)]; resources = resources.reduce(((a, b) => parseInt(a) + parseInt(b))); if (options.resources || options.resources == 0) resources = options.resources; let type = options.fret; let fret; if (type == 202) { fret = fleetDispatcher.fleetHelper.shipsData[202].baseCargoCapacity } else if (type == 203) { fret = fleetDispatcher.fleetHelper.shipsData[203].baseCargoCapacity } else if (type == 219) { fret = fleetDispatcher.fleetHelper.shipsData[219].baseCargoCapacity } else if (type == 210) { fret = fleetDispatcher.fleetHelper.shipsData[210].baseCargoCapacity } else if (type == 209) { fret = 0 } let total = resources / fret; if (options.moreFret) total *= 107 / 100; return Math.ceil(total) } function calcOptiExpedition() { let maxTotal = 0 let minPT = 0 let minGT = 0 if (player.topScore < 1e4) { maxTotal = 4e4; minPT = 273; minGT = 91 } else if (player.topScore < 1e5) { maxTotal = 5e5; minPT = 423; minGT = 141 } else if (player.topScore < 1e6) { maxTotal = 12e5; minPT = 423; minGT = 191 } else if (player.topScore < 5e6) { maxTotal = 18e5; minPT = 423; minGT = 191 } else if (player.topScore < 25e6) { maxTotal = 24e5; minPT = 573; minGT = 191 } else if (player.topScore < 5e7) { maxTotal = 3e6; minPT = 723; minGT = 241 } else if (player.topScore < 75e6) { maxTotal = 36e5; minPT = 873; minGT = 291 } else if (player.topScore < 1e8) { maxTotal = 42e5; minPT = 1023; minGT = 341 } else { maxTotal = 5e6; minPT = 1223; minGT = 417 } return {maxTotal: player.class == ogame_class.PLAYER_CLASS_EXPLORER ? maxTotal * 3 * 5 : maxTotal * 2, minPT:minPT, minGT:minGT} } /* **************************************************************/ /* ******************* SCRIPT UTILS *****************************/ /* **************************************************************/ function scheduleRefreshPage(callbacks) { //destroyRefresh(); if (isAutoExpeditionActivated() || isRaidAlertActivated()) { const wait = _refreshTime * 60 * 1000 _nextRefresh = new Date(Date.now() + wait) log_info(`Refresh: `+ _nextRefresh) _refreshTimer = setInterval(() => { location.reload(); }, wait) } next(callbacks) } function destroyRefresh() { if (_refreshTimer != null) { clearInterval(_refreshTimer); } } function next(callbacks) { if (callbacks != null) { if (callbacks.length != 0) { callbacks[0](callbacks.slice(1)) } else { callbacks(null) } } } function nextSlow(wait, callbacks) { if (callbacks != null) { if (callbacks.length != 0) { setTimeout(function () { callbacks[0](callbacks.slice(1)) }, wait * 1000); } else { callbacks(null) } } } /* **************************************************************/ /* ********************** SCRIPT ********************************/ /* **************************************************************/ function runRaidAlert(callbacks) { if (isRaidAlertActivated()){ log_info(`Raid Alert`) if ($('#attack_alert').hasClass("soon")) { notifyAlert(); } } next(callbacks); } function rescue(callbacks) { next(callbacks) } function runAutoExpeditions(callbacks) { if (isAutoExpeditionActivated()) { log_info(`Auto Expedition`) if (!/page=ingame&component=fleetdispatch/.test(location.href)) { clickOn(menu_btn.fleet) } else { const expeTotal = $("span:contains('Expéditions:')"); if (expeTotal.length != 0) { const data = expeTotal.text(); const current = parseInt(data.slice(data.indexOf('/') - 2, data.indexOf('/'))); const total = parseInt(data.slice(data.indexOf('/') + 1, data.indexOf('/') + 3)); const availableSlot = total - current log(`expeditions ${availableSlot}=${current}/${total}`) setExpeditionSystem(getExpeditionSystem() == 0 ? 1 : 0); if (availableSlot >0) { const availablePT = parseInt($(menu_fleet.pt).text().replace(".", "")); const availableGT = parseInt($(menu_fleet.gt).text().replace(".", "")); let {maxTotal, minPT, minGT } = calcOptiExpedition(); let maxPT = Math.max(minPT, calcNeededShips({fret: 202, resources: maxTotal})); let maxGT = Math.max(minGT, calcNeededShips({fret: 203, resources: maxTotal})); log(`available fleet ${availablePT}pt[${maxPT}] and ${availableGT}gt[${maxGT}]`) if (availablePT != 0 || availableGT != 0) { const possiblePT = availablePT / availableSlot; const possibleGT = availableGT / availableSlot; let sendGT = 0; let sendPT = 0; if (isExpeditionOnlyPT()) { sendPT = Math.min(possiblePT, maxPT); } else if (isExpeditionOnlyGT()) { sendGT = Math.min(possibleGT, maxGT); } else { sendPT = Math.min(possiblePT, maxPT) if (sendPT < maxPT) { sendGT = Math.min(possibleGT, maxGT - (sendPT / 5)) } } function _setupFleet(cb) { fleet.pickShip(fleet_id.pt, Math.round(sendPT)); fleet.pickShip(fleet_id.gt, Math.round(sendGT)); fleet.pickShip(fleet_id.explorer, 1); fleet.pickShip(fleet_id.spy, 1); fleet.pickShip(fleet_id.interceptor, 1); fleet.pickShip(fleet_id.reaper, 1); log(`expedition send ${sendPT}pt and ${sendGT}gt`) nextSlow(_wait, cb); } function _nextFleet(cb) { $(menu_btn.fleet_continue).click() nextSlow(_wait_long, cb); } function _setPosition(cb){ if (isExpeditionSwitchSystem()) { const current = Number.parseInt($("#system").val()); console.log(`system: ${current} - ${getExpeditionSystem()} = ${current - getExpeditionSystem()}`) fleet.setTargetPos(null, current - getExpeditionSystem(), 16); } else { fleet.setTargetPos(null, null, 16); } nextSlow(_wait_long, cb) } function _selectMission(cb){ $(menu_btn.mission_exploration).click() nextSlow(_wait, cb) } function _launchFleet(cb) { $(menu_btn.fleet_send).click() nextSlow(_wait_long, cb); } function _repeat(cb) { clickOn(menu_btn.home) nextSlow(_wait_long, cb); } const scripts = [_setupFleet, _nextFleet, _setPosition, _selectMission, _launchFleet, _repeat, ...callbacks]; return nextSlow(_wait_long, scripts); } } else { setExpeditionSystem(getExpeditionSystem() == 0 ? 1 : 0); } } else { log_error("is lost"); } } } return next(callbacks); } /* **************************************************************/ /* ********************* STARTUP ********************************/ /* **************************************************************/ function launch() { setExpeditionSwitchSystem(true); setExpeditionOnlyPT(false); console.log(`pt[${isExpeditionOnlyPT()}], gt[${isExpeditionOnlyGT()}], [${isExpeditionSwitchSystem()}]system(${getExpeditionSystem()})`) player.topScore = 55000000 if (document.querySelector("#characterclass .explorer")) { player.class = ogame_class.PLAYER_CLASS_EXPLORER } else if (document.querySelector("#characterclass .warrior")) { player.class = ogame_class.PLAYER_CLASS_WARRIOR } else if (document.querySelector("#characterclass .miner")) { player.class = ogame_class.PLAYER_CLASS_MINER } else { player.class = ogame_class.PLAYER_CLASS_NONE } log_success("ready"); browser_allow_notifications(); const scripts = [scheduleRefreshPage, runRaidAlert, rescue, runAutoExpeditions]; try { next(scripts); } catch (e) { scheduleRefreshPage() } } /* **************************************************************/ /* ******************* GUI - PANEL ******************************/ /* **************************************************************/ unsafeWindow.ui_activateRaidAlert = function (cb) { activateRaidAlert(cb.checked); } unsafeWindow.ui_activateAutoExpedition = function (cb) { activateAutoExpedition(cb.checked); } unsafeWindow.ui_activateRescue = function (cb) { activateRescue(cb.checked); } function ui_drawMenu(visible) { if (!visible) { $("#ui-menuOptions").hide(); } else { $("#ui-menuOptions").show(); $("#ui-raidAlertActive").prop('checked', isRaidAlertActivated()); $("#ui-expeditionActive").prop('checked', isAutoExpeditionActivated()); $("#ui-RescueActive").prop('checked', isRescueActivated()); } } function ui_drawMenuDetails(visible, target) { switch (target) { case "raidAlert" : $("#ui-menuDetails-expedition").hide(); $("#ui-menuDetails-rescue").hide(); break; case "expedition" : $("#ui-menuDetails-raidAlert").hide(); $("#ui-menuDetails-rescue").hide(); break; case "rescue" : $("#ui-menuDetails-raidAlert").hide(); $("#ui-menuDetails-expedition").hide(); break; } if (!visible) { $("#ui-menuDetails").hide(); } else { $("#ui-menuDetails").show(); $("#ui-menuDetails-" + target).show(); } } $("body").append(` <div id="ui-menuDetails" style="display: none;"> <div id="ui-menuDetails-raidAlert" style="display: none;"> <h3>RaidAlert Options</h3> </div> <div id="ui-menuDetails-expedition" style="display: none;"> <h3>Expedition Options</h3> </div> <div id="ui-menuDetails-rescue" style="display: none;"> <h3>Rescue Options</h3> </div> <br/> <button id="ui-closeMenuDetailExpe" type="button">Close</button> </div> <div id="ui-menuOptions" style="display: none;"> <ul id="ui-scripts"> <li id="ui-raidAlert"> <span class="alert_icon"/> <label class="tooltipRight js_hideTipOnMobile ui-switch"> <input id="ui-raidAlertActive" type="checkbox" onclick="ui_activateRaidAlert(this);"> <span class="ui-slider round"></span> </label> <a class="menubutton" id="ui-raidAlert-menu" href="#" accesskey="" target="_self"> <span class="textlabel">Alert on Raids</span> </a> </li> <li id="ui-runExpedition"> <span class="expedition_icon"> <label class="tooltipRight js_hideTipOnMobile ui-switch"> <input id="ui-expeditionActive" type="checkbox" onclick="ui_activateAutoExpedition(this);"> <span class="ui-slider round"></span> </label> <a class="menubutton" id="ui-runExpedition-menu" href="#" accesskey="" target="_self"> <span class="textlabel" onClick="">Auto Expeditions</span> </a> </li> <li id="ui-rescue"> <span class="attack_icon"> <label class="tooltipRight js_hideTipOnMobile ui-switch"> <input id="ui-rescueActive" type="checkbox" onclick="ui_activateRescue(this);"> <span class="ui-slider round"></span> </label> <a class="menubutton" id="ui-rescue-menu" href="#" accesskey="" target="_self"> <span class="textlabel" onClick="">Save my Ass</span> </a> </li> </ul> <br/> <button id="ui-closeMenuOptions" type="button">Close</button> </div> `); document.getElementById('ui-raidAlert-menu').addEventListener("click", function (event) { ui_drawMenuDetails($("#ui-menuDetails-raidAlert").is(":hidden"), "raidAlert"); }, true); document.getElementById('ui-runExpedition-menu').addEventListener("click", function (event) { ui_drawMenuDetails($("#ui-menuDetails-expedition").is(":hidden"), "expedition"); }, true); document.getElementById('ui-rescue-menu').addEventListener("click", function (event) { ui_drawMenuDetails($("#ui-menuDetails-rescue").is(":hidden"), "rescue"); }, true); $("#ui-closeMenuOptions").click(function () { ui_drawMenu(false); }); $("#ui-closeMenuDetails").click(function () { ui_drawMenuDetails(false); }); /* **************************************************************/ /* *********************** GUI **********************************/ /* **************************************************************/ unsafeWindow.ui_quickActivate = function (cb) { activateRaidAlert(cb.checked); activateRescue(cb.checked); activateAutoExpedition(cb.checked); } if (!/page=empire/.test(location.href)) { var aff_option = ` <span class="menu_icon"> <label class="tooltipRight js_hideTipOnMobile ui-switch"> <input id="ui-quickAction" type="checkbox" onclick="ui_quickActivate(this);"> <span class="ui-slider round"></span> </label> </span> <a id="ui-drawOption" class="menubutton" href="#" accesskey="" target="_self"> <span class="textlabel">OG-CoPilot</span> </a>`; const menu = document.createElement("li"); menu.innerHTML = aff_option; menu.className += "ui-menu"; menu.id = 'ui-copilot-menu'; document.getElementById('menuTable').appendChild(menu); $("#ui-quickAction").prop('checked', isRaidAlertActivated() || isRescueActivated() || isAutoExpeditionActivated()); document.getElementById('ui-drawOption').addEventListener("click", function (event) { ui_drawMenu($("#ui-menuOptions").is(":hidden")); }, true); launch(); } /* **************************************************************/ /* ************************ CSS *********************************/ /* **************************************************************/ GM_addStyle(` /*** THEME ***/ #menuTable > .ui-menu { margin-top: 10px !important; margin-bottom: 10px !important; } #menuTable > .ui-menu ~ #ui-menu { margin-top: -10px !important; margin-bottom: 10px !important; } #ui-copilot-menu a span { color: #68a2ff !important; } /*** TOGGLE SWITCH ***/ .ui-switch { position: relative; display: inline-block; width: 30px; height: 17px; margin-top: 5px; } .ui-switch input { opacity: 0; width: 0; height: 0; } .ui-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ff4949; -webkit-transition: .4s; transition: .4s; } .ui-slider:before { position: absolute; content: ""; height: 13px; width: 13px; left: 2px; bottom: 2px; background-color: white; -webkit-transition: .4s; transition: .4s; } input:checked + .ui-slider { background-color: #0664b0; } input:focus + .ui-slider { box-shadow: 0 0 1px #0664b0; } input:checked + .ui-slider:before { -webkit-transform: translateX(13px); -ms-transform: translateX(13px); transform: translateX(13px); } .ui-slider.round { border-radius: 17px; } .ui-slider.round:before { border-radius: 50%; } /*** MENU ***/ .inputError { color: red; visibility: none; } #ui-menuOptions { position: fixed; top: 70%; left: 0; width: 300px; padding: 10px; background: rgba(51, 102, 153, 0.9); border: 2px black; border-radius: 1ex; z-index: 777; } #ui-menuOptions button{ cursor: pointer; margin: 1em 1em 0; border: 1px outset buttonface; } #ui-menuDetails { position: fixed; top: 50%; left: 0; width: 200px; height: 100px; padding: 10px; background: rgba(51, 102, 153, 0.9); border: 2px black; border-radius: 1ex; z-index: 777; } #ui-menuDetails button{ cursor: pointer; margin: 1em 1em 0; border: 1px outset buttonface; } `); })();