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:",
|
"debug_mode": "Debug Tilstand:",
|
||||||
"close": "Luk"
|
"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": {
|
"messages": {
|
||||||
"placing_poop": "Placerer lortepose på position",
|
"placing_poop": "Placerer lortepose på position",
|
||||||
"attempting_doorbell": "Forsøger at ringe på døren",
|
"attempting_doorbell": "Forsøger at ringe på døren",
|
||||||
|
|
|
@ -34,6 +34,14 @@
|
||||||
"debug_mode": "Debug Modus:",
|
"debug_mode": "Debug Modus:",
|
||||||
"close": "Schließen"
|
"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": {
|
"messages": {
|
||||||
"placing_poop": "Kacktüte wird platziert an Position",
|
"placing_poop": "Kacktüte wird platziert an Position",
|
||||||
"attempting_doorbell": "Versuche zu klingeln",
|
"attempting_doorbell": "Versuche zu klingeln",
|
||||||
|
|
|
@ -34,6 +34,14 @@
|
||||||
"debug_mode": "Debug Mode:",
|
"debug_mode": "Debug Mode:",
|
||||||
"close": "Close"
|
"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": {
|
"messages": {
|
||||||
"placing_poop": "Placing poop bag at position",
|
"placing_poop": "Placing poop bag at position",
|
||||||
"attempting_doorbell": "Attempting to ring doorbell",
|
"attempting_doorbell": "Attempting to ring doorbell",
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import 'package:flame/components.dart';
|
import 'package:flame/components.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:shitman/game/components/vision_cone.dart';
|
||||||
|
import 'package:shitman/game/shitman_game.dart';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
class Neighborhood extends Component {
|
class Neighborhood extends Component {
|
||||||
|
@ -75,8 +77,7 @@ class Neighborhood extends Component {
|
||||||
super.render(canvas);
|
super.render(canvas);
|
||||||
|
|
||||||
// Draw streets
|
// Draw streets
|
||||||
final streetPaint = Paint()
|
final streetPaint = Paint()..color = const Color(0xFF333333);
|
||||||
..color = const Color(0xFF333333);
|
|
||||||
|
|
||||||
// Horizontal streets
|
// Horizontal streets
|
||||||
for (int row = 0; row <= 3; row++) {
|
for (int row = 0; row <= 3; row++) {
|
||||||
|
@ -85,7 +86,7 @@ class Neighborhood extends Component {
|
||||||
0,
|
0,
|
||||||
row * (houseSize + streetWidth) - streetWidth / 2,
|
row * (houseSize + streetWidth) - streetWidth / 2,
|
||||||
800,
|
800,
|
||||||
streetWidth
|
streetWidth,
|
||||||
),
|
),
|
||||||
streetPaint,
|
streetPaint,
|
||||||
);
|
);
|
||||||
|
@ -98,7 +99,7 @@ class Neighborhood extends Component {
|
||||||
col * (houseSize + streetWidth) - streetWidth / 2,
|
col * (houseSize + streetWidth) - streetWidth / 2,
|
||||||
0,
|
0,
|
||||||
streetWidth,
|
streetWidth,
|
||||||
600
|
600,
|
||||||
),
|
),
|
||||||
streetPaint,
|
streetPaint,
|
||||||
);
|
);
|
||||||
|
@ -106,7 +107,7 @@ class Neighborhood extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class House extends RectangleComponent {
|
class House extends RectangleComponent with HasGameReference<ShitmanGame> {
|
||||||
bool isTarget;
|
bool isTarget;
|
||||||
int houseType;
|
int houseType;
|
||||||
bool hasLights = false;
|
bool hasLights = false;
|
||||||
|
@ -115,15 +116,13 @@ class House extends RectangleComponent {
|
||||||
|
|
||||||
Vector2? doorPosition;
|
Vector2? doorPosition;
|
||||||
Vector2? yardCenter;
|
Vector2? yardCenter;
|
||||||
|
VisionCone? visionCone;
|
||||||
|
|
||||||
House({
|
House({
|
||||||
required Vector2 position,
|
required Vector2 position,
|
||||||
required this.isTarget,
|
required this.isTarget,
|
||||||
required this.houseType,
|
required this.houseType,
|
||||||
}) : super(
|
}) : super(position: position, size: Vector2.all(Neighborhood.houseSize));
|
||||||
position: position,
|
|
||||||
size: Vector2.all(Neighborhood.houseSize),
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> onLoad() async {
|
Future<void> onLoad() async {
|
||||||
|
@ -141,16 +140,51 @@ class House extends RectangleComponent {
|
||||||
hasLights = random.nextBool();
|
hasLights = random.nextBool();
|
||||||
hasSecurityCamera = random.nextDouble() < 0.3;
|
hasSecurityCamera = random.nextDouble() < 0.3;
|
||||||
hasWatchDog = random.nextDouble() < 0.2;
|
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() {
|
Color _getHouseColor() {
|
||||||
switch (houseType) {
|
switch (houseType) {
|
||||||
case 0:
|
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:
|
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:
|
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:
|
default:
|
||||||
return const Color(0xFF696969);
|
return const Color(0xFF696969);
|
||||||
}
|
}
|
||||||
|
@ -161,16 +195,17 @@ class House extends RectangleComponent {
|
||||||
super.render(canvas);
|
super.render(canvas);
|
||||||
|
|
||||||
// Draw door
|
// Draw door
|
||||||
final doorPaint = Paint()
|
final doorPaint = Paint()..color = const Color(0xFF654321);
|
||||||
..color = const Color(0xFF654321);
|
|
||||||
canvas.drawRect(
|
canvas.drawRect(
|
||||||
Rect.fromLTWH(size.x / 2 - 8, size.y - 4, 16, 4),
|
Rect.fromLTWH(size.x / 2 - 8, size.y - 4, 16, 4),
|
||||||
doorPaint,
|
doorPaint,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Draw windows
|
// Draw windows
|
||||||
final windowPaint = Paint()
|
final windowPaint =
|
||||||
..color = hasLights ? const Color(0xFFFFFF00) : const Color(0xFF87CEEB);
|
Paint()
|
||||||
|
..color =
|
||||||
|
hasLights ? const Color(0xFFFFFF00) : const Color(0xFF87CEEB);
|
||||||
|
|
||||||
// Left window
|
// Left window
|
||||||
canvas.drawRect(
|
canvas.drawRect(
|
||||||
|
@ -186,31 +221,41 @@ class House extends RectangleComponent {
|
||||||
|
|
||||||
// Draw security features
|
// Draw security features
|
||||||
if (hasSecurityCamera) {
|
if (hasSecurityCamera) {
|
||||||
final cameraPaint = Paint()
|
final cameraPaint = Paint()..color = const Color(0xFF000000);
|
||||||
..color = const Color(0xFF000000);
|
canvas.drawCircle(Offset(size.x * 0.9, size.y * 0.1), 4, cameraPaint);
|
||||||
canvas.drawCircle(
|
|
||||||
Offset(size.x * 0.9, size.y * 0.1),
|
|
||||||
4,
|
|
||||||
cameraPaint,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasWatchDog) {
|
if (hasWatchDog) {
|
||||||
// Draw dog house in yard
|
// Draw dog house in yard
|
||||||
final dogHousePaint = Paint()
|
final dogHousePaint = Paint()..color = const Color(0xFF8B4513);
|
||||||
..color = const Color(0xFF8B4513);
|
canvas.drawRect(Rect.fromLTWH(-20, size.y + 10, 15, 15), dogHousePaint);
|
||||||
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
|
// Draw target indicator
|
||||||
if (isTarget) {
|
if (isTarget) {
|
||||||
final targetPaint = Paint()
|
final targetPaint =
|
||||||
..color = const Color(0xFFFF0000)
|
Paint()
|
||||||
..style = PaintingStyle.stroke
|
..color = const Color(0xFFFF0000)
|
||||||
..strokeWidth = 3.0;
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 3.0;
|
||||||
canvas.drawCircle(
|
canvas.drawCircle(
|
||||||
Offset(size.x / 2, size.y / 2),
|
Offset(size.x / 2, size.y / 2),
|
||||||
size.x / 2 + 10,
|
size.x / 2 + 10,
|
||||||
|
@ -220,17 +265,45 @@ class House extends RectangleComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
double getDetectionRadius() {
|
double getDetectionRadius() {
|
||||||
double radius = 50.0;
|
double radius = 30.0; // Reduced base radius
|
||||||
if (hasLights) radius += 20.0;
|
if (hasLights) radius += 10.0; // Reduced light bonus
|
||||||
if (hasSecurityCamera) radius += 40.0;
|
if (hasSecurityCamera) radius += 20.0; // Reduced camera bonus
|
||||||
if (hasWatchDog) radius += 30.0;
|
if (hasWatchDog) radius += 15.0; // Reduced dog bonus
|
||||||
return radius;
|
return radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool canDetectPlayer(Vector2 playerPosition, double playerStealthLevel) {
|
bool canDetectPlayer(Vector2 playerPosition, double playerStealthLevel) {
|
||||||
|
// Basic radius detection
|
||||||
final distance = (playerPosition - yardCenter!).length;
|
final distance = (playerPosition - yardCenter!).length;
|
||||||
final detectionRadius = getDetectionRadius() * (1.0 - playerStealthLevel);
|
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,11 +1,11 @@
|
||||||
import 'package:flame/components.dart';
|
import 'package:flame/components.dart';
|
||||||
import 'package:flame/events.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:shitman/game/shitman_game.dart';
|
import 'package:shitman/game/shitman_game.dart';
|
||||||
import 'package:shitman/game/components/poop_bag.dart';
|
import 'package:shitman/game/components/poop_bag.dart';
|
||||||
import 'package:shitman/game/components/neighborhood.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> {
|
class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
|
||||||
static const double speed = 100.0;
|
static const double speed = 100.0;
|
||||||
|
@ -16,6 +16,8 @@ class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
|
||||||
bool isHidden = false;
|
bool isHidden = false;
|
||||||
double stealthLevel = 0.0; // 0.0 = fully visible, 1.0 = completely hidden
|
double stealthLevel = 0.0; // 0.0 = fully visible, 1.0 = completely hidden
|
||||||
PoopBag? placedPoopBag;
|
PoopBag? placedPoopBag;
|
||||||
|
VisionCone? playerVisionCone;
|
||||||
|
double lastMovementDirection = 0.0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> onLoad() async {
|
Future<void> onLoad() async {
|
||||||
|
@ -23,12 +25,22 @@ class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
|
||||||
|
|
||||||
// Create a simple colored rectangle as player
|
// Create a simple colored rectangle as player
|
||||||
size = Vector2.all(playerSize);
|
size = Vector2.all(playerSize);
|
||||||
position = Vector2(400, 300); // Start in center
|
position = Vector2(200, 200); // Start at center intersection
|
||||||
|
|
||||||
// Set player color
|
// Set player color
|
||||||
paint = Paint()..color = const Color(0xFF0000FF); // Blue player
|
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) {
|
void handleInput(Set<LogicalKeyboardKey> keysPressed) {
|
||||||
velocity = Vector2.zero();
|
velocity = Vector2.zero();
|
||||||
|
@ -62,7 +74,6 @@ class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void updateStealthLevel(double dt) {
|
void updateStealthLevel(double dt) {
|
||||||
// Simple stealth calculation - can be enhanced later
|
// Simple stealth calculation - can be enhanced later
|
||||||
// For now, player is more hidden when moving slowly or not at all
|
// 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
|
// Create and place the poop bag
|
||||||
placedPoopBag = PoopBag();
|
placedPoopBag = PoopBag();
|
||||||
placedPoopBag!.position = position + Vector2(playerSize / 2, playerSize + 10);
|
placedPoopBag!.position =
|
||||||
|
position + Vector2(playerSize / 2, playerSize + 10);
|
||||||
game.world.add(placedPoopBag!);
|
game.world.add(placedPoopBag!);
|
||||||
|
|
||||||
hasPoopBag = false;
|
hasPoopBag = false;
|
||||||
|
@ -146,6 +158,9 @@ class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
|
||||||
velocity = velocity.normalized() * speed;
|
velocity = velocity.normalized() * speed;
|
||||||
position += velocity * dt;
|
position += velocity * dt;
|
||||||
|
|
||||||
|
// Update movement direction for vision cone
|
||||||
|
lastMovementDirection = atan2(velocity.y, velocity.x);
|
||||||
|
|
||||||
// Keep player on screen (basic bounds checking)
|
// Keep player on screen (basic bounds checking)
|
||||||
position.x = position.x.clamp(0, 800 - size.x);
|
position.x = position.x.clamp(0, 800 - size.x);
|
||||||
position.y = position.y.clamp(0, 600 - size.y);
|
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
|
// Check for detection by houses
|
||||||
checkForDetection();
|
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() {
|
void checkForDetection() {
|
||||||
final neighborhood = game.world.children.whereType<Neighborhood>().firstOrNull;
|
final neighborhood =
|
||||||
|
game.world.children.whereType<Neighborhood>().firstOrNull;
|
||||||
if (neighborhood == null) return;
|
if (neighborhood == null) return;
|
||||||
|
|
||||||
for (final house in neighborhood.houses) {
|
for (final house in neighborhood.houses) {
|
||||||
|
@ -173,21 +204,29 @@ class Player extends RectangleComponent with HasGameReference<ShitmanGame> {
|
||||||
@override
|
@override
|
||||||
void render(Canvas canvas) {
|
void render(Canvas canvas) {
|
||||||
// Update paint color based on stealth level
|
// Update paint color based on stealth level
|
||||||
paint = Paint()
|
paint =
|
||||||
..color = isHidden ?
|
Paint()
|
||||||
const Color(0xFF00FF00).withOpacity(0.7) : // Green when hidden
|
..color =
|
||||||
const Color(0xFF0000FF).withOpacity(0.9); // Blue when visible
|
isHidden
|
||||||
|
? const Color(0xFF00FF00).withValues(alpha: 0.7)
|
||||||
|
: // Green when hidden
|
||||||
|
const Color(
|
||||||
|
0xFF0000FF,
|
||||||
|
).withValues(alpha: 0.9); // Blue when visible
|
||||||
|
|
||||||
super.render(canvas);
|
super.render(canvas);
|
||||||
|
|
||||||
// Draw stealth indicator in debug mode
|
// Draw stealth indicator in debug mode
|
||||||
if (game.debugMode) {
|
if (game.debugMode) {
|
||||||
final stealthPaint = Paint()
|
final stealthPaint =
|
||||||
..color = Color.lerp(const Color(0xFFFF0000), const Color(0xFF00FF00), stealthLevel)!;
|
Paint()
|
||||||
canvas.drawRect(
|
..color =
|
||||||
Rect.fromLTWH(-5, -10, size.x + 10, 5),
|
Color.lerp(
|
||||||
stealthPaint,
|
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/components.dart';
|
||||||
import 'package:flame/effects.dart';
|
import 'package:flame/effects.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
enum PoopBagState { placed, lit, burning, extinguished }
|
enum PoopBagState { placed, lit, burning, extinguished }
|
||||||
|
@ -50,7 +49,8 @@ class PoopBag extends CircleComponent {
|
||||||
burnTimer += dt;
|
burnTimer += dt;
|
||||||
|
|
||||||
// Generate smoke particles
|
// Generate smoke particles
|
||||||
if (burnTimer % 0.2 < dt) { // Every 0.2 seconds
|
if (burnTimer % 0.2 < dt) {
|
||||||
|
// Every 0.2 seconds
|
||||||
generateSmokeParticle();
|
generateSmokeParticle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,10 +71,10 @@ class PoopBag extends CircleComponent {
|
||||||
void generateSmokeParticle() {
|
void generateSmokeParticle() {
|
||||||
final random = Random();
|
final random = Random();
|
||||||
final particle = SmokeParticle(
|
final particle = SmokeParticle(
|
||||||
position: position + smokeOffset + Vector2(
|
position:
|
||||||
random.nextDouble() * 10 - 5,
|
position +
|
||||||
random.nextDouble() * 5,
|
smokeOffset +
|
||||||
),
|
Vector2(random.nextDouble() * 10 - 5, random.nextDouble() * 5),
|
||||||
);
|
);
|
||||||
smokeParticles.add(particle);
|
smokeParticles.add(particle);
|
||||||
}
|
}
|
||||||
|
@ -95,12 +95,14 @@ class PoopBag extends CircleComponent {
|
||||||
|
|
||||||
// Draw flame effect when lit
|
// Draw flame effect when lit
|
||||||
if (state == PoopBagState.lit) {
|
if (state == PoopBagState.lit) {
|
||||||
final flamePaint = Paint()
|
final flamePaint =
|
||||||
..color = Color.lerp(
|
Paint()
|
||||||
const Color(0xFFFF4500),
|
..color =
|
||||||
const Color(0xFFFFD700),
|
Color.lerp(
|
||||||
sin(burnTimer * 10) * 0.5 + 0.5,
|
const Color(0xFFFF4500),
|
||||||
)!;
|
const Color(0xFFFFD700),
|
||||||
|
sin(burnTimer * 10) * 0.5 + 0.5,
|
||||||
|
)!;
|
||||||
|
|
||||||
// Draw flickering flame
|
// Draw flickering flame
|
||||||
canvas.drawCircle(
|
canvas.drawCircle(
|
||||||
|
@ -148,8 +150,8 @@ class SmokeParticle {
|
||||||
|
|
||||||
void render(Canvas canvas) {
|
void render(Canvas canvas) {
|
||||||
final alpha = (life / maxLife).clamp(0.0, 1.0);
|
final alpha = (life / maxLife).clamp(0.0, 1.0);
|
||||||
final smokePaint = Paint()
|
final smokePaint =
|
||||||
..color = Color(0xFF666666).withOpacity(alpha * 0.3);
|
Paint()..color = Color(0xFF666666).withValues(alpha: alpha * 0.3);
|
||||||
|
|
||||||
canvas.drawCircle(
|
canvas.drawCircle(
|
||||||
Offset(position.x, position.y),
|
Offset(position.x, position.y),
|
||||||
|
|
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 }
|
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 Player player;
|
||||||
late Neighborhood neighborhood;
|
late Neighborhood neighborhood;
|
||||||
late TargetHouse targetHouse;
|
late TargetHouse targetHouse;
|
||||||
late CameraComponent gameCamera;
|
late CameraComponent gameCamera;
|
||||||
|
|
||||||
GameState gameState = GameState.mainMenu;
|
GameState gameState = GameState.mainMenu;
|
||||||
@override
|
|
||||||
bool debugMode = false;
|
|
||||||
int missionScore = 0;
|
int missionScore = 0;
|
||||||
int totalMissions = 0;
|
int totalMissions = 0;
|
||||||
bool infiniteMode = false;
|
bool infiniteMode = false;
|
||||||
|
@ -39,10 +38,15 @@ class ShitmanGame extends FlameGame with HasKeyboardHandlerComponents, HasCollis
|
||||||
width: 800,
|
width: 800,
|
||||||
height: 600,
|
height: 600,
|
||||||
);
|
);
|
||||||
addAll([gameCamera, world]);
|
camera = gameCamera;
|
||||||
|
addAll([world]);
|
||||||
|
|
||||||
// Initialize debug mode from settings
|
// 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() {
|
void startGame() {
|
||||||
|
@ -111,7 +115,11 @@ class ShitmanGame extends FlameGame with HasKeyboardHandlerComponents, HasCollis
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@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;
|
if (gameState != GameState.playing) return KeyEventResult.ignored;
|
||||||
|
|
||||||
// Handle pause
|
// Handle pause
|
||||||
|
|
|
@ -7,5 +7,11 @@ final gameSettings = SettingsGroup(
|
||||||
items: [
|
items: [
|
||||||
/// Debug mode, additional elements to help with development
|
/// Debug mode, additional elements to help with development
|
||||||
BoolSetting(key: 'debug_mode', defaultValue: false),
|
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';
|
static const String overlayID = 'Settings';
|
||||||
final ShitmanGame game;
|
final ShitmanGame game;
|
||||||
|
|
||||||
SettingsUI(this.game, {super.key});
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Material(
|
return Material(
|
||||||
|
@ -185,8 +213,8 @@ class SettingsUI extends StatelessWidget with AppSettings {
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.close, color: Colors.white),
|
icon: Icon(Icons.close, color: Colors.white),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
game.overlays.remove(SettingsUI.overlayID);
|
widget.game.overlays.remove(SettingsUI.overlayID);
|
||||||
game.overlays.add(MainMenuUI.overlayID);
|
widget.game.overlays.add(MainMenuUI.overlayID);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -194,29 +222,100 @@ class SettingsUI extends StatelessWidget with AppSettings {
|
||||||
SizedBox(height: 24),
|
SizedBox(height: 24),
|
||||||
|
|
||||||
// Language selector
|
// 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(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text('Language / Sprog / Sprache:', style: TextStyle(color: Colors.white)),
|
Expanded(
|
||||||
DropdownButton<String>(
|
child: Column(
|
||||||
value: context.locale.languageCode,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
dropdownColor: Colors.black,
|
children: [
|
||||||
style: TextStyle(color: Colors.white),
|
Text('settings.show_vision_cones'.tr(), style: TextStyle(color: Colors.white)),
|
||||||
items: [
|
Text('settings.vision_cones_help'.tr(), style: TextStyle(color: Colors.white60, fontSize: 11)),
|
||||||
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))),
|
),
|
||||||
],
|
NesCheckBox(
|
||||||
onChanged: (String? newValue) {
|
value: showVisionCones,
|
||||||
if (newValue != null) {
|
onChange: (value) async {
|
||||||
context.setLocale(Locale(newValue));
|
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
|
// Debug mode toggle
|
||||||
Row(
|
Row(
|
||||||
|
@ -224,9 +323,13 @@ class SettingsUI extends StatelessWidget with AppSettings {
|
||||||
children: [
|
children: [
|
||||||
Text('ui.debug_mode'.tr(), style: TextStyle(color: Colors.white)),
|
Text('ui.debug_mode'.tr(), style: TextStyle(color: Colors.white)),
|
||||||
NesCheckBox(
|
NesCheckBox(
|
||||||
value: false, // TODO: Connect to settings
|
value: debugMode,
|
||||||
onChange: (value) {
|
onChange: (value) async {
|
||||||
// TODO: Update settings
|
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(
|
child: NesButton(
|
||||||
type: NesButtonType.primary,
|
type: NesButtonType.primary,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
game.overlays.remove(SettingsUI.overlayID);
|
widget.game.overlays.remove(SettingsUI.overlayID);
|
||||||
game.overlays.add(MainMenuUI.overlayID);
|
widget.game.overlays.add(MainMenuUI.overlayID);
|
||||||
},
|
},
|
||||||
child: Text('menu.back_to_menu'.tr()),
|
child: Text('menu.back_to_menu'.tr()),
|
||||||
),
|
),
|
||||||
|
|
Loading…
Add table
Reference in a new issue