|
| 1 | +import pygame |
| 2 | +import math |
| 3 | +import random |
| 4 | + |
| 5 | +TILE_SIZE = 40 |
| 6 | +ENEMY_SPEED = 2 |
| 7 | +HEARING_RANGE = 250 |
| 8 | +SIGHT_RANGE = 180 |
| 9 | +PATROL_SPEED = 1 |
| 10 | + |
| 11 | +class Enemy(pygame.sprite.Sprite): |
| 12 | + def __init__(self, color, walls, damage=10): |
| 13 | + super().__init__() |
| 14 | + self.image = pygame.Surface((TILE_SIZE // 2, TILE_SIZE // 2)) |
| 15 | + self.image.fill(color) |
| 16 | + self.rect = self.image.get_rect(center=self.random_position(walls)) |
| 17 | + self.target = None |
| 18 | + self.damage = damage |
| 19 | + |
| 20 | + def random_position(self, walls): |
| 21 | + pos = (random.randint(TILE_SIZE, 800 - TILE_SIZE), random.randint(TILE_SIZE, 600 - TILE_SIZE)) |
| 22 | + while any(self.rect.colliderect(wall.rect) for wall in walls): |
| 23 | + pos = (random.randint(TILE_SIZE, 800 - TILE_SIZE), random.randint(TILE_SIZE, 600 - TILE_SIZE)) |
| 24 | + return pos |
| 25 | + |
| 26 | + def move_towards(self, target_pos, walls, speed=ENEMY_SPEED): |
| 27 | + dx = target_pos[0] - self.rect.centerx |
| 28 | + dy = target_pos[1] - self.rect.centery |
| 29 | + dist = math.hypot(dx, dy) |
| 30 | + if dist > 0: |
| 31 | + dx, dy = dx / dist * speed, dy / dist * speed |
| 32 | + new_rect = self.rect.move(dx, dy) |
| 33 | + if not any(new_rect.colliderect(wall.rect) for wall in walls): |
| 34 | + self.rect = new_rect |
| 35 | + |
| 36 | + def is_in_light(self, light_positions): |
| 37 | + for pos, radius in light_positions: |
| 38 | + dist = math.hypot(self.rect.centerx - pos[0], self.rect.centery - pos[1]) |
| 39 | + if dist < radius: |
| 40 | + return True |
| 41 | + return False |
| 42 | + |
| 43 | +class SoundEnemy(Enemy): |
| 44 | + def __init__(self, walls): |
| 45 | + super().__init__((255, 0, 0), walls, damage=15) |
| 46 | + self.heard_sounds = [] # List of (pos, strength) |
| 47 | + |
| 48 | + def update(self, player, walls, sound_pos, light_positions): |
| 49 | + if sound_pos: |
| 50 | + dist = math.hypot(self.rect.centerx - sound_pos[0], self.rect.centery - sound_pos[1]) |
| 51 | + if dist < HEARING_RANGE: |
| 52 | + strength = HEARING_RANGE - dist |
| 53 | + self.heard_sounds.append((sound_pos, strength)) |
| 54 | + if self.heard_sounds: |
| 55 | + self.heard_sounds = [(pos, str - 1) for pos, str in self.heard_sounds if str > 0] |
| 56 | + if self.heard_sounds: |
| 57 | + strongest = max(self.heard_sounds, key=lambda x: x[1]) |
| 58 | + self.move_towards(strongest[0], walls) |
| 59 | + elif self.is_in_light(light_positions): |
| 60 | + self.move_towards(player.rect.center, walls) |
| 61 | + |
| 62 | +class LightEnemy(Enemy): |
| 63 | + def __init__(self, walls): |
| 64 | + super().__init__((0, 0, 255), walls, damage=20) |
| 65 | + |
| 66 | + def update(self, player, walls, sound_pos, light_positions): |
| 67 | + if self.is_in_light(light_positions): |
| 68 | + self.move_towards(player.rect.center, walls, speed=ENEMY_SPEED * 1.2) |
| 69 | + elif sound_pos and random.random() < 0.5: |
| 70 | + dist = math.hypot(self.rect.centerx - sound_pos[0], self.rect.centery - sound_pos[1]) |
| 71 | + if dist < HEARING_RANGE / 2: |
| 72 | + self.move_towards(sound_pos, walls) |
| 73 | + |
| 74 | +class PatrolEnemy(Enemy): |
| 75 | + def __init__(self, walls): |
| 76 | + super().__init__((0, 255, 0), walls, damage=10) |
| 77 | + self.patrol_points = [self.random_position(walls) for _ in range(4)] |
| 78 | + self.current_patrol = 0 |
| 79 | + |
| 80 | + def update(self, player, walls, sound_pos, light_positions): |
| 81 | + if self.is_in_light(light_positions): |
| 82 | + self.move_towards(player.rect.center, walls) |
| 83 | + elif sound_pos: |
| 84 | + dist = math.hypot(self.rect.centerx - sound_pos[0], self.rect.centery - sound_pos[1]) |
| 85 | + if dist < HEARING_RANGE: |
| 86 | + self.move_towards(sound_pos, walls) |
| 87 | + else: |
| 88 | + # Patrol |
| 89 | + target = self.patrol_points[self.current_patrol] |
| 90 | + self.move_towards(target, walls, speed=PATROL_SPEED) |
| 91 | + if math.hypot(self.rect.centerx - target[0], self.rect.centery - target[1]) < 10: |
| 92 | + self.current_patrol = (self.current_patrol + 1) % len(self.patrol_points) |
0 commit comments