LoU Suite

By Tobias Hansen Last update Mar 19, 2012 — Installed 6,183 times.

Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)

// ==UserScript==
// @name           LoU Suite
// @description    Various Helpers for Lord Of Ultima
// @namespace      WildKatana
// @include        http://prodgame*.lordofultima.com/*/index.aspx*
// @version        1.0
// ==/UserScript==

/**
 * http://refresh-sf.com/yui/
 */

(function(){

var LS_mainFunction = function() {
  function createSuite() {
    var louSuite = {};
    var LT = {};

    qx.Class.define("louSuite.main", {
      type: "singleton",
      extend: qx.core.Object,
      members: {
        _player: null,
        app: null,
        srvBar: null,
        bosses: null,
        dungeons: null,
        city: null,
        cities: null,
        cityId: null,
        boss_raider: null,
        dungeon_raider: null,
        LS_RAID: 8,
        LS_RAID_REPEAT: 1,
        LS_RAID_ONCE: 0,
        options: null,
        optionsPage: null,

        initialize: function() {
			this.app = qx.core.Init.getApplication();
			this.cities = {};
			this.bosses = {};
			this.dungeons = {};
			var t_c = webfrontend.data.Player.getInstance().cities;
			for (var cityId in t_c) {
				var c = t_c[cityId];
				var cont = webfrontend.data.Server.getInstance().getContinentFromCoords(c.xPos, c.yPos);
				this.bosses['c' + cont] = {};
				this.dungeons['c' + cont] = {};
			}
			this._player = webfrontend.data.Player.getInstance().getId();
			this.srvBar = this.app.serverBar;
			this.startScript();
        },

        startScript: function() {
		  _LT = LT;
          this.loadOptions();
          LT.options = this.options;
          LT.srvTime = webfrontend.data.ServerTime.getInstance().refTime; // reference time (server start date in milisec?)
          LT.debug = this.debug;
          LT.a = this.app;
          LT.main = this;
          LT.player = webfrontend.data.Player.getInstance();
          LT.curr_city = webfrontend.data.City.getInstance();
          this.scanAllCities();

          // ***** Options button ***** //
          btn = new qx.ui.form.Button("S");
          btn.set({width: 30, appearance: "button-text-small", toolTipText: "LoU Suite Settings"});
          btn.addListener("click", this.showOptionsPage, this);
          this.srvBar.add(btn, {top: 2, left: 350});

          // ***** Listeners ***** //
          this.optionsPage = new window.louSuite.optionsPage();
        },

        sendTroops: function (order_type, city_id, x, y, units, repeat, callbackFunc) {
          var updateManager = webfrontend.net.UpdateManager.getInstance();
          var data = {
            session: updateManager.getInstanceGuid(), 
            cityid: city_id, 
            units: units, 
            targetPlayer: "", 
            targetCity: x + ':' + y, 
            order: order_type, 
            transport: 1, 
            createCity: "", 
            timeReferenceType: 1, 
            referenceTimeUTCMillis: 0, 
            raidTimeReferenceType: repeat, // Repeat raid or not
            raidReferenceTimeUTCMillis: 0, 
            iUnitOrderOptions: 0, 
          };
          var req = new qx.io.remote.Request(updateManager.getUpdateService() + "/Service.svc/ajaxEndpoint/OrderUnits", "POST", "application/json");
          req.setProhibitCaching(false);
          req.setRequestHeader("Content-Type", "application/json");
          req.setData(qx.util.Json.stringify(data));
          req.setTimeout(10000);
          req.addListener("completed", function(e) {
            callbackFunc(e, {cid: city_id, x: x, y: y, units: units});
          }, this);
          req.send();
        },

        moveToContRegion: function (cont, q) {
          var c = 0;
          for (var x = 1; x < 4; x++) {
            for (var y = 1; y < 4; y++) {
              c += 1;
              if (c == q) {
                var xm = x/4;
                var ym = y/4;
                var col = Math.floor(cont % 10);
                var row = Math.floor(cont / 10);
                var srv = webfrontend.data.Server.getInstance();
                var height = srv.getContinentHeight();	
                var width = srv.getContinentWidth();
                var x = Math.floor(col * width + 0.5 * width);
                var y = Math.floor(row * height + 0.5 * height);
               
                LT.a.setMainView('r', 0, x * LT.a.visMain.getTileWidth(), y * LT.a.visMain.getTileHeight());
              }
            }
          }
        },

        scanRegion: function () {
          if (LT.a.visMain.mapmode == "r") {
            p = webfrontend.data.Player.getInstance().getName();
            clib = ClientLib.Vis.VisMain.GetInstance();
            region_object = clib.get_Region();
            if (region_object == null || region_object == undefined) return;
            for (var o in region_object) {
              if (region_object[o] != null && region_object[o].hasOwnProperty("d"))
                data_object = region_object[o];
            }
            if (data_object == null || data_object == undefined) return;
            d_object = data_object.d;
            if (d_object == null || d_object == undefined) return;
            for (var n in d_object) {
              data_table = null;
              // this is just way too much, but couldn't find better solution to get obfuscated and version changing variables
              try {
                for (var dob in d_object[n]) {
                  if (d_object[n][dob] instanceof Array && d_object[n][dob].length == 32) {
                    for (e=0; e<d_object[n][dob].length; e++) {
                      r = d_object[n][dob][e];
                      if (r == null && typeof r == "object") {
                        data_table = d_object[n][dob];
                        break;
                      } else {
                        for (f=0; f<r.length; f++) {
                          if (r[f] == null && typeof r[f] == "object") {
                            data_table = d_object[n][dob];
                            break;
                          }
                        }
                      }
                    }
                  }
                  if (data_table != null) break;
                }
              } catch (e) {
                
              }
              //data_table = d_object[n].SIB;
              if (data_table == null || data_table == undefined) continue;
              for (q=0; q<data_table.length; q++) {
                row = data_table[q];
                for (w=0; w<row.length; w++) {
                  if (row[w] != null) {
                    crd = row[w].get_Coordinates();
                    posX = crd & 0xFFFF;
                    posY = crd >> 16;
                    uit = row[w].get_UIType();
                    //console.log("(" + posX + ":" + posY +"), " + uit);
                    // uit - City / LawlessCity / Shrine / Dungeon / Boss / FreeSlot / null (moongate)
                    if (uit == "Boss") {
                      // Determine what kind of boss we have, and what level
                      var b_type = null;
                      var b_level = null;
                      for (var fg in row[w]) {
                        if (row[w][fg] != null && row[w][fg].hasOwnProperty("BossLevel")) {
                          b_type = row[w][fg]['BossType'];
                          b_level = row[w][fg]['BossLevel'];
                          b_state = row[w][fg]['State'];
                        }
                      }
                      switch (b_type) {
                        case 5:
                          b_name = "Octopus";
                          break;
						case 6:
                          b_name = "Dragon";
                          break;
                        case 7:
                          b_name = "Moloch";
                          break;
                        case 8:
                          b_name = "Hydra";
                          break;
                        default:
                          b_name = "Boss";
                          break;
                      }
                      //console.log("(" + posX + ":" + posY + ") - " + b_name + " Level " + b_level + " State: " + b_state);
                      //console.log(row[w]);
                      var cont = webfrontend.data.Server.getInstance().getContinentFromCoords(posX, posY);
                      try {
                        this.bosses['c' + cont][posX + "-" + posY] = {
                          'type': b_name,
                          'level': b_level,
                          'x': posX,
                          'y': posY,
                          'state': b_state,
                          't': b_type
                        };
                      }
                      catch (e) {
                        // The boss was from a different continent. Oh well
                      }
                    }
                    if (uit == "Dungeon") {
                      // Determine what kind of dungeon we have, and what level
                      var d_type = null;
                      var d_level = null;
                      var d_state = null;
                      var d_progress = null;
                      for (var fg in row[w]) {
                       if (row[w][fg] != null && row[w][fg].hasOwnProperty("DungeonLevel")) {
                          d_type = row[w][fg]['DungeonType'];
                          d_level = row[w][fg]['DungeonLevel'];
                          d_state = row[w][fg]['State'];
                          d_progress = row[w][fg]['Progress'];
                        }
                      }
                      switch (d_type) {
                        case 3:
                          d_name = "Hill";
                          break;
                        case 4:
                          d_name = "Mountain";
                          break;
                        case 5:
                          d_name = "Forest";
                          break;
                        default:
                          d_name = "Unknown Dungeon";
                          break;
                      }
                      var cont = webfrontend.data.Server.getInstance().getContinentFromCoords(posX, posY);
                      try {
                        this.dungeons['c' + cont][posX + "-" + posY] = {
                          'type': d_name,
                          'level': d_level,
                          'x': posX,
                          'y': posY,
                          'progress': d_progress,
                          'state': d_state,
                          't': d_type
                        };
                      }
                      catch (e) {
                       
                      }
                    }
                  }
                }
              }
            }
          }
        },

        scanAllCities: function() {
          var storage = bos.Storage.getInstance();
          for (var cid in LT.player.cities) {
            var city = storage.loadCity(cid);
            this.cities[cid] = city;
          }
        },

        getAllCityUnits: function() {
          for (var cid in LT.player.cities) {
            this.getCityUnits(cid);
          }
        },

        getCityUnits: function(id) {
          var city = this.cities[id];
          var c_info = LT.player.cities[id];
          c_name = c_info['name'];
          var units = {};
          if (city != null) {
            for (var uid in city['units']) {
              u_name = this.getUnitName(uid);
              u_count = city['units'][uid]['count'];
              u_speed = city['units'][uid]['speed'];
              u_total = city['units'][uid]['total'];
              //console.log(c_name + " : " + u_count + "/" + u_total + " " + u_name + " available.");
              units[uid] = {
                count: u_count,
                name: u_name,
                total: u_total
              };
            }
          }
          
          return units;
        },

        getUnitName: function(id) {
          switch(id){
            case '1':
                return "City Guard";
            case '2':
                return "Ballista";
            case '3':
                return "Ranger";
            case '4':
                return "Guardian";
            case '5':
                return "Templar";
            case '6':
                return "Berserker";
            case '7':
                return "Mage";
            case '8':
                return "Scout";
            case '9':
                return "Crossbow";
            case '10':
                return "Paladin";
            case '11':
                return "Knight";
            case '12':
                return "Warlock";
            case '13':
                return "Ram";
            case '14':
                return "Catapult";
            case '15':
                return "Frigate";
            case '16':
                return "Sloop";
            case '17':
                return "Galleon";
            case '19':
                return "Baron";
            default:
                return id;
          }
        },

        setMessage: function (message) {
          var dialog = new webfrontend.gui.ConfirmationWidget();
          dialog.showGenericNotice("Error", message, message, "webfrontend/ui/bgr_popup_survey.gif");
          qx.core.Init.getApplication().getDesktop().add(dialog, {left: 0, right: 0, top: 0, bottom: 0});
          dialog.show();
        },

        showOptionsPage: function() {
          this.app.switchOverlay(this.optionsPage);
        },

        loadOptions: function() {
          forceSave = false;
          _str = localStorage.getItem("LT_options");
          if (_str)
            this.options = qx.util.Json.parse(_str);
          else {
            this.options = {
              "lowestLevelMax": [10,10,10,10,10,10]
            };
          }
          
          this.app.setUserData("LT_options", this.options);
          if (forceSave) {
            str = qx.util.Json.stringify(this.options);
            localStorage.setItem("LT_options", str);
          }
        },

        debug: function(s) {
          if (typeof console != 'undefined') console.log(s);
          else if (window.opera) opera.postError(s);
          else GM_log(s);
        }
      }
    });

    qx.Class.define("lou_suite.gui.attackMonitor", {
      extend: bos.gui.SummaryPage,
      type: "singleton",
      construct: function() {
        this.timer = new qx.event.Timer(300000);
        this.timer.addListener("interval", this.check_attacks, this); 
        this.timer.start();
        this.message_length = "short";
        this.message_frequency = 0;

        bos.gui.SummaryPage.call(this);
        this.email = localStorage['lousuite_email'];
        this.active = localStorage['lousuite_active'];
        this.attacks = new Object();
        this.i = SessionId;
        // Load some variables
        this.player = webfrontend.data.Player.getInstance();
        this.serverTime = webfrontend.data.ServerTime.getInstance();
        var lou_suite = window.louSuite.main.getInstance();
        this.setLabel("Attack Monitor");
        this.setLayout(new qx.ui.layout.VBox(10));
        
        var layout = new qx.ui.layout.Grid(9, 5);
        layout.setColumnAlign(0, "right", "top");
        layout.setColumnAlign(2, "right", "top");
        layout.setColumnWidth(1, 160);
        layout.setColumnWidth(2, 72);
        layout.setColumnWidth(3, 108);
        
        var page = new qx.ui.tabview.Page("Attack Monitor Settings");
        page.setLayout(layout);
        page.setPadding(10);
        
        var label = new qx.ui.basic.Label().set({
          value: "The attack monitor will send you an email whenever you have a new attack. It will work as long as anyone in your alliance is logged on and running Attack Monitor.",
          rich : true,
          width: 500
        });
        this.add(label);
        
        page.add(new qx.ui.basic.Label("Email").set({
          allowShrinkX: false,
          paddingTop: 3
        }), {row: 1, column : 0});
        
        this.email_field = new qx.ui.form.TextField(this.email);
        this.email_field.addListener("keypress", function (e) {return false;}, this);
        page.add(this.email_field, {row:1, column:1});
        
        this.active_checkbox = new qx.ui.form.CheckBox("Send Emails?");
        if (typeof this.active == 'undefined') {
          this.active = false;
        }
        this.active_checkbox.setValue(this.active);
        page.add(this.active_checkbox, {row:3, column:0});
        
        this.btnSave = new qx.ui.form.Button("Save");
        this.btnSave.setWidth(80);
        page.add(this.btnSave, {row: 4, column : 0});
        this.btnSave.addListener("execute", function(evt) {
          // Save the settings
          this.email = this.email_field.getValue();
          localStorage.setItem('lousuite_email', this.email);
          
          this.active = this.active_checkbox.getValue();
          localStorage.setItem('lousuite_active', this.active);
        }, this);
        
        this.add(page);
        this.check_attacks();
      },
      members: { 
        attacks: null,
        player: null,
        serverTime: null,
        email: null,
        timer: null,
        email_field: null,
        i: null,
        btnSave: null,
        active: null,
        message_length: null,
        message_frequency: null,
        active_checkbox: null,
        initialize: function () {         
        },
        check_attacks: function (subject, message) {
        },
      }
    });


    qx.Class.define("louSuite.optionsPage", {
      extend: webfrontend.gui.OverlayWidget,
      construct: function() {
        webfrontend.gui.OverlayWidget.call(this);
        this.clientArea.setLayout(new qx.ui.layout.Canvas());
        this.setTitle("LoU Suite");
        this.tabView = new qx.ui.tabview.TabView().set({contentPaddingLeft: 15, contentPaddingRight: 10, contentPaddingTop: 10, contentPaddingBottom: 10});
        this.tabView.add(new lou_suite.gui.BossRaider());
        this.tabView.add(new lou_suite.gui.DungeonRaider());
        var att = new lou_suite.gui.attackMonitor();
        this.tabView.add(att);  
        this.clientArea.add(this.tabView, {top: 0, right: 3, bottom: 30, left: 3});
      },
      members: {
        tabView: null,
        tabPages: null,
        clrSel: null,
        saveOptions: function() {
          str = qx.util.Json.stringify(LT.options);
          localStorage.setItem("LT_options", str);
          LT.a.switchOverlay(null);
        },
      }
    });

    qx.Class.define("lou_suite.gui.DungeonRaider", {
      extend: bos.gui.SummaryPage,
      construct: function() {
        var lou_suite = window.louSuite.main.getInstance();
        lou_suite.dungeon_raider = this;
        bos.gui.SummaryPage.call(this);
        this.setLabel("Dungeon Raider");
        this.setLayout(new qx.ui.layout.VBox(10));
        this.add(this._createToolBar());    
        this._tableModel = new qx.ui.table.model.Simple();
        var columnNames = [ "Id", "Row Info", "Type", "Level", "Pos", "Name", "Distance", "Units", "Actions"];
        var columnIds = ["id", "row_info", "dungeon_type", "dungeon_level", "position", "name", "distance", "units", "actions"];
       
        this._tableModel.setColumns(columnNames, columnIds);
        this._setupSorting(this._tableModel);
        this._tableModel.sortByColumn(4, true);
        
        var custom = {
          tableColumnModel : function(obj) {
            return new qx.ui.table.columnmodel.Resize(obj);
          }
        };
        
        this.table = new bos.ui.table.Table(this._tableModel, custom);
        this.table.addListener("cellClick", this._handleCellClick, this);
    
        var columnModel = this.table.getTableColumnModel();

        columnModel.setColumnVisible(0, false);
        columnModel.setColumnVisible(1, false);
        columnModel.setDataCellRenderer(4, new bos.ui.table.cellrenderer.ClickableLook());
        columnModel.setDataCellRenderer(5, new bos.ui.table.cellrenderer.ClickableLook());
        columnModel.setDataCellRenderer(8, new bos.ui.table.cellrenderer.ClickableLook());
        
        var tcm = this.table.getTableColumnModel();
        var resizeBehavior = tcm.getBehavior();
        resizeBehavior.setWidth(2, 60);
        resizeBehavior.setWidth(3, 20);
        resizeBehavior.setWidth(4, 50);
        resizeBehavior.setWidth(5, 100);
        resizeBehavior.setWidth(6, 70);
        resizeBehavior.setWidth(7, 80);
        resizeBehavior.setWidth(8, "1*");
        resizeBehavior.setMinWidth(8, 100);  
        this.add(this.table, {flex: 1});          
      },

      members: {
        sbContinents: null,
        btnUpdateView: null,
        sbLevel: null,
        classicView: function() {
          this.btnUpdateView.setEnabled(LT.a.visMain.mapmode == "r");
          var rowData = [];
          var lou_suite = window.louSuite.main.getInstance();
          var selectedContinent = this.sbContinents.getSelection()[0].getModel();
          var min_level = this.sbLevel.getSelection()[0].getModel();
          
          // Refresh the city list to get new troop counts
          lou_suite.scanAllCities();
         
          // First get all the dungeons in the area for the continent we are working with
          var dungeons = lou_suite.dungeons;
          for (var d_key in dungeons['c' + selectedContinent]) {
            var dungeon = dungeons['c' + selectedContinent][d_key];
            if (dungeon['state'] && (dungeon['level'] >= min_level)) {

			// Then get all the cities on that continent that can hit it
              for (var cityId in LT.player.cities) {
                c = LT.player.cities[cityId];
                if (c == null) {
                  continue;
                }
                var city_cont = webfrontend.data.Server.getInstance().getContinentFromCoords(c.xPos, c.yPos);

                // Check that the city is on the right continent
                if (city_cont != selectedContinent) {
                  continue;
                }
                var city = lou_suite.cities[cityId];
                if (city == undefined) {
                  continue;
                }
              
                // Check that the city has enough troops
                if (city.getUnitCount() == 0) {
                  continue;
                }
                var units = lou_suite.getCityUnits(cityId);
                
				// Check that there are enough units to send for this dungeon
                var units_to_send = this.getTroopsNeeded(dungeon['type'], dungeon['level'], units);
                if (typeof(units_to_send)=='object') {
                  var row = [];
                  
                  var units_string = "";
                  for (var u_key in units_to_send) {
                    units_string = units_string + " " + units_to_send[u_key] + " " + units[u_key]['name'];
                  }
                  
                  this._addBlankValuesToRow(row, this._tableModel);
                  row["units"] = units_string;
                  row["id"] = cityId;
                  row["row_info"] = {
                    units: units_to_send,
                    dungeon: dungeon,
                    city: c
                  };
                  row["name"] = c.name;
                  var diffX = Math.abs(c.xPos - dungeon['x']);
                  var diffY = Math.abs(c.yPos - dungeon['y']);
                  row["distance"] = Math.sqrt(diffX * diffX + diffY * diffY);
                  row["position"] = dungeon['x'] + ":" + dungeon['y'];
                  row["dungeon_type"] = dungeon['type'];
                  row["dungeon_level"] = dungeon['level'];
                  row["actions"] = "Send";
                  
                  rowData.push(row);
                }
              }
            }
         }
    
          return rowData;
        },

        createRowData: function() {
          return this.classicView();
        }, 

        getUnitsPerDungeon: function () {
          var units = {
            Mountain: {
              3: [3, 13, 59, 150, 500, 1134, 2500, 4167, 6667, 10000], // Ranger
              4: [7, 32, 146, 375, 1250, 2834, 6250, 10417, 16667, 25000], // Guardian
              6: [15, 75, 350, 900, 3000, 6800, 15000, 25000, 40000, 60000], // Berserker

			  },


            Hill: {
              3: [6, 26, 100, 340, 1000, 2000, 4800, 8000, 14000, 20000], // Ranger
              4: [5, 20, 75, 255, 750, 1500, 3600, 6000, 10500, 15000], // Guardian
              6: [15, 65, 250, 850, 2500, 5000, 12000, 20000, 35000, 50000], // Berserker
              7: [30, 130, 500, 1700, 5000, 10000, 24000, 40000, 70000, 100000], // Mage
              12: [15, 65, 250, 850, 2500, 5000, 12000, 20000, 35000, 50000], // Warlock
            },

            Forest: {
              3: [6, 26, 100, 340, 1000, 2000, 4800, 8000, 14000, 20000], // Ranger
              4: [5, 20, 75, 255, 750, 1500, 3600, 6000, 10500, 15000], // Guardian
              6: [15, 65, 250, 850, 2500, 5000, 12000, 20000, 35000, 50000], // Berserker
              11: [10, 44, 167, 567, 1667, 3334, 8000, 13334, 23334, 33334], // Knight
              10: [8, 33, 125, 425, 1250, 2500, 6000, 10000, 17500, 25000], // Paladin
            },

            Sea: {
              16: [2, 9, 57, 110, 277, 417, 567, 834, 1250, 1667], // Sloop
              15: [1, 4, 23, 44, 111, 167, 227, 334, 500, 667], // Frigate
              17: [1, 1, 6, 11, 28, 42, 57, 84, 125, 167], // War Galleon
            }
          };

          return units;
        },

        getTroopsNeeded: function (dungeon_name, dungeon_level, units) {
          var units_min = this.getUnitsPerDungeon();
          var kill_percent = 0.0;
          var queued_units = {};
          for (var u_key in units) {

            // TODO - Figure out a good way to combine berserkers and/or rangers with guardians
            if (u_key == 4 || u_key == 3) {
              continue;
            }
            var unit = units[u_key];
            if (unit['count'] > 0) {
              try {
                var units_needed = units_min[dungeon_name][u_key][dungeon_level - 1];
                var this_kill_percent = unit['count'] / units_needed;
                var total_kill = this_kill_percent + kill_percent;
                if (total_kill >= 1) {
                  // We have enough, return the queued units
                  var percent_needed = 1 - kill_percent;
                  units_needed = Math.ceil(units_needed * percent_needed);
                  
                  queued_units[u_key] = units_needed;
                  return queued_units;
                }
                else {
                  // Otherwise, let's add these to the queued units
                  queued_units[u_key] = unit['count'];
                  kill_percent += this_kill_percent;
                }
              }
              catch (e) {
                
              }
            }
          }
          
          // If we got this far, there isn't enough units to hit that dungeon
          return false;
        },

        _shouldBeIncluded: function(city) {
          return true;
        }, 

        _handleCellClick: function(event) {
          var row = event.getRow();
          var column = event.getColumn();
          var rowData = this._tableModel.getRowDataAsMap(row);
          var cityId = rowData["id"];
          var row_info = rowData["row_info"];
          var selectedContinent = this.sbContinents.getSelection()[0].getModel();
          switch (column) {
            case 5:
              LT.a.setMainView("c", cityId, -1, -1);
              break;
            case 4:
              var dungeon = row_info['dungeon'];
              LT.a.setMainView('r', 0, dungeon['x'] * LT.a.visMain.getTileWidth(), dungeon['y'] * LT.a.visMain.getTileHeight());
              break;
            case 8:
              // Send the proper units from the correct city to the correct boss
              var lou_suite = window.louSuite.main.getInstance();
              var dungeon = row_info['dungeon'];
              var city = row_info['city'];
              var units = row_info['units'];
              var units_to_send = [];
              
              for (var u_key in units) {
                units_to_send.push({t: u_key, c: units[u_key]});
              }
              
              this._tableModel.setValue(column, row, "");
              lou_suite.sendTroops(lou_suite.LS_RAID, cityId, dungeon['x'], dungeon['y'], units_to_send, lou_suite.LS_RAID_REPEAT, this.onTroopsSent);
              break;
          }
        },

        onTroopsSent: function (event, v) {
          if (event.getContent() == null) {
            return;
          }
          var message = "";
          var code = event.getContent();
          switch (code) {
            case 0:
              // Success
              var lou_suite = window.louSuite.main.getInstance();
              var selectedContinent = lou_suite.dungeon_raider.sbContinents.getSelection()[0].getModel();
              
              // Subtract units sent from city
              var city_id = v['cid'];
              var units_sent = v['units'];
              for (var u_key in units_sent) {
                var unit = units_sent[u_key];
                var total = lou_suite.cities[city_id]['units'][unit['t']]['count'];
                total = total - unit['c'];
                lou_suite.cities[city_id]['units'][unit['t']]['count'] = total;
              }

              // Save the city
              bos.Storage.getInstance().saveCity(lou_suite.cities[city_id]);

              // Update the view
              lou_suite.dungeon_raider.updateView();
              return;
              break;
            case 4: // Not enough units
              message = "Not enough units. Try rescanning the city.";
              break;
            case 64: // No more command slots
              message = "No more command slots. Use a different city.";
              break;
            default:
              message= "Unknown Error: " + code;
              break;
          }
          
          // Failed
          
          var lou_suite = window.louSuite.main.getInstance();
          var selectedContinent = lou_suite.dungeon_raider.sbContinents.getSelection()[0].getModel();
          
          if (message) {
            lou_suite.setMessage(message);
          }
          
          // Update the view
          lou_suite.dungeon_raider.updateView();
        },

        createCitiesContinentsSelectBox: function() {
          var sb = new qx.ui.form.SelectBox().set({
            width: 60,
            height: 28
          });
          var cities = webfrontend.data.Player.getInstance().cities;
    
          sb.setToolTipText("Filter by: <b>continents</b>");
    
          var continents = [];
          for (var cityId in cities) {
            var city = cities[cityId];
            var cont = webfrontend.data.Server.getInstance().getContinentFromCoords(city.xPos, city.yPos);
            continents["c" + cont] = true;
          }
          
          var list = [];
          for (var key in continents) {
            if (key.substring != undefined && qx.lang.Type.isString(key)) {
              var cont = parseInt(key.substring(1), 10);
              if (!isNaN(cont)) {
                list.push(cont);
              }
            }
          }
          
          list.sort();
          
          for (var i = 0; i < list.length; i++) {
            var cont = list[i];
            sb.add(new qx.ui.form.ListItem("C" + cont, null, cont));
          }
    
          return sb;
        },    

        _createToolBar: function() {
          // TODO - Add a selector for repeat or non-repeat
          var toolBar = new qx.ui.groupbox.GroupBox();
          toolBar.setLayout(new qx.ui.layout.Flow(10, 10));
          
          this.sbContinents = this.createCitiesContinentsSelectBox();
          this.sbContinents.addListener("changeSelection", function(evt) {
            this.updateView();
          }, this);
          toolBar.add(this.sbContinents);
          
          this.sbLevel = new qx.ui.form.SelectBox().set({
            width: 70,
            height: 28
          });
          
          this.sbLevel.setToolTipText("Filter by: <b>min level</b>");
          
          var min_level = localStorage.getItem("lou_suite_min_level");
          
          if (typeof min_level == 'undefined') {
            min_level = 1;
          }
          
          var levels_list = [1, 2, 3, 4, 5, 6, 7, 8, 9];
          
          for (var i = 1; i < 10; i++) {
            var item = new qx.ui.form.ListItem("Level " + i, null, i);
            this.sbLevel.add(item);
            if (i == min_level) {
              this.sbLevel.setSelection([item]);
            }
          }
          
          this.sbLevel.addListener("changeSelection", function(evt) {
            localStorage.setItem("lou_suite_min_level", this.sbLevel.getSelection()[0].getModel());
            this.updateView();
          }, this);
          toolBar.add(this.sbLevel);
          
          var btnViewRegion = new qx.ui.form.Button("View Region");
          btnViewRegion.setWidth(120);
          toolBar.add(btnViewRegion);
          btnViewRegion.addListener("execute", function(evt) {
            var selectedContinent = this.sbContinents.getSelection()[0].getModel();
            window.louSuite.main.getInstance().moveToContRegion(selectedContinent, 4);
            this.updateView();
          }, this);
          
          this.btnUpdateView = new qx.ui.form.Button("Scan");
          this.btnUpdateView.setWidth(80);
          toolBar.add(this.btnUpdateView);
          this.btnUpdateView.addListener("execute", function(evt) {
            window.louSuite.main.getInstance().scanRegion();
            this.btnUpdateView.setEnabled(false);
            this.updateView();
          }, this);
          this.btnUpdateView.setEnabled(false);
          
          var btnRecount = new qx.ui.form.Button("Recount Troops");
          btnRecount.setWidth(120);
          toolBar.add(btnRecount);
          btnRecount.addListener("execute", function(evt) {
            var server = new bos.Server();
            
            var citiesIds = [];
            
            var selectedContinent = this.sbContinents.getSelection()[0].getModel();
            // Limit by selected continent
            for (var cityId in LT.player.cities) {
              c = LT.player.cities[cityId];
              
              if (c == null) {
                continue;
              }
              
              var city_cont = webfrontend.data.Server.getInstance().getContinentFromCoords(c.xPos, c.yPos);
              // Check that the city is on the right continent
              if (city_cont != selectedContinent) {
                continue;
              }
              citiesIds.push(cityId);
            }
            server.pollCities(citiesIds);
            
            this.updateView();
          }, this);
          
          return toolBar;
        }
      }
    });

    qx.Class.define("lou_suite.gui.BossRaider", {

      extend: bos.gui.SummaryPage,

      construct: function() {

        var lou_suite = window.louSuite.main.getInstance();

        lou_suite.boss_raider = this;

        bos.gui.SummaryPage.call(this);

        this.setLabel("Boss Raider");

        this.setLayout(new qx.ui.layout.VBox(10));

        this.add(this._createToolBar());    

        this._tableModel = new qx.ui.table.model.Simple();

        var columnNames = [ "Id", "Row Info", "Type", "Level", "Pos", "Name", "Distance", "Units", "Actions"];

        var columnIds = ["id", "row_info", "boss_type", "boss_level", "position", "name", "distance", "units", "actions"];

        

        this._tableModel.setColumns(columnNames, columnIds);

        

        this._setupSorting(this._tableModel);

        this._tableModel.sortByColumn(4, true);

        

        var custom = {

          tableColumnModel : function(obj) {

            return new qx.ui.table.columnmodel.Resize(obj);

          }

        };

        

        this.table = new bos.ui.table.Table(this._tableModel, custom);

        this.table.addListener("cellClick", this._handleCellClick, this);

    

        var columnModel = this.table.getTableColumnModel();

    

        columnModel.setColumnVisible(0, false);

        columnModel.setColumnVisible(1, false);

        columnModel.setDataCellRenderer(4, new bos.ui.table.cellrenderer.ClickableLook());

        columnModel.setDataCellRenderer(5, new bos.ui.table.cellrenderer.ClickableLook());

        columnModel.setDataCellRenderer(8, new bos.ui.table.cellrenderer.ClickableLook());

        

        var tcm = this.table.getTableColumnModel();

        var resizeBehavior = tcm.getBehavior();

        resizeBehavior.setWidth(2, 60);

        resizeBehavior.setWidth(3, 20);

        resizeBehavior.setWidth(4, 50);

        resizeBehavior.setWidth(5, 100);

        resizeBehavior.setWidth(6, 70);

        resizeBehavior.setWidth(7, 80);

        resizeBehavior.setWidth(8, "1*");

        resizeBehavior.setMinWidth(8, 100);  

        

        this.add(this.table, {flex: 1});          

      }, 

      members: {

        sbContinents: null,

        btnUpdateView: null,

        sbLevel: null,

        classicView: function() {

          this.btnUpdateView.setEnabled(LT.a.visMain.mapmode == "r");

          

          var rowData = [];

          var lou_suite = window.louSuite.main.getInstance();

          var selectedContinent = this.sbContinents.getSelection()[0].getModel();

          var min_level = this.sbLevel.getSelection()[0].getModel();

          

          // Refresh the city list to get new troop counts

          lou_suite.scanAllCities();

          

          // First get all the bosses in the area for the continent we are working with

          var bosses = lou_suite.bosses;

          for (var b_key in bosses['c' + selectedContinent]) {

            var boss = bosses['c' + selectedContinent][b_key];

            if (boss['state'] && (boss['level'] >= min_level)) {

              // Then get all the cities on that continent that can hit that boss

              // Show the distance for the city to hit the boss

              

              for (var cityId in LT.player.cities) {

                c = LT.player.cities[cityId];

                

                if (c == null) {

                  continue;

                }

                

                var city_cont = webfrontend.data.Server.getInstance().getContinentFromCoords(c.xPos, c.yPos);

                // Check that the city is on the right continent

                if (city_cont != selectedContinent) {

                  continue;

                }

                

                var city = lou_suite.cities[cityId];

                if (city == undefined) {

                  continue;

                }

                

                // Check that the city has enough troops to hit the boss

                if (city.getUnitCount() == 0) {

                  continue;

                }

                

                var units = lou_suite.getCityUnits(cityId);

                // Check that there are enough units to send for this boss

                var units_to_send = this.getTroopsNeeded(boss['type'], boss['level'], units);

                if (typeof(units_to_send)=='object') {

                  var row = [];

                  

                  var units_string = "";

                  for (var u_key in units_to_send) {

                    units_string = units_string + " " + units_to_send[u_key] + " " + units[u_key]['name'];

                  }

                  

                  this._addBlankValuesToRow(row, this._tableModel);

                  row["units"] = units_string;

                  row["id"] = cityId;

                  row["row_info"] = {

                    units: units_to_send,

                    boss: boss,

                    city: c

                  };

                  row["name"] = c.name;

                  var diffX = Math.abs(c.xPos - boss['x']);

                  var diffY = Math.abs(c.yPos - boss['y']);

                  row["distance"] = Math.sqrt(diffX * diffX + diffY * diffY);

                  row["position"] = boss['x'] + ":" + boss['y'];

                  row["boss_type"] = boss['type'];

                  row["boss_level"] = boss['level'];

                  row["actions"] = "Send";

                  

                  rowData.push(row);

                }

              }

            }

          }

    

          return rowData;

        },

        createRowData: function() {

          return this.classicView();

        }, 

        getUnitsPerBoss: function () {

          var units = {
            Dragon: {
              3: [84, 500, 3334, 6667, 16667, 25000, 33334, 50000, 75000, 100000], // Ranger
              4: [250, 1500, 10000, 20000, 50000, 75000, 100000, 150000, 225000, 300000], // Guardian
              5: [100, 600, 4000, 8000, 20000, 30000, 40000, 60000, 90000, 120000], // Templar
              6: [50, 300, 2000, 4000, 10000, 15000, 20000, 30000, 45000, 60000], // Berserker
              7: [36, 215, 1429, 2858, 7143, 10715, 14286, 21429, 32143, 42858], // Mage
              9: [42, 250, 1700, 3300, 8300, 12500, 17000, 25000, 37500, 50000], // Crossbowmen
              10: [28, 167, 1134, 2200, 5534, 8334, 11334, 16667, 25000, 33334], // Paladin
              11: [19, 112, 756, 1467, 3689, 5556, 7556, 11112, 16667, 22223], // Knight
              12: [21, 125, 834, 1667, 4167, 6250, 8334, 12500, 18750, 250000], // Warlock
            },

            Moloch: {
              3: [84, 500, 3334, 6667, 16667, 25000, 33334, 50000, 75000, 100000], // Ranger
              4: [250, 1500, 10000, 20000, 50000, 75000, 100000, 150000, 225000, 300000], // Guardian
              5: [100, 600, 4000, 8000, 20000, 30000, 40000, 60000, 90000, 120000], // Templar
              6: [50, 300, 2000, 4000, 10000, 15000, 20000, 30000, 45000, 60000], // Berserker
              7: [24, 143, 972, 1886, 4743, 7143, 9715, 14286, 21429, 28572], // Mage
              9: [63, 375, 2500, 5000, 12500, 18750, 25000, 37500, 56250, 75000], // Crossbowmen
              10: [42, 250, 1667, 3334, 8334, 12500, 16667, 25000, 37500, 50000], // Paladin
              11: [28, 167, 1112, 2223, 5556, 8334, 11112, 16667, 25000, 33334], // Knight
              12: [14, 84, 567, 1100, 2767, 4167, 5667, 8664, 12500, 16667], // Warlock
            },

            Hydra: {
              3: [56, 334, 2267, 4400, 11067, 16667, 22667, 33334, 50000, 66667], // Ranger
              4: [168, 1000, 6800, 13200, 33200, 50000, 68000, 100000, 150000, 200000], // Guardian
              5: [68, 400, 2720, 5280, 13280, 20000, 27200, 40000, 60000, 80000], // Templar
              6: [34, 200, 1360, 2640, 6640, 10000, 13600, 20000, 30000, 40000], // Berserker
              7: [36, 215, 1429, 2858, 7143, 10715, 14286, 21429, 32143, 42858], // Mage
              9: [63, 375, 2500, 5000, 12500, 18750, 25000, 37500, 56250, 75000], // Crossbowmen
              10: [42, 250, 1667, 3334, 8334, 12500, 16667, 25000, 37500, 50000], // Paladin
              11: [28, 167, 1112, 2223, 5556, 8334, 11112, 16667, 25000, 33334], // Knight
              12: [21, 125, 834, 1667, 4167, 6250, 8334, 12500, 18750, 250000], // Warlock
            },

            Octopus: {
              15: [1, 4, 23, 44, 111, 167, 227, 334, 500, 667], // Frigate
              16: [2, 9, 57, 110, 277, 417, 567, 834, 1250, 1667], // Sloop
              17: [1, 1, 6, 11, 28, 42, 57, 84, 125, 167], // War Galleon
            }
          };
          // Apply the research and shrine bonuses
          var tech = webfrontend.data.Tech.getInstance();

          for (var boss_id in units) {
            var unit_counts = units[boss_id];
            for (var unit_id in unit_counts) {
              unit_count = unit_counts[unit_id];
              var bf = tech.getBonus("unitDamage", webfrontend.data.Tech.research, parseInt(unit_id));
              var be = tech.getBonus("unitDamage", webfrontend.data.Tech.shrine, parseInt(unit_id));
              var bonus = bf + be;
              for (var i = 0; i < unit_count.length; i++) {
                var units_needed = units[boss_id][unit_id][i];
                var new_units_needed = Math.ceil(units_needed / (1 + (bonus / 100)));
                units[boss_id][unit_id][i] = new_units_needed;
              }
            }
          }
          
          return units;
        },

        getTroopsNeeded: function (boss_name, boss_level, units) {
          var units_min = this.getUnitsPerBoss();
          var kill_percent = 0.0;
          var queued_units = {};

          for (var u_key in units) {
            var unit = units[u_key];
            if (unit['count'] > 0) {
              try {
                var units_needed = units_min[boss_name][u_key][boss_level - 1];
                var this_kill_percent = unit['count'] / units_needed;
                var total_kill = this_kill_percent + kill_percent;
                if (total_kill >= 1) {
                  // We have enough, return the queued units
                  var percent_needed = 1 - kill_percent;
                  units_needed = Math.ceil(units_needed * percent_needed);
                  
                  queued_units[u_key] = units_needed;
                  return queued_units;
                }
                else {
                  // Otherwise, let's add these to the queued units
                  queued_units[u_key] = unit['count'];
                  kill_percent += this_kill_percent;
                }
              }
              catch (e) {
                
              }
            }
          }
          
          // If we got this far, there isn't enough units to hit that boss
          return false;
        },

        _shouldBeIncluded: function(city) {

          return true;

        }, 

        _handleCellClick: function(event) {

          var row = event.getRow();

          var column = event.getColumn();

          var rowData = this._tableModel.getRowDataAsMap(row);

          var cityId = rowData["id"];

          var row_info = rowData["row_info"];

          var selectedContinent = this.sbContinents.getSelection()[0].getModel();

          switch (column) {

            case 5:

              LT.a.setMainView("c", cityId, -1, -1);

              break;

            case 4:

              var boss = row_info['boss'];

              LT.a.setMainView('r', 0, boss['x'] * LT.a.visMain.getTileWidth(), boss['y'] * LT.a.visMain.getTileHeight());

              break;

            case 8:

              // Send the proper units from the correct city to the correct boss

              var lou_suite = window.louSuite.main.getInstance();

              var boss = row_info['boss'];

              var city = row_info['city'];

              var units = row_info['units'];

              var units_to_send = [];

              

              for (var u_key in units) {

                units_to_send.push({t: u_key, c: units[u_key]});

              }

              

              // Show some kind of progress thing

              this._tableModel.setValue(column, row, "");

              lou_suite.sendTroops(lou_suite.LS_RAID, cityId, boss['x'], boss['y'], units_to_send, lou_suite.LS_RAID_ONCE, this.onTroopsSent);

              break;

          }

        },

        onTroopsSent: function (event, v) {

          console.log(v);

          if (event.getContent() == null) {

            console.log('invalid');

            return;

          }

          var message = "";

          var removeBoss = true;

          var code = event.getContent();

          //console.log("Code Received: " + code);

          switch (code) {

            case 0:

              // Success

              var lou_suite = window.louSuite.main.getInstance();

              var selectedContinent = lou_suite.boss_raider.sbContinents.getSelection()[0].getModel();

              

              // Subtract units sent from city

              var city_id = v['cid'];

              var units_sent = v['units'];

              for (var u_key in units_sent) {

                var unit = units_sent[u_key];

                var total = lou_suite.cities[city_id]['units'][unit['t']]['count'];

                total = total - unit['c'];

                lou_suite.cities[city_id]['units'][unit['t']]['count'] = total;

              }

              

              // Save the city

              bos.Storage.getInstance().saveCity(lou_suite.cities[city_id]);

              

              if (removeBoss) {

                // Mark the boss as taken

                lou_suite.bosses['c' + selectedContinent][v['x'] + '-' + v['y']]['state'] = false;

              }

              

              // Update the view

              lou_suite.boss_raider.updateView();

              return;

              break;

            case 33554436: // Already ordered

              message = "Boss was already taken.";

              break;

            case 33554432: // Already ordered from a different castle

              message = "Boss was already taken.";

              break;

            case 16781312: // Dungeon is closed

              message = "Boss was already taken";

              break;

            case 4: // Not enough units

              message = "Not enough units. Try rescanning the city.";

              removeBoss = false;

              break;

            case 64: // No more command slots

              message = "No more command slots. Use a different city.";

              removeBoss = false;

              break;

            default:

              message= "Unknown Error: " + code;

              break;

          }

          

          // Failed

          console.log("Failed with status code " + code);

          

          var lou_suite = window.louSuite.main.getInstance();

          var selectedContinent = lou_suite.boss_raider.sbContinents.getSelection()[0].getModel();

          

          if (message) {

            lou_suite.setMessage(message);

          }

          

          if (removeBoss) {

            // Mark the boss as taken

            lou_suite.bosses['c' + selectedContinent][v['x'] + '-' + v['y']]['state'] = false;

          }

          

          // Update the view

          lou_suite.boss_raider.updateView();

        },

        createCitiesContinentsSelectBox: function() {

          var sb = new qx.ui.form.SelectBox().set({

            width: 60,

            height: 28

          });

          var cities = webfrontend.data.Player.getInstance().cities;

    

          sb.setToolTipText("Filter by: <b>continents</b>");

    

          var continents = [];

          for (var cityId in cities) {

            var city = cities[cityId];

    

            var cont = webfrontend.data.Server.getInstance().getContinentFromCoords(city.xPos, city.yPos);

            continents["c" + cont] = true;

          }

          

          var list = [];

          for (var key in continents) {

            if (key.substring != undefined && qx.lang.Type.isString(key)) {

              var cont = parseInt(key.substring(1), 10);

              if (!isNaN(cont)) {

                list.push(cont);

              }

            }

          }

          

          list.sort();

    

          //sb.add(new qx.ui.form.ListItem("All", null, "A"));

          for (var i = 0; i < list.length; i++) {

            var cont = list[i];

            sb.add(new qx.ui.form.ListItem("C" + cont, null, cont));

          }

    

          return sb;

        },    

        _createToolBar: function() {

          var toolBar = new qx.ui.groupbox.GroupBox();

          toolBar.setLayout(new qx.ui.layout.Flow(10, 10));

          

          this.sbContinents = this.createCitiesContinentsSelectBox();

          this.sbContinents.addListener("changeSelection", function(evt) {

            this.updateView();

          }, this);

          toolBar.add(this.sbContinents);

          

          this.sbLevel = new qx.ui.form.SelectBox().set({

            width: 70,

            height: 28

          });

          

          this.sbLevel.setToolTipText("Filter by: <b>min level</b>");

          

          var min_level = localStorage.getItem("lou_suite_min_level");

          

          if (typeof min_level == 'undefined') {

            min_level = 1;

          }

          

          var levels_list = [1, 2, 3, 4, 5, 6, 7, 8, 9];

          

          for (var i = 1; i < 10; i++) {

            var item = new qx.ui.form.ListItem("Level " + i, null, i);

            this.sbLevel.add(item);

            if (i == min_level) {

              this.sbLevel.setSelection([item]);

            }

          }

          

          this.sbLevel.addListener("changeSelection", function(evt) {

            localStorage.setItem("lou_suite_min_level", this.sbLevel.getSelection()[0].getModel());

            this.updateView();

          }, this);

          toolBar.add(this.sbLevel);

          

          var btnViewRegion = new qx.ui.form.Button("View Region");

          btnViewRegion.setWidth(120);

          toolBar.add(btnViewRegion);

          btnViewRegion.addListener("execute", function(evt) {

            var selectedContinent = this.sbContinents.getSelection()[0].getModel();

            window.louSuite.main.getInstance().moveToContRegion(selectedContinent, 4);

            this.updateView();

          }, this);

          

          this.btnUpdateView = new qx.ui.form.Button("Scan");

          this.btnUpdateView.setWidth(80);

          toolBar.add(this.btnUpdateView);

          this.btnUpdateView.addListener("execute", function(evt) {

            window.louSuite.main.getInstance().scanRegion();

            this.btnUpdateView.setEnabled(false);

            this.updateView();

          }, this);

          this.btnUpdateView.setEnabled(false);

          

          var btnRecount = new qx.ui.form.Button("Recount Troops");

          btnRecount.setWidth(120);

          toolBar.add(btnRecount);

          btnRecount.addListener("execute", function(evt) {

            var server = new bos.Server();

            

            var citiesIds = [];

            

            var selectedContinent = this.sbContinents.getSelection()[0].getModel();

            // Limit by selected continent

            for (var cityId in LT.player.cities) {

              c = LT.player.cities[cityId];

              

              if (c == null) {

                continue;

              }

              

              var city_cont = webfrontend.data.Server.getInstance().getContinentFromCoords(c.xPos, c.yPos);

              // Check that the city is on the right continent

              if (city_cont != selectedContinent) {

                continue;

              }

              citiesIds.push(cityId);

            }

            server.pollCities(citiesIds);

            

            this.updateView();

          }, this);

          

          return toolBar;

        }

      }

    });

  } 

  

  function LT_checkIfLoaded() {

    try {

      if (qx === undefined) {

        window.setTimeout(LT_checkIfLoaded, 1000);

      }

      else {

        if (typeof qx != 'undefined') {

          a = qx.core.Init.getApplication(); // application

          c = a.cityInfoView;

          wdst = webfrontend.data.ServerTime.getInstance().refTime;

          if (a && c && wdst) {

            createSuite();

            window.louSuite.main.getInstance().initialize();

          } else

            window.setTimeout(LT_checkIfLoaded, 1000);

        } else {

          window.setTimeout(LT_checkIfLoaded, 1000);

        }

      }

    } catch (e) {

      if (typeof console != 'undefined') console.log(e);

      else if (window.opera) opera.postError(e);

      else GM_log(e);

    }

  }

  if (/lordofultima\.com/i.test(document.domain))

    window.setTimeout(LT_checkIfLoaded, 20000);

      

}



  // injecting, because there seem to be problems when creating game interface with unsafeWindow

  var louSuiteScript = document.createElement("script");

    txt = LS_mainFunction.toString();

    louSuiteScript.innerHTML = "(" + txt + ")();";

    louSuiteScript.type = "text/javascript";

  if (/lordofultima\.com/i.test(document.domain))

    document.getElementsByTagName("head")[0].appendChild(louSuiteScript);

})();