| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838 |
- import 'dart:async';
- import 'dart:math';
- import 'package:flutter/cupertino.dart';
- import 'package:get/get.dart';
- import 'package:fixnum/fixnum.dart';
- import 'package:grpc/grpc.dart';
- import 'package:trackoffical_app/model/game_map.dart';
- import 'package:trackoffical_app/model/settlement.dart';
- import 'package:trackoffical_app/pb.dart' as pb;
- import 'package:trackoffical_app/service/app_map.dart';
- import 'package:trackoffical_app/service/game/game_model.dart';
- import 'package:trackoffical_app/service/game/plug.dart';
- import 'package:trackoffical_app/service/game/plug_location.dart';
- import 'package:trackoffical_app/service/game/plug_sport_wear.dart';
- import 'package:trackoffical_app/service/game/rule_in_order.dart';
- import 'package:trackoffical_app/service/game/show_position_controller.dart';
- import 'package:trackoffical_app/utils.dart';
- import 'package:trackoffical_app/view/ingame/settlement_view.dart';
- import 'package:vibration/vibration.dart';
- import 'package:wakelock/wakelock.dart';
- import '../../logger.dart';
- import '../api.dart';
- import '../../model.dart';
- import '../app.dart';
- import '../database.dart';
- import 'map_status.dart';
- import 'plug_orientation.dart';
- import 'rule.dart';
- import '../image.dart';
- enum GameStatus {
- idle,
- // 准备进入游戏
- preparing,
- loading,
- playing,
- settlement,
- }
- class GameService extends GetxService {
- static GameService get to => Get.find();
- final DatabaseService _database = Get.find();
- final GameModel _model = Get.find();
- final App _app = Get.find();
- final errorMsg = ''.obs;
- final name = "".obs;
- var activityId = 0;
- var mapRouteId = Int64(0);
- var _status = GameStatus.idle;
- Rule rule = RuleMock();
- GameStatus get status => _status;
- set status(GameStatus v) {
- info('游戏状态:[$v]');
- _status = v;
- }
- pb.GameSettlement _lastGameSettlement = pb.GameDetailReply();
- pb.GameSettlement get lastGameSettlement => _lastGameSettlement;
- MPosition? get myPosition => _model.myPosition.value;
- Offset? get positionOnMap => _model.myPositionOnMap.value;
- double get myPositionHistoryLenKm => _model.myPositionHistoryLen.value.km;
- final _plugs = <Plug>[];
- GameState get gameState => _model.gameSrcState.value;
- var isShowCompass = true.obs;
- static const _progressMap = 0.6;
- static const _progressApi = 0.2;
- static const _progressFinal = 0.2;
- final _loadProgress = 0.0.obs;
- double get loadProgress => _loadProgress.value;
- List<MControlPoint> get controlPointWantSequence =>
- _model.controlPointWantSequence;
- bool get isNfcScanUseDialog {
- final platform = _app.platformInfo;
- if (platform is PlatformInfoIOS) {
- if (platform.deviceVersion >= 8) {
- return true;
- }
- }
- return false;
- }
- // 创建时间
- DateTime get createTime => gameState.createTime;
- // 关门时间
- Duration get maxPassDuration => gameState.pbGameData.maxDuration.toDuration();
- // 强制结束时间
- Duration get maxForcedEndDuration =>
- gameState.pbGameData.maxForcedEndDuration.toDuration();
- DateTime get now => _app.now;
- // 打开始点的时间
- DateTime? get startAt => _model.startAt;
- DateTime? get endAt => _model.endAt;
- List<pb.ControlPoint> get _controlPointWantSequence =>
- gameState.pbGameData.controlPointSortedList;
- List<pb.ControlPointSimple> get _controlPointAll =>
- gameState.pbGameData.controlPointAll;
- var _lastCheckedPoint = Int64(0);
- var _lastCheckedPointTime = DateTime(0);
- List<MControlPoint> get checkedPointsHistory => _model.checkedPointsHistory;
- final beginDuration = 0.seconds.obs;
- final mapStatus = MapStatus();
- Timer? _beginDurationTicker;
- Timer? _checkStopTicker;
- int get checkedCount => _model.checkedCount;
- // 所有计分点数量
- int get controlPointAllNum => _model.validCPAllNum;
- // 打卡进度
- double get checkProgress => _model.checkProgress;
- bool get isOutBoundary {
- final p = _model.myPositionOnMap.value;
- if (p != null) {
- final mapWidth = mapStatus.gameMapData.width;
- final mapHeight = mapStatus.gameMapData.height;
- if (p.dx <= 0 || p.dx >= mapWidth || p.dy <= 0 || p.dy >= mapHeight) {
- return true;
- }
- }
- return false;
- }
- AnimationController? showLocationController;
- showLocation() {
- final p = positionOnMap;
- if (p != null) {
- final dst = _model.mapRotateCenter;
- if (_model.isEnableUserLocation && !_model.isAlwaysShowMyLocation) {
- Get.find<ShowPositionController>().show();
- }
- if (dst != null) {
- mapStatus.movePicPointTo(p, dst);
- }
- }
- }
- _setStartAt(DateTime? time) {
- if (time != null) {
- gameState.pbGameSave.startAt = time.toPb();
- } else {
- gameState.pbGameSave.clearStartAt();
- }
- _model.gameSrcState.refresh();
- }
- _setEndAt(DateTime? time) {
- if (time != null) {
- gameState.pbGameSave.stopAt = time.toPb();
- } else {
- gameState.pbGameSave.clearStopAt();
- }
- _model.gameSrcState.refresh();
- }
- _recordLastPoint(Int64 id) {
- _lastCheckedPoint = id;
- _lastCheckedPointTime = now;
- }
- bool _isCheckTooFast(Int64 id) {
- return _lastCheckedPoint == id &&
- now.difference(_lastCheckedPointTime) <= 10.seconds;
- }
- bool isPointStart(pb.GameSaveControlPoint p) {
- final seq = _controlPointWantSequence;
- if (seq.isNotEmpty) {
- return p.controlPointId == seq.first.id;
- }
- return false;
- }
- bool isPointFinish(pb.GameSaveControlPoint p) {
- final seq = _controlPointWantSequence;
- if (seq.isNotEmpty) {
- return p.controlPointId == seq.last.id;
- }
- return false;
- }
- void setBeginMatrix() {
- if (mapStatus.isSetBeginMatrix) {
- return;
- }
- final next = _model.nextPlanPoint;
- final f = _model.mapRotateCenter;
- if (next != null && f != null) {
- mapStatus.movePicPointTo(next.onMap, f);
- mapStatus.isSetBeginMatrix = true;
- }
- }
- pb.ControlPointSimple gameSaveControlPointFindInProject(
- pb.GameSaveControlPoint p) {
- final all = gameState.pbGameData.controlPointAll;
- for (final one in all) {
- if (one.id == p.controlPointId) {
- return one;
- }
- }
- return pb.ControlPointSimple();
- }
- Future<List<MControlPoint>> _getControlPointWantSequence() async {
- final out = <MControlPoint>[];
- final checkedIndex = checkedCount;
- for (var i = 0; i < _controlPointWantSequence.length; i++) {
- final value = _controlPointWantSequence[i];
- final one = value.toModel();
- one.sn = '$i';
- if (i == 0) {
- one.isStart = true;
- }
- if (i == _controlPointWantSequence.length - 1) {
- one.isFinish = true;
- }
- one.isSuccess = i < checkedIndex;
- one.isNext = i == checkedIndex;
- one.onMap =
- await mapStatus.gameMapData.worldToPixel(value.mapPosition.toModel());
- final extraInfo = one.extraInfo;
- if(extraInfo is CPExtraInfoChoiceQuestion){
- extraInfo.beanCount = gameState.pbGameData.answerSysPoint;
- await extraInfo.image?.loadMemory();
- }
- out.add(one);
- }
- return out;
- }
- MControlPoint? getNextWantPoint(int offset) =>
- _model.getNextWantPoint(offset);
- MControlPoint? getLastCheckedPoint() {
- if (checkedPointsHistory.isNotEmpty) {
- return checkedPointsHistory.last;
- } else {
- return null;
- }
- }
- void _checkGameStop({bool willToSettlementView = false}) {
- if ([
- GameStatus.preparing,
- GameStatus.playing,
- ].contains(status)) {
- if (now.difference(createTime) > maxForcedEndDuration) {
- gameGiveUp(willToSettlementView: willToSettlementView);
- }
- }
- }
- Future<void> _saveToDatabase() async {
- await DatabaseService.to.saveGameState(gameState);
- }
- Future<void> _saveToServer() async {
- while (!isClosed) {
- try {
- final save = gameState.pbGameSave;
- await ApiService.to
- .gameSaveUpload(pb.GameSaveUploadRequest()..gameSave= save);
- return;
- } on GrpcError catch (e) {
- warn('上传失败:', e);
- if (e.code != StatusCode.unavailable) {
- return;
- }
- } catch (e) {
- warn('上传失败:', e);
- return;
- }
- await Future.delayed(500.milliseconds);
- }
- }
- void _checkHistoryAdd(MControlPoint cp) {
- checkedPointsHistory.add(cp);
- gameState.pbGameSave.checkedSortedList.add(cp.toPbSave());
- }
- void _updateCheckHistory(){
- gameState.pbGameSave.checkedSortedList.clear();
- gameState.pbGameSave.checkedSortedList.addAll(checkedPointsHistory.map((e) => e.toPbSave()).toList());
- }
- void save() {
- _updateCheckHistory();
- _saveToDatabase().then((value) => info('本地存档完成'));
- _saveToServer().then((value) => info('上传存档完成'));
- }
- Future<void> gameGiveUp({bool willToSettlementView = false}) async {
- // if(status == GameStatus.idle || status == GameStatus.settlement){
- // return;
- // }
- _gameStopSetValues();
- _updateCheckHistory();
- _saveToDatabase().then((value) => info('本地存档完成'));
- _settlement(isGiveUp: true, willToSettlementView: willToSettlementView);
- }
- void _gameStopSetValues({DateTime? now}) {
- info('游戏结束');
- if (now != null) {
- _setEndAt(now);
- } else {
- _setEndAt(this.now);
- }
- }
- void _updateNewCPState(MControlPoint point, {DateTime? now}) {
- now = now ?? this.now;
- point.checkAfterStart = now.difference(startAt ?? now);
- point.checkAfterPrev = point.checkAfterStart;
- point.checkDistanceAfterStart = _model.myPositionHistoryLen.value;
- point.checkDistanceAfterPrev = point.checkDistanceAfterStart;
- final last = getLastCheckedPoint();
- if (last != null) {
- point.checkAfterPrev = point.checkAfterStart - last.checkAfterStart;
- point.checkDistanceAfterPrev = point.checkDistanceAfterStart - last.checkDistanceAfterStart;
- }
- }
- checkPointNFC(
- String identifier,
- void Function(MControlPoint cp) onChecked,
- Future<bool> Function () onConfirmFinish,
- void Function(pb.ControlPointSimple point) onProjectPoint,
- VoidCallback onNoPoint,
- ) {
- final checkedPoint = _findControlPointInRouteByNfcId(identifier);
- if (checkedPoint != null) {
- checkPoint(checkedPoint, onChecked, onConfirmFinish);
- } else {
- final point = _findControlPointInProjectByNfcId(identifier);
- if (point != null) {
- if (_isCheckTooFast(point.id)) {
- return;
- }
- final result = point.toModel();
- _updateNewCPState(result);
- result.isSuccess = false;
- _checkHistoryAdd(result);
- save();
- _recordLastPoint(point.id);
- onProjectPoint(point);
- } else {
- onNoPoint();
- }
- }
- }
- checkPointGps(
- void Function(MControlPoint cp) onChecked,
- Future<bool> Function () onConfirmFinish,
- ) {
- if(_model.isInPlanControlPointArea){
- pb.ControlPoint? checkedPoint;
- final point = _model.nextPlanPoint;
- if (point != null) {
- checkedPoint = _findControlPointInRouteByM(point);
- }
- checkPoint(checkedPoint!, onChecked, onConfirmFinish);
- }else if (_model.isInWantControlPointArea) {
- pb.ControlPoint? checkedPoint;
- final point = _model.getNextWantPoint(0);
- if (point != null) {
- checkedPoint = _findControlPointInRouteByM(point);
- }
- checkPoint(checkedPoint!, onChecked, onConfirmFinish);
- }
- }
- Future<void> checkPoint(
- pb.ControlPoint checkedPoint,
- void Function (MControlPoint cp) onChecked,
- /// 打了结束点,询问是否结束
- Future<bool> Function () onConfirmFinish,
- ) async{
- final cp = checkedPoint.toModel();
- if (rule.checkNeedReturn(cp)){
- return;
- }
- final now = this.now;
- final result = rule.checkPoint(checkedPoint.toModel());
- var isFinish = result.isSuccess && result.isFinish;
- if(!result.isSuccess && result.isFinish){
- isFinish = await onConfirmFinish();
- if(!isFinish){
- return;
- }
- }
- _updateNewCPState(result, now: now);
- if(result.isStart && result.isSuccess){
- _setStartAt(now);
- }
- if(isFinish){
- _gameStopSetValues(now: now);
- }
- _checkHistoryAdd(result);
- if(startAt!=null){
- gameState.pbGameSave.duration = now.difference(startAt!).toPb();
- if(endAt != null){
- gameState.pbGameSave.duration = endAt!.difference(startAt!).toPb();
- }
- }
- _model.gameSrcState.refresh();
- if(isFinish){
- _settlement();
- }else{
- save();
- }
- rule.recordLastPoint(cp);
- onChecked(result);
- }
- void showNextPoint() {
- final next = _model.nextPlanPoint;
- final focalPoint = _model.mapRotateCenter;
- if (next != null && focalPoint != null) {
- mapStatus.movePicPointTo(next.onMap, focalPoint);
- }
- }
- Future<void> gameGiveUpAndToFinishView({GameState? state}) async {
- if (state != null) {
- _model.gameSrcState.value = state;
- }
- gameGiveUp();
- SettlementView.show();
- // Get.offAll(() => const GameFinishView(),
- // binding: GameFinishView.bindings());
- }
- Future<void> _gameReset() async {
- errorMsg.value = '';
- _model.clear();
- await _plugsClear();
- }
- Future<void> gameStart() async {
- info('项目Id[$activityId]准备开始');
- await _gameReset();
- final data = await ApiService.to.gameStart(activityId, mapRouteId);
- _model.gameSrcState.value = data.toGameState();
- status = GameStatus.preparing;
- _saveToDatabase();
- info('活动Id[$activityId],游戏Id[${gameState.pbGameData.gameId}]已开始');
- }
- Future<void> gameLoad() async {
- info('载入项目[$activityId], Id[${gameState.pbGameData.gameId}]');
- await _plugsClear();
- _loadProgress.value = 0;
- status = GameStatus.loading;
- _app.userProfile.cleanGameSettingsLock();
- _app.userProfile.gameSettingsLoadLock(gameState.pbGameData.ruleList);
- /// 游戏规则
- // rule = RuleInOrder();
- _model.compassRealNorthOffset = gameState.pbGameData.declination * pi / 180;
- _loadProgress.value = _progressApi;
- final gameMap = gameState.pbGameData.mapZip.toGameMap();
- mapStatus.gameMapData = gameMap;
- await gameMap.loadMemory(onReceiveProgress: (c, a) {
- if (a > 0) {
- var p = c.toDouble() / a;
- p = p * _progressMap + _progressApi;
- _loadProgress.value = p;
- }
- });
- mapStatus.mapImageData.value = gameMap.pic!;
- // --- 确定地图图片首次缩放比例 ---
- final screenSize = _app.screenSize;
- final fitted = applyBoxFit(
- BoxFit.contain,
- Size(gameMap.width, gameMap.height),
- Size(screenSize.width, screenSize.height));
- mapStatus.picFirstScale = fitted.destination.width / fitted.source.width;
- // --------------
- _model.mapRotateCenter =
- Offset(screenSize.width / 2, screenSize.height / 2);
- await mapStatus.resetMatrix();
- _model.controlPointWantSequence.value =
- await _getControlPointWantSequence();
- _gameLoadCheckedCP();
- final plugLocation = PlugLocation(
- gameMap: gameMap,
- lastInfo: _model.gameSrcState.value.pbGameSave.gameGpsInfos);
- _plugs.add(plugLocation);
- _plugs.add(PlugSportWear(
- lastHr: _model.gameSrcState.value.pbGameSave.gameHrInfos));
- _plugs.add(PlugOrientation(mapStatus: mapStatus));
- await _plugsAllInit();
- _loadProgress.value = 1;
- status = GameStatus.playing;
- Wakelock.enable();
- info('载入完成');
- }
- void _gameLoadCheckedCP() {
- final controlPointWantIdValueMap = <Int64, MControlPoint>{};
- final controlPointAllIdValueMap = <Int64, pb.ControlPointSimple>{};
- for (var one in _model.controlPointWantSequence) {
- controlPointWantIdValueMap[one.intId] = one;
- }
- for (var one in gameState.pbGameData.controlPointAll) {
- controlPointAllIdValueMap[one.id] = one;
- }
- for (var i = 0; i < gameState.pbGameSave.checkedSortedList.length; i++) {
- final one = gameState.pbGameSave.checkedSortedList[i];
- final his = one.toModel();
- final hisInfo1 = controlPointWantIdValueMap[his.intId];
- final hisInfo2 = controlPointAllIdValueMap[his.intId];
- if (hisInfo2 != null) {
- his.updateBySimple(hisInfo2);
- }
- if (hisInfo1 != null) {
- his.updateBy(hisInfo1);
- his.type = hisInfo1.type;
- }
- checkedPointsHistory.add(his);
- }
- for (var i = 0; i < checkedPointsHistory.length; i++) {
- final his = checkedPointsHistory[i];
- if (i > 0) {
- final last = checkedPointsHistory[i - 1];
- his.checkAfterPrev = his.checkAfterStart - last.checkAfterStart;
- his.checkDistanceAfterPrev = his.checkDistanceAfterStart - last.checkDistanceAfterStart;
- } else {
- his.checkAfterPrev = his.checkAfterStart;
- his.checkDistanceAfterPrev = his.checkDistanceAfterStart;
- }
- }
- }
- Future<void> _plugsAllInit() async {
- for (var one in _plugs) {
- await one.init();
- }
- }
- Future<void> _plugsClear() async {
- for (var one in _plugs) {
- one.close();
- }
- for (var one in _plugs) {
- await one.join();
- }
- _plugs.clear();
- }
- Settlement getSettlement() {
- return Settlement(
- data: gameState.pbGameData,
- save: gameState.pbGameSave,
- durationAfterStartCheck: beginDuration.value);
- }
- pb.ControlPoint? _findControlPointInRouteByNfcId(String identifier) {
- pb.ControlPoint? found;
- for (var one in _controlPointWantSequence) {
- for (var nfcId in one.nfcIdList) {
- if (nfcId.toUpperCase() == identifier.toUpperCase()) {
- found = one;
- break;
- }
- }
- }
- return found;
- }
- pb.ControlPoint? _findControlPointInRouteByM(MControlPoint point) {
- pb.ControlPoint? found;
- for (var one in _controlPointWantSequence) {
- if (one.id == point.intId) {
- found = one;
- break;
- }
- }
- return found;
- }
- pb.ControlPointSimple? _findControlPointInProjectByNfcId(String identifier) {
- for (var one in _controlPointAll) {
- for (var nfcId in one.nfcIdList) {
- if (nfcId.toUpperCase() == identifier.toUpperCase()) {
- return one;
- }
- }
- }
- return null;
- }
- Future<void> _settlementDeal(bool isGiveUp) async {
- while (!isClosed) {
- try {
- final save = gameState.pbGameSave;
- await ApiService.to
- .gameSaveUpload(pb.GameSaveUploadRequest()..gameSave= save);
- info('进度已上传');
- break;
- } on GrpcError catch (e) {
- if (e.code == StatusCode.unavailable) {
- errorMsg.value = '无法连接至服务器,正在重试';
- } else {
- break;
- }
- error(e);
- } catch (e) {
- error(e);
- break;
- }
- }
- await _plugsClear();
- while (!isClosed) {
- try {
- info('正在结算');
- _lastGameSettlement = await ApiService.to
- .gameFinish(gameState.pbGameData.gameId, isGiveUp);
- info('结算完成');
- break;
- } on GrpcError catch (e) {
- if (e.code == StatusCode.unavailable) {
- errorMsg.value = '网络错误,正在重试';
- } else {
- break;
- }
- error(e);
- Future.delayed(500.milliseconds);
- } catch (e) {
- error(e);
- break;
- }
- }
- await _saveToDatabase();
- status = GameStatus.idle;
- }
- _settlement({bool isGiveUp = false, bool willToSettlementView = false}) {
- _updateCheckHistory();
- status = GameStatus.settlement;
- errorMsg.value = '';
- _settlementDeal(isGiveUp).then((value) => info('结算完成'));
- if (willToSettlementView) {
- SettlementView.show();
- }
- }
- @override
- void onReady() {
- _beginDurationTicker = Timer.periodic(100.milliseconds, (timer) {
- final startAt = this.startAt;
- if (startAt != null) {
- final end = endAt ?? now;
- beginDuration.value = end.difference(startAt);
- } else {
- beginDuration.value = 0.seconds;
- }
- });
- }
- @override
- void onClose() {
- _checkStopTicker?.cancel();
- _beginDurationTicker?.cancel();
- }
- Future<bool> loadOnlineUnFinishGame() async {
- try {
- final online = await ApiService.to.getInGameData();
- info('存在线上未完成游戏');
- status = GameStatus.preparing;
- _model.gameSrcState.value = GameState()
- ..pbGameData = online.data
- ..pbGameSave = online.save;
- _checkGameStop();
- return true;
- } on GrpcError catch (e) {
- if (e.code != StatusCode.notFound) {
- warn(e);
- }
- return false;
- } catch (e) {
- warn(e);
- return false;
- }
- }
- void workAutoSave() async {
- while (!isClosed) {
- await Future.delayed(500.milliseconds);
- if (_model.gameSrcState.value.pbGameData.gameId > 0 &&
- _model.isStarted &&
- !_model.isFinish) {
- await _saveToDatabase();
- }
- }
- }
- void workTrajectory() async {
- while (!isClosed) {
- await Future.delayed(1000.milliseconds);
- final duration = _app.userProfile.inGameTrajectorySeconds.val.seconds;
- var out = <Offset>[];
- for (var i = _model.myPositionHistory.length - 1; i >= 0; i--) {
- var one = _model.myPositionHistory[i];
- if (now.difference(one.timestamp) > duration) {
- break;
- }
- out.add(await mapStatus.gameMapData.worldToPixel(one));
- }
- _model.trajectoryPoints.clear();
- _model.trajectoryPoints.addAll(out);
- }
- }
- void workPace() async {
- while (!isClosed) {
- await Future.delayed(1000.milliseconds);
- final startDuration = _model.startedDuration;
- if (startDuration.inMilliseconds == 0) {
- continue;
- }
- _model.paceSecondKm.value =
- pacePerKm(myPositionHistoryLenKm.km, startDuration);
- if (checkedPointsHistory.isEmpty) {
- _model.paceSecondKmFromLastCP.value = _model.paceSecondKm.value;
- } else {
- final cp = checkedPointsHistory.last;
- _model.paceSecondKmFromLastCP.value = pacePerKm(
- _model.myPositionHistoryLenFromLastCP,
- startDuration - cp.checkAfterStart);
- }
- }
- }
- static Future<GameService> init() async {
- final gs = GameService();
- gs.mapStatus.canVibrate = await Vibration.hasVibrator() ?? false;
- final save = await gs._database.getExistGameData();
- if (save != null) {
- info('存在本地未完成游戏');
- gs.status = GameStatus.preparing;
- gs._model.gameSrcState.value = save.toState();
- gs._checkGameStop();
- } else {
- gs.loadOnlineUnFinishGame();
- }
- gs._checkStopTicker = Timer.periodic(100.milliseconds, (timer) {
- gs._checkGameStop(willToSettlementView: true);
- });
- gs.workAutoSave();
- gs.workTrajectory();
- gs.workPace();
- return gs;
- }
- }
|