diff --git a/config.py b/config.py index 1995967..c9f9d0c 100644 --- a/config.py +++ b/config.py @@ -31,6 +31,7 @@ class Config: # ===== Симулятор робота ===== MAP_WIDTH = 800 MAP_HEIGHT = 600 + BORDER_THICKNESS = 10 MAP_OBSTACLES = [ (200, 150, 100, 200), (500, 300, 150, 50), @@ -41,7 +42,7 @@ class Config: ROBOT_IMAGE_PATH = 'robot.png' # или None INITIAL_HP = 100 - INITIAL_CHARGE = 100 + INITIAL_FIRE_CHARGE = 4 COLLISION_DAMAGE = 10 # Конус перед роботом diff --git a/main.py b/main.py index bb2b6fb..4c79803 100644 --- a/main.py +++ b/main.py @@ -63,6 +63,7 @@ def main(): landmarks = result['landmarks'] # Статический жест + special = special_detector.predict(landmarks) # Вкл/выкл управления жестами if special == 'cross' and cross_cooldown == 0: @@ -93,11 +94,13 @@ def main(): if dynamic_gesture: last_dynamic = dynamic_gesture dynamic_counter = 30 - + + ''' if dynamic_gesture == 'wave_left': robot.reset_position() elif dynamic_gesture == 'wave_right': - robot.restart_game() + robot.restart() + ''' # Отрисовка vis = detector.draw_landmarks(frame, result['pose_landmarks']) diff --git a/robot/simulated_robot.py b/robot/simulated_robot.py index 87391be..1bc5303 100644 --- a/robot/simulated_robot.py +++ b/robot/simulated_robot.py @@ -19,7 +19,18 @@ class DummySimRobot(RobotController): self.clock = pygame.time.Clock() self.font = pygame.font.SysFont(None, 28) - self.obstacles = config.MAP_OBSTACLES + border_thickness = config.BORDER_THICKNESS + if border_thickness > 0: + border_obstacles = [ + (0, 0, self.width, border_thickness), + (0, self.height - border_thickness, self.width, border_thickness), + (0, 0, border_thickness, self.height), + (self.width - border_thickness, 0, border_thickness, self.height) + ] + self.obstacles = list(config.MAP_OBSTACLES) + border_obstacles + else: + self.obstacles = list(config.MAP_OBSTACLES) + self.start = config.START_POS self.finish = config.FINISH_POS self.robot_radius = config.ROBOT_RADIUS @@ -34,8 +45,8 @@ class DummySimRobot(RobotController): self.max_hp = config.INITIAL_HP self.hp = self.max_hp - self.max_charge = config.INITIAL_CHARGE - self.charge = self.max_charge + self.max_fire_charge = config.INITIAL_FIRE_CHARGE + self.charge = self.max_fire_charge self.collision_damage = config.COLLISION_DAMAGE self.extinguish_radius = config.EXTINGUISH_RADIUS self.extinguish_angle = math.radians(config.EXTINGUISH_ANGLE) @@ -43,9 +54,18 @@ class DummySimRobot(RobotController): self.fires = [] self.num_fires = config.NUM_FIRES + self.fire_radius = config.FIRE_RADIUS + self.score = 0 self.game_over = False self.won = False + + self.extinguish_active = False + self.extinguish_timer = 0 + self.extinguish_cooldown = 0 + self.extinguish_cone_length = 80 + + self.invincibility_timer = 0 self.x, self.y = self.start self.angle = 0.0 @@ -57,17 +77,41 @@ class DummySimRobot(RobotController): def _generate_fires(self): self.fires.clear() attempts = 0 - while len(self.fires) < self.num_fires and attempts < 1000: - fx = random.randint(self.robot_radius, self.width - self.robot_radius) - fy = random.randint(self.robot_radius, self.height - self.robot_radius) + margin = 30 # минимальное расстояние между центрами костров + + while len(self.fires) < self.num_fires and attempts < 2000: + fx = random.randint(self.robot_radius + 10, self.width - self.robot_radius - 10) + fy = random.randint(self.robot_radius + 10, self.height - self.robot_radius - 10) + + # Проверка: не на препятствии if self._point_collides_with_obstacle(fx, fy, self.robot_radius): + attempts += 1 + continue + + # Проверка: не слишком близко к старту + if math.hypot(fx - self.start[0], fy - self.start[1]) < self.robot_radius * 3: + attempts += 1 continue - if math.hypot(fx - self.start[0], fy - self.start[1]) < self.robot_radius * 2: + + # Проверка: не слишком близко к финишу + if math.hypot(fx - self.finish[0], fy - self.finish[1]) < self.robot_radius * 3: + attempts += 1 continue - if math.hypot(fx - self.finish[0], fy - self.finish[1]) < self.robot_radius * 2: + + # Проверка: не пересекается с уже существующими кострами + overlap = False + for fire in self.fires: + if math.hypot(fx - fire.x, fy - fire.y) < 2 * self.fire_radius + margin: + overlap = True + break + if overlap: + attempts += 1 continue - self.fires.append(Fire(fx, fy, 15)) + + self.fires.append(Fire(fx, fy, self.fire_radius)) attempts += 1 + + print(f"Сгенерировано {len(self.fires)} очагов возгорания") def _point_collides_with_obstacle(self, x, y, radius): for ox, oy, ow, oh in self.obstacles: @@ -81,6 +125,7 @@ class DummySimRobot(RobotController): self.linear_speed = max(-1.0, min(1.0, v)) self.angular_speed = max(-1.0, min(1.0, w)) + ''' def send_fire_ext_burst_cmd(self): if self.charge <= 0: return False @@ -91,36 +136,72 @@ class DummySimRobot(RobotController): self.score += self.score_per_fire return True return False - - def _is_fire_in_front(self, fire): - dx = fire.x - self.x - dy = fire.y - self.y - dist = math.hypot(dx, dy) - if dist > self.extinguish_radius: + ''' + + def send_fire_ext_burst_cmd(self): + if self.extinguish_cooldown > 0 or self.extinguish_active: + return False + if self.charge <= 0: return False - angle_to_fire = math.atan2(dy, dx) - angle_diff = abs(angle_to_fire - self.angle) - angle_diff = min(angle_diff, 2*math.pi - angle_diff) - return angle_diff < self.extinguish_angle / 2 + + self.extinguish_active = True + self.extinguish_timer = 30 + self.extinguish_cooldown = 10 + + self.charge -= 1 + + cone_len = self.extinguish_cone_length + cone_angle_half = self.extinguish_angle / 2 + extinguished = False + + for fire in self.fires[:]: + dx = fire.x - self.x + dy = fire.y - self.y + dist = math.hypot(dx, dy) + if dist > cone_len: + continue + + angle_to_fire = math.atan2(dy, dx) + angle_diff = angle_to_fire - self.angle + angle_diff = (angle_diff + math.pi) % (2*math.pi) - math.pi #[-π, π] + + if abs(angle_diff) <= cone_angle_half: + self.fires.remove(fire) + self.score += self.score_per_fire + extinguished = True + break + + return extinguished def update(self, dt=1/30.0): if self.game_over or self.won: return - + + if self.invincibility_timer > 0: + self.invincibility_timer -= 1 + if self.extinguish_timer > 0: + self.extinguish_timer -= 1 + if self.extinguish_timer == 0: + self.extinguish_active = False + if self.extinguish_cooldown > 0: + self.extinguish_cooldown -= 1 + + # Движение self.angle += self.angular_speed * dt * 2 dx = self.linear_speed * math.cos(self.angle) * dt * 100 dy = self.linear_speed * math.sin(self.angle) * dt * 100 new_x = self.x + dx new_y = self.y + dy - - new_x = max(self.robot_radius, min(self.width - self.robot_radius, new_x)) - new_y = max(self.robot_radius, min(self.height - self.robot_radius, new_y)) - + + # Проверка: столкновение if self._point_collides_with_obstacle(new_x, new_y, self.robot_radius): - self.hp -= self.collision_damage + if self.invincibility_timer == 0: + self.hp -= self.collision_damage + self.invincibility_timer = 30 else: self.x, self.y = new_x, new_y - + + # Конец игры? if math.hypot(self.x - self.finish[0], self.y - self.finish[1]) < self.robot_radius: self.won = True if self.hp <= 0: @@ -128,26 +209,53 @@ class DummySimRobot(RobotController): def draw(self): self.screen.fill((255,255,255)) + + # Препятствия for ox, oy, ow, oh in self.obstacles: pygame.draw.rect(self.screen, (100,100,100), (ox, oy, ow, oh)) + + # Финиш pygame.draw.circle(self.screen, (0,200,0), (int(self.finish[0]), int(self.finish[1])), self.robot_radius, 3) + + # Костры for fire in self.fires: pygame.draw.circle(self.screen, (255,0,0), (int(fire.x), int(fire.y)), fire.radius) pygame.draw.circle(self.screen, (255,100,0), (int(fire.x), int(fire.y)), fire.radius-3) - - if self.robot_image: - rotated = pygame.transform.rotate(self.robot_image, -math.degrees(self.angle)) - rect = rotated.get_rect(center=(int(self.x), int(self.y))) - self.screen.blit(rotated, rect) + + # Конус тушения + if self.extinguish_active: + cone_surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA) + num_points = 20 + points = [(self.x, self.y)] + half_angle = self.extinguish_angle / 2 + cone_len = self.extinguish_cone_length + for i in range(num_points + 1): + angle = self.angle - half_angle + (self.extinguish_angle) * (i / num_points) + x = self.x + cone_len * math.cos(angle) + y = self.y + cone_len * math.sin(angle) + points.append((x, y)) + if len(points) > 2: + pygame.draw.polygon(cone_surface, (0, 200, 255, 80), points) + self.screen.blit(cone_surface, (0, 0)) + + # Робот + if self.invincibility_timer > 0 and (self.invincibility_timer % 6 < 3): + pass else: - nose = (self.x + self.robot_radius * math.cos(self.angle), - self.y + self.robot_radius * math.sin(self.angle)) - left = (self.x + self.robot_radius * math.cos(self.angle + 2.5), - self.y + self.robot_radius * math.sin(self.angle + 2.5)) - right = (self.x + self.robot_radius * math.cos(self.angle - 2.5), - self.y + self.robot_radius * math.sin(self.angle - 2.5)) - pygame.draw.polygon(self.screen, (0,0,255), [nose, left, right]) - + if self.robot_image: + rotated = pygame.transform.rotate(self.robot_image, -math.degrees(self.angle)) + rect = rotated.get_rect(center=(int(self.x), int(self.y))) + self.screen.blit(rotated, rect) + else: + nose = (self.x + self.robot_radius * math.cos(self.angle), + self.y + self.robot_radius * math.sin(self.angle)) + left = (self.x + self.robot_radius * math.cos(self.angle + 2.5), + self.y + self.robot_radius * math.sin(self.angle + 2.5)) + right = (self.x + self.robot_radius * math.cos(self.angle - 2.5), + self.y + self.robot_radius * math.sin(self.angle - 2.5)) + pygame.draw.polygon(self.screen, (0,0,255), [nose, left, right]) + + # Статистика hp_text = self.font.render(f"HP: {self.hp}", True, (0,0,0)) charge_text = self.font.render(f"Charge: {self.charge}", True, (0,0,0)) score_text = self.font.render(f"Score: {self.score}", True, (0,0,0)) @@ -156,7 +264,8 @@ class DummySimRobot(RobotController): self.screen.blit(charge_text, (10, 40)) self.screen.blit(score_text, (10, 70)) self.screen.blit(fires_text, (10, 100)) - + + # Конец игры if self.game_over: msg = self.font.render("GAME OVER – Press R to restart", True, (255,0,0)) self.screen.blit(msg, (self.width//2 - 150, self.height//2)) @@ -195,6 +304,10 @@ class DummySimRobot(RobotController): self.score = 0 self.game_over = False self.won = False + self.invincibility_timer = 0 + self.extinguish_active = False + self.extinguish_timer = 0 + self.extinguish_cooldown = 0 self._generate_fires() def reset_position(self):