Search code examples
javascriptscalascreeps

Game Screeps - find filter and static typed languages (Scala) - how to facade correctly?


I am trying to port the JavaScript tutorial code to Scala.js and got stuck on filtering Structures - any suggestions would be appreciated.

Original code:

// If creep is supposed to transfer energy to a structure
if (creep.memory.working == true) {

    // Find closest spawn, extension or tower which is not full
    var structure = creep.pos.findClosestByPath(FIND_MY_STRUCTURES, {
        filter: (s) => (s.structureType == STRUCTURE_SPAWN
        || s.structureType == STRUCTURE_EXTENSION
        || s.structureType == STRUCTURE_TOWER)
        && s.energy < s.energyCapacity });

Code in Scala.js:

val structure = Option[Structure](creep.pos.findClosestByPath[Structure](FIND_MY_STRUCTURES,
FindFilter[Structure](structure => {
   (structure.structureType == STRUCTURE_SPAWN ||
    structure.structureType == STRUCTURE_EXTENSION ||
    structure.structureType == STRUCTURE_TOWER) &&
    structure.energy < structure.energyCapacity })))

The problem is that not all structure types have .energy so this won't compile, despite the fact that the filtered ones have?

I tried to define a trait HasEnergy (similar to a JavaScript interface) and using it like FindFilter[Structure with HasEnergy], which compiles, but now I am getting a type error at runtime -> TypeError: a.k is not a function.

My Facades look like this:

@js.native
trait HasEnergy extends js.Object {
  val energy: Int
  val energyCapacity: Int
}

@js.native
trait Structure extends RoomObject with HasID {
  val hits: UndefOr[Int]
  val hitsMax: UndefOr[Int]
  val id: String
  val structureType: String
  def destroy(): Int
  def isActive(): Boolean
  def notifyWhenAttacked(enabled: Boolean): Int
}

Solution

  • OK, here is the solution I came up with:

      val structure = {
        Option(creep.pos.findClosestByPath[OwnedStructureWithEnergy](FIND_MY_STRUCTURES, FindFilter[OwnedStructureWithEnergy](s =>
          ((s.structureType == STRUCTURE_SPAWN) ||
            (s.structureType == STRUCTURE_EXTENSION) ||
            (s.structureType == STRUCTURE_TOWER)) &&
            (s.energy < s.energyCapacity))))
      }
    

    It turns out there was an error in my findClosestByPath Facade. The correct version per API supports FindFilter and FindFilterAlgorithm - the former was missing from my mapping. The correct one now looks like this:

      def findClosestByPath[T](typ: Int, opts: FindFilter[T] | FindFilterAlgorithm[T] = ???): T
      @JSName("findClosestByPath")
    

    Now to use member access via dot, I added the trait OwnedStructureWithEnergy:

    @js.native
    trait OwnedStructureWithEnergy extends OwnedStructure {
      val energy: Int = js.native
      val energyCapacity: Int = js.native
    }