Ejemplo n.º 1
0
    def __init__(self,
                 auto_init=True,
                 start_finished=False,
                 productionlines=None,
                 utilisation_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 utilisation_calculator: one of utilisatoin_mapping
		@param settler_upgrade_lines: data for settler upgrades. can one day be generalised 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 utilisation_calculator is not None
        self.__utilisation = utilisation_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
Ejemplo n.º 2
0
	def __init__(self, auto_init=True, start_finished=False, productionlines=None,
	             utilisation_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 utilisation_calculator: one of utilisatoin_mapping
		@param settler_upgrade_lines: data for settler upgrades. can one day be generalised 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 utilisation_calculator is not None
		self.__utilisation = utilisation_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
Ejemplo n.º 3
0
    def _upgrade_to_rev61(self, db):
        from horizons.world.building.settler import SettlerUpgradeData

        # settler upgrade lines used to be the same for several levels
        for (settler,
             level) in db("SELECT rowid, level FROM building WHERE type = 3"):
            #if settler == 100268:import pdb ; pdb.set_trace()
            # the id used to always be 35
            db(
                "UPDATE production SET prod_line_id = ? WHERE owner = ? and prod_line_id = 35",
                SettlerUpgradeData.get_production_line_id(level + 1), settler)
Ejemplo n.º 4
0
    def _upgrade_to_rev61(self, db):
        from horizons.world.building.settler import SettlerUpgradeData

        # settler upgrade lines used to be the same for several levels
        for (settler, level) in db("SELECT rowid, level FROM building WHERE type = 3"):
            # if settler == 100268:import pdb ; pdb.set_trace()
            # the id used to always be 35
            db(
                "UPDATE production SET prod_line_id = ? WHERE owner = ? and prod_line_id = 35",
                SettlerUpgradeData.get_production_line_id(level + 1),
                settler,
            )
Ejemplo n.º 5
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]

	utilisation_mapping = {
	    'FieldUtilisation': FieldUtilisation,
	    'FullUtilisation': FullUtilisation
	}

	production_class = Production

	# INIT
	def __init__(self, auto_init=True, start_finished=False, productionlines=None,
	             utilisation_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 utilisation_calculator: one of utilisatoin_mapping
		@param settler_upgrade_lines: data for settler upgrades. can one day be generalised 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 utilisation_calculator is not None
		self.__utilisation = utilisation_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 utilisation level is currently ok
		self.__utilisation_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_utilisation, 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 utilisation 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.__utilisation
		self.__utilisation = FullUtilisation()
		Scheduler().add_new_object(Callback(self.__set_utilisation, 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_utilisation(self):
		"""Called by the scheduler to update the utilisation regularly"""
		if not self.capacity_utilisation_below(ProductivityLowStatus.threshold) is not self.__utilisation_ok:
			self.__utilisation_ok = not self.__utilisation_ok
			if self.__utilisation_ok:
				RemoveStatusIcon.broadcast(self, self.instance, ProductivityLowStatus)
			else:
				icon = ProductivityLowStatus(self.instance)
				AddStatusIcon.broadcast(self, icon)

	@property
	def capacity_utilisation(self):
		return self.__utilisation.capacity_utilisation(self)

	def capacity_utilisation_below(self, limit):
		return self.__utilisation.capacity_utilisation_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_utilisation_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_utilisation(self, utilisation):
		self.__utilisation = utilisation

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

		utilisation = None
		if 'utilisation' in arguments:
			if arguments['utilisation'] in cls.utilisation_mapping:
				utilisation = cls.utilisation_mapping[arguments['utilisation']]()
			del arguments['utilisation']
		else:
			utilisation = Utilisation()

		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(utilisation_calculator=utilisation, **arguments)
Ejemplo n.º 6
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]

    utilisation_mapping = {
        'FieldUtilisation': FieldUtilisation,
        'FullUtilisation': FullUtilisation
    }

    production_class = Production

    # INIT
    def __init__(self,
                 auto_init=True,
                 start_finished=False,
                 productionlines=None,
                 utilisation_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 utilisation_calculator: one of utilisatoin_mapping
		@param settler_upgrade_lines: data for settler upgrades. can one day be generalised 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 utilisation_calculator is not None
        self.__utilisation = utilisation_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 utilisation level is currently ok
        self.__utilisation_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_utilisation,
                                       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 utilisation 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.__utilisation
        self.__utilisation = FullUtilisation()
        Scheduler().add_new_object(Callback(self.__set_utilisation, 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_utilisation(self):
        """Called by the scheduler to update the utilisation regularly"""
        if not self.capacity_utilisation_below(
                ProductivityLowStatus.threshold) is not self.__utilisation_ok:
            self.__utilisation_ok = not self.__utilisation_ok
            if self.__utilisation_ok:
                RemoveStatusIcon.broadcast(self, self.instance,
                                           ProductivityLowStatus)
            else:
                icon = ProductivityLowStatus(self.instance)
                AddStatusIcon.broadcast(self, icon)

    @property
    def capacity_utilisation(self):
        return self.__utilisation.capacity_utilisation(self)

    def capacity_utilisation_below(self, limit):
        return self.__utilisation.capacity_utilisation_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_utilisation_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_utilisation(self, utilisation):
        self.__utilisation = utilisation

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

        utilisation = None
        if 'utilisation' in arguments:
            if arguments['utilisation'] in cls.utilisation_mapping:
                utilisation = cls.utilisation_mapping[
                    arguments['utilisation']]()
            del arguments['utilisation']
        else:
            utilisation = Utilisation()

        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(utilisation_calculator=utilisation, **arguments)