import 'package:flame/game.dart'; import 'package:flame/components.dart'; import 'package:flame/events.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:shitman/game/components/player.dart'; import 'package:shitman/game/levels/operation_shitstorm.dart'; import 'package:shitman/game/shitman_world.dart'; import 'package:shitman/ui/touch_controls.dart'; import 'package:shitman/settings/app_settings.dart'; /// Shitman Game /// A 2D top-down "hitman" style game, but instead of assassinating people, /// your objective is to place flaming bags of dog poop on the doorsteps of /// your targets without getting caught. enum GameState { mainMenu, playing, paused, gameOver, missionComplete } class ShitmanGame extends FlameGame with HasKeyboardHandlerComponents, HasCollisionDetection, AppSettings { late Player player; late OperationShitstorm currentLevel; late CameraComponent gameCamera; GameState gameState = GameState.mainMenu; int missionScore = 0; int totalMissions = 0; bool infiniteMode = false; @override Future onLoad() async { await super.onLoad(); await initSettings(); world = ShitmanWorld(); // Setup camera gameCamera = CameraComponent.withFixedResolution( world: world, width: 800, height: 600, ); 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 _initializeTouchControls() { try { final touchControlsEnabled = appSettings.getBool('ui.touch_enabled'); if (touchControlsEnabled) { overlays.add('TouchControls'); } } catch (e) { // If setting doesn't exist, auto-detect based on platform if (TouchDetection.isTouchDevice) { // Enable touch controls by default on touch devices try { appSettings.setBool('ui.touch_enabled', true); overlays.add('TouchControls'); } catch (e) { // Settings not ready, just add overlay overlays.add('TouchControls'); } } } } Future startGame() async { gameState = GameState.playing; await initializeLevel(); // Initialize touch controls when game starts _initializeTouchControls(); } Future startInfiniteMode() async { infiniteMode = true; overlays.remove('MainMenu'); overlays.add('InGameUI'); await startGame(); } void stopGame() { gameState = GameState.mainMenu; world.removeAll(world.children); missionScore = 0; // Remove touch controls when stopping game overlays.remove('TouchControls'); } void pauseGame() { gameState = GameState.paused; // Keep touch controls visible during pause for consistency } void resumeGame() { gameState = GameState.playing; // Touch controls remain visible } Future initializeLevel() async { // Clear previous level world.removeAll(world.children); // Create the Operation Shitstorm level currentLevel = OperationShitstorm(); // Add and wait for the level to load await world.add(currentLevel); // Start the level await currentLevel.startLevel(); // Get the player from the level player = currentLevel.player; // Setup camera to follow player gameCamera.follow(player); } void completeCurrentMission() { gameState = GameState.missionComplete; missionScore += 100; totalMissions++; if (infiniteMode) { // Generate new mission after delay, but keep allowing input Future.delayed(Duration(seconds: 2), () { currentLevel.selectRandomTarget(); gameState = GameState.playing; }); } } void failMission() { gameState = GameState.gameOver; // TODO: Show game over screen } @override KeyEventResult onKeyEvent( KeyEvent event, Set keysPressed, ) { super.onKeyEvent(event, keysPressed); if (gameState != GameState.playing && gameState != GameState.missionComplete && gameState != GameState.gameOver) { 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); } // Mission completion is now handled by the player's escape sequence // No automatic completion when near target return KeyEventResult.handled; } /// Handle touch movement from virtual joystick void handleTouchMovement(Vector2 input) { if (gameState == GameState.playing) { player.handleTouchMovement(input); } } /// Handle poop bag action from touch void handleTouchPoopBag() { if (gameState == GameState.playing) { player.handleTouchPoopBag(); } } /// Handle doorbell action from touch void handleTouchDoorbell() { if (gameState == GameState.playing) { player.handleTouchDoorbell(); } } /// Toggle touch controls on/off void toggleTouchControls(bool enabled) { try { appSettings.setBool('ui.touch_enabled', enabled); if (enabled && (gameState == GameState.playing || gameState == GameState.paused || gameState == GameState.missionComplete || gameState == GameState.gameOver)) { // Only show touch controls during actual gameplay states overlays.add('TouchControls'); } else { overlays.remove('TouchControls'); } } catch (e) { // Handle settings error } } @override void update(double dt) { super.update(dt); // Only update game logic when playing if (gameState != GameState.playing) return; // Game-specific update logic here } }