Tiberium Alliances Combat Simulator

Allows you to simulate combat before actually attacking.

// ==UserScript==
// @name           Tiberium Alliances Combat Simulator
// @description    Allows you to simulate combat before actually attacking.
// @namespace      https://prodgame*.alliances.commandandconquer.com/*/index.aspx* 
// @include        https://prodgame*.alliances.commandandconquer.com/*/index.aspx*
// @version        1.3.3.0
// @author         WildKatana        
// ==/UserScript==
(function () {
  var TASuite_mainFunction = function () {
      function createTweak() {
        var TASuite = {};
        qx.Class.define("TASuite.main", {
          type: "singleton",
          extend: qx.core.Object,
          members: {
            buttonSimulateCombat: null,
            buttonLayoutSave: null,
            buttonLayoutLoad: null,
            buttonReturnSetup: null,
            buttonUnlockAttack: null,
            buttonTools: null,
            busy: null,
            
            add_ViewModeChange: null,
            add_ArmyChanged: null,
            
            attacker_modules: null,
            defender_modules: null,
            
            lastPercentage: null,
            lastRepairTime: null,
            lastEnemyPercentage: null,
            lastDFPercentage: null,
            lastCYPercentage: null,
            lastInfantryPercentage: null,
            lastVehiclePercentage: null,
            lastAirPercentage: null,
            lastEnemyUnitsPercentage: null,
            lastEnemyBuildingsPercentage: null,
            lastInfantryRepairTime: null,
            lastVehicleRepairTime: null,
            lastAircraftRepairTime: null,
            
            tiberiumSpoils: null,
            crystalSpoils: null,
            creditSpoils: null,
            researchSpoils: null,
            
            units: null,
            units_list: null,
            saved_units: null,
            layoutsList: null,
            layoutsLabelText: null,
            
            battleResultsBox: null,
            statsPage: null,
            
            troopDamageLabel: null,
            enemyTroopStrengthLabel: null,
            enemyBuildingsStrengthLabel: null,
            enemyUnitsStrengthLabel: null,
            airTroopStrengthLabel: null,
            infantryTroopStrengthLabel: null,
            vehicleTroopStrengthLabel: null,
            CYTroopStrengthLabel: null,
            DFTroopStrengthLabel: null,
            simTroopDamageLabel: null,
            simRepairTimeLabel: null,
            simVictoryLabel: null,
            enemySupportLevelLabel: null,
            enemySupportStrengthLabel: null,
            
            initialize: function () {
              this.add_ViewModeChange = (new ClientLib.Vis.ViewModeChange).GEDTYY(this, this.onViewChange);
              this.add_ArmyChanged = (new $I.WMJHOK).GEDTYY(this, this.onUnitMoved);
              this.buttonSimulateCombat = new qx.ui.form.Button("Simulate");
              this.buttonSimulateCombat.set({
                width: 80,
                appearance: "button-text-small",
                toolTipText: "Start Combat Simulation"
              });
              this.buttonSimulateCombat.addListener("click", this.startSimulation, this);

              this.buttonReturnSetup = new qx.ui.form.Button("Setup");
              this.buttonReturnSetup.set({
                width: 80,
                appearance: "button-text-small",
                toolTipText: "Return to Combat Setup"
              });
              this.buttonReturnSetup.addListener("click", this.returnSetup, this);
              
              var replayBar = qx.core.Init.getApplication().getReportReplayOverlay();
              replayBar.add(this.buttonReturnSetup, {
                top: 10,
                left: 0
              });
              
              var armyBar = qx.core.Init.getApplication().getUIItem(ClientLib.Data.Missions.PATH.BAR_ATTACKSETUP);

              this.buttonUnlockAttack = new qx.ui.form.Button("Unlock");
              this.buttonUnlockAttack.set({
                width: 60,
                height: 45,
                appearance: "button-text-small",
                toolTipText: "Unlock"
              });
              this.buttonUnlockAttack.addListener("click", this.unlockAttacks, this);
              this.buttonUnlockAttack.setOpacity(0.5);
              armyBar.add(this.buttonUnlockAttack, {
                top: 103,
                right: 0
              });
              
              this.buttonTools = new qx.ui.form.Button("Tools");
              this.buttonTools.set({
                width: 80,
                appearance: "button-text-small",
                toolTipText: "Open Simulator Tools"
              });
              this.buttonTools.addListener("click", this.toggleTools, this);
              
              busy = false;

              _this = this;
              setTimeout(function () {
                try {
                  // Get the active modules
                  // Doing this the hard and unreliable way for now, until we figure out a better way
                  _this.attacker_modules = {};
                  _this.attacker_modules.l = [];
                  var g = ClientLib.Res.ResMain.GetInstance();
                  
                  // Get the player faction
                  // var gdi_unit_ids = g.GetFactionUnitIds(1);
                  // var nod_unit_ids = g.GetFactionUnitIds(2);
                  // var forgotten_unit_ids = g.GetFactionUnitIds(3);
                  
                  var player_research = ClientLib.Data.MainData.GetInstance().get_Player().get_PlayerResearch();
                  
                  for (var i in g.IYHFVG.units) {
                    var ug = g.GetUnit_Obj(i);
                    var research = player_research.GetResearchItemFomMdbId(ug.tl);
                    
                    var modules = ug.m;
                    for (var j in modules) {
                      var module = modules[j];
                      if (research && module.r.length > 0) {
                        try {
                          // This is an upgradeable ability
                          var required_level = module.r[0].l;
                          var current_level = research.get_CurrentLevel();
                          if (current_level >= required_level) {
                            _this.attacker_modules.l.push(module.i);
                          }
                        }
                        catch (e) {
                          console.log(e);
                        }
                      }
                      else {
                        _this.attacker_modules.l.push(module.i);
                      }
                    }
                  }
                  
                  // Get the defender modules

                  _this.defender_modules = _this.attacker_modules;
                  ClientLib.Vis.VisMain.GetInstance().add_ViewModeChange(_this.add_ViewModeChange);
                  
                  armyBar.add(_this.buttonTools, {
                    top: 20,
                    left: 0
                  });
                  armyBar.add(_this.buttonSimulateCombat, {
                    top: 50,
                    left: 0
                  });
                } catch (e) {
                  console.log(e);
                }
              }, 10000);
              
              // The Battle Simulator box
              this.battleResultsBox = new qx.ui.window.Window("Battle Simulator");
              this.battleResultsBox.setPadding(1);
              this.battleResultsBox.setLayout(new qx.ui.layout.VBox(1));
              this.battleResultsBox.setShowMaximize(false);
              this.battleResultsBox.setShowMinimize(false);
              this.battleResultsBox.moveTo(125, 125);
              this.battleResultsBox.setHeight(300);
              this.battleResultsBox.setWidth(200);
              
              var tabView = new qx.ui.tabview.TabView();
              tabView.setPadding(5);
              this.battleResultsBox.add(tabView);

              ////////////////// Stats ////////////////////
              this.statsPage = new qx.ui.tabview.Page("Stats");
              this.statsPage.setLayout(new qx.ui.layout.VBox(5));
              this.statsPage.setPadding(1);
              tabView.add(this.statsPage);

              // The Enemy Vertical Box
              var eVBox = new qx.ui.container.Composite()
              eVBox.setLayout(new qx.ui.layout.VBox(5));
              eVBox.setThemedFont("bold");
              eVBox.setThemedPadding(2);
              eVBox.setThemedBackgroundColor("#eef");
              this.statsPage.add(eVBox);
              // The Enemy Troop Strength Label
              var eHBox1 = new qx.ui.container.Composite();
              eHBox1.setLayout(new qx.ui.layout.HBox(5));
              eHBox1.add(new qx.ui.basic.Label("Enemy Base: "));
              this.enemyTroopStrengthLabel = new qx.ui.basic.Label("100");
              eHBox1.add(this.enemyTroopStrengthLabel);
              this.enemyTroopStrengthLabel.setTextColor("red");
              eVBox.add(eHBox1);
              // Units
              var eHBox4 = new qx.ui.container.Composite();
              eHBox4.setLayout(new qx.ui.layout.HBox(5));
              eHBox4.add(new qx.ui.basic.Label("Defences: "));
              this.enemyUnitsStrengthLabel = new qx.ui.basic.Label("100");
              eHBox4.add(this.enemyUnitsStrengthLabel);
              this.enemyUnitsStrengthLabel.setTextColor("green");
              eVBox.add(eHBox4);
              // Buildings
              var eHBox5 = new qx.ui.container.Composite();
              eHBox5.setLayout(new qx.ui.layout.HBox(5));
              eHBox5.add(new qx.ui.basic.Label("Buildings: "));
              this.enemyBuildingsStrengthLabel = new qx.ui.basic.Label("100");
              eHBox5.add(this.enemyBuildingsStrengthLabel);
              this.enemyBuildingsStrengthLabel.setTextColor("green");
              eVBox.add(eHBox5);
              // Command Center
              var eHBox2 = new qx.ui.container.Composite();
              eHBox2.setLayout(new qx.ui.layout.HBox(5));
              eHBox2.add(new qx.ui.basic.Label("Construction Yard: "));
              this.CYTroopStrengthLabel = new qx.ui.basic.Label("100");
              eHBox2.add(this.CYTroopStrengthLabel);
              this.CYTroopStrengthLabel.setTextColor("red");
              eVBox.add(eHBox2);
              // Defense Facility
              var eHBox3 = new qx.ui.container.Composite();
              eHBox3.setLayout(new qx.ui.layout.HBox(5));
              eHBox3.add(new qx.ui.basic.Label("Defense Facility: "));
              this.DFTroopStrengthLabel = new qx.ui.basic.Label("100");
              eHBox3.add(this.DFTroopStrengthLabel);
              this.DFTroopStrengthLabel.setTextColor("red");
              eVBox.add(eHBox3);
              // The Support Horizontal Box
              var hboxSupportContainer = new qx.ui.container.Composite();
              hboxSupportContainer.setLayout(new qx.ui.layout.HBox(5));
              this.enemySupportLevelLabel = new qx.ui.basic.Label("Suport lvl ");
              hboxSupportContainer.add(this.enemySupportLevelLabel);
              this.enemySupportStrengthLabel = new qx.ui.basic.Label("--: 100");
              hboxSupportContainer.add(this.enemySupportStrengthLabel);
              this.enemySupportStrengthLabel.setTextColor("red");
              eVBox.add(hboxSupportContainer);
              // The Troops Vertical Box
              var tVBox = new qx.ui.container.Composite()
              tVBox.setLayout(new qx.ui.layout.VBox(5));
              tVBox.setThemedFont("bold");
              tVBox.setThemedPadding(2);
              tVBox.setThemedBackgroundColor("#eef");
              this.statsPage.add(tVBox);
              // The Repair Time Label
              var tHBox1 = new qx.ui.container.Composite();
              tHBox1.setLayout(new qx.ui.layout.HBox(5));
              tHBox1.add(new qx.ui.basic.Label("Repair Time: "));
              this.simRepairTimeLabel = new qx.ui.basic.Label("0:00:00");
              tHBox1.add(this.simRepairTimeLabel);
              this.simRepairTimeLabel.setTextColor("blue");
              tVBox.add(tHBox1);
              // The Troop Strength Label
              var tHBox5 = new qx.ui.container.Composite();
              tHBox5.setLayout(new qx.ui.layout.HBox(5));
              tHBox5.add(new qx.ui.basic.Label("Overall: "));
              this.simTroopDamageLabel = new qx.ui.basic.Label("100");
              tHBox5.add(this.simTroopDamageLabel);
              this.simTroopDamageLabel.setTextColor("blue");
              tVBox.add(tHBox5);
              // The Infantry Troop Strength Label
              var tHBox2 = new qx.ui.container.Composite();
              tHBox2.setLayout(new qx.ui.layout.HBox(5));
              tHBox2.add(new qx.ui.basic.Label("Infantry: "));
              this.infantryTroopStrengthLabel = new qx.ui.basic.Label("100");
              tHBox2.add(this.infantryTroopStrengthLabel);
              this.infantryTroopStrengthLabel.setTextColor("green");
              tVBox.add(tHBox2);
              // The Vehicle Troop Strength Label
              var tHBox3 = new qx.ui.container.Composite();
              tHBox3.setLayout(new qx.ui.layout.HBox(5));
              tHBox3.add(new qx.ui.basic.Label("Vehicle: "));
              this.vehicleTroopStrengthLabel = new qx.ui.basic.Label("100");
              tHBox3.add(this.vehicleTroopStrengthLabel);
              this.vehicleTroopStrengthLabel.setTextColor("green");
              tVBox.add(tHBox3);
              // The Air Troop Strength Label
              var tHBox4 = new qx.ui.container.Composite();
              tHBox4.setLayout(new qx.ui.layout.HBox(5));
              tHBox4.add(new qx.ui.basic.Label("Aircraft: "));
              this.airTroopStrengthLabel = new qx.ui.basic.Label("100");
              tHBox4.add(this.airTroopStrengthLabel);
              this.airTroopStrengthLabel.setTextColor("green");
              tVBox.add(tHBox4);

              // The inner Vertical Box
              var vBox = new qx.ui.container.Composite()
              vBox.setLayout(new qx.ui.layout.VBox(5));
              vBox.setThemedFont("bold");
              vBox.setThemedPadding(2);
              vBox.setThemedBackgroundColor("#eef");
              // The Victory Label
              var hBox2 = new qx.ui.container.Composite()
              hBox2.setLayout(new qx.ui.layout.HBox(5));
              hBox2.add(new qx.ui.basic.Label("Outcome: "));
              this.simVictoryLabel = new qx.ui.basic.Label("Unknown");
              hBox2.add(this.simVictoryLabel);
              this.simVictoryLabel.setTextColor("green");
              vBox.add(hBox2);
              
              this.statsPage.add(vBox);
              
              
              ////////////////// Layouts ////////////////////
              var layoutPage = new qx.ui.tabview.Page("Layouts");
              layoutPage.setLayout(new qx.ui.layout.VBox());
              tabView.add(layoutPage);

              this.layoutsList = new qx.ui.form.List();
              this.layoutsList.set({
                height: 200,
                width: 180,
                selectionMode: "one"
              });
              layoutPage.add(this.layoutsList);

              // Add the two buttons for save and load
              var layHBox = new qx.ui.container.Composite();
              layHBox.setLayout(new qx.ui.layout.HBox(5));
              // Load button
              this.buttonLayoutLoad = new qx.ui.form.Button("Load");
              this.buttonLayoutLoad.set({
                width: 80,
                appearance: "button-text-small",
                toolTipText: "Load this saved layout."
              });
              this.buttonLayoutLoad.addListener("click", this.loadCityLayout, this);
              layHBox.add(this.buttonLayoutLoad);
              // Delete button
              this.buttonLayoutDelete = new qx.ui.form.Button("Delete");
              this.buttonLayoutDelete.set({
                width: 80,
                appearance: "button-text-small",
                toolTipText: "Delete this saved layout."
              });
              this.buttonLayoutDelete.addListener("click", this.deleteCityLayout, this);
              layHBox.add(this.buttonLayoutDelete);
              layoutPage.add(layHBox);

              var layVBox = new qx.ui.container.Composite()
              layVBox.setLayout(new qx.ui.layout.VBox(5));
              layVBox.setThemedFont("bold");
              layVBox.setThemedPadding(2);
              layVBox.setThemedBackgroundColor("#eef");
              // The Label Textbox
              var layHBox2 = new qx.ui.container.Composite()
              layHBox2.setLayout(new qx.ui.layout.HBox(5));
              layHBox2.add(new qx.ui.basic.Label("Name: "));
              this.layoutsLabelText = new qx.ui.form.TextField();
              layHBox2.add(this.layoutsLabelText);
              layVBox.add(layHBox2);

              this.buttonLayoutSave = new qx.ui.form.Button("Save");
              this.buttonLayoutSave.set({
                width: 80,
                appearance: "button-text-small",
                toolTipText: "Save this layout."
              });
              this.buttonLayoutSave.addListener("click", this.saveCityLayout, this);
              layVBox.add(this.buttonLayoutSave);
              layoutPage.add(layVBox);

              ////////////////// Info ////////////////////
              var infoPage = new qx.ui.tabview.Page("Info");
              infoPage.setLayout(new qx.ui.layout.VBox(5));
              tabView.add(infoPage);

              // The Help Vertical Box
              var pVBox = new qx.ui.container.Composite()
              pVBox.setLayout(new qx.ui.layout.VBox(5));
              pVBox.setThemedFont("bold");
              pVBox.setThemedPadding(2);
              pVBox.setThemedBackgroundColor("#eef");
              infoPage.add(pVBox);
              var proHelpBar = new qx.ui.basic.Label().set({
                value: "<a target='_blank' href='http://userscripts.org/scripts/discuss/130344'>Forums</a>",
                rich: true
              });
              pVBox.add(proHelpBar);
              // The Spoils
              var psVBox = new qx.ui.container.Composite()
              psVBox.setLayout(new qx.ui.layout.VBox(5));
              psVBox.setThemedFont("bold");
              psVBox.setThemedPadding(2);
              psVBox.setThemedBackgroundColor("#eef");
              infoPage.add(psVBox);
              psVBox.add(new qx.ui.basic.Label("Spoils"));
              // Tiberium
              this.tiberiumSpoils = new qx.ui.basic.Atom("0", "webfrontend/ui/common/icn_res_tiberium.png");
              psVBox.add(this.tiberiumSpoils);
              // Crystal
              this.crystalSpoils = new qx.ui.basic.Atom("0", "webfrontend/ui/common/icn_res_chrystal.png");
              psVBox.add(this.crystalSpoils);
              // Credits
              this.creditSpoils = new qx.ui.basic.Atom("0", "webfrontend/ui/common/icn_res_dollar.png");
              psVBox.add(this.creditSpoils);
              // Research
              this.researchSpoils = new qx.ui.basic.Atom("0", "webfrontend/ui/common/icn_res_research_mission.png");
              psVBox.add(this.researchSpoils);

              this.battleResultsBox.add(tabView);
            },
            closeToolsBox: function () {
              try {
                var units = this.getCityPreArmyUnits();
                if (units) {
                  units.remove_ArmyChanged(this.add_ArmyChanged);
                }
                this.battleResultsBox.close();
              } catch (e) {
                console.log(e);
              }
            },
            toggleTools: function () {
              var units = this.getCityPreArmyUnits();
              this.units = units.get_ArmyUnits().l;
              if (this.battleResultsBox.isVisible()) {
                this.closeToolsBox();
              }
              else {
                // Add the event listener for armybar
                try {
                  units.remove_ArmyChanged(this.add_ArmyChanged);
                  units.add_ArmyChanged(this.add_ArmyChanged);
                } catch (e) {
                  console.log(e);
                }
                
                this.updateLayoutsList();
                this.calculateLoot();
                this.calculateSimResults();
                this.updateStatsWindow();
                this.battleResultsBox.open();
              }
            },
            getCityPreArmyUnits: function () {
              var armyBar = qx.core.Init.getApplication().getUIItem(ClientLib.Data.Missions.PATH.BAR_ATTACKSETUP);
              var units = null;
              for (var key in armyBar) {
                try {
                  if (armyBar[key] instanceof ClientLib.Data.CityPreArmyUnits) { // ClientLib.Data.CityPreArmyUnits renamed to $I.UIG  = $I.NSVPME *CHECKED*
                    units = armyBar[key];
                    break;
                  }
                } catch (e) {

                }
              }

              return units;
            },
            calculateLoot: function () {
              // Adapted from the CNC Loot script: http://userscripts.org/scripts/show/135953
              var city = ClientLib.Data.MainData.GetInstance().get_Cities().get_CurrentCity();
              var num = city.get_CityBuildingsData().RVQBJT(); // m_BuildSlotsCurrent renamed to RVQBJT()
              var spoils = {
                1: 0,
                2: 0,
                3: 0,
                6: 0,
                7: 0
              };

              for (var j = num; --j >= 0;) {
                var building = city.get_CityBuildingsData().RSRTMA().l[j]; //m_Buildings renamed to RSRTMA()
                var mod = building.NRCJKK / building.UBEEJD; //m_CurrentHealth renamed to NRCJKK, m_MaxHealth renamed to UBEEJD
                for (var i = building.RBGTWS.rer.length; --i >= 0;) { //m_UnitLevelRequirements renamed to RBGTWS
                  spoils[building.RBGTWS.rer[i].t] += mod * building.RBGTWS.rer[i].c;
                }
              }

              if (city.get_CityUnitsData().SNVBHX != null) { // m_DefenseUnits renamed to SNVBHX
                num = city.get_CityUnitsData().SNVBHX.l.length;
                for (j = num; --j >= 0;) {
                  var unit = city.get_CityUnitsData().SNVBHX.l[j];
                  mod = unit.NRCJKK / unit.UBEEJD;
                  for (i = unit.RBGTWS.rer.length; --i >= 0;) {
                    spoils[unit.RBGTWS.rer[i].t] += mod * unit.RBGTWS.rer[i].c;
                  }
                }
              }
              this.tiberiumSpoils.setLabel(this.formatNumberWithCommas(spoils[1]));
              this.crystalSpoils.setLabel(this.formatNumberWithCommas(spoils[2]));
              this.creditSpoils.setLabel(this.formatNumberWithCommas(spoils[3]));
              this.researchSpoils.setLabel(this.formatNumberWithCommas(spoils[6]));
            },
            calculateSimResults: function () {
              var battleground = this.setupBattleground(this.getCityPreArmyUnits());
              
              // Run the simulation until it's done
              while (battleground.HXGRQD.ICPGRO(false)) {} // DoStep$0 was renamed to ICPGRO, m_Simulation was renamed to HXGRQD
              
              this.calculateTroopStrengths(battleground);
            },
            onUnitMoved: function (sender, e) {
              if (!busy) {
                var ta = window.TASuite.main.getInstance();
                ta.calculateSimResults();
                ta.updateStatsWindow();
              }
            },
            onDamageDone: function (sender, e) {
              var ta = window.TASuite.main.getInstance();
              battleground = sender.GETXBE.i[0].o; // DamageDone was renamed to GETXBE
              // For the sake of performance, only run this every 10th step
              if (battleground.LTUPDH % 10 == 0) { // m_CurrentStep changed to LTUPDH
                ta.calculateTroopStrengths(battleground);
                ta.updateStatsWindow();
              }
            },
            calculateTroopStrengths: function (battleground) {
              var battleground = ClientLib.Vis.VisMain.GetInstance().get_Battleground();
              var total_hp = 0;
              var end_hp = 0;
              var e_total_hp = 0;
              var e_end_hp = 0;
              var eb_total_hp = 0;
              var eb_end_hp = 0;
              var eu_total_hp = 0;
              var eu_end_hp = 0;
              var i_end_hp = 0;
              var v_end_hp = 0;
              var a_end_hp = 0;
              var v_total_hp = 0;
              var a_total_hp = 0;
              var i_total_hp = 0;
              this.lastDFPercentage = 0;
              this.lastCYPercentage = 0;
              this.SupportLevel = 0;
              this.lastSupportPercentage = 0;
              this.lastInfantryRepairTime = 0;
              this.lastVehicleRepairTime = 0;
              this.lastAircraftRepairTime = 0;
              
              var own_city = ClientLib.Data.MainData.GetInstance().get_Cities().get_CurrentOwnCity();
              var crd = own_city.get_CityRepairData();
              var cud = own_city.get_CityUnitsData();
              var repair_times = own_city.get_CityUnitsData().NBLSAX.d; // m_FullRawRepairTimeForUnitGroupTypes renamed to NBLSAX
              var r_types = ClientLib.Base.EResourceType;
              
              var entities = battleground.RMCABE.d; // m_Entities has been renamed to RMCABE
              
              for (var i in entities) {
                var entity = entities[i];
                var i_entity = entity.HLUBJW; // get_Entity$0() has been removed. Propery is $I.TQL - HLUBJW
                var a_entity = entity.CMCLEW; // ??? has been renamed to CMCLEW
                var current_hp = i_entity.XKHWEF; // m_iHitpointsCurrent has been renamed to XKHWEF
                var max_hp = i_entity.WCJQEN; // m_iHitpoints has been renamed to WCJQEN
                if (a_entity.PSVMGQ == 2) { // ??? has been renamed to PSVMGQ, Attacker is 2
                  // This is one of the good guys
                  end_hp += current_hp;
                  total_hp += max_hp;
                  switch (a_entity.UXBOFH) { // movement type has been renamed to UXBOFH
                    case ClientLib.Base.EUnitMovementType.Air:
                    case ClientLib.Base.EUnitMovementType.Air2:
                      a_end_hp += current_hp;
                      a_total_hp += max_hp;
                      break;
                    case ClientLib.Base.EUnitMovementType.Feet:
                      i_end_hp += current_hp;
                      i_total_hp += max_hp;
                      break;
                    case ClientLib.Base.EUnitMovementType.Track:
                    case ClientLib.Base.EUnitMovementType.Wheel:
                      v_end_hp += current_hp;
                      v_total_hp += max_hp;
                      break;
                    case ClientLib.Base.EUnitMovementType.Structure:
                      break;
                  }
                }
                else {
                  // Enemy Overall
                  e_total_hp += max_hp;
                  e_end_hp += current_hp;
                  
                  switch (a_entity.UXBOFH) {
                    case ClientLib.Base.EUnitMovementType.Structure:
                      // Enemy Building
                      eb_total_hp += max_hp;
                      eb_end_hp += current_hp;
  
                      if (i_entity.UYECHX >= 200 && i_entity.UYECHX <= 205) {
                        this.SupportLevel = parseInt(i_entity.m_iLevel);
                        this.lastSupportPercentage = (current_hp / max_hp) * 100;
                      } else {
                        switch (i_entity.UYECHX) { // m_MDCTypeId has been renamed to UYECHX
                        case 112: // CONSTRUCTION YARD
                        case 151:
                        case 177:
                          this.lastCYPercentage = (current_hp / max_hp) * 100;
                          break;
                        case 158: // DEFENSE FACILITY
                        case 131:
                        case 195:
                          this.lastDFPercentage = (current_hp / max_hp) * 100;
                          break;
                        }
                      }
                      break;
                      
                    default:
                      // Enemy Defence
                      eu_total_hp += max_hp;
                      eu_end_hp += current_hp;
                      break;
                  }
                }
              }

              this.lastInfantryPercentage = i_total_hp ? (i_end_hp / i_total_hp) * 100 : 100;
              this.lastVehiclePercentage = v_total_hp ? (v_end_hp / v_total_hp) * 100 : 100;
              this.lastAirPercentage = a_total_hp ? (a_end_hp / a_total_hp) * 100 : 100;

              this.lastEnemyUnitsPercentage = (eu_end_hp / eu_total_hp) * 100;
              this.lastEnemyBuildingsPercentage = (eb_end_hp / eb_total_hp) * 100;
              this.lastEnemyPercentage = (e_end_hp / e_total_hp) * 100;
              this.lastPercentage = (end_hp / total_hp) * 100;
              
              // Calculate the repair time
              crd.ConvertRepairCost = crd.RNUKEW;// ConvertRepairCost has been renamed to RNUKEW
              this.lastInfantryRepairTime = crd.ConvertRepairCost(r_types.RepairChargeInf, repair_times[ClientLib.Data.EUnitGroup.Infantry], (1 - this.lastInfantryPercentage / 100));
              this.lastAircraftRepairTime = crd.ConvertRepairCost(r_types.RepairChargeAir, repair_times[ClientLib.Data.EUnitGroup.Aircraft], (1 - this.lastAirPercentage / 100));
              this.lastVehicleRepairTime = crd.ConvertRepairCost(r_types.RepairChargeVeh, repair_times[ClientLib.Data.EUnitGroup.Vehicle], (1 - this.lastVehiclePercentage / 100));
              this.lastRepairTime = Math.max(this.lastVehicleRepairTime, this.lastAircraftRepairTime, this.lastInfantryRepairTime);
            },
            setLabelColor: function (obj, val, dir) {
              var colors = ['black', 'blue', 'green', 'red'];
              var color = colors[0];
              var v = val;
              if (dir >= 0) v = 100.0 - v;
              if (v > 99.99) color = colors[3];
              else if (v > 50) color = colors[2];
              else if (v > 0) color = colors[1];
              obj.setTextColor(color);
            },
            updateLabel100: function (obj, val, dir) {
              this.setLabelColor(obj, val, dir);
              obj.setValue(val.toFixed(2).toString());
            },
            updateLabel100time: function (obj, val, dir, time) {
              var s = val.toFixed(2).toString() + " @ ";
              s += this.formatSecondsAsTime(time, "h:mm:ss");
              this.setLabelColor(obj, val, dir);
              obj.setValue(s);
            },
            updateStatsWindow: function () {
              var colors = ['black', 'blue', 'green', 'red'];
              var s = "";
              var n = 0;
              // VICTORY
              if (this.lastCYPercentage == 0) {
                s = "Total Victory";
                n = 0;
              } else if (this.lastEnemyBuildingsPercentage < 100) {
                s = "Victory";
                n = 1;
              } else {
                s = "Total Defeat";
                n = 3;
              }
              this.simVictoryLabel.setValue(s);
              this.simVictoryLabel.setTextColor(colors[n]);
              this.updateLabel100(this.enemyTroopStrengthLabel, this.lastEnemyPercentage, -1);
              this.updateLabel100(this.enemyUnitsStrengthLabel, this.lastEnemyUnitsPercentage, -1);
              this.updateLabel100(this.enemyBuildingsStrengthLabel, this.lastEnemyBuildingsPercentage, -1);
              this.updateLabel100(this.CYTroopStrengthLabel, this.lastCYPercentage, -1);
              this.updateLabel100(this.DFTroopStrengthLabel, this.lastDFPercentage, -1);
              // -SUPPORT
              var SLabel = (this.SupportLevel > 0) ? this.SupportLevel.toString() : '--';
              this.enemySupportLevelLabel.setValue('Suport lvl ' + SLabel + ': ');
              this.updateLabel100(this.enemySupportStrengthLabel, this.lastSupportPercentage, -1);
              // ATTACKER
              this.setLabelColor(this.simRepairTimeLabel, this.lastRepairTime / 14400.0, -1); //max is 4h
              this.simRepairTimeLabel.setValue(this.formatSecondsAsTime(this.lastRepairTime, "h:mm:ss"));
              // OVERALL
              this.updateLabel100(this.simTroopDamageLabel, this.lastPercentage, 1);
              // INF
              this.updateLabel100time(this.infantryTroopStrengthLabel, this.lastInfantryPercentage, 1, this.lastInfantryRepairTime);
              // VEH
              this.updateLabel100time(this.vehicleTroopStrengthLabel, this.lastVehiclePercentage, 1, this.lastVehicleRepairTime);
              // AIR
              this.updateLabel100time(this.airTroopStrengthLabel, this.lastAirPercentage, 1, this.lastAircraftRepairTime);
            },
            formatNumberWithCommas: function (x) {
              return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
            },
            formatSecondsAsTime: function (secs, format) {
              var hr = Math.floor(secs / 3600);
              var min = Math.floor((secs - (hr * 3600)) / 60);
              var sec = Math.floor(secs - (hr * 3600) - (min * 60));

              if (hr < 10) {
                hr = "0" + hr;
              }
              if (min < 10) {
                min = "0" + min;
              }
              if (sec < 10) {
                sec = "0" + sec;
              }

              if (format != null) {
                var formatted_time = format.replace('hh', hr);
                formatted_time = formatted_time.replace('h', hr * 1 + "");
                formatted_time = formatted_time.replace('mm', min);
                formatted_time = formatted_time.replace('m', min * 1 + "");
                formatted_time = formatted_time.replace('ss', sec);
                formatted_time = formatted_time.replace('s', sec * 1 + "");
                return formatted_time;
              } else {
                return hr + ':' + min + ':' + sec;
              }
            },
            unlockAttacks: function () {
              var armyBar = qx.core.Init.getApplication().getUIItem(ClientLib.Data.Missions.PATH.BAR_ATTACKSETUP);
              armyBar.remove(this.buttonUnlockAttack);
              var _this = this;
              setTimeout(function () {
                armyBar.add(_this.buttonUnlockAttack);
              }, 2000);
            },
            onViewChange: function (oldMode, newMode) {
              try {
                if (oldMode == webfrontend.gui.PlayArea.PlayArea.modes.EMode_CombatSetupDefense && newMode == webfrontend.gui.PlayArea.PlayArea.modes.EMode_PlayerOffense) {
                  // Actually we are doing this when we press the Simulate button instead for now
                  // Switched from Combat Setup to the Simulation, show the stats box
                  //this.battleResultsBox.open();
                  //var battleground = ClientLib.Vis.VisMain.GetInstance().get_Battleground();
                  //this.calculateTroopStrengths(battleground);
                  //this.updateStatsWindow();
                }
                else {
                  // Close the stats box
                  this.battleResultsBox.close();
                }
              } catch (e) {
                console.log(e);
              }
            },
            returnSetup: function () {
              // Set the scene again, just in case it didn't work the first time
              var app = qx.core.Init.getApplication();
              var player_cities = ClientLib.Data.MainData.GetInstance().get_Cities();
              var current_city = player_cities.get_CurrentCity();
              try {
                app.getPlayArea().setView(webfrontend.gui.PlayArea.PlayArea.modes.EMode_CombatSetupDefense, localStorage.ta_sim_last_city, 0, 0);
              } catch (e) {
                app.getPlayArea().setView(webfrontend.gui.PlayArea.modes.EMode_CombatSetupDefense, localStorage.ta_sim_last_city, 0, 0);
              }
            },
            setupBattleground: function (offense_units) {
              try {
                var mainData = ClientLib.Data.MainData.GetInstance();
                var player_cities = mainData.get_Cities();
                var current_city = player_cities.get_CurrentCity();
                var own_city = player_cities.get_CurrentOwnCity();
                
                // Bust the cache
                //own_city.get_CityArmyFormationsManager().ZJG.d[own_city.get_CityArmyFormationsManager().XJG].UpdateFormation();
                
                localStorage.ta_sim_last_city = current_city.get_Id();

                var alliance = ClientLib.Data.MainData.GetInstance().get_Alliance();
                var combatData = (new ClientLib.Data.Combat).$ctor();
                //var combatData = (new $I.CM).QB();
                combatData.TTAUEY = 1; // Version is TTAUEY
                
                var unitData = own_city.get_CityUnitsData().HHYWQM().l; // Attacker Units renamed to HHYWQM
                if (offense_units) {
                  offense_units = offense_units.PFTXHT.l;
                }
                else {
                  offense_units = own_city.get_CityArmyFormationsManager().GetFormationByTargetBaseId(current_city.get_Id()).get_ArmyUnits().l;
                }
                var data = new Array();

                for (var i = 0; i < unitData.length; i++) {
          if (offense_units[i].get_Enabled()) {
            var info = new Object();
            info.h = unitData[i].get_Health();
            info.i = unitData[i].get_MdbUnitId();
            info.l = unitData[i].get_CurrentLevel();
            info.x = offense_units[i].get_CoordX();
            info.y = offense_units[i].get_CoordY();
            data.push(info);
          }
                }

                combatData.VVWJIF = data; // Attackers renamed to VVWJIF

                unitData = current_city.get_CityUnitsData().OCYIKN.l; // Defender Units renamed to OCYIKN
                data = new Array();
                for (i = 0; i < unitData.length; i++) {
                  info = new Object();
                  info.h = unitData[i].get_Health();
                  info.i = unitData[i].get_MdbUnitId();
                  info.l = unitData[i].get_CurrentLevel();
                  info.x = unitData[i].get_CoordX();
                  info.y = unitData[i].get_CoordY();
                  data.push(info);
                }
                combatData.MBBLBJ = data; // Defenders renamed to MBBLBJ

                data = new Array();
                for (var i = 0; i < 9; i++) {
                  for (var j = 0; j < 8 ; j++) {
                    var terrainType = current_city.GetResourceType(i, (j + 8));
                    var unitType = -1;
                    switch (terrainType) {
                      case ClientLib.Data.ECityTerrainType.FOREST:
                        unitType = ClientLib.Base.EUnit.Forest;
                        break;
                      case ClientLib.Data.ECityTerrainType.BRIAR:
                        unitType = ClientLib.Base.EUnit.Scrub;
                        break;
                      case ClientLib.Data.ECityTerrainType.SWAMP:
                        unitType = ClientLib.Base.EUnit.Swamp;
                        break;
                      case ClientLib.Data.ECityTerrainType.WATER:
                        unitType = ClientLib.Base.EUnit.Water;
                        break;
                    }
                    
                    if (unitType != -1) {
                      info = new Object();
                      info.h = 100;
                      info.i = unitType;
                      info.l = 1;
                      info.x = i;
                      info.y = j;
                      data.push(info);
                    }
                  }
                }
                
                combatData.IPFXEV = data; // Terrain renamed to IPFXEV

                unitData = current_city.get_CityBuildingsData().ZATNVD.l; // City Buildings renamed to ZATNVD
                data = new Array();
                for (i = 0; i < unitData.length; i++) {
                  info = new Object();
                  info.h = unitData[i].get_Health();
                  info.i = unitData[i].get_MdbUnitId();
                  info.l = unitData[i].get_CurrentLevel();
                  info.x = unitData[i].get_CoordX();
                  info.y = unitData[i].get_CoordY();
                  data.push(info);
                }
                
                combatData.OQAQXC = data; // Buildings renamed to OQAQXC

                combatData.FOTQBA = null; // Support Structures renamed to FOTQBA
                combatData.DJWUYH = 8696244; // Start Step - this is just a random number - renamed to DJWUYH
                combatData.m_CombatSteps = 1;
                combatData.m_BoostInfantry = alliance.get_POIInfantryBonus();
                combatData.m_BoostVehicle = alliance.get_POIVehicleBonus();
                combatData.m_BoostAir = alliance.get_POIAirBonus();
                combatData.m_BoostDefense = current_city.m_AllianceDefenseBonus ? current_city.m_AllianceDefenseBonus : 0; // This might not be working
                combatData.m_AttackerBaseId = own_city.get_Id();
                combatData.m_AttackerBaseName = own_city.get_Name();
                combatData.m_AttackerPlayerId = own_city.get_PlayerId();
                combatData.m_AttackerPlayerName = own_city.get_PlayerName();
                combatData.m_AttackerAllianceId = own_city.get_AllianceId();
                combatData.m_AttackerAllianceName = own_city.get_AllianceName();
                combatData.m_DefenderBaseId = current_city.get_Id();
                combatData.m_DefenderBaseName = current_city.get_Name();
                combatData.m_DefenderPlayerId = current_city.get_PlayerId();
                combatData.m_DefenderPlayerName = current_city.get_OwnerName();
                combatData.m_DefenderAllianceId = current_city.get_AllianceId();
                combatData.m_DefenderAllianceName = current_city.get_OwnerAllianceName();
                combatData.m_DefenderBlockStep = 0;
                combatData.m_AttackTimeStamp = new Date().getTime();
                var resourceLayout = new Object();
                resourceLayout.l = new Array();
                combatData.m_ResourceLayout = resourceLayout;
                combatData.m_DefenderFaction = current_city.get_CityFaction();
                combatData.m_AttackerModules = this.attacker_modules;
                combatData.m_DefenderModules = this.defender_modules;
                
                if (((combatData.m_DefenderFaction == ClientLib.Base.EFactionType.FORFaction) || (combatData.m_DefenderFaction == ClientLib.Base.EFactionType.NPCBase)) || (combatData.m_DefenderFaction == ClientLib.Base.EFactionType.NPCCamp)) {
                  combatData.WWAFXZ(); // This might not be needed
                }

                combatData.m_MaxDuration = 120;
                combatData.m_Complete = false;
                combatData.RJXSNR = null; // Debug renamed to RJXSNR

                var battleground = ClientLib.Vis.VisMain.GetInstance().get_Battleground();
                battleground.Reset();
                battleground.QYEDFT = combatData; // m_currentreplay
                battleground.InitBattle();
                battleground.PRTRPU(combatData); // Set combat data
                battleground.StartBattle();

                return battleground;
              } catch (e) {
                console.log(e);
              }
            },
            startSimulation: function () {
              try {
                var app = qx.core.Init.getApplication();
                var player_cities = ClientLib.Data.MainData.GetInstance().get_Cities();
                var current_city = player_cities.get_CurrentCity();

                try {
                  app.getPlayArea().setView(webfrontend.gui.PlayArea.PlayArea.modes.EMode_CombatReplay, current_city.get_Id(), 0, 0);
                } catch (e) {
                  app.getPlayArea().setView(webfrontend.gui.PlayArea.modes.EMode_CombatReplay, current_city.get_Id(), 0, 0);
                }
                var battleground = this.setupBattleground();
                
                // Add the event listeners
                battleground.HXGRQD.GUADUH((new $I.WMJHOK).GEDTYY(this, this.onDamageDone)); // m_Simulation became HXGRQD, The add_DamageDone$0 has been renamed to GUADUH, System.EventHandler.$ctor was renamed to $I.WMJHOK.GEDTYY

                // Set the scene again, just in case it didn't work the first time
                try {
                  app.getPlayArea().setView(webfrontend.gui.PlayArea.PlayArea.modes.EMode_CombatReplay, current_city.get_Id(), 0, 0);
                } catch (e) {
                  app.getPlayArea().setView(webfrontend.gui.PlayArea.modes.EMode_CombatReplay, current_city.get_Id(), 0, 0);
                }
                
                this.battleResultsBox.close();
              } catch (e) {
                console.log(e);
              }
            },
            updateLayoutsList: function () {
              this.layoutsList.removeAll();
              // Load the saved layouts for this city
              var city_layouts = this.loadCityLayouts();
              if (city_layouts) {
                for (var i in city_layouts) {
                  var layout = city_layouts[i];
                  var item = new qx.ui.form.ListItem(layout.label, null, layout.id);
                  this.layoutsList.add(item);
                };
              }
            },
            deleteCityLayout: function () {
              try {
                var layouts = this.loadLayouts();
                var current_city = ClientLib.Data.MainData.GetInstance().get_Cities().get_CurrentCity().get_Id();
                var own_city = ClientLib.Data.MainData.GetInstance().get_Cities().get_CurrentOwnCity().get_Id();
                var lid = this.layoutsList.getSelection()[0].getModel();
                if (layouts && typeof layouts[current_city] != 'undefined' && typeof layouts[current_city][own_city] != 'undefined' && typeof layouts[current_city][own_city][lid] != 'undefined') {
                  delete layouts[current_city][own_city][lid];
                  this.saveLayouts(layouts);
                  this.updateLayoutsList();
                }
              } catch (e) {
                console.log(e);
              }
            },
            loadCityLayout: function () {
              try {
                var city_layouts = this.loadCityLayouts();
                var lid = this.layoutsList.getSelection()[0].getModel();
                if (city_layouts && typeof city_layouts[lid] != 'undefined') {
                  // Load the selected city layout
                  var saved_units = city_layouts[lid].layout;
                  this.restoreFormation(saved_units);
                }
              } catch (e) {
                console.log(e);
              }
            },
            saveCityLayout: function () {
              try {
                // Save the current layout for this city
                var current_city = ClientLib.Data.MainData.GetInstance().get_Cities().get_CurrentCity().get_Id();
                var own_city = ClientLib.Data.MainData.GetInstance().get_Cities().get_CurrentOwnCity().get_Id();
                var layouts = this.loadLayouts();
                this.saveFormation();
                var lid = new Date().getTime().toString();
                var title = this.layoutsLabelText.getValue();
                if (!title) {
                  return;
                }
                title += " (TS: " + this.lastPercentage.toFixed(2).toString() + ")";
                var city_layouts = this.loadCityLayouts();
                if (!layouts.hasOwnProperty(current_city)) {
                  layouts[current_city] = {};
                }
                if (!layouts[current_city].hasOwnProperty(own_city)) {
                  layouts[current_city][own_city] = city_layouts;
                }
                layouts[current_city][own_city][lid] = {
                  id: lid,
                  label: title,
                  layout: this.saved_units,
                };
                this.saveLayouts(layouts);
                this.updateLayoutsList();
                this.layoutsLabelText.setValue("");
              } catch (e) {
                console.log(e);
              }
            },
            loadLayouts: function () {
              var temp = localStorage.tasim_layouts;
              if (temp) {
                return JSON.parse(temp);
              }
              return {};
            },
            loadCityLayouts: function () {
              var temp = localStorage.tasim_layouts;
              if (temp) {
                var layouts = JSON.parse(temp);
                var current_city = ClientLib.Data.MainData.GetInstance().get_Cities().get_CurrentCity().get_Id();
                var own_city = ClientLib.Data.MainData.GetInstance().get_Cities().get_CurrentOwnCity().get_Id();

                if (layouts.hasOwnProperty(current_city) && layouts[current_city].hasOwnProperty(own_city)) {
                  return layouts[current_city][own_city];
                }
              }
              return {};
            },
            saveLayouts: function (layouts) {
              // TODO - Remove cities that are no longer existing
              localStorage.tasim_layouts = JSON.stringify(layouts);
            },
            restoreFormation: function (saved_units) {
              saved_units = saved_units || this.saved_units;
              var units = this.getCityPreArmyUnits();
              var units_list = units.get_ArmyUnits().l;
              for (var i = 0;
              (i < units_list.length); i++) {
                var saved_unit = saved_units[i];
                units_list[i].set_CoordX(saved_unit.x);
                units_list[i].set_CoordY(saved_unit.y);
                units_list[i].BZZGTH = saved_unit.id; // m_UnitId renamed to BZZGTH
              }

              units.TNEWNF(); // UpdateArmyLayout$0() has been renamed to TNEWNF()
              units.AOMXAS(); // RefreshData() has been renamed to AOMXAS()
            },
            saveFormation: function () {
              this.saved_units = [];
              for (var i = 0;
              (i < this.units.length); i++) {
                var unit = this.units[i];
                var armyUnit = {};
                armyUnit.x = unit.get_CoordX();
                armyUnit.y = unit.get_CoordY();
                armyUnit.id = unit.get_Id();
                this.saved_units.push(armyUnit);
              }
            }
          }
        });
      }

      function TASuite_checkIfLoaded() {
        try {
          if (typeof qx != 'undefined') {
            a = qx.core.Init.getApplication(); // application
            mb = qx.core.Init.getApplication().getMenuBar();
            if (a && mb) {
              createTweak();
              window.TASuite.main.getInstance().initialize();
            } else window.setTimeout(TASuite_checkIfLoaded, 1000);
          } else {
            window.setTimeout(TASuite_checkIfLoaded, 1000);
          }
        } catch (e) {
          if (typeof console != 'undefined') console.log(e);
          else if (window.opera) opera.postError(e);
          else GM_log(e);
        }
      }

      if (/commandandconquer\.com/i.test(document.domain)) {
        window.setTimeout(TASuite_checkIfLoaded, 1000);
      }
    }

    // injecting, because there seem to be problems when creating game interface with unsafeWindow
  var TASuiteScript = document.createElement("script");
  var txt = TASuite_mainFunction.toString();
  TASuiteScript.innerHTML = "(" + txt + ")();";
  TASuiteScript.type = "text/javascript";
  if (/commandandconquer\.com/i.test(document.domain)) {
    document.getElementsByTagName("head")[0].appendChild(TASuiteScript);
  }

})();