Desire To Leave

Every Denizen arrives with a desireToLeave. Now, each one could appear with some sort of expiry timer, but that would make their presence, aside from the initial chance of a particular one showing up at all, totally deterministic.

class Denizen: {  
  var name: String
  var desireToLeave: Int
  var chanceToAppear: Int

  init(name: String, desireToLeave: Int, chanceToAppear: Int) {
    self.name = name
    self.desireToLeave = desireToLeave
    self.chanceToAppear = chanceToAppear
  }
}

These would be the absolute minimum needed for the chance-based loading and the eventual implementation of desireToLeave's determining the denizen's exit.

public struct DenizenManager {

  static var denizens: [Denizen] = []
  private var appearanceRoll: Int { return Int(arc4random_uniform(1000)) }

We can keep all of the loading and handling of these guys in a struct.

  private func loadDenizenData() -> [Denizen] {
    var mainBundle = NSBundle.mainBundle()
    var path = mainBundle.pathForResource("Denizens", ofType: "plist")!
    var gameData: NSArray? = NSArray(contentsOfFile: path)
      if let gameData = NSArray(contentsOfFile: path) {
        var denizenData: [Denizen] = []
        for item in gameData {
          if let dict = item as? NSDictionary {
            let name = dict["name"] as! String
            let cta = dict["chanceToAppear"] as! Int
            let dtl = dict["desireToLeave"] as! Int
            let denizen = Denizen(name: name, desireToLeave: dtl, chanceToAppear: cta)
            denizenData.append(denizen)
          }
        }
        return denizenData
      }
    return []
  }

We'll keep an array of dictionaries in Denizens.plist, each one containing the info for each of the possible Denizens.

And then roll for each one of them to see which will be included in the batch to appear.

  private func loadDenizens() -> [Denizen] {
    var denizenStable: [Denizen] = []
    denizenStable = loadDenizenData().filter{$0.chanceToAppear <= self.appearanceRoll}
    return denizenStable
  }
  public func load() {
    DenizenManager.denizens = loadDenizens()
  }
}


With every update of the game loop, we could increment the desireToLeave, let's say by one. But, if we're running at a hypothetical 60fps, then setting a maximum dTL of 1,000 would only take 16.66 seconds, 10,000 would take 166 seconds, and so on.

In other words, we'd have to use a rather large number to make sure anyone stuck around for a bit...

And that brings up questions of differentiation; would we have a varying maximum dTL's for each "type" of Denizen? Or even for every single instance?

For starters, we can slow down the whole process, divorce it from the update loop, and move it and its dependent processes into our own update() function inside DenizenManager.

Then make use of NSDate, which we've already been using for all the other time based checks going on throughout the game.

public struct GameTime {  
  static var lastUpdateTime: NSDate?

Inside DenizenManager, we check to see if GameTime.lastUpdateTime has ever been set, and if it hasn't, set it to now.

  public func update() {
    let now = NSDate()
    if GameTime.lastUpdateTime == nil {
      GameTime.lastUpdateTime = now
    }
    let timeSinceUpdate = now.timeIntervalSinceDate(GameTime.lastUpdateTime!)

And then, in this example, if timeSinceUpdate, is more than five seconds, all denizens which have been loaded, have their desireToLeave incremented.

    if timeSinceUpdate >= 5.0 {
      for item in DenizenManager.denizens {
        item.desireToLeave += 1
      }
      GameTime.lastUpdateTime = now
    }
  }

Lastly, GameTime.lastUpdateTime gets reset so we can start waiting again.

Right now, desireToLeave simply increments by one, but this behavior will eventually be complicated by various multipliers/modifiers. I'm unsure if this should be taken care of by DenizenManager or from within the Denizen class.

Each Denizen type could conceivably have multiple modifier variables which the Denizens.plist would contain.

class Denizen: {  
  var name: String
  var desireToLeave: Int
  var chanceToAppear: Int
  var likesToBeOutside: Bool?
  var isOnVacationAlone: Bool?
  var hasBeenDrinking: Bool?