This commit is contained in:
parent
bc128cef3d
commit
76408247b0
10 changed files with 598 additions and 185 deletions
|
@ -34,6 +34,14 @@
|
|||
"debug_mode": "Debug Tilstand:",
|
||||
"close": "Luk"
|
||||
},
|
||||
"settings": {
|
||||
"gameplay": "Gameplay",
|
||||
"accessibility": "Tilgængelighed",
|
||||
"show_vision_cones": "Vis Synsfelt",
|
||||
"show_detection_radius": "Vis Detektionsområder",
|
||||
"vision_cones_help": "Viser synsfelt for fjender og spiller",
|
||||
"detection_help": "Viser detektionsområder omkring sikkerhedsfunktioner"
|
||||
},
|
||||
"messages": {
|
||||
"placing_poop": "Placerer lortepose på position",
|
||||
"attempting_doorbell": "Forsøger at ringe på døren",
|
||||
|
|
|
@ -34,6 +34,14 @@
|
|||
"debug_mode": "Debug Modus:",
|
||||
"close": "Schließen"
|
||||
},
|
||||
"settings": {
|
||||
"gameplay": "Gameplay",
|
||||
"accessibility": "Barrierefreiheit",
|
||||
"show_vision_cones": "Sichtfelder Anzeigen",
|
||||
"show_detection_radius": "Erkennungsbereiche Anzeigen",
|
||||
"vision_cones_help": "Zeigt Sichtfelder für Feinde und Spieler",
|
||||
"detection_help": "Zeigt Erkennungsbereiche um Sicherheitsmerkmale"
|
||||
},
|
||||
"messages": {
|
||||
"placing_poop": "Kacktüte wird platziert an Position",
|
||||
"attempting_doorbell": "Versuche zu klingeln",
|
||||
|
|
|
@ -34,6 +34,14 @@
|
|||
"debug_mode": "Debug Mode:",
|
||||
"close": "Close"
|
||||
},
|
||||
"settings": {
|
||||
"gameplay": "Gameplay",
|
||||
"accessibility": "Accessibility",
|
||||
"show_vision_cones": "Show Vision Cones",
|
||||
"show_detection_radius": "Show Detection Areas",
|
||||
"vision_cones_help": "Shows field of view for enemies and player",
|
||||
"detection_help": "Shows detection areas around security features"
|
||||
},
|
||||
"messages": {
|
||||
"placing_poop": "Placing poop bag at position",
|
||||
"attempting_doorbell": "Attempting to ring doorbell",
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import 'package:flame/components.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shitman/game/components/vision_cone.dart';
|
||||
import 'package:shitman/game/shitman_game.dart';
|
||||
import 'dart:math';
|
||||
|
||||
class Neighborhood extends Component {
|
||||
static const double streetWidth = 60.0;
|
||||
static const double houseSize = 80.0;
|
||||
static const double yardSize = 40.0;
|
||||
|
||||
|
||||
List<House> houses = [];
|
||||
late List<Vector2> streetPaths;
|
||||
|
||||
|
@ -19,44 +21,44 @@ class Neighborhood extends Component {
|
|||
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));
|
||||
|
@ -73,32 +75,31 @@ class Neighborhood extends Component {
|
|||
@override
|
||||
void render(Canvas canvas) {
|
||||
super.render(canvas);
|
||||
|
||||
|
||||
// Draw streets
|
||||
final streetPaint = Paint()
|
||||
..color = const Color(0xFF333333);
|
||||
|
||||
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
|
||||
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
|
||||
col * (houseSize + streetWidth) - streetWidth / 2,
|
||||
0,
|
||||
streetWidth,
|
||||
600,
|
||||
),
|
||||
streetPaint,
|
||||
);
|
||||
|
@ -106,51 +107,84 @@ class Neighborhood extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
class House extends RectangleComponent {
|
||||
class House extends RectangleComponent with HasGameReference<ShitmanGame> {
|
||||
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,
|
||||
}) : super(
|
||||
position: position,
|
||||
size: Vector2.all(Neighborhood.houseSize),
|
||||
);
|
||||
}) : super(position: position, size: Vector2.all(Neighborhood.houseSize));
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
|
||||
// Set house color based on type
|
||||
paint = Paint()..color = _getHouseColor();
|
||||
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
return isTarget
|
||||
? const Color(0xFFFF6B6B)
|
||||
: const Color(0xFF228B22); // Green/Red if target
|
||||
default:
|
||||
return const Color(0xFF696969);
|
||||
}
|
||||
|
@ -159,58 +193,69 @@ class House extends RectangleComponent {
|
|||
@override
|
||||
void render(Canvas canvas) {
|
||||
super.render(canvas);
|
||||
|
||||
|
||||
// Draw door
|
||||
final doorPaint = Paint()
|
||||
..color = const Color(0xFF654321);
|
||||
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);
|
||||
|
||||
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,
|
||||
);
|
||||
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,
|
||||
);
|
||||
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 (game.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;
|
||||
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,
|
||||
|
@ -220,17 +265,45 @@ class House extends RectangleComponent {
|
|||
}
|
||||
|
||||
double getDetectionRadius() {
|
||||
double radius = 50.0;
|
||||
if (hasLights) radius += 20.0;
|
||||
if (hasSecurityCamera) radius += 40.0;
|
||||
if (hasWatchDog) radius += 30.0;
|
||||
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);
|
||||
|
||||
return distance < detectionRadius;
|
||||
|
||||
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 = game.appSettings.getBool(
|
||||
'game.show_vision_cones',
|
||||
);
|
||||
visionCone!.updateOpacity(showVisionCones ? 0.3 : 0.0);
|
||||
} catch (e) {
|
||||
visionCone!.updateOpacity(0.0); // Hide if settings not ready
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,52 +1,64 @@
|
|||
import 'package:flame/components.dart';
|
||||
import 'package:flame/events.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:shitman/game/shitman_game.dart';
|
||||
import 'package:shitman/game/components/poop_bag.dart';
|
||||
import 'package:shitman/game/components/neighborhood.dart';
|
||||
import 'package:shitman/game/components/vision_cone.dart';
|
||||
import 'dart:math';
|
||||
|
||||
class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
|
||||
static const double speed = 100.0;
|
||||
static const double playerSize = 32.0;
|
||||
|
||||
|
||||
Vector2 velocity = Vector2.zero();
|
||||
bool hasPoopBag = true;
|
||||
bool isHidden = false;
|
||||
double stealthLevel = 0.0; // 0.0 = fully visible, 1.0 = completely hidden
|
||||
PoopBag? placedPoopBag;
|
||||
VisionCone? playerVisionCone;
|
||||
double lastMovementDirection = 0.0;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
|
||||
// Create a simple colored rectangle as player
|
||||
size = Vector2.all(playerSize);
|
||||
position = Vector2(400, 300); // Start in center
|
||||
|
||||
position = Vector2(200, 200); // Start at center intersection
|
||||
|
||||
// Set player color
|
||||
paint = Paint()..color = const Color(0xFF0000FF); // Blue player
|
||||
}
|
||||
|
||||
// Create player vision cone
|
||||
playerVisionCone = VisionCone(
|
||||
origin: size / 2, // Relative to player center
|
||||
direction: lastMovementDirection,
|
||||
range: 80.0,
|
||||
fov: pi / 2, // 90 degrees
|
||||
color: Colors.blue,
|
||||
opacity: 0.0, // Start hidden
|
||||
);
|
||||
add(playerVisionCone!);
|
||||
}
|
||||
|
||||
void handleInput(Set<LogicalKeyboardKey> keysPressed) {
|
||||
velocity = Vector2.zero();
|
||||
|
||||
|
||||
// Movement controls
|
||||
if (keysPressed.contains(LogicalKeyboardKey.arrowUp) ||
|
||||
if (keysPressed.contains(LogicalKeyboardKey.arrowUp) ||
|
||||
keysPressed.contains(LogicalKeyboardKey.keyW)) {
|
||||
velocity.y -= speed;
|
||||
}
|
||||
if (keysPressed.contains(LogicalKeyboardKey.arrowDown) ||
|
||||
if (keysPressed.contains(LogicalKeyboardKey.arrowDown) ||
|
||||
keysPressed.contains(LogicalKeyboardKey.keyS)) {
|
||||
velocity.y += speed;
|
||||
}
|
||||
if (keysPressed.contains(LogicalKeyboardKey.arrowLeft) ||
|
||||
if (keysPressed.contains(LogicalKeyboardKey.arrowLeft) ||
|
||||
keysPressed.contains(LogicalKeyboardKey.keyA)) {
|
||||
velocity.x -= speed;
|
||||
}
|
||||
if (keysPressed.contains(LogicalKeyboardKey.arrowRight) ||
|
||||
if (keysPressed.contains(LogicalKeyboardKey.arrowRight) ||
|
||||
keysPressed.contains(LogicalKeyboardKey.keyD)) {
|
||||
velocity.x += speed;
|
||||
}
|
||||
|
@ -62,7 +74,6 @@ class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void updateStealthLevel(double dt) {
|
||||
// Simple stealth calculation - can be enhanced later
|
||||
// For now, player is more hidden when moving slowly or not at all
|
||||
|
@ -71,36 +82,37 @@ class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
|
|||
} else {
|
||||
stealthLevel = (stealthLevel - dt * 1.5).clamp(0.0, 1.0);
|
||||
}
|
||||
|
||||
|
||||
isHidden = stealthLevel > 0.7;
|
||||
}
|
||||
|
||||
void placePoop() {
|
||||
if (!hasPoopBag) return;
|
||||
|
||||
|
||||
debugPrint('Placing poop bag at $position');
|
||||
|
||||
|
||||
// Create and place the poop bag
|
||||
placedPoopBag = PoopBag();
|
||||
placedPoopBag!.position = position + Vector2(playerSize / 2, playerSize + 10);
|
||||
placedPoopBag!.position =
|
||||
position + Vector2(playerSize / 2, playerSize + 10);
|
||||
game.world.add(placedPoopBag!);
|
||||
|
||||
|
||||
hasPoopBag = false;
|
||||
|
||||
|
||||
// Check if near target house
|
||||
checkMissionProgress();
|
||||
}
|
||||
|
||||
void ringDoorbell() {
|
||||
debugPrint('Attempting to ring doorbell');
|
||||
|
||||
|
||||
// Check if near target house door
|
||||
if (game.targetHouse.isPlayerNearTarget(position)) {
|
||||
if (placedPoopBag != null) {
|
||||
// Light the poop bag on fire
|
||||
placedPoopBag!.lightOnFire();
|
||||
debugPrint('Ding dong! Poop bag is lit! RUN!');
|
||||
|
||||
|
||||
// Start escape timer - player has limited time to escape
|
||||
startEscapeSequence();
|
||||
} else {
|
||||
|
@ -140,28 +152,47 @@ class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
|
|||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
|
||||
|
||||
// Apply movement
|
||||
if (velocity.length > 0) {
|
||||
velocity = velocity.normalized() * speed;
|
||||
position += velocity * dt;
|
||||
|
||||
|
||||
// Update movement direction for vision cone
|
||||
lastMovementDirection = atan2(velocity.y, velocity.x);
|
||||
|
||||
// Keep player on screen (basic bounds checking)
|
||||
position.x = position.x.clamp(0, 800 - size.x);
|
||||
position.y = position.y.clamp(0, 600 - size.y);
|
||||
}
|
||||
|
||||
|
||||
// Update stealth level based on environment
|
||||
updateStealthLevel(dt);
|
||||
|
||||
|
||||
// Check for detection by houses
|
||||
checkForDetection();
|
||||
|
||||
// Update player vision cone
|
||||
if (playerVisionCone != null) {
|
||||
playerVisionCone!.updatePosition(size / 2, lastMovementDirection);
|
||||
|
||||
// Update vision cone visibility based on settings
|
||||
try {
|
||||
final showVisionCones = game.appSettings.getBool(
|
||||
'game.show_vision_cones',
|
||||
);
|
||||
playerVisionCone!.updateOpacity(showVisionCones ? 0.2 : 0.0);
|
||||
} catch (e) {
|
||||
playerVisionCone!.updateOpacity(0.0); // Hide if settings not ready
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void checkForDetection() {
|
||||
final neighborhood = game.world.children.whereType<Neighborhood>().firstOrNull;
|
||||
final neighborhood =
|
||||
game.world.children.whereType<Neighborhood>().firstOrNull;
|
||||
if (neighborhood == null) return;
|
||||
|
||||
|
||||
for (final house in neighborhood.houses) {
|
||||
if (house.canDetectPlayer(position, stealthLevel)) {
|
||||
getDetected();
|
||||
|
@ -173,21 +204,29 @@ class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
|
|||
@override
|
||||
void render(Canvas canvas) {
|
||||
// Update paint color based on stealth level
|
||||
paint = Paint()
|
||||
..color = isHidden ?
|
||||
const Color(0xFF00FF00).withOpacity(0.7) : // Green when hidden
|
||||
const Color(0xFF0000FF).withOpacity(0.9); // Blue when visible
|
||||
|
||||
paint =
|
||||
Paint()
|
||||
..color =
|
||||
isHidden
|
||||
? const Color(0xFF00FF00).withValues(alpha: 0.7)
|
||||
: // Green when hidden
|
||||
const Color(
|
||||
0xFF0000FF,
|
||||
).withValues(alpha: 0.9); // Blue when visible
|
||||
|
||||
super.render(canvas);
|
||||
|
||||
|
||||
// Draw stealth indicator in debug mode
|
||||
if (game.debugMode) {
|
||||
final stealthPaint = Paint()
|
||||
..color = Color.lerp(const Color(0xFFFF0000), const Color(0xFF00FF00), stealthLevel)!;
|
||||
canvas.drawRect(
|
||||
Rect.fromLTWH(-5, -10, size.x + 10, 5),
|
||||
stealthPaint,
|
||||
);
|
||||
final stealthPaint =
|
||||
Paint()
|
||||
..color =
|
||||
Color.lerp(
|
||||
const Color(0xFFFF0000),
|
||||
const Color(0xFF00FF00),
|
||||
stealthLevel,
|
||||
)!;
|
||||
canvas.drawRect(Rect.fromLTWH(-5, -10, size.x + 10, 5), stealthPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flame/components.dart';
|
||||
import 'package:flame/effects.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'dart:math';
|
||||
|
||||
enum PoopBagState { placed, lit, burning, extinguished }
|
||||
|
@ -11,17 +10,17 @@ class PoopBag extends CircleComponent {
|
|||
double burnTimer = 0.0;
|
||||
static const double burnDuration = 3.0; // seconds to burn
|
||||
static const double bagSize = 16.0;
|
||||
|
||||
|
||||
late Vector2 smokeOffset;
|
||||
List<SmokeParticle> smokeParticles = [];
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
|
||||
radius = bagSize / 2;
|
||||
paint = Paint()..color = const Color(0xFF8B4513); // Brown color
|
||||
|
||||
|
||||
smokeOffset = Vector2(0, -radius - 5);
|
||||
}
|
||||
|
||||
|
@ -29,7 +28,7 @@ class PoopBag extends CircleComponent {
|
|||
if (state == PoopBagState.placed) {
|
||||
state = PoopBagState.lit;
|
||||
burnTimer = 0.0;
|
||||
|
||||
|
||||
// Add flame effect
|
||||
add(
|
||||
ScaleEffect.to(
|
||||
|
@ -37,7 +36,7 @@ class PoopBag extends CircleComponent {
|
|||
EffectController(duration: 0.5, infinite: true, reverseDuration: 0.5),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
debugPrint('Poop bag is now on fire!');
|
||||
}
|
||||
}
|
||||
|
@ -45,22 +44,23 @@ class PoopBag extends CircleComponent {
|
|||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
|
||||
|
||||
if (state == PoopBagState.lit) {
|
||||
burnTimer += dt;
|
||||
|
||||
|
||||
// Generate smoke particles
|
||||
if (burnTimer % 0.2 < dt) { // Every 0.2 seconds
|
||||
if (burnTimer % 0.2 < dt) {
|
||||
// Every 0.2 seconds
|
||||
generateSmokeParticle();
|
||||
}
|
||||
|
||||
|
||||
// Check if fully burned
|
||||
if (burnTimer >= burnDuration) {
|
||||
state = PoopBagState.burning;
|
||||
extinguish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update smoke particles
|
||||
smokeParticles.removeWhere((particle) {
|
||||
particle.update(dt);
|
||||
|
@ -71,10 +71,10 @@ class PoopBag extends CircleComponent {
|
|||
void generateSmokeParticle() {
|
||||
final random = Random();
|
||||
final particle = SmokeParticle(
|
||||
position: position + smokeOffset + Vector2(
|
||||
random.nextDouble() * 10 - 5,
|
||||
random.nextDouble() * 5,
|
||||
),
|
||||
position:
|
||||
position +
|
||||
smokeOffset +
|
||||
Vector2(random.nextDouble() * 10 - 5, random.nextDouble() * 5),
|
||||
);
|
||||
smokeParticles.add(particle);
|
||||
}
|
||||
|
@ -82,26 +82,28 @@ class PoopBag extends CircleComponent {
|
|||
void extinguish() {
|
||||
state = PoopBagState.extinguished;
|
||||
removeAll(children.whereType<Effect>());
|
||||
|
||||
|
||||
// Change to burnt color
|
||||
paint = Paint()..color = const Color(0xFF2F2F2F);
|
||||
|
||||
|
||||
debugPrint('Poop bag has burned out');
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
super.render(canvas);
|
||||
|
||||
|
||||
// Draw flame effect when lit
|
||||
if (state == PoopBagState.lit) {
|
||||
final flamePaint = Paint()
|
||||
..color = Color.lerp(
|
||||
const Color(0xFFFF4500),
|
||||
const Color(0xFFFFD700),
|
||||
sin(burnTimer * 10) * 0.5 + 0.5,
|
||||
)!;
|
||||
|
||||
final flamePaint =
|
||||
Paint()
|
||||
..color =
|
||||
Color.lerp(
|
||||
const Color(0xFFFF4500),
|
||||
const Color(0xFFFFD700),
|
||||
sin(burnTimer * 10) * 0.5 + 0.5,
|
||||
)!;
|
||||
|
||||
// Draw flickering flame
|
||||
canvas.drawCircle(
|
||||
Offset(0, -radius - 5),
|
||||
|
@ -109,7 +111,7 @@ class PoopBag extends CircleComponent {
|
|||
flamePaint,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Render smoke particles
|
||||
for (final particle in smokeParticles) {
|
||||
particle.render(canvas);
|
||||
|
@ -127,8 +129,8 @@ class SmokeParticle {
|
|||
double life;
|
||||
double maxLife;
|
||||
bool shouldRemove = false;
|
||||
|
||||
SmokeParticle({required this.position})
|
||||
|
||||
SmokeParticle({required this.position})
|
||||
: velocity = Vector2(
|
||||
Random().nextDouble() * 20 - 10,
|
||||
-Random().nextDouble() * 30 - 20,
|
||||
|
@ -140,7 +142,7 @@ class SmokeParticle {
|
|||
position += velocity * dt;
|
||||
velocity *= 0.98; // Slight air resistance
|
||||
life -= dt;
|
||||
|
||||
|
||||
if (life <= 0) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
|
@ -148,13 +150,13 @@ class SmokeParticle {
|
|||
|
||||
void render(Canvas canvas) {
|
||||
final alpha = (life / maxLife).clamp(0.0, 1.0);
|
||||
final smokePaint = Paint()
|
||||
..color = Color(0xFF666666).withOpacity(alpha * 0.3);
|
||||
|
||||
final smokePaint =
|
||||
Paint()..color = Color(0xFF666666).withValues(alpha: alpha * 0.3);
|
||||
|
||||
canvas.drawCircle(
|
||||
Offset(position.x, position.y),
|
||||
6.0 * (1.0 - life / maxLife),
|
||||
smokePaint,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
158
lib/game/components/vision_cone.dart
Normal file
158
lib/game/components/vision_cone.dart
Normal file
|
@ -0,0 +1,158 @@
|
|||
import 'package:flame/components.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:math';
|
||||
|
||||
class VisionCone extends PositionComponent {
|
||||
double direction; // Angle in radians
|
||||
final double range;
|
||||
final double fov; // Field of view angle in radians
|
||||
final Color color;
|
||||
double opacity;
|
||||
|
||||
List<Vector2> visionVertices = [];
|
||||
late Paint fillPaint;
|
||||
late Paint strokePaint;
|
||||
|
||||
VisionCone({
|
||||
required Vector2 origin,
|
||||
required this.direction,
|
||||
this.range = 150.0,
|
||||
this.fov = pi / 3, // 60 degrees
|
||||
this.color = Colors.red,
|
||||
this.opacity = 0.3,
|
||||
}) : super(position: origin);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
fillPaint = Paint()
|
||||
..color = color.withValues(alpha: opacity)
|
||||
..style = PaintingStyle.fill;
|
||||
|
||||
strokePaint = Paint()
|
||||
..color = color.withValues(alpha: opacity + 0.2)
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 2.0;
|
||||
|
||||
_calculateVisionCone();
|
||||
}
|
||||
|
||||
void updatePosition(Vector2 newOrigin, double newDirection) {
|
||||
position.setFrom(newOrigin);
|
||||
direction = newDirection;
|
||||
_calculateVisionCone();
|
||||
}
|
||||
|
||||
void _calculateVisionCone() {
|
||||
visionVertices.clear();
|
||||
|
||||
// Start from the origin (0,0 relative to this component)
|
||||
visionVertices.add(Vector2.zero());
|
||||
|
||||
// Calculate the two edge rays of the vision cone
|
||||
final leftAngle = direction - fov / 2;
|
||||
final rightAngle = direction + fov / 2;
|
||||
|
||||
// Create points along the vision cone arc
|
||||
const int arcPoints = 10;
|
||||
for (int i = 0; i <= arcPoints; i++) {
|
||||
final angle = leftAngle + (rightAngle - leftAngle) * (i / arcPoints);
|
||||
final point = Vector2(cos(angle), sin(angle)) * range;
|
||||
visionVertices.add(point);
|
||||
}
|
||||
|
||||
// Close the cone back to origin
|
||||
visionVertices.add(Vector2.zero());
|
||||
}
|
||||
|
||||
/// Check if a point is within the vision cone
|
||||
bool canSee(Vector2 point) {
|
||||
// Get the world position of this vision cone by adding parent position
|
||||
final parentPosition = (parent as PositionComponent?)?.position ?? Vector2.zero();
|
||||
final worldPosition = parentPosition + position;
|
||||
final toPoint = point - worldPosition;
|
||||
final distance = toPoint.length;
|
||||
|
||||
|
||||
// Check if within range
|
||||
if (distance > range) return false;
|
||||
|
||||
// Check if within angle
|
||||
final angleToPoint = atan2(toPoint.y, toPoint.x);
|
||||
final angleDiff = _normalizeAngle(angleToPoint - direction);
|
||||
|
||||
return angleDiff.abs() <= fov / 2;
|
||||
}
|
||||
|
||||
/// Normalize angle to [-pi, pi]
|
||||
double _normalizeAngle(double angle) {
|
||||
while (angle > pi) {
|
||||
angle -= 2 * pi;
|
||||
}
|
||||
while (angle < -pi) {
|
||||
angle += 2 * pi;
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
|
||||
/// Raycast from origin to target, checking for obstructions
|
||||
bool hasLineOfSight(Vector2 target, List<Component> obstacles) {
|
||||
// Simple line-of-sight check - can be enhanced with proper raycasting
|
||||
final parentPosition = (parent as PositionComponent?)?.position ?? Vector2.zero();
|
||||
final worldPosition = parentPosition + position;
|
||||
final rayDirection = target - worldPosition;
|
||||
final distance = rayDirection.length;
|
||||
final normalizedDir = rayDirection.normalized();
|
||||
|
||||
// Check points along the ray for obstacles
|
||||
const double stepSize = 5.0;
|
||||
for (double step = stepSize; step < distance; step += stepSize) {
|
||||
final checkPoint = worldPosition + normalizedDir * step;
|
||||
|
||||
// Check if this point intersects with any obstacles
|
||||
for (final obstacle in obstacles) {
|
||||
if (obstacle is RectangleComponent) {
|
||||
if (obstacle.containsPoint(checkPoint)) {
|
||||
return false; // Line of sight blocked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true; // Clear line of sight
|
||||
}
|
||||
|
||||
void updateOpacity(double newOpacity) {
|
||||
opacity = newOpacity;
|
||||
fillPaint = Paint()
|
||||
..color = color.withValues(alpha: opacity)
|
||||
..style = PaintingStyle.fill;
|
||||
|
||||
strokePaint = Paint()
|
||||
..color = color.withValues(alpha: opacity + 0.2)
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 2.0;
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
if (visionVertices.length < 3 || opacity <= 0.0) return;
|
||||
|
||||
// Create path for vision cone
|
||||
final path = Path();
|
||||
path.moveTo(visionVertices[0].x, visionVertices[0].y);
|
||||
|
||||
for (int i = 1; i < visionVertices.length; i++) {
|
||||
path.lineTo(visionVertices[i].x, visionVertices[i].y);
|
||||
}
|
||||
path.close();
|
||||
|
||||
// Draw filled vision cone
|
||||
canvas.drawPath(path, fillPaint);
|
||||
|
||||
// Draw vision cone outline
|
||||
canvas.drawPath(path, strokePaint);
|
||||
}
|
||||
|
||||
}
|
|
@ -15,15 +15,14 @@ import 'package:shitman/settings/app_settings.dart';
|
|||
|
||||
enum GameState { mainMenu, playing, paused, gameOver, missionComplete }
|
||||
|
||||
class ShitmanGame extends FlameGame with HasKeyboardHandlerComponents, HasCollisionDetection, AppSettings {
|
||||
class ShitmanGame extends FlameGame
|
||||
with HasKeyboardHandlerComponents, HasCollisionDetection, AppSettings {
|
||||
late Player player;
|
||||
late Neighborhood neighborhood;
|
||||
late TargetHouse targetHouse;
|
||||
late CameraComponent gameCamera;
|
||||
|
||||
|
||||
GameState gameState = GameState.mainMenu;
|
||||
@override
|
||||
bool debugMode = false;
|
||||
int missionScore = 0;
|
||||
int totalMissions = 0;
|
||||
bool infiniteMode = false;
|
||||
|
@ -32,17 +31,22 @@ class ShitmanGame extends FlameGame with HasKeyboardHandlerComponents, HasCollis
|
|||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
await initSettings();
|
||||
|
||||
|
||||
// Setup camera
|
||||
gameCamera = CameraComponent.withFixedResolution(
|
||||
world: world,
|
||||
width: 800,
|
||||
height: 600,
|
||||
);
|
||||
addAll([gameCamera, world]);
|
||||
|
||||
camera = gameCamera;
|
||||
addAll([world]);
|
||||
|
||||
// Initialize debug mode from settings
|
||||
debugMode = appSettings.getBool('game.debug_mode');
|
||||
try {
|
||||
debugMode = appSettings.getBool('game.debug_mode');
|
||||
} catch (e) {
|
||||
debugMode = false; // Fallback if settings not ready
|
||||
}
|
||||
}
|
||||
|
||||
void startGame() {
|
||||
|
@ -74,19 +78,19 @@ class ShitmanGame extends FlameGame with HasKeyboardHandlerComponents, HasCollis
|
|||
void initializeLevel() {
|
||||
// Clear previous level
|
||||
world.removeAll(world.children);
|
||||
|
||||
|
||||
// Create neighborhood
|
||||
neighborhood = Neighborhood();
|
||||
world.add(neighborhood);
|
||||
|
||||
|
||||
// Create target house
|
||||
targetHouse = TargetHouse();
|
||||
world.add(targetHouse);
|
||||
|
||||
|
||||
// Create player
|
||||
player = Player();
|
||||
world.add(player);
|
||||
|
||||
|
||||
// Setup camera to follow player
|
||||
gameCamera.follow(player);
|
||||
}
|
||||
|
@ -95,7 +99,7 @@ class ShitmanGame extends FlameGame with HasKeyboardHandlerComponents, HasCollis
|
|||
gameState = GameState.missionComplete;
|
||||
missionScore += 100;
|
||||
totalMissions++;
|
||||
|
||||
|
||||
if (infiniteMode) {
|
||||
// Generate new mission after delay
|
||||
Future.delayed(Duration(seconds: 2), () {
|
||||
|
@ -111,34 +115,38 @@ class ShitmanGame extends FlameGame with HasKeyboardHandlerComponents, HasCollis
|
|||
}
|
||||
|
||||
@override
|
||||
KeyEventResult onKeyEvent(KeyEvent event, Set<LogicalKeyboardKey> keysPressed) {
|
||||
KeyEventResult onKeyEvent(
|
||||
KeyEvent event,
|
||||
Set<LogicalKeyboardKey> keysPressed,
|
||||
) {
|
||||
super.onKeyEvent(event, keysPressed);
|
||||
if (gameState != GameState.playing) return KeyEventResult.ignored;
|
||||
|
||||
|
||||
// Handle pause
|
||||
if (keysPressed.contains(LogicalKeyboardKey.escape)) {
|
||||
pauseGame();
|
||||
overlays.add('PauseMenu');
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
|
||||
|
||||
// Handle player input
|
||||
player.handleInput(keysPressed);
|
||||
|
||||
|
||||
// Handle action keys on key down
|
||||
if (event is KeyDownEvent) {
|
||||
player.handleAction(event.logicalKey);
|
||||
}
|
||||
|
||||
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
|
||||
|
||||
// Only update game logic when playing
|
||||
if (gameState != GameState.playing) return;
|
||||
|
||||
|
||||
// Game-specific update logic here
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,5 +7,11 @@ final gameSettings = SettingsGroup(
|
|||
items: [
|
||||
/// Debug mode, additional elements to help with development
|
||||
BoolSetting(key: 'debug_mode', defaultValue: false),
|
||||
|
||||
/// Show vision cones for enemies and player (accessibility & debugging)
|
||||
BoolSetting(key: 'show_vision_cones', defaultValue: false),
|
||||
|
||||
/// Show detection radius circles around houses
|
||||
BoolSetting(key: 'show_detection_radius', defaultValue: false),
|
||||
],
|
||||
);
|
||||
|
|
|
@ -154,12 +154,40 @@ class MainMenuUI extends StatelessWidget with AppSettings {
|
|||
}
|
||||
}
|
||||
|
||||
class SettingsUI extends StatelessWidget with AppSettings {
|
||||
class SettingsUI extends StatefulWidget with AppSettings {
|
||||
static const String overlayID = 'Settings';
|
||||
final ShitmanGame game;
|
||||
|
||||
SettingsUI(this.game, {super.key});
|
||||
|
||||
@override
|
||||
State<SettingsUI> createState() => _SettingsUIState();
|
||||
}
|
||||
|
||||
class _SettingsUIState extends State<SettingsUI> with AppSettings {
|
||||
bool showVisionCones = false;
|
||||
bool showDetectionRadius = false;
|
||||
bool debugMode = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadSettings();
|
||||
}
|
||||
|
||||
void _loadSettings() {
|
||||
try {
|
||||
showVisionCones = widget.game.appSettings.getBool('game.show_vision_cones');
|
||||
showDetectionRadius = widget.game.appSettings.getBool('game.show_detection_radius');
|
||||
debugMode = widget.game.appSettings.getBool('game.debug_mode');
|
||||
} catch (e) {
|
||||
// Settings not ready, use defaults
|
||||
showVisionCones = false;
|
||||
showDetectionRadius = false;
|
||||
debugMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
|
@ -185,8 +213,8 @@ class SettingsUI extends StatelessWidget with AppSettings {
|
|||
IconButton(
|
||||
icon: Icon(Icons.close, color: Colors.white),
|
||||
onPressed: () {
|
||||
game.overlays.remove(SettingsUI.overlayID);
|
||||
game.overlays.add(MainMenuUI.overlayID);
|
||||
widget.game.overlays.remove(SettingsUI.overlayID);
|
||||
widget.game.overlays.add(MainMenuUI.overlayID);
|
||||
},
|
||||
),
|
||||
],
|
||||
|
@ -194,29 +222,100 @@ class SettingsUI extends StatelessWidget with AppSettings {
|
|||
SizedBox(height: 24),
|
||||
|
||||
// Language selector
|
||||
Text('Language / Sprog / Sprache:', style: TextStyle(color: Colors.white)),
|
||||
SizedBox(height: 8),
|
||||
NesDropdownMenu<String>(
|
||||
initialValue: context.locale.languageCode,
|
||||
entries: const [
|
||||
NesDropdownMenuEntry(
|
||||
value: 'en',
|
||||
label: 'English',
|
||||
),
|
||||
NesDropdownMenuEntry(
|
||||
value: 'da',
|
||||
label: 'Dansk',
|
||||
),
|
||||
NesDropdownMenuEntry(
|
||||
value: 'de',
|
||||
label: 'Deutsch',
|
||||
),
|
||||
],
|
||||
onChanged: (String? value) {
|
||||
if (value != null) {
|
||||
context.setLocale(Locale(value));
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
SizedBox(height: 16),
|
||||
|
||||
// Gameplay Settings Section
|
||||
Text(
|
||||
'settings.gameplay'.tr(),
|
||||
style: TextStyle(color: Colors.orange, fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
|
||||
// Vision Cones toggle
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('Language / Sprog / Sprache:', style: TextStyle(color: Colors.white)),
|
||||
DropdownButton<String>(
|
||||
value: context.locale.languageCode,
|
||||
dropdownColor: Colors.black,
|
||||
style: TextStyle(color: Colors.white),
|
||||
items: [
|
||||
DropdownMenuItem(value: 'en', child: Text('English', style: TextStyle(color: Colors.white))),
|
||||
DropdownMenuItem(value: 'da', child: Text('Dansk', style: TextStyle(color: Colors.white))),
|
||||
DropdownMenuItem(value: 'de', child: Text('Deutsch', style: TextStyle(color: Colors.white))),
|
||||
],
|
||||
onChanged: (String? newValue) {
|
||||
if (newValue != null) {
|
||||
context.setLocale(Locale(newValue));
|
||||
}
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('settings.show_vision_cones'.tr(), style: TextStyle(color: Colors.white)),
|
||||
Text('settings.vision_cones_help'.tr(), style: TextStyle(color: Colors.white60, fontSize: 11)),
|
||||
],
|
||||
),
|
||||
),
|
||||
NesCheckBox(
|
||||
value: showVisionCones,
|
||||
onChange: (value) async {
|
||||
setState(() {
|
||||
showVisionCones = value;
|
||||
});
|
||||
await widget.game.appSettings.setBool('game.show_vision_cones', value);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
|
||||
// Detection Radius toggle
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('settings.show_detection_radius'.tr(), style: TextStyle(color: Colors.white)),
|
||||
Text('settings.detection_help'.tr(), style: TextStyle(color: Colors.white60, fontSize: 11)),
|
||||
],
|
||||
),
|
||||
),
|
||||
NesCheckBox(
|
||||
value: showDetectionRadius,
|
||||
onChange: (value) async {
|
||||
setState(() {
|
||||
showDetectionRadius = value;
|
||||
});
|
||||
await widget.game.appSettings.setBool('game.show_detection_radius', value);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 16),
|
||||
SizedBox(height: 20),
|
||||
|
||||
// Accessibility Section
|
||||
Text(
|
||||
'settings.accessibility'.tr(),
|
||||
style: TextStyle(color: Colors.orange, fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
|
||||
// Debug mode toggle
|
||||
Row(
|
||||
|
@ -224,9 +323,13 @@ class SettingsUI extends StatelessWidget with AppSettings {
|
|||
children: [
|
||||
Text('ui.debug_mode'.tr(), style: TextStyle(color: Colors.white)),
|
||||
NesCheckBox(
|
||||
value: false, // TODO: Connect to settings
|
||||
onChange: (value) {
|
||||
// TODO: Update settings
|
||||
value: debugMode,
|
||||
onChange: (value) async {
|
||||
setState(() {
|
||||
debugMode = value;
|
||||
});
|
||||
await widget.game.appSettings.setBool('game.debug_mode', value);
|
||||
widget.game.debugMode = value;
|
||||
},
|
||||
),
|
||||
],
|
||||
|
@ -237,8 +340,8 @@ class SettingsUI extends StatelessWidget with AppSettings {
|
|||
child: NesButton(
|
||||
type: NesButtonType.primary,
|
||||
onPressed: () {
|
||||
game.overlays.remove(SettingsUI.overlayID);
|
||||
game.overlays.add(MainMenuUI.overlayID);
|
||||
widget.game.overlays.remove(SettingsUI.overlayID);
|
||||
widget.game.overlays.add(MainMenuUI.overlayID);
|
||||
},
|
||||
child: Text('menu.back_to_menu'.tr()),
|
||||
),
|
||||
|
|
Loading…
Add table
Reference in a new issue