NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Tiberium Alliances The Movement // @version 1.0.3 // @namespace https://openuserjs.org/users/petui // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html // @author petui // @description Strategical territory simulator // @include http*://prodgame*.alliances.commandandconquer.com/*/index.aspx* // @updateURL https://openuserjs.org/meta/petui/Tiberium_Alliances_The_Movement.meta.js // ==/UserScript== 'use strict'; (function() { var main = function() { 'use strict'; function createTheMovement() { console.log('TheMovement loaded'); qx.Class.define('TheMovement', { type: 'singleton', extend: Object, members: { entrypoints: [], actions: [], /** * @param {TheMovement.Entrypoint.Interface} entrypoint */ registerEntrypoint: function(entrypoint) { qx.core.Assert.assertInterface(entrypoint, TheMovement.Entrypoint.Interface); this.entrypoints.push(entrypoint); for (var i = 0; i < this.actions.length; i++) { entrypoint.addAction(this.actions[i]); } }, /** * @param {TheMovement.Action.Interface} action */ registerAction: function(action) { qx.core.Assert.assertInterface(action, TheMovement.Action.Interface); this.actions.push(action); for (var i = 0; i < this.entrypoints.length; i++) { this.entrypoints[i].addAction(action); } } } }); qx.Interface.define('TheMovement.Entrypoint.Interface', { members: { /** * @param {TheMovement.Action.Interface} action * @returns {Number} */ addAction: function(action) { this.assertInterface(action, TheMovement.Action.Interface); }, /** * @param {TheMovement.Action.Interface} action * @param {ClientLib.Vis.Region.RegionObject} regionObject */ execute: function(action, regionObject) { this.assertInterface(action, TheMovement.Action.Interface); this.assertInstance(regionObject, ClientLib.Vis.Region.RegionObject); }, /** * @param {TheMovement.Action.Interface} action * @param {ClientLib.Vis.Region.RegionObject} regionObject * @param {Object} undoDetails */ onExecution: function(action, regionObject, undoDetails) { this.assertInterface(action, TheMovement.Action.Interface); this.assertInstance(regionObject, ClientLib.Vis.Region.RegionObject); this.assertInstance(undoDetails, Object); } } }); qx.Class.define('TheMovement.Entrypoint.Abstract', { type: 'abstract', extend: Object, implement: [TheMovement.Entrypoint.Interface], construct: function(history) { this.history = history; this.actions = {}; }, members: { actions: null, history: null, actionCount: 0, /** * @param {TheMovement.Action.Interface} action * @returns {Number} */ addAction: function(action) { this.actions[this.actionCount] = action; return this.actionCount++; }, /** * @param {TheMovement.Action.Interface} action * @param {ClientLib.Vis.Region.RegionObject} regionObject */ execute: function(action, regionObject) { var undoDetails = action.execute(regionObject, this); if (!qx.Class.hasInterface(action.constructor, TheMovement.Action.IndirectExecutionInterface)) { this.onExecution(action, regionObject, undoDetails); } }, /** * @param {TheMovement.Action.Interface} action * @param {ClientLib.Vis.Region.RegionObject} regionObject * @param {Object} undoDetails */ onExecution: function(action, regionObject, undoDetails) { for (var i in this.actions) { if (qx.Class.hasInterface(this.actions[i].constructor, TheMovement.Action.ObserverInterface)) { this.actions[i].onActionExecute(action, regionObject); } } this.history.push(action, undoDetails); } } }); qx.Class.define('TheMovement.Entrypoint.RegionMenu', { extend: TheMovement.Entrypoint.Abstract, construct: function(history) { TheMovement.Entrypoint.Abstract.call(this, history); this.selectedObjectMemberName = webfrontend.gui.region.RegionCityMenu.prototype.onTick.toString() .match(/if\(this\.([A-Za-z0-9_]+)!=null\)this\.[A-Za-z0-9_]+\(\);/)[1]; this.actionButtons = {}; this.blankMenu = new qx.ui.container.Composite(new qx.ui.layout.VBox(0)).set({ padding: 2 }); webfrontend.gui.region.RegionCityMenu.getInstance().addListener('appear', this.__onRegionCityMenuAppear, this); }, members: { actionButtons: null, blankMenu: null, selectedObjectMemberName: null, /** * @param {TheMovement.Action.Interface} action * @returns {Number} */ addAction: function(action) { var id = TheMovement.Entrypoint.Abstract.prototype.addAction.call(this, action); var button; if (qx.Class.hasInterface(action.constructor, TheMovement.Action.TwoStepExecutionInterface)) { button = new qx.ui.form.MenuButton().set({ appearance: 'button', menu: new qx.ui.menu.Menu().set({ position: 'right-top' }) }); if (this.__isMenuButtonBroken()) { button.addListener('pointerdown', button.open, button); } } else { button = new qx.ui.form.Button(); } button.set({ label: this.__formatActionName(action), paddingLeft: -1, paddingRight: -1 }); button.setUserData('actionId', id); button.addListener('execute', this.__onClickActionButton, this); this.actionButtons[id] = button; return id; }, /** * Detects if browser is affected by {@link https://github.com/qooxdoo/qooxdoo/issues/9182} * @returns {Boolean} */ __isMenuButtonBroken: function() { return 'PointerEvent' in window && !qx.bom.client.Event.getMsPointer(); }, __onRegionCityMenuAppear: function() { var menu = webfrontend.gui.region.RegionCityMenu.getInstance(); var regionObject = menu[this.selectedObjectMemberName]; if (!menu.hasChildren()) { menu.add(this.blankMenu); } var subMenu = menu.getChildren()[0]; for (var id in this.actions) { if (this.actions[id].supports(regionObject)) { subMenu.add(this.actionButtons[id]); this.actionButtons[id].setLabel(this.__formatActionName(this.actions[id])); } else if (this.actionButtons[id].getLayoutParent() === subMenu) { subMenu.remove(this.actionButtons[id]); } } }, /** * @param {qx.event.type.Event} event */ __onClickActionButton: function(event) { var id = event.getTarget().getUserData('actionId'); var action = this.actions[id]; var regionObject = webfrontend.gui.region.RegionCityMenu.getInstance()[this.selectedObjectMemberName]; this.execute(action, regionObject); if (qx.Class.hasInterface(action.constructor, TheMovement.Action.TwoStepExecutionInterface)) { var options = action.getTwoStepOptions(); var twoStepMenu = event.getTarget().getMenu(); this.__clearMenu(twoStepMenu); for (var i = 0; i < options.length; i++) { var option = options[i]; var menuButton = new qx.ui.menu.Button(option.label).set({ marginLeft: -12, textColor: option.color }); menuButton.setUserData('actionId', id); menuButton.setUserData('optionData', option.data); menuButton.addListener('execute', this.__onClickTwoStepMenuButton, this); twoStepMenu.add(menuButton); } } else { qx.core.Init.getApplication().getBackgroundArea().closeCityInfo(); } }, /** * @param {qx.event.type.Event} event */ __onClickTwoStepMenuButton: function(event) { var button = event.getTarget(); var id = button.getUserData('actionId'); var optionData = button.getUserData('optionData'); this.actions[id].onTwoStepOptionSelected(optionData, button.getLabel()); }, /** * @param {TheMovement.Action.Interface} action * @returns {String} */ __formatActionName: function(action) { var name = action.getName(); if (qx.Class.hasInterface(action.constructor, TheMovement.Action.TwoStepExecutionInterface)) { name += ' \u00BB'; } return name; }, /** * @param {qx.ui.menu.Menu} menu */ __clearMenu: function(menu) { var children = menu.getChildren(); menu.removeAll(); for (var i = 0; i < children.length; i++) { children[i].dispose(); } } } }); qx.Class.define('TheMovement.History', { extend: Object, construct: function() { this.changes = []; }, members: { changes: null, /** * @param {TheMovement.Action.Interface} action * @param {Object} undoDetails */ push: function(action, undoDetails) { this.changes.push({ action: action, details: undoDetails }); }, /** * @returns {Boolean} */ isEmpty: function() { return !this.changes.length; }, /** * @returns {String} */ getLastActionName: function() { if (!this.changes.length) { throw new Error(this.name + '.prototype.getLastActionName called when history is empty'); } return this.changes[this.changes.length - 1].action.getName(); }, /** * @returns {Boolean} */ undo: function() { if (!this.changes.length) { throw new Error(this.name + '.prototype.undo called when history is empty'); } var entry = this.changes.pop(); entry.action.undo(entry.details); return this.changes.length > 0; }, clear: function() { this.changes = []; } } }); qx.Class.define('TheMovement.WorldManipulator', { extend: Object, construct: function(regionManipulator, worldObjectWrapper, hash) { this.regionManipulator = regionManipulator; this.worldObjectWrapper = worldObjectWrapper; this.hash = hash; this.dirtySectors = {}; var matches = ClientLib.Data.WorldSector.prototype.SetDetails.toString() .match(/case \$I\.[A-Z]{6}\.City:{.+?this\.([A-Z]{6})\.[A-Z]{6}\(\(\(e<<0x10\)\|d\),g\);.+?var h=this\.([A-Z]{6})\.d\[g\.[A-Z]{6}\];if\(h==null\){return false;}var i=\(\(h\.([A-Z]{6})!=0\) \? this\.([A-Z]{6})\.d\[h\.\3\] : null\);/); this.worldSectorObjectsMemberName = matches[1]; this.worldSectorPlayersMemberName = matches[2]; this.playerAllianceDataIndexMemberName = matches[3]; this.worldSectorAlliancesMemberName = matches[4]; this.playerIdMemberName = ClientLib.Vis.Region.RegionCity.prototype.get_PlayerId.toString().match(/return [A-Za-z]+\.([A-Z]{6});/)[1]; this.playerNameMemberName = ClientLib.Vis.Region.RegionCity.prototype.get_PlayerName.toString().match(/return [A-Za-z]+\.([A-Z]{6});/)[1]; this.playerFactionMemberName = ClientLib.Vis.Region.RegionCity.prototype.get_PlayerFaction.toString().match(/return [A-Za-z]+\.([A-Z]{6});/)[1]; this.allianceIdMemberName = ClientLib.Vis.Region.RegionCity.prototype.get_AllianceId.toString().match(/return [A-Za-z]+\.([A-Z]{6});/)[1]; this.allianceNameMemberName = ClientLib.Vis.Region.RegionCity.prototype.get_AllianceName.toString().match(/return [A-Za-z]+\.([A-Z]{6});/)[1]; this.worldSectorVersionMemberName = ClientLib.Data.WorldSector.prototype.get_Version.toString() .match(/return this\.([A-Z]{6});/)[1]; this.updateData$ctorMethodName = ClientLib.Vis.MouseTool.CreateUnitTool.prototype.Activate.toString() .match(/\$I\.[A-Z]{6}\.[A-Z]{6}\(\)\.[A-Z]{6}\(\)\.[A-Z]{6}\(\)\.[A-Z]{6}\(\(new \$I\.[A-Z]{6}\)\.([A-Z]{6})\(this,this\.[A-Z]{6}\)\);/)[1]; }, members: { regionManipulator: null, worldObjectWrapper: null, hash: null, dirtySectors: null, worldSectorObjectsMemberName: null, worldSectorPlayersMemberName: null, playerAllianceDataIndexMemberName: null, worldSectorAlliancesMemberName: null, playerIdMemberName: null, playerNameMemberName: null, playerFactionMemberName: null, allianceIdMemberName: null, allianceNameMemberName: null, worldSectorVersionMemberName: null, updateData$ctorMethodName: null, /** * @param {ClientLib.Data.WorldSector} sector */ markDirty: function(sector) { if (!(sector.get_Id() in this.dirtySectors)) { this.dirtySectors[sector.get_Id()] = { alliance: [], player: [] }; } }, /** * @returns {Boolean} */ isDirty: function() { return Object.keys(this.dirtySectors).length > 0; }, /** * @param {ClientLib.Data.WorldSector} sourceSector * @param {ClientLib.Data.WorldSector} targetSector * @param {ClientLib.Data.WorldSector.WorldObjectCity} worldObject * @returns {Number} */ __getOrCreatePlayerDataIdFromWorldObject: function(sourceSector, targetSector, worldObject) { var playerData = sourceSector.GetPlayer(this.worldObjectWrapper.getPlayerDataIndex(worldObject)); var allianceData = sourceSector.GetAlliance(playerData[this.playerAllianceDataIndexMemberName]); return this.getOrCreatePlayerDataId(targetSector, playerData[this.playerIdMemberName], playerData[this.playerNameMemberName], playerData[this.playerFactionMemberName], allianceData ? allianceData[this.allianceIdMemberName] : 0, allianceData ? allianceData[this.allianceNameMemberName] : '' ); }, /** * @param {ClientLib.Data.WorldSector} targetSector * @param {Number} allianceId * @param {String} allianceName * @param {ClientLib.Base.EFactionType} [playerFaction] * @returns {Number} */ createAnonymousPlayerDataId: function(targetSector, allianceId, allianceName, playerFaction) { playerFaction = playerFaction || ClientLib.Base.EFactionType.NotInitialized; return this.getOrCreatePlayerDataId(targetSector, 0, '\uFEFF', playerFaction, allianceId, allianceName); }, /** * @param {ClientLib.Data.WorldSector} targetSector * @param {Number} playerId * @param {String} playerName * @param {ClientLib.Base.EFactionType} playerFaction * @param {Number} allianceId * @param {String} allianceName * @returns {Number} */ getOrCreatePlayerDataId: function(targetSector, playerId, playerName, playerFaction, allianceId, allianceName) { var sectorPlayers = targetSector[this.worldSectorPlayersMemberName]; var playerDataId = null; if (playerId !== 0) { for (var dataId in sectorPlayers.d) { if (sectorPlayers.d[dataId][this.playerIdMemberName] === playerId) { playerDataId = dataId; break; } } } if (playerDataId === null) { var sectorChanges = this.dirtySectors[targetSector.get_Id()] || { alliance: [], player: [] }; var sectorAlliances = targetSector[this.worldSectorAlliancesMemberName]; var allianceDataId = null; for (dataId in sectorAlliances.d) { if (sectorAlliances.d[dataId][this.allianceIdMemberName] === allianceId) { allianceDataId = dataId; break; } } if (allianceDataId === null) { var allianceData = (new ClientLib.Data.WorldSector.Alliance).$ctor( this.hash.encodeNumber(allianceId) + this.hash.encodeNumber(0) // unused + allianceName, 0 ); var index = 1024; while (sectorAlliances.d[--index]); sectorAlliances.d[index] = allianceData; sectorAlliances.c++; allianceDataId = index; sectorChanges.alliance.push(index); } var factionAndAllianceMask = ((playerFaction % 4) << 1) | (allianceDataId << 3); var playerData = (new ClientLib.Data.WorldSector.Player).$ctor( this.hash.encodeNumber(playerId) + this.hash.encodeNumber(0) // unused + this.hash.encodeNumber(factionAndAllianceMask, 2) + playerName, 0 ); index = 1024; while (sectorPlayers.d[--index]); sectorPlayers.d[index] = playerData; sectorPlayers.c++; playerDataId = index; sectorChanges.player.push(index); this.dirtySectors[targetSector.get_Id()] = sectorChanges; } return playerDataId; }, /** * @param {Object} object * @returns {Object} */ __clone: function(object) { var clone = new object.constructor(); for (var key in object) { if (object.hasOwnProperty(key)) { clone[key] = object[key]; } } return clone; }, /** * @param {Number} x * @param {Number} y * @returns {Number} */ __encodeSectorCoords: function(x, y) { return ((y % 0x20) << 0x10) | (x % 0x20); }, /** * @param {Number} sourceX * @param {Number} sourceY * @param {Number} destinationX * @param {Number} destinationY */ relocate: function(sourceX, sourceY, destinationX, destinationY) { var world = ClientLib.Data.MainData.GetInstance().get_World(); var sourceSector = world.GetWorldSectorByCoords(sourceX, sourceY); var destinationSector = world.GetWorldSectorByCoords(destinationX, destinationY); var encodedSourceSectorCoords = this.__encodeSectorCoords(sourceX, sourceY); var worldObject = sourceSector[this.worldSectorObjectsMemberName].d[encodedSourceSectorCoords]; if (sourceSector !== destinationSector) { var playerDataId = this.__getOrCreatePlayerDataIdFromWorldObject(sourceSector, destinationSector, worldObject); this.worldObjectWrapper.setPlayerDataIndex(worldObject, playerDataId); } this.insertWorldObject(worldObject, destinationX, destinationY); this.removeWorldObject(sourceX, sourceY); this.markDirty(sourceSector); this.markDirty(destinationSector); }, /** * @param {ClientLib.Data.WorldSector.WorldObject} worldObject * @param {Number} x * @param {Number} y */ insertWorldObject: function(worldObject, x, y) { var sector = ClientLib.Data.MainData.GetInstance().get_World().GetWorldSectorByCoords(x, y); var encodedSectorCoords = this.__encodeSectorCoords(x, y); sector[this.worldSectorObjectsMemberName].d[encodedSectorCoords] = worldObject; }, /** * @param {Number} x * @param {Number} y */ removeWorldObject: function(x, y) { var sector = ClientLib.Data.MainData.GetInstance().get_World().GetWorldSectorByCoords(x, y); var encodedSectorCoords = this.__encodeSectorCoords(x, y); delete sector[this.worldSectorObjectsMemberName].d[encodedSectorCoords]; }, reset: function() { var world = ClientLib.Data.MainData.GetInstance().get_World(); for (var sectorId in this.dirtySectors) { var changes = this.dirtySectors[sectorId]; var sector = world.GetSector(sectorId); if (changes.alliance.length > 0) { var alliances = sector[this.worldSectorAlliancesMemberName]; for (var i = 0; i < changes.alliance.length; i++) { delete alliances.d[changes.alliance[i]]; } } if (changes.player.length > 0) { var players = sector[this.worldSectorPlayersMemberName]; for (var i = 0; i < changes.player.length; i++) { delete players.d[changes.player[i]]; } } // Resetting version causes the whole sector to reload in next Poll request sector[this.worldSectorVersionMemberName] = 0; } this.dirtySectors = {}; ClientLib.Net.CommunicationManager.GetInstance().RegisterDataReceiver('WORLD', (new ClientLib.Net.UpdateData)[this.updateData$ctorMethodName](this, this.__updateWorldDetour)); }, /** * @param {String} type * @param {Object} data */ __updateWorldDetour: function(type, data) { var world = ClientLib.Data.MainData.GetInstance().get_World(); world.Update(type, data); if (type === 'WORLD') { this.regionManipulator.updateVisuals(); ClientLib.Net.CommunicationManager.GetInstance().RegisterDataReceiver('WORLD', (new ClientLib.Net.UpdateData)[this.updateData$ctorMethodName](world, world.Update)); } } } }); qx.Class.define('TheMovement.RegionManipulator', { extend: Object, construct: function(worldObjectWrapper) { this.worldObjectWrapper = worldObjectWrapper; this.worldSetTerritoryOwnershipMethodName = ClientLib.Data.EndGame.HubCenter.prototype.$ctor.toString() .match(/h\.([A-Z]{6})\(i,j,\$I\.[A-Z]{6}\.NPC,0,0,100,true\);/)[1]; this.regionUpdateMethodName = ClientLib.Vis.Region.Region.prototype.SetPosition.toString() .match(/this\.([A-Z]{6})\(\);/)[1]; var updateSectorsMethodName = ClientLib.Vis.Region.Region.prototype.SetActive.toString() .match(/this\.([A-Z]{6})\(\);/)[1]; var matches = ClientLib.Vis.Region.Region.prototype[updateSectorsMethodName].toString() .match(/if \(\(([a-z])\.\$r=this\.([A-Z]{6})\.([A-Z]{6})\([a-z],\1\),([a-z])=\1\.b,\1\.\$r\)\)\{.+\4=\(new \$I\.([A-Z]{6})\)\.([A-Z]{6})\(this, \(([a-z])\.[A-Z]{6}\(\)\*0x20\), \(\7\.[A-Z]{6}\(\)\*0x20\)\);/); this.regionSectorsMemberName = matches[2]; this.regionSectorsTryGetValueMethodName = matches[3]; var regionSectorClassName = matches[5]; var regionSector$ctorMethodName = matches[6]; this.regionSectorObjectsMemberName = $I[regionSectorClassName].prototype[regionSector$ctorMethodName].toString() .match(/this\.([A-Z]{6})=\$I\.[A-Z]{6}\.[A-Z]{6}\(\$I\.[A-Z]{6},0x20, 0x20\);/)[1]; }, members: { worldObjectWrapper: null, worldSetTerritoryOwnershipMethodName: null, regionUpdateMethodName: null, regionSectorsMemberName: null, regionSectorsTryGetValueMethodName: null, regionSectorObjectsMemberName: null, /** * @param {Number} x * @param {Number} y */ removeInfluence: function(x, y) { this.setInfluence(x, y, ClientLib.Data.EOwnerType.Player, 0, 0, 0, false); }, /** * @param {ClientLib.Data.WorldSector.WorldObject} worldObject * @param {Number} x * @param {Number} y * @param {Number} [allianceId] * @param {Number} [playerId] */ insertObjectInfluence: function(worldObject, x, y, allianceId, playerId) { var ownerType, ownerId; switch (worldObject.Type) { case ClientLib.Data.WorldSector.ObjectType.City: case ClientLib.Data.WorldSector.ObjectType.Ruin: var hasAlliance = allianceId !== 0 || playerId === 0; ownerType = hasAlliance ? ClientLib.Data.EOwnerType.Alliance : ClientLib.Data.EOwnerType.Player; ownerId = hasAlliance ? allianceId : playerId; break; case ClientLib.Data.WorldSector.ObjectType.NPCBase: ownerType = ClientLib.Data.EOwnerType.NPC; ownerId = 0; break; default: throw new Error(this.name + '.prototype.insertObjectInfluence called with unsupported worldObject'); } this.setInfluence(x, y, ownerType, ownerId, this.worldObjectWrapper.getTerritoryRadius(worldObject), this.worldObjectWrapper.getBaseLevel(worldObject), true); }, /** * @param {Number} x * @param {Number} y * @param {Number} allianceId * @param {Number} playerId * @param {Number} territoryRadius * @param {Number} baseLevel */ insertPlayerInfluence: function(x, y, allianceId, playerId, territoryRadius, baseLevel) { var hasAlliance = allianceId !== 0 || playerId === 0; var ownerId = hasAlliance ? allianceId : playerId; this.setInfluence(x, y, hasAlliance ? ClientLib.Data.EOwnerType.Alliance : ClientLib.Data.EOwnerType.Player, ownerId, territoryRadius, baseLevel, true); }, /** * @param {Number} x * @param {Number} y * @param {ClientLib.Data.EOwnerType} ownerType * @param {Number} ownerId * @param {Number} territoryRadius * @param {Number} baseLevel * @param {Boolean} isBlocked * @returns {Boolean} True if territory changed, false if it remained as is */ setInfluence: function(x, y, ownerType, ownerId, territoryRadius, baseLevel, isBlocked) { var world = ClientLib.Data.MainData.GetInstance().get_World(); return world[this.worldSetTerritoryOwnershipMethodName](x, y, ownerType, ownerId, territoryRadius, baseLevel, isBlocked); }, updateVisuals: function() { ClientLib.Vis.VisMain.GetInstance().get_Region()[this.regionUpdateMethodName](); }, /** * @param {Number} territoryRadius * @param {Number} baseLevel * @param {Number} allianceId * @param {Number} playerId * @param {Number} sourceX * @param {Number} sourceY * @param {Number} destinationX * @param {Number} destinationY */ __relocateTerritory: function(territoryRadius, baseLevel, allianceId, playerId, sourceX, sourceY, destinationX, destinationY) { this.removeInfluence(sourceX, sourceY); this.insertPlayerInfluence(destinationX, destinationY, allianceId, playerId, territoryRadius, baseLevel); this.updateVisuals(); }, /** * @param {ClientLib.Data.WorldSector.WorldObjectCity} worldObjectCity * @param {Number} allianceId * @param {Number} playerId * @param {Number} sourceX * @param {Number} sourceY * @param {Number} destinationX * @param {Number} destinationY */ relocateWorldObjectCityTerritory: function(worldObjectCity, allianceId, playerId, sourceX, sourceY, destinationX, destinationY) { var territoryRadius = this.worldObjectWrapper.getTerritoryRadius(worldObjectCity); var baseLevel = this.worldObjectWrapper.getBaseLevel(worldObjectCity); this.__relocateTerritory(territoryRadius, baseLevel, allianceId, playerId, sourceX, sourceY, destinationX, destinationY); }, /** * @param {ClientLib.Vis.Region.RegionCity} regionCity * @param {Number} destinationX * @param {Number} destinationY */ relocateRegionCityTerritory: function(regionCity, destinationX, destinationY) { var worldObject = this.worldObjectWrapper.getWorldObject(regionCity); this.relocateWorldObjectCityTerritory(worldObject, regionCity.get_AllianceId(), regionCity.get_PlayerId(), regionCity.get_RawX(), regionCity.get_RawY(), destinationX, destinationY); }, /** * @param {Number} x * @param {Number} y */ removeObject: function(x, y) { var sectorId = ClientLib.Data.MainData.GetInstance().get_World().GetWorldSectorByCoords(x, y).get_Id(); var region = ClientLib.Vis.VisMain.GetInstance().get_Region(); var regionX = x % 0x20; var regionY = y % 0x20; var temp = {}; if (region[this.regionSectorsMemberName][this.regionSectorsTryGetValueMethodName](sectorId, temp)) { var regionSector = temp.b; if (regionSector[this.regionSectorObjectsMemberName][regionX][regionY] !== null) { regionSector[this.regionSectorObjectsMemberName][regionX][regionY].Dispose(); regionSector[this.regionSectorObjectsMemberName][regionX][regionY] = null; } } } } }); qx.Class.define('TheMovement.WorldObjectWrapper', { extend: Object, construct: function() { this.visObjectTypeNameMap = {}; this.visObjectTypeNameMap[ClientLib.Vis.VisObject.EObjectType.RegionCityType] = ClientLib.Vis.Region.RegionCity.prototype.get_ConditionDefense.toString().match(/&&\(this\.([A-Z]{6})\.[A-Z]{6}>=0\)/)[1]; this.visObjectTypeNameMap[ClientLib.Vis.VisObject.EObjectType.RegionNPCBase] = ClientLib.Vis.Region.RegionNPCBase.prototype.get_BaseLevel.toString().match(/return this\.([A-Z]{6})\.[A-Z]{6};/)[1]; this.territoryRadiusMemberNameMap = {}; this.territoryRadiusMemberNameMap[ClientLib.Data.WorldSector.ObjectType.City] = ClientLib.Data.WorldSector.WorldObjectCity.prototype.$ctor.toString().match(/this\.([A-Z]{6})=\(\([a-z]>>0x\d+\)&15\);/)[1]; this.territoryRadiusMemberNameMap[ClientLib.Data.WorldSector.ObjectType.NPCBase] = ClientLib.Data.WorldSector.WorldObjectNPCBase.prototype.$ctor.toString().match(/this\.([A-Z]{6})=\(\([a-z]>>0x12\)&15\);/)[1]; this.territoryRadiusMemberNameMap[ClientLib.Data.WorldSector.ObjectType.Ruin] = ClientLib.Data.WorldSector.WorldObjectRuin.prototype.$ctor.toString().match(/this\.([A-Z]{6})=\(\(g>>9\)&15\);/)[1]; this.baseLevelMemberNameMap = {}; this.baseLevelMemberNameMap[ClientLib.Data.WorldSector.ObjectType.City] = ClientLib.Vis.Region.RegionCity.prototype.get_BaseLevel.toString().match(/return this\.[A-Z]{6}\.([A-Z]{6});/)[1]; this.baseLevelMemberNameMap[ClientLib.Data.WorldSector.ObjectType.NPCBase] = ClientLib.Vis.Region.RegionNPCBase.prototype.get_BaseLevel.toString().match(/return this\.[A-Z]{6}\.([A-Z]{6});/)[1]; this.baseLevelMemberNameMap[ClientLib.Data.WorldSector.ObjectType.Ruin] = ClientLib.Vis.Region.RegionRuin.prototype.get_BaseLevel.toString().match(/return this\.[A-Z]{6}\.([A-Z]{6});/)[1]; this.playerDataIndexMemberNameMap = {}; this.playerDataIndexMemberNameMap[ClientLib.Data.WorldSector.ObjectType.City] = ClientLib.Data.WorldSector.prototype.SetDetails.toString().match(/case \$I\.[A-Z]{6}\.City:{.+?var ([A-Za-z]+)=this\.[A-Z]{6}\.d\[[A-Za-z]+\.([A-Z]{6})\];if\(\1==null\){return false;}/)[2]; this.playerDataIndexMemberNameMap[ClientLib.Data.WorldSector.ObjectType.Ruin] = ClientLib.Data.WorldSector.prototype.SetDetails.toString().match(/case \$I\.[A-Z]{6}\.Ruin:{.+?var ([A-Za-z]+)=this\.[A-Z]{6}\.d\[[A-Za-z]+\.([A-Z]{6})\];if\(\1==null\){return false;}/)[2]; }, members: { visObjectTypeNameMap: null, territoryRadiusMemberNameMap: null, baseLevelMemberNameMap: null, playerDataIndexMemberNameMap: null, /** * @param {ClientLib.Vis.Region.RegionObject} regionObject * @returns {ClientLib.Data.WorldSector.WorldObject} */ getWorldObject: function(regionObject) { var visObjectType = regionObject.get_VisObjectType(); if (visObjectType in this.visObjectTypeNameMap) { return regionObject[this.visObjectTypeNameMap[visObjectType]]; } return ClientLib.Data.MainData.GetInstance().get_World().GetObjectFromPosition(regionObject.get_RawX(), regionObject.get_RawY()); }, /** * @param {ClientLib.Data.WorldSector.WorldObject} worldObject * @returns {Number} */ getTerritoryRadius: function(worldObject) { if (!(worldObject.Type in this.territoryRadiusMemberNameMap)) { throw new Error(this.name + '.prototype.getTerritoryRadius called with unsupported worldObject'); } return worldObject[this.territoryRadiusMemberNameMap[worldObject.Type]]; }, /** * @param {ClientLib.Data.WorldSector.WorldObject} worldObject * @param {Number} territoryRadius */ setTerritoryRadius: function(worldObject, territoryRadius) { if (!(worldObject.Type in this.territoryRadiusMemberNameMap)) { throw new Error(this.name + '.prototype.setTerritoryRadius called with unsupported worldObject'); } worldObject[this.territoryRadiusMemberNameMap[worldObject.Type]] = territoryRadius; }, /** * @param {ClientLib.Data.WorldSector.WorldObject} worldObject * @returns {Number} */ getBaseLevel: function(worldObject) { if (!(worldObject.Type in this.baseLevelMemberNameMap)) { throw new Error(this.name + '.prototype.getBaseLevel called with unsupported worldObject'); } return worldObject[this.baseLevelMemberNameMap[worldObject.Type]]; }, /** * @param {ClientLib.Data.WorldSector.WorldObject} worldObject * @param {Number} baseLevel */ setBaseLevel: function(worldObject, baseLevel) { if (!(worldObject.Type in this.baseLevelMemberNameMap)) { throw new Error(this.name + '.prototype.setBaseLevel called with unsupported worldObject'); } worldObject[this.baseLevelMemberNameMap[worldObject.Type]] = baseLevel; }, /** * @param {ClientLib.Data.WorldSector.WorldObjectCity|ClientLib.Data.WorldSector.WorldObjectRuin} worldObject * @returns {Number} */ getPlayerDataIndex: function(worldObject) { if (!(worldObject.Type in this.playerDataIndexMemberNameMap)) { throw new Error(this.name + '.prototype.getPlayerDataIndex called with unsupported worldObject'); } return worldObject[this.playerDataIndexMemberNameMap[worldObject.Type]]; }, /** * @param {ClientLib.Data.WorldSector.WorldObjectCity|ClientLib.Data.WorldSector.WorldObjectRuin} worldObject * @param {Number} playerDataIndex */ setPlayerDataIndex: function(worldObject, playerDataIndex) { if (!(worldObject.Type in this.playerDataIndexMemberNameMap)) { throw new Error(this.name + '.prototype.setPlayerDataIndex called with unsupported worldObject'); } worldObject[this.playerDataIndexMemberNameMap[worldObject.Type]] = playerDataIndex; } } }); qx.Class.define('TheMovement.TerritoryIdentity', { extend: Object, construct: function() { this.GetTerritoryTypeByCoordinatesMethodName = ClientLib.Data.World.prototype.CheckFoundBase.toString() .match(/switch \(this\.([A-Z]{6})\([a-z],[a-z]\)\)/)[1]; var rewrittenFunctionBody = ClientLib.Data.World.prototype.GetTerritoryTypeByCoordinates.toString().replace( /^(function\s*\()/, '$1territoryIdentity,' ).replace( /var ([a-z])=(\$I\.[A-Z]{6}\.[A-Z]{6}\(\)\.[A-Z]{6}\(\))\.[A-Z]{6}\(\);var ([a-z])=\2\.[A-Z]{6}\(\);/, 'var $1=territoryIdentity.playerId;var $3=territoryIdentity.allianceId;' ); this.GetTerritoryTypeByCoordinatesPatched = eval('(' + rewrittenFunctionBody + ')'); this.CheckMoveBaseMethodName = ClientLib.Vis.MouseTool.MoveBaseTool.prototype.VisUpdate.toString() .match(/var [A-Za-z]+=[A-Za-z]+\.([A-Z]{6})\([A-Za-z]+,[A-Za-z]+,this\.[A-Z]{6}\.[A-Z]{6}\(\),this\.[A-Z]{6}\.[A-Z]{6}\(\),this\.[A-Z]{6}\);/)[1]; // The second replace takes care of landing on a ruin and the third one landing next to a ruin rewrittenFunctionBody = ClientLib.Data.World.prototype[this.CheckMoveBaseMethodName].toString().replace( /^(function\s*\()/, '$1territoryIdentity,' ).replace( /(var ([A-Za-z]+)=([A-Za-z]+)\.[A-Z]{6}\((n\.[A-Z]{6})\);if\(\(\2!=\$I\.[A-Z]{6}\.[A-Z]{6}\(\)\.[A-Z]{6}\(\)\.[A-Z]{6}\(\)\)&&)\(\$I\.[A-Z]{6}\.[A-Z]{6}\(\)\.[A-Z]{6}\(\)\.[A-Z]{6}\(\2\)==null\)(\)\{[A-Za-z]+\|=\$I\.[A-Z]{6}\.FailFieldOccupied;)/, '$1 $3.GetPlayerAllianceId($4) != territoryIdentity.allianceId$5' ).replace( /(var ([A-Za-z]+)=([A-Za-z]+)\.[A-Z]{6}\((w\.[A-Z]{6})\);if\(\(\2!=\$I\.[A-Z]{6}\.[A-Z]{6}\(\)\.[A-Z]{6}\(\)\.[A-Z]{6}\(\)\)&&)\(\$I\.[A-Z]{6}\.[A-Z]{6}\(\)\.[A-Z]{6}\(\)\.[A-Z]{6}\(\2\)==null\)(\)\{[A-Za-z]+\|=\(\$I\.[A-Z]{6}\.FailNeighborRuin)/, '$1 $3.GetPlayerAllianceId($4) != territoryIdentity.allianceId$5' ); this.CheckMoveBasePatched = eval('(' + rewrittenFunctionBody + ')'); }, members: { playerId: null, allianceId: null, active: false, GetTerritoryTypeByCoordinatesMethodName: null, GetTerritoryTypeByCoordinatesPatched: null, CheckMoveBaseMethodName: null, CheckMoveBasePatched: null, /** * @param {Number} playerId * @param {Number} allianceId */ activate: function(playerId, allianceId) { this.playerId = playerId; this.allianceId = allianceId; var world = ClientLib.Data.MainData.GetInstance().get_World(); world[this.GetTerritoryTypeByCoordinatesMethodName] = this.GetTerritoryTypeByCoordinatesPatched.bind(world, this); world[this.CheckMoveBaseMethodName] = this.CheckMoveBasePatched.bind(world, this); this.active = true; }, deactivate: function() { var world = ClientLib.Data.MainData.GetInstance().get_World(); world[this.GetTerritoryTypeByCoordinatesMethodName] = ClientLib.Data.World.prototype[this.GetTerritoryTypeByCoordinatesMethodName]; world[this.CheckMoveBaseMethodName] = ClientLib.Data.World.prototype[this.CheckMoveBaseMethodName]; this.active = false; }, /** * @returns {Boolean} */ isActive: function() { return this.active; } } }); qx.Class.define('TheMovement.Hash', { extend: Object, construct: function() { var matches = ClientLib.Data.AllianceSupportState.prototype.Update.toString() .match(/switch \(\$I\.([A-Z]{6})\.([A-Z]{6})\([a-z]\.c\[[a-z]\]\.charCodeAt\(0\)\)\)\{/); var hashEncoderClassname = matches[1]; var decodeCharCodeMethodName = matches[2]; var hashTableMemberName = $I[hashEncoderClassname][decodeCharCodeMethodName].toString() .match(/return \$I\.[A-Z]{6}\.([A-Z]{6})\[[a-z]\];/)[1]; this.hashTable = $I[hashEncoderClassname][hashTableMemberName]; }, members: { hashTable: null, /** * @param {Number} value * @param {Number} [length] * @returns {String} */ encodeNumber: function(value, length) { length = length || 5; var result = []; for (var i = length - 1; i >= 0; i--) { var exponent = Math.pow(0x5b, i); var addition = Math.floor(value / exponent); value %= exponent; result.push(String.fromCharCode(this.hashTable.indexOf(addition))); } return result.reverse().join(''); }, /** * @param {String} value * @returns {String} */ encodeString: function(value) { return '' + this.encodeNumber(value.length, 1) + value; } } }); qx.Interface.define('TheMovement.Action.Interface', { members: { /** * @returns {String} */ getName: function() {}, /** * @param {ClientLib.Vis.Region.RegionObject} regionObject * @returns {Boolean} */ supports: function(regionObject) { this.assertInstance(regionObject, ClientLib.Vis.Region.RegionObject); }, /** * @param {ClientLib.Vis.Region.RegionObject} regionObject * @param {TheMovement.Entrypoint.Interface} entrypoint * @returns {Object} Undo details; information needed by the action to revert the change later */ execute: function(regionObject, entrypoint) { this.assertInstance(regionObject, ClientLib.Vis.Region.RegionObject); this.assertInterface(entrypoint, TheMovement.Entrypoint.Interface); }, /** * @param {Object} details */ undo: function(details) { this.assertInstance(details, Object); } } }); /** * Implementation may call entrypoint.onExecution() on actual execution to propagate history change */ qx.Interface.define('TheMovement.Action.IndirectExecutionInterface'); /** * For implementations that have a selection of options the user should choose from before executing */ qx.Interface.define('TheMovement.Action.TwoStepExecutionInterface', { members: { /** * @returns {Array} */ getTwoStepOptions: function() {}, /** * @param {*} data * @param {String} label */ onTwoStepOptionSelected: function(data, label) {} } }); qx.Interface.define('TheMovement.Action.ObserverInterface', { members: { /** * @param {TheMovement.Action.Interface} action * @param {ClientLib.Vis.Region.RegionObject} regionObject */ onActionExecute: function(action, regionObject) { this.assertInterface(action, TheMovement.Action.Interface); this.assertInstance(regionObject, ClientLib.Vis.Region.RegionObject); } } }); qx.Class.define('TheMovement.Action.PlanMove', { extend: Object, implement: [TheMovement.Action.Interface, TheMovement.Action.IndirectExecutionInterface], construct: function(worldManipulator, regionManipulator, territoryIdentity) { this.worldManipulator = worldManipulator; this.regionManipulator = regionManipulator; this.territoryIdentity = territoryIdentity; this.moveInfoOnMouseUpMethodName = Function.prototype.toString.call(webfrontend.gui.region.RegionCityMoveInfo.constructor) .match(/attachNetEvent\(this\.[A-Za-z0-9_]+,[A-Za-z]+,ClientLib\.Vis\.MouseTool\.OnMouseUp,this,this\.([A-Za-z0-9_]+)\);/)[1]; }, members: { worldManipulator: null, regionManipulator: null, territoryIdentity: null, originalOwnCityId: null, currentRegionCity: null, entrypoint: null, moveInfoOnMouseUpMethodName: null, /** * @returns {String} */ getName: function() { return 'Plan move base'; }, /** * @param {ClientLib.Vis.Region.RegionObject} regionObject * @returns {Boolean} */ supports: function(regionObject) { return regionObject instanceof ClientLib.Vis.Region.RegionCity; }, /** * @param {ClientLib.Vis.Region.RegionCity} regionCity * @param {TheMovement.Entrypoint.Interface} entrypoint */ execute: function(regionCity, entrypoint) { this.originalOwnCityId = null; this.currentRegionCity = regionCity; this.entrypoint = entrypoint; var cities = ClientLib.Data.MainData.GetInstance().get_Cities(); if (regionCity.get_VisObjectType() === ClientLib.Vis.VisObject.EObjectType.RegionCityType && regionCity.get_Type() !== ClientLib.Vis.Region.RegionCity.ERegionCityType.Own) { var city = cities.GetCity(regionCity.get_Id()); var player = ClientLib.Data.MainData.GetInstance().get_Player(); if (city.get_Version() < 0) { // City data is not available, so fill in the minimum required information based on the regionObject city.SetPosition(regionCity.get_RawX(), regionCity.get_RawY()); city.set_BaseLevel(regionCity.get_BaseLevel()); if (regionCity.get_hasMoveRestriction()) { var restrictions = city.get_MoveRestrictions(); restrictions.d[regionCity.get_MoveRestrictionCoord()] = regionCity.get_MoveRestrictionEndStep(); restrictions.c++; } } if (regionCity.get_AllianceId() !== player.get_AllianceId() || (!player.get_AllianceId() && !regionCity.IsOwnBase())) { this.territoryIdentity.activate(regionCity.get_PlayerId(), regionCity.get_AllianceId()); } this.originalOwnCityId = cities.get_CurrentOwnCityId(); cities.set_CurrentOwnCityId(regionCity.get_Id()); } var mouseTool = ClientLib.Vis.VisMain.GetInstance().GetMouseTool(ClientLib.Vis.MouseTool.EMouseTool.MoveBase); phe.cnc.Util.attachNetEvent(mouseTool, 'OnDeactivate', ClientLib.Vis.MouseTool.OnDeactivate, this, this.__onDeactivateMoveBaseTool); var cityMoveInfo = webfrontend.gui.region.RegionCityMoveInfo.getInstance(); phe.cnc.Util.detachNetEvent(mouseTool, 'OnMouseUp', ClientLib.Vis.MouseTool.OnMouseUp, cityMoveInfo, cityMoveInfo[this.moveInfoOnMouseUpMethodName]); phe.cnc.Util.attachNetEvent(mouseTool, 'OnMouseUp', ClientLib.Vis.MouseTool.OnMouseUp, this, this.__onMouseUp); cityMoveInfo.setCity(regionCity); ClientLib.Vis.VisMain.GetInstance().SetMouseTool(ClientLib.Vis.MouseTool.EMouseTool.MoveBase, cities.get_CurrentOwnCityId()); }, __onDeactivateMoveBaseTool: function() { var mouseTool = ClientLib.Vis.VisMain.GetInstance().GetMouseTool(ClientLib.Vis.MouseTool.EMouseTool.MoveBase); phe.cnc.Util.detachNetEvent(mouseTool, 'OnDeactivate', ClientLib.Vis.MouseTool.OnDeactivate, this, this.__onDeactivateMoveBaseTool); var cityMoveInfo = webfrontend.gui.region.RegionCityMoveInfo.getInstance(); phe.cnc.Util.detachNetEvent(mouseTool, 'OnMouseUp', ClientLib.Vis.MouseTool.OnMouseUp, this, this.__onMouseUp); phe.cnc.Util.attachNetEvent(mouseTool, 'OnMouseUp', ClientLib.Vis.MouseTool.OnMouseUp, cityMoveInfo, cityMoveInfo[this.moveInfoOnMouseUpMethodName]); if (this.originalOwnCityId !== null) { ClientLib.Data.MainData.GetInstance().get_Cities().set_CurrentOwnCityId(this.originalOwnCityId); this.originalOwnCityId = null; } if (this.territoryIdentity.isActive()) { this.territoryIdentity.deactivate(); } }, /** * @param {Number} visX * @param {Number} visY * @param {String} mouseButton */ __onMouseUp: function(visX, visY, mouseButton) { if (mouseButton === 'right') { return; } if (this.currentRegionCity === null) { throw new Error(this.name + '.prototype.onMouseUp called without city being selected'); } else if (this.entrypoint === null) { throw new Error(this.name + '.prototype.onMouseUp called without entrypoint'); } var region = ClientLib.Vis.VisMain.GetInstance().get_Region(); var x = Math.floor(visX / region.get_GridWidth()); var y = Math.floor(visY / region.get_GridHeight()); var moveBaseResult = ClientLib.Vis.VisMain.GetInstance().GetMouseTool(ClientLib.Vis.MouseTool.EMouseTool.MoveBase).GetCheckMoveBaseResult(x, y); if (moveBaseResult === ClientLib.Data.EMoveBaseResult.OK || moveBaseResult === ClientLib.Data.EMoveBaseResult.FailCampIsAttacked) { var undoDetails = { cityId: this.currentRegionCity.get_Id(), allianceId: this.currentRegionCity.get_AllianceId(), playerId: this.currentRegionCity.get_PlayerId(), source: { x: x, y: y }, destination: { x: this.currentRegionCity.get_RawX(), y: this.currentRegionCity.get_RawY() } }; this.__moveRegionCity(this.currentRegionCity, x, y); ClientLib.Vis.VisMain.GetInstance().SetMouseTool(ClientLib.Vis.MouseTool.EMouseTool.SelectRegion, null); this.entrypoint.onExecution(this, this.currentRegionCity, undoDetails); this.entrypoint = this.currentRegionCity = null; } else if (moveBaseResult & ClientLib.Data.EMoveBaseResult.FailOldBasePosition) { ClientLib.Vis.VisMain.GetInstance().SetMouseTool(ClientLib.Vis.MouseTool.EMouseTool.SelectRegion, null); } }, /** * @param {ClientLib.Vis.Region.RegionCity} regionCity * @param {Number} destinationX * @param {Number} destinationY */ __moveRegionCity: function(regionCity, destinationX, destinationY) { this.regionManipulator.relocateRegionCityTerritory(regionCity, destinationX, destinationY); this.worldManipulator.relocate(regionCity.get_RawX(), regionCity.get_RawY(), destinationX, destinationY); var city = ClientLib.Data.MainData.GetInstance().get_Cities().GetCity(regionCity.get_Id()); city.SetPosition(destinationX, destinationY); }, /** * @param {Object} details */ undo: function(details) { var worldObject = ClientLib.Data.MainData.GetInstance().get_World().GetObjectFromPosition(details.source.x, details.source.y); var city = ClientLib.Data.MainData.GetInstance().get_Cities().GetCity(details.cityId); if (worldObject === null || worldObject.Type !== ClientLib.Data.WorldSector.ObjectType.City) { throw new Error(this.name + '.prototype.undo cannot find city at ' + details.source.x + ':' + details.source.y); } this.worldManipulator.relocate(details.source.x, details.source.y, details.destination.x, details.destination.y); this.regionManipulator.relocateWorldObjectCityTerritory(worldObject, details.allianceId, details.playerId, details.source.x, details.source.y, details.destination.x, details.destination.y); if (city !== null) { city.SetPosition(details.destination.x, details.destination.y); } } } }); qx.Class.define('TheMovement.Action.PlanRuin', { extend: Object, implement: [TheMovement.Action.Interface], construct: function(worldManipulator, regionManipulator, worldObjectWrapper, hash) { this.worldManipulator = worldManipulator; this.regionManipulator = regionManipulator; this.worldObjectWrapper = worldObjectWrapper; this.hash = hash; }, members: { worldManipulator: null, regionManipulator: null, worldObjectWrapper: null, hash: null, /** * @returns {String} */ getName: function() { return 'Plan ruin'; }, /** * @param {ClientLib.Vis.Region.RegionObject} regionObject * @returns {Boolean} */ supports: function(regionObject) { return regionObject.get_VisObjectType() === ClientLib.Vis.VisObject.EObjectType.RegionNPCBase || (regionObject.get_VisObjectType() === ClientLib.Vis.VisObject.EObjectType.RegionCityType && regionObject.get_Type() !== ClientLib.Vis.Region.RegionCity.ERegionCityType.Own); }, /** * @param {ClientLib.Vis.Region.RegionObject} regionObject * @param {TheMovement.Entrypoint.Interface} entrypoint * @returns {Object} */ execute: function(regionObject, entrypoint) { var world = ClientLib.Data.MainData.GetInstance().get_World(); var sector = world.GetWorldSectorByCoords(regionObject.get_RawX(), regionObject.get_RawY()); var hash = this._createRuinHash(regionObject); sector.SetDetails(hash, 1); this.worldManipulator.markDirty(sector); this.regionManipulator.updateVisuals(); return { allianceId: regionObject.get_AllianceId(), playerId: regionObject.get_PlayerId(), worldObject: this.worldObjectWrapper.getWorldObject(regionObject), x: regionObject.get_RawX(), y: regionObject.get_RawY() }; }, /** * @param {ClientLib.Vis.Region.RegionCity|ClientLib.Vis.Region.RegionNPCBase} regionObject * @param {Number} [playerDataId] * @param {String} [attackerCityName] * @returns {String} */ _createRuinHash: function(regionObject, playerDataId, attackerCityName) { var encodeNumber = this.hash.encodeNumber.bind(this.hash); var encodeString = this.hash.encodeString.bind(this.hash); if (playerDataId === undefined || attackerCityName === undefined) { var attackerCity = ClientLib.Data.MainData.GetInstance().get_Cities().get_CurrentOwnCity(); var world = ClientLib.Data.MainData.GetInstance().get_World(); var targetSector = world.GetWorldSectorByCoords(regionObject.get_RawX(), regionObject.get_RawY()); this.worldManipulator.markDirty(targetSector); playerDataId = this.worldManipulator.getOrCreatePlayerDataId(targetSector, attackerCity.get_PlayerId(), attackerCity.get_PlayerName(), attackerCity.get_CityFaction(), attackerCity.get_AllianceId(), attackerCity.get_AllianceName()); attackerCityName = attackerCity.get_Name(); } var isPlayerCity = regionObject.get_VisObjectType() === ClientLib.Vis.VisObject.EObjectType.RegionCityType; var worldObject = this.worldObjectWrapper.getWorldObject(regionObject); var mask = isPlayerCity ? 1 : 0; mask |= (regionObject.get_BaseLevel() & 0xff) << 1; mask |= (this.worldObjectWrapper.getTerritoryRadius(worldObject) & 0xf) << 9; mask |= (playerDataId & 0x3ff) << 13; var detailsHash = ''; detailsHash += encodeNumber(ClientLib.Data.MainData.GetInstance().get_Time().GetServerStep()); detailsHash += encodeString(attackerCityName); if (isPlayerCity) { detailsHash += encodeNumber(regionObject.get_PlayerId()); detailsHash += encodeNumber(regionObject.get_AllianceId()); detailsHash += encodeNumber(regionObject.get_PlayerFaction()); detailsHash += encodeString(regionObject.get_PlayerName()); detailsHash += encodeString(regionObject.get_AllianceName()); detailsHash += regionObject.get_Name(); } var locationMask = (regionObject.get_RawX() % 0x20) | ((regionObject.get_RawY() % 0x20) << 5) | (ClientLib.Data.WorldSector.ObjectType.Ruin << 10); var locationHash = 'C' + encodeNumber(locationMask, 2); var maskHash = encodeNumber(mask, 4); return locationHash + maskHash + detailsHash; }, /** * @param {Object} details */ undo: function(details) { // Replace ruin with whatever was at the coordinates this.worldManipulator.insertWorldObject(details.worldObject, details.x, details.y); this.regionManipulator.insertObjectInfluence(details.worldObject, details.x, details.y, details.allianceId, details.playerId); try { // Remove object from region to make visual update immediate. If this fails, region will still be updated a second later this.regionManipulator.removeObject(details.x, details.y); } catch (e) { } this.regionManipulator.updateVisuals(); } } }); qx.Class.define('TheMovement.Action.PlanRuinFor', { extend: TheMovement.Action.PlanRuin, implement: [TheMovement.Action.IndirectExecutionInterface, TheMovement.Action.TwoStepExecutionInterface], statics: { RelationshipColors: {} }, defer: function(statics) { statics.RelationshipColors[ClientLib.Data.EAllianceDiplomacyStatus.None] = '#ff4500'; statics.RelationshipColors[ClientLib.Data.EAllianceDiplomacyStatus.Friend] = '#00cc00'; statics.RelationshipColors[ClientLib.Data.EAllianceDiplomacyStatus.NAP] = '#f5f5dc'; statics.RelationshipColors[ClientLib.Data.EAllianceDiplomacyStatus.Foe] = '#960018'; statics.RelationshipColors[ClientLib.Data.EAllianceDiplomacyStatus.Neutral] = '#ff4500'; }, members: { relationshipColors: null, regionObject: null, entrypoint: null, /** * @returns {String} */ getName: function() { return 'Plan ruin for'; }, /** * @param {ClientLib.Vis.Region.RegionObject} regionObject * @param {TheMovement.Entrypoint.Interface} entrypoint */ execute: function(regionObject, entrypoint) { this.regionObject = regionObject; this.entrypoint = entrypoint; }, /** * @returns {Array} */ getTwoStepOptions: function() { var ownAlliance = ClientLib.Data.MainData.GetInstance().get_Alliance(); var alliances = [{ label: 'No alliance', color: this.constructor.RelationshipColors[ClientLib.Data.EAllianceDiplomacyStatus.None], data: 0 }]; if (ownAlliance.get_Exists() && ownAlliance.get_Relationships() !== null) { alliances = alliances.concat(ownAlliance.get_Relationships() .filter(function(relationship) { return relationship.IsConfirmed; }, this) .map(function(relationship) { return { label: relationship.OtherAllianceName, color: this.constructor.RelationshipColors[relationship.Relationship], data: relationship.OtherAllianceId }; }, this) .sort(function(a, b) { return a.label.localeCompare(b.label); }) ); } return alliances; }, /** * @param {*} id * @param {String} label */ onTwoStepOptionSelected: function(id, label) { var world = ClientLib.Data.MainData.GetInstance().get_World(); var sector = world.GetWorldSectorByCoords(this.regionObject.get_RawX(), this.regionObject.get_RawY()); var playerDataId = this.worldManipulator.createAnonymousPlayerDataId(sector, id, label); var hash = this._createRuinHash(this.regionObject, playerDataId, label); sector.SetDetails(hash, 1); this.worldManipulator.markDirty(sector); this.regionManipulator.updateVisuals(); this.entrypoint.onExecution(this, this.regionObject, { allianceId: this.regionObject.get_AllianceId(), playerId: this.regionObject.get_PlayerId(), worldObject: this.worldObjectWrapper.getWorldObject(this.regionObject), x: this.regionObject.get_RawX(), y: this.regionObject.get_RawY() }); this.entrypoint = this.regionObject = null; } } }); qx.Class.define('TheMovement.Action.PlanLevelUp', { extend: Object, implement: [TheMovement.Action.Interface], construct: function(worldManipulator, regionManipulator, worldObjectWrapper) { this.worldManipulator = worldManipulator; this.regionManipulator = regionManipulator; this.worldObjectWrapper = worldObjectWrapper; }, members: { worldManipulator: null, regionManipulator: null, worldObjectWrapper: null, /** * @returns {String} */ getName: function() { return 'Plan level up'; }, /** * @param {ClientLib.Vis.Region.RegionObject} regionObject * @returns {Boolean} */ supports: function(regionObject) { return regionObject.get_VisObjectType() === ClientLib.Vis.VisObject.EObjectType.RegionCityType && regionObject.get_BaseLevel() < ClientLib.Data.MainData.GetInstance().get_Server().get_PlayerUpgradeCap(); }, /** * @param {ClientLib.Vis.Region.RegionCity} regionCity * @param {TheMovement.Entrypoint.Interface} entrypoint * @returns {Object} */ execute: function(regionCity, entrypoint) { var x = regionCity.get_RawX(); var y = regionCity.get_RawY(); var sector = ClientLib.Data.MainData.GetInstance().get_World().GetWorldSectorByCoords(x, y); this.worldManipulator.markDirty(sector); var worldObjectCity = this.worldObjectWrapper.getWorldObject(regionCity); this.worldObjectWrapper.setBaseLevel(worldObjectCity, regionCity.get_BaseLevel() + 1); this.regionManipulator.insertObjectInfluence(worldObjectCity, x, y, regionCity.get_AllianceId(), regionCity.get_PlayerId()); this.regionManipulator.removeObject(x, y); this.regionManipulator.updateVisuals(); return { allianceId: regionCity.get_AllianceId(), playerId: regionCity.get_PlayerId(), worldObject: worldObjectCity, x: x, y: y }; }, /** * @param {Object} details */ undo: function(details) { var baseLevel = this.worldObjectWrapper.getBaseLevel(details.worldObject) - 1; this.worldObjectWrapper.setBaseLevel(details.worldObject, baseLevel); this.regionManipulator.insertObjectInfluence(details.worldObject, details.x, details.y, details.allianceId, details.playerId); this.regionManipulator.removeObject(details.x, details.y); this.regionManipulator.updateVisuals(); } } }); qx.Class.define('TheMovement.Action.PlanRemove', { extend: Object, implement: [TheMovement.Action.Interface], construct: function(worldManipulator, regionManipulator, worldObjectWrapper) { this.worldManipulator = worldManipulator; this.regionManipulator = regionManipulator; this.worldObjectWrapper = worldObjectWrapper; }, members: { worldManipulator: null, regionManipulator: null, worldObjectWrapper: null, /** * @returns {String} */ getName: function() { return 'Plan remove'; }, /** * @param {ClientLib.Vis.Region.RegionObject} regionObject * @returns {Boolean} */ supports: function(regionObject) { return regionObject.get_VisObjectType() === ClientLib.Vis.VisObject.EObjectType.RegionNPCBase || regionObject.get_VisObjectType() === ClientLib.Vis.VisObject.EObjectType.RegionRuin || (regionObject.get_VisObjectType() === ClientLib.Vis.VisObject.EObjectType.RegionCityType && regionObject.get_Type() !== ClientLib.Vis.Region.RegionCity.ERegionCityType.Own); }, /** * @param {ClientLib.Vis.Region.RegionObject} regionObject * @param {TheMovement.Entrypoint.Interface} entrypoint * @returns {Object} */ execute: function(regionObject, entrypoint) { var x = regionObject.get_RawX(); var y = regionObject.get_RawY(); var sector = ClientLib.Data.MainData.GetInstance().get_World().GetWorldSectorByCoords(x, y); this.worldManipulator.markDirty(sector); var worldObject = this.worldObjectWrapper.getWorldObject(regionObject); this.worldManipulator.removeWorldObject(x, y); this.regionManipulator.removeInfluence(x, y); this.regionManipulator.updateVisuals(); return { allianceId: regionObject.get_AllianceId(), playerId: regionObject.get_PlayerId(), worldObject: worldObject, x: x, y: y }; }, /** * @param {Object} details */ undo: function(details) { this.worldManipulator.insertWorldObject(details.worldObject, details.x, details.y); this.regionManipulator.insertObjectInfluence(details.worldObject, details.x, details.y, details.allianceId, details.playerId); this.regionManipulator.updateVisuals(); } } }); qx.Class.define('TheMovement.Action.Undo', { extend: Object, implement: [TheMovement.Action.Interface, TheMovement.Action.IndirectExecutionInterface], construct: function(regionManipulator, history) { this.regionManipulator = regionManipulator; this.history = history; }, members: { regionManipulator: null, history: null, /** * @returns {String} */ getName: function() { var name = 'Undo'; if (!this.history.isEmpty()) { var actionName = this.history.getLastActionName(); name += ' ' + actionName.substr(0, 1).toLowerCase() + actionName.substr(1); } return name; }, /** * @param {ClientLib.Vis.Region.RegionObject} regionObject * @returns {Boolean} */ supports: function(regionObject) { return !this.history.isEmpty(); }, /** * @param {ClientLib.Vis.Region.RegionObject} regionObject * @param {TheMovement.Entrypoint.Interface} entrypoint */ execute: function(regionObject, entrypoint) { try { this.history.undo(); } finally { this.regionManipulator.updateVisuals(); } }, /** * @param {Object} details */ undo: function(details) { // Class implements IndirectExecutionInterface, but never calls Entrypoint.onExecution() -> nothing to undo } } }); qx.Class.define('TheMovement.Action.Reset', { extend: Object, implement: [TheMovement.Action.Interface, TheMovement.Action.IndirectExecutionInterface], construct: function(worldManipulator, regionManipulator, history) { this.worldManipulator = worldManipulator; this.regionManipulator = regionManipulator; this.history = history; }, members: { worldManipulator: null, regionManipulator: null, history: null, /** * @returns {String} */ getName: function() { return 'Reset plans'; }, /** * @param {ClientLib.Vis.Region.RegionObject} regionObject * @returns {Boolean} */ supports: function(regionObject) { return this.worldManipulator.isDirty(); }, /** * @param {ClientLib.Vis.Region.RegionObject} regionObject * @param {TheMovement.Entrypoint.Interface} entrypoint */ execute: function(regionObject, entrypoint) { try { if (!this.history.isEmpty()) { while (this.history.undo()); } } catch (e) { this.history.clear(); throw e; } finally { this.regionManipulator.updateVisuals(); this.worldManipulator.reset(); } }, /** * @param {Object} details */ undo: function(details) { // Class implements IndirectExecutionInterface, but never calls Entrypoint.onExecution() -> nothing to undo } } }); } function waitForGame() { try { if (typeof qx !== 'undefined' && qx.core.Init.getApplication() && qx.core.Init.getApplication().initDone) { createTheMovement(); var history = new TheMovement.History(); var hash = new TheMovement.Hash(); var worldObjectWrapper = new TheMovement.WorldObjectWrapper(); var regionManipulator = new TheMovement.RegionManipulator(worldObjectWrapper); var worldManipulator = new TheMovement.WorldManipulator(regionManipulator, worldObjectWrapper, hash); var territoryIdentity = new TheMovement.TerritoryIdentity(); var instance = TheMovement.getInstance(); instance.registerEntrypoint(new TheMovement.Entrypoint.RegionMenu(history)); instance.registerAction(new TheMovement.Action.Reset(worldManipulator, regionManipulator, history)); instance.registerAction(new TheMovement.Action.Undo(regionManipulator, history)); instance.registerAction(new TheMovement.Action.PlanMove(worldManipulator, regionManipulator, territoryIdentity)); instance.registerAction(new TheMovement.Action.PlanRuin(worldManipulator, regionManipulator, worldObjectWrapper, hash)); instance.registerAction(new TheMovement.Action.PlanRuinFor(worldManipulator, regionManipulator, worldObjectWrapper, hash)); instance.registerAction(new TheMovement.Action.PlanLevelUp(worldManipulator, regionManipulator, worldObjectWrapper)); instance.registerAction(new TheMovement.Action.PlanRemove(worldManipulator, regionManipulator, worldObjectWrapper)); } else { setTimeout(waitForGame, 1000); } } catch (e) { console.log('TheMovement: ', e.toString()); } } setTimeout(waitForGame, 1000); }; var script = document.createElement('script'); script.innerHTML = '(' + main.toString() + ')();'; script.type = 'text/javascript'; document.getElementsByTagName('head')[0].appendChild(script); })();