Scripting:STNE SRS

From STNE Wiki

Jump to: navigation, search

Getting short range sensor data

The CMyShip has a method to retrieve data on ships in the current sector. It's accessed through the SRS method of CMyShip. Each object is a ship in the sector, whether or nor it's yours. Because the object is based on a list, you can easily retrieve the number of ships in the sector by using the Count property of the list.

// Assume your probe and another probe is in the sector, this would return 2
Var Ship As CMyShip = new CMyShip(123456);
WriteLine (Ship.SRS.Count); // output the number of ships in the sector

Recently, LRSShipSlots was added. As of the time of this writing, it returns SRS slots count of the sector.

// Assume your probe and another probe is in the sector, this would return 1.0
Var Ship As CMyShip = new CMyShip(123456);
WriteLine (Ship.LRSShipSlots); // output the slot count of the ships in the sector

If you wanted to filter out your ships, you would need to iterate through the list of ships in SRS and not add them to an accumulator. This is accomplished by checking the CMyShip.UserID against your userid. The caveat is, it won't capture copiloted ships, that needs to be handled differently.

Var Uid As Integer = 67890; // your uid
Var Sid As Integer = 123456; // the ship to use for SRS data
Var ShipCount As Integer = 0;
Var ShipSlots As Double = 0.0;
Var StringSlots As String; // used to run time convert slots to a string formatting
Var SRSShip As CShip; // Note, this is a CShip, not a CMyShip
Var Message As CStringBuilder = New CStringBuilder(); // Avoid ineffecient string concatenations of '&'
Var Ship As CMyShip = New CMyShip(Sid);
Var iter As IEnumerator = Ship.SRS.GetEnumerator; // don't store SRS, just get the iterator directly
While (iter.MoveNext) {
  SRSShip = iter.Current; // cast back to a CShip object
  If (SRSShip.UserID <> Uid) { // This does not work for copiloted ships, just your own
    ShipCount = ShipCount + 1;
    ShipSlots = ShipSlots + SRSShip.Definition.Slots;
  }
}

StringSlots = ShipSlots;
Message.Append("Number of ships is ");
Message.Append(ShipCount);
Message.Append(", for ");
Message.Append(StringSlots.Replace(",", ".");
Message.Append(" slots.");
WriteLine (Message.ToString);

Assuming the same two probes are in the sector, the output would be “Number of ships is 1, for .5 slots.” As I stated above, this code would only show ships that were not owned by you. In order to filter out copiloted ships as well, you need to go one step further and make a list of all the ships you have control of. Luckily there's an iterator you can use to build a list of ship IDs that you control.

// Function to create and return a CIntegerList of ships under your control
Function MyShipList() As CIntegerList {
  Var List As CIntegerList = New CIntegerList();
  Var Ship As CMyShip;
  Var iter As CShipEnumerator = New CShipEnumerator();
  While (iter.Next()) {
    Ship = iter.CurrentShip;
    List.Add(Ship.ShipID);
  }
  Return List;
}

Var Sid As Integer = 123456; // the ship to use the SRS data from
Var ShipCount As Integer = 0;
Var ShipSlots As Double = 0.0;
Var StringSlots As String;
Var SRSShip As CShip; // Note, this is a CShip, not a CMyShip
Var Message As CStringBuilder = New CStringBuilder(); // Avoid ineffecient string concatenations of '&'
Var Ship As CMyShip = New CMyShip(Sid);
Var Ships As CIntegerList = MyShipList(); // retrieve the ship list (nccs only)
Var iter As IEnumerator = Ship.SRS.GetEnumerator; // get the iterator directly again
While (iter.MoveNext) {
  SRSShip = iter.Current; // recast to a CShip
  // use the list builtin method to check if a value exists within the list
  If (NOT Ships.Contains(SRSShip.ShipID)) { 
    ShipCount = ShipCount + 1;
    ShipSlots = ShipSlots + SRSShip.Definition.Slots;
  } 
}

StringSlots = ShipSlots;
Message.Append("Number of ships is ");
Message.Append(ShipCount);
Message.Append(", for ");
Message.Append(StringSlots.Replace(",", ".");
Message.Append(" slots.");
WriteLine (Message.ToString);

Note the addition of the MyShipList function. It iterates though the CShipEnumerator, which contains all the ships under your control, and adds them to a list that can then be cross referenced. The rest of the code, except for the owner check, is the same as before.

Keep in mind your colonies also have the SRS object, so it could be applied to your colonies just as easily. It's a matter of changing The variable Ship from CMyShip to CMyColony. Of course you should also change the variable names to reflect that the object is a colony not a ship.

Function MyShipList() As CIntegerList {
  Var List As CIntegerList = New CIntegerList();
  Var Ship As CMyShip;
  Var iter As CShipEnumerator = New CShipEnumerator();
  While (iter.Next()) {
    Ship = iter.CurrentShip;
    List.Add(Ship.ShipID);
  }
  Return List;
}

Var Cid As Integer = 01234; // Was Sid
Var ShipCount As Integer = 0;
Var ShipSlots As Double = 0.0;
Var StringSlots As String;
Var SRSShip As CShip; // Note, this is a CShip, not a CMyShip
Var Message As CStringBuilder = New CStringBuilder(); // Avoid ineffecient string concatenations of '&'
Var Colony As CMyColony = New CMyColony(Cid); // Was Ship
Var Ships As CIntegerList = MyShipList();
Var iter As IEnumerator = Colony.SRS.GetEnumerator;
While (iter.MoveNext) {
  SRSShip = iter.Current;
  If (NOT Ships.Contains(SRSShip.ShipID)) { 
    ShipCount = ShipCount + 1;
    ShipSlots = ShipSlots + SRSShip.Definition.Slots;
  } 
}

StringSlots = ShipSlots;
Message.Append("Number of ships is ");
Message.Append(ShipCount);
Message.Append(", for ");
Message.Append(StringSlots.Replace(",", ".");
Message.Append(" slots.");
WriteLine (Message.ToString);

By now you're ready to wrap your code into a class. For the most part, it should be accessed statically. You'll want to alter the function that filters in order to allow for either a ship or a colony. If you move the loops for SRS into its own method, and use functions for ship and colony that just handle the differences between the two, this becomes a clean and fairly easy task

// Class to return the slots and count data as a single object, thereby requiring less specific functions
Class SRSSummary {
  Var Slots As Double;
  Var Count As Integer;
  // I could have left this out,, it isn't used in the example. Included for completeness
  Function New (sid As Integer) { 
    ;
  }
  // Create and initialize the class
  Function New (c As Integer, s As Double) {
    Slots = s;
    Count = c;
  }
  // Beans style Get/Set methods for data access
  Function GetShipSlots() As Double {
    Return Slots;
  }
  Function SetShipSlots(s As Double) {
    Slots = s;
  }
  Function GetShipCount() As Integer {
    Return Count;
  }
  Function SetShipCount(c As Integer) {
    Count = c;
  }
}

// The class that provides the desired data
// Note the lack of a new function. Without it, this class is only accessible statically
Class SRSData {
  // Make a CIntegerList of NCCs under your control
  Function MyShipList() As CIntegerList {
    Var List As CIntegerList = New CIntegerList();
    Var Ship As CMyShip;
    Var iter As CShipEnumerator = New CShipEnumerator();
    While (iter.Next()) {
      Ship = iter.CurrentShip;
      List.Add(Ship.ShipID);
    }
    Return List;
  }
  // Filter (CMyShip|CMyColony).SRS so only ships you don't control show up
  Function SRSSearch(srs As CShipList) As SRSSummary {
    Var shipcount As Integer = 0;
    Var shipslots As Double = 0.0;
    Var srsship As CShip; // Note, this is a CShip, not a CMyShip
    Var summary As SRSSummary;
    Var ships As CIntegerList = SRSData.MyShipList();
    Var iter As IEnumerator = srs.GetEnumerator;
    While (iter.MoveNext) {
      srsship = iter.Current;
      If (NOT ships.Contains(srsship.ShipID)) { 
        shipcount = shipcount + 1;
        shipslots = shipslots + srsship.Definition.Slots;
      }
    }
    Return New SRSSummary(shipcount, shipslots);
  }

  // I just can't bring myself to use a method named LRS to retrieve SRS data
  // Either the name will change to match the data, or the value will change to match the name.
  // This method returns the total of ships and slots within (CMyShip|CMyColony).SRS 
  Function SRSAll(srs As CShipList) As SRSSummary {
    Var shipcount As Integer = 0;
    Var shipslots As Double = 0.0;
    Var srsship As CShip; // Note, this is a CShip, not a CMyShip
    Var summary As SRSSummary;
    Var iter As IEnumerator = srs.GetEnumerator;
    While (iter.MoveNext) {
      srsship = iter.Current;
      shipcount = shipcount + 1;
      shipslots = shipslots + srsship.Definition.Slots;
    }
    Return New SRSSummary(srs.Count, shipslots);
  }
  // Return the filtered version of SRS for a ship  
  Function GetFilteredShipSRSSummary(shipID As Integer) As SRSSummary {
    Var ship As CMyShip = New CMyShip(shipID)
    Return SRSData.SRSSearch(Ship.SRS);
  }
  // Return the filtered version of SRS for a colony
  Function GetFilteredColonySRSSummary(colonyID As Integer) As SRSSummary {
    Var Colony As CMyColony = New CMyColony(colonyID);
    Return SRSData.SRSSearch(Colony.SRS);
  }
  // Return the total version of SRS for a ship  
  Function GetShipSRSSummary(shipID As Integer) As SRSSummary {
    Var ship As CMyShip = New CMyShip(shipid);
    Return SRSData.SRSAll(Ship.SRS);
  }
  // Return the total version of SRS for a colony
  Function GetColonySRSSummary(colonyID As Integer) As SRSSummary {
    Var colony As CMyColony = New CMyColony(colonyID);
    Return SRSData.SRSAll(colony.SRS);
  }
}

// A fast output method. I could have done this within a table, but it adds unnecessary complexity for the example
Function ReportSRS (name As String, filtered As SRSSummary, total As SRSSummary) {
  Var StringSlots As String;
  Var message as New CStringBuilder();
  message.Append("SRS summary for ");
  message.Append(name);
  message.Append(": ");
  message.Append(filtered.GetShipCount());
  message.Append ("/");
  message.Append(total.GetShipCount());
  message.Append(" ships for ");
  StringSlots = filtered.GetShipSlots();
  message.Append(StringSlots.Replace(",", "."));
  message.Append("/");
  StringSlots = total.GetShipSlots();
  message.Append(StringSlots.Replace(",", "."));
  message.Append(" slots.");
  ScriptContext.WriteAppLog(message.ToString());
}  
  
// The Script Main() function.
Function Main() {
  Var summarytotal As SRSSummary; // SRS total counts
  Var summaryother As SRSSummary; // SRS filtered counts
  Var shipid As Integer = 123456; // The ship ncc to use
  var colid As Integer = 01234; // The colony ID to use
  Var ship As New CmyShip(shipID); // call the create for the objects
  Var colony As New CmyColony(colid); // ship and colony
  summarytotal = SRSData.GetShipSRSSummary(shipid); // Get the totals for the ship
  summaryother = SRSData.GetFilteredShipSRSSummary(shipid); // and the filtered values
  ReportSRS(ship.Name, summaryother, summarytotal); // show the data
  summarytotal = SRSData.GetColonySRSSummary(colid); // Totals for the colony object
  summaryother = SRSData.GetFilteredColonySRSSummary(colid); // and the filtered values
  ReportSRS(colony.Name, summaryother, summarytotal); // Show them as well
}

// Call the main function
Main();

As you can see from the code above, the SRS data from both the colonies and the ships are handled by the same method statically. This is true for both the filtered and non filtered version of the routine. Also, notice that rather than rewrite a function for each type of data, I just created a small class with beans style get/set methods to return the data. It adds a little more CPU and memory usage to do it this way, but the memory usage is easily offset by the code reuse. (you halved the amount of code by only writing 4 very minimal methods and 2 small methods instead of 4 small methods). Essentially you end up using a small amount of extra CPU for maintainable clean code. While this tutorial retrieves just the ship slots and count, from here it would be easy to extend the class to return any available data within a CShip object.

--Miltiades (En-1:56599) 06:07, 6 November 2011 (CET)

Personal tools