re factor rog
This commit is contained in:
98
lib/features/state/game_state.dart
Normal file
98
lib/features/state/game_state.dart
Normal file
@ -0,0 +1,98 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:rogapp/features/data/checkpoint.dart';
|
||||
import 'package:rogapp/features/data/user_location.dart';
|
||||
|
||||
enum GameStatus {
|
||||
notStarted,
|
||||
started,
|
||||
inProgress,
|
||||
atCheckpoint,
|
||||
gameCompleted,
|
||||
}
|
||||
|
||||
enum PendingAction {
|
||||
none,
|
||||
checkInPhoto,
|
||||
receiptPhoto,
|
||||
endGamePhoto,
|
||||
}
|
||||
|
||||
@immutable
|
||||
class GameState {
|
||||
final GameStatus status;
|
||||
final List<Feature> markers;
|
||||
final UserLocation? currentLocation;
|
||||
final Set<Feature> visitedMarkers;
|
||||
final bool isWithinStartRadius;
|
||||
final bool hasLeftStartArea;
|
||||
final bool hasVisitedCheckpoint;
|
||||
final Feature? currentCheckpoint;
|
||||
final bool checkpointPhotoTaken;
|
||||
final bool receiptPhotoTaken;
|
||||
final PendingAction pendingAction;
|
||||
|
||||
const GameState({
|
||||
this.status = GameStatus.notStarted,
|
||||
this.markers = const [],
|
||||
this.currentLocation,
|
||||
this.visitedMarkers = const {},
|
||||
this.isWithinStartRadius = false,
|
||||
this.hasLeftStartArea = false,
|
||||
this.hasVisitedCheckpoint = false,
|
||||
this.currentCheckpoint,
|
||||
this.checkpointPhotoTaken = false,
|
||||
this.receiptPhotoTaken = false,
|
||||
this.pendingAction = PendingAction.none,
|
||||
});
|
||||
|
||||
LatLng? getUserLatLng() {
|
||||
return currentLocation != null
|
||||
? LatLng(currentLocation!.latitude, currentLocation!.longitude)
|
||||
: null;
|
||||
}
|
||||
|
||||
GameState.fromMap(Map<String, dynamic> map)
|
||||
: status = GameStatus.values[map['status']],
|
||||
markers =
|
||||
List<Feature>.from(map['markers'].map((x) => Feature.fromMap(x))),
|
||||
currentLocation = UserLocation.fromMap(map['currentLocation']),
|
||||
visitedMarkers = Set<Feature>.from(
|
||||
map['visitedMarkers'].map((x) => Feature.fromMap(x))),
|
||||
isWithinStartRadius = map['isWithinStartRadius'],
|
||||
hasLeftStartArea = map['hasLeftStartArea'],
|
||||
hasVisitedCheckpoint = map['hasVisitedCheckpoint'],
|
||||
currentCheckpoint = map['currentCheckpoint'] != null
|
||||
? Feature.fromMap(map['currentCheckpoint'])
|
||||
: null,
|
||||
checkpointPhotoTaken = map['checkpointPhotoTaken'],
|
||||
receiptPhotoTaken = map['receiptPhotoTaken'],
|
||||
pendingAction = PendingAction.values[map['pendingAction']];
|
||||
|
||||
GameState copyWith({
|
||||
GameStatus? status,
|
||||
List<Feature>? markers,
|
||||
UserLocation? currentLocation,
|
||||
Set<Feature>? visitedMarkers,
|
||||
bool? isWithinStartRadius,
|
||||
bool? hasLeftStartArea,
|
||||
bool? hasVisitedCheckpoint,
|
||||
Feature? currentCheckpoint,
|
||||
bool? checkpointPhotoTaken,
|
||||
bool? receiptPhotoTaken,
|
||||
PendingAction? pendingAction,
|
||||
}) {
|
||||
return GameState(
|
||||
status: status ?? this.status,
|
||||
markers: markers ?? this.markers,
|
||||
currentLocation: currentLocation ?? this.currentLocation,
|
||||
visitedMarkers: visitedMarkers ?? this.visitedMarkers,
|
||||
isWithinStartRadius: isWithinStartRadius ?? this.isWithinStartRadius,
|
||||
hasLeftStartArea: hasLeftStartArea ?? this.hasLeftStartArea,
|
||||
hasVisitedCheckpoint: hasVisitedCheckpoint ?? this.hasVisitedCheckpoint,
|
||||
currentCheckpoint: currentCheckpoint ?? this.currentCheckpoint,
|
||||
checkpointPhotoTaken: checkpointPhotoTaken ?? this.checkpointPhotoTaken,
|
||||
receiptPhotoTaken: receiptPhotoTaken ?? this.receiptPhotoTaken,
|
||||
pendingAction: pendingAction ?? this.pendingAction);
|
||||
}
|
||||
}
|
||||
199
lib/features/state/game_view_model.dart
Normal file
199
lib/features/state/game_view_model.dart
Normal file
@ -0,0 +1,199 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:rogapp/features/data/checkpoint.dart';
|
||||
import 'package:rogapp/features/data/data_provider.dart';
|
||||
import 'package:rogapp/features/data/user_location.dart';
|
||||
import 'package:rogapp/features/state/game_state.dart';
|
||||
import 'package:rogapp/features/utils/params.dart';
|
||||
|
||||
final gameViewModelProvider =
|
||||
StateNotifierProvider<GameViewModel, GameState>((ref) {
|
||||
final dataProvider = DataProvider();
|
||||
return GameViewModel(dataProvider);
|
||||
});
|
||||
|
||||
class GameViewModel extends StateNotifier<GameState> {
|
||||
final DataProvider _dataProvider;
|
||||
|
||||
GameViewModel(this._dataProvider) : super(const GameState()) {
|
||||
loadInitialData();
|
||||
}
|
||||
|
||||
Future<void> loadInitialData() async {
|
||||
List<Feature> markers = await _dataProvider.getMarkers();
|
||||
state = state.copyWith(markers: markers);
|
||||
}
|
||||
|
||||
void setMarkers(List<Feature> markers) {
|
||||
state = state.copyWith(markers: markers);
|
||||
}
|
||||
|
||||
void startGame() {
|
||||
if (state.isWithinStartRadius) {
|
||||
state = state.copyWith(status: GameStatus.started);
|
||||
}
|
||||
}
|
||||
|
||||
void updateLocation(UserLocation location) {
|
||||
Feature startMarker = state.markers.firstWhere(
|
||||
(marker) => marker.properties.cp == -1,
|
||||
orElse: () => Feature.empty());
|
||||
List<Feature> checkpoints =
|
||||
state.markers.where((marker) => marker.properties.cp != -1).toList();
|
||||
|
||||
bool isWithinStartRadius =
|
||||
_checkProximity(location, startMarker /* start marker location */
|
||||
);
|
||||
bool hasLeftArea =
|
||||
state.hasLeftStartArea || _checkHasLeftStartArea(location, startMarker);
|
||||
|
||||
Feature? atCheckpoint = _checkAtAnyCheckpoint(location, checkpoints);
|
||||
|
||||
// Check if at any checkpoint (except start point after game started)
|
||||
bool isAtCheckpoint =
|
||||
state.status == GameStatus.started && atCheckpoint != null;
|
||||
if (isAtCheckpoint) {
|
||||
_handleCheckpointArrival(location, atCheckpoint);
|
||||
}
|
||||
|
||||
// Update game state based on the new location
|
||||
state = state.copyWith(
|
||||
currentLocation: location,
|
||||
isWithinStartRadius: isWithinStartRadius,
|
||||
hasLeftStartArea: hasLeftArea,
|
||||
status: isAtCheckpoint ? GameStatus.atCheckpoint : state.status,
|
||||
);
|
||||
}
|
||||
|
||||
void _handleCheckpointArrival(UserLocation location, Feature marker) {
|
||||
// Directly set the current checkpoint; no need to check if the photo was taken here
|
||||
state = state.copyWith(currentCheckpoint: marker);
|
||||
|
||||
// If no checkpoint photo has been taken yet, set the pending action to take a checkpoint photo
|
||||
if (!state.checkpointPhotoTaken) {
|
||||
state = state.copyWith(pendingAction: PendingAction.checkInPhoto);
|
||||
}
|
||||
|
||||
// Additional actions specific to buy points can be prepared here, but it's usually better
|
||||
// to handle them after the checkpoint photo is submitted, not at the moment of arrival.
|
||||
}
|
||||
|
||||
void submitCheckInPhoto(String photoPath) {
|
||||
// Logic to save or upload the check-in photo...
|
||||
state = state.copyWith(checkpointPhotoTaken: true);
|
||||
|
||||
// Determine the next action based on the checkpoint type
|
||||
if (state.currentCheckpoint != null &&
|
||||
isBuyPoint(state.currentCheckpoint!)) {
|
||||
state = state.copyWith(pendingAction: PendingAction.receiptPhoto);
|
||||
} else {
|
||||
// If it's not a buy point, mark the checkpoint visit as complete
|
||||
_markCheckpointVisited(state.currentCheckpoint!);
|
||||
}
|
||||
}
|
||||
|
||||
void submitReceiptPhoto(String photoPath) {
|
||||
// Logic to save or upload the receipt photo...
|
||||
state = state.copyWith(receiptPhotoTaken: true);
|
||||
|
||||
// After submitting the receipt photo, mark the checkpoint visit as complete
|
||||
_markCheckpointVisited(state.currentCheckpoint!);
|
||||
}
|
||||
|
||||
void _markCheckpointVisited(Feature checkpoint) {
|
||||
state = state.copyWith(
|
||||
visitedMarkers: Set.from(state.visitedMarkers)..add(checkpoint),
|
||||
hasVisitedCheckpoint: true,
|
||||
pendingAction: PendingAction.none, // Reset the pending action
|
||||
);
|
||||
|
||||
// Additional logic to determine if the game is completed...
|
||||
}
|
||||
|
||||
bool isBuyPoint(Feature checkpoint) {
|
||||
if (checkpoint.properties.buyPoint != null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _checkProximity(UserLocation location, Feature startMarker) {
|
||||
const Distance distance = Distance();
|
||||
return distance(
|
||||
LatLng(location.latitude, location.longitude),
|
||||
LatLng(startMarker.geometry.latitude,
|
||||
startMarker.geometry.longitude)) <=
|
||||
startMinDistance;
|
||||
}
|
||||
|
||||
bool _checkHasLeftStartArea(UserLocation location, Feature startMarker) {
|
||||
const Distance distance = Distance();
|
||||
return distance(
|
||||
LatLng(location.latitude, location.longitude),
|
||||
LatLng(startMarker.geometry.latitude,
|
||||
startMarker.geometry.longitude)) >=
|
||||
minAwayfromStartDistance;
|
||||
}
|
||||
|
||||
Feature? _checkAtAnyCheckpoint(UserLocation location, List<Feature> markers) {
|
||||
const Distance distance = Distance();
|
||||
|
||||
for (Feature marker in markers) {
|
||||
final double distanceInMeters = distance(
|
||||
LatLng(location.latitude, location.longitude),
|
||||
LatLng(marker.geometry.latitude, marker.geometry.longitude),
|
||||
);
|
||||
if (marker.properties.checkinRadious != null &&
|
||||
marker.properties.checkinRadious! >= distanceInMeters) {
|
||||
return marker;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Feature? _getCheckpointFromLocation(UserLocation location) {
|
||||
const thresholdDistance = 50.0; // meters, adjust based on your game design
|
||||
const Distance distance = Distance();
|
||||
|
||||
for (final marker in state.markers) {
|
||||
final double distanceInMeters = distance(
|
||||
LatLng(location.latitude, location.longitude),
|
||||
LatLng(marker.geometry.latitude, marker.geometry.longitude),
|
||||
);
|
||||
|
||||
if (distanceInMeters <=
|
||||
(marker.properties.checkinRadious ?? thresholdDistance)) {
|
||||
return marker;
|
||||
}
|
||||
}
|
||||
|
||||
return null; // No checkpoint is close enough to the user's location
|
||||
}
|
||||
|
||||
bool _isBackAtStart(UserLocation location) {
|
||||
const startThresholdDistance = 150.0; // Define proximity threshold
|
||||
const Distance distance = Distance();
|
||||
|
||||
// Assuming the start point is the first marker
|
||||
// Adjust based on your game's logic
|
||||
final startPoint = state.markers.first;
|
||||
|
||||
final double distanceToStart = distance(
|
||||
LatLng(location.latitude, location.longitude),
|
||||
LatLng(startPoint.geometry.latitude, startPoint.geometry.longitude),
|
||||
);
|
||||
|
||||
// User is considered back at the start if within the threshold distance
|
||||
return distanceToStart <= startThresholdDistance;
|
||||
}
|
||||
|
||||
void endGame() {
|
||||
if (state.status == GameStatus.gameCompleted) {
|
||||
// Perform any end game actions
|
||||
}
|
||||
}
|
||||
|
||||
void loadGameState(GameState gameState) {
|
||||
state = gameState;
|
||||
}
|
||||
}
|
||||
18
lib/features/state/user_state.dart
Normal file
18
lib/features/state/user_state.dart
Normal file
@ -0,0 +1,18 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:rogapp/features/data/user.dart';
|
||||
|
||||
class UserNotifier extends StateNotifier<User?> {
|
||||
UserNotifier() : super(null);
|
||||
|
||||
void setUser(User? user) {
|
||||
state = user;
|
||||
}
|
||||
|
||||
void clearUser() {
|
||||
state = null;
|
||||
}
|
||||
}
|
||||
|
||||
final userNotifierProvider = StateNotifierProvider<UserNotifier, User?>((ref) {
|
||||
return UserNotifier();
|
||||
});
|
||||
Reference in New Issue
Block a user