Exemplo n.º 1
0
	def initialize(self):
		self.__init()
		# add production lines as specified in db.
		if self.__auto_init:
			for prod_line, attributes in self.production_lines.iteritems():
				if 'enabled_by_default' in attributes and not attributes['enabled_by_default']:
					continue  # It's set to False, don't add
				prod = self.create_production(prod_line)
				self.add_production(prod)
		# For newly built producers we set the utilization to full for the first
		# few seconds, this avoids the low productivity icon being shown every
		# time a new producer is built
		temp_util = self.__utilization
		self.__utilization = FullUtilization()
		Scheduler().add_new_object(Callback(self.__set_utilization, temp_util), self, Scheduler().get_ticks(15))
Exemplo n.º 2
0
	def initialize(self):
		self.__init()
		# add production lines as specified in db.
		if self.__auto_init:
			for prod_line, attributes in self.production_lines.iteritems():
				if 'enabled_by_default' in attributes and not attributes['enabled_by_default']:
					continue  # It's set to false, don't add
				prod = self.create_production(prod_line)
				self.add_production(prod)
		# For newly built producers we set the utilization to full for the first
		# few seconds, this avoids the low productivity icon being shown every
		# time a new producer is built
		temp_util = self.__utilization
		self.__utilization = FullUtilization()
		Scheduler().add_new_object(Callback(self.__set_utilization, temp_util), self, Scheduler().get_ticks(15))
Exemplo n.º 3
0
class Producer(Component):
	"""Class for objects, that produce something.
	@param auto_init: bool. If True, the producer automatically adds one
	production for each production_line.
	"""
	log = logging.getLogger("world.production")

	NAME = "producer"
	DEPENDENCIES = [StorageComponent]

	utilization_mapping = {
	    'FieldUtilization': FieldUtilization,
	    'FullUtilization': FullUtilization
	}

	production_class = Production

	# INIT
	def __init__(self, auto_init=True, start_finished=False, productionlines=None,
	             utilization_calculator=None, is_mine_for=None, settler_upgrade_lines=None,
	             **kwargs):
		"""
		@param productionline: yaml-dict for prod line data. Must not be changed since it is cached.
		@param utilization_calculator: one of utilization_mapping
		@param settler_upgrade_lines: data for settler upgrades. can one day be generalized to other upgrades
		"""
		if productionlines is None:
			productionlines = {}
		super(Producer, self).__init__(**kwargs)
		self.__auto_init = auto_init
		self.__start_finished = start_finished
		self.production_lines = productionlines
		assert utilization_calculator is not None
		self.__utilization = utilization_calculator

		if settler_upgrade_lines:
			from horizons.world.building.settler import SettlerUpgradeData
			self.settler_upgrade_lines = SettlerUpgradeData(self, settler_upgrade_lines)

			self.production_lines = self.production_lines.copy()
			self.production_lines.update(self.settler_upgrade_lines.get_production_lines())
		else:
			self.settler_upgrade_lines = None


	def __init(self):
		# we store productions in 2 dicts, one for the active ones, and one for the inactive ones.
		# the inactive ones won't get considered for needed_resources and such.
		# the production_line id is the key in the dict (=> a building must not have two identical
		# production lines)
		self._productions = {}
		self._inactive_productions = {}
		# Store whether or not the producer is active
		self.__active = True
		# Store whether or not the utilization level is currently ok
		self.__utilization_ok = True

		# BIG FAT NOTE: this has to be executed for all players for mp
		# even if this building has no status icons
		# TODO: think about whether this is enough gui-related so it belongs to the ExtScheduler
		# also check its performance when moving
		interval = Scheduler().get_ticks(3)
		run_in = self.session.random.randint(1, interval) # don't update all at once
		if self.instance.has_status_icon:
			Scheduler().add_new_object(self.update_capacity_utilization, self, run_in=run_in,
			                           loops=-1, loop_interval=interval)

	def initialize(self):
		self.__init()
		# add production lines as specified in db.
		if self.__auto_init:
			for prod_line, attributes in self.production_lines.iteritems():
				if 'enabled_by_default' in attributes and not attributes['enabled_by_default']:
					continue  # It's set to false, don't add
				prod = self.create_production(prod_line)
				self.add_production(prod)
		# For newly built producers we set the utilization to full for the first
		# few seconds, this avoids the low productivity icon being shown every
		# time a new producer is built
		temp_util = self.__utilization
		self.__utilization = FullUtilization()
		Scheduler().add_new_object(Callback(self.__set_utilization, temp_util), self, Scheduler().get_ticks(15))

	def get_production_lines_by_level(self, level):
		prod_lines = []
		for key, data in self.production_lines.iteritems():
			if 'level' in data and level in data['level']:
				prod_lines.append(key)
		return prod_lines

	def create_production(self, id, load=False):
		"""
		@param id: production line id
		@param load: whether the production is used for loading.
		"""
		data = self.production_lines[id]
		production_class = self.production_class
		owner_inventory = self.instance._get_owner_inventory()

		# not really fancy way of selecting special production class
		if self.settler_upgrade_lines:
			if id == self.settler_upgrade_lines.get_production_line_id(self.instance.level+1):
				production_class = SingleUseProduction

		return production_class(inventory=self.instance.get_component(StorageComponent).inventory,
		                        owner_inventory=owner_inventory, prod_id=id, prod_data=data,
		                        load=load, start_finished=self.__start_finished)

	def add_production_by_id(self, production_line_id):
		"""Convenience method.
		@param production_line_id: Production line from db
		"""
		production = self.create_production(production_line_id)
		self.add_production( production )
		return production

	def update_capacity_utilization(self):
		"""Called by the scheduler to update the utilization regularly"""
		if not self.capacity_utilization_below(ProductivityLowStatus.threshold) is not self.__utilization_ok:
			self.__utilization_ok = not self.__utilization_ok
			if self.__utilization_ok:
				RemoveStatusIcon.broadcast(self, self.instance, ProductivityLowStatus)
			else:
				icon = ProductivityLowStatus(self.instance)
				AddStatusIcon.broadcast(self, icon)

	@property
	def capacity_utilization(self):
		return self.__utilization.capacity_utilization(self)

	def capacity_utilization_below(self, limit):
		return self.__utilization.capacity_utilization_below(limit, self)

	def load(self, db, worldid):
		# Call this before super, because we have to make sure this is called before the
		# ConcreteObject's callback which is added during loading
		Scheduler().add_new_object(self._on_production_change, self, run_in=0)
		super(Producer, self).load(db, worldid)
		# load all productions
		self.__init()
		for line_id in db.get_production_lines_by_owner(worldid):
			production = self.create_production(line_id, load=True)
			assert isinstance(production, Production)
			production.load(db, worldid)
			self.add_production(production)

		self._update_decommissioned_icon()

	def save(self, db):
		super(Producer, self).save(db)
		for production in self.get_productions():
			production.save(db, self.instance.worldid)

	# INTERFACE
	def add_production(self, production):
		assert isinstance(production, Production)
		self.log.debug('%s: added production line %s', self, production.get_production_line_id())
		if production.is_paused():
			self.log.debug('%s: added production line %s is paused', self, production.get_production_line_id())
			self._inactive_productions[production.get_production_line_id()] = production
		else:
			self.log.debug('%s: added production line %s is active', self, production.get_production_line_id())
			self._productions[production.get_production_line_id()] = production
		production.add_production_finished_listener(self._production_finished)
		# This would be called multiple times during init, just add it later this tick.
		# It also ensures that the changelistener would stick, we used to readd
		# the listener in load(), which was explained by this comment:
			# Listener has been removed in the productions.load(), because the
			# changelistener's load is called
		Scheduler().add_new_object(
		  Callback(production.add_change_listener, self._on_production_change, call_listener_now=True), self, run_in=0
		)
		self.instance._changed()

	def _production_finished(self, production):
		"""Gets called when a production finishes. Intercepts call, adds info
		and forwards it"""
		produced_resources = production.get_produced_resources()
		self.on_production_finished(produced_resources)

	def finish_production_now(self):
		"""Cheat, makes current production finish right now (and produce the resources).
		Useful to make trees fully grown at game start."""
		for production in self._productions.itervalues():
			production.finish_production_now()

	def has_production_line(self, prod_line_id):
		"""Checks if this instance has a production with a certain production line id"""
		return bool( self._get_production(prod_line_id) )

	def remove_production(self, production):
		"""Removes a production instance.
		@param production: Production instance"""
		production.remove() # production "destructor"
		if self.is_active(production):
			del self._productions[production.get_production_line_id()]
			# update decommissioned icon after removing production
			self._update_decommissioned_icon()

			self.instance._changed()
			self.on_activity_changed(self.is_active())

		else:
			del self._inactive_productions[production.get_production_line_id()]

	def remove_production_by_id(self, prod_line_id):
		"""
		Convenience method. Assumes, that this production line id has been added to this instance.
		@param prod_line_id: production line id to remove
		"""
		self.remove_production( self._get_production(prod_line_id) )

	def alter_production_time(self, modifier, prod_line_id=None):
		"""Multiplies the original production time of all production lines by modifier
		@param modifier: a numeric value
		@param prod_line_id: id of production line to alter. None means every production line"""
		if prod_line_id is None:
			for production in self.get_productions():
				production.alter_production_time(modifier)
		else:
			self._get_production(prod_line_id).alter_production_time(modifier)

	def remove(self):
		Scheduler().rem_all_classinst_calls(self)
		for production in self.get_productions():
			self.remove_production(production)
		# call super() after removing all productions since it removes the instance (make it invalid)
		# which can be needed by changelisteners' actions (e.g. in remove_production method)
		super(Producer, self).remove()
		assert not self.get_productions(), 'Failed to remove %s ' % self.get_productions()


	# PROTECTED METHODS
	def _get_current_state(self):
		"""Returns the current state of the producer. It is the most important
		state of all productions combined. Check the PRODUCTION.STATES constant
		for list of states and their importance."""
		current_state = PRODUCTION.STATES.none
		for production in self.get_productions():
			state = production.get_animating_state()
			if state is not None and current_state < state:
				current_state = state
		return current_state

	def get_productions(self):
		"""Returns all productions, inactive and active ones, as list"""
		return self._productions.values() + self._inactive_productions.values()

	def get_production_lines(self):
		"""Returns all production lines that have been added.
		@return: a list of prodline ids"""
		return self._productions.keys() + self._inactive_productions.keys()

	def _get_production(self, prod_line_id):
		"""Returns a production of this producer by a production line id.
		@return: instance of Production or None"""
		if prod_line_id in self._productions:
			return self._productions[prod_line_id]
		elif prod_line_id in self._inactive_productions:
			return self._inactive_productions[prod_line_id]
		else:
			return None

	def is_active(self, production=None):
		"""Checks if a production, or the at least one production if production is None, is active"""
		if production is None:
			for production in self.get_productions():
				if not production.is_paused():
					return True
			return False
		else:
			assert production.get_production_line_id() in self._productions or \
			       production.get_production_line_id() in self._inactive_productions
			return not production.is_paused()

	def set_active(self, production=None, active=True):
		"""Pause or unpause a production (aka set it active/inactive).
		see also: is_active, toggle_active
		@param production: instance of Production. if None, we do it to all productions.
		@param active: whether to set it active or inactive"""
		if production is None:
			# set all
			for production in self.get_productions():
				self.set_active(production, active)
		else:
			line_id = production.get_production_line_id()
			if active:
				if not self.is_active(production):
					self.log.debug("ResHandler %s: reactivating production %s", self.instance.worldid, line_id)
					self._productions[line_id] = production
					del self._inactive_productions[line_id]
					production.pause(pause=False)
			else:
				if self.is_active(production):
					self.log.debug("ResHandler %s: deactivating production %s", self.instance.worldid, line_id)
					self._inactive_productions[line_id] = production
					del self._productions[line_id]
					production.pause()
			self._update_decommissioned_icon()

		self.instance._changed()
		self.on_activity_changed(self.is_active())

	def _update_decommissioned_icon(self):
		"""Add or remove decommissioned icon."""
		if not self.instance.has_status_icon:
			return

		if self.is_active() is not self.__active:
			self.__active = not self.__active
			if self.__active:
				RemoveStatusIcon.broadcast(self, self.instance, DecommissionedStatus)
			else:
				icon = DecommissionedStatus(self.instance)
				AddStatusIcon.broadcast(self, icon)

	def toggle_active(self, production=None):
		if production is None:
			for production in self.get_productions():
				self.toggle_active(production)
		else:
			active = self.is_active(production)
			self.set_active(production, active = not active)

	def _on_production_change(self):
		"""Makes the instance act according to the producers
		current state"""
		state = self._get_current_state()
		new_action = 'idle'
		if state is PRODUCTION.STATES.producing:
			new_action = "work"
		elif state is PRODUCTION.STATES.inventory_full:
			new_action = "idle_full"

		# don't force restarts as not to disturb sequences such as tree growth
		self.instance.act(new_action, repeating=True, force_restart=False)

		if self.instance.has_status_icon:
			full = state is PRODUCTION.STATES.inventory_full
			if full and not hasattr(self, "_producer_status_icon"):
				affected_res = set() # find them:
				for prod in self.get_productions():
					affected_res = affected_res.union( prod.get_unstorable_produced_res() )
				self._producer_status_icon = InventoryFullStatus(self.instance, affected_res)
				AddStatusIcon.broadcast(self, self._producer_status_icon)

			if not full and hasattr(self, "_producer_status_icon"):
				RemoveStatusIcon.broadcast(self, self.instance, InventoryFullStatus)
				del self._producer_status_icon

	def get_status_icons(self):
		l = super(Producer, self).get_status_icons()
		if self.capacity_utilization_below(ProductivityLowStatus.threshold):
			l.append( ProductivityLowStatus() )
		return l

	def __str__(self):
		return "Producer(owner: " + str(self.instance) + ")"

	def get_production_progress(self):
		"""Returns the current progress of the active production."""
		for production in self._productions.itervalues():
			# Always return first production
			return production.progress
		for production in self._inactive_productions.itervalues():
			# try inactive ones, if no active ones are found
			# this makes e.g. the boatbuilder's progress bar constant when you pause it
			return production.progress
		return 0 # No production available

	def __set_utilization(self, utilization):
		self.__utilization = utilization

	@classmethod
	def get_instance(cls, arguments=None):
		arguments = arguments and arguments.copy() or {}

		utilization = None
		if 'utilization' in arguments:
			if arguments['utilization'] in cls.utilization_mapping:
				utilization = cls.utilization_mapping[arguments['utilization']]()
			del arguments['utilization']
		else:
			utilization = Utilization()

		if arguments.get('is_mine_for'):
			# this is more of an aspect than an actual subclass, but python doesn't allow
			# fast aspect-oriented programming
			cls = MineProducer

		return cls(utilization_calculator=utilization, **arguments)
Exemplo n.º 4
0
class Producer(Component):
    """Class for objects, that produce something.
	@param auto_init: bool. If True, the producer automatically adds one
	production for each production_line.
	"""
    log = logging.getLogger("world.production")

    NAME = "producer"
    DEPENDENCIES = [StorageComponent]

    utilization_mapping = {
        'FieldUtilization': FieldUtilization,
        'FullUtilization': FullUtilization
    }

    produces_resource = True
    production_class = Production

    # INIT
    def __init__(self,
                 auto_init=True,
                 start_finished=False,
                 productionlines=None,
                 utilization_calculator=None,
                 is_mine_for=None,
                 settler_upgrade_lines=None,
                 **kwargs):
        """
		@param productionline: yaml-dict for prod line data. Must not be changed since it is cached.
		@param utilization_calculator: one of utilization_mapping
		@param settler_upgrade_lines: data for settler upgrades. can one day be generalized to other upgrades
		"""
        if productionlines is None:
            productionlines = {}
        super(Producer, self).__init__(**kwargs)
        self.__auto_init = auto_init
        self.__start_finished = start_finished
        self.production_lines = productionlines
        assert utilization_calculator is not None
        self.__utilization = utilization_calculator

        if settler_upgrade_lines:
            from horizons.world.building.settler import SettlerUpgradeData
            self.settler_upgrade_lines = SettlerUpgradeData(
                self, settler_upgrade_lines)

            self.production_lines = self.production_lines.copy()
            self.production_lines.update(
                self.settler_upgrade_lines.get_production_lines())
        else:
            self.settler_upgrade_lines = None

    def __init(self):
        # we store productions in 2 dicts, one for the active ones, and one for the inactive ones.
        # the inactive ones won't get considered for needed_resources and such.
        # the production_line id is the key in the dict (=> a building must not have two identical
        # production lines)
        self._productions = {}
        self._inactive_productions = {}
        # Store whether or not the producer is active
        self.__active = True
        # Store whether or not the utilization level is currently ok
        self.__utilization_ok = True
        # Track if the producer is being removed
        self.__removal_started = False

        # BIG FAT NOTE: this has to be executed for all players for mp
        # even if this building has no status icons
        # TODO: think about whether this is enough gui-related so it belongs to the ExtScheduler
        # also check its performance when moving
        interval = Scheduler().get_ticks(3)
        run_in = self.session.random.randint(
            1, interval)  # don't update all at once
        if self.instance.has_status_icon:
            Scheduler().add_new_object(self.update_capacity_utilization,
                                       self,
                                       run_in=run_in,
                                       loops=-1,
                                       loop_interval=interval)

    def initialize(self):
        self.__init()
        # add production lines as specified in db.
        if self.__auto_init:
            for prod_line, attributes in self.production_lines.items():
                if 'enabled_by_default' in attributes and not attributes[
                        'enabled_by_default']:
                    continue  # It's set to False, don't add
                prod = self.create_production(prod_line)
                self.add_production(prod)
        # For newly built producers we set the utilization to full for the first
        # few seconds, this avoids the low productivity icon being shown every
        # time a new producer is built
        temp_util = self.__utilization
        self.__utilization = FullUtilization()
        Scheduler().add_new_object(Callback(self.__set_utilization, temp_util),
                                   self,
                                   Scheduler().get_ticks(15))

    def get_production_lines_by_level(self, level):
        prod_lines = []
        for key, data in self.production_lines.items():
            if 'level' in data and level in data['level']:
                prod_lines.append(key)
        return prod_lines

    def create_production(self, id, load=False):
        """
		@param id: production line id
		@param load: whether the production is used for loading.
		"""
        data = self.production_lines[id]
        production_class = self.production_class
        owner_inventory = self.instance._get_owner_inventory()

        # not really fancy way of selecting special production class
        if self.settler_upgrade_lines:
            if id == self.settler_upgrade_lines.get_production_line_id(
                    self.instance.level + 1):
                production_class = SingleUseProduction

        return production_class(
            inventory=self.instance.get_component(StorageComponent).inventory,
            owner_inventory=owner_inventory,
            prod_id=id,
            prod_data=data,
            load=load,
            start_finished=self.__start_finished)

    def create_production_line(self, id):
        """Creates a production line instance, this is meant only for data transfer and READONLY use!
		If you want to use production lines for anything else, go the proper way of the production class."""
        assert id in self.production_lines
        data = self.production_lines[id]
        return ProductionLine(id, data)

    def add_production_by_id(self, production_line_id):
        """Convenience method.
		@param production_line_id: Production line from db
		"""
        production = self.create_production(production_line_id)
        self.add_production(production)
        return production

    def update_capacity_utilization(self):
        """Called by the scheduler to update the utilization regularly"""
        if not self.capacity_utilization_below(
                ProductivityLowStatus.threshold) is not self.__utilization_ok:
            self.__utilization_ok = not self.__utilization_ok
            if self.__utilization_ok:
                RemoveStatusIcon.broadcast(self, self.instance,
                                           ProductivityLowStatus)
            else:
                self._add_status_icon(ProductivityLowStatus(self.instance))

    @property
    def capacity_utilization(self):
        return self.__utilization.capacity_utilization(self)

    def capacity_utilization_below(self, limit):
        return self.__utilization.capacity_utilization_below(limit, self)

    def load(self, db, worldid):
        # Call this before super, because we have to make sure this is called before the
        # ConcreteObject's callback which is added during loading
        Scheduler().add_new_object(self._on_production_change, self, run_in=0)
        super(Producer, self).load(db, worldid)
        # load all productions
        self.__init()
        for line_id in db.get_production_lines_by_owner(worldid):
            production = self.create_production(line_id, load=True)
            assert isinstance(production, Production)
            production.load(db, worldid)
            self.add_production(production)

        self._update_decommissioned_icon()

    def save(self, db):
        super(Producer, self).save(db)
        for production in self.get_productions():
            production.save(db, self.instance.worldid)

    # INTERFACE
    def add_production(self, production):
        assert isinstance(production, Production)
        self.log.debug('%s: added production line %s', self,
                       production.get_production_line_id())
        if production.is_paused():
            self.log.debug('%s: added production line %s is paused', self,
                           production.get_production_line_id())
            self._inactive_productions[
                production.get_production_line_id()] = production
        else:
            self.log.debug('%s: added production line %s is active', self,
                           production.get_production_line_id())
            self._productions[production.get_production_line_id()] = production
        production.add_production_finished_listener(self._production_finished)
        # This would be called multiple times during init, just add it later this tick.
        # It also ensures that the changelistener would stick, we used to readd
        # the listener in load(), which was explained by this comment:
        # Listener has been removed in the productions.load(), because the
        # changelistener's load is called
        Scheduler().add_new_object(Callback(production.add_change_listener,
                                            self._on_production_change,
                                            call_listener_now=True),
                                   self,
                                   run_in=0)
        self.instance._changed()

    def _production_finished(self, production):
        """Gets called when a production finishes. Intercepts call, adds info
		and forwards it"""
        produced_resources = production.get_produced_resources()
        self.on_production_finished(produced_resources)

    def finish_production_now(self):
        """Cheat, makes current production finish right now (and produce the resources).
		Useful to make trees fully grown at game start."""
        for production in self._productions.values():
            production.finish_production_now()

    def has_production_line(self, prod_line_id):
        """Checks if this instance has a production with a certain production line id"""
        return bool(self._get_production(prod_line_id))

    def remove_production(self, production):
        """Removes a production instance.
		@param production: Production instance"""
        production.remove()  # production "destructor"
        if self.is_active(production):
            del self._productions[production.get_production_line_id()]
            # update decommissioned icon after removing production
            self._update_decommissioned_icon()

            self.instance._changed()
            self.on_activity_changed(self.is_active())

        else:
            del self._inactive_productions[production.get_production_line_id()]

    def remove_production_by_id(self, prod_line_id):
        """
		Convenience method. Assumes, that this production line id has been added to this instance.
		@param prod_line_id: production line id to remove
		"""
        self.remove_production(self._get_production(prod_line_id))

    def alter_production_time(self, modifier, prod_line_id=None):
        """Multiplies the original production time of all production lines by modifier
		@param modifier: a numeric value
		@param prod_line_id: id of production line to alter. None means every production line"""
        if prod_line_id is None:
            for production in self.get_productions():
                production.alter_production_time(modifier)
        else:
            self._get_production(prod_line_id).alter_production_time(modifier)

    def remove(self):
        self.__removal_started = True
        Scheduler().rem_all_classinst_calls(self)
        for production in self.get_productions():
            self.remove_production(production)
        # call super() after removing all productions since it removes the instance (make it invalid)
        # which can be needed by changelisteners' actions (e.g. in remove_production method)
        super(Producer, self).remove()
        assert not self.get_productions(), 'Failed to remove {} '.format(
            self.get_productions())

    # PROTECTED METHODS
    def _get_current_state(self):
        """Returns the current state of the producer. It is the most important
		state of all productions combined. Check the PRODUCTION.STATES constant
		for list of states and their importance."""
        current_state = PRODUCTION.STATES.none
        for production in self.get_productions():
            state = production.get_animating_state()
            if state is not None and current_state < state:
                current_state = state
        return current_state

    def get_productions(self):
        """Returns all productions, inactive and active ones, as list"""
        return list(self._productions.values()) + list(
            self._inactive_productions.values())

    def get_production_lines(self):
        """Returns all production lines that have been added.
		@return: a list of prodline ids"""
        return list(self._productions.keys()) + list(
            self._inactive_productions.keys())

    def _get_production(self, prod_line_id):
        """Returns a production of this producer by a production line id.
		@return: instance of Production or None"""
        if prod_line_id in self._productions:
            return self._productions[prod_line_id]
        elif prod_line_id in self._inactive_productions:
            return self._inactive_productions[prod_line_id]
        else:
            return None

    def is_active(self, production=None):
        """Checks if a production, or the at least one production if production is None, is active"""
        if production is None:
            for production in self.get_productions():
                if not production.is_paused():
                    return True
            return False
        else:
            assert production.get_production_line_id() in self._productions or \
                   production.get_production_line_id() in self._inactive_productions
            return not production.is_paused()

    def set_active(self, production=None, active=True):
        """Pause or unpause a production (aka set it active/inactive).
		see also: is_active, toggle_active
		@param production: instance of Production. if None, we do it to all productions.
		@param active: whether to set it active or inactive"""
        if production is None:
            # set all
            for production in self.get_productions():
                self.set_active(production, active)
        else:
            line_id = production.get_production_line_id()
            if active:
                if not self.is_active(production):
                    self.log.debug("ResHandler %s: reactivating production %s",
                                   self.instance.worldid, line_id)
                    self._productions[line_id] = production
                    del self._inactive_productions[line_id]
                    production.pause(pause=False)
            else:
                if self.is_active(production):
                    self.log.debug("ResHandler %s: deactivating production %s",
                                   self.instance.worldid, line_id)
                    self._inactive_productions[line_id] = production
                    del self._productions[line_id]
                    production.pause()
            self._update_decommissioned_icon()

        self.instance._changed()
        self.on_activity_changed(self.is_active())

    def _add_status_icon(self, icon):
        if not self.__removal_started:
            AddStatusIcon.broadcast(self, icon)

    def _update_decommissioned_icon(self):
        """Add or remove decommissioned icon."""
        if not self.instance.has_status_icon:
            return

        if self.is_active() is not self.__active:
            self.__active = not self.__active
            if self.__active:
                RemoveStatusIcon.broadcast(self, self.instance,
                                           DecommissionedStatus)
            else:
                self._add_status_icon(DecommissionedStatus(self.instance))

    def toggle_active(self, production=None):
        if production is None:
            for production in self.get_productions():
                self.toggle_active(production)
        else:
            active = self.is_active(production)
            self.set_active(production, active=not active)

    def _on_production_change(self):
        """Makes the instance act according to the producers
		current state"""
        state = self._get_current_state()
        new_action = 'idle'
        if state is PRODUCTION.STATES.producing:
            new_action = "work"
        elif state is PRODUCTION.STATES.inventory_full:
            new_action = "idle_full"

        # don't force restarts as not to disturb sequences such as tree growth
        self.instance.act(new_action, repeating=True, force_restart=False)

        if self.instance.has_status_icon:
            full = state is PRODUCTION.STATES.inventory_full
            if full and not hasattr(self, "_producer_status_icon"):
                affected_res = set()  # find them:
                for prod in self.get_productions():
                    affected_res = affected_res.union(
                        prod.get_unstorable_produced_res())
                self._producer_status_icon = InventoryFullStatus(
                    self.instance, affected_res)
                self._add_status_icon(self._producer_status_icon)

            if not full and hasattr(self, "_producer_status_icon"):
                RemoveStatusIcon.broadcast(self, self.instance,
                                           InventoryFullStatus)
                del self._producer_status_icon

    def get_status_icons(self):
        l = super(Producer, self).get_status_icons()
        if self.capacity_utilization_below(ProductivityLowStatus.threshold):
            l.append(ProductivityLowStatus())
        return l

    def __str__(self):
        return 'Producer(owner: ' + str(self.instance) + ')'

    def get_production_progress(self):
        """Returns the current progress of the active production."""
        for production in self._productions.values():
            # Always return first production
            return production.progress
        for production in self._inactive_productions.values():
            # try inactive ones, if no active ones are found
            # this makes e.g. the boatbuilder's progress bar constant when you pause it
            return production.progress
        return 0  # No production available

    def __set_utilization(self, utilization):
        self.__utilization = utilization

    @classmethod
    def get_instance(cls, arguments=None):
        arguments = arguments and arguments.copy() or {}

        utilization = None
        if 'utilization' in arguments:
            if arguments['utilization'] in cls.utilization_mapping:
                utilization = cls.utilization_mapping[
                    arguments['utilization']]()
            del arguments['utilization']
        else:
            utilization = Utilization()

        if arguments.get('is_mine_for'):
            # this is more of an aspect than an actual subclass, but python doesn't allow
            # fast aspect-oriented programming
            cls = MineProducer

        return cls(utilization_calculator=utilization, **arguments)