| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598 |
- import 'dart:async';
- import 'dart:io';
- import 'dart:math';
- import 'package:get_storage/get_storage.dart';
- import 'package:trackoffical_app/model/game_map.dart';
- import 'package:trackoffical_app/model/game_person_data.dart';
- import 'package:trackoffical_app/service/app.dart';
- import 'package:trackoffical_app/service/game/game_instance.dart';
- import 'package:trackoffical_app/service/game/game_instance_std/game_instance_std.dart';
- import 'package:trackoffical_app/service/user_profile.dart';
- import 'package:trackoffical_app/utils.dart';
- import 'package:trackoffical_app/view/ingame/game_compass/game_compass_base.dart';
- import 'package:nfc_manager/nfc_manager.dart';
- import 'package:nfc_manager/platform_tags.dart';
- import '../../../generated/assets.dart';
- import '../../../logger.dart';
- import '../../../widget/matrix_gesture_detector.dart';
- import '../dialog/dialog_base.dart';
- import '../dialog/dialog_check_rich2.dart';
- import '../dialog/dialog_check_text.dart';
- import '../dialog/dialog_cp_order_err.dart';
- import 'route_planning.dart';
- import 'dialog_finish.dart';
- import 'package:screen_brightness/screen_brightness.dart';
- import '../../../model/m_control_point.dart';
- import '../../../widget/compass2.dart';
- import '../../game_settings.dart';
- import '../layer/layer_controller.dart';
- export '../layer/layer.dart';
- import 'package:sensor/sensor.dart' as sensor;
- import 'package:vector_math/vector_math_64.dart' as vec;
- import 'utils.dart';
- class GameStdController extends LayerController {
- GameStdController(this.instance);
- void _listenSettings() {
- final box = GetStorage();
- cancelListen = box.listen(_updateSettings);
- }
- void _updateSettings() {
- final profile = _app.userProfile;
- isSimpleDashboard.value = profile.gameSettingsSimpleDashboard.value;
- isEnableRoutePreview.value =profile.gameSettingsRoutePreview.value;
- isEnableUserLocationState.value = isEnableUserLocation;
- gameUIMode.value = profile.gameSettingsUIMode.value;
- }
- bool get isNfcScanUseDialog {
- final platform = _app.platformInfo;
- if (platform is PlatformInfoIOS) {
- if (platform.deviceVersion >= 8) {
- return true;
- }
- }
- return false;
- }
- bool get isShowCheckCPButton {
- final next = instance.model.nextWantPoint;
- final nextPlan = instance.model.nextPlanPoint;
- return isNfcScanUseDialog ||
- next?.type == MControlPointType.gps ||
- nextPlan?.type == MControlPointType.gps;
- }
- bool get isCheckCPButtonEnable {
- final next = instance.model.nextWantPoint;
- final nextPlan = instance.model.nextPlanPoint;
- final iosEnable = isNfcScanUseDialog &&
- (next?.type == MControlPointType.nfc ||
- nextPlan?.type == MControlPointType.nfc);
- return (instance.model.isInPlanControlPointArea ||
- instance.model.isInWantControlPointArea ||
- iosEnable) &&
- isShowCheckCPButton;
- }
- /// 下个点方向弧度
- double? get nextCPRadians {
- final p0 = instance.model.myPosition;
- final p1 = instance.model.nextPlanPoint?.position;
- if(p0 == null || p1 == null){
- return null;
- }
- return instance.compassRadiansFused.value + p0.directionTo(p1);
- }
- void showMyLocation() {
- if (isEnableUserLocation) {
- final p = instance.model.myPositionOnMap;
- if (p != null) {
- final dst = mapRotateCenter.value;
- moveOnMapPointToScreen(p, dst);
- }
- }
- }
- void setRotateCenterToScreenCenter() {
- final size = mapWidgetSize.value;
- if (size != null) {
- final screenCenter = Offset(size.width / 2, size.height / 2);
- mapRotateCenter.value = screenCenter;
- }
- }
- void setRotateCenterToCompassCenter() {
- mapRotateCenter.value = compassCenter;
- }
- @override
- void onMapSizeChange(Size size) {
- super.onMapSizeChange(size);
- flushRotateCenter();
- if (!isLockScreenCenterToMyPositionSystem) {
- showNextPoint();
- }
- mapDoScale(2.3);
- }
- void flushRotateCenter() {
- if (isMapRotateAtCompassCenter.value) {
- setRotateCenterToCompassCenter();
- } else {
- setRotateCenterToScreenCenter();
- }
- if (isLockScreenCenterToMyPositionSystem) {
- showMyLocation();
- }
- }
- void mapModeSwitch() {
- switch (mapRotationMode.value) {
- case MapMode.original:
- _setMapMode(MapMode.compass);
- break;
- case MapMode.compass:
- _setMapMode(MapMode.original);
- break;
- }
- }
- void _setMapMode(MapMode mode) {
- resetMatrix();
- mapRotationMode.value = mode;
- }
- @override
- bool get isEnableUserTouchRotation {
- if (mapRotationMode.value == MapMode.compass) {
- return false;
- }
- return true;
- }
- @override
- bool get isEnableUserTouchTranslation {
- if (isLockScreenCenterToMyPositionSystem) {
- return false;
- }
- return true;
- }
- void showNextPoint() {
- final next = instance.model.nextPlanPoint;
- if (next != null) {
- moveOnMapPointToScreen(next.onMap, mapRotateCenter.value);
- }
- }
- Future<void> toSettings() async {
- await Get.to(() => const GameSettingsView(isInGame: true));
- }
- Future<void> showCheckedCP() async {
- showCheckedPoints(instance.checkedPointsHistory);
- }
- Future<void> onSwitchCompassSize() async {
- _compassSizeIndex++;
- if (_compassSizeIndex >= _compassSizeList.length) {
- _compassSizeIndex = 0;
- }
- compassDiameter.value = _compassSizeList[_compassSizeIndex];
- flushRotateCenter();
- }
- void showCompassSwitch() {
- isShowCompass.value = !isShowCompass.value;
- }
- void forceExit() {
- instance.gameGiveUp();
- }
- _playCheckSound(MControlPoint cp) async {
- if (cp.isFinish) {
- return;
- }
- await Future.delayed(200.milliseconds);
- var src = cp.isSuccess ? 'assets/sound/ok.wav' : 'assets/sound/fail.wav';
- if (!cp.isSuccess && cp.isPlan) {
- src = Assets.soundPlanOk;
- }
- if (isClosed) {
- return;
- }
- await _app.soundPlayAsset(src);
- }
- _closeExistDialog() {
- if (Get.isOverlaysOpen) {
- Get.back();
- }
- }
- void dialogRoutePlanning(){
- Get.dialog(Center(
- child: Obx((){
- final model = instance.model;
- return RoutePlanning(
- want: model.controlPointWantSequence,
- nextPlanPoint: model.nextPlanPoint,
- nextWantPoint: model.nextWantPoint,
- onClick: (point){
- model.nextPlanPoint=point;
- });
- }) ),
- );
- }
- _onChecked(MControlPoint cp) {
- _closeExistDialog();
- final isEnablePunchErrorPrompt = _app.userProfile.gameSettingsPunchErrorPrompt.value;
- final next = instance.model.nextWantPoint;
- _playCheckSound(cp);
- if (cp.isSuccess) {
- if (cp.isStart) {
- Get.dialog(DialogCheckText(
- text: '开始',
- color: Colors.white,
- autoPlayAfter: 200.milliseconds,
- ));
- } else {
- showDialogCheckRich(cp, instance.model.gameQuestionShowDuration)
- .then((value) {
- instance.save();
- });
- }
- } else {
- if (cp.isPlan) {
- Get.dialog(const DialogCheckText(
- text: '已打点',
- color: Color(0xffff870d),
- ));
- } else if (next != null) {
- if(isEnablePunchErrorPrompt){
- dialogCPOrderErr(next);
- }
- } else {
- if(isEnablePunchErrorPrompt) {
- Get.dialog(
- const DialogCheckText(text: '打点错误', color: Colors.red));
- }
- }
- }
- }
- _onProjectPoint(MControlPointInProject point) {
- _closeExistDialog();
- _playCheckSound(MControlPoint()..isSuccess = false);
- Get.dialog(dialogTitle('非线路检查点', Colors.red, const Text('请检查地图线路'),
- offAfter: 3.seconds));
- }
- _onNoPoint() {
- _closeExistDialog();
- _playCheckSound(MControlPoint()..isSuccess = false);
- Get.dialog(dialogTitle('打点错误', Colors.red, const Text('不是检查点'),
- offAfter: 3.seconds));
- }
- Future<void> _onIosNfcStart() async {
- if (!(await App.to.isNfcAvailable)) {
- Get.showSnackbar(const GetSnackBar(
- message: 'NFC不可用',
- ));
- return;
- }
- NfcManager.instance.startSession(
- alertMessage: '请靠近打卡点',
- onDiscovered: (tag) async {
- try {
- await _onNfcDiscovered(tag);
- await NfcManager.instance.stopSession(alertMessage: '打卡成功');
- } catch (e) {
- await NfcManager.instance.stopSession(errorMessage: '$e');
- }
- },
- );
- }
- Future<void> onCheckControlPoint() async {
- final next = instance.model.nextWantPoint;
- if (next != null) {
- switch (next.type) {
- case MControlPointType.nfc:
- await _onIosNfcStart();
- break;
- case MControlPointType.gps:
- await instance.checkPointGps(_onChecked, _onConfirmFinish);
- break;
- }
- }
- }
- void updateCompassRotateMap(double radians) {
- if (mapRotationMode.value == MapMode.compass) {
- setRotate(radians);
- }
- }
- void _registerNFC() {
- if (!isNfcScanUseDialog) {
- NfcManager.instance.startSession(
- onDiscovered: _onNfcDiscovered, alertMessage: '保持NFC靠近');
- info('Nfc开始扫描');
- }
- }
- Future<void> _onNfcDiscovered(NfcTag tag) async {
- info("NFC: \n ${tag.data}");
- String identifier = "";
- if (Platform.isAndroid) {
- identifier = (NfcA.from(tag)?.identifier ??
- NfcB.from(tag)?.identifier ??
- NfcF.from(tag)?.identifier ??
- NfcV.from(tag)?.identifier ??
- Uint8List(0))
- .toHexString();
- }
- if (Platform.isIOS) {
- identifier = (Iso15693.from(tag)?.identifier ??
- Iso7816.from(tag)?.identifier ??
- MiFare.from(tag)?.identifier ??
- Uint8List(0))
- .toHexString();
- }
- info('Id: $identifier');
- instance.checkPointNFC(
- identifier, _onChecked, _onConfirmFinish, _onProjectPoint, _onNoPoint);
- }
- Future<bool> _onConfirmFinish() async {
- return true;
- // return await dialogAskConfirmFinish();
- }
- void _onPositionUpdate(List<Offset> offset) {
- if (offset.isNotEmpty) {
- final p = offset.last;
- if (isLockScreenCenterToMyPositionSystem) {
- showMyLocation();
- }
- }
- }
- Future<void> _workPlayDistanceSound()async{
- while(!isClosed){
- final model = instance.model;
- final distance = model.nextPlanCPDistance;
- if(distance != null && model.endAt==null){
- var d = 5200.milliseconds;
- String? src = 'assets/sound/beep.wav';
- if(distance < 30.meter){
- d = 1200.milliseconds;
- }
- if(distance < 10.meter){
- d = 700.milliseconds;
- }
- if((model.isInPlanControlPointArea && model.nextPlanPoint?.type == MControlPointType.gps)
- || (model.isInWantControlPointArea&& model.nextWantPoint?.type == MControlPointType.gps)){
- d = 3400.milliseconds;
- src = 'assets/sound/punch_alarm.mp3';
- }
- await _app.soundPlayAsset(src);
- await Future.delayed(d);
- }else{
- await Future.delayed(500.milliseconds);
- }
- }
- }
- @override
- void onReady() {
- super.onReady();
- _updateSettings();
- _listenSettings();
- setIsBrightnessMax(isBrightnessMax);
- _setMapMode(MapMode.compass);
- _workPlayDistanceSound();
- state.bindStream(instance.stateStream);
- settlementTip.bindStream(instance.errorMsg.stream);
- _subscriptions.add(instance.stateStream.listen((state) {
- debug('游戏状态:$state');
- if (state == GameInstanceState.closed) {
- dialogFinishResult(instance.finishData);
- }
- }));
- _subscriptions.add(instance.compassRadiansFused.listen((r) {
- updateCompassRotateMap(r);
- }));
- _subscriptions
- .add(instance.model.myPositionOnMapHistory.listen(_onPositionUpdate));
- _registerNFC();
- gameCompassController.duration.bindStream(instance.model.duration.stream);
- gameCompassController.compassRadians.bindStream(instance.compassRadiansSrc.stream);
- _subscriptions.add(instance.compassRadiansSrc.listen((p) {
- final p0 = instance.model.myPosition;
- final p1 = instance.model.nextPlanPoint?.position;
- if(p0 == null || p1 == null){
- return;
- }
- gameCompassController.nextPointRadians.value=p + p0.directionTo(p1);
- }));
- gameCompassController.heartRatePercent.bindStream(instance.model.heartRatePercent.stream);
- gameCompassController.heartRate.bindStream(instance.model.heartRate.stream);
- gameCompassController.stepCount.bindStream(instance.model.stepCount.stream);
- gameCompassController.kCal.bindStream(instance.model.kCal.stream);
- gameCompassController.ck.bindStream(instance.model.ck.stream);
- gameCompassController.ei.bindStream(instance.model.ei.stream);
- }
- @override
- void onClose() {
- super.onClose();
- for (final one in _subscriptions) {
- one.cancel();
- }
- if (!isNfcScanUseDialog) {
- NfcManager.instance.stopSession();
- }
- cancelListen?.call();
- state.close();
- settlementTip.close();
- }
- final gameCompassController = GameCompassController();
- final GameInstanceStd instance;
- final _app = App.to;
- UserProfile get _profile => _app.userProfile;
- final _subscriptions = <StreamSubscription>[];
- @override
- GameMap? get gameMap => instance.gameMapData;
- final isShowTrace = true.obs;
- final isMapRotateAtCompassCenter = false.obs;
- static const bottomBarHeight = 120.0;
- final isSimpleDashboard = true.obs;
- final isEnableRoutePreview = true.obs;
- final isEnableUserLocationState = true.obs;
- final gameUIMode = GameUIMode.electronicMap.obs;
- void Function()? cancelListen;
- var isLockScreenCenterToMyPosition = false;
- /// 用户设置是否锁定旋转中心
- bool get isLockScreenCenterToMyPositionSystem =>
- isLockScreenCenterToMyPosition && isEnableUserLocation;
- bool get isEnableUserLocation => _profile.gameSettingsShowMyLocation.value;
- set isEnableUserLocation(v) {
- _profile.gameSettingsShowMyLocation.value = v;
- }
- final compassDiameter = 160.0.obs;
- Offset get compassCenter => Offset(App.to.screenSize.width / 2,
- App.to.screenSize.height - bottomBarHeight - compassDiameter / 2);
- final mapRotationMode = MapMode.compass.obs;
- final state = GameInstanceState.uninitialized.obs;
- MNetImage get legend =>
- instance.model.gameSrcState.value.pbGameData.legendImage.toModel();
- bool get isBrightnessMax =>
- App.to.userProfile.isEnableInGameBrightnessMax.val;
- Future<void> setIsBrightnessMax(bool v) async {
- App.to.userProfile.isEnableInGameBrightnessMax.val = v;
- if (v) {
- await ScreenBrightness().setScreenBrightness(1);
- } else {
- await ScreenBrightness().resetScreenBrightness();
- }
- }
- final compassLevel = Compass2.levelMin.obs;
- var _compassSizeIndex = 0;
- final _compassSizeList = <double>[160.0, 200, 240, 280];
- final isShowCompass = true.obs;
- final isNoMapRulerScaleMode = false.obs;
- /// 是否显示下一个点的方向
- final isShowNextCPRadians = true.obs;
- final isShowRuler = false.obs;
- bool get isCheckCPButtonWarn =>
- instance.model.isInWantControlPointArea && isCheckCPButtonEnable;
- final settlementTip = '正在计算,请稍后'.obs;
- double get compassPlantRadian {
- if (mapRotationMode.value == MapMode.compass) {
- return instance.compassRadiansFused.value;
- }
- final ogRm = mapTransformMatrix.value.clone();
- double radian = MatrixGestureDetector.decomposeToValues(ogRm).rotation;
- return radian;
- }
- int get compassShowDegrees {
- var d1 = -(compassPlantRadian - instance.compassRadiansFused.value);
- if (mapRotationMode.value == MapMode.compass) {
- d1 = -compassPlantRadian;
- }
- var d = d1 * 180 ~/ pi;
- while (d < 0) {
- d += 360;
- }
- while (d > 360) {
- d -= 360;
- }
- return d;
- }
- /// 地图比例尺 1:[userSetMapScale]
- final Rx<double?> userSetMapScale = Rx(null);
- sensor.Orientation get orientation => instance.orientation.value;
- bool get isPhoneHorizontal {
- final x = vec.degrees(orientation.x);
- final y = vec.degrees(orientation.y);
- if (x.abs() > 30 || y.abs() > 30) {
- return false;
- } else {
- return true;
- }
- }
- bool get isShowPhoneHorizontalWarn =>
- (!isPhoneHorizontal) && (!instance.isPersonMoving);
- bool get isOutBoundary {
- final p = instance.model.myPositionOnMap;
- if (p != null) {
- final mapWidth = instance.gameMapData.width;
- final mapHeight = instance.gameMapData.height;
- if (p.dx <= 0 || p.dx >= mapWidth || p.dy <= 0 || p.dy >= mapHeight) {
- return true;
- }
- }
- return false;
- }
- bool get isShowOutBoundaryWarn =>
- App.to.userProfile.gameSettingsBoundaryWarn.value && isOutBoundary;
- }
|