Handling Weather - ThePix/QuestJS GitHub Wiki

There are all sorts of ways to do this in your game; this is just a suggestion.

The system uses a set of Weather objects that link to each other in specific ways. One could describe hot weather with blue skies, and this could link to another with clouds appearing, which in turn could link to another where rain is falling.

We will store the current weather situation on the player object to ensure it gets saved. This will be in four attributes: "currentWeatherName", "currentWeatherCount", "currentWeatherTotal" and "currentWeatherDisabled".

We need something to happen every turn, where we will potentially change the current weather. It can also set the starting weather. We can do this by creating an object or dictionary with an "update" function attribute, and adding it to io.modulesToUpdate.

const weather = {
  update:function() {
    if (player.currentWeatherDisabled) return
    if (player.currentWeatherName) {
      weatherTypes[player.currentWeatherName].turn()
    }
    else {
      player.currentWeatherName = Object.keys(weatherTypes)[0]
      player.currentWeatherCount = 0
      player.currentWeatherTotal = weatherTypes[player.currentWeatherName].getNumberOfTurns()
    }  
  }
}
io.modulesToUpdate.push(weather)

All the different weather types will be stored in a dictionary. Note that this will not be saved when the user saves her game.

const weatherTypes = {}

Next we define a class for the weather types. The reason for doing this is that a lot of the code will be common to them all, and is best defined in the class definition - you only need to do it once, but you also can override it for a specific weather type. Note that the constructor adds the weather type to the dictionary we just set up.

class Weather {
  constructor(name, data) {
    this.name = name
    for (const key in data) this[key] = data[key]
    if (weatherTypes[name]) return errormsg("Two weather types called \"" + name + "\".")
    weatherTypes[name] = this
  }
  
  turn() {
    player.currentWeatherCount++
    if (this.wetness) {
      if (!player.currentWeatherWetness) player.currentWeatherWetness = 0
      player.currentWeatherWetness += this.wetness
      if (this.wetness > 100) this.wetness = 100
      if (this.wetness < 0) this.wetness = 0
      if (this.wetness > 80) this.wetness--
      if (this.wetness > 60) this.wetness--
    }
    if (player.currentWeatherCount >= player.currentWeatherTotal) {
      const currentName = this.getNext()
      log(currentName)
      const current = weatherTypes[currentName]
      player.currentWeatherName = currentName
      player.currentWeatherCount = 0
      player.currentWeatherTotal = current.getNumberOfTurns()
      if (current.start) current.start(this.name)
    }
    else {
      if (this.ongoing) this.ongoing()
    }
  }
  
  outside(includeVisible) {
    if (settings.weatherReportsAssumeYes && currentLocation.noWeather) return false
    if (!settings.weatherReportsAssumeYes && !currentLocation.yesWeather) return false
    if (includeVisible) return true
    return !currentLocation.weatherModifier
  }
  
  report(s, options) {
    if (!this.outside(true)) return false
    if (currentLocation.weatherModifier) s = currentLocation.weatherModifier.replace('#', s)
    msg(s, options)
    return true
  }
  
  getCloudCover() { return 100 }
}

You may need to tweak that if there are other things that interest you, such as temperature or wind speed or fog or snow. This handles rain and cloud cover and that is about it.

Now we need the weather types. I am just doing three here, but you can do hundred if you want.

new Weather("hot", {
  getNumberOfTurns:function() { return 3 },
  getNext:function() { return random.fromArray(['hot', 'cloudingOver', 'stormPrelude']) },
  getDescription:function() { return "It is hot!" },
  getCloudCover:function() { return 0 },
  wetness:-1,
})
new Weather("cloudingOver", {
  getNumberOfTurns:function() { return 3 },
  getNext:function() { return random.fromArray(['rain', 'clearing', 'drizzle', 'cloudy']) },
  getDescription:function() { return "It is getting cloudy." },
  getCloudCover:function() { return Math.round(player.currentWeatherCount / player.currentWeatherTotal * 100) },
  start:function() { this.report("It is starting to get cloudy.") },
})
new Weather("rain", {
  getNumberOfTurns:function() { return 3 },
  getNext:function() { return random.fromArray(['rain', 'clearing', 'cloudy']) },
  getDescription:function() { return "It is raining." },
  start:function() { this.report("It is starting to rain.") },
  wetness:2,
})

The important attributes:

  • getNumberOfTurns: A function that returns the number of turns that this weather type will last for. You might want a random number here.
  • getNext: A function that returns a string that is the name of the next weather type. It could always return the same string, or pick one randomly from a set, or decide based on the season or time of day.
  • getDescription: A function that returns a descriptive phrase for the current weather.
  • getCloudCover: A function that returns a number from 0 to 100, the percentage cloud cover.
  • wetness: A number used to determine how wet everything is, ranged from -1 (drying out) to 5 (deluge).
  • start: A function run when the weather type starts, and can be used to note a change in the weather.
  • ongoing: A function run each turn this weather type is running; could be used randomly to note lightning.
  • report: Use this to print weather messages; it will test if the weather can be see.