Beispiel #1
0
class ArcherTowerShort(ArcherTower):
    def __init__(self, x, y, range, damage):
        super().__init__(x, y,range, damage)
        self.archer_imgs = archer_imgs[:]
        self.archer_count = 0
        self.range = range
        self.damage = damage
        self.original_damage = self.damage
        self.inRange = False
        self.left = -1
        # self.timer = time.time()
        self.name = 'archer_2'
        self.cost = [2500, 5500, 7000, 'MAX']

        self.menu = Menu(self, self.x, self.y, menu_bg, [2500, 5500, 7000, 'MAX'])
        self.menu.add_button(upgrade_button, 'upgrade_button', self.cost)
Beispiel #2
0
class ArcherTowerShort(ArcherTowerLong):
    def __init__(self, x, y):
        super().__init__(x, y)
        self.tower_imgs = tower_imgs2[:]
        self.archer_imgs = archer_imgs[:]
        self.name = "archer_Tower2"
        self.archer_total_imgs = len(archer_imgs)
        self.archer_count = 0
        self.range = 100
        self.inRange = False
        self.original_range = self.range
        self.original_damage = self.damage
        self.left = True
        self.moving = False
        self.damage = 2
        self.animation_speed_multiplier = 2
        self.menu = Menu(self.x, self.y, self, menu_bg, [2000, 5000, "MAX"])
        self.menu.add_button(upgrade_button, "Upgrade")
Beispiel #3
0
class Tower:
	"""
	Abstract class for towers
	"""
	def __init__(self, x, y):
		self.x = x
		self.y = y
		self.width = self.height = 96
		self.sell_price = [0,0,0]
		self.price = [0,0,0]
		self.level = 1
		self.range = 200
		self.original_range = self.range # Not sure if this should be in here
		self.selected = False
		# define menu and buttons
		self.menu = Menu(self.x, self.y, self, menu_bg, [2000, 5000, 9000, 12000, "MAX"])
		self.menu.add_button(upgrade_button, "Upgrade")
		self.moving = False
		self.tower_imgs = []
		self.damage = 1
		self.place_color = (0,0,255, 100)

	def draw(self, win):
		"""
		draws the tower
		:param win: surface
		:return: None
		"""
		img = self.tower_imgs[self.level - 1]
		win.blit(img, (self.x-img.get_width()//2, self.y-img.get_height()//2))

		# draw menu
		if self.selected:
			self.menu.draw(win)

	def draw_radius(self, win):
		# draw range circle if selected
		if self.selected:	
			surface = pygame.Surface((self.range*2, self.range*2), pygame.SRCALPHA, 32)
			pygame.draw.circle(surface, (128, 128, 128, 100), (self.range, self.range), self.range, 0)

			win.blit(surface, (self.x - self.range, self.y - self.range))

	def draw_placement(self, win):
		# draw range circle
		surface = pygame.Surface((self.range*2, self.range*2), pygame.SRCALPHA, 32)
		pygame.draw.circle(surface, self.place_color, (58, 58), 58, 0) # 58 = 96/2 + 10

		win.blit(surface, (self.x - 58, self.y - 58))

	def click(self, X, Y):
		"""
		returns if tower has been clicked on
		and select tower if it was clicked
		:param X: int
		:param Y: int
		:return: bool
		"""
		img = self.tower_imgs[self.level - 1]
		if X  <= self.x - img.get_width()//2 + self.width and X >= self.x - img.get_width()//2:
			if Y <= self.y + self.height - img.get_height()//2 and Y >= self.y - img.get_height()//2:
				return True
		return False

	def sell(self):
		"""
		call to sell the tower, returns sel price
		:retutn: int
		"""
		return self.sell_price[self.level - 1]

	def upgrade(self):
		"""
		upgrades the tower for a given cost
		:return: None
		"""
		if self.level < len(self.tower_imgs):
			self.level += 1
			self.damage += 1

	def get_upgrade_cost(self):
		"""
		gets the upgrade cost
		:return: int
		"""
		return self.menu.get_item_cost()

	def move(self, x, y):
		"""
		moves tower to given x and y
		:param x: int
		:param y: int
		:return: None
		"""
		self.x = x
		self.y = y
		self.menu.x = x
		self.menu.y = y
		self.menu.update()

	def collide(self, otherTower):
		x2 = otherTower.x
		y2 = otherTower.y

		dis = math.sqrt((x2 - self.x)**2 + (y2 - self.y)**2)

		return True if dis <= 116 else False # width and height of the towers are equals to 96 and adding 10 from both sides = 116
class Tower(GameObjects):
    """
    Base structural class for our towers
    @param (STR) name: name of object 
    @param (TUPLE) coord: (x, y) coordinates of object 
    """
    def __init__(self, name, coord):
        super().__init__(name, coord)
        # Tower coordinates
        self.x = self.coord[0]
        self.y = self.coord[1]

        # Allows for upgrade references
        self.level = 1

        # Upgrade Cost
        self.cost = TowerConstants.UPGRADE_COST[name]

        # Selling logistics
        self.original_price = TowerConstants.ORIGINAL_PRICE[name]
        self.sell_price = [self.original_price, self.cost[0], self.cost[1]]

        # Tower dimensions
        self.width = self.dimensions[0]
        self.height = self.dimensions[1]

        # Clicking on a tower
        self.selected = False

        # Tower images
        self.tower_images = []

        # Specific range for each tower
        self.range = TowerConstants.TOWER_RADIUS_RANGE[name]

        # Leveling up animation
        self.level_up_animation = False
        self.level_animation = 0
        self.level_up = level_up

        # Menu logistics
        self.menu = Menu(self, menu_background)
        self.menu.add_button("upgrade", upgrade_button)
        self.menu.add_button("sell", sell_button)

        # Damage for towers that deal damage
        self.base_damage = 0
        self.damage = self.base_damage

        # For moving the archer tower when purchasing from the shops
        self.dragged = False

        # Padding for clicking purposes
        self.extra_padding = 10

        # Keeps track of kill count
        self.kill_count = 0
        self.font = game_font

    def draw(self, window):
        """
        Draws the tower, menu, and level-up animation upon condition
        @param (SURFACE) window: surface for rendering the drawing
        
        --> return: None
        """
        if self.selected:
            self.menu.draw(window)  #Drawing menu
            window.blit(kill_count_table, (self.x + self.width // 2 - 15,
                                           self.y - self.height // 2 + 35))
            kills = self.font.render(
                str(self.kill_count) + " Kills", 1, (255, 255, 255))
            window.blit(
                kills,
                (self.x + self.width // 2 + 5, self.y - self.height // 2 + 43))

        tower_image = self.tower_images[self.level - 1]

        if not self.level_up_animation:  #Always draw the tower except when leveling up
            window.blit(tower_image, (self.x - tower_image.get_width() // 2,
                                      self.y - tower_image.get_height() // 2))

        else:  #Leveling up animation procedure
            window.blit(self.level_up[self.level_animation // 2],
                        (self.x - tower_image.get_width() - 75, self.y - 225))
            self.level_animation += 1
            if self.level_animation == len(level_up) * 2:
                self.level_up_animation = False
                self.level_animation = 0

    def click(self, X, Y):
        """
        returns True if tower has been selected else False
        @param (INT) X: mouse position's x-coordinate
        @param (INT) Y: mouse position's y-coordinate
        
        --> return: Bool
        """
        tower_image = self.tower_images[self.level - 1]

        if X <= self.x + tower_image.get_width(
        ) // 2 - 2 * self.extra_padding and X >= self.x - tower_image.get_width(
        ) // 2 + self.extra_padding // 2:
            if self.name in TowerConstants.MAGIC_TOWER_NAMES or self.name in TowerConstants.SUP_TOWER_NAMES:
                if Y <= self.y + self.height // 2 - (
                        2 * self.extra_padding
                ) and Y >= self.y - self.height // 2 + (2 *
                                                        self.extra_padding):
                    return True
            else:
                if Y <= self.y + self.height // 2 - (
                        4 * self.extra_padding
                ) and Y >= self.y - self.height // 2 + (2 *
                                                        self.extra_padding):
                    return True
        return False

    def get_sell_cost(self):
        """
        Sells the tower at a specific level at a reduced cost using our self.sell_price list of prices

        --> return: Int
        """
        return round(0.75 * self.sell_price[self.level - 1])

    def upgrade(self):
        """
        Upgrades our tower and increment certain tower characteristics 

        --> return: None
        """
        if self.level < len(self.tower_images):
            self.level_up_animation = True
            self.level += 1
            self.base_damage += 3
            self.damage = self.base_damage

            #Since level does not upgrade in menu we have to manually do it here
            self.menu.tower_level += 1

    def get_upgrade_cost(self):
        """
        Returns our upgrade cost for the next tower level

        --> return: Int
        """
        return self.cost[self.level - 1]

    def move(self, X, Y):
        """
        Moves our tower to the coordinate (x, y) and updates menu coordinates
        @param (INT) X: mouse position's x-coordinate
        @param (INT) Y: mouse position's y-coordinate
        
        --> return: None
        """
        self.menu.x, self.menu.y = X, Y
        self.x, self.y = X, Y
        self.menu.update_buttons()

    def get_closest_distance_to_path(self, path):
        """
        Gets the closest distance from a tower at any point to the path 
        @param (LIST) path: list of tuples containing path coordinates

        --> return: Float
        """
        min_distance_to_line = float("inf")
        for p in path:
            game_path = p[:]

            game_path.sort(key=lambda coord: calculate_distance(self, coord))
            point_A = game_path[
                0]  # Closest point out of all the points on the path to to the tower

            try:
                point_after_A = p[p.index(point_A) + 1]
                point_before_A = p[p.index(point_A) - 1]
                closest_to_A = min(
                    point_after_A,
                    point_before_A,
                    key=lambda point: calculate_distance(point_A, point))
            except:
                if p.index(point_A) == 0:
                    closest_to_A = p[p.index(point_A) + 1]

                elif p.index(point_A) == len(p) - 1:
                    closest_to_A = p[p.index(point_A) - 1]
            finally:
                if closest_to_A[0] != point_A[0]:
                    m = (closest_to_A[1] - point_A[1]) / (closest_to_A[0] -
                                                          point_A[0])
                else:
                    m = 2

                b = point_A[1] - m * point_A[0]

                closest_distance = abs(-m * self.x + self.y -
                                       b) / math.sqrt((-m)**2 + 1)
                min_distance_to_line = min(closest_distance,
                                           min_distance_to_line)

        return min_distance_to_line
Beispiel #5
0
class ArcherTowerLong(Tower):
    def __init__(self, x, y):
        super().__init__(x, y)
        self.tower_imgs = tower_imgs1[:]
        self.archer_imgs = archer_imgs[:]
        self.name = "archer_Tower"
        self.archer_total_imgs = len(archer_imgs)
        self.archer_count = 0
        self.range = 200
        self.original_range = self.range
        self.original_damage = self.damage
        self.inRange = False
        self.left = True
        self.damage = 1
        # self.animation_speed_multiplier = 1
        self.width = self.height = 96
        self.moving = False
        self.menu = Menu(self.x, self.y, self, menu_bg,
                         [2000, 5000, 9000, "MAX"])
        self.menu.add_button(upgrade_button, "Upgrade")

    def draw(self, win):
        """
		draw the archer tower and the animated archer
		:param win: surface
		:return: int
		"""
        super().draw_radius(win)
        super().draw(win)

        if self.inRange and not self.moving:
            self.archer_count += 1
            if self.archer_count >= len(self.archer_imgs) * 2:
                self.archer_count = 0
        else:
            self.archer_count = 0

        archer = self.archer_imgs[self.archer_count // 2]
        win.blit(archer, (self.x - (archer.get_width() / 2),
                          (self.y - archer.get_height() - 20)))

    def change_range(self, r):
        """
		change range of archer tower
		:param r: int
		:return: None
		"""
        self.range = r

    def attack(self, enemies):
        """
		attacks an enemy in the enemy list, modifies list
		:param enemies: list of enemies
		:return: None
		"""
        money = 0
        self.inRange = False
        enemy_closest = []
        for enemy in enemies:
            x, y = enemy.x, enemy.y

            dis = math.sqrt((self.x - x)**2 + (self.y - y)**2)
            if dis < self.range:
                self.inRange = True
                enemy_closest.append(enemy)

        enemy_closest.sort(key=lambda x: x.path_pos, reverse=True)
        if len(enemy_closest) > 0:
            first_enemy = enemy_closest[0]
            if self.archer_count / 2 == 8:
                if first_enemy.hit(self.damage) == True:
                    money = first_enemy.money
                    enemies.remove(first_enemy)

            if first_enemy.x < self.x and not self.left:
                self.left = True
                for x, img in enumerate(self.archer_imgs):
                    self.archer_imgs[x] = pygame.transform.flip(
                        img, True, False)

            elif first_enemy.x > self.x and self.left:
                self.left = False
                for x, img in enumerate(self.archer_imgs):
                    self.archer_imgs[x] = pygame.transform.flip(
                        img, True, False)

        return money

        def flip_archer_imgs(self):  # no need atm.
            pass
Beispiel #6
0
class ArcherTower(Tower):
    def __init__(self, x, y, range, damage):
        super().__init__(x, y)
        self.archer_imgs = archer_imgs1[:]
        self.archer_count = 0
        self.range = range
        self.original_range = self.range
        self.inRange = False
        self.damage = damage
        self.left = -1
        self.timer = time.time()
        self.tower_enemy_closest = []
        self.original_damage = self.damage
        self.moving = False
        self.name = 'archer'
        self.cost = [2000, 3500, 6000, 'MAX']

        self.menu = Menu(self, self.x, self.y, menu_bg, [2000, 3500, 6000, "MAX"])
        self.menu.add_button(upgrade_button, "upgrade_button", self.cost)

    def draw(self, win):
        '''
        draw the archer tower and its orientation specially
        :param win: surface
        :return:
        '''
        super().draw_radius(win)
        super().draw_menu(win)
        super().draw(win)

        if self.inRange and not self.moving:
            self.archer_count += 1

            if self.archer_count >= len(self.archer_imgs)*2:
                self.archer_count = 0
        else:
            self.archer_count = 0

        archer = self.archer_imgs[self.archer_count // 2]
        # archer01 = pygame.transform.scale(archer, (100, 100))
        win.blit(archer, ((self.x + self.width - archer.get_width()/2 + 2), (self.y + self.height - archer.get_height()/2 - 30)))


    def change_range(self,range):
        self.range = range + (self.level-1)*50

    def attack(self, enemies):
        '''
        attacks an enemy in the enemy list
        :param enemies: list of enemies
        :return: int
        '''
        money = 0
        self.inRange = False
        self.left = 1
        for enemy in enemies:
            enemy_x, enemy_y = enemy.x, enemy.y
            dis = math.sqrt((self.x-enemy_x)**2 + (self.y-enemy_y)**2)
            # print(dis)
            if dis < self.range and enemy.health > 0.5 and enemy.x <= 1080:
                self.inRange = True
                self.tower_enemy_closest.append(enemy)

        if len(self.tower_enemy_closest)>0:
            first_enemy = self.tower_enemy_closest[0]
            if first_enemy.health < 0:
                self.tower_enemy_closest.clear()

            if first_enemy:
                if time.time() - self.timer >= 0.5:
                    self.timer = time.time()
                    if first_enemy.hit():
                        # self.tower_enemy_closest.remove(first_enemy)
                        if first_enemy in enemies:
                            money = first_enemy.money
                            enemies.remove(first_enemy)

        # print(money)
        return money
Beispiel #7
0
class Tower:
    '''
    abstract class for towers
    '''
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.width = 0
        self.height = 0
        self.level = 1
        self.sell_cost = [0, 0, 0]
        self.price = [5000, 7000, 12000, 'MAX']
        self.selected = False
        self.item_cost = [5000, 7000, 12000, 'MAX']
        self.menu = Menu(self, self.x, self.y, menu_bg, self.item_cost)
        self.tower_imgs = tower_imgs1[:]
        self.damage = 1
        self.range = 0
        self.inRange = 0
        self.left = 1
        self.menu.add_button(upgrade_button, 'upgrade_button', 0)
        self.place_color = (36, 120, 132, 100)
        self.effected = True

    def draw(self, win):
        imgs = self.tower_imgs[self.level - 1]
        win.blit(imgs, (self.x - imgs.get_width()//2, self.y - imgs.get_height()//2))

    def draw_menu(self, win):
        # draw menu
        if self.selected:
            self.menu.draw(win)

        pass

    def draw_radius(self, win):
        # draw range circle
        if self.selected:
            # print(self.range*4, self.range*4)
            circle_surface = pygame.Surface((self.range * 4, self.range * 4), pygame.SRCALPHA, 32)
            # circle_surface.set_alpha(128)
            # circle_surface.fill(0,255,0))
            pygame.draw.circle(circle_surface, (44, 195, 206, 20), (self.range, self.range), self.range, 0)
            win.blit(circle_surface, (self.x - self.range, self.y - self.range))
            # pygame.draw.circle(win, (255, 0, 0), (self.x, self.y), 200, 3)

    def draw_placement(self, win):
        # draw range circle
        circle_surface = pygame.Surface((self.range * 4, self.range * 4), pygame.SRCALPHA, 32)
        pygame.draw.circle(circle_surface, self.place_color, (50, 50), 45, 0)
        win.blit(circle_surface, (self.x - 50, self.y - 50))
        # print('draw placement range')

    def draw_shadow(self, win):
        circle_surface = pygame.Surface((self.range * 4, self.range * 4), pygame.SRCALPHA, 32)
        pygame.draw.circle(circle_surface, (0, 0, 0, 50), (50, 50), 50)
        win.blit(circle_surface, (self.x - 50, self.y - 45))

    def click(self, X, Y):
        '''
        return true if tower be clicked, else false
        :param x: int
        :param y: int
        :return: bool
        '''
        img = self.tower_imgs[self.level-1]
        if X <= self.x - img.get_width() // 2 + 120 and X >= self.x - img.get_width() // 2:
            if Y <= self.y + 120 - img.get_height() // 2 and Y >= self.y - img.get_height() // 2:
                return True
        return False

        pass

    def sell(self):
        '''
        call to sell the tower, returns sell price
        :return: int
        '''
        pass

    def upgrade(self):
        '''
        upgrade the tower by cost
        :return:
        '''
        if self.level <= 2:
            self.level += 1
            self.damage += self.damage * 0.5
            self.range += self.range * 0.25
            self.effected = True
        pass

    def get_upgrade_cost(self):
        return self.price[self.level - 1]

    def move(self, x, y):
        '''
        moves tower to given x and y
        :param x: int
        :param y: int
        :return:
        '''
        self.x = x
        self.y = y
        self.menu.x = x
        self.menu.y = y
        self.menu.update()

    def collide(self, otherTower):
        x2 = otherTower.x
        y2 = otherTower.y

        dis = math.sqrt((x2 - self.x)**2 + (y2 - self.y)**2)
        if dis >= 100:
            return False
        else:
            return True

    def occupyTheRoad(self):
        '''
        for each two node points in path
        judge whether the position where tower to be constructed
        is in a range of t that might occupy the road for monsters
        :param self: tower object
        :param path: path node list
        :param t: range that a tower cant be constructed this amount away from the path of monsters
        :return: true or false
        '''
        t = 50
        for nodeIndex in range(len(path) - 1):
            x1 = path[nodeIndex][0]
            y1 = path[nodeIndex][1]

            x2 = path[nodeIndex + 1][0]
            y2 = path[nodeIndex + 1][1]
            # the line where the segment from is Ax+By+C=0
            # B=1
            A = -((y2 - y1) / (x2 - x1))
            C = x1 * ((y2 - y1) / (x2 - x1)) - y1

            disToSegment = abs(A * self.x + self.y + C) / math.sqrt(A ** 2 + 1)

            disToNodePoint1 = math.sqrt((self.x - x1) ** 2 + (self.y - y1) ** 2)
            disToNodePoint2 = math.sqrt((self.x-x2) ** 2 + (self.y - y2) ** 2)

            maxDis_NotToIgnore = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2 + t ** 2)

            if disToSegment < t and disToNodePoint1 <= maxDis_NotToIgnore and disToNodePoint2 <= maxDis_NotToIgnore:
                return True

        return False