Exemple #1
0
	def __init(self, origin, rotation, owner, level=None, remaining_ticks_of_month=None):
		self.owner = owner
		if level is None:
			level = 0 if self.owner is None else self.owner.settler_level
		self.level = level
		self._action_set_id = self.session.db.get_random_action_set(self.id, self.level)[0]
		self.rotation = rotation
		if self.rotation in (135, 315): # Rotate the rect correctly
			self.position = ConstRect(origin, self.size[1]-1, self.size[0]-1)
		else:
			self.position = ConstRect(origin, self.size[0]-1, self.size[1]-1)

		self.loading_area = self.position # shape where collector get resources

		self._instance = self.getInstance(self.session, origin.x, origin.y, rotation=rotation,\
		                                  action_set_id=self._action_set_id)
		self._instance.setId(str(self.worldid))

		if self.has_running_costs: # Get payout every 30 seconds
			interval = self.session.timer.get_ticks(GAME.INGAME_TICK_INTERVAL)
			run_in = remaining_ticks_of_month if remaining_ticks_of_month is not None else interval
			Scheduler().add_new_object(self.get_payout, self, \
			                           run_in=run_in, loops=-1, loop_interval=interval)
		
		# play ambient sound, if available every 30 seconds
		if self.session.world.player == self.owner:
			if self.soundfiles:
				play_every = 15 + random.randint(0, 15)
				for soundfile in self.soundfiles:
					self.play_ambient(soundfile, True, play_every)
    def __init(self, origin, rotation, owner, level=None):
        self.owner = owner
        if level is None:
            if self.owner is None:
                level = 0
            else:
                level = self.owner.settler_level
        self.level = level
        self._action_set_id = self.session.db.get_random_action_set(self.id, self.level)[0]
        self.position = ConstRect(origin, self.size[0] - 1, self.size[1] - 1)
        self.rotation = rotation
        if self.rotation in [135, 315]:  # Rotate the rect correctly
            self.position = ConstRect(origin, self.size[1] - 1, self.size[0] - 1)
        else:
            self.position = ConstRect(origin, self.size[0] - 1, self.size[1] - 1)
        self._instance = self.getInstance(self.session, origin.x, origin.y, rotation=rotation)
        self._instance.setId(str(self.worldid))

        if self.running_costs != 0:  # Get payout every 30 seconds
            Scheduler().add_new_object(
                self.get_payout, self, runin=self.session.timer.get_ticks(GAME.INGAME_TICK_INTERVAL), loops=-1
            )

            # play ambient sound, if available every 30 seconds
        if self.session.world.player == self.owner:
            play_every = 15 + randint(0, 15)
            for soundfile in self.soundfiles:
                self.play_ambient(soundfile, True, play_every)
Exemple #3
0
	def __init(self, origin, rotation, owner, level=None, remaining_ticks_of_month=None):
		self.owner = owner
		if level is None:
			level = 0 if self.owner is None else self.owner.settler_level
		self.level = level
		self._action_set_id = self.session.db.get_random_action_set(self.id, self.level)[0]
		self.rotation = rotation
		if self.rotation in (135, 315): # Rotate the rect correctly
			self.position = ConstRect(origin, self.size[1]-1, self.size[0]-1)
		else:
			self.position = ConstRect(origin, self.size[0]-1, self.size[1]-1)

		self.loading_area = self.position # shape where collector get resources

		self._instance = self.getInstance(self.session, origin.x, origin.y, rotation=rotation,\
		                                  action_set_id=self._action_set_id)
		self._instance.setId(str(self.worldid))

		if self.has_running_costs: # Get payout every 30 seconds
			interval = self.session.timer.get_ticks(GAME.INGAME_TICK_INTERVAL)
			run_in = remaining_ticks_of_month if remaining_ticks_of_month is not None else interval
			Scheduler().add_new_object(self.get_payout, self, \
			                           run_in=run_in, loops=-1, loop_interval=interval)
		
		# play ambient sound, if available every 30 seconds
		if self.session.world.player == self.owner:
			if self.soundfiles:
				play_every = 15 + random.randint(0, 15)
				for soundfile in self.soundfiles:
					self.play_ambient(soundfile, True, play_every)
	def __pre_init(self, owner, rotation, origin, level=None):
		"""Here we face the awkward situation of requiring a forth init function.
		It is called like __init, but before other parts are inited via super().
		This is necessary since some attributes are used by these other parts."""
		self.owner = owner
		if level is None:
			level = 0 if self.owner is None else self.owner.settler_level
		self.level = level
		self.rotation = rotation
		if self.rotation in (135, 315): # Rotate the rect correctly
			self.position = ConstRect(origin, self.size[1]-1, self.size[0]-1)
		else:
			self.position = ConstRect(origin, self.size[0]-1, self.size[1]-1)
Exemple #5
0
class BasicBuilding(AmbientSound, ConcretObject):
	"""Class that represents a building. The building class is mainly a super class for other buildings."""

	# basic properties of class
	walkable = False # whether we can walk on this building (true for e.g. streets, trees..)
	buildable_upon = False # whether we can build upon this building
	is_building = True
	tearable = True
	show_buildingtool_preview_tab = True # whether to show the tab of the building. not shown for
																			# e.g. paths. the tab hides a part of the map.
	tabs = ()
	enemy_tabs = (EnemyBuildingOverviewTab, )
	layer = LAYERS.OBJECTS

	log = logging.getLogger("world.building")

	"""
	@param x, y: int position of the building.
	@param owner: Player that owns the building.
	"""
	def __init__(self, x, y, rotation, owner, island, level=None, **kwargs):
		super(BasicBuilding, self).__init__(x=x, y=y, rotation=rotation, owner=owner, \
								                        island=island, **kwargs)
		self.__init(Point(x, y), rotation, owner, level)
		self.island = island
		self.settlement = self.island.get_settlement(Point(x, y)) or \
				self.island.add_settlement(self.position, self.radius, owner) if \
				owner is not None else None

	def __init(self, origin, rotation, owner, level=None, remaining_ticks_of_month=None):
		self.owner = owner
		if level is None:
			level = 0 if self.owner is None else self.owner.settler_level
		self.level = level
		self._action_set_id = self.session.db.get_random_action_set(self.id, self.level)[0]
		self.rotation = rotation
		if self.rotation in (135, 315): # Rotate the rect correctly
			self.position = ConstRect(origin, self.size[1]-1, self.size[0]-1)
		else:
			self.position = ConstRect(origin, self.size[0]-1, self.size[1]-1)

		self.loading_area = self.position # shape where collector get resources

		self._instance = self.getInstance(self.session, origin.x, origin.y, rotation=rotation,\
		                                  action_set_id=self._action_set_id)
		self._instance.setId(str(self.worldid))

		if self.has_running_costs: # Get payout every 30 seconds
			interval = self.session.timer.get_ticks(GAME.INGAME_TICK_INTERVAL)
			run_in = remaining_ticks_of_month if remaining_ticks_of_month is not None else interval
			Scheduler().add_new_object(self.get_payout, self, \
			                           run_in=run_in, loops=-1, loop_interval=interval)
		
		# play ambient sound, if available every 30 seconds
		if self.session.world.player == self.owner:
			if self.soundfiles:
				play_every = 15 + random.randint(0, 15)
				for soundfile in self.soundfiles:
					self.play_ambient(soundfile, True, play_every)

	@property
	def name(self):
		return self._name

	def toggle_costs(self):
		self.running_costs , self.running_costs_inactive = \
				self.running_costs_inactive, self.running_costs

	def running_costs_active(self):
		return (self.running_costs > self.running_costs_inactive)

	def get_payout(self):
		"""gets the payout from the settlement in form of it's running costs"""
		self.owner.inventory.alter(RES.GOLD_ID, -self.running_costs)

	def remove(self):
		"""Removes the building"""
		self.log.debug("building: remove %s", self.worldid)
		self.island.remove_building(self)
		#instance is owned by layer...
		#self._instance.thisown = 1
		super(BasicBuilding, self).remove()
		# NOTE: removing layers from the renderer here will affect others players too!

	def save(self, db):
		super(BasicBuilding, self).save(db)
		db("INSERT INTO building (rowid, type, x, y, rotation, location, level) \
		   VALUES (?, ?, ?, ?, ?, ?, ?)", \
								                       self.worldid, self.__class__.id, self.position.origin.x, \
								                       self.position.origin.y, self.rotation, \
								                       (self.settlement or self.island).worldid, self.level)
		if self.has_running_costs:
			remaining_ticks = Scheduler().get_remaining_ticks(self, self.get_payout)
			db("INSERT INTO remaining_ticks_of_month(rowid, ticks) VALUES(?, ?)", self.worldid, remaining_ticks)

	def load(self, db, worldid):
		super(BasicBuilding, self).load(db, worldid)
		x, y, location, rotation, level = db.get_building_row(worldid)

		owner_id = db.get_settlement_owner(location)
		owner = None if owner_id is None else WorldObject.get_object_by_id(owner_id)

		remaining_ticks_of_month = None
		if self.has_running_costs:
			remaining_ticks_of_month = db("SELECT ticks FROM remaining_ticks_of_month WHERE rowid=?", worldid)[0][0]

		self.__init(Point(x, y), rotation, owner, level=level, \
		            remaining_ticks_of_month=remaining_ticks_of_month)

		self.island, self.settlement = self.load_location(db, worldid)

		# island.add_building handles registration of building for island and settlement
		self.island.add_building(self, self.owner)

	def load_location(self, db, worldid):
		"""
		Does not alter self, just gets island and settlement from a savegame.
		@return: tuple: (island, settlement)
		"""
		location_obj = WorldObject.get_object_by_id(db.get_building_location(worldid))
		if isinstance(location_obj, Settlement):
			# workaround: island can't be fetched from world, because it isn't fully constructed
			island = WorldObject.get_object_by_id(db.get_settlement_island(location_obj.worldid))
			# settlement might not have been registered in island, so do it if getter fails
			settlement = island.get_settlement(self.position.center()) or \
								 island.add_existing_settlement(self.position, self.radius, location_obj)
		else: # loc is island
			island = location_obj
			settlement = None
		return (island, settlement)

	def get_buildings_in_range(self):
		# TODO Think about moving this to the Settlement class
		buildings = self.settlement.buildings
		ret_building = []
		for building in buildings:
			if building == self:
				continue
			if self.position.distance( building.position ) <= self.radius:
				ret_building.append( building )
		return ret_building

	def update_action_set_level(self, level=0):
		"""Updates this buildings action_set to a random actionset from the specified level
		(if an action set exists in that level).
		It's different to get_random_action_set is, that it just checks one lvl, and doesn't
		search for an action set everywhere, which makes it alot more effective, if you're
		just updating.
		@param level: int level number"""
		action_sets = self.session.db.get_random_action_set(self.id, level, exact_level=True)
		if action_sets:
			self._action_set_id = action_sets[0] # Set the new action_set
			self.act(self._action, repeating=True)

	def level_upgrade(self, lvl):
		"""Upgrades building to another increment"""
		self.level = lvl
		self.update_action_set_level(lvl)

	@classmethod
	def getInstance(cls, session, x, y, action='idle', level=0, rotation=45, action_set_id=None):
		"""Get a Fife instance
		@param x, y: The coordinates
		@param action: The action, defaults to 'idle'
		@param level: object level. Relevant for choosing an action set
		@param rotation: rotation of the object. Any of [ 45 + 90*i for i in xrange(0, 4) ]
		@param action_set_id: can be set if the action set is already known. If set, level isn't considered.
		"""
		assert isinstance(x, int)
		assert isinstance(y, int)
		#rotation = cls.check_build_rotation(session, rotation, x, y)
		# TODO: replace this with new buildable api
		# IDEA: save rotation in savegame
		facing_loc = fife.Location(session.view.layers[cls.layer])
		instance_coords = list((x, y, 0))
		layer_coords = list((x, y, 0))

		# NOTE:
		# nobody acctually knows how the code below works.
		# it's for adapting the facing location and instance coords in
		# different rotations, and works with all quadratic buildings (tested up to 4x4)
		# for the first unquadratic building (2x4), a hack fix was put into it.
		# the plan for fixing this code in general is to wait until there are more
		# unquadratic buildings, and figure out a pattern of the placement error,
		# then fix that generally.

		if rotation == 45:
			layer_coords[0] = x+cls.size[0]+3

			if cls.size[0] == 2 and cls.size[1] == 4:
				# HACK: fix for 4x2 buildings
				instance_coords[0] -= 1
				instance_coords[1] += 1

		elif rotation == 135:
			instance_coords[1] = y + cls.size[1] - 1
			layer_coords[1] = y-cls.size[1]-3

			if cls.size[0] == 2 and cls.size[1] == 4:
				# HACK: fix for 4x2 buildings
				instance_coords[0] += 1
				instance_coords[1] -= 1

		elif rotation == 225:
			instance_coords = list(( x + cls.size[0] - 1, y + cls.size[1] - 1, 0))
			layer_coords[0] = x-cls.size[0]-3

			if cls.size[0] == 2 and cls.size[1] == 4:
				# HACK: fix for 4x2 buildings
				instance_coords[0] += 1
				instance_coords[1] -= 1

		elif rotation == 315:
			instance_coords[0] = x + cls.size[0] - 1
			layer_coords[1] = y+cls.size[1]+3

			if cls.size[0] == 2 and cls.size[1] == 4:
				# HACK: fix for 4x2 buildings
				instance_coords[0] += 1
				instance_coords[1] -= 1

		else:
			return None
		instance = session.view.layers[cls.layer].createInstance(cls._object, \
											                                       fife.ModelCoordinate(*instance_coords))
		facing_loc.setLayerCoordinates(fife.ModelCoordinate(*layer_coords))

		if action_set_id is None:
			action_set_id = session.db.get_random_action_set(cls.id, level=level)[0]
		fife.InstanceVisual.create(instance)

		action_sets = ActionSetLoader.get_action_sets()
		if not action in action_sets[action_set_id]:
			if 'idle' in action_sets[action_set_id]:
				action='idle'
			elif 'idle_full' in action_sets[action_set_id]:
				action='idle_full'
			else:
				# set first action
				action = action_sets[action_set_id].keys()[0]

		instance.act(action+"_"+str(action_set_id), facing_loc, True)
		return instance

	@classmethod
	def have_resources(cls, inventory_holders, owner):
		return Build.check_resources({}, cls.costs, owner, inventory_holders)[0]

	def init(self):
		"""init the building, called after the constructor is run and the building is positioned (the settlement variable is assigned etc)
		"""
		pass

	def start(self):
		"""This function is called when the building is built, to start production for example."""
		pass

	#@decorators.relese_mode(ret="Building")
	def __str__(self): # debug
		classname = horizons.main.db.cached_query("SELECT name FROM building where id = ?", self.id)[0][0]
		return '%s(id=%s;worldid=%s)' % (classname, self.id, \
								                     self.worldid)
class BasicBuilding(AmbientSound, ConcretObject):
    """Class that represents a building. The building class is mainly a super class for other buildings."""

    # basic properties of class
    walkable = False  # whether we can walk on this building (true for e.g. streets, trees..)
    buildable_upon = False  # whether we can build upon this building
    is_building = True
    tearable = True
    show_buildingtool_preview_tab = True  # whether to show the tab of the building. not shown for
    # e.g. paths. the tab hides a part of the map.
    enemy_tabs = (EnemyBuildingOverviewTab, )
    layer = LAYERS.OBJECTS

    log = logging.getLogger("world.building")
    """
	@param x, y: int position of the building.
	@param owner: Player that owns the building.
	"""
    def __init__(self, x, y, rotation, owner, island, level=None, **kwargs):
        super(BasicBuilding, self).__init__(x=x, y=y, rotation=rotation, owner=owner, \
                                      island=island, **kwargs)
        self.__init(Point(x, y), rotation, owner, level)
        self.island = island
        self.settlement = self.island.get_settlement(Point(x, y)) or \
          self.island.add_settlement(self.position, self.radius, owner) if \
          owner is not None else None

    def __init(self,
               origin,
               rotation,
               owner,
               level=None,
               remaining_ticks_of_month=None):
        self.owner = owner
        if level is None:
            if self.owner is None:
                level = 0
            else:
                level = self.owner.settler_level
        self.level = level
        self._action_set_id = self.session.db.get_random_action_set(
            self.id, self.level)[0]
        self.rotation = rotation
        if self.rotation in (135, 315):  # Rotate the rect correctly
            self.position = ConstRect(origin, self.size[1] - 1,
                                      self.size[0] - 1)
        else:
            self.position = ConstRect(origin, self.size[0] - 1,
                                      self.size[1] - 1)

        self.loading_area = self.position  # shape where collector get resources

        self._instance = self.getInstance(self.session, origin.x, origin.y, rotation=rotation,\
                                          action_set_id=self._action_set_id)
        self._instance.setId(str(self.worldid))

        if self.has_running_costs:  # Get payout every 30 seconds
            interval = self.session.timer.get_ticks(GAME.INGAME_TICK_INTERVAL)
            run_in = remaining_ticks_of_month if remaining_ticks_of_month is not None else interval
            Scheduler().add_new_object(self.get_payout, self, \
                                       run_in=run_in, loops=-1, loop_interval=interval)

        # play ambient sound, if available every 30 seconds
        if self.session.world.player == self.owner:
            if self.soundfiles:
                play_every = 15 + random.randint(0, 15)
                for soundfile in self.soundfiles:
                    self.play_ambient(soundfile, True, play_every)

    @property
    def name(self):
        return self._name

    def toggle_costs(self):
        self.running_costs , self.running_costs_inactive = \
          self.running_costs_inactive, self.running_costs

    def running_costs_active(self):
        return (self.running_costs > self.running_costs_inactive)

    def get_payout(self):
        """gets the payout from the settlement in form of it's running costs"""
        self.owner.inventory.alter(RES.GOLD_ID, -self.running_costs)

    def remove(self):
        """Removes the building"""
        self.log.debug("building: remove %s", self.worldid)
        self.island.remove_building(self)
        #instance is owned by layer...
        #self._instance.thisown = 1
        super(BasicBuilding, self).remove()
        # NOTE: removing layers from the renderer here will affect others players too!

    def save(self, db):
        super(BasicBuilding, self).save(db)
        db("INSERT INTO building (rowid, type, x, y, rotation, health, location, level) \
		   VALUES (?, ?, ?, ?, ?, ?, ?, ?)"                                           , \
                                     self.worldid, self.__class__.id, self.position.origin.x, \
                                     self.position.origin.y, self.rotation, \
                                     self.health, (self.settlement or self.island).worldid, self.level)
        if self.has_running_costs:
            remaining_ticks = Scheduler().get_remaining_ticks(
                self, self.get_payout)
            db(
                "INSERT INTO remaining_ticks_of_month(rowid, ticks) VALUES(?, ?)",
                self.worldid, remaining_ticks)

    def load(self, db, worldid):
        super(BasicBuilding, self).load(db, worldid)
        x, y, self.health, location, rotation, level= \
         db("SELECT x, y, health, location, rotation, level FROM building WHERE rowid = ?", worldid)[0]

        owner_db = db("SELECT owner FROM settlement WHERE rowid = ?", location)
        owner = None if len(owner_db) == 0 else WorldObject.get_object_by_id(
            owner_db[0][0])

        remaining_ticks_of_month = None
        if self.has_running_costs:
            remaining_ticks_of_month = db(
                "SELECT ticks FROM remaining_ticks_of_month WHERE rowid=?",
                worldid)[0][0]

        self.__init(Point(x, y), rotation, owner, level=level, \
                    remaining_ticks_of_month=remaining_ticks_of_month)

        self.island, self.settlement = self.load_location(db, worldid)

        # island.add_building handles registration of building for island and settlement
        self.island.add_building(self, self.owner)

    def load_location(self, db, worldid):
        """
		Does not alter self, just gets island and settlement from a savegame.
		@return: tuple: (island, settlement)
		"""
        location = db("SELECT location FROM building WHERE rowid = ?",
                      worldid)[0][0]
        location_obj = WorldObject.get_object_by_id(location)
        if isinstance(location_obj, Settlement):
            # workaround: island can't be fetched from world, because it isn't fully constructed
            island_id = db("SELECT island FROM settlement WHERE rowid = ?",
                           location_obj.worldid)[0][0]
            island = WorldObject.get_object_by_id(island_id)
            # settlement might not have been registered in island, so do it if getter fails
            settlement = island.get_settlement(self.position.center()) or \
                  island.add_existing_settlement(self.position, self.radius, location_obj)
        else:  # loc is island
            island = location_obj
            settlement = None
        return (island, settlement)

    def get_buildings_in_range(self):
        # TODO Think about moving this to the Settlement class
        buildings = self.settlement.buildings
        ret_building = []
        for building in buildings:
            if building == self:
                continue
            if self.position.distance(building.position) <= self.radius:
                ret_building.append(building)
        return ret_building

    def update_action_set_level(self, level=0):
        """Updates this buildings action_set to a random actionset from the specified level
		(if an action set exists in that level).
		It's different to get_random_action_set is, that it just checks one lvl, and doesn't
		search for an action set everywhere, which makes it alot more effective, if you're
		just updating.
		@param level: int level number"""
        action_sets = self.session.db.get_random_action_set(self.id,
                                                            level,
                                                            exact_level=True)
        if action_sets:
            self._action_set_id = action_sets[0]  # Set the new action_set
            self.act(self._action, repeating=True)

    def level_upgrade(self, lvl):
        """Upgrades building to another increment"""
        self.level = lvl
        self.update_action_set_level(lvl)

    @classmethod
    def getInstance(cls,
                    session,
                    x,
                    y,
                    action='idle',
                    level=0,
                    rotation=45,
                    action_set_id=None):
        """Get a Fife instance
		@param x, y: The coordinates
		@param action: The action, defaults to 'idle'
		@param level: object level. Relevant for choosing an action set
		@param rotation: rotation of the object. Any of [ 45 + 90*i for i in xrange(0, 4) ]
		@param action_set_id: can be set if the action set is already known. If set, level isn't considered.
		"""
        assert isinstance(x, int)
        assert isinstance(y, int)
        #rotation = cls.check_build_rotation(session, rotation, x, y)
        # TODO: replace this with new buildable api
        # IDEA: save rotation in savegame
        facing_loc = fife.Location(session.view.layers[cls.layer])
        instance_coords = list((x, y, 0))
        layer_coords = list((x, y, 0))

        # NOTE:
        # nobody acctually knows how the code below works.
        # it's for adapting the facing location and instance coords in
        # different rotations, and works with all quadratic buildings (tested up to 4x4)
        # for the first unquadratic building (2x4), a hack fix was put into it.
        # the plan for fixing this code in general is to wait until there are more
        # unquadratic buildings, and figure out a pattern of the placement error,
        # then fix that generally.

        if rotation == 45:
            layer_coords[0] = x + cls.size[0] + 3

            if cls.size[0] == 2 and cls.size[1] == 4:
                # HACK: fix for 4x2 buildings
                instance_coords[0] -= 1
                instance_coords[1] += 1

        elif rotation == 135:
            instance_coords[1] = y + cls.size[1] - 1
            layer_coords[1] = y - cls.size[1] - 3

            if cls.size[0] == 2 and cls.size[1] == 4:
                # HACK: fix for 4x2 buildings
                instance_coords[0] += 1
                instance_coords[1] -= 1

        elif rotation == 225:
            instance_coords = list(
                (x + cls.size[0] - 1, y + cls.size[1] - 1, 0))
            layer_coords[0] = x - cls.size[0] - 3

            if cls.size[0] == 2 and cls.size[1] == 4:
                # HACK: fix for 4x2 buildings
                instance_coords[0] += 1
                instance_coords[1] -= 1

        elif rotation == 315:
            instance_coords[0] = x + cls.size[0] - 1
            layer_coords[1] = y + cls.size[1] + 3

            if cls.size[0] == 2 and cls.size[1] == 4:
                # HACK: fix for 4x2 buildings
                instance_coords[0] += 1
                instance_coords[1] -= 1

        else:
            return None
        instance = session.view.layers[cls.layer].createInstance(cls._object, \
                                                        fife.ModelCoordinate(*instance_coords))
        facing_loc.setLayerCoordinates(fife.ModelCoordinate(*layer_coords))

        if action_set_id is None:
            action_set_id = session.db.get_random_action_set(cls.id,
                                                             level=level)[0]
        fife.InstanceVisual.create(instance)

        action_sets = ActionSetLoader.get_action_sets()
        if not action in action_sets[action_set_id]:
            if 'idle' in action_sets[action_set_id]:
                action = 'idle'
            elif 'idle_full' in action_sets[action_set_id]:
                action = 'idle_full'
            else:
                # set first action
                action = action_sets[action_set_id].keys()[0]

        instance.act(action + "_" + str(action_set_id), facing_loc, True)
        return instance

    def init(self):
        """init the building, called after the constructor is run and the building is positioned (the settlement variable is assigned etc)
		"""
        pass

    def start(self):
        """This function is called when the building is built, to start production for example."""
        pass

    #@decorators.relese_mode(ret="Building")
    def __str__(self):  # debug
        classname = horizons.main.db.cached_query(
            "SELECT name FROM building where id = ?", self.id)[0][0]
        return '%s(id=%s;worldid=%s)' % (classname, self.id, \
                                   self.worldid)
class BasicBuilding(ComponentHolder, ConcreteObject):
	"""Class that represents a building. The building class is mainly a super class for other buildings."""

	# basic properties of class
	walkable = False # whether we can walk on this building (true for e.g. streets, trees..)
	buildable_upon = False # whether we can build upon this building
	is_building = True
	tearable = True
	layer = LAYERS.OBJECTS


	log = logging.getLogger("world.building")

	"""
	@param x, y: int position of the building.
	@param rotation: value passed to getInstance
	@param owner: Player that owns the building.
	@param level: start in this increment
	@param action_set_id: use this action set id. None means choose one at random
	"""
	def __init__(self, x, y, rotation, owner, island, level=None, action_set_id=None, **kwargs):
		self.__pre_init(owner, rotation, Point(x, y), level=level)
		super(BasicBuilding, self).__init__(x=x, y=y, rotation=rotation, owner=owner, \
								                        island=island, **kwargs)
		self.__init( action_set_id=action_set_id )
		self.island = island

		settlements = self.island.get_settlements(self.position, owner)
		if settlements:
			self.settlement = settlements[0]
		else:
			# create one if we have an owner
			self.settlement = self.island.add_settlement(self.position, self.radius, owner) if \
			    owner is not None else None

		assert self.settlement is None or isinstance(self.settlement, Settlement)

	def __pre_init(self, owner, rotation, origin, level=None):
		"""Here we face the awkward situation of requiring a forth init function.
		It is called like __init, but before other parts are inited via super().
		This is necessary since some attributes are used by these other parts."""
		self.owner = owner
		if level is None:
			level = 0 if self.owner is None else self.owner.settler_level
		self.level = level
		self.rotation = rotation
		if self.rotation in (135, 315): # Rotate the rect correctly
			self.position = ConstRect(origin, self.size[1]-1, self.size[0]-1)
		else:
			self.position = ConstRect(origin, self.size[0]-1, self.size[1]-1)

	def __init(self, remaining_ticks_of_month=None, action_set_id=None):
		self._action_set_id = action_set_id if action_set_id is not None else \
		    self.get_random_action_set(self.level)[0]

		self.loading_area = self.position # shape where collector get resources

		origin = self.position.origin
		self._instance, action_set_id = \
		  self.getInstance(self.session, origin.x, origin.y, rotation=self.rotation,\
		                   action_set_id=self._action_set_id)
		self._instance.setId(str(self.worldid))

		if self.has_running_costs: # Get payout every 30 seconds
			interval = self.session.timer.get_ticks(GAME.INGAME_TICK_INTERVAL)
			run_in = remaining_ticks_of_month if remaining_ticks_of_month is not None else interval
			Scheduler().add_new_object(self.get_payout, self, \
			                           run_in=run_in, loops=-1, loop_interval=interval)

	def toggle_costs(self):
		self.running_costs , self.running_costs_inactive = \
				self.running_costs_inactive, self.running_costs

	def running_costs_active(self):
		"""Returns whether the building currently payes the running costs for status 'active'"""
		return (self.running_costs > self.running_costs_inactive)

	def get_payout(self):
		"""gets the payout from the settlement in form of it's running costs"""
		self.owner.get_component(StorageComponent).inventory.alter(RES.GOLD_ID, -self.running_costs)

	def remove(self):
		"""Removes the building"""
		self.log.debug("building: remove %s", self.worldid)
		if hasattr(self, "disaster"):
			self.disaster.recover(self)
		self.island.remove_building(self)
		#instance is owned by layer...
		#self._instance.thisown = 1
		super(BasicBuilding, self).remove()
		# NOTE: removing layers from the renderer here will affect others players too!

	def save(self, db):
		super(BasicBuilding, self).save(db)
		db("INSERT INTO building (rowid, type, x, y, rotation, location, level) \
		   VALUES (?, ?, ?, ?, ?, ?, ?)", \
		                                self.worldid, self.__class__.id, self.position.origin.x, \
		                                self.position.origin.y, self.rotation, \
		                                (self.settlement or self.island).worldid, self.level)
		if self.has_running_costs:
			remaining_ticks = Scheduler().get_remaining_ticks(self, self.get_payout)
			db("INSERT INTO remaining_ticks_of_month(rowid, ticks) VALUES(?, ?)", self.worldid, remaining_ticks)

	def load(self, db, worldid):
		self.island, self.settlement = self.load_location(db, worldid)
		x, y, location, rotation, level = db.get_building_row(worldid)
		owner_id = db.get_settlement_owner(location)
		owner = None if owner_id is None else WorldObject.get_object_by_id(owner_id)

		# early init before super() call
		self.__pre_init(owner, rotation, Point(x, y), level=level)

		super(BasicBuilding, self).load(db, worldid)

		remaining_ticks_of_month = None
		if self.has_running_costs:
			db_data = db("SELECT ticks FROM remaining_ticks_of_month WHERE rowid=?", worldid)
			if len(db_data) == 0:
				# this can happen when running costs are set when there were no before
				# we shouldn't crash because of changes in yaml code, still it's suspicous
				print 'WARNING: object %s of type %s does not know when to pay its rent.'
				print 'Disregard this when loading old savegames or on running cost changes.'
				remaining_ticks_of_month = 1
			else:
				remaining_ticks_of_month = db_data[0][0]

		self.__init(remaining_ticks_of_month=remaining_ticks_of_month)


		# island.add_building handles registration of building for island and settlement
		self.island.add_building(self, self.owner, load=True)

	def load_location(self, db, worldid):
		"""
		Does not alter self, just gets island and settlement from a savegame.
		@return: tuple: (island, settlement)
		"""
		location_obj = WorldObject.get_object_by_id(db.get_building_location(worldid))
		if isinstance(location_obj, Settlement):
			# workaround: island can't be fetched from world, because it isn't fully constructed
			island = WorldObject.get_object_by_id(db.get_settlement_island(location_obj.worldid))
			settlement = location_obj
		else: # loc is island
			island = location_obj
			settlement = None
		return (island, settlement)

	def get_buildings_in_range(self):
		# TODO Think about moving this to the Settlement class
		buildings = self.settlement.buildings
		for building in buildings:
			if building is self:
				continue
			if self.position.distance( building.position ) <= self.radius:
				yield building

	def update_action_set_level(self, level=0):
		"""Updates this buildings action_set to a random actionset from the specified level
		(if an action set exists in that level).
		It's different to get_random_action_set is, that it just checks one lvl, and doesn't
		search for an action set everywhere, which makes it alot more effective, if you're
		just updating.
		@param level: int level number"""
		action_set = self.get_random_action_set(level, exact_level=True)[0]
		if action_set:
			self._action_set_id = action_set # Set the new action_set
			self.act(self._action, repeating=True)

	def level_upgrade(self, lvl):
		"""Upgrades building to another increment"""
		self.level = lvl
		self.update_action_set_level(lvl)

	@classmethod
	def getInstance(cls, session, x, y, action='idle', level=0, rotation=45, action_set_id=None):
		"""Get a Fife instance
		@param x, y: The coordinates
		@param action: The action, defaults to 'idle'
		@param level: object level. Relevant for choosing an action set
		@param rotation: rotation of the object. Any of [ 45 + 90*i for i in xrange(0, 4) ]
		@param action_set_id: can be set if the action set is already known. If set, level isn't considered.
		@return: tuple (fife_instance, action_set_id)
		"""
		assert isinstance(x, int)
		assert isinstance(y, int)
		#rotation = cls.check_build_rotation(session, rotation, x, y)
		# TODO: replace this with new buildable api
		# IDEA: save rotation in savegame
		facing_loc = fife.Location(session.view.layers[cls.layer])
		instance_coords = list((x, y, 0))
		layer_coords = list((x, y, 0))

		# NOTE:
		# nobody actually knows how the code below works.
		# it's for adapting the facing location and instance coords in
		# different rotations, and works with all quadratic buildings (tested up to 4x4)
		# for the first unquadratic building (2x4), a hack fix was put into it.
		# the plan for fixing this code in general is to wait until there are more
		# unquadratic buildings, and figure out a pattern of the placement error,
		# then fix that generally.

		if rotation == 45:
			layer_coords[0] = x+cls.size[0]+3

			if cls.size[0] == 2 and cls.size[1] == 4:
				# HACK: fix for 4x2 buildings
				instance_coords[0] -= 1
				instance_coords[1] += 1

		elif rotation == 135:
			instance_coords[1] = y + cls.size[1] - 1
			layer_coords[1] = y-cls.size[1]-3

			if cls.size[0] == 2 and cls.size[1] == 4:
				# HACK: fix for 4x2 buildings
				instance_coords[0] += 1
				instance_coords[1] -= 1

		elif rotation == 225:
			instance_coords = list(( x + cls.size[0] - 1, y + cls.size[1] - 1, 0))
			layer_coords[0] = x-cls.size[0]-3

			if cls.size[0] == 2 and cls.size[1] == 4:
				# HACK: fix for 4x2 buildings
				instance_coords[0] += 1
				instance_coords[1] -= 1

		elif rotation == 315:
			instance_coords[0] = x + cls.size[0] - 1
			layer_coords[1] = y+cls.size[1]+3

			if cls.size[0] == 2 and cls.size[1] == 4:
				# HACK: fix for 4x2 buildings
				instance_coords[0] += 1
				instance_coords[1] -= 1

		else:
			return None
		instance = session.view.layers[cls.layer].createInstance(cls._object, \
											                                       fife.ModelCoordinate(*instance_coords))
		facing_loc.setLayerCoordinates(fife.ModelCoordinate(*layer_coords))

		if action_set_id is None:
			action_set_id = cls.get_random_action_set(level=level)[0]
		fife.InstanceVisual.create(instance)

		action_sets = ActionSetLoader.get_sets()
		if not action in action_sets[action_set_id]:
			if 'idle' in action_sets[action_set_id]:
				action='idle'
			elif 'idle_full' in action_sets[action_set_id]:
				action='idle_full'
			else:
				# set first action
				action = action_sets[action_set_id].keys()[0]

		instance.act(action+"_"+str(action_set_id), facing_loc, True)
		return (instance, action_set_id)

	@classmethod
	def have_resources(cls, inventory_holders, owner):
		return Build.check_resources({}, cls.costs, owner, inventory_holders)[0]

	def init(self):
		"""init the building, called after the constructor is run and the building is positioned (the settlement variable is assigned etc)
		"""
		pass

	def start(self):
		"""This function is called when the building is built, to start production for example."""
		pass

	#@decorators.relese_mode(ret="Building")
	def __str__(self): # debug
		return '%s(id=%s;worldid=%s)' % (self.name, self.id, self.worldid if hasattr(self, 'worldid') else 'none')
Exemple #8
0
	def __set_position(self, rotation, origin):
			self.rotation = rotation
			if self.rotation in (135, 315): # Rotate the rect correctly
				self.position = ConstRect(origin, self.size[1]-1, self.size[0]-1)
			else:
				self.position = ConstRect(origin, self.size[0]-1, self.size[1]-1)