158 lines
5.6 KiB
Ruby
158 lines
5.6 KiB
Ruby
# 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
|