Files
rog_app/lib/features/state/game_view_model.dart
2024-04-01 09:26:56 +05:30

200 lines
6.6 KiB
Dart

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