NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Shockr - Tiberium Alliances Tools // @author Shockr <shockr@c.ac.nz> // @description Tools to work with Tiberium alliances http://c.ac.nz/taopt // @include http*://prodgame*.alliances.commandandconquer.com/*/index.aspx* // @grant GM_xmlhttpRequest // @grant GM_updatingEnabled // @grant unsafeWindow // @version 4.5.3 // @downloadURL https://c.ac.nz/client/shockrtools.user.js // @icon https://c.ac.nz/favicon.png // ==/UserScript== var setupShockrTools = function() { /* Begin: client/modules/main.js */ var ST = window.ST || {}; ST.modules = []; ST.register = function(module) { ST[module.name.toLowerCase()] = module; ST[module.name] = module; for (var i = 0; i < ST.modules.length; i++) { var stMod = ST.modules[i]; if (stMod.name === module.name) { ST.log.info('Destroy [' + module.name + ']'); if (typeof stMod.destroy === 'function') { stMod.destroy(); } ST.modules.slice(i, 1); break; } } ST.modules.push(module); if (ST.util.isLoaded() === false) { return; } if (typeof module.startup === 'function') { module.startup(); } }; ST.startup = function() { if (ST.config === undefined) { ST.config = ST.storage.get('config') || {}; } if (ST.util.isLoaded() === false) { setTimeout(function() { ST.startup(); }, 1000); return; } ST.modules.forEach(function(o) { if (ST.config[o.name.toLowerCase()] === false) { return ST.log.info('Skipping [' + o.name + '] as its disabled'); } ST.log.info('Starting [' + o.name + ']'); if (typeof o.startup === 'function') { o.startup(); } }); }; ST.log = function() { console.log.apply(console, arguments); }; ST.log.WARN = 40; ST.log.INFO = 30; ST.log.DEBUG = 20; ST.log.level = ST.log.WARN; ST.log.info = function() { if (ST.log.level > ST.log.INFO) { return; } ST.log(arguments); }; ST.log.warn = function() { if (ST.log.level > ST.log.WARN) { return; } ST.log(arguments); }; ST.log.debug = function() { if (ST.log.level > ST.log.DEBUG) { return; } ST.log(arguments); }; // Wrap localStorage // - Prefix keys with "ST:" // - Automatically convert to/from JSON // - Remove item if setting null or undefined ST.storage = {}; ST.storage.get = function(key) { var value = localStorage.getItem('ST:' + key); if (value === null) { return value; } return JSON.parse(value); }; ST.storage.set = function(key, value) { if (value === null || value === undefined) { return ST.storage.remove(key); } if (typeof value !== 'string') { value = JSON.stringify(value); } return localStorage.setItem('ST:' + key, value); }; ST.storage.remove = function(key) { return localStorage.removeItem('ST:' + key); }; ST.util = { // URL: 'wss://localhost:443/websocket', URL: 'wss://c.ac.nz/websocket', isLoaded: function() { if (typeof qx === 'undefined') { return false; } var a = qx.core.Init.getApplication(); if (a === null || a === undefined) { return false; } var mb = qx.core.Init.getApplication().getMenuBar(); if (mb === null || mb === undefined) { return false; } var md = ClientLib.Data.MainData.GetInstance(); if (md === null || md === undefined) { return false; } var player = md.get_Player(); if (player === null || player === undefined) { return false; } if (player.get_Name() === '') { return false; } return true; }, connect: function(callback) { var options = { endpoint: ST.util.URL, SocketConstructor: WebSocket }; var ddp = new DDP(options); ddp.on('connected', function() { ST.log.info('Websocket connected'); ST.util.ddp = ddp; if (typeof callback === 'function') { callback(); } }); }, api: function(method, data, callback) { if (typeof callback === 'undefined') { callback = ST.util.noop; } if (ST.util.ddp === undefined) { return ST.util.connect(function() { ST.util.api(method, data, callback); }); } ST.util.ddp.method(method, [data], callback); }, alert: function(message) { window.alert(message); }, handleError: function() { console.log('Error Caught:', arguments); }, noop: function() {}, _g: function(k, r, q, m) { var p = []; var o = k.toString(); var n = o.replace(/\s/gim, ''); p = n.match(r); var l; for (l = 1; l < (m + 1); l++) { if (p !== null && p[l].length === 6) { console.debug(q, l, p[l]); } else { if (p !== null && p[l].length > 0) { console.warn(q, l, p[l]); } else { console.error('Error - ', q, l, 'not found'); console.warn(q, n); } } } return p; }, // Empty button until one gets made for us. button: { setLabel: function() {} } }; window.ST = ST; ST.startup(); window.onerror = function() { ST.util.handleError(arguments); };/* End: client/modules/main.js */ }; var ST_MODULES = window.ST_MODULES = []; ST_MODULES.push(setupShockrTools); var setupShockrModules = function() { console.time('ST:LoadModules'); /* Begin: client/modules/ddp.js */ (function(root, factory) { if (typeof define === 'function' && define.amd) { define(factory); } else if (typeof exports === 'object') { module.exports = factory(); } else { root.DDP = factory(); } }(this, function() { 'use strict'; var uniqueId = (function() { var i = 0; return function() { return (i++).toString(); }; })(); var INIT_DDP_MESSAGE = '{\"server_id\":\"0\"}'; // After hitting the plateau, it'll try to reconnect // every 16.5 seconds var RECONNECT_ATTEMPTS_BEFORE_PLATEAU = 10; var TIMER_INCREMENT = 300; var DEFAULT_PING_INTERVAL = 10000; var DDP_SERVER_MESSAGES = [ 'added', 'changed', 'connected', 'error', 'failed', 'nosub', 'ready', 'removed', 'result', 'updated', 'ping', 'pong' ]; var DDP_VERSION = '1'; var DDP = function(options) { // Configuration this._endpoint = options.endpoint; this._SocketConstructor = options.SocketConstructor; this._autoreconnect = !options.do_not_autoreconnect; this._ping_interval = options._ping_interval || DEFAULT_PING_INTERVAL; this._socketInterceptFunction = options.socketInterceptFunction; // Subscriptions callbacks this._onReadyCallbacks = {}; this._onStopCallbacks = {}; this._onErrorCallbacks = {}; // Methods callbacks this._onResultCallbacks = {}; this._onUpdatedCallbacks = {}; this._events = {}; this._queue = []; // Setup this.readyState = -1; this._reconnect_count = 0; this._reconnect_incremental_timer = 0; // Init if (!options.do_not_autoconnect) { this.connect(); } }; DDP.prototype.constructor = DDP; DDP.prototype.connect = function() { this.readyState = 0; this._socket = new this._SocketConstructor(this._endpoint); this._socket.onopen = this._on_socket_open.bind(this); this._socket.onmessage = this._on_socket_message.bind(this); this._socket.onerror = this._on_socket_error.bind(this); this._socket.onclose = this._on_socket_close.bind(this); }; DDP.prototype.method = function(name, params, onResult, onUpdated) { var id = uniqueId(); this._onResultCallbacks[id] = onResult; this._onUpdatedCallbacks[id] = onUpdated; this._send({ msg: 'method', id: id, method: name, params: params }); return id; }; DDP.prototype.sub = function(name, params, onReady, onStop, onError) { var id = uniqueId(); this._onReadyCallbacks[id] = onReady; this._onStopCallbacks[id] = onStop; this._onErrorCallbacks[id] = onError; this._send({ msg: 'sub', id: id, name: name, params: params }); return id; }; DDP.prototype.unsub = function(id) { this._send({ msg: 'unsub', id: id }); return id; }; DDP.prototype.on = function(name, handler) { this._events[name] = this._events[name] || []; this._events[name].push(handler); }; DDP.prototype.off = function(name, handler) { if (!this._events[name]) { return; } var index = this._events[name].indexOf(handler); if (index !== -1) { this._events[name].splice(index, 1); } }; DDP.prototype._emit = function(name /* , arguments */ ) { if (!this._events[name]) { return; } var args = arguments; var self = this; this._events[name].forEach(function(handler) { handler.apply(self, Array.prototype.slice.call(args, 1)); }); }; DDP.prototype._send = function(object) { if (this.readyState !== 1 && object.msg !== 'connect') { this._queue.push(object); return; } var message; if (typeof EJSON === 'undefined') { message = JSON.stringify(object); } else { message = EJSON.stringify(object); } if (this._socketInterceptFunction) { this._socketInterceptFunction({ type: 'socket_message_sent', message: message, timestamp: Date.now() }); } this._socket.send(message); }; DDP.prototype._try_reconnect = function() { if (this._reconnect_count < RECONNECT_ATTEMPTS_BEFORE_PLATEAU) { setTimeout(this.connect.bind(this), this._reconnect_incremental_timer); this._reconnect_count += 1; this._reconnect_incremental_timer += TIMER_INCREMENT * this._reconnect_count; } else { setTimeout(this.connect.bind(this), this._reconnect_incremental_timer); } }; DDP.prototype._on_result = function(data) { if (this._onResultCallbacks[data.id]) { this._onResultCallbacks[data.id](data.error, data.result); delete this._onResultCallbacks[data.id]; if (data.error) { delete this._onUpdatedCallbacks[data.id]; } } else { if (data.error) { delete this._onUpdatedCallbacks[data.id]; throw data.error; } } }; DDP.prototype._on_updated = function(data) { var self = this; data.methods.forEach(function(id) { if (self._onUpdatedCallbacks[id]) { self._onUpdatedCallbacks[id](); delete self._onUpdatedCallbacks[id]; } }); }; DDP.prototype._on_nosub = function(data) { if (data.error) { if (!this._onErrorCallbacks[data.id]) { delete this._onReadyCallbacks[data.id]; delete this._onStopCallbacks[data.id]; throw new Error(data.error); } this._onErrorCallbacks[data.id](data.error); delete this._onReadyCallbacks[data.id]; delete this._onStopCallbacks[data.id]; delete this._onErrorCallbacks[data.id]; return; } if (this._onStopCallbacks[data.id]) { this._onStopCallbacks[data.id](); } delete this._onReadyCallbacks[data.id]; delete this._onStopCallbacks[data.id]; delete this._onErrorCallbacks[data.id]; }; DDP.prototype._on_ready = function(data) { var self = this; data.subs.forEach(function(id) { if (self._onReadyCallbacks[id]) { self._onReadyCallbacks[id](); delete self._onReadyCallbacks[id]; } }); }; DDP.prototype._on_error = function(data) { this._emit('error', data); }; DDP.prototype._on_connected = function(data) { var self = this; var firstCon = self._reconnect_count === 0; var eventName = firstCon ? 'connected' : 'reconnected'; self.readyState = 1; self._reconnect_count = 0; self._reconnect_incremental_timer = 0; var length = self._queue.length; for (var i = 0; i < length; i++) { self._send(self._queue.shift()); } self._emit(eventName, data); // Set up keepalive ping-s self._ping_interval_handle = setInterval(function() { var id = uniqueId(); self._send({ msg: 'ping', id: id }); }, self._ping_interval); }; DDP.prototype._on_failed = function(data) { this.readyState = 4; this._emit('failed', data); }; DDP.prototype._on_added = function(data) { this._emit('added', data); }; DDP.prototype._on_removed = function(data) { this._emit('removed', data); }; DDP.prototype._on_changed = function(data) { this._emit('changed', data); }; DDP.prototype._on_ping = function(data) { this._send({ msg: 'pong', id: data.id }); }; DDP.prototype._on_pong = function() { // For now, do nothing. // In the future we might want to log latency or so. }; DDP.prototype._on_socket_close = function() { if (this._socketInterceptFunction) { this._socketInterceptFunction({ type: 'socket_close', timestamp: Date.now() }); } clearInterval(this._ping_interval_handle); this.readyState = 4; this._emit('socket_close'); if (this._autoreconnect) { this._try_reconnect(); } }; DDP.prototype._on_socket_error = function(e) { if (this._socketInterceptFunction) { this._socketInterceptFunction({ type: 'socket_error', error: JSON.stringify(e), timestamp: Date.now() }); } clearInterval(this._ping_interval_handle); this.readyState = 4; this._emit('socket_error', e); }; DDP.prototype._on_socket_open = function() { if (this._socketInterceptFunction) { this._socketInterceptFunction({ type: 'socket_open', timestamp: Date.now() }); } this._send({ msg: 'connect', version: DDP_VERSION, support: [DDP_VERSION] }); }; DDP.prototype._on_socket_message = function(message) { if (this._socketInterceptFunction) { this._socketInterceptFunction({ type: 'socket_message_received', message: message.data, timestamp: Date.now() }); } var data; if (message.data === INIT_DDP_MESSAGE) { return; } try { if (typeof EJSON === 'undefined') { data = JSON.parse(message.data); } else { data = EJSON.parse(message.data); } if (DDP_SERVER_MESSAGES.indexOf(data.msg) === -1) { throw new Error(); } } catch (e) { console.warn('Non DDP message received:', INIT_DDP_MESSAGE); console.warn(message.data); return; } this['_on_' + data.msg](data); }; return DDP; }));/* End: client/modules/ddp.js */ /* Begin: client/modules/button.js */ /* globals ClientLib, qx, ST */ var Button = { name: 'Button', startup: function() { registerButtons(); // Init the button ST.qx.main.getInstance(); ST.util.button.setLabel('Scan'); } }; function registerButtons() { qx.Class.define('ST.qx.main', { type: 'singleton', extend: qx.core.Object, construct: function() { this.button = new qx.ui.form.Button('Scan'); this.button.set({ width: 100, appearance: 'button-bar-center', toolTipText: 'Scan' }); ST.util.button = this.button; this.button.addListener('click', this.click, this); var mainBar = qx.core.Init.getApplication().getUIItem(ClientLib.Data.Missions.PATH.BAR_MENU); mainBar.getChildren()[1].addAt(this.button, 8, { top: 0, right: 0 }); console.log('ST:Util - Scan Button added'); }, members: { click: function() { if (ST.BaseScanner.isScanning()) { return ST.BaseScanner.abort(); } ST.BaseScanner.scan(); } } }); } ST.register(Button);/* End: client/modules/button.js */ /* Begin: client/modules/basescanner.js */ /* globals ClientLib, phe, ST */ var MAX_FAILS = 25; var BaseScanner = { name: 'BaseScanner', _patched: false, _bases: {}, _selectionBases: {}, _toScan: [], _scanning: false, failCount: 0, scan: function() { if (BaseScanner._scanning) { return; } window.open('https://c.ac.nz/#/bases/' + ClientLib.Data.MainData.GetInstance().get_Player().get_Name(), 'ST-BaseScanner'); ST.util.button.setLabel('Scanning'); BaseScanner._bases = {}; BaseScanner._scanning = true; BaseScanner._count = 0; BaseScanner._done = 0; BaseScanner.index = -1; BaseScanner._toScanMap = {}; BaseScanner._toScan = []; var allCities = ClientLib.Data.MainData.GetInstance().get_Cities().get_AllCities().d; for (var selectedBaseID in allCities) { if (!allCities.hasOwnProperty(selectedBaseID)) { continue; } var selectedBase = allCities[selectedBaseID]; if (selectedBase === undefined) { throw new Error('unable to find base: ' + selectedBaseID); } BaseScanner.getNearByBases(selectedBase); } BaseScanner.scanNextBase(); }, getNearByBases: function(base) { var x = base.get_PosX(); var y = base.get_PosY(); var maxAttack = ClientLib.Data.MainData.GetInstance().get_Server().get_MaxAttackDistance() - 0.5; var world = ClientLib.Data.MainData.GetInstance().get_World(); ST.log.debug('[BaseScanner] Scanning from ' + x + ':' + y); var toScanCount = 0; for (var scanY = y - 11; scanY <= y + 11; scanY++) { for (var scanX = x - 11; scanX <= x + 11; scanX++) { var distX = Math.abs(x - scanX); var distY = Math.abs(y - scanY); var distance = Math.sqrt((distX * distX) + (distY * distY)); // too far away to scan if (distance >= maxAttack) { continue; } // already scanning this base from another city. if (BaseScanner._toScanMap[scanX + ':' + scanY] !== undefined || BaseScanner._bases[scanX + ':' + scanY] !== undefined) { continue; } var object = world.GetObjectFromPosition(scanX, scanY); // Nothing to scan if (object === null) { continue; } // Object isnt a NPC Base/Camp/Outpost if (object.Type !== ClientLib.Data.WorldSector.ObjectType.NPCBase && object.Type !== ClientLib.Data.WorldSector.ObjectType.NPCCamp) { continue; } if (typeof object.getCampType === 'function' && object.getCampType() === ClientLib.Data.Reports.ENPCCampType.Destroyed) { continue; } // Cached var offlineBase = BaseScanner.getOfflineBase(scanX, scanY); if (offlineBase !== null && offlineBase.id === object.getID()) { delete offlineBase.obj; BaseScanner._bases[scanX + ':' + scanY] = offlineBase; var data = { 'base': offlineBase, 'world': ClientLib.Data.MainData.GetInstance().get_Server().get_WorldId(), 'player': ClientLib.Data.MainData.GetInstance().get_Player().get_Name(), 'alliance': ClientLib.Data.MainData.GetInstance().get_Alliance().get_Id() }; BaseScanner._count ++; ST.util.api('scanBase', data, BaseScanner.done); continue; } var scanBase = { x: scanX, y: scanY, level: object.getLevel(), id: object.getID(), distance: distance, selectedBaseID: base.get_Id(), alliance: ClientLib.Data.MainData.GetInstance().get_Alliance().get_Id(), failCount: 0 }; BaseScanner._toScan.push(scanBase); BaseScanner._toScanMap[scanX + ':' + scanY] = scanBase; toScanCount++; } } ST.log.info('[BaseScanner] Found ' + toScanCount + ' new bases to scan from:' + base.get_Name()); }, abort: function() { BaseScanner._scanning = false; BaseScanner._abort = true; }, done: function() { BaseScanner._done ++; if (BaseScanner._scanning === false && BaseScanner._count === BaseScanner._done) { ST.util.button.setLabel('Done! (' + BaseScanner._count + ')'); setTimeout(function(){ ST.util.button.setLabel('Scan'); }, 2000); } else if (BaseScanner._scanning === false) { ST.util.button.setLabel('Scan'); } else { ST.util.button.setLabel('Scanning... (' + BaseScanner._count + '/' + BaseScanner._toScan.length + ')'); } }, getBaseLayout: function(base) { if (BaseScanner._abort) { BaseScanner._abort = false; BaseScanner._scanning = false; return ST.log.info('[BaseScanner] aborting'); } if (base === undefined) { BaseScanner._abort = false; BaseScanner._scanning = false; BaseScanner.done(); return; } if (BaseScanner._lastBaseID !== base.selectedBaseID) { BaseScanner.setCurrentBase(base.selectedBaseID); } // var currentCity = ClientLib.Data.MainData.GetInstance().get_Cities().get_CurrentOwnCity(); // var world = ClientLib.Data.MainData.GetInstance().get_World(); ClientLib.Data.MainData.GetInstance().get_Cities().set_CurrentCityId(base.id); var scanBase = ClientLib.Data.MainData.GetInstance().get_Cities().GetCity(base.id); var comm = ClientLib.Net.CommunicationManager.GetInstance(); comm.UserAction(); // base was destroyed. if (scanBase.get_IsGhostMode()) { return BaseScanner.scanNextBase(); } // Base hasnt loaded yet. if (scanBase.GetBuildingsConditionInPercent() === 0) { base.failCount++; if (base.failCount === MAX_FAILS) { return BaseScanner.scanNextBase(); } return setTimeout(function() { BaseScanner.getBaseLayout(base); }, 100); } var baseName = scanBase.get_Name(); if (baseName !== 'Camp' && baseName !== 'Outpost' && baseName !== 'Base') { return BaseScanner.scanNextBase(); } base.layout = BaseScanner.getLayout(scanBase); base.name = baseName; BaseScanner._bases[base.x + ':' + base.y] = base; // cache the base in local storage ST.storage.set('base-' + base.x + ':' + base.y, base); var data = { 'base': base, 'world': ClientLib.Data.MainData.GetInstance().get_Server().get_WorldId(), 'player': ClientLib.Data.MainData.GetInstance().get_Player().get_Name() }; BaseScanner._count ++; ST.util.api('scanBase', data, function() { BaseScanner.printScanResults(base); BaseScanner.done(); }); BaseScanner.scanNextBase(); }, scanNextBase: function() { if (BaseScanner.index === undefined) { BaseScanner.index = 0; } else { BaseScanner.index++; } var base = BaseScanner._toScan[BaseScanner.index]; BaseScanner.getBaseLayout(base); }, isScanning: function() { return BaseScanner._scanning === true; }, printScanResults: function(base) { ST.util.button.setLabel((' ' + BaseScanner.index).slice(-3) + '/' + BaseScanner._toScan.length); console.log('[' + (' ' + BaseScanner.index).slice(-3) + '/' + BaseScanner._toScan.length + ']\t' + base.x + ':' + base.y + ' ' + base.layout + ' (' + base.failCount + ')'); }, getLayout: function(base) { var layout = []; for (var y = 0; y < 16; y++) { for (var x = 0; x < 9; x++) { var resourceType = base.GetResourceType(x, y); switch (resourceType) { case 0: // Nothing layout.push('.'); break; case 1: // Crystal layout.push('c'); break; case 2: // Tiberium layout.push('t'); break; case 4: // Woods layout.push('j'); break; case 5: // Scrub layout.push('h'); break; case 6: // Oil layout.push('l'); break; case 7: // Swamp layout.push('k'); break; } } } return layout.join(''); }, setCurrentBase: function(baseID) { var allCities = ClientLib.Data.MainData.GetInstance().get_Cities().get_AllCities().d; var selectedBase = allCities[baseID]; ClientLib.Vis.VisMain.GetInstance().CenterGridPosition(selectedBase.get_PosX(), selectedBase.get_PosY()); ClientLib.Vis.VisMain.GetInstance().Update(); ClientLib.Vis.VisMain.GetInstance().ViewUpdate(); BaseScanner._lastBaseID = baseID; }, getOfflineBase: function(x, y) { return ST.storage.get('base-' + x + ':' + y); }, startup: function() { PatchClientLib.patch(); // Listen for base changes phe.cnc.Util.attachNetEvent(ClientLib.Vis.VisMain.GetInstance(), 'SelectionChange', ClientLib.Vis.SelectionChange, BaseScanner, BaseScanner.onSelectionChange); }, destroy: function() { phe.cnc.Util.detachNetEvent(ClientLib.Vis.VisMain.GetInstance(), 'SelectionChange', ClientLib.Vis.SelectionChange, BaseScanner, BaseScanner.onSelectionChange); }, onSelectionChange: function() { BaseScanner.failCount = 0; try { // Maybe we caused the change in selection if (BaseScanner.isScanning()) { return; } if (BaseScanner.selectionChange !== undefined) { clearTimeout(BaseScanner.selectionChange); BaseScanner.selectionChange = undefined; } var currentObj = ClientLib.Vis.VisMain.GetInstance().get_SelectedObject(); if (currentObj === null) { return; } var currentType = currentObj.get_VisObjectType(); if (currentType === ClientLib.Vis.VisObject.EObjectType.RegionNPCCamp || currentType === ClientLib.Vis.VisObject.EObjectType.RegionNPCBase) { BaseScanner.scanCurrentBase(); } } catch (e) { ST.log.warn('Error in selection change', e); } }, scanCurrentBase: function() { var data; var cities = ClientLib.Data.MainData.GetInstance().get_Cities(); var base = cities.get_CurrentCity(); BaseScanner.failCount++; if (BaseScanner.failCount > MAX_FAILS) { return; } if (base === null) { BaseScanner.selectionChange = setTimeout(BaseScanner.scanCurrentBase, 100); return; } var obj = { x: base.get_PosX(), y: base.get_PosY(), id: base.get_Id() }; // already scanned var offlineBase = BaseScanner.getOfflineBase(obj.x, obj.y); if (offlineBase !== null && offlineBase.id === obj.id) { delete offlineBase.obj; BaseScanner._bases[obj.x + ':' + obj.y] = offlineBase; BaseScanner.failCount = 0; data = { 'base': offlineBase, 'world': ClientLib.Data.MainData.GetInstance().get_Server().get_WorldId(), 'player': ClientLib.Data.MainData.GetInstance().get_Player().get_Name(), 'alliance': ClientLib.Data.MainData.GetInstance().get_Alliance().get_Id() }; BaseScanner._count ++; ST.util.api('scanBase', data, BaseScanner.done); return; } if (base.get_IsGhostMode()) { BaseScanner.failCount = 0; return; } if (base.GetBuildingsConditionInPercent() === 0) { BaseScanner.selectionChange = setTimeout(BaseScanner.scanCurrentBase, 100); return; } BaseScanner.failCount = 0; var baseName = base.get_Name(); if (baseName !== 'Camp' && baseName !== 'Outpost' && baseName !== 'Base') { return; } obj.layout = BaseScanner.getLayout(base); obj.name = baseName; obj.alliance = ClientLib.Data.MainData.GetInstance().get_Alliance().get_Id(); BaseScanner._bases[obj.x + ':' + obj.y] = obj; BaseScanner._selectionBases[obj.x + ':' + obj.y] = obj; // cache the base in local storage ST.storage.set('base-' + obj.x + ':' + obj.y, JSON.stringify(obj)); ST.log.info('[BaseScanner:AutoScan] ' + obj.x + ':' + obj.y + ' ' + obj.layout); data = { 'base': obj, 'world': ClientLib.Data.MainData.GetInstance().get_Server().get_WorldId(), 'player': ClientLib.Data.MainData.GetInstance().get_Player().get_Name(), 'alliance': ClientLib.Data.MainData.GetInstance().get_Alliance().get_Id() }; ST.util.api('scanBase', data, function() { console.log('SAVED BASE', data, arguments); }); } }; var PatchClientLib = { _g: function(k, r, q, m) { var p = []; var o = k.toString(); var n = o.replace(/\s/gim, ''); p = n.match(r); var l; for (l = 1; l < (m + 1); l++) { if (p !== null && p[l].length === 6) { console.debug(q, l, p[l]); } else { if (p !== null && p[l].length > 0) { console.warn(q, l, p[l]); } else { console.error('Error - ', q, l, 'not found'); console.warn(q, n); } } } return p; }, patch: function() { if (BaseScanner._patched) { return; } var t = ClientLib.Data.WorldSector.WorldObjectCity.prototype; var re = /this\.(.{6})=\(?\(?g>>8\)?\&.*d\+=f;this\.(.{6})=\(/; var y = PatchClientLib._g(t.$ctor, re, ClientLib.Data.WorldSector.WorldObjectCity, 2); if (y !== null && y[1].length === 6) { t.getLevel = function() { return this[y[1]]; }; } else { console.error('Error - ClientLib.Data.WorldSector.WorldObjectCity.Level undefined'); } if (y !== null && y[2].length === 6) { t.getID = function() { return this[y[2]]; }; } else { console.error('Error - ClientLib.Data.WorldSector.WorldObjectCity.ID undefined'); } t = ClientLib.Data.WorldSector.WorldObjectNPCBase.prototype; re = /100\){0,1};this\.(.{6})=Math.floor.*d\+=f;this\.(.{6})=\(/; var x = PatchClientLib._g(t.$ctor, re, 'ClientLib.Data.WorldSector.WorldObjectNPCBase', 2); if (x !== null && x[1].length === 6) { t.getLevel = function() { return this[x[1]]; }; } else { console.error('Error - ClientLib.Data.WorldSector.WorldObjectNPCBase.Level undefined'); } if (x !== null && x[2].length === 6) { t.getID = function() { return this[x[2]]; }; } else { console.error('Error - ClientLib.Data.WorldSector.WorldObjectNPCBase.ID undefined'); } t = ClientLib.Data.WorldSector.WorldObjectNPCCamp.prototype; re = /100\){0,1};this\.(.{6})=Math.floor.*this\.(.{6})=\(*g\>\>(22|0x16)\)*\&.*=-1;\}this\.(.{6})=\(/; var w = PatchClientLib._g(t.$ctor, re, 'ClientLib.Data.WorldSector.WorldObjectNPCCamp', 4); if (w !== null && w[1].length === 6) { t.getLevel = function() { return this[w[1]]; }; } else { console.error('Error - ClientLib.Data.WorldSector.WorldObjectNPCCamp.Level undefined'); } if (w !== null && w[2].length === 6) { t.getCampType = function() { return this[w[2]]; }; } else { console.error('Error - ClientLib.Data.WorldSector.WorldObjectNPCCamp.CampType undefined'); } if (w !== null && w[4].length === 6) { t.getID = function() { return this[w[4]]; }; } else { console.error('Error - ClientLib.Data.WorldSector.WorldObjectNPCCamp.ID undefined'); } BaseScanner._patched = true; } }; ST.register(BaseScanner);/* End: client/modules/basescanner.js */ /* Begin: client/modules/playerinfo.js */ /* globals ClientLib, GAMEDATA, ST */ var PlayerInfo = { name: 'PlayerInfo', instance: null, output: {}, versions: {}, getInfo: function() { PlayerInfo.patchClientLib(); console.time('ST:getInfo'); var oldVersions = {}; Object.keys(PlayerInfo.versions).forEach(function(o){ oldVersions[o] = PlayerInfo.versions[o]; }); // ST.log.debug('getInfo'); PlayerInfo.instance = ClientLib.Data.MainData.GetInstance(); PlayerInfo.output.world = PlayerInfo.instance.get_Server().get_WorldId(); PlayerInfo.output.worldname = PlayerInfo.instance.get_Server().get_Name(); PlayerInfo._getPlayerInfo(); PlayerInfo._getNextMVC(); PlayerInfo._getCities(); ST.log.debug(PlayerInfo.output); console.timeEnd('ST:getInfo'); var shouldUpdate = false; Object.keys(PlayerInfo.versions).forEach(function(o){ if (PlayerInfo.versions[o] !== oldVersions[o]) { shouldUpdate = true; } }); if (shouldUpdate) { PlayerInfo.saveInfo(); } }, _getPlayerInfo: function() { // ST.log.debug('\t getPlayerInfo'); var player = PlayerInfo.instance.get_Player(); PlayerInfo.output.id = player.get_Id(); PlayerInfo.output.faction = PlayerInfo.map.faction[player.get_Faction()]; PlayerInfo.output.player = player.get_Name(); PlayerInfo.output.score = player.get_ScorePoints(); PlayerInfo.output.rank = player.get_OverallRank(); var sub = PlayerInfo.instance.get_PlayerSubstitution().getOutgoing(); if (sub) { PlayerInfo.output.sub = sub.n; } var alliance = PlayerInfo.instance.get_Alliance(); PlayerInfo.output.alliance = { id: alliance.get_Id(), name: alliance.get_Name(), bonus: { power: alliance.get_POIPowerBonus(), crystal: alliance.get_POICrystalBonus(), tiberium: alliance.get_POITiberiumBonus(), air: alliance.get_POIAirBonus(), def: alliance.get_POIDefenseBonus(), vec: alliance.get_POIVehicleBonus(), inf: alliance.get_POIInfantryBonus() }, players: [] }; PlayerInfo.output.rp = player.get_ResearchPoints(); PlayerInfo.output.credit = player.get_Credits().Base; PlayerInfo.output.command = { max: player.GetCommandPointMaxStorage(), current: player.GetCommandPointCount() }; }, _getNextMVC: function() { // ST.log.debug('\t getNextMVC'); var player = PlayerInfo.instance.get_Player(); var TechId = ClientLib.Base.Tech.GetTechIdFromTechNameAndFaction( ClientLib.Base.ETechName.Research_BaseFound, player.get_Faction()); var PlayerResearch = player.get_PlayerResearch(); var ResearchItem = PlayerResearch.GetResearchItemFomMdbId(TechId); if (ResearchItem === null) { return; } var NextLevelInfo = ResearchItem.get_NextLevelInfo_Obj(); var resourcesNeeded = []; for (var i in NextLevelInfo.rr) { if (NextLevelInfo.rr[i].t > 0) { resourcesNeeded[NextLevelInfo.rr[i].t] = NextLevelInfo.rr[i].c; } } var creditsNeeded = resourcesNeeded[ClientLib.Base.EResourceType.Gold]; var creditsResourceData = player.get_Credits(); var creditsGrowthPerHour = (creditsResourceData.Delta + creditsResourceData.ExtraBonusDelta) * ClientLib.Data.MainData.GetInstance().get_Time().get_StepsPerHour(); var creditsTimeLeftInHours = (creditsNeeded - player.GetCreditsCount()) / creditsGrowthPerHour; var mcvTime = creditsTimeLeftInHours * 60 * 60; if (mcvTime !== Infinity && !isNaN(mcvTime)) { PlayerInfo.output.mcvtime = mcvTime; } }, _getCities: function() { // ST.log.debug('\t getCities'); PlayerInfo.output.bases = []; var allCities = ClientLib.Data.MainData.GetInstance().get_Cities().get_AllCities().d; for (var selectedBaseID in allCities) { if (!allCities.hasOwnProperty(selectedBaseID)) { continue; } var selectedBase = allCities[selectedBaseID]; if (selectedBase === undefined) { throw new Error('unable to find base: ' + selectedBaseID); } PlayerInfo._getCity(selectedBase); } }, _getCity: function(c) { // ST.log.debug('\t\t getCity - ' + c.get_Name()); var base = {}; PlayerInfo.output.repair = c.GetResourceMaxStorage(ClientLib.Base.EResourceType.RepairChargeInf); base.defense = c.get_LvlDefense(); base.offense = c.get_LvlOffense(); base.power = c.GetResourceGrowPerHour(ClientLib.Base.EResourceType.Power, false, false) + c.GetResourceBonusGrowPerHour(ClientLib.Base.EResourceType.Power); base.tiberium = c.GetResourceGrowPerHour(ClientLib.Base.EResourceType.Tiberium, false, false) + c.GetResourceBonusGrowPerHour(ClientLib.Base.EResourceType.Tiberium); base.crystal = c.GetResourceGrowPerHour(ClientLib.Base.EResourceType.Crystal, false, false) + c.GetResourceBonusGrowPerHour(ClientLib.Base.EResourceType.Crystal); base.credits = ClientLib.Base.Resource.GetResourceGrowPerHour(c.get_CityCreditsProduction(), false) + ClientLib.Base.Resource.GetResourceBonusGrowPerHour(c.get_CityCreditsProduction(), false); base.health = c.GetBuildingsConditionInPercent(); base.current = {}; base.current.power = c.GetResourceCount(ClientLib.Base.EResourceType.Power); base.current.tiberium = c.GetResourceCount(ClientLib.Base.EResourceType.Tiberium); base.current.crystal = c.GetResourceCount(ClientLib.Base.EResourceType.Crystal); base.level = c.get_LvlBase(); base.id = c.get_Id(); base.x = c.get_PosX(); base.y = c.get_PosY(); base.v = c.get_Version(); base.buildings = PlayerInfo._getBuildings(c, base); base.units = PlayerInfo._getUnits(c, base); base.repair = {}; base.repair.infantry = c.get_CityUnitsData().GetRepairTimeFromEUnitGroup(ClientLib.Data.EUnitGroup.Infantry, false); base.repair.vehicle = c.get_CityUnitsData().GetRepairTimeFromEUnitGroup(ClientLib.Data.EUnitGroup.Vehicle, false); base.repair.air = c.get_CityUnitsData().GetRepairTimeFromEUnitGroup(ClientLib.Data.EUnitGroup.Aircraft, false); base.repair.time = c.GetResourceCount(ClientLib.Base.EResourceType.RepairChargeInf); base.name = c.get_Name(); if (typeof base.name === 'string') { base.name.replace(/\./g, ''); } PlayerInfo.output.bases.push(base); PlayerInfo.versions[base.name] = base.v; }, saveInfo: function() { ST.util.api('savePlayer', PlayerInfo.output); }, _getUnits: function(base) { var D = {}; var O = {}; var x, y, o; for (var k in base) { var currentFunc = base[k]; if (typeof currentFunc !== 'object') { continue; } for (var k2 in currentFunc) { var listObj = currentFunc[k2]; if (listObj === null || typeof listObj !== 'object' || listObj.d === undefined) { continue; } var lst = listObj.d; if (typeof lst !== 'object') { continue; } for (var i in lst) { var unit = lst[i]; if (typeof unit !== 'object' || unit.get_UnitGameData_Obj === undefined) { continue; } var name = unit.get_UnitGameData_Obj().n; x = unit.get_CoordX(); y = unit.get_CoordY(); var level = unit.get_CurrentLevel(); var dName = PlayerInfo.map.defense[name]; var oName = PlayerInfo.map.offense[name]; if (dName !== undefined) { D[x + ':' + y] = level + dName; } if (oName !== undefined) { O[x + ':' + y] = level + oName; } } } } var out = []; for (y = 0; y < 8; y++) { for (x = 0; x < 9; x++) { o = D[x + ':' + y]; if (o === undefined) { out.push(PlayerInfo.getResourceType(base.GetResourceType(x, y + 8))); } else { out.push(o); } } } for (y = 0; y < 4; y++) { for (x = 0; x < 9; x++) { o = O[x + ':' + y]; if (o === undefined) { out.push('.'); } else { out.push(o); } } } return out.join(''); }, getResourceType: function(type) { switch (type) { case 0: // Nothing return '.'; case 1: // Crystal return 'c'; case 2: // Tiberium return 't'; case 4: // Woods return 'j'; case 5: // Scrub return 'h'; case 6: // Oil return 'l'; case 7: // Swamp return 'k'; } }, _getBuildings: function(base) { var buildings = base.get_Buildings(); var buildingData = {}; for (var b in buildings.d) { var build = buildings.d[b]; buildingData[build.get_CoordX() + ':' + build.get_CoordY()] = build; } var layout = []; // buildings for (var y = 0; y < 8; y++) { for (var x = 0; x < 9; x++) { var resourceType = base.GetResourceType(x, y); var building = buildingData[x + ':' + y]; var token = '.'; var level = 1; if (building !== undefined) { var info = GAMEDATA.Tech[building.get_MdbBuildingId()]; token = PlayerInfo.map.buildings[info.n]; level = building.get_CurrentLevel(); } if (level > 1) { layout.push(level); } switch (resourceType) { case 0: layout.push(token); break; case 1: if (building === undefined) { layout.push('c'); } else { layout.push('n'); } break; case 2: if (building === undefined) { layout.push('t'); } else { layout.push('h'); } break; } } } return layout.join(''); }, startup: function() { PlayerInfo.getInfo(); PlayerInfo.interval = setInterval(PlayerInfo.getInfo, 120000); }, destroy: function() { if (PlayerInfo.interval === undefined) { return; } clearInterval(PlayerInfo.interval); PlayerInfo.interval = undefined; }, patchClientLib: function() { var patches = [{ proto: ClientLib.Data.WorldSector.WorldObjectCity.prototype, str: ClientLib.Data.WorldSector.WorldObjectCity.prototype.$ctor.toString(), funcs: { getBaseName: /this.(.{6})=c.substr/, getBaseHealth: /\}this.(.{6}).*if \(n/, getOwnerID: /(.{6})=\(\(g>>0x16/ } }, { proto: ClientLib.Data.World.prototype, str: ClientLib.Data.World.prototype.GetSector.toString(), funcs: { getSectors: /\$r=this.(.{6})./ } }, { proto: ClientLib.Data.WorldSector.prototype, str: ClientLib.Data.WorldSector.prototype.GetObject.toString(), funcs: { getBases: /\$r=this.(.{6})./ } }]; function makeReturn(str) { return function() { return this[str]; }; } for (var i = 0; i < patches.length; i++) { var patch = patches[i]; var str = patch.str; var functionNames = Object.keys(patch.funcs); for (var j = 0; j < functionNames.length; j++) { var funcName = functionNames[j]; var reg = patch.funcs[funcName]; if (patch.proto[funcName] !== undefined) { continue; } var matches = str.match(reg); if (!matches) { console.error('Unable to map "' + funcName + '"'); continue; } patch.proto[funcName] = makeReturn(matches[1]); } } }, map: { faction: { 1: 'GDI', 2: 'NOD' }, buildings: { /* GDI Buildings */ 'GDI_Accumulator': 'a', 'GDI_Refinery': 'r', 'GDI_Trade Center': 'u', 'GDI_Silo': 's', 'GDI_Power Plant': 'p', 'GDI_Construction Yard': 'y', 'GDI_Airport': 'd', 'GDI_Barracks': 'b', 'GDI_Factory': 'f', 'GDI_Defense HQ': 'q', 'GDI_Defense Facility': 'w', 'GDI_Command Center': 'e', 'GDI_Support_Art': 'z', 'GDI_Support_Air': 'x', 'GDI_Support_Ion': 'i', /* Nod Buildings */ 'NOD_Refinery': 'r', 'NOD_Power Plant': 'p', 'NOD_Harvester': 'h', 'NOD_Construction Yard': 'y', 'NOD_Airport': 'd', 'NOD_Trade Center': 'u', 'NOD_Defense HQ': 'q', 'NOD_Barracks': 'b', 'NOD_Silo': 's', 'NOD_Factory': 'f', 'NOD_Harvester_Crystal': 'n', 'NOD_Command Post': 'e', 'NOD_Support_Art': 'z', 'NOD_Support_Ion': 'i', 'NOD_Accumulator': 'a', 'NOD_Support_Air': 'x', 'NOD_Defense Facility': 'w', }, defense: { /* GDI Defense Units */ 'GDI_Wall': 'w', 'GDI_Cannon': 'c', 'GDI_Antitank Barrier': 't', 'GDI_Barbwire': 'b', 'GDI_Turret': 'm', 'GDI_Flak': 'f', 'GDI_Art Inf': 'r', 'GDI_Art Air': 'e', 'GDI_Art Tank': 'a', 'GDI_Def_APC Guardian': 'g', 'GDI_Def_Missile Squad': 'q', 'GDI_Def_Pitbull': 'p', 'GDI_Def_Predator': 'd', 'GDI_Def_Sniper': 's', 'GDI_Def_Zone Trooper': 'z', /* Nod Defense Units */ 'NOD_Def_Antitank Barrier': 't', 'NOD_Def_Art Air': 'e', 'NOD_Def_Art Inf': 'r', 'NOD_Def_Art Tank': 'a', 'NOD_Def_Attack Bike': 'p', 'NOD_Def_Barbwire': 'b', 'NOD_Def_Black Hand': 'z', 'NOD_Def_Cannon': 'c', 'NOD_Def_Confessor': 's', 'NOD_Def_Flak': 'f', 'NOD_Def_MG Nest': 'm', 'NOD_Def_Militant Rocket Soldiers': 'q', 'NOD_Def_Reckoner': 'g', 'NOD_Def_Scorpion Tank': 'd', 'NOD_Def_Wall': 'w', }, offense: { /* GDI Offense Units */ 'GDI_APC Guardian': 'g', 'GDI_Commando': 'c', 'GDI_Firehawk': 'f', 'GDI_Juggernaut': 'j', 'GDI_Kodiak': 'k', 'GDI_Mammoth': 'm', 'GDI_Missile Squad': 'q', 'GDI_Orca': 'o', 'GDI_Paladin': 'a', 'GDI_Pitbull': 'p', 'GDI_Predator': 'd', 'GDI_Riflemen': 'r', 'GDI_Sniper Team': 's', 'GDI_Zone Trooper': 'z', /* Nod Offense Units */ 'NOD_Attack Bike': 'b', 'NOD_Avatar': 'a', 'NOD_Black Hand': 'z', 'NOD_Cobra': 'r', 'NOD_Commando': 'c', 'NOD_Confessor': 's', 'NOD_Militant Rocket Soldiers': 'q', 'NOD_Militants': 'm', 'NOD_Reckoner': 'k', 'NOD_Salamander': 'l', 'NOD_Scorpion Tank': 'o', 'NOD_Specter Artilery': 'p', 'NOD_Venom': 'v', 'NOD_Vertigo': 't', '': '' } } }; ST.register(PlayerInfo);/* End: client/modules/playerinfo.js */ /* Begin: client/modules/basescount.js */ /* globals ClientLib, qx, webfrontend, ST */ var BaseCounter = { name: 'BaseCounter', ui: {}, pasteOutput: function(x, y, baseCount, baseData) { var input = qx.core.Init.getApplication().getChat().getChatWidget().getEditable(); var dom = input.getContentElement().getDomElement(); var output = []; output.push(dom.value.substring(0, dom.selectionStart)); output.push('[[coords]' + x + ':' + y + '[/coords]] Found ' + baseCount + ' Bases - ' + baseData); output.push(dom.value.substring(dom.selectionEnd, dom.value.length)); input.setValue(output.join(' ')); }, countBases: function(x, y, paste) { var levelCount = []; var count = 0; var waves = 1; var maxAttack = 10; var world = ClientLib.Data.MainData.GetInstance().get_World(); for (var scanY = y - 10; scanY <= y + 10; scanY++) { for (var scanX = x - 10; scanX <= x + 10; scanX++) { var distX = Math.abs(x - scanX); var distY = Math.abs(y - scanY); var distance = Math.sqrt((distX * distX) + (distY * distY)); // too far away to scan if (distance > maxAttack) { continue; } var object = world.GetObjectFromPosition(scanX, scanY); // Nothing to scan if (object === null) { continue; } // Object isnt a NPC Base/Camp/Outpost if (object.Type !== ClientLib.Data.WorldSector.ObjectType.NPCBase) { continue; } if (typeof object.getCampType === 'function' && object.getCampType() === ClientLib.Data.Reports.ENPCCampType.Destroyed) { continue; } if (typeof object.getLevel !== 'function') { BaseCounter._patchClientLib(); } var level = object.getLevel(); levelCount[level] = (levelCount[level] || 0) + 1; count++; } } if(count > 49) { waves = 5; } else if(count > 39) { waves = 4; } else if(count > 29) { waves = 3; } else if(count > 19) { waves = 2; } var output = []; for (var i = 0; i < levelCount.length; i++) { var lvl = levelCount[i]; if (lvl !== undefined) { output.push(lvl + ' x ' + i); } } // console.log('[' + x + ':' + y + '] Found ' + count + ' bases - ' + output.join(', ')); if (paste === undefined || paste === true) { BaseCounter.pasteOutput(x, y, count, output.join(', '), 'waves:', waves); } return { total: count, levels: levelCount, formatted: output.join(', '), waves: waves }; }, count: function(paste) { if (BaseCounter.selectedBase === null || BaseCounter.selectedBase === undefined) { return; } return BaseCounter.countBases(BaseCounter.selectedBase.get_RawX(), BaseCounter.selectedBase.get_RawY(), paste); }, onRegionShow: function(c) { try { var target = c.getTarget(); var object = target.getLayoutParent().getObject(); var count = BaseCounter.countBases(object.get_RawX(), object.get_RawY(), false); BaseCounter.ui.region.total.setValue(count.total); BaseCounter.ui.region.levels.setValue(count.formatted); BaseCounter.ui.region.waves.setValue(' [ ' + count.waves + ' waves ]'); target.add(BaseCounter.ui.region.container); } catch (e) { console.error(e); } }, onBaseMoveChange: function(x, y) { var coord = ClientLib.Base.MathUtil.EncodeCoordId(x, y); var count = BaseCounter.moveCache[coord]; if (count === undefined) { count = BaseCounter.countBases(x, y, false); BaseCounter.moveCache[coord] = count; } BaseCounter.ui.move.total.setValue(count.total); BaseCounter.ui.move.levels.setValue(count.formatted); BaseCounter.ui.move.waves.setValue(' [ ' + count.waves + ' waves ]'); }, onBaseMoveDeActivate: function() { BaseCounter.moveCache = {}; }, onBaseMoveActivate: function() { BaseCounter.moveCache = {}; }, buildMoveUI: function() { BaseCounter.ui.move = {}; var a = new qx.ui.container.Composite(new qx.ui.layout.HBox(6)); a.add(new qx.ui.basic.Label('# Forgotten bases:').set({ alignY: 'middle' })); BaseCounter.ui.move.total = new qx.ui.basic.Label().set({ alignY: 'middle', font: 'bold', textColor: 'text-region-value' }); a.add(BaseCounter.ui.move.total); BaseCounter.ui.move.waves = new qx.ui.basic.Label().set({ textColor: 'text-region-value' }); a.add(BaseCounter.ui.move.waves); var b = new qx.ui.container.Composite(new qx.ui.layout.HBox(6)); b.add(new qx.ui.basic.Label('Levels:').set({ alignY: 'middle' })); BaseCounter.ui.move.levels = new qx.ui.basic.Label().set({ alignY: 'middle', font: 'bold', textColor: 'text-region-value' }); b.add(BaseCounter.ui.move.levels); BaseCounter.ui.move.container = new qx.ui.container.Composite(new qx.ui.layout.VBox()).set({ textColor: 'text-region-tooltip' }); BaseCounter.ui.move.container.add(a); BaseCounter.ui.move.container.add(b); webfrontend.gui.region.RegionCityMoveInfo.getInstance().addAt(BaseCounter.ui.move.container, 3); }, buildRegionUI: function() { BaseCounter.ui.region = {}; var a = new qx.ui.container.Composite(new qx.ui.layout.HBox(4)); a.add(new qx.ui.basic.Label('# Forgotten bases:')); BaseCounter.ui.region.total = new qx.ui.basic.Label().set({ textColor: 'text-region-value' }); a.add(BaseCounter.ui.region.total); BaseCounter.ui.region.waves = new qx.ui.basic.Label().set({ textColor: 'text-region-value' }); a.add(BaseCounter.ui.region.waves); var b = new qx.ui.container.Composite(new qx.ui.layout.HBox(4)); b.add(new qx.ui.basic.Label('Levels:')); BaseCounter.ui.region.levels = new qx.ui.basic.Label().set({ textColor: 'text-region-value' }); b.add(BaseCounter.ui.region.levels); BaseCounter.ui.region.container = new qx.ui.container.Composite(new qx.ui.layout.VBox()).set({ marginTop: 6, textColor: 'text-region-tooltip' }); BaseCounter.ui.region.container.add(a); BaseCounter.ui.region.container.add(b); }, startup: function() { if (typeof webfrontend.gui.region.RegionCityInfo.prototype.getObject !== 'function') { var a = webfrontend.gui.region.RegionCityInfo.prototype.setObject.toString(); var b = a.match(/^function[^;]+;\W*var\W*([A-Za-z]+)[^\}]+\}\s;.*.+this\.([A-Za-z_]+)=\1;/); if(b) { webfrontend.gui.region.RegionCityInfo.prototype.getObject = function() { return this[b[2]]; }; } } BaseCounter.bindings = [ webfrontend.gui.region.RegionCityStatusInfoOwn, webfrontend.gui.region.RegionCityStatusInfoAlliance, webfrontend.gui.region.RegionCityStatusInfoEnemy, webfrontend.gui.region.RegionNPCBaseStatusInfo, webfrontend.gui.region.RegionNPCCampStatusInfo, webfrontend.gui.region.RegionRuinStatusInfo ]; BaseCounter._listeners = []; for (var i = 0; i < BaseCounter.bindings.length; i++) { var bind = BaseCounter.bindings[i]; var bindID = bind.getInstance().addListener('appear', BaseCounter.onRegionShow); BaseCounter._listeners[i] = bindID; } var mouseTool = ClientLib.Vis.VisMain.GetInstance().GetMouseTool(ClientLib.Vis.MouseTool.EMouseTool.MoveBase); phe.cnc.Util.attachNetEvent(mouseTool, 'OnCellChange', ClientLib.Vis.MouseTool.OnCellChange, BaseCounter, BaseCounter.onBaseMoveChange); phe.cnc.Util.attachNetEvent(mouseTool, 'OnDeactivate', ClientLib.Vis.MouseTool.OnDeactivate, BaseCounter, BaseCounter.onBaseMoveDeActivate); phe.cnc.Util.attachNetEvent(mouseTool, 'OnActivate', ClientLib.Vis.MouseTool.OnActivate, BaseCounter, BaseCounter.onBaseMoveActivate); BaseCounter.buildRegionUI(); BaseCounter.buildMoveUI(); BaseCounter.registerButton(); }, destroy: function() { for (var i = 0; i < BaseCounter.bindings.length; i++) { var bindID = BaseCounter._listeners[i]; if (bindID !== undefined) { BaseCounter.bindings[i].getInstance().removeListenerById(bindID); } } BaseCounter._listeners = []; var mouseTool = ClientLib.Vis.VisMain.GetInstance().GetMouseTool(ClientLib.Vis.MouseTool.EMouseTool.MoveBase); phe.cnc.Util.detachNetEvent(mouseTool, 'OnCellChange', ClientLib.Vis.MouseTool.OnCellChange, BaseCounter, BaseCounter.onBaseMoveChange); phe.cnc.Util.detachNetEvent(mouseTool, 'OnDeactivate', ClientLib.Vis.MouseTool.OnDeactivate, BaseCounter, BaseCounter.onBaseMoveDeActivate); phe.cnc.Util.detachNetEvent(mouseTool, 'OnActivate', ClientLib.Vis.MouseTool.OnActivate, BaseCounter, BaseCounter.onBaseMoveActivate); webfrontend.gui.region.RegionCityMoveInfo.getInstance().removeAt(3); }, onShow: function(c) { console.log(c); }, registerButton: function() { if (!webfrontend.gui.region.RegionCityMenu.prototype.__baseCounterButton_showMenu) { webfrontend.gui.region.RegionCityMenu.prototype.__baseCounterButton_showMenu = webfrontend.gui.region.RegionCityMenu.prototype.showMenu; webfrontend.gui.region.RegionCityMenu.prototype.showMenu = function(selectedVisObject) { BaseCounter.selectedBase = selectedVisObject; if (this.__baseCounterButton_initialized !== true) { this.__baseCounterButton_initialized = true; this.__baseCountButton = new qx.ui.form.Button('Paste BaseCount'); this.__baseCountButton.addListener('execute', function() { BaseCounter.count(); }); } if (BaseCounter.lastBase !== BaseCounter.selectedBase) { var count = BaseCounter.count(false); this.__baseCountButton.setLabel('Bases: ' + count.total + ' [' + count.waves + ']'); BaseCounter.lastBase = BaseCounter.selectedBase; } // console.log(children); this.__baseCounterButton_showMenu(selectedVisObject); switch (selectedVisObject.get_VisObjectType()) { case ClientLib.Vis.VisObject.EObjectType.RegionNPCCamp: case ClientLib.Vis.VisObject.EObjectType.RegionNPCBase: case ClientLib.Vis.VisObject.EObjectType.RegionPointOfInterest: case ClientLib.Vis.VisObject.EObjectType.RegionRuin: case ClientLib.Vis.VisObject.EObjectType.RegionHubControl: case ClientLib.Vis.VisObject.EObjectType.RegionHubServer: case ClientLib.Vis.VisObject.EObjectType.RegionCityType: this.add(this.__baseCountButton); break; default: console.log(selectedVisObject.get_VisObjectType()); } }; } }, _patchClientLib: function() { var proto = ClientLib.Data.WorldSector.WorldObjectNPCBase.prototype; var re = /100\){0,1};this\.(.{6})=Math.floor.*d\+=f;this\.(.{6})=\(/; var x = ST.util._g(proto.$ctor, re, 'ClientLib.Data.WorldSector.WorldObjectNPCBase', 2); if (x !== null && x[1].length === 6) { proto.getLevel = function() { return this[x[1]]; }; } else { console.error('Error - ClientLib.Data.WorldSector.WorldObjectNPCBase.Level undefined'); } } }; ST.register(BaseCounter);/* End: client/modules/basescount.js */ /* Begin: client/modules/supportstats.js */ /* globals ClientLib, GAMEDATA, ST */ var SupportStats = { name: 'SupportStats', _players: {}, _supports: {}, _levels: {}, _alliance: {}, _stats: [], refresh: function() { SupportStats.reset(); SupportStats.getStats(); }, getStats: function() { SupportStats.reset(); var allSupports = ClientLib.Data.MainData.GetInstance().get_AllianceSupportState().get_Bases().d; var allPlayers = ClientLib.Data.MainData.GetInstance().get_Alliance().get_MemberData().d; var AllianceName = ClientLib.Data.MainData.GetInstance().get_Alliance().get_Name(); var keys = Object.keys(allSupports); SupportStats.addStat(AllianceName, null, SupportStats._alliance); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var support = allSupports[key]; var player = allPlayers[support.get_PlayerId()]; if (player === undefined) { continue; } var stats = { x: support.get_X(), y: support.get_Y(), level: support.get_Level(), player: player.Name, type: support.get_Type(), name: GAMEDATA.supportTechs[support.get_Type()].dn }; SupportStats._stats.push(stats); SupportStats.addStat(player.Name, stats, SupportStats._players, player); SupportStats.addStat(stats.type, stats, SupportStats._supports); SupportStats.addStat(stats.level, stats, SupportStats._levels); SupportStats.addStat(AllianceName, stats, SupportStats._alliance); } for (var playerId in allPlayers) { var alliancePlayer = allPlayers[playerId]; SupportStats.addStat(alliancePlayer.Name, null, SupportStats._players, alliancePlayer); SupportStats._alliance[AllianceName].bases += alliancePlayer.Bases; } }, getPlayers: function() { SupportStats.getStats(); var data = []; var players = Object.keys(SupportStats._players); for (var i = 0; i < players.length; i++) { var player = players[i]; var stats = SupportStats._players[player]; data.push(stats); } data.sort(function(a, b) { return b.average - a.average; }); var finalOutput = []; data.forEach(function(o) { var output = [ (o.average || 0).toFixed(2), o.name ]; finalOutput.push(output.join(' \t ')); }); return finalOutput; }, print: function() { SupportStats.getStats(); var AllianceName = ClientLib.Data.MainData.GetInstance().get_Alliance().get_Name(); var output = []; var stats = SupportStats._alliance[AllianceName]; output.push('Alliance Report for "' + AllianceName + '"'); output.push('-----------'); output.push('Bases: ' + stats.bases + '\tSupports: ' + stats.count + ' (' + ((stats.count / stats.bases) * 100).toFixed(2) + '%)'); output.push('Average: ' + (stats.level / stats.count).toFixed(2)); output.push('-----------'); output.push('Biggest Support'); output.push('Player: ' + stats.big_support.player); output.push('Type: ' + stats.big_support.name); output.push('Level: ' + stats.big_support.level + ' @ [coords]' + stats.big_support.x + ':' + stats.big_support.y + '[/coords]'); output.push('-----------'); output.push('Smallest Support'); output.push('Player: ' + stats.small_support.player); output.push('Type: ' + stats.small_support.name); output.push('Level: ' + stats.small_support.level + ' @ [coords]' + stats.small_support.x + ':' + stats.small_support.y + '[/coords]'); output.push('-----------'); output.push('Breakdown'); output.push('-----------'); for (var supportId in GAMEDATA.supportTechs) { var supportName = GAMEDATA.supportTechs[supportId]; var supportStat = SupportStats._supports[supportId]; var count = supportStat === undefined ? 0 : supportStat.count; var avg = supportStat === undefined ? 0 : (supportStat.level / supportStat.count); output.push(SupportStats.pad(supportName.dn, 18) + ' count:' + count + ' avg:' + avg); } output.push('-----------'); output.push('Players'); output.push('-----------'); output.push('Average Player'); output = output.concat(SupportStats.getPlayers()); console.log(output.join('\n')); }, pad: function(str, len) { return str + new Array(len + 1 - str.length).join(' '); }, addStat: function(name, support, obj, player) { var data = obj[name]; if (data === undefined) { data = { name: name, count: 0, level: 0, big: 0, small: -1, support: [] }; if (player !== undefined) { data.bases = player.Bases; } else { data.bases = 0; } obj[name] = data; } if (support === null) { return; } data.count++; data.level += support.level; if (data.bases > 0) { data.average = data.level / data.bases; } if (support.level > data.big) { data.big = support.level; data.big_support = support; } if (support.level < data.small || data.small === -1) { data.small = support.level; data.small_support = support; } data.support.push(support); }, reset: function() { SupportStats._players = {}; SupportStats._supports = {}; SupportStats._levels = {}; SupportStats._stats = []; SupportStats._alliance = {}; } }; ST.register(SupportStats);/* End: client/modules/supportstats.js */ /* Begin: client/modules/killinfo.js */ var KillInfo = { name: 'KillInfo', /* globals $I */ startup: function() { KillInfo.findPrototype(); if (KillInfo.protoName === undefined) { ST.log.warn('ST:KillInfo - Cant find prototype'); return; } var proto = $I[KillInfo.protoName]; if (proto === undefined || proto.prototype[KillInfo.funcName] === undefined) { ST.log.warn('ST:KillInfo - Cant find function'); return; } KillInfo.oldFunc = proto.prototype[KillInfo.funcName]; proto.prototype[KillInfo.funcName] = function(c) { if (typeof c.get_UnitDetails !== 'function') { return KillInfo.oldFunc.call(this, c); } KillInfo.oldFunc.call(this, c); if (ClientLib.Vis.VisMain.GetInstance().get_MouseMode() !== 0) { return; } var unit = c.get_UnitDetails(); // TODO adjust plunder to hp // Does modifying the plunder object have any other effects // var hp = unit.get_HitpointsPercent(); var plunder = unit.get_UnitLevelRepairRequirements(); var data = unit.get_UnitGameData_Obj(); if (this[KillInfo.internalObj] !== null) { this[KillInfo.internalObj][KillInfo.showFunc](data.dn, data.ds, plunder, ''); } }; }, findPrototype: function() { var funcNameMatch = '"tnf:full hp needed to upgrade")'; var funcContentMatch = 'DefenseTerrainFieldType'; var funcName = ''; function searchFunction(proto) { for (var j in proto) { if (j.length !== 6) { continue; } var func = proto[j]; if (typeof func === 'function') { var str = func.toString(); if (str.indexOf(funcNameMatch) !== -1) { console.log(j); return j; } } } return ''; } for (var i in $I) { var obj = $I[i]; if (obj.prototype === undefined) { continue; } if (funcName === '') { funcName = searchFunction(obj.prototype); if (funcName === '') { continue; } } var func = obj.prototype[funcName]; if (func === undefined) { continue; } var str = func.toString(); // not the particular version we are looking for if (str.indexOf(funcContentMatch) === -1) { continue; } KillInfo.protoName = i; KillInfo.funcName = funcName; var matches = str.match(/(.{6}).(.{6})\(d,e,i,f\)/); if (matches !== null && matches.length === 3) { KillInfo.internalObj = matches[1]; KillInfo.showFunc = matches[2]; } } }, destroy: function() { if (KillInfo.oldFunc === undefined) { return; } // reset the function $I[KillInfo.protoName].prototype[KillInfo.funcName] = KillInfo.oldFunc; KillInfo.oldFunc = undefined; } }; ST.register(KillInfo);/* End: client/modules/killinfo.js */ console.timeEnd('ST:LoadModules'); }; ST_MODULES.push(setupShockrModules); function innerHTML(functions) { var output = []; for (var i = 0; i < functions.length; i++) { var func = functions[i]; output.push('try {( ' + func.toString() + ')()' + '} catch(e) { console.log("Error Registering function", e);};'); } return output.join('\n\n'); } if (window.location.pathname !== ('/login/auth')) { var script = document.createElement('script'); script.innerHTML = innerHTML(ST_MODULES); script.type = 'text/javascript'; document.getElementsByTagName('head')[0].appendChild(script); }