import 'dart:math'; import 'package:get/get.dart'; import 'package:trackoffical_app/appcore/ffi.dart' as ffi; import 'package:trackoffical_app/logger.dart'; import 'package:trackoffical_app/model.dart'; import 'package:trackoffical_app/service/app.dart'; import 'package:trackoffical_app/service/image.dart'; import 'package:trackoffical_app/utils.dart'; import 'package:flutter/material.dart'; import '../generated/assets.dart'; import '../global.dart'; import 'game_map.dart'; import 'm_control_point.dart'; import 'package:trackoffical_app/pb.dart' as pb; typedef MControlPointInProject = pb.ControlPointSimple; class GamePersonData { var userId = -1; var userName = ''; var userHead = MNetImage().obs; final duration = 0.seconds.obs; final gameSrcState = GameState().obs; DateTime? get startAt { final save = gameSrcState.value.pbGameSave; if (save.hasStartAt()) { if (save.startAt.seconds == 0) { return null; } return save.startAt.toModel(); } return null; } DateTime? get endAt { final save = gameSrcState.value.pbGameSave; if (save.hasStopAt()) { if (save.stopAt.seconds == 0) { return null; } return save.stopAt.toModel(); } return null; } final controlPointWantSequence = [].obs; final checkedPointsHistory = [].obs; final myPositionHistory = [].obs; MPosition? get myPosition => myPositionHistory.isEmpty ? null : myPositionHistory.last; final myPositionOnMapHistory = [].obs; Offset? get myPositionOnMap => myPositionOnMapHistory.isEmpty ? null : myPositionOnMapHistory.last; final heartRate = 0.obs; final heartRatePercent = 0.0.obs; final ei = 0.0.obs; final ck = 0.0.obs; final kCal = 0.0.obs; final exerciseKcal = 0.0.obs; final hrMean = 0.obs; final hrMax = 0.obs; final stepCount = 0.obs; final pacePerKm = 0.seconds.obs; final myPositionHistoryLen = 0.0.meter.obs; final speedPreSecond = 0.meter.obs; Distance get myPositionHistoryLenFromLastCP { if (checkedPointsHistory.isEmpty) { return myPositionHistoryLen.value; } final cp = checkedPointsHistory.last; return myPositionHistoryLen.value - cp.checkDistanceAfterStart; } // 百味豆 final beanCount = 0.obs; final paceSecondKm = 0.seconds.obs; final paceSecondKmFromLastCP = 0.seconds.obs; final Rx zone = Rx(null); // 打点误差 Distance get _punchRadiusError => App.to.userProfile.gameSettingsTargetErrorMeter.value.meter; // 打点半径 Distance get _punchRadius => GlobalVar.isGpsTest ? 100.km : App.to.userProfile.gameSettingsPunchRadiusMeter.value.meter + _punchRadiusError; int get checkedCount { var i = 0; for (var one in gameSrcState.value.pbGameSave.checkedSortedList) { if (one.isCheckSuccess) { i++; } } return i; } final Rx nextPlanPointIndex = Rx(null); // 所有计分点数量 int get validCPAllNum { return controlPointWantSequence.length; } bool get isStarted => startAt != null; bool get isFinish => endAt != null; Future cpWantSequenceRefresh( GameMap gameMap, { void Function(double count, double total)? onProgress, bool loadPic = false, }) async { if (gameMap.width == 0 && gameMap.height == 0) { return; } final out = []; final checkedIndex = checkedCount; final controlPointWantSequenceSrc = gameSrcState.value.pbGameData.controlPointSortedList; for (var i = 0; i < controlPointWantSequenceSrc.length; i++) { final value = controlPointWantSequenceSrc[i]; final one = value.toModel(); one.sn = '$i'; if (i == 0) { one.isStart = true; } if (i == controlPointWantSequenceSrc.length - 1) { one.isFinish = true; } one.isSuccess = i < checkedIndex; one.isNext = i == checkedIndex; one.onMap = await gameMap.worldToPixel(value.mapPosition.toModel()); final extraInfo = one.extraInfo; if ((extraInfo is CPExtraInfoChoiceQuestion) && loadPic) { extraInfo.beanCount = gameSrcState.value.pbGameData.answerSysPoint; try { await extraInfo.image?.loadMemory(); } catch (e) { warn('加载问题图片失败:$e'); } } out.add(one); onProgress?.call( i.toDouble(), controlPointWantSequenceSrc.length.toDouble()); } controlPointWantSequence.value = out; } Future initControlPointWantSequence( GameMap gameMap, { void Function(double count, double total)? onProgress, bool loadPic = false, }) async { if (controlPointWantSequence.isNotEmpty) { return; } await cpWantSequenceRefresh(gameMap, onProgress: onProgress, loadPic: loadPic); } MControlPoint? findCPWantByNfcId(String identifier) { MControlPoint? found; for (var one in gameSrcState.value.pbGameData.controlPointSortedList) { for (var nfcId in one.nfcIdList) { if (nfcId.toUpperCase() == identifier.toUpperCase()) { found = one.toModel(); break; } } } return found; } MControlPointInProject? findCPInProjectByNfcId(String identifier) { for (var one in gameSrcState.value.pbGameData.controlPointAll) { for (var nfcId in one.nfcIdList) { if (nfcId.toUpperCase() == identifier.toUpperCase()) { return one; } } } return null; } MControlPoint? findCPInRoute(MControlPoint point) { MControlPoint? found; for (var one in controlPointWantSequence) { if (one.intId == point.intId) { found = one; break; } } return found; } void checkHistoryAdd(MControlPoint cp) { checkedPointsHistory.add(cp); gameSrcState.value.pbGameSave.checkedSortedList.add(cp.toPbSave()); } void updateCheckHistory(){ gameSrcState.value.pbGameSave.checkedSortedList.clear(); gameSrcState.value.pbGameSave.checkedSortedList.addAll(checkedPointsHistory.map((e) => e.toPbSave()).toList()); } MControlPoint? get nextWantPoint { final i = checkedCount; return controlPointWantSequence.length > i ? controlPointWantSequence[i] : null; } Duration get gameQuestionShowDuration => gameSrcState.value.pbGameData.oiShowTime.seconds; Distance? get nextWantCPDistance { final p1 = myPosition; final p2 = nextWantPoint?.position; if (p1 != null && p2 != null) { return p2.distance(p1); } return null; } Distance? get nextWantCPDistanceKmShow { final real = nextWantCPDistance; if (real == null) { return null; } var show = real - _punchRadiusError; if (show < 0.meter) { show = 0.meter; } return show; } Distance? get nextPlanCPDistance{ final p1 = myPosition; final p2 = nextPlanPoint?.position; if(p1 != null && p2 != null){ return p2.distance(p1); } return null; } Distance? get nextPlanCPDistanceKmShow{ final real = nextPlanCPDistance; if(real == null){ return null; } var show = real - _punchRadiusError; if(show < 0 .meter){ show = 0.meter; } return show; } double get compassRealNorthOffset=>gameSrcState.value.pbGameData.declination * pi / 180; set nextPlanPoint(MControlPoint? v){ if(startAt==null){ return; } if(v==null){ nextPlanPointIndex.value=null; return; } for(var i=0;i i? controlPointWantSequence[i] : null; } bool get isInWantControlPointArea { final p = nextWantPoint; if(p != null){ final disKm = nextWantCPDistance; if(disKm != null && p.type == MControlPointType.gps){ return disKm <= _punchRadius; } } return false; } bool get isInPlanControlPointArea { final p = nextPlanPoint; if(p != null){ final disKm = nextPlanCPDistance; if(disKm != null && p.type == MControlPointType.gps){ return disKm <= _punchRadius; } } return false; } }