Добавлена поддержка витрины (нестабильно!)

This commit is contained in:
se_ga 2026-03-16 11:36:42 +03:00
parent 5dd372a0ad
commit 2fc1bc5fe1
17 changed files with 834 additions and 1976 deletions

View File

@ -11349,7 +11349,7 @@ Material:
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
- _EnvMatrixRotation: {r: 0, g: 0, b: 0, a: 0}
- _FaceColor: {r: 1, g: 1, b: 1, a: 1}
- _GlowColor: {r: 0.46582127, g: 0, b: 1, a: 1}
- _GlowColor: {r: 1, g: 0.528794, b: 0, a: 1}
- _MaskCoord: {r: 0, g: 0, b: 32767, a: 32767}
- _OutlineColor: {r: 0.554717, g: 0.554717, b: 0.554717, a: 1}
- _ReflectFaceColor: {r: 0, g: 0, b: 0, a: 1}

View File

@ -5769,7 +5769,7 @@ MonoBehaviour:
m_EditorClassIdentifier:
Rows: 7
Cols: 7
dummyBots: 1
dummyBots: 0
botsAmount: 5
dummyPreset: {fileID: 11400000, guid: 35ba7f84d7557e8479f7f086269256f6, type: 2}
MistressPreset: {fileID: 11400000, guid: 9aec48759dff81140a8ede5cbefe07d5, type: 2}
@ -5851,6 +5851,7 @@ MonoBehaviour:
soundManager: {fileID: 151433694}
vitrinaPanel: {fileID: 61619038}
qrPanel: {fileID: 1060301113}
vitrinaCatcher: {fileID: 1984502421}
--- !u!4 &259182280
Transform:
m_ObjectHideFlags: 0
@ -5973,7 +5974,7 @@ MonoBehaviour:
ImagePosition: {x: -172.5, y: 9.9}
ImageSize: 445
audioClip: {fileID: 8300000, guid: 7fb340d179cafc04cb2e4baa2ddde96f, type: 3}
vitrinaPanel: {fileID: 0}
vitrinaPanel: {fileID: 61619038}
--- !u!114 &259182291
MonoBehaviour:
m_ObjectHideFlags: 0
@ -34634,6 +34635,7 @@ RectTransform:
m_Children:
- {fileID: 130760984}
- {fileID: 584665085}
- {fileID: 1842377327}
m_Father: {fileID: 61619037}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
@ -53630,6 +53632,141 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1841617193}
m_CullTransparentMesh: 1
--- !u!1 &1842377326
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1842377327}
- component: {fileID: 1842377329}
- component: {fileID: 1842377328}
m_Layer: 5
m_Name: Text (TMP)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1842377327
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1842377326}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1156431208}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0}
m_AnchorMax: {x: 0.5, y: 0}
m_AnchoredPosition: {x: 0, y: 20}
m_SizeDelta: {x: 793.97, y: 50}
m_Pivot: {x: 0.5, y: 0}
--- !u!114 &1842377328
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1842377326}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: "\u0427\u0442\u043E\u0431\u044B \u043E\u0442\u043A\u0440\u044B\u0442\u044C,
\u0434\u0435\u0440\u0436\u0438 \u0412\u041D\u0418\u0417 3 \u0441\u0435\u043A\u0443\u043D\u0434\u044B"
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 3f10bbcf486189d4f95b8de6dc2e2177, type: 2}
m_sharedMaterial: {fileID: 4262305343556741190, guid: 3f10bbcf486189d4f95b8de6dc2e2177, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 32
m_fontSizeBase: 32
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 16
m_fontSizeMax: 64
m_fontStyle: 0
m_HorizontalAlignment: 2
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!222 &1842377329
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1842377326}
m_CullTransparentMesh: 1
--- !u!1 &1844986817
GameObject:
m_ObjectHideFlags: 0
@ -56568,6 +56705,57 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1983771878}
m_CullTransparentMesh: 1
--- !u!1 &1984502420
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1984502422}
- component: {fileID: 1984502421}
m_Layer: 0
m_Name: VitrinaCatcher
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1984502421
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1984502420}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: bf7510f112a729f48898310fab415f6a, type: 3}
m_Name:
m_EditorClassIdentifier:
notification:
Title: "\u0412\u0438\u0442\u0440\u0438\u043D\u0430"
Message:
Image: {fileID: 899680085, guid: 8b52ebc6fd45dde49a9cd603177ac97a, type: 3}
ImagePosition: {x: -172.5, y: 9.9}
ImageSize: 445
audioClip: {fileID: 8300000, guid: 1c48900c9ab0aa8418920a8193050870, type: 3}
--- !u!4 &1984502422
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1984502420}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -7.0255466, y: 1.3595145, z: -11.291598}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1997844673
GameObject:
m_ObjectHideFlags: 0
@ -72400,3 +72588,4 @@ SceneRoots:
- {fileID: 2115833904}
- {fileID: 1165555974}
- {fileID: 592995719}
- {fileID: 1984502422}

View File

@ -122,7 +122,7 @@ NavMeshSettings:
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &540927406
--- !u!1 &1394197046
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
@ -130,173 +130,37 @@ GameObject:
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 540927408}
- component: {fileID: 540927407}
- component: {fileID: 1394197048}
- component: {fileID: 1394197047}
m_Layer: 0
m_Name: Server
m_Name: GameObject
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &540927407
--- !u!114 &1394197047
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 540927406}
m_GameObject: {fileID: 1394197046}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 27edee241c0fb934ebe299af31cf6c77, type: 3}
m_Script: {fileID: 11500000, guid: 9fe16682c3dc1f84293b4c295da6b8e0, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!4 &540927408
--- !u!4 &1394197048
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 540927406}
m_GameObject: {fileID: 1394197046}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 4.206707, y: 5.733204, z: 5.57958}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1115965265
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1115965268}
- component: {fileID: 1115965267}
- component: {fileID: 1115965266}
m_Layer: 0
m_Name: Main Camera
m_TagString: MainCamera
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!81 &1115965266
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1115965265}
m_Enabled: 1
--- !u!20 &1115965267
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1115965265}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 1
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_Iso: 200
m_ShutterSpeed: 0.005
m_Aperture: 16
m_FocusDistance: 10
m_FocalLength: 50
m_BladeCount: 5
m_Curvature: {x: 2, y: 11}
m_BarrelClipping: 0.25
m_Anamorphism: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
field of view: 60
orthographic: 1
orthographic size: 5
m_Depth: -1
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 1
m_AllowMSAA: 1
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!4 &1115965268
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1115965265}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1722234523
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1722234525}
- component: {fileID: 1722234524}
m_Layer: 0
m_Name: Settings
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1722234524
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1722234523}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 896b3b244e3f8db4c8a6f4591ca9c68f, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!4 &1722234525
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1722234523}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 4.206707, y: 5.733204, z: 5.57958}
m_LocalPosition: {x: -7.0255466, y: 1.3595145, z: -11.291598}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
@ -306,6 +170,4 @@ Transform:
SceneRoots:
m_ObjectHideFlags: 0
m_Roots:
- {fileID: 1115965268}
- {fileID: 540927408}
- {fileID: 1722234525}
- {fileID: 1394197048}

View File

@ -0,0 +1,23 @@
using UnityEngine;
public class VitrinaCatcher : MonoBehaviour
{
public static VitrinaCatcher Instance { get; private set; }
public Notification notification;
public void Initialize()
{
Instance = this;
PrizeStandController.OpeningFinished += PrizeStandController_OpeningFinished;
}
public void PrizeStandController_OpeningFinished(bool arg1, string arg2)
{
Debug.Log($"vitrina: {arg1} : {arg2}");
//if (!arg1) return;
notification.Title = "Витрина";
notification.Message = arg2;
NotificationSystem.instance.Notificate(notification);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bf7510f112a729f48898310fab415f6a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,53 +1,20 @@
using System;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using UnityEngine;
public static class ExternalFilesGetter
{
const string GAMEDATA_FOLDER = "C4";
const string IMAGES_DIR = "Images";
const string SERIAL_PATH = "serialdata.dat";
const string OVERRIDES_FILE = "overrides/Light.conf";
const string ELEKTRO_VERSION_FILE = "elektro_version.rr";
const string SENSOR_VERSION_FILE = "HopperSensor.rr";
const string SETTINGS_PASSWORD = "SettingsPwd.rr";
/// <summary>
///
/// </summary>
/// <param name="spriteName">передавать без расширения</param>
/// <returns></returns>
public static Sprite GetChipSprite(string spriteName)
{
string docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string path = Path.Combine(docPath, GAMEDATA_FOLDER,IMAGES_DIR, spriteName+".png");
//проверить есть ли файл
if (File.Exists(path))
{
var bytes = File.ReadAllBytes(path);
var tex = new Texture2D(2,2,TextureFormat.ARGB32, false);
tex.LoadImage(bytes);
tex.filterMode = FilterMode.Point;
var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f),pixelsPerUnit: 625);
return sprite;
}
else
{
Debug.LogWarning($"Не найден файл {spriteName}");
return null;
}
}
/// <summary>
/// Читает серийные данные из файла serialdata.dat
/// </summary>
/// <returns>Текст из файла или "<не указано>", если файл не существует</returns>
public static string GetSerial()
{
string docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

View File

@ -9,6 +9,7 @@ using System.Collections;
using PrizeSystem;
#if UNITY_EDITOR
using UnityEditor;
#endif
@ -434,6 +435,9 @@ public class GameManager : MonoBehaviour
public VitrinaPanel vitrinaPanel;
public QrPanel qrPanel;
private Logger logger;
public VitrinaCatcher vitrinaCatcher;
void Awake()
{
Cursor.lockState = CursorLockMode.Locked;
@ -444,7 +448,7 @@ public class GameManager : MonoBehaviour
if (instance == null)
{
Application.targetFrameRate = 60;
instance = this;
fieldModel = new FieldModel(Cols, Rows);
chipTransforms = new Transform[Rows, Cols];
@ -459,7 +463,59 @@ public class GameManager : MonoBehaviour
//Тестируем пока что. Не прод(ну ладно прод.)
ComWrapper.Initialize(ComNativeWrapper.ListPorts(), boardVersion);
logger = GetComponent<Logger>();
//ИНИЦИАЛИЗАЦИЯ УСТРОЙСТВ
var devices = ComPortScanner.ScanPorts();
logger.Log($"Найдено устройств: {devices.Count}");
bool controllerFound = false;
string vitrinaPort = null;
PrizeStandController.InitializationComplete += PrizeStandController_InitializationComplete;
foreach (var device in devices)
{
//Витрина
if (device.Value == ComPortScanner.DeviceType.PrizeShowcase)
{
vitrinaPort = device.Key;
logger.Log($"нашел витрину на порту, {vitrinaPort}");
}
else if (device.Value == ComPortScanner.DeviceType.ControllerV1)
{
if (!controllerFound)
{
logger.Log("Нашел периферийник1 открываю.");
ComWrapper.Initialize(device.Key, boardVersion: 1);
controllerFound = true;
}
}
else if (device.Value == ComPortScanner.DeviceType.ControllerV2)
{
if (!controllerFound)
{
logger.Log("Нашел периферийник2 открываю.");
ComWrapper.Initialize(device.Key, boardVersion: 2);
controllerFound = true;
}
}
else
{
logger.Log($"неизвестный тип {device.Key}:{device.Value}");
}
}
if (vitrinaPort != null)
{
logger.Log($"Витрину открываем (порт {vitrinaPort}");
int h = ComNativeWrapper.Serial_Open(vitrinaPort, 115200, 8, 0, 1, 4, 10);
if (h > 0) logger.Log("Открыл");
else logger.Log("Не Открыл");
PrizeStandController.Initialize(h, 5, logger);
}
//рабочая(до витрины) версия
//ComWrapper.Initialize(ComNativeWrapper.ListPorts(), boardVersion);
SingleWallet.Initialize();
@ -470,6 +526,9 @@ public class GameManager : MonoBehaviour
soundManager.Initialize();
GetComponent<NotificationSystem>().Initialize();
vitrinaCatcher.Initialize();
ProcessDifficultyCounter();
GetComponent<In>().Initialize();
@ -491,6 +550,14 @@ public class GameManager : MonoBehaviour
avatarController.Initialize();
}
private void OnApplicationQuit()
{
ComNativeWrapper.Serial_CloseAll();
}
private void PrizeStandController_InitializationComplete(bool arg1, string arg2)
{
logger.Log($"Инициализация витрины: {arg1} : {arg2}");
}
private static void InitializePrefs()
{
@ -634,7 +701,7 @@ public class GameManager : MonoBehaviour
string difficultyType = SettingsManager.GetString("BaseBotDifficulty", "?");
//Инициализация
if(!PlayerPrefs.HasKey("PulsesToResetDifficulty"))
if (!PlayerPrefs.HasKey("PulsesToResetDifficulty"))
PlayerPrefs.SetInt("PulsesToResetDifficulty", 100);
//если переход с повышенной на обычную - сброс счетчика
@ -676,7 +743,7 @@ public class GameManager : MonoBehaviour
print("Сложность повышена");
}
}
IEnumerator AddBotRecordEvery2Days()
{

View File

@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
public static class ComPortScanner
{
// Параметры соединения
private const int BaudRate = 115200;
private const int DataBits = 8;
private const int Parity = 0; // 0 = None
private const int StopBits = 1; // 1 = One
private const int ReadTimeoutMs = 5;
private const int WriteTimeoutMs = 10;
// Команды устройств
private const string CmdPrize = "97"; // Для витрины и периферийника2
private const string CmdControllerV1 = "666"; // Только для периферийника1
// Типы устройств
public enum DeviceType
{
None,
ControllerV1, // Периферийник1
ControllerV2, // Периферийник2
PrizeShowcase // Витрина
}
// Префиксы ответов
private const string ControllerV1ResponsePrefix = "ENC:"; // Периферийник1 на 666
private const string ControllerV1NotActivatedPrefix = "NOT_ACTIVATED:"; // Периферийник1 на всё остальное
private const string RolePrefix = "ROLE :"; // Периферийник2 и витрина на 97
private const string PrizeRoleValue = "vitrina"; // Витрина: ROLE : vitrina
/// <summary>
/// Сканирует все доступные COM-порты и возвращает словарь {порт = тип устройства}
/// </summary>
public static Dictionary<string, DeviceType> ScanPorts()
{
var result = new Dictionary<string, DeviceType>();
string[] ports = ComNativeWrapper.ListPorts();
foreach (string port in ports)
{
DeviceType type = CheckPort(port);
if (type != DeviceType.None)
{
result[port] = type;
}
}
return result;
}
/// <summary>
/// Проверяет один порт на наличие известных устройств
/// </summary>
private static DeviceType CheckPort(string port)
{
Debug.Log($"Проверка порта {port}");
// ШАГ 1: Проверяем периферийник1 по команде 666
string response666 = CheckDevice(port, CmdControllerV1);
if (response666 != null)
{
// Если ответ начинается с "ENC:" - это точно периферийник1
if (response666.StartsWith(ControllerV1ResponsePrefix) || response666.StartsWith("ALREADY_ACTIVATED"))
{
return DeviceType.ControllerV1;
}
}
// ШАГ 2: Проверяем по команде 97 (витрина и периферийник2)
string response97 = CheckDevice(port, CmdPrize);
if (response97 != null && response97.StartsWith(RolePrefix))
{
Debug.Log($"Role: {response97}");
// Извлекаем значение после "ROLE : "
string roleValue = response97.Substring(RolePrefix.Length).Trim();
// Если роль = "vitrina" - это витрина
if (string.Equals(roleValue, PrizeRoleValue, StringComparison.OrdinalIgnoreCase))
{
return DeviceType.PrizeShowcase;
}
else
{
return DeviceType.ControllerV2;
}
}
// ШАГ 3: Дополнительная проверка для неактивированного периферийника1
// Если устройство ответило на 97 с "NOT_ACTIVATED:" - это периферийник1
if (response97 != null && response97.StartsWith(ControllerV1NotActivatedPrefix))
{
return DeviceType.ControllerV1;
}
return DeviceType.None;
}
/// <summary>
/// Очищает входной буфер порта
/// </summary>
private static void ClearPortBuffer(int handle)
{
byte[] tempBuffer = new byte[1024];
const float idleTimeout = 0.5f;
const int maxreads = 50;
int reads = 0;
float startTime = Time.unscaledTime;
while (reads < maxreads)
{
int bytesRead = ComNativeWrapper.Serial_ReadLineUTF8_bytes(handle, tempBuffer, tempBuffer.Length);
reads++;
if (bytesRead > 0)
{
startTime = Time.unscaledTime;
continue;
}
else if (bytesRead == 0)
{
float elapsed = Time.unscaledTime - startTime;
if (elapsed >= idleTimeout)
{
break;
}
}
else
{
break;
}
}
}
/// <summary>
/// Проверяет устройство на порту и возвращает ответ
/// </summary>
private static string CheckDevice(string port, string command)
{
int handle = ComNativeWrapper.Serial_Open(port, BaudRate, DataBits, Parity, StopBits, 10, WriteTimeoutMs);
if (handle <= 0)
{
return null;
}
try
{
ClearPortBuffer(handle);
int writeResult = ComNativeWrapper.Serial_WriteStringUTF8(handle, command, 1);
if (writeResult < 0)
{
return null;
}
byte[] buffer = new byte[1024];
int bytesRead = ComNativeWrapper.Serial_ReadLineUTF8_bytes(handle, buffer, buffer.Length);
if (bytesRead <= 0)
{
return null;
}
string line = Encoding.UTF8.GetString(buffer, 0, bytesRead).Trim();
return string.IsNullOrEmpty(line) ? null : line;
}
finally
{
ComNativeWrapper.Serial_CloseAll();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae6854b106d72fc4fa2513b693762a61
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -27,26 +27,43 @@ public static class ComWrapper
private static int h;
public static int BoardVersion { get; private set; }
private static ILogger logger;
private static string[] AvailablePorts;
private static string[] AvailablePorts => ComNativeWrapper.ListPorts();
private static int _lastInputTick = 0;
private const int INPUT_IDLE_BEFORE_PING_MS = 60_000;//1 минута
public static void Initialize(string[] availablePorts, int boardVersion)//Добавить логгер в конструктор
//public static void Initialize(string[] availablePorts, int boardVersion)//Добавить логгер в конструктор
//{
// BoardVersion = boardVersion;
// AvailablePorts = availablePorts;
// if (_lifetimeCts != null) return;
// _ = OpenPortAsync(AvailablePorts);
// if (h <= 0)
// {
// Log("Порт не открылся при инициализации");
// return;
// }
// UnityMainThreadDispatcher.Initialize();
// _lifetimeCts = new CancellationTokenSource();
// StartReaderThread();
// _ = SetupBoardAsync(_lifetimeCts.Token);
//}
public static void Initialize(string port, int boardVersion)//Добавить логгер в конструктор
{
BoardVersion = boardVersion;
AvailablePorts = availablePorts;
if (_lifetimeCts != null) return;
_ = OpenPortAsync(AvailablePorts);
_ = OpenPortAsync(port);
if (h <= 0)
{
Log("Порт не открылся при инициализации");
return;
}
UnityMainThreadDispatcher.Initialize();
_lifetimeCts = new CancellationTokenSource();
@ -269,14 +286,39 @@ public static class ComWrapper
_ = StartKeepAliveProcessAsync(ct);
}
private static async Task OpenPortAsync(string[] availablePorts)
//private static async Task OpenPortAsync(string[] availablePorts)
//{
// Log("Инициализация порта");
// var ports = availablePorts;
// if (ports.Length == 0) { Log("Нет портов, завершаю"); return; }
// string port = ports[^1];
// ComNativeWrapper.Serial_CloseAll();
// Log("Открываю порт: " + port);
// h = ComNativeWrapper.Serial_Open(port, 115200, 8, 0, 1, 4, 10);
// Log($"handle: {h}");
// if (h <= 0)
// {
// Log("Open failed: " + ComNativeWrapper.GetLastError());
// return;
// }
// else
// {
// Log($"Открыл порт: {port}");
// FlushPortBuffer();
// }
// WaitAllCoins();
//}
private static async Task OpenPortAsync(string port)
{
Log("Инициализация порта");
var ports = availablePorts;
if (ports.Length == 0) { Log("Нет портов, завершаю"); return; }
string port = ports[^1];
ComNativeWrapper.Serial_CloseAll();
Log("Открываю порт: " + port);
@ -494,7 +536,9 @@ public static class ComWrapper
CancelAllWaiters();
ClosePort();
OpenPortAsync(AvailablePorts);
//OpenPortAsync(AvailablePorts);
OpenPortAsync(AvailablePorts[^1]);
if (h <= 0)
{
Log($"Ошибка при переподключении.");

View File

@ -0,0 +1,69 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
private const int BaudRate = 115200;
private const int DataBits = 8;
private const int Parity = 0; // 0 = None
private const int StopBits = 1; // 1 = One
private const int ReadTimeoutMs = 5;
private const int WriteTimeoutMs = 10;
string port = "";
private void Start()
{
ComNativeWrapper.Serial_CloseAll();
var devices = ComPortScanner.ScanPorts();
print($"Найдено устройств: {devices.Count}");
foreach (var device in devices)
{
//Витрина
if (device.Value == ComPortScanner.DeviceType.PrizeShowcase)
{
port = device.Key;
print($"{device.Key} нашел витрину, запускаю инициализацию");
}
else if (device.Value == ComPortScanner.DeviceType.ControllerV1)
{
port = device.Key;
print($"{device.Key} Нашел периферийник1 открываю.");
}
else if (device.Value == ComPortScanner.DeviceType.ControllerV2)
{
port = device.Key;
print($"{device.Key} Нашел периферийник2 открываю.");
}
else
{
print($"неизвестный тип {device.Key}:{device.Value}");
}
}
}
int h;
private void Update()
{
if (Input.GetKeyDown(KeyCode.Alpha1))
{
h = ComNativeWrapper.Serial_Open(port, 115200, 8, 0, 1, 4, 10);
if(h <= 0)
{
print($"Неудачное открытие {port}, h={h}");
}
}
else if (Input.GetKeyDown(KeyCode.Alpha2))
{
int res = ComNativeWrapper.Serial_Close(h);
print($"Закрытие {port}: {res}");
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9fe16682c3dc1f84293b4c295da6b8e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -2,7 +2,7 @@
using System.IO;
using UnityEngine;
public class Logger : MonoBehaviour
public class Logger : MonoBehaviour, ILogger
{
private static Logger instance;
private string logFilePath;
@ -16,6 +16,10 @@ public class Logger : MonoBehaviour
}
}
public ILogHandler logHandler { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public bool logEnabled { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public LogType filterLogType { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
/// <summary>
/// Инициализирует логгер и создает файл лога
/// </summary>
@ -29,7 +33,7 @@ public class Logger : MonoBehaviour
// Создаем имя файла с датой и временем
string fileName = $"comlog_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.txt";
logFilePath = Path.Combine(persistentPath, fileName);
logFilePath = Path.Combine(persistentPath,"LOGS", fileName);
// Создаем директорию если не существует
string directory = Path.GetDirectoryName(logFilePath);
@ -56,7 +60,7 @@ public class Logger : MonoBehaviour
/// Записывает сообщение в лог
/// </summary>
/// <param name="message">Сообщение для записи</param>
public void Log(string message)
public void Log(object message)
{
if (writer == null)
{
@ -107,4 +111,67 @@ public class Logger : MonoBehaviour
{
Close();
}
public bool IsLogTypeAllowed(LogType logType)
{
return false;
}
public void Log(LogType logType, object message)
{
}
public void Log(LogType logType, object message, UnityEngine.Object context)
{
}
public void Log(LogType logType, string tag, object message)
{
}
public void Log(LogType logType, string tag, object message, UnityEngine.Object context)
{
}
public void Log(string tag, object message)
{
}
public void Log(string tag, object message, UnityEngine.Object context)
{
}
public void LogWarning(string tag, object message)
{
}
public void LogWarning(string tag, object message, UnityEngine.Object context)
{
}
public void LogError(string tag, object message)
{
}
public void LogError(string tag, object message, UnityEngine.Object context)
{
}
public void LogFormat(LogType logType, string format, params object[] args)
{
}
public void LogException(Exception exception)
{
}
public void LogFormat(LogType logType, UnityEngine.Object context, string format, params object[] args)
{
}
public void LogException(Exception exception, UnityEngine.Object context)
{
}
}

View File

@ -1,27 +1,27 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class VitrinaPanel : MonoBehaviour
{
private bool initialized = false;
//запросить коробки, проверить какие требуется, найти пересечение. Если нет, то возврат.
//запросить коробки, проверить какие требуется, найти пересечение. Если нет, то возврат.
public event Action OnComplete;
[SerializeField] private Transform BoxesRoot;
[SerializeField] private GameObject BoxViewPrefab;
private List<VitrinaBox> generatedBoxes;
private List<VitrinaBox> generatedBoxes = new();
private int pointerIndex;
[SerializeField] private float timer = 0;
private float timeToAct = 3;
VitrinaBox CurrentBox => generatedBoxes[pointerIndex];
private void OnEnable()
{
initialized = false;
initialized = false;
}
private void OnDisable()
{
@ -32,14 +32,44 @@ public class VitrinaPanel : MonoBehaviour
public void Initialize(int[] requiredBoxed)
{
if (initialized) return;
//тут проверка что есть такие коробки
//тут проверка что есть такие коробки
int[] readyBoxes = PrizeStandController.GetReadyBoxes();
int[] res = FindIntersection(requiredBoxed, readyBoxes);
if (res.Length == 0)
{
OnComplete?.Invoke();
VitrinaCatcher.Instance.PrizeStandController_OpeningFinished(false, "Нет призов");
gameObject.SetActive(false);
return;
}
pointerIndex = 0;
GenerateBoxes(requiredBoxed);
GenerateBoxes(res);
CurrentBox.SetEnabled(true);
initialized = true;
}
public static int[] FindIntersection(int[] array1, int[] array2)
{
if (array1 == null || array2 == null || array1.Length == 0 || array2.Length == 0)
{
return Array.Empty<int>();
}
// Используем HashSet для эффективного поиска
HashSet<int> set1 = new HashSet<int>(array1);
// Находим пересечение
return array2.Where(x => set1.Contains(x)).Distinct().ToArray();
}
private void GenerateBoxes(int[] indexes)
{
if (generatedBoxes == null)
@ -58,15 +88,18 @@ public class VitrinaPanel : MonoBehaviour
else
{
Destroy(go);
Debug.Log("Префаб не содержит компонента VitrinaBoxView", gameObject);
Debug.Log("Префаб не содержит компонента VitrinaBoxView", gameObject);
}
}
}
private void ClearBoxes()
{
foreach (var box in generatedBoxes)
Destroy(box.gameObject);
generatedBoxes.Clear();
if (generatedBoxes != null)
{
foreach (var box in generatedBoxes)
Destroy(box.gameObject);
generatedBoxes.Clear();
}
}
private void Update()
@ -77,7 +110,7 @@ public class VitrinaPanel : MonoBehaviour
if (input.direction != 0)
{
int count = generatedBoxes == null ? 0 : generatedBoxes.Count;
//отобразить изменения
//отобразить изменения
CurrentBox.SetEnabled(false);
CurrentBox.shake = false;
@ -102,11 +135,12 @@ public class VitrinaPanel : MonoBehaviour
if (timer >= timeToAct)
{
_ = PrizeStandController.OpenBoxAsync(pointerIndex + 1);
_ = PrizeStandController.OpenBoxAsync(CurrentBox.Number);
timer = 0;
OnComplete?.Invoke();
gameObject.SetActive(false);
return;
}
}

View File

@ -1,4 +1,4 @@
using PrizeSystem;
using PrizeSystem;
using System;
using TMPro;
using UnityEngine;
@ -29,11 +29,14 @@ public class QrPanel : MonoBehaviour
{
if (initialized) return;
if (prize is QrPrize qrPrize)
{
string message = $"Çäðàâñòâóéòå! ß ïîáåäèë íà àïïàðàòå {PlayerPrefs.GetString("Serial")}. Æäó ïðèç!" +
$" Äàòà âûèãðûøà: {DateTime.Now:dd.MM.yyyy HH:mm}. " +
$"Ïðèç: {qrPrize.PrizeText}";
string message = $"Здравствуйте! Я победил на аппарате {PlayerPrefs.GetString("Serial")}. Жду приз!" +
$" Дата выигрыша: {DateTime.Now:dd.MM.yyyy HH:mm}. " +
$"Приз: {qrPrize.PrizeText}";
message = workOnSuffix(message);

View File

@ -96,7 +96,7 @@ public static class PrizeStandController
{
if(!await _lock.WaitAsync(0))
{
OpeningFinished?.Invoke(false, "Выдача уже в процессе");
OpeningFinished?.Invoke(true, "Выдача уже в процессе");
return;
}
@ -134,7 +134,7 @@ public static class PrizeStandController
else
{
_logger?.Log($"[Модуль витрины] Ошибка при открытии ячейки {boxNumber}");
OpeningFinished?.Invoke(false, $"Ошибка при открытии ячейки номер {boxNumber}");
OpeningFinished?.Invoke(true, $"Ошибка при открытии ячейки {boxNumber}");
}
}
catch (Exception ex)
@ -164,20 +164,22 @@ public static class PrizeStandController
{
bool isOk = true;
StringBuilder sb = new StringBuilder();
sb.AppendLine($"[Модуль витрины. Проверка перед открытием]");
//sb.AppendLine($"[Модуль витрины. Проверка перед открытием]");
//Проверка инициализации
if (!Initialized)
{
isOk = false;
sb.AppendLine("- Модуль не инициализирован.");
//sb.AppendLine("- Модуль не инициализирован.");
sb.AppendLine("Витрина не подключена");
}
bool boxNumberIsValid = boxNumber > 0 && boxNumber <= BoxesCount;
if (!boxNumberIsValid)
{
isOk = false;
sb.AppendLine($"- Номер коробки={boxNumber} невалидный. Требуется от 1 до {BoxesCount}.");
//sb.AppendLine($"- Номер коробки={boxNumber} невалидный. Требуется от 1 до {BoxesCount}.");
sb.AppendLine($"Ошибка");
}
//Не лезем в порт с запросом статуса коробки, если то, что выше дало НеОК
@ -189,12 +191,14 @@ public static class PrizeStandController
if (!READY_TO_OPEN_STATUSES.Contains(boxStatus))
{
isOk = false;
sb.AppendLine($"- Статус коробки {boxNumber}: {boxStatus} не позволяет провести открытие.");
//sb.AppendLine($"- Статус коробки {boxNumber}: {boxStatus} не позволяет провести открытие.");
sb.AppendLine($"Ошибка");
}
}
else
{
sb.AppendLine($"- Невозможно проверить статус коробки {boxNumber} из-за ошибок выше.");
sb.AppendLine($"Ошибка");
//sb.AppendLine($"- Невозможно проверить статус коробки {boxNumber} из-за ошибок выше.");
}
return (isOk, sb.ToString());
@ -380,6 +384,65 @@ public static class PrizeStandController
_ => BoxStatus.ParsingError
};
#endregion
////ТЕСТОВОК
/// <summary>
/// Получить номера ячеек, готовых к открытию (статус OK или OKplusCard)
/// </summary>
/// <returns>Массив номеров готовых к выдаче ячеек</returns>
public static async Task<int[]> GetReadyBoxesAsync()
{
try
{
// Получаем статусы всех ячеек
Dictionary<int, BoxStatus> allStatuses = await GetStatusesAsync();
// Фильтруем только те, что готовы к открытию
var readyBoxes = allStatuses
.Where(kvp => READY_TO_OPEN_STATUSES.Contains(kvp.Value))
.Select(kvp => kvp.Key)
.ToArray();
_logger?.Log($"[Модуль витрины] Найдено готовых к выдаче ячеек: {readyBoxes.Length}");
return readyBoxes;
}
catch (Exception ex)
{
_logger?.Log($"[Модуль витрины] Ошибка при получении списка готовых ячеек: {ex.Message}");
return Array.Empty<int>();
}
}
/// <summary>
/// Получить номера ячеек, готовых к открытию (синхронная обертка с таймаутом)
/// </summary>
/// <param name="timeoutMs">Таймаут ожидания в миллисекундах</param>
/// <returns>Массив номеров готовых к выдаче ячеек</returns>
public static int[] GetReadyBoxes(int timeoutMs = 1000)
{
try
{
var task = Task.Run(async () => await GetReadyBoxesAsync());
if (task.Wait(timeoutMs))
{
return task.Result;
}
else
{
_logger?.Log($"[Модуль витрины] Таймаут при получении списка готовых ячеек");
return Array.Empty<int>();
}
}
catch (Exception ex)
{
_logger?.Log($"[Модуль витрины] Ошибка при получении списка готовых ячеек: {ex.Message}");
return Array.Empty<int>();
}
}
}
public enum BoxStatus

File diff suppressed because one or more lines are too long