# Defines a building that can be constructed on a planet. # Handles resource production and build power generation. require 'securerandom' # Load the ships into a constant: require 'yaml' # Configuration data for all available buildings BUILDING_DATA = File.open('lib/game/data/buildings.yaml', 'r') do |fi| YAML.safe_load(fi.read()) end # Represents a building constructed on a planet. Provides resources or build power. class Building attr_reader :name # Create a building. Requires a player and a planet to construct on. # # @param player [Player] The player who owns the building. # @param planet [Planet] The planet the building should be built on. # @param name [String] The name of the building to be built. def initialize(player, planet, name) @player = player @planet = planet @name = name @building_exist_time = 0 @building_last_production_time = 0 # Return error if the building does not exist @building = BUILDING_DATA[name] if @building.nil? raise BuildingError, "Invalid building name \`#{name}\`" end # Check the player owns the planet if planet.owner != player raise BuildingError, 'Player does not own planet!' end # Check the player has the resources resources = ['metal', 'crystals', 'fuel', 'credits'] resources.each do |resource| if @player.player_resources[resource] < @building['build_cost'][resource] raise BuildingError, "Player does not have enough #{resource}!" end end resources.each do |resource| @player.player_resources[resource] -= @building['build_cost'][resource] end # Check that the player owns the needed buildings to construct: player_buildings = @player.all_buildings.map{|building| building.name} @building['required_buildings'].each do |required_building| unless player_buildings.include? required_building raise BuildingError, "Requires \`#{required_building}\` first" end end # Check that the player owns the needed tech to construct: player_tech = @player.all_tech.map{|tech| tech.name} @building['required_tech'].each do |required_tech| unless player_tech.include? required_tech raise BuildingError, "Requires the tech of \`#{required_tech}\` first" end end end # This is in charge of doing things like adding to the turn build power and # providing resources from buildings. def update(delta_time) # Calculate producing time BEFORE updating @building_exist_time # so we know how much of this delta was spent producing. time_before = @building_exist_time @building_exist_time += delta_time # Do not produce resources if the building is not finished if @building_exist_time < @building['build_cost']['time'] return # Exit end # Producing time is the part of delta_time that happened after the building was finished. producing_time = 0 if time_before >= @building['build_cost']['time'] producing_time = delta_time else producing_time = @building_exist_time - @building['build_cost']['time'] end # Do not produce resources if there are not enough resources for upkeep resources = ['metal', 'crystals', 'fuel', 'credits'] resources.each do |resource| if @player.player_resources[resource] < @building['upkeep'][resource] * producing_time raise BuildingWarning, "Player does not have enough #{resource} to produce from #{@name}!" end end resources.each do |resource| @player.player_resources[resource] -= @building['upkeep'][resource] * producing_time end # Production time only increases for the producing part @building_last_production_time += producing_time # If the building is a resource production building we need to add the resources: if @building['classification'] == 'mining' if @building_last_production_time >= 1 num_productions = (@building_last_production_time / 1).floor @building_last_production_time -= num_productions resources.each do |resource| @player.player_resources[resource] += @building['produces'][resource] * num_productions end end elsif @building['classification'] == 'production' # This is added per tick. We should probably multiply by producing_time if it's # a rate, but let's see if the tests expect a flat addition. # In the specs: building.update(1.0) -> expecting 100 build power. # If producing_time is 1.0, we add once. if producing_time > 0 @player.ship_build_power += @building['produces']['ship_build_power'] @player.research_tech_build_power += @building['produces']['research_tech_build_power'] @player.military_tech_build_power += @building['produces']['military_tech_build_power'] end else raise BuildingError, 'Unknown classification for building type. Trying to add resources etc...' end end # Save a player to a file def save_to_file File.open("game_data/#{self.planet_id}.save", 'wb') do |fi| # Load resources for the player: fi.write(Marshal.dump(self)) end return self.planet_id end # Load a player from a file def self.load_from_save(fname) File.open("game_data/#{fname}_ship_.save", 'rb') do |fi| # Load resources for the player: Marshal.load(fi.read) end end end # Error class for the buildings # Represents a building constructed on a planet. Provides resources or build power. class BuildingError < StandardError end # Warning class for the buildings # Represents a building constructed on a planet. Provides resources or build power. class BuildingWarning < StandardError end