import 'package:flame/components.dart'; import 'package:flutter/material.dart'; import 'package:shitman/game/components/vision_cone.dart'; import 'package:shitman/game/components/base.dart'; import 'package:shitman/settings/app_settings.dart'; import 'dart:math'; class Neighborhood extends DecorativeShit with Stationary, AppSettings { static const double streetWidth = 60.0; static const double houseSize = 80.0; static const double yardSize = 40.0; List houses = []; late List streetPaths; @override Future onLoad() async { await super.onLoad(); await initSettings(); generateNeighborhood(); appLog.fine('Neighborhood loaded with ${houses.length} houses'); } void generateNeighborhood() { houses.clear(); removeAll(children); // Create a simple 3x3 grid of houses final random = Random(); for (int row = 0; row < 3; row++) { for (int col = 0; col < 3; col++) { // Skip center for street intersection if (row == 1 && col == 1) continue; final housePosition = Vector2( col * (houseSize + streetWidth) + streetWidth, row * (houseSize + streetWidth) + streetWidth, ); final house = House( position: housePosition, isTarget: false, // Target will be set separately houseType: random.nextInt(3), // 3 different house types ); houses.add(house); add(house); } } // Generate street paths generateStreetPaths(); } void generateStreetPaths() { streetPaths = []; // Horizontal streets for (int i = 0; i < 4; i++) { streetPaths.add(Vector2(0, i * (houseSize + streetWidth))); streetPaths.add(Vector2(800, i * (houseSize + streetWidth))); } // Vertical streets for (int i = 0; i < 4; i++) { streetPaths.add(Vector2(i * (houseSize + streetWidth), 0)); streetPaths.add(Vector2(i * (houseSize + streetWidth), 600)); } } House? getRandomHouse() { if (houses.isEmpty) return null; final random = Random(); return houses[random.nextInt(houses.length)]; } @override void render(Canvas canvas) { // Draw streets final streetPaint = Paint()..color = const Color(0xFF333333); // Horizontal streets for (int row = 0; row <= 3; row++) { canvas.drawRect( Rect.fromLTWH( 0, row * (houseSize + streetWidth) - streetWidth / 2, 800, streetWidth, ), streetPaint, ); } // Vertical streets for (int col = 0; col <= 3; col++) { canvas.drawRect( Rect.fromLTWH( col * (houseSize + streetWidth) - streetWidth / 2, 0, streetWidth, 600, ), streetPaint, ); } } @override Future reset() async { appLog.fine('Resetting neighborhood'); // Reset all houses for (final house in houses) { await house.reset(); } // Regenerate neighborhood if needed generateNeighborhood(); } } class House extends DecorativeShit with Stationary, AppSettings { bool isTarget; int houseType; bool hasLights = false; bool hasSecurityCamera = false; bool hasWatchDog = false; Vector2? doorPosition; Vector2? yardCenter; VisionCone? visionCone; House({ required Vector2 position, required this.isTarget, required this.houseType, }) { this.position = position; size = Vector2.all(Neighborhood.houseSize); } @override Future onLoad() async { await super.onLoad(); await initSettings(); // Calculate door and yard positions doorPosition = position + Vector2(size.x / 2, size.y); yardCenter = position + size / 2; // Randomly add security features final random = Random(); hasLights = random.nextBool(); hasSecurityCamera = random.nextDouble() < 0.3; hasWatchDog = random.nextDouble() < 0.2; // Create vision cone for houses with security cameras if (hasSecurityCamera) { _createVisionCone(); } appLog.fine('House loaded at $position (type: $houseType, target: $isTarget)'); } void _createVisionCone() { // Create vision cone facing towards the street final cameraPosition = Vector2(size.x * 0.9, size.y * 0.1); final direction = _getOptimalCameraDirection(); visionCone = VisionCone( origin: cameraPosition, direction: direction, range: 120.0, fov: pi / 2, // 90 degrees color: Colors.red, opacity: 0.2, ); add(visionCone!); } double _getOptimalCameraDirection() { // Point camera towards street/center area final centerOfMap = Vector2(400, 300); final houseCenter = position + size / 2; final toCenter = centerOfMap - houseCenter; return atan2(toCenter.y, toCenter.x); } Color _getHouseColor() { switch (houseType) { case 0: return isTarget ? const Color(0xFFFF6B6B) : const Color(0xFF8B4513); // Brown/Red if target case 1: return isTarget ? const Color(0xFFFF6B6B) : const Color(0xFF4682B4); // Blue/Red if target case 2: return isTarget ? const Color(0xFFFF6B6B) : const Color(0xFF228B22); // Green/Red if target default: return const Color(0xFF696969); } } @override void render(Canvas canvas) { // Draw house with color based on type and target status final housePaint = Paint()..color = _getHouseColor(); canvas.drawRect(size.toRect(), housePaint); // Draw door final doorPaint = Paint()..color = const Color(0xFF654321); canvas.drawRect( Rect.fromLTWH(size.x / 2 - 8, size.y - 4, 16, 4), doorPaint, ); // Draw windows final windowPaint = Paint() ..color = hasLights ? const Color(0xFFFFFF00) : const Color(0xFF87CEEB); // Left window canvas.drawRect( Rect.fromLTWH(size.x * 0.2, size.y * 0.3, 12, 12), windowPaint, ); // Right window canvas.drawRect( Rect.fromLTWH(size.x * 0.7, size.y * 0.3, 12, 12), windowPaint, ); // Draw security features if (hasSecurityCamera) { final cameraPaint = Paint()..color = const Color(0xFF000000); canvas.drawCircle(Offset(size.x * 0.9, size.y * 0.1), 4, cameraPaint); } if (hasWatchDog) { // Draw dog house in yard final dogHousePaint = Paint()..color = const Color(0xFF8B4513); canvas.drawRect(Rect.fromLTWH(-20, size.y + 10, 15, 15), dogHousePaint); } // Draw detection radius if setting is enabled try { if (appSettings.getBool('game.show_detection_radius')) { final radiusPaint = Paint() ..color = const Color(0xFFFF9800).withValues(alpha: 0.2) ..style = PaintingStyle.stroke ..strokeWidth = 2.0; canvas.drawCircle( Offset(size.x / 2, size.y / 2), getDetectionRadius(), radiusPaint, ); } } catch (e) { // Settings not ready, skip rendering } // Draw target indicator if (isTarget) { final targetPaint = Paint() ..color = const Color(0xFFFF0000) ..style = PaintingStyle.stroke ..strokeWidth = 3.0; canvas.drawCircle( Offset(size.x / 2, size.y / 2), size.x / 2 + 10, targetPaint, ); } } double getDetectionRadius() { double radius = 30.0; // Reduced base radius if (hasLights) radius += 10.0; // Reduced light bonus if (hasSecurityCamera) radius += 20.0; // Reduced camera bonus if (hasWatchDog) radius += 15.0; // Reduced dog bonus return radius; } bool canDetectPlayer(Vector2 playerPosition, double playerStealthLevel) { // Basic radius detection final distance = (playerPosition - yardCenter!).length; final detectionRadius = getDetectionRadius() * (1.0 - playerStealthLevel); bool radiusDetection = distance < detectionRadius; // Vision cone detection for security cameras bool visionDetection = false; if (hasSecurityCamera && visionCone != null) { visionDetection = visionCone!.canSee(playerPosition) && visionCone!.hasLineOfSight(playerPosition, []); } return radiusDetection || visionDetection; } @override void update(double dt) { super.update(dt); // Update vision cone visibility based on settings if (visionCone != null) { try { final showVisionCones = appSettings.getBool('game.show_vision_cones'); visionCone!.updateOpacity(showVisionCones ? 0.3 : 0.0); } catch (e) { visionCone!.updateOpacity(0.0); // Hide if settings not ready } } } @override Future reset() async { isTarget = false; hasLights = false; hasSecurityCamera = false; hasWatchDog = false; // Reset vision cone if (visionCone != null) { await visionCone!.reset(); removeAll(children.whereType()); visionCone = null; } // Regenerate security features final random = Random(); hasLights = random.nextBool(); hasSecurityCamera = random.nextDouble() < 0.3; hasWatchDog = random.nextDouble() < 0.2; // Recreate vision cone if needed if (hasSecurityCamera) { _createVisionCone(); } appLog.fine('House reset at $position'); } }