Skip to content
Snippets Groups Projects
Unverified Commit a8eef080 authored by Mihkel Hain's avatar Mihkel Hain
Browse files

Firing add ig lk im just chill guy

parent f0460ff7
Branches towerFire
No related tags found
No related merge requests found
File added
"""This module contains the Enemy class."""
import pygame as pg
class Enemy:
"""Class for the enemy object."""
def __init__(self, hp: float, speed: float, damage: float, texture_path: str):
"""Initializes the enemy object."""
self.hp = hp
self.max_hp = hp
self.speed = speed
self.damage = damage
self.texture_path = texture_path
......@@ -14,13 +12,27 @@ class Enemy:
self.path_position = 0
self.screen_position = (0, 0)
self.full_path_time = 20
self.alive = True
def take_damage(self, amount: float):
"""
Reduces the enemy's HP by the given damage amount.
Parameters:
amount:
The amount of damage to apply to the enemy.
"""
self.hp -= amount
if self.hp <= 0:
self.hp = 0
self.alive = False # Mark enemy as dead
def calculate_current_path_segment(self, path: tuple[tuple[int, int]]) -> int:
"""
Returns the index of current path segment and the distance along the path segment.
Returns the index of the current path segment and the distance along the path segment.
Parameters:
waypoints:
path:
The waypoints of the path.
"""
# First find the length of each path segment
......@@ -37,8 +49,9 @@ class Enemy:
return i, segment_position
current_path_length += path_lengths[i]
if current_path_length >= full_path_length:
# Return the end of the last path segment if the path_position more than 1
# Return the end of the last path segment if the path_position is more than 1
return len(path) - 2, 1
def move_along_path(self, dt: float, path: tuple[tuple[int, int]]):
"""
Moves the enemy along the path.
......@@ -55,23 +68,48 @@ class Enemy:
path[path_segment][0] + (path[path_segment + 1][0] - path[path_segment][0]) * segment_position,
path[path_segment][1] + (path[path_segment + 1][1] - path[path_segment][1]) * segment_position
)
def draw(self, surface):
"""
Draws the enemy to the passed surface.
Draws the enemy and its health bar to the passed surface.
Parameters:
surface:
The surface to draw the enemy to.
"""
# Draw the enemy sprite
self.texture_rect = self.texture.get_rect()
self.texture_rect.center = self.screen_position
surface.blit(self.texture, self.texture_rect)
# Health bar bit
if self.hp > 0:
bar_width = self.texture_rect.width
bar_height = 6
health_ratio = self.hp / self.max_hp
health_bar_rect = pg.Rect(
self.texture_rect.left, self.texture_rect.top - bar_height - 4,
bar_width * health_ratio, bar_height
)
background_bar_rect = pg.Rect(
self.texture_rect.left, self.texture_rect.top - bar_height - 4,
bar_width, bar_height
)
# Background bar (gray)
pg.draw.rect(surface, (100, 100, 100), background_bar_rect)
# Foreground bar (green)
pg.draw.rect(surface, (0, 200, 0), health_bar_rect)
# TODO: Rotate the sprite to face the direction of movement.
#Hope you dont mind me slotting this here
def kill(self):
self.hp = 0
self.alive = False
class Zombie(Enemy):
"""Class for the zombie object."""
def __init__(self):
"""Initializes the zombie object."""
super().__init__(100, 1, 10,'assets/images/zombie/skeleton-idle_0.png')
super().__init__(100, 1, 10, 'assets/images/zombie/skeleton-idle_0.png')
......@@ -20,6 +20,7 @@ from world import World
from turret import Turret
from gui import GUI
def main():
# World object creation
world = World()
......@@ -32,17 +33,14 @@ def main():
dt = 1 / 1000
time_now = time.time()
# MOUSE TESTING
mouseSquare = pg.Rect(0, 0, 100, 100)
#Button implemnentation test :)
# Button implementation
button_width, button_height = 400, 60
button_x = world.window_resolution[0] - button_width - 100 # right
button_y = world.window_resolution[1] - button_height - 20 # Bottom
buy_button = pg.Rect(button_x, button_y, button_width, button_height)
#Neccessary boolean dont question >:(
# Necessary boolean
can_place_turrets = False
# Groups
......@@ -57,9 +55,6 @@ def main():
dt = time.time() - time_now
time_now = time.time()
#Turret delta time
turret_group.update(world.enemies, dt)
# Input handling
for event in pg.event.get():
if event.type == pg.QUIT:
......@@ -68,22 +63,11 @@ def main():
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
running = False
# Choose the tower to place with the number keys
if event.key == pg.K_1:
if active_tower != 2:
active_tower = 2
else:
active_tower = 0
if event.key == pg.K_2:
if active_tower != 2:
active_tower = 2
else:
active_tower = 0
# Start the round with the space key
if event.key == pg.K_SPACE and not world.round_active:
world.request_to_start_round = True
#Turret Placement logic central 101
# Turret placement logic
if event.type == pg.MOUSEBUTTONDOWN and event.button == 1:
mouse_pos = pg.mouse.get_pos()
# Buy button pressing check
......@@ -93,8 +77,6 @@ def main():
elif can_place_turrets:
Turret.spawn_turret(cursor_turret, mouse_pos, turret_group)
# Game state updates
if world.round_active:
# Spawn enemies at the correct time
......@@ -105,9 +87,11 @@ def main():
break
for enemy in world.enemies:
enemy.move_along_path(dt, world.waypoints)
# Dead ennemy removal
world.enemies = [enemy for enemy in world.enemies if enemy.alive]
#Turret turn update
turret_group.update(world.enemies)
# Update turrets (hitscan mechanism)
turret_group.update(world.enemies)
if world.request_to_start_round:
world.current_round += 1
......@@ -118,18 +102,22 @@ def main():
# Drawing
world.draw()
# Box for collision tower purposes
# Highlight turret placement area
if can_place_turrets:
mouseSquare.center = pg.mouse.get_pos()
pg.draw.rect(world.window, (255, 0, 0), mouseSquare, 6, 1)
# Turret draw
mouse_pos = pg.mouse.get_pos()
placement_indicator = pg.Rect(0, 0, 100, 100)
placement_indicator.center = mouse_pos
pg.draw.rect(world.window, (255, 0, 0), placement_indicator, 6, 1)
# Draw turrets
turret_group.draw(world.window)
#Button
button_color = (0, 200, 0) if can_place_turrets else (200, 0, 0) #I think feedback is good if the button is active or not
# Buy button
button_color = (0, 200, 0) if can_place_turrets else (200, 0, 0)
pg.draw.rect(world.window, button_color, buy_button)
gui.draw_text(world.window, "BUY TURRET", (button_x + button_width / 2, button_y + button_height / 2), 'center')
# Draw the round number
gui.draw_text(world.window, f"ROUND: {world.current_round}", (world.window_resolution[0] - margin, margin), 'topright')
......
import pygame as pg
import math
#need to make init actually take numbers instead of preset
class Turret(pg.sprite.Sprite):
def __init__(self, image, pos):
def __init__(self, image, pos, range_radius=200, damage=10, fire_rate=0.5):
super().__init__()
self.original_image = image
self.image = image
self.rect = self.image.get_rect()
self.rect.center = pos
self.target_angle = 0
self.range_radius = range_radius
self.damage = damage
self.fire_rate = fire_rate
self.last_shot_time = 0
@staticmethod
def spawn_turret(image, mouse_pos, turret_group):
#Spawn function that actually doesnt allow collision wow :) (I spent 4 hours on this :(.)
collision = False
if mouse_pos[0] < 1920 and mouse_pos[1] < 1080:
temp_turret = Turret(image, mouse_pos)
......@@ -22,28 +25,43 @@ class Turret(pg.sprite.Sprite):
turret_group.add(temp_turret)
def update(self, enemies):
#Basically finds the nearest enemy :)
if enemies:
# Find the closest enemy
nearest_enemy = min(enemies, key=lambda e: self.distance_to_enemy(e))
self.target_angle = self.calculate_angle(nearest_enemy)
self.rotate_turret()
nearest_enemy = self.find_nearest_enemy(enemies)
if nearest_enemy and self.is_within_range(nearest_enemy):
# Rotate towards the enemy
self.target_angle = self.calculate_angle(nearest_enemy)
self.rotate_turret()
# Check fire rate and shoot
current_time = pg.time.get_ticks() / 1000 # Convert ms to seconds
if current_time - self.last_shot_time >= self.fire_rate:
self.shoot(nearest_enemy)
self.last_shot_time = current_time
def find_nearest_enemy(self, enemies):
return min(enemies, key=lambda e: self.distance_to_enemy(e), default=None)
def is_within_range(self, enemy): return self.distance_to_enemy(enemy) <= self.range_radius
#I hate math (feeds towards update)
def distance_to_enemy(self, enemy):
return math.hypot(enemy.screen_position[0] - self.rect.centerx,
enemy.screen_position[1] - self.rect.centery)
#Angel towards enemy (Feeds towards update)
#I hate meth
def calculate_angle(self, enemy):
dx = enemy.screen_position[0] - self.rect.centerx
dy = enemy.screen_position[1] - self.rect.centery
angle = math.degrees(math.atan2(-dy, dx)) # Negative Y-axis correction
return angle - 90 # Adjust for the turret facing upward
angle = math.degrees(math.atan2(-dy, dx))
return angle - 90
#Whats written on the tin (Feesd towards update)
def rotate_turret(self):
"""Rotate the turret image based on the calculated angle."""
self.image = pg.transform.rotate(self.original_image, self.target_angle)
self.rect = self.image.get_rect(center=self.rect.center)
def shoot(self, enemy):
#Print is for console, purposes, might disable later???
print(f"Turret at {self.rect.center} hits enemy at {enemy.screen_position} for {self.damage} damage!")
enemy.take_damage(self.damage) # Use the take_damage method
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment