added now functional cannon needs projectiles (and alot more)
|
After Width: | Height: | Size: 226 B |
|
After Width: | Height: | Size: 247 B |
|
After Width: | Height: | Size: 232 B |
|
After Width: | Height: | Size: 218 B |
|
After Width: | Height: | Size: 220 B |
|
After Width: | Height: | Size: 247 B |
|
After Width: | Height: | Size: 237 B |
|
After Width: | Height: | Size: 202 B |
|
After Width: | Height: | Size: 222 B |
|
Before Width: | Height: | Size: 226 B After Width: | Height: | Size: 208 B |
|
Before Width: | Height: | Size: 247 B After Width: | Height: | Size: 223 B |
|
Before Width: | Height: | Size: 232 B After Width: | Height: | Size: 212 B |
|
Before Width: | Height: | Size: 218 B After Width: | Height: | Size: 205 B |
|
Before Width: | Height: | Size: 220 B After Width: | Height: | Size: 194 B |
|
Before Width: | Height: | Size: 247 B After Width: | Height: | Size: 213 B |
|
Before Width: | Height: | Size: 237 B After Width: | Height: | Size: 221 B |
|
Before Width: | Height: | Size: 202 B After Width: | Height: | Size: 179 B |
|
Before Width: | Height: | Size: 222 B After Width: | Height: | Size: 205 B |
@@ -2,7 +2,7 @@ from game.util import *
|
|||||||
import pygame as pg
|
import pygame as pg
|
||||||
import math
|
import math
|
||||||
|
|
||||||
class Enemy(pg.sprite.Group):
|
class Enemy(pg.sprite.Sprite):
|
||||||
def __init__(self, game, speed, health):
|
def __init__(self, game, speed, health):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.game = game
|
self.game = game
|
||||||
@@ -23,6 +23,12 @@ class Enemy(pg.sprite.Group):
|
|||||||
|
|
||||||
self.rect.center = self.pos
|
self.rect.center = self.pos
|
||||||
|
|
||||||
|
self.healthbar = HealthBar(
|
||||||
|
18,
|
||||||
|
5,
|
||||||
|
self.health
|
||||||
|
)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
self.move()
|
self.move()
|
||||||
self.rotate()
|
self.rotate()
|
||||||
@@ -72,3 +78,50 @@ class Enemy(pg.sprite.Group):
|
|||||||
scaled_rect.center = scaled_pos
|
scaled_rect.center = scaled_pos
|
||||||
|
|
||||||
surf.blit(scaled_image, scaled_rect)
|
surf.blit(scaled_image, scaled_rect)
|
||||||
|
self.healthbar.render(
|
||||||
|
surf,
|
||||||
|
camera,
|
||||||
|
self.pos,
|
||||||
|
self.rect.height
|
||||||
|
)
|
||||||
|
|
||||||
|
def take_damage(self, amount):
|
||||||
|
self.health -= amount
|
||||||
|
self.healthbar.hp = self.health
|
||||||
|
if self.health <= 0:
|
||||||
|
self.kill()
|
||||||
|
|
||||||
|
class EnemyGroup(pg.sprite.Group):
|
||||||
|
def render(self, surf, camera):
|
||||||
|
for enemy in self.sprites():
|
||||||
|
enemy.render(surf, camera)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
for enemy in self.sprites():
|
||||||
|
enemy.update()
|
||||||
|
|
||||||
|
class HealthBar():
|
||||||
|
def __init__(self, w, h, max_hp):
|
||||||
|
self.w = w
|
||||||
|
self.h = h
|
||||||
|
self.hp = max_hp
|
||||||
|
self.max_hp = max_hp
|
||||||
|
|
||||||
|
def render(self, surf, camera, pos, enemy_height):
|
||||||
|
ratio = self.hp / self.max_hp
|
||||||
|
|
||||||
|
world_pos = (
|
||||||
|
pos.x - self.w / 2,
|
||||||
|
pos.y + enemy_height / 2 -20
|
||||||
|
)
|
||||||
|
|
||||||
|
screen_pos = camera.world_to_screen(world_pos)
|
||||||
|
|
||||||
|
scaled_w = self.w * camera.zoom
|
||||||
|
scaled_h = self.h * camera.zoom
|
||||||
|
|
||||||
|
bg_rect = pg.Rect(screen_pos[0], screen_pos[1], scaled_w, scaled_h)
|
||||||
|
fg_rect = pg.Rect(screen_pos[0], screen_pos[1], scaled_w * ratio, scaled_h)
|
||||||
|
|
||||||
|
pg.draw.rect(surf, "red", bg_rect)
|
||||||
|
pg.draw.rect(surf, "green", fg_rect)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class Game:
|
|||||||
"enemy": load_image('/entities/enemies/enemy.png'),
|
"enemy": load_image('/entities/enemies/enemy.png'),
|
||||||
"cannon": load_image('/entities/towers/cannon/shoot/0.png'),
|
"cannon": load_image('/entities/towers/cannon/shoot/0.png'),
|
||||||
"red_cannon": load_image('/entities/towers/cannon/red_cannon.png'),
|
"red_cannon": load_image('/entities/towers/cannon/red_cannon.png'),
|
||||||
"cannon/shoot": Animation(load_images('/entities/towers/cannon/shoot'), 10, False)
|
"cannon/shoot": Animation(load_images_alpha('/entities/towers/cannon/shoot'), 10, False)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.screens = {
|
self.screens = {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from game.screens.base_screen import BaseScreen
|
from game.screens.base_screen import BaseScreen
|
||||||
from game.enemy import Enemy
|
from game.enemy import Enemy, EnemyGroup
|
||||||
from game.camera import Camera
|
from game.camera import Camera
|
||||||
from game.tower import Tower, TowerGroup
|
from game.tower import Tower, TowerGroup
|
||||||
from game.util import *
|
from game.util import *
|
||||||
@@ -29,10 +29,11 @@ class GamePlay(BaseScreen):
|
|||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print('File not Found')
|
print('File not Found')
|
||||||
|
|
||||||
self.enemy_group = pg.sprite.Group()
|
self.enemy_group = EnemyGroup()
|
||||||
self.tower_group = TowerGroup()
|
self.tower_group = TowerGroup()
|
||||||
|
|
||||||
self.enemy = Enemy(self.game, 0.5, 100)
|
self.enemy = Enemy(self.game, 0.5, 200)
|
||||||
|
self.enemy_group.add(self.enemy)
|
||||||
|
|
||||||
self.buy_button = pgi.elements.UIButton(
|
self.buy_button = pgi.elements.UIButton(
|
||||||
relative_rect=pg.Rect(
|
relative_rect=pg.Rect(
|
||||||
@@ -134,8 +135,8 @@ class GamePlay(BaseScreen):
|
|||||||
self.movment[3] = False
|
self.movment[3] = False
|
||||||
|
|
||||||
def update(self, dt):
|
def update(self, dt):
|
||||||
self.enemy.update()
|
self.enemy_group.update()
|
||||||
self.tower_group.update("sigma")
|
self.tower_group.update(self.enemy_group)
|
||||||
|
|
||||||
self.camera.move((self.movment[1] - self.movment[0]) * 2, (self.movment[3] - self.movment[2]) * 2)
|
self.camera.move((self.movment[1] - self.movment[0]) * 2, (self.movment[3] - self.movment[2]) * 2)
|
||||||
|
|
||||||
@@ -145,7 +146,7 @@ class GamePlay(BaseScreen):
|
|||||||
|
|
||||||
self.game.tilemap.render_game(self.game_surface, self.camera)
|
self.game.tilemap.render_game(self.game_surface, self.camera)
|
||||||
|
|
||||||
self.enemy.render(self.game_surface, self.camera)
|
self.enemy_group.render(self.game_surface, self.camera)
|
||||||
self.tower_group.render(self.game_surface, self.camera)
|
self.tower_group.render(self.game_surface, self.camera)
|
||||||
|
|
||||||
if self.bought:
|
if self.bought:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import pygame as pg
|
import pygame as pg
|
||||||
from game.util import *
|
from game.util import *
|
||||||
|
import math
|
||||||
|
|
||||||
class Tower(pg.sprite.Sprite):
|
class Tower(pg.sprite.Sprite):
|
||||||
def __init__(self, game, pos, tile_pos, type, range, level=1):
|
def __init__(self, game, pos, tile_pos, type, range, level=1):
|
||||||
@@ -12,6 +13,12 @@ class Tower(pg.sprite.Sprite):
|
|||||||
|
|
||||||
self.range = range
|
self.range = range
|
||||||
self.selected = False
|
self.selected = False
|
||||||
|
self.angle = 0
|
||||||
|
self.target = None
|
||||||
|
|
||||||
|
self.fire_rate = 180 # frames between shots — tune to taste
|
||||||
|
self.fire_cooldown = 0
|
||||||
|
self.damage = 50
|
||||||
|
|
||||||
self.pos = pos
|
self.pos = pos
|
||||||
self.tile_pos = tile_pos
|
self.tile_pos = tile_pos
|
||||||
@@ -19,8 +26,17 @@ class Tower(pg.sprite.Sprite):
|
|||||||
self.action = ""
|
self.action = ""
|
||||||
self.set_action('shoot')
|
self.set_action('shoot')
|
||||||
|
|
||||||
def update(self, enemies):
|
def update(self, enemy_group):
|
||||||
self.animation.update()
|
self.animation.update()
|
||||||
|
self.pick_target(enemy_group)
|
||||||
|
self.rotate()
|
||||||
|
|
||||||
|
if self.fire_cooldown > 0:
|
||||||
|
self.fire_cooldown -= 1
|
||||||
|
|
||||||
|
if self.target is not None and self.fire_cooldown == 0:
|
||||||
|
print("shoot fired")
|
||||||
|
self.shoot()
|
||||||
|
|
||||||
def set_action(self, action):
|
def set_action(self, action):
|
||||||
if action != self.action:
|
if action != self.action:
|
||||||
@@ -33,15 +49,16 @@ class Tower(pg.sprite.Sprite):
|
|||||||
))
|
))
|
||||||
|
|
||||||
anim_img = self.animation.img()
|
anim_img = self.animation.img()
|
||||||
|
rotated_img = pg.transform.rotate(anim_img, self.angle)
|
||||||
scaled_image = pg.transform.scale(
|
scaled_image = pg.transform.scale(
|
||||||
anim_img,
|
rotated_img,
|
||||||
(
|
(
|
||||||
int(anim_img.get_width() * camera.zoom),
|
int(rotated_img.get_width() * camera.zoom),
|
||||||
int(anim_img.get_height() * camera.zoom)
|
int(rotated_img.get_height() * camera.zoom)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
rect = scaled_image.get_rect(center=screen_pos)
|
self.rect = scaled_image.get_rect(center=screen_pos)
|
||||||
|
|
||||||
if self.selected:
|
if self.selected:
|
||||||
scaled_range = int(self.range * camera.zoom)
|
scaled_range = int(self.range * camera.zoom)
|
||||||
@@ -50,7 +67,40 @@ class Tower(pg.sprite.Sprite):
|
|||||||
range_rect = range_surf.get_rect(center=screen_pos)
|
range_rect = range_surf.get_rect(center=screen_pos)
|
||||||
surf.blit(range_surf, range_rect)
|
surf.blit(range_surf, range_rect)
|
||||||
|
|
||||||
surf.blit(scaled_image, rect)
|
surf.blit(scaled_image, self.rect)
|
||||||
|
|
||||||
|
def rotate(self):
|
||||||
|
if self.target is None:
|
||||||
|
# print("no target")
|
||||||
|
return # keep last angle, or reset to a default — your call
|
||||||
|
|
||||||
|
tower_world_pos = pg.Vector2(
|
||||||
|
tile_to_pixel(self.tile_pos, self.game.tilemap.tile_size, True)
|
||||||
|
)
|
||||||
|
direction = self.target.pos - tower_world_pos
|
||||||
|
self.angle = math.degrees(math.atan2(-direction[1], direction[0])) -90
|
||||||
|
# print(f"angle {self.angle}")
|
||||||
|
|
||||||
|
def pick_target(self, enemy_group):
|
||||||
|
closest = None
|
||||||
|
closest_dist = float('inf')
|
||||||
|
|
||||||
|
tower_world_pos = pg.Vector2(
|
||||||
|
tile_to_pixel(self.tile_pos, self.game.tilemap.tile_size, True)
|
||||||
|
)
|
||||||
|
|
||||||
|
for enemy in enemy_group:
|
||||||
|
distance = tower_world_pos.distance_to(enemy.pos)
|
||||||
|
if distance <= self.range and distance < closest_dist:
|
||||||
|
closest = enemy
|
||||||
|
closest_dist = distance
|
||||||
|
|
||||||
|
self.target = closest # explicitly None if nothing in range
|
||||||
|
|
||||||
|
def shoot(self):
|
||||||
|
self.target.take_damage(self.damage)
|
||||||
|
self.animation.play_animation()
|
||||||
|
self.fire_cooldown = self.fire_rate
|
||||||
|
|
||||||
class TowerGroup(pg.sprite.Group):
|
class TowerGroup(pg.sprite.Group):
|
||||||
def render(self, surf, camera):
|
def render(self, surf, camera):
|
||||||
|
|||||||
@@ -15,6 +15,16 @@ def load_images(dir_path):
|
|||||||
images.append(load_image(dir_path + '/' + img_name))
|
images.append(load_image(dir_path + '/' + img_name))
|
||||||
return images
|
return images
|
||||||
|
|
||||||
|
def load_image_alpha(path):
|
||||||
|
img = pg.image.load(img_path + path).convert_alpha()
|
||||||
|
return img
|
||||||
|
|
||||||
|
def load_images_alpha(dir_path):
|
||||||
|
images = []
|
||||||
|
for img_name in os.listdir(img_path + dir_path):
|
||||||
|
images.append(load_image_alpha(dir_path + '/' + img_name))
|
||||||
|
return images
|
||||||
|
|
||||||
def tile_to_pixel(waypoint, tile_size, center=False):
|
def tile_to_pixel(waypoint, tile_size, center=False):
|
||||||
if center:
|
if center:
|
||||||
return (
|
return (
|
||||||
@@ -40,17 +50,27 @@ class Animation:
|
|||||||
self.img_duration = img_dur
|
self.img_duration = img_dur
|
||||||
self.done = False
|
self.done = False
|
||||||
self.frame = 0
|
self.frame = 0
|
||||||
|
self.playing = False # not playing until told to
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
return Animation(self.images, self.img_duration, self.loop)
|
return Animation(self.images, self.img_duration, self.loop)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
if not self.playing:
|
||||||
|
return
|
||||||
|
|
||||||
if self.loop:
|
if self.loop:
|
||||||
self.frame = (self.frame + 1) % (self.img_duration * len(self.images))
|
self.frame = (self.frame + 1) % (self.img_duration * len(self.images))
|
||||||
else:
|
else:
|
||||||
self.frame = min(self.frame + 1, self.img_duration * len(self.images))
|
self.frame = min(self.frame + 1, self.img_duration * len(self.images) - 1)
|
||||||
if self.frame >= self.img_duration * len(self.images) - 1:
|
if self.frame >= self.img_duration * len(self.images) - 1:
|
||||||
self.done = True
|
self.done = True
|
||||||
|
self.playing = False
|
||||||
|
|
||||||
|
def play_animation(self):
|
||||||
|
self.playing = True
|
||||||
|
self.done = False
|
||||||
|
self.frame = 0
|
||||||
|
|
||||||
def img(self):
|
def img(self):
|
||||||
return self.images[int(self.frame / self.img_duration) - 1]
|
return self.images[self.frame // self.img_duration]
|
||||||