some basic movement
Some checks failed
/ build-web (push) Has been cancelled

This commit is contained in:
zeyus 2025-07-22 22:35:21 +02:00
parent bc128cef3d
commit 76408247b0
Signed by: zeyus
GPG key ID: A836639BA719C614
10 changed files with 598 additions and 185 deletions

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -1,5 +1,7 @@
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 {
@ -75,8 +77,7 @@ class Neighborhood extends Component {
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++) {
@ -85,7 +86,7 @@ class Neighborhood extends Component {
0,
row * (houseSize + streetWidth) - streetWidth / 2,
800,
streetWidth
streetWidth,
),
streetPaint,
);
@ -98,7 +99,7 @@ class Neighborhood extends Component {
col * (houseSize + streetWidth) - streetWidth / 2,
0,
streetWidth,
600
600,
),
streetPaint,
);
@ -106,7 +107,7 @@ class Neighborhood extends Component {
}
}
class House extends RectangleComponent {
class House extends RectangleComponent with HasGameReference<ShitmanGame> {
bool isTarget;
int houseType;
bool hasLights = false;
@ -115,15 +116,13 @@ class House extends RectangleComponent {
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 {
@ -141,16 +140,51 @@ class House extends RectangleComponent {
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);
}
@ -161,16 +195,17 @@ class House extends RectangleComponent {
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(
@ -186,28 +221,38 @@ class House extends RectangleComponent {
// 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()
final targetPaint =
Paint()
..color = const Color(0xFFFF0000)
..style = PaintingStyle.stroke
..strokeWidth = 3.0;
@ -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
}
}
}
}

View file

@ -1,11 +1,11 @@
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;
@ -16,6 +16,8 @@ class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
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 {
@ -23,12 +25,22 @@ class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
// 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();
@ -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
@ -82,7 +93,8 @@ class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
// 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;
@ -146,6 +158,9 @@ class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
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);
@ -156,10 +171,26 @@ class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
// 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) {
@ -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);
}
}
}

View file

@ -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 }
@ -50,7 +49,8 @@ class PoopBag extends CircleComponent {
burnTimer += dt;
// Generate smoke particles
if (burnTimer % 0.2 < dt) { // Every 0.2 seconds
if (burnTimer % 0.2 < dt) {
// Every 0.2 seconds
generateSmokeParticle();
}
@ -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);
}
@ -95,8 +95,10 @@ class PoopBag extends CircleComponent {
// Draw flame effect when lit
if (state == PoopBagState.lit) {
final flamePaint = Paint()
..color = Color.lerp(
final flamePaint =
Paint()
..color =
Color.lerp(
const Color(0xFFFF4500),
const Color(0xFFFFD700),
sin(burnTimer * 10) * 0.5 + 0.5,
@ -148,8 +150,8 @@ 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),

View 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);
}
}

View file

@ -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;
@ -39,10 +38,15 @@ class ShitmanGame extends FlameGame with HasKeyboardHandlerComponents, HasCollis
width: 800,
height: 600,
);
addAll([gameCamera, world]);
camera = gameCamera;
addAll([world]);
// Initialize debug mode from settings
try {
debugMode = appSettings.getBool('game.debug_mode');
} catch (e) {
debugMode = false; // Fallback if settings not ready
}
}
void startGame() {
@ -111,7 +115,11 @@ 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

View file

@ -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),
],
);

View file

@ -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))),
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)),
],
onChanged: (String? newValue) {
if (newValue != null) {
context.setLocale(Locale(newValue));
}
),
),
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()),
),