Доработать игровые режимы, призы и настройки

Добавить таймеры и шкалы прогресса, расширить призовую систему и контроль остатков, восстановить настройки и обновить таблицу призов главного меню.
This commit is contained in:
Сергей Бондарь 2026-09-14 10:47:25 +03:00
parent 16847fedf3
commit 826ff5782a
83 changed files with 5954 additions and 749 deletions

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u043C\u0443\u0437\u044B\u043A\u0438"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 1

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u043F\u0440\u043E\u043C\u043E
\u0440\u043E\u043B\u0438\u043A\u0430"
m_EditorClassIdentifier: Assembly-CSharp::Setting

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u044D\u0444\u0444\u0435\u043A\u0442\u043E\u0432"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 1

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0412\u044B\u0440\u0443\u0447\u043A\u0430\u0414\u043E\u0421\u0431\u0440\u043E\u0441\u0430
\u0441\u043B\u043E\u0436\u043D\u043E\u0441\u0442\u0438"
m_EditorClassIdentifier: Assembly-CSharp::Setting

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0412\u044B\u0441\u043E\u0442\u0430 \u043F\u043E\u043B\u044F"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 0

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0420\u0435\u0436\u0438\u043C \u0430\u043F\u043F\u0430\u0440\u0430\u0442\u0430"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 2

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0421\u043B\u043E\u0436\u043D\u043E\u0441\u0442\u044C"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 2

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0428\u0438\u0440\u0438\u043D\u0430 \u043F\u043E\u043B\u044F"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 0

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u041D\u043E\u043C\u0435\u0440 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 4

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u041D\u043E\u043C\u0435\u0440 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 4

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u041F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C \u043A\u043E\u043D\u0442\u0430\u043A\u0442\u044B
\u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438"
m_EditorClassIdentifier: Assembly-CSharp::Setting

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0411\u0435\u0441\u043F\u043B\u0430\u0442\u043D\u0430\u044F \u0438\u0433\u0440\u0430"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 3

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0412\u0430\u043B\u044E\u0442\u0430\u0417\u0430\u0418\u043C\u043F\u0443\u043B\u044C\u0441"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 0

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0421\u0431\u0440\u043E\u0441 \u0431\u0430\u043B\u0430\u043D\u0441\u0430"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 4

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0426\u0435\u043D\u0430 PVE"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 0

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0426\u0435\u043D\u0430 PVP"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 0

View File

@ -22,4 +22,5 @@ MonoBehaviour:
- {fileID: 11400000, guid: ff6ba1e5433e65f42b32e699881216df, type: 2}
- {fileID: 11400000, guid: cf78629a01d1c324f9174222008fd1aa, type: 2}
- {fileID: 11400000, guid: c017e427d6f01bd4581d43eeaced04ea, type: 2}
- {fileID: 11400000, guid: b8e303c41b6074d4d912fe9d680a6676, type: 2}
- {fileID: 11400000, guid: 3f8fa9e7aa5f2c2408d056bfab8434f4, type: 2}

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0412\u044B\u0432\u043E\u0434 \u043E\u0448\u0438\u0431\u043A\u0438 \u0432\u044B\u0434\u0430\u0447\u0438
\u0432 \u043C\u0435\u043D\u044E"
m_EditorClassIdentifier: Assembly-CSharp::Setting

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0412\u044B\u0432\u043E\u0434\u0438\u0442\u044C \u043F\u0440\u0438\u0437\u044B
\u0437\u0430\u043A\u043E\u043D\u0447\u0438\u043B\u0438\u0441\u044C"
m_EditorClassIdentifier: Assembly-CSharp::Setting

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0412\u044B\u0434\u0430\u0432\u0430\u0442\u044C \u043F\u0440\u0438\u0437
\u043F\u0440\u0438 \u043E\u0448\u0438\u0431\u043A\u0435"
m_EditorClassIdentifier: Assembly-CSharp::Setting

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0412\u044B\u0434\u0430\u0447\u0430 \u043F\u0440\u0438\u0437\u043E\u0432"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 3

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0417\u0430\u0431\u0440\u0430\u0442\u044C \u0438\u043B\u0438 \u0440\u0438\u0441\u043A\u043D\u0443\u0442\u044C"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 3

View File

@ -0,0 +1,39 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u041A\u043E\u043D\u0442\u0440\u043E\u043B\u0438\u0440\u043E\u0432\u0430\u0442\u044C
\u043E\u0441\u0442\u0430\u0442\u043A\u0438"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 3
Id: PrizeStockControl
Alias: "\u041A\u043E\u043D\u0442\u0440\u043E\u043B\u0438\u0440\u043E\u0432\u0430\u0442\u044C
\u043E\u0441\u0442\u0430\u0442\u043A\u0438"
Description: "\u0412\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u0443\u0447\u0451\u0442
\u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043A\u0438\u0445 Countable-\u043F\u0440\u0438\u0437\u043E\u0432.
\u041F\u0440\u0438 \u0443\u0441\u043F\u0435\u0448\u043D\u043E\u0439 \u0432\u044B\u0434\u0430\u0447\u0435
\u043E\u0441\u0442\u0430\u0442\u043E\u043A \u0443\u043C\u0435\u043D\u044C\u0448\u0430\u0435\u0442\u0441\u044F
\u043D\u0430 \u043E\u0434\u0438\u043D. \u041F\u0440\u0438 \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0438
\u043E\u0441\u0442\u0430\u0442\u043E\u043A \u043D\u0435 \u0441\u043F\u0438\u0441\u044B\u0432\u0430\u0435\u0442\u0441\u044F
\u0438 \u043F\u0440\u0435\u0434\u0443\u043F\u0440\u0435\u0436\u0434\u0435\u043D\u0438\u0435
\u0432 \u043C\u0435\u043D\u044E \u0441\u043A\u0440\u044B\u0442\u043E."
BoolValue: 0
IntValue: 0
IntMin: 0
IntMax: 0
IntStep: 0
FloatValue: 0
FloatMin: 0
FloatMax: 0
FloatStep: 0
StringValue:
AllowedValues: []
SignalValue:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b8e303c41b6074d4d912fe9d680a6676
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u041D\u0430\u0441\u0442\u0440\u043E\u0438\u0442\u044C \u043F\u0440\u0438\u0437\u044B"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 4

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0421\u0431\u0440\u043E\u0441 \u043E\u0448\u0438\u0431\u043A\u0438 \u0432\u044B\u0434\u0430\u0447\u0438"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 4

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0441\u0447\u0435\u0442\u0447\u0438\u043A\u0438"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 4

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0422\u0435\u0441\u0442 \u044D\u043A\u0440\u0430\u043D\u0430"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 4

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0422\u0435\u0441\u0442QR"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 4

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0422\u0435\u0441\u0442\u0412\u0432\u043E\u0434\u0430"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 4

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0422\u0435\u0441\u0442\u0417\u0432\u0443\u043A\u0430"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 4

View File

@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c97246c0e3a7eff46acd53fd591fb800, type: 3}
m_Script: {fileID: 11500000, guid: 596f85640c2743e4d8be1668d117676b, type: 3}
m_Name: "\u0422\u0435\u0441\u0442\u0425\u043E\u043F\u043F\u0435\u0440\u0430"
m_EditorClassIdentifier: Assembly-CSharp::Setting
Type: 4

View File

@ -67,7 +67,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::CharacterSystem.VisualModule
icon: {fileID: -8149160197350721816, guid: fbeb9853b48ea2844a0115637f7d3427, type: 3}
iconSmall: {fileID: 0}
iconSmall: {fileID: 6506343174705358099, guid: b84c4d324265bea4ba491f56d3c8b1c8, type: 3}
chipImage: {fileID: -8703082228858259314, guid: 86e3e7edd9d4ad44badffb017e45ffc7, type: 3}
chipSpawningAnim: {fileID: 0}
chipThrowClip: {fileID: 0}

View File

@ -67,7 +67,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::CharacterSystem.VisualModule
icon: {fileID: -529477370105943367, guid: 229dd13b366e5b04ab570cce8b4796e4, type: 3}
iconSmall: {fileID: 0}
iconSmall: {fileID: 3349644112325863184, guid: b84c4d324265bea4ba491f56d3c8b1c8, type: 3}
chipImage: {fileID: 6137273327222775856, guid: 22249bfcd1079244da596525df47b46c, type: 3}
chipSpawningAnim: {fileID: 0}
chipThrowClip: {fileID: 0}

View File

@ -158,7 +158,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::CharacterSystem.VisualModule
icon: {fileID: -1307639399570084333, guid: e35c65295e3ac234b9975dbe342f4090, type: 3}
iconSmall: {fileID: 0}
iconSmall: {fileID: 7174167584646727459, guid: b84c4d324265bea4ba491f56d3c8b1c8, type: 3}
chipImage: {fileID: -5258756546513945857, guid: c99c8ec80e4ab564f8f6e07f132a6a08, type: 3}
chipSpawningAnim: {fileID: 0}
chipThrowClip: {fileID: 0}

View File

@ -67,7 +67,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::CharacterSystem.VisualModule
icon: {fileID: 1809351836, guid: f52ee716872562446b5261ef80b4e8cd, type: 3}
iconSmall: {fileID: 0}
iconSmall: {fileID: -77006150763170147, guid: b84c4d324265bea4ba491f56d3c8b1c8, type: 3}
chipImage: {fileID: 7841686331975577030, guid: 5acad8b165856bf429879df3068a8f53, type: 3}
chipSpawningAnim: {fileID: 0}
chipThrowClip: {fileID: 0}

View File

@ -67,7 +67,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::CharacterSystem.VisualModule
icon: {fileID: -1325076913608113314, guid: b865778670f0b8c418a3450c41975937, type: 3}
iconSmall: {fileID: 0}
iconSmall: {fileID: -8315295956674182197, guid: b84c4d324265bea4ba491f56d3c8b1c8, type: 3}
chipImage: {fileID: -3439533911878954697, guid: be3a1e10203463d42a388463674e5a4f, type: 3}
chipSpawningAnim: {fileID: 0}
chipThrowClip: {fileID: 0}

View File

@ -196,13 +196,179 @@ RectTransform:
- {fileID: 1269013709772846192}
- {fileID: 1880094574948393022}
- {fileID: 5350466361621466464}
- {fileID: 7444578042112201723}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -92.10007, y: -293.43}
m_SizeDelta: {x: 642, y: 170.16}
m_SizeDelta: {x: 642, y: 215}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &5448719765887461587
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7444578042112201723}
- component: {fileID: 6958207988827824189}
- component: {fileID: 6001319883499774906}
- component: {fileID: 6566384581605980013}
- component: {fileID: 693705565310617844}
m_Layer: 5
m_Name: No prizes
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &7444578042112201723
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5448719765887461587}
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: 5673176593425789071}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 1}
m_AnchorMax: {x: 0.5, y: 1}
m_AnchoredPosition: {x: 0, y: -61}
m_SizeDelta: {x: 641.82, y: 30}
m_Pivot: {x: 0.5, y: 1}
--- !u!222 &6958207988827824189
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5448719765887461587}
m_CullTransparentMesh: 1
--- !u!114 &6001319883499774906
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5448719765887461587}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 0
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: "\u043C\u0430\u043B\u0435\u043D\u044C\u043A\u0438\u0435 \u043F\u0440\u0438\u0437\u044B
\u0437\u0430\u043A\u043E\u043D\u0447\u0438\u043B\u0438\u0441\u044C"
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 354e5ca841e41ac448f1254f8140bc2b, type: 2}
m_sharedMaterial: {fileID: 2554744838589517846, guid: 354e5ca841e41ac448f1254f8140bc2b, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4278208767
m_fontColor: {r: 1, g: 0.48, b: 0.18, 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: 23
m_fontSizeBase: 23
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 16
m_fontSizeMax: 64
m_fontStyle: 0
m_HorizontalAlignment: 1
m_VerticalAlignment: 256
m_textAlignment: 65535
m_characterSpacing: 0
m_characterHorizontalScale: 1
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 0
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 0
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 0
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: 0
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!225 &6566384581605980013
CanvasGroup:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5448719765887461587}
m_Enabled: 1
m_Alpha: 0
m_Interactable: 0
m_BlocksRaycasts: 0
m_IgnoreParentGroups: 0
--- !u!114 &693705565310617844
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5448719765887461587}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: bd2c707ec06de6d40b4beb1d35c177c2, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::PrizeSystem.PrizeStockEmptyLabel
_canvasGroup: {fileID: 6566384581605980013}
--- !u!1 &6074142002268861725
GameObject:
m_ObjectHideFlags: 0
@ -237,7 +403,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 1}
m_AnchorMax: {x: 0.5, y: 1}
m_AnchoredPosition: {x: 0.85001, y: -98.1}
m_AnchoredPosition: {x: 0.85001, y: -132}
m_SizeDelta: {x: 640.12, y: 30}
m_Pivot: {x: 0.5, y: 1}
--- !u!222 &778414474455501966
@ -375,7 +541,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 1}
m_AnchorMax: {x: 0.5, y: 1}
m_AnchoredPosition: {x: 0, y: -132.6}
m_AnchoredPosition: {x: 0, y: -169}
m_SizeDelta: {x: 641.82, y: 30}
m_Pivot: {x: 0.5, y: 1}
--- !u!222 &4430333533982892564
@ -526,7 +692,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 1}
m_AnchorMax: {x: 0.5, y: 1}
m_AnchoredPosition: {x: 0.000061035156, y: -61.60687}
m_AnchoredPosition: {x: 0.000061035156, y: -95}
m_SizeDelta: {x: 641.82, y: 30}
m_Pivot: {x: 0.5, y: 1}
--- !u!222 &3620820917664449704

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 928b3227336257848940a99bce2d45e8
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -100,43 +100,43 @@ MonoBehaviour:
obsoleteHasProbeVolumes:
m_Keys: []
m_Values:
m_PrefilteringModeMainLightShadows: 1
m_PrefilteringModeMainLightShadows: 4
m_PrefilteringModeAdditionalLight: 4
m_PrefilteringModeAdditionalLightShadows: 1
m_PrefilterXRKeywords: 0
m_PrefilteringModeForwardPlus: 1
m_PrefilteringModeDeferredRendering: 1
m_PrefilteringModeScreenSpaceOcclusion: 1
m_PrefilterDebugKeywords: 0
m_PrefilterWriteRenderingLayers: 0
m_PrefilterHDROutput: 0
m_PrefilterAlphaOutput: 0
m_PrefilterSSAODepthNormals: 0
m_PrefilterSSAOSourceDepthLow: 0
m_PrefilterSSAOSourceDepthMedium: 0
m_PrefilterSSAOSourceDepthHigh: 0
m_PrefilterSSAOInterleaved: 0
m_PrefilterSSAOBlueNoise: 0
m_PrefilterSSAOSampleCountLow: 0
m_PrefilterSSAOSampleCountMedium: 0
m_PrefilterSSAOSampleCountHigh: 0
m_PrefilterDBufferMRT1: 0
m_PrefilterDBufferMRT2: 0
m_PrefilterDBufferMRT3: 0
m_PrefilterSoftShadowsQualityLow: 0
m_PrefilterSoftShadowsQualityMedium: 0
m_PrefilterSoftShadowsQualityHigh: 0
m_PrefilteringModeAdditionalLightShadows: 0
m_PrefilterXRKeywords: 1
m_PrefilteringModeForwardPlus: 0
m_PrefilteringModeDeferredRendering: 0
m_PrefilteringModeScreenSpaceOcclusion: 0
m_PrefilterDebugKeywords: 1
m_PrefilterWriteRenderingLayers: 1
m_PrefilterHDROutput: 1
m_PrefilterAlphaOutput: 1
m_PrefilterSSAODepthNormals: 1
m_PrefilterSSAOSourceDepthLow: 1
m_PrefilterSSAOSourceDepthMedium: 1
m_PrefilterSSAOSourceDepthHigh: 1
m_PrefilterSSAOInterleaved: 1
m_PrefilterSSAOBlueNoise: 1
m_PrefilterSSAOSampleCountLow: 1
m_PrefilterSSAOSampleCountMedium: 1
m_PrefilterSSAOSampleCountHigh: 1
m_PrefilterDBufferMRT1: 1
m_PrefilterDBufferMRT2: 1
m_PrefilterDBufferMRT3: 1
m_PrefilterSoftShadowsQualityLow: 1
m_PrefilterSoftShadowsQualityMedium: 1
m_PrefilterSoftShadowsQualityHigh: 1
m_PrefilterSoftShadows: 0
m_PrefilterScreenCoord: 0
m_PrefilterScreenSpaceIrradiance: 0
m_PrefilterNativeRenderPass: 0
m_PrefilterScreenCoord: 1
m_PrefilterScreenSpaceIrradiance: 1
m_PrefilterNativeRenderPass: 1
m_PrefilterUseLegacyLightmaps: 0
m_PrefilterBicubicLightmapSampling: 0
m_PrefilterBicubicLightmapSampling: 1
m_PrefilterReflectionProbeRotation: 0
m_PrefilterReflectionProbeBlending: 0
m_PrefilterReflectionProbeBoxProjection: 0
m_PrefilterReflectionProbeAtlas: 0
m_PrefilterPointSamplingUpsampling: 0
m_PrefilterReflectionProbeBlending: 1
m_PrefilterReflectionProbeBoxProjection: 1
m_PrefilterReflectionProbeAtlas: 1
m_PrefilterPointSamplingUpsampling: 1
m_ShaderVariantLogLevel: 0
m_ShadowCascades: 0
m_Textures:

View File

@ -68,7 +68,20 @@ MonoBehaviour:
- rid: 5129510139066056976
- rid: 5129510139066056977
m_RuntimeSettings:
m_List: []
m_List:
- rid: 5129510139066056947
- rid: 5129510139066056952
- rid: 5129510139066056953
- rid: 5129510139066056954
- rid: 5129510139066056957
- rid: 5129510139066056959
- rid: 5129510139066056962
- rid: 5129510139066056963
- rid: 5129510139066056965
- rid: 5129510139066056967
- rid: 5129510139066056969
- rid: 5129510139066056970
- rid: 5129510139066056973
m_AssetVersion: 10
m_ObsoleteDefaultVolumeProfile: {fileID: 0}
m_RenderingLayerNames:

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,10 @@ namespace CharacterSystem
public class PrizeModule : MonoBehaviour
{
private List<Prize> _prizes;
public bool HasPrize { get; private set; }
public bool HasConfiguredPrize
=> _prizes != null && _prizes.Exists(prize => prize is not EmptyPrize);
public void SetPrizes(PrizeSet prizesSet)
{
@ -52,4 +55,4 @@ namespace CharacterSystem
return false;
}
}
}
}

View File

@ -2,6 +2,7 @@
using PrizeSystem;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum GameMode { pve, pvp }
public class GameManager : MonoBehaviour
@ -69,9 +70,11 @@ public class GameManager : MonoBehaviour
public TakeOrRiskPanel takeOrRiskPanel;
private const int PvpWinningScore = 5;
private const int VictoryNameLength = 10;
private bool _isLeavingGameplay;
private int _paidEntryAmount;
private bool _canRefundEntry;
private bool _returnToMenuAfterPrize;
public void Initialize()
{
@ -87,6 +90,9 @@ public class GameManager : MonoBehaviour
StatsService = new();
StatsService.Initialize();
timerManager.TimerEnded -= turnManager.HandleTimerEnded;
timerManager.TimerEnded += turnManager.HandleTimerEnded;
StartCoroutine(AddBotRecordEvery2Days());// добавлять каждые 2 дня запись
//stateMachine.ChangeState(new MenuState());
}
@ -142,6 +148,17 @@ public class GameManager : MonoBehaviour
vsBotThinkTime += Time.deltaTime;
}
private void OnDestroy()
{
if (timerManager != null && turnManager != null)
timerManager.TimerEnded -= turnManager.HandleTimerEnded;
if (nameInput != null)
nameInput.OnComplete -= HandleVictoryNameEntered;
UnsubscribeFromPrizePanels();
}
public void GotoMenu()
{
if (_isLeavingGameplay)
@ -149,7 +166,9 @@ public class GameManager : MonoBehaviour
RefundEntryIfAvailable();
_isLeavingGameplay = true;
CancelPrizeFlow();
ControlsPerformer.Instance.SetControlable(null);
timerManager.HideBars();
VideoUtility.CrossFadeToMenu();
turnManager.CancelPlayerPreparation();
_fieldCleaner.Clear();
@ -209,6 +228,50 @@ public class GameManager : MonoBehaviour
StartPveRound(nextCharacterIndex);
}
public bool TryStartPvePrizeFlow()
{
if (gameMode != GameMode.pve
|| !SettingsManager.GetBool(SettingKey.PRIZING)
|| !CharactersController.HasActiveCharacter)
return false;
if (!CharactersController.CurrentCharacter.prizeModule.TryFetchPrize(out List<Prize> configuredPrizes))
return false;
List<Prize> availablePrizes = GetAvailablePrizes(configuredPrizes);
if (availablePrizes.Count == 0)
return false;
bool isLastBot = CharactersController.CharacterIndex >= CharactersController.CharactersAmount - 1;
bool takeOrRiskEnabled = SettingsManager.GetBool(SettingKey.TAKE_OR_RISK);
bool continueAvailable = takeOrRiskEnabled && !isLastBot;
_returnToMenuAfterPrize = continueAvailable;
if (availablePrizes.Count > 1 || continueAvailable)
{
SubscribeToPrizePanels();
takeOrRiskPanel.Open(availablePrizes, continueAvailable, GetNextBotPrize());
}
else
{
StartPrizeIssue(availablePrizes[0]);
}
return true;
}
public void ShowFinalVictoryNameInput()
{
ControlsPerformer.Instance.SetControlable(null);
nameInput.OnComplete -= HandleVictoryNameEntered;
nameInput.OnComplete += HandleVictoryNameEntered;
nameInput.SetDestroyOnComplete(false);
nameInput.gameObject.SetActive(true);
nameInput.Initialize(VictoryNameLength, Alphabets.RUS_AND_DIGITS);
ControlsPerformer.Instance.SetControlable(nameInput);
}
public void ContinueAfterPveLoss()
{
if (CharactersController.HasActiveCharacter)
@ -239,6 +302,8 @@ public class GameManager : MonoBehaviour
player1score += 1;
else
player2score += 1;
timerManager.RefreshPvpScores();
}
public bool HasPvpMatchWinner(Team player)
@ -341,6 +406,134 @@ public class GameManager : MonoBehaviour
OnRoundStarted?.Invoke();
}
private static List<Prize> GetAvailablePrizes(IEnumerable<Prize> prizes)
{
List<Prize> result = new();
if (prizes == null)
return result;
foreach (Prize prize in prizes)
{
if (prize != null && prize is not EmptyPrize)
result.Add(prize);
}
return result;
}
private Prize GetNextBotPrize()
{
int nextIndex = CharactersController.CharacterIndex + 1;
if (nextIndex >= CharactersController.CharactersAmount)
return null;
List<Prize> nextPrizes = GetAvailablePrizes(
CharactersController.GetCharactersArray()[nextIndex].prizeModule.CheckPrizes());
return nextPrizes.Count > 0 ? nextPrizes[0] : null;
}
private void SubscribeToPrizePanels()
{
UnsubscribeFromPrizePanels();
takeOrRiskPanel.PrizeSelected += StartPrizeIssue;
takeOrRiskPanel.ContinueSelected += HandlePrizeSkipped;
qrPanel.OnComplete += HandlePrizeIssueFinished;
vitrinaPanel.OnComplete += HandleVitrinaIssueFinished;
}
private void UnsubscribeFromPrizePanels()
{
if (takeOrRiskPanel != null)
{
takeOrRiskPanel.PrizeSelected -= StartPrizeIssue;
takeOrRiskPanel.ContinueSelected -= HandlePrizeSkipped;
}
if (qrPanel != null)
qrPanel.OnComplete -= HandlePrizeIssueFinished;
if (vitrinaPanel != null)
vitrinaPanel.OnComplete -= HandleVitrinaIssueFinished;
PrizeSystemManager.PrizeIssueFinished -= HandleCountablePrizeIssueFinished;
}
private void StartPrizeIssue(Prize prize)
{
SubscribeToPrizePanels();
if (prize is CountablePrize)
{
PrizeSystemManager.PrizeIssueFinished -= HandleCountablePrizeIssueFinished;
PrizeSystemManager.PrizeIssueFinished += HandleCountablePrizeIssueFinished;
PrizeSystemManager.GivePrize(prize);
}
else if (prize is QrPrize)
{
qrPanel.gameObject.SetActive(true);
qrPanel.Initialize(prize);
}
else if (prize is VitrinaPrize vitrinaPrize)
{
vitrinaPanel.gameObject.SetActive(true);
vitrinaPanel.Initialize(vitrinaPrize.Boxes);
}
else
{
HandlePrizeIssueFinished();
}
}
private void HandlePrizeSkipped()
{
_returnToMenuAfterPrize = false;
UnsubscribeFromPrizePanels();
ContinueAfterPveWin();
}
private void HandleCountablePrizeIssueFinished(bool success)
{
PrizeSystemManager.PrizeIssueFinished -= HandleCountablePrizeIssueFinished;
HandlePrizeIssueFinished();
}
private void HandleVitrinaIssueFinished(bool success)
=> HandlePrizeIssueFinished();
private void HandlePrizeIssueFinished()
{
UnsubscribeFromPrizePanels();
if (_returnToMenuAfterPrize)
{
GotoMenu();
return;
}
bool isLastBot = CharactersController.CharacterIndex >= CharactersController.CharactersAmount - 1;
if (isLastBot)
ShowFinalVictoryNameInput();
else
ContinueAfterPveWin();
}
private void CancelPrizeFlow()
{
UnsubscribeFromPrizePanels();
if (takeOrRiskPanel != null)
takeOrRiskPanel.gameObject.SetActive(false);
if (qrPanel != null)
qrPanel.gameObject.SetActive(false);
if (vitrinaPanel != null)
vitrinaPanel.gameObject.SetActive(false);
}
private void RefundEntryIfAvailable()
{
if (!_canRefundEntry)
@ -355,6 +548,16 @@ public class GameManager : MonoBehaviour
{
player1score = 0;
player2score = 0;
timerManager.RefreshPvpScores();
}
private void HandleVictoryNameEntered(string playerName)
{
nameInput.OnComplete -= HandleVictoryNameEntered;
ControlsPerformer.Instance.SetControlable(null);
StatsService.AddRecord(playerName, TurnTracker.Timer, TurnTracker.Moves);
nameInput.gameObject.SetActive(false);
GotoMenu();
}

View File

@ -5,6 +5,7 @@
public const string DISPLAY_PRIZING_ERROR = "DisplayPrizingError";
public const string PRIZING = "Prizing";
public const string PRIZE_STOCK_CONTROL = "PrizeStockControl";
public const string DIFFICULTY_MODE = "DifficultyMode";
@ -34,4 +35,4 @@
public const string CASH_TO_RESET_DIFFICULTY = "CashToResetDifficulty";
}
}

View File

@ -117,12 +117,22 @@ public class Board : MonoBehaviour
private void PerformChipsClear()
{
SoundManager.instance.PlaySfx(_clearSound);
GameManager.Instance.currentChip?.RemoveFromField();
if (Grid == null) return;
if (Grid == null)
return;
bool fieldHadChips = false;
foreach (Chip chip in Grid)
chip?.RemoveFromField();
{
if (chip == null)
continue;
fieldHadChips = true;
chip.RemoveFromField();
}
if (fieldHadChips)
SoundManager.instance.PlaySfx(_clearSound);
}
//todo: реализовать

View File

@ -63,6 +63,23 @@ public class Bot : BasePlayer, IGamePlayer
StopThinking();
}
public void CancelTurn()
{
StopThinking();
if (currentChip != null)
{
if (state == PlayerState.Dropping)
currentChip.transform.position = dropTargetPosition;
else
ChipPool.Instance.Release(currentChip);
}
currentChip = null;
availableColumns = null;
SetState(PlayerState.Sleep);
}
public void UpdateControls()
{
switch (state)

View File

@ -35,6 +35,13 @@ public class EndGameHandler : MonoBehaviour
WinHandler(lastPlayer, gameMode, lastResult.WinningIndexes);
}
public void HandlePveTimeout()
{
MachineCountersService.RecordBotResult(false);
EventBus.TriggerEvent(GameEvent.BotWin);
_lossPanel.Open(GameManager.Instance.ContinueAfterPveLoss);
}
private void DrawHandler(GameMode gameMode)
{
if (gameMode == GameMode.pve)
@ -65,7 +72,17 @@ public class EndGameHandler : MonoBehaviour
{
MachineCountersService.RecordBotResult(true);
EventBus.TriggerEvent(GameEvent.PlayerWin);
_winPanel.Open();
if (GameManager.Instance.TryStartPvePrizeFlow())
return;
bool isLastBot = CharactersController.CharacterIndex
== CharactersController.CharactersAmount - 1;
if (isLastBot)
GameManager.Instance.ShowFinalVictoryNameInput();
else
_winPanel.Open();
}
else
{

View File

@ -0,0 +1,34 @@
using CharacterSystem;
using TMPro;
using UnityEngine;
public sealed class GameplayLevelBar : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI _title;
[SerializeField] private Timer _timer;
[SerializeField] private LevelBarCell[] _cells;
public Timer Timer => _timer;
public void ConfigurePve(int currentCharacterIndex, Character[] characters)
{
_title.text = "ВРЕМЯ ДО ПРОИГРЫША";
for (int index = 0; index < _cells.Length; index++)
{
bool hasCharacter = index < characters.Length;
bool filled = hasCharacter && index < currentCharacterIndex;
bool framed = hasCharacter && index == currentCharacterIndex;
bool hasGift = hasCharacter && characters[index].prizeModule.HasConfiguredPrize;
_cells[index].Configure(filled, framed, hasGift);
}
}
public void ConfigurePvp(int playerNumber, int score)
{
_title.text = $"ИГРОК {playerNumber}";
for (int index = 0; index < _cells.Length; index++)
_cells[index].Configure(index < score, false, false);
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 31e0148b68635f044adc45cff5009655

View File

@ -0,0 +1,15 @@
using UnityEngine;
public sealed class LevelBarCell : MonoBehaviour
{
[SerializeField] private GameObject _frame;
[SerializeField] private GameObject _gift;
[SerializeField] private GameObject _fill;
public void Configure(bool filled, bool framed, bool hasGift)
{
_fill.SetActive(filled);
_frame.SetActive(framed);
_gift.SetActive(hasGift);
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f46fc7826a8594f47ba0f15591288bec

View File

@ -64,8 +64,7 @@ public class PveLossPanel : MonoBehaviour, IControlable
public void Close()
{
int selectedButtonIndex = buttonIndex;
if (selectedButtonIndex == 0 && revengeButton is RevengeButton paidRestartButton &&
!paidRestartButton.TryPay(GameMode.pve))
if (selectedButtonIndex == 0 && !PaymentSystem.TryPayForRevenge(GameMode.pve))
return;
ControlsEnabled = false;

View File

@ -36,7 +36,7 @@ public class RevengeButton : PveLossButton
}
public bool TryPay(GameMode gameMode)
=> PaymentSystem.TryPay(PaymentSystem.GetRevengePrice(gameMode));
=> PaymentSystem.TryPayForRevenge(gameMode);
public override void Interact()
{

View File

@ -46,6 +46,21 @@ public class Player : BasePlayer, IGamePlayer
availableColumns = null;
}
public void CancelTurn()
{
if (currentChip != null)
{
if (state == PlayerState.Dropping)
currentChip.transform.position = dropTargetPosition;
else
ChipPool.Instance.Release(currentChip);
}
currentChip = null;
availableColumns = null;
SetState(PlayerState.Sleep);
}
public void ReturnPreparedChipToPool()
{
if (currentChip == null || state == PlayerState.Sleep || state == PlayerState.Dropping)
@ -57,6 +72,18 @@ public class Player : BasePlayer, IGamePlayer
SetState(PlayerState.Sleep);
}
public bool ForceDrop()
{
if (currentChip == null
|| availableColumns == null
|| state == PlayerState.Sleep
|| state == PlayerState.Dropping)
return false;
BeginDrop();
return true;
}
public void UpdateControls()
{
// Always update state machine regardless of ControlsEnabled
@ -157,7 +184,14 @@ public class Player : BasePlayer, IGamePlayer
private void TryDrop()
{
if (!IsActionPressed()) return;
if (!IsActionPressed())
return;
BeginDrop();
}
private void BeginDrop()
{
dropTargetPosition = board.GetTargetPosition(SelectedColumn);

View File

@ -2,7 +2,9 @@
public interface IGamePlayer : IControlable
{
event Action<int, int> OnChipDropped;
void PrepareChip();
void Sleep();
event Action<int, int> OnChipDropped;
}
void CancelTurn();
}

View File

@ -1,3 +1,4 @@
using System;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
@ -5,6 +6,8 @@ using UnityEngine.Serialization;
public class Timer : MonoBehaviour
{
public event Action TimerEnded;
public UnityEvent OnTimerEnded;
[FormerlySerializedAs("TimeLabel")] public TextMeshProUGUI timeLabel;
@ -14,37 +17,53 @@ public class Timer : MonoBehaviour
private bool _processing;
private bool _recentlyPlayed;
private float _lastSignalTime;
private int _lastDisplayedSecond;
public float RemainingTime => _restTime;
public bool IsRunning => _processing;
private void Update()
{
if (_processing == false)
return;
UiDraw();
_restTime -= Time.deltaTime;
if (_restTime <= 0f)
{
_restTime = 0f;
UiDraw();
StopTimer();
TimerEnded?.Invoke();
OnTimerEnded?.Invoke();
return;
}
int rest = (int)_restTime;
if (_recentlyPlayed == false && (rest % 60 == 0 || rest == 30 || rest < 11))
UiDraw();
int rest = Mathf.CeilToInt(_restTime);
bool warningTime = rest > 0 && (rest % 60 == 0 || rest == 30 || rest < 11);
if (rest != _lastDisplayedSecond && _recentlyPlayed == false && warningTime)
{
SoundManager.instance.PlaySfx(_warningSound);
if (_warningSound != null)
SoundManager.instance.PlaySfx(_warningSound);
_recentlyPlayed = true;
_lastSignalTime = Time.time;
}
_lastDisplayedSecond = rest;
if (_recentlyPlayed && Time.time - _lastSignalTime >= 1f)
_recentlyPlayed = false;
}
public void SetupTimer(float seconds)
{
_restTime = seconds;
_processing = false;
_restTime = Mathf.Max(seconds, 0f);
_recentlyPlayed = false;
_lastDisplayedSecond = Mathf.CeilToInt(_restTime);
UiDraw();
}
@ -54,17 +73,14 @@ public class Timer : MonoBehaviour
public void StopTimer()
=> _processing = false;
public void StopAndReset()
=> SetupTimer(0f);
private void UiDraw()
{
string minutes = ((int)(_restTime / 60)).ToString();
string seconds = ((int)(_restTime % 60)).ToString();
if (minutes.Length < 2)
minutes = "0" + minutes;
if (seconds.Length < 2)
seconds = "0" + seconds;
timeLabel.text = minutes + ":" + seconds;
int totalSeconds = Mathf.CeilToInt(_restTime);
int minutes = totalSeconds / 60;
int seconds = totalSeconds % 60;
timeLabel.text = $"{minutes:00}:{seconds:00}";
}
}

View File

@ -1,47 +1,102 @@
using CharacterSystem;
using System;
using UnityEngine;
using UnityEngine.Serialization;
public class TimerManager : MonoBehaviour
{
[SerializeField, FormerlySerializedAs("player1Timer")] private Timer _player1Timer;
[SerializeField, FormerlySerializedAs("player2Timer")] private Timer _player2Timer;
[SerializeField, FormerlySerializedAs("vsBotTime")] private float _vsBotTime = 180f;
[SerializeField, FormerlySerializedAs("pvpTime")] private float _pvpTime = 90f;
public event Action<Team> TimerEnded;
[SerializeField] private GameplayLevelBar _player1LevelBar;
[SerializeField] private GameplayLevelBar _player2LevelBar;
[SerializeField] private float _pveCharacterTime = 120f;
[SerializeField] private float _pvpTurnTime = 30f;
private GameMode _gameMode;
private Timer Player1Timer => _player1LevelBar.Timer;
private Timer Player2Timer => _player2LevelBar.Timer;
private void Awake()
{
Player1Timer.TimerEnded -= HandlePlayer1TimerEnded;
Player1Timer.TimerEnded += HandlePlayer1TimerEnded;
Player2Timer.TimerEnded -= HandlePlayer2TimerEnded;
Player2Timer.TimerEnded += HandlePlayer2TimerEnded;
}
private void OnDestroy()
{
Player1Timer.TimerEnded -= HandlePlayer1TimerEnded;
Player2Timer.TimerEnded -= HandlePlayer2TimerEnded;
}
public void SetupTimers(GameMode gameMode)
{
if (_player1Timer != null)
_player1Timer.SetupTimer(gameMode == GameMode.pve ? _vsBotTime : _pvpTime);
_gameMode = gameMode;
bool isPvp = gameMode == GameMode.pvp;
_player1LevelBar.gameObject.SetActive(true);
_player2LevelBar.gameObject.SetActive(isPvp);
if (gameMode == GameMode.pvp && _player2Timer != null)
_player2Timer.SetupTimer(_pvpTime);
if (isPvp)
{
RefreshPvpScores();
Player1Timer.StopAndReset();
Player2Timer.StopAndReset();
return;
}
SetTimersActivity(gameMode);
}
public void SetTimersActivity(GameMode gameMode)
{
bool activateSecondTimer = gameMode == GameMode.pvp;
if (_player1Timer != null)
_player1Timer.gameObject.SetActive(true);
if (_player2Timer != null)
_player2Timer.gameObject.SetActive(activateSecondTimer);
_player1LevelBar.ConfigurePve(
CharactersController.CharacterIndex,
CharactersController.GetCharactersArray());
Player1Timer.SetupTimer(_pveCharacterTime);
Player2Timer.StopAndReset();
}
public void StopAllTimers()
{
_player1Timer?.StopTimer();
_player2Timer?.StopTimer();
Player1Timer.StopAndReset();
Player2Timer.StopAndReset();
}
public void HideBars()
{
StopAllTimers();
_player1LevelBar.gameObject.SetActive(false);
_player2LevelBar.gameObject.SetActive(false);
}
public void StopTimer(Team player)
{
Timer timer = player == Team.Player1 ? Player1Timer : Player2Timer;
timer.StopTimer();
}
public void SetActiveTimer(Team currentPlayer)
{
Timer activeTimer = currentPlayer == Team.Player1 ? _player1Timer : _player2Timer;
Timer inactiveTimer = currentPlayer == Team.Player1 ? _player2Timer : _player1Timer;
Timer activeTimer = currentPlayer == Team.Player1 ? Player1Timer : Player2Timer;
Timer inactiveTimer = currentPlayer == Team.Player1 ? Player2Timer : Player1Timer;
activeTimer?.StartTimer();
inactiveTimer?.StopTimer();
if (_gameMode == GameMode.pvp)
{
inactiveTimer.StopAndReset();
activeTimer.SetupTimer(_pvpTurnTime);
}
activeTimer.StartTimer();
}
public void RefreshPvpScores()
{
if (GameManager.Instance == null)
return;
_player1LevelBar.ConfigurePvp(1, GameManager.Instance.player1score);
_player2LevelBar.ConfigurePvp(2, GameManager.Instance.player2score);
}
private void HandlePlayer1TimerEnded()
=> TimerEnded?.Invoke(Team.Player1);
private void HandlePlayer2TimerEnded()
=> TimerEnded?.Invoke(Team.Player2);
}

View File

@ -27,11 +27,30 @@ public class TurnManager : MonoBehaviour
public void CancelPlayerPreparation()
{
_player1.OnMoveCommitted -= OnPlayerMoveCommitted;
_player2.OnMoveCommitted -= OnPlayerTwoMoveCommitted;
_player1.OnThinkingStarted -= OnPlayerThinkingStarted;
_player2.OnThinkingStarted -= OnPlayerTwoThinkingStarted;
_player1.OnChipDropped -= OnTurnCompleted;
_player1.ReturnPreparedChipToPool();
}
public void HandleTimerEnded(Team player)
{
if (_isGameOver)
return;
if (_gameMode == GameMode.pve)
{
if (player == Team.Player1)
HandlePveTimeout();
return;
}
if (_currentPlayer is Player currentPlayer && currentPlayer.team == player)
currentPlayer.ForceDrop();
}
private void SetupPlayers()
=> _currentPlayer = _player1;
@ -40,9 +59,7 @@ public class TurnManager : MonoBehaviour
if (_isGameOver)
return;
bool isPlayerOneTurn = player == _player1;
if (isPlayerOneTurn)
if (player == _player1)
{
_player1.OnMoveCommitted -= OnPlayerMoveCommitted;
_player1.OnMoveCommitted += OnPlayerMoveCommitted;
@ -50,6 +67,14 @@ public class TurnManager : MonoBehaviour
_player1.OnThinkingStarted -= OnPlayerThinkingStarted;
_player1.OnThinkingStarted += OnPlayerThinkingStarted;
}
else if (player == _player2)
{
_player2.OnMoveCommitted -= OnPlayerTwoMoveCommitted;
_player2.OnMoveCommitted += OnPlayerTwoMoveCommitted;
_player2.OnThinkingStarted -= OnPlayerTwoThinkingStarted;
_player2.OnThinkingStarted += OnPlayerTwoThinkingStarted;
}
if (_gameMode == GameMode.pve)
{
@ -61,6 +86,9 @@ public class TurnManager : MonoBehaviour
player.PrepareChip();
ControlsPerformer.Instance.SetControlable(player);
_currentPlayer = player;
if (_gameMode == GameMode.pvp && Board.Instance.MovesCount > 0)
GameManager.Instance.timerManager.SetActiveTimer(((BasePlayer)player).team);
}
private void OnTurnCompleted(int column, int row)
@ -111,6 +139,7 @@ public class TurnManager : MonoBehaviour
_isGameOver = true;
_currentPlayer.Sleep();
ControlsPerformer.Instance.SetControlable(null);
GameManager.Instance.timerManager.StopAllTimers();
Team lastPlayerTeam = ((BasePlayer)_currentPlayer).team;
CheckResult result = FieldChecker.lastResult;
@ -120,6 +149,17 @@ public class TurnManager : MonoBehaviour
private void OnPlayerMoveCommitted()
{
_player1.OnMoveCommitted -= OnPlayerMoveCommitted;
HandlePlayerMoveCommitted(_player1);
}
private void OnPlayerTwoMoveCommitted()
{
_player2.OnMoveCommitted -= OnPlayerTwoMoveCommitted;
HandlePlayerMoveCommitted(_player2);
}
private void HandlePlayerMoveCommitted(Player player)
{
MachineCountersService.RecordFirstMove();
GameManager.Instance.DisableEarlyMenuExit();
@ -128,6 +168,10 @@ public class TurnManager : MonoBehaviour
GameManager.Instance.turnTracker.EnableTimer(false);
GameManager.Instance.turnTracker.IncrementMoves();
}
else
{
GameManager.Instance.timerManager.StopTimer(player.team);
}
}
private void OnPlayerThinkingStarted()
@ -138,4 +182,18 @@ public class TurnManager : MonoBehaviour
if (_gameMode == GameMode.pve)
GameManager.Instance.turnTracker.EnableTimer(true);
}
private void OnPlayerTwoThinkingStarted()
=> _player2.OnThinkingStarted -= OnPlayerTwoThinkingStarted;
private void HandlePveTimeout()
{
_isGameOver = true;
_currentPlayer.OnChipDropped -= OnTurnCompleted;
_currentPlayer.CancelTurn();
ControlsPerformer.Instance.SetControlable(null);
GameManager.Instance.turnTracker.EnableTimer(false);
GameManager.Instance.timerManager.StopAllTimers();
GameManager.Instance.endGameHandler.HandlePveTimeout();
}
}

View File

@ -52,6 +52,9 @@ public static class PaymentSystem
return TryPay(GetModePrice(mode));
}
public static bool TryPayForRevenge(GameMode mode)
=> TryPay(GetRevengePrice(mode));
public static void AddCredit()
{
ChangeBalance(ValutaPerImpulse);

View File

@ -0,0 +1,56 @@
using System;
using UnityEngine;
namespace PrizeSystem
{
public static class PrizeStockService
{
public static event Action StateChanged;
public static int Count => Mathf.Max(PlayerPrefs.GetInt(PrefsKeyVault.PRIZES_AVAILABLE, 0), 0);
public static bool Enabled => SettingsManager.Initialized
&& SettingsManager.GetBool(SettingKey.PRIZE_STOCK_CONTROL);
public static void Initialize()
{
PlayerPrefs.SetInt(PrefsKeyVault.PRIZES_AVAILABLE, Count);
SettingsManager.OnSettingsChanged -= NotifyStateChanged;
SettingsManager.OnSettingsChanged += NotifyStateChanged;
StateChanged?.Invoke();
}
public static void Add(int amount)
{
if (amount <= 0)
return;
SetCount(Count + amount);
}
public static void SetCount(int count)
{
int normalizedCount = Mathf.Clamp(count, 0, 999999);
if (normalizedCount == Count)
return;
PlayerPrefs.SetInt(PrefsKeyVault.PRIZES_AVAILABLE, normalizedCount);
PlayerPrefs.Save();
StateChanged?.Invoke();
}
public static void SetEnabled(bool enabled)
=> SettingsManager.SetBool(SettingKey.PRIZE_STOCK_CONTROL, enabled);
public static void RecordSuccessfulCountableIssue()
{
if (Enabled == false)
return;
SetCount(Count - 1);
}
private static void NotifyStateChanged()
=> StateChanged?.Invoke();
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d590d25b0cf067c48b79825603f8bcbd

View File

@ -12,6 +12,7 @@ namespace PrizeSystem
public static event Action<PrizeContainer> OnPrizesChanged;
public static event Action<bool> OnErrorStateChanged;
public static event Action OnPrizeIssued;
public static event Action<bool> PrizeIssueFinished;
[Header("Звук")]
[SerializeField] private AudioClip prizeErrorSound;
@ -50,6 +51,7 @@ namespace PrizeSystem
public void Initialize()
{
I = this;
PrizeStockService.Initialize();
PrizeSerializer.Initialize(_defaultPrizes);
PrizeContainer = PrizeSerializer.LoadFromFile();
@ -69,16 +71,23 @@ namespace PrizeSystem
public static void GivePrize(Prize prize)
{
bool prizingEnabled = SettingsManager.GetBool(SettingKey.PRIZING);
if (prizingEnabled == false) return;
if (prizingEnabled == false)
{
PrizeIssueFinished?.Invoke(false);
return;
}
if (prize == null)
{
Debug.Log("Приз Null"); return;
Debug.Log("Приз Null");
PrizeIssueFinished?.Invoke(false);
return;
}
if (prize is EmptyPrize)
{
Debug.Log("Пустой приз. Возврат.");
PrizeIssueFinished?.Invoke(false);
return;
}
else if (prize is CountablePrize countablePrize)
@ -93,6 +102,10 @@ namespace PrizeSystem
{
qrPrizes.Enqueue(qrPrize);
}
else
{
PrizeIssueFinished?.Invoke(false);
}
}
private IEnumerator GiveCountablePrizeCoroutine(int amount, CountablePrizeType Type)
@ -100,6 +113,7 @@ namespace PrizeSystem
if (prizingInProcess)
{
Debug.LogError("Уже идет выдача приза. Повторная выдача отключена");
PrizeIssueFinished?.Invoke(false);
yield break;
}
prizingInProcess = true;
@ -109,9 +123,10 @@ namespace PrizeSystem
for (int i = 0; i < amount; i++)
{
bool noPrizWhenError = SettingsManager.GetBool(SettingKey.PRIZING_WHEN_ERROR);
bool prizeWhenError = SettingsManager.GetBool(SettingKey.PRIZING_WHEN_ERROR);
bool errorRegistred = PlayerPrefs.GetInt(PrefsKeyVault.PRIZING_ERROR_REGISTRED, 0) == 1;
if (errorRegistred && noPrizWhenError) yield break;
if (errorRegistred && !prizeWhenError)
break;
PlayerPrefs.SetInt(PrefsKeyVault.PRIZE_GIVEN_BUFFER, 0);
ComWrapper.Send(StartHopperCommand);
@ -133,10 +148,7 @@ namespace PrizeSystem
if (success)
{
//контроль остатков
int prizesCount = PlayerPrefs.GetInt(PrefsKeyVault.PRIZES_AVAILABLE);
prizesCount = Mathf.Clamp(prizesCount - 1, 0, 9999);
PlayerPrefs.SetInt(PrefsKeyVault.PRIZES_AVAILABLE, prizesCount);
PrizeStockService.RecordSuccessfulCountableIssue();
//Счетчики
IncreasePrizeCounters(Type);
@ -158,6 +170,7 @@ namespace PrizeSystem
}
prizingInProcess = false;
PrizeIssueFinished?.Invoke(atLeastOnePrize);
}
public static void SetPrizingError(bool error)

View File

@ -1,83 +1,109 @@
using PrizeSystem;
using PrizeSystem;
using System;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class QrPanel : MonoBehaviour
public class QrPanel : MonoBehaviour, IControlable
{
private bool initialized = false;
public event Action OnComplete;
[SerializeField] private Image QrImage;
[SerializeField] private TextMeshProUGUI Label;
[SerializeField] private float _timeout = 300f;
[SerializeField] private float timer = 0;
private float timeToAct = 3;
private Sprite _generatedSprite;
private Texture2D _generatedTexture;
private float _closeAt;
private bool _initialized;
private bool _waitForActionRelease;
private void OnEnable()
public bool ControlsEnabled { get; set; }
private void Update()
{
initialized = false;
if (_initialized && Time.unscaledTime >= _closeAt)
Close();
}
private void OnDisable()
{
initialized = false;
ControlsEnabled = false;
_initialized = false;
Clear();
}
public void Initialize(Prize prize)
{
if (initialized) return;
if (_initialized)
return;
if (prize is QrPrize qrPrize)
{
var tex = QrMaker.MakePrizeTexture(qrPrize);
tex.filterMode = FilterMode.Point;
var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));
QrImage.sprite = sprite;
Label.text = qrPrize.PrizeText;
PrizeSystemManager.ReportPrizeIssued();
}
else
if (prize is not QrPrize qrPrize)
{
OnComplete?.Invoke();
return;
}
initialized = true;
Clear();
_generatedTexture = QrMaker.MakePrizeTexture(qrPrize);
_generatedTexture.filterMode = FilterMode.Point;
_generatedSprite = Sprite.Create(
_generatedTexture,
new Rect(0, 0, _generatedTexture.width, _generatedTexture.height),
new Vector2(0.5f, 0.5f));
QrImage.sprite = _generatedSprite;
QrImage.preserveAspect = true;
Label.text = qrPrize.PrizeText;
PrizeSystemManager.ReportPrizeIssued();
_closeAt = Time.unscaledTime + _timeout;
_waitForActionRelease = true;
_initialized = true;
ControlsEnabled = true;
ControlsPerformer.Instance.SetControlable(this);
}
public void UpdateControls()
{
if (!_initialized)
return;
if (_waitForActionRelease)
{
if (In.GetAction(JoystickSide.Both, InteractMode.Continious))
return;
_waitForActionRelease = false;
}
if (In.GetAction(JoystickSide.Both, InteractMode.Discrete))
Close();
}
public void Close()
{
if (!_initialized)
return;
_initialized = false;
ControlsEnabled = false;
ControlsPerformer.Instance.SetControlable(null);
gameObject.SetActive(false);
OnComplete?.Invoke();
}
private void Clear()
{
if (QrImage != null)
QrImage.sprite = null;
if (_generatedSprite != null)
Destroy(_generatedSprite);
if (_generatedTexture != null)
Destroy(_generatedTexture);
_generatedSprite = null;
_generatedTexture = null;
}
private void Update()
{
if (!initialized) return;
var input = CollectInputs();
if (input)
{
timer = Mathf.Clamp(timer + Time.deltaTime, 0, timeToAct);
}
else
{
timer = 0;
}
if (timer >= timeToAct)
{
OnComplete?.Invoke();
gameObject.SetActive(false);
}
}
bool CollectInputs()
{
bool action = In.GetAction(JoystickSide.Both, InteractMode.Continious);
return action;
}
}

View File

@ -16,6 +16,14 @@ public class PrizeView : MonoBehaviour
public void SetPrize(Prize prize)
{
if (prize == null || prize is EmptyPrize)
{
_image.sprite = null;
_label.text = GetPrizeText(prize);
Prize = prize;
return;
}
_image.sprite = PrizeSystemManager.GetPrizeIcon(prize.PrizeType);
_label.text = GetText(prize);
_label.rectTransform.sizeDelta = new Vector2(_label.preferredWidth, _label.rectTransform.sizeDelta.y);
@ -26,6 +34,7 @@ public class PrizeView : MonoBehaviour
{
_image.sprite = null;
_label.text = "";
Prize = null;
}
private string GetText(Prize prize) => GetPrizeText(prize);
@ -52,7 +61,7 @@ public class PrizeView : MonoBehaviour
}
else if (prize is QrPrize qp)
{
return $"Qr: {qp.PrizeText}</b>";
return $"QR: {qp.PrizeText}";
}
return "";
}

View File

@ -1,11 +1,16 @@
using System;
using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace PrizeSystem
{
public class TakeOrRiskPanel : MonoBehaviour
public class TakeOrRiskPanel : MonoBehaviour, IControlable
{
public event Action<Prize> PrizeSelected;
public event Action ContinueSelected;
[Header("Ссылки на панели")]
[SerializeField] private Image CurrentPrizeFrame;
[SerializeField] private Image NextPrizeFrame;
@ -14,69 +19,146 @@ namespace PrizeSystem
[Header("Цветовая анимация")]
[SerializeField] private Color DisabledColor;
[SerializeField] float HsvSpeed = 0.3f;
[SerializeField] private float HsvSpeed = 0.3f;
[SerializeField] private AudioClip StartAcceptingClip;
private TakeOrRiskOption SelectedOption;
public event Action<TakeOrRiskOption> OnComplete;
private readonly List<Prize> _prizes = new();
private int _prizeIndex;
private bool _continueAvailable;
private bool _continueSelected;
private bool _waitForActionRelease;
private TextMeshProUGUI _title;
private Image activeFrame;
private Image inactiveFrame;
public bool ControlsEnabled { get; set; }
private void OnEnable()
private void Awake()
{
SelectedOption = TakeOrRiskOption.Take;
Transform titleTransform = transform.Find("Title");
activeFrame = CurrentPrizeFrame;
inactiveFrame = NextPrizeFrame;
if (titleTransform != null)
_title = titleTransform.GetComponent<TextMeshProUGUI>();
}
private void Update()
{
if (!ControlsEnabled)
return;
DisplayGraphics();
}
private void OnDisable()
{
ControlsEnabled = false;
ClearPrizes();
}
void Update()
public void Open(IReadOnlyList<Prize> prizes, bool continueAvailable, Prize nextPrize)
{
if(In.GetButtonDown(InSignal.startOnePlayer))
_prizes.Clear();
if (prizes != null)
{
SelectedOption = TakeOrRiskOption.Take;
Accept();
foreach (Prize prize in prizes)
{
if (prize != null && prize is not EmptyPrize)
_prizes.Add(prize);
}
}
if (In.GetButtonDown(InSignal.startTwoPlayers))
if (_prizes.Count == 0)
return;
_prizeIndex = 0;
_continueAvailable = continueAvailable;
_continueSelected = false;
_waitForActionRelease = true;
if (_title != null)
{
SelectedOption = TakeOrRiskOption.Risk;
Accept();
_title.text = _continueAvailable
? "ЗАБРАТЬ ПРИЗ ИЛИ ИГРАТЬ ДАЛЬШЕ?"
: _prizes.Count > 1
? "ВЫБЕРИТЕ ПРИЗ"
: "ЗАБРАТЬ ПРИЗ";
}
NextPrizeFrame.gameObject.SetActive(_continueAvailable);
CurrentPrizeView.SetPrize(_prizes[_prizeIndex]);
if (_continueAvailable && nextPrize != null)
NextPrizeView.SetPrize(nextPrize);
else
NextPrizeView.Clear();
gameObject.SetActive(true);
ControlsEnabled = true;
ControlsPerformer.Instance.SetControlable(this);
if (StartAcceptingClip != null)
SoundManager.instance.PlaySfx(StartAcceptingClip);
DisplayGraphics();
}
public void SetPrizes(Prize CurrentPrize, Prize NextPrize)
public void Close()
{
CurrentPrizeView.SetPrize(CurrentPrize);
NextPrizeView.SetPrize(NextPrize);
ControlsEnabled = false;
ControlsPerformer.Instance.SetControlable(null);
gameObject.SetActive(false);
}
public void UpdateControls()
{
if (_waitForActionRelease)
{
if (In.GetAction(JoystickSide.Both, InteractMode.Continious))
return;
_waitForActionRelease = false;
}
Vector2Int direction = In.GetAxis(JoystickSide.Both, InteractMode.Discrete);
if (_continueAvailable && direction.x != 0)
{
_continueSelected = direction.x > 0;
DisplayGraphics();
}
if (!_continueSelected && direction.y != 0 && _prizes.Count > 1)
{
_prizeIndex = (_prizeIndex - direction.y + _prizes.Count) % _prizes.Count;
CurrentPrizeView.SetPrize(_prizes[_prizeIndex]);
}
if (!In.GetAction(JoystickSide.Both, InteractMode.Discrete))
return;
bool continueSelected = _continueSelected;
Prize selectedPrize = _prizes[_prizeIndex];
Close();
if (continueSelected)
ContinueSelected?.Invoke();
else
PrizeSelected?.Invoke(selectedPrize);
}
private void ClearPrizes()
{
CurrentPrizeView.Clear();
NextPrizeView.Clear();
_prizes.Clear();
}
private void DisplayGraphics()
{
//Цвет рамки
float hue = (Time.time * HsvSpeed) % 1f;
Color animatedColor = Color.HSVToRGB(hue, 1, 1);
activeFrame.color = animatedColor;
inactiveFrame.color = animatedColor;
}
private void Accept()
{
OnComplete?.Invoke(SelectedOption);
gameObject.SetActive(false);
float hue = (Time.unscaledTime * HsvSpeed) % 1f;
Color animatedColor = Color.HSVToRGB(hue, 1f, 1f);
CurrentPrizeFrame.color = _continueSelected ? DisabledColor : animatedColor;
NextPrizeFrame.color = _continueSelected ? animatedColor : DisabledColor;
}
}
}
}

View File

@ -0,0 +1,35 @@
using UnityEngine;
namespace PrizeSystem
{
public sealed class PrizeStockEmptyLabel : MonoBehaviour
{
[SerializeField] private CanvasGroup _canvasGroup;
private void OnEnable()
{
PrizeStockService.StateChanged -= Refresh;
PrizeStockService.StateChanged += Refresh;
SettingsManager.OnSettingsChanged -= Refresh;
SettingsManager.OnSettingsChanged += Refresh;
Refresh();
}
private void OnDisable()
{
PrizeStockService.StateChanged -= Refresh;
SettingsManager.OnSettingsChanged -= Refresh;
}
private void Refresh()
{
bool visible = SettingsManager.Initialized
&& PrizeStockService.Enabled
&& PrizeStockService.Count == 0
&& SettingsManager.GetBool(SettingKey.DISPLAY_NOPRIZES);
_canvasGroup.alpha = visible ? 1f : 0f;
_canvasGroup.blocksRaycasts = false;
_canvasGroup.interactable = false;
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: bd2c707ec06de6d40b4beb1d35c177c2

View File

@ -1,157 +1,185 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
//Панель с результатами открытия витрины
public class VitrinaPanel : MonoBehaviour
public class VitrinaPanel : MonoBehaviour, IControlable
{
private bool initialized = false;
//запросить коробки, проверить какие требуется, найти пересечение. Если нет, то возврат.
public event Action OnComplete;
public event Action<bool> OnComplete;
[SerializeField] private Transform BoxesRoot;
[SerializeField] private GameObject BoxViewPrefab;
private List<VitrinaBox> generatedBoxes = new();
private int pointerIndex;
private readonly List<VitrinaBox> _generatedBoxes = new();
private int _pointerIndex;
private bool _initialized;
private bool _waitForActionRelease;
private bool _waitingForResult;
[SerializeField] private float timer = 0;
private float timeToAct = 3;
VitrinaBox CurrentBox => generatedBoxes[pointerIndex];
public bool ControlsEnabled { get; set; }
private VitrinaBox CurrentBox => _generatedBoxes[_pointerIndex];
private void OnEnable()
{
initialized = false;
}
private void OnDisable()
{
initialized = false;
ControlsEnabled = false;
_initialized = false;
_waitingForResult = false;
UnsubscribeFromResult();
ClearBoxes();
}
public void Initialize(int[] requiredBoxed)
public async void Initialize(int[] requiredBoxes)
{
if (initialized) return;
//тут проверка что есть такие коробки
if (_initialized)
return;
int[] readyBoxes = PrizeStandController.GetReadyBoxes();
_initialized = true;
ControlsEnabled = false;
ClearBoxes();
int[] res = FindIntersection(requiredBoxed, readyBoxes);
if (res.Length == 0)
if (VitrinaCatcher.Instance == null)
{
OnComplete?.Invoke();
VitrinaCatcher.Instance.DisplayOpenResult(false, "Нет призов");
Debug.LogError("VitrinaCatcher не инициализирован", gameObject);
_initialized = false;
gameObject.SetActive(false);
OnComplete?.Invoke(false);
return;
}
SubscribeToResult();
if (!PrizeStandController.Initialized)
{
_waitingForResult = true;
VitrinaCatcher.Instance.DisplayOpenResult(false, "Витрина не подключена");
return;
}
pointerIndex = 0;
GenerateBoxes(res);
int[] readyBoxes = await PrizeStandController.GetReadyBoxesAsync();
if (!_initialized)
return;
int[] availableBoxes = FindIntersection(requiredBoxes, readyBoxes);
if (availableBoxes.Length == 0)
{
_waitingForResult = true;
VitrinaCatcher.Instance.DisplayOpenResult(false, "Нет доступных призов в витрине");
return;
}
_pointerIndex = 0;
GenerateBoxes(availableBoxes);
CurrentBox.SetEnabled(true);
initialized = true;
_waitForActionRelease = true;
ControlsEnabled = true;
ControlsPerformer.Instance.SetControlable(this);
}
public void UpdateControls()
{
if (!_initialized || _waitingForResult)
return;
if (_waitForActionRelease)
{
if (In.GetAction(JoystickSide.Both, InteractMode.Continious))
return;
_waitForActionRelease = false;
}
int direction = In.GetAxis(JoystickSide.Both, InteractMode.Discrete).x;
if (direction != 0 && _generatedBoxes.Count > 1)
{
CurrentBox.SetEnabled(false);
CurrentBox.shake = false;
_pointerIndex = (_pointerIndex + direction + _generatedBoxes.Count) % _generatedBoxes.Count;
CurrentBox.SetEnabled(true);
}
if (!In.GetAction(JoystickSide.Both, InteractMode.Discrete))
return;
ControlsEnabled = false;
ControlsPerformer.Instance.SetControlable(null);
CurrentBox.shake = true;
_waitingForResult = true;
_ = PrizeStandController.OpenBoxAsync(CurrentBox.Number);
}
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();
HashSet<int> requiredBoxes = new(array1);
return array2.Where(requiredBoxes.Contains).Distinct().OrderBy(number => number).ToArray();
}
public void Close()
{
ControlsEnabled = false;
ControlsPerformer.Instance.SetControlable(null);
gameObject.SetActive(false);
}
private void GenerateBoxes(int[] indexes)
{
if (generatedBoxes == null)
generatedBoxes = new();
foreach (int index in indexes)
{
var go = Instantiate(BoxViewPrefab, BoxesRoot);
GameObject boxObject = Instantiate(BoxViewPrefab, BoxesRoot);
if (go.TryGetComponent<VitrinaBox>(out var Box))
if (boxObject.TryGetComponent(out VitrinaBox box))
{
generatedBoxes.Add(Box);
Box.SetNumber(index);
_generatedBoxes.Add(box);
box.SetNumber(index);
}
else
{
Destroy(go);
Debug.Log("Префаб не содержит компонента VitrinaBoxView", gameObject);
Destroy(boxObject);
Debug.LogError("Префаб не содержит компонента VitrinaBox", gameObject);
}
}
}
private void ClearBoxes()
{
if (generatedBoxes != null)
foreach (VitrinaBox box in _generatedBoxes)
{
foreach (var box in generatedBoxes)
if (box != null)
Destroy(box.gameObject);
generatedBoxes.Clear();
}
_generatedBoxes.Clear();
}
private void Update()
private void SubscribeToResult()
{
if (!initialized) return;
var input = CollectInputs();
if (input.direction != 0)
{
int count = generatedBoxes == null ? 0 : generatedBoxes.Count;
//отобразить изменения
CurrentBox.SetEnabled(false);
CurrentBox.shake = false;
timer = 0;
pointerIndex = (pointerIndex + input.direction + count) % count;
CurrentBox.SetEnabled(true);
}
if (input.action)
{
CurrentBox.shake = true;
timer = Mathf.Clamp(timer + Time.deltaTime, 0, timeToAct);
}
else
{
CurrentBox.shake = false;
timer = 0;
}
if (timer >= timeToAct)
{
_ = PrizeStandController.OpenBoxAsync(CurrentBox.Number);
timer = 0;
OnComplete?.Invoke();
gameObject.SetActive(false);
if (VitrinaCatcher.Instance == null)
return;
}
VitrinaCatcher.Instance.ResultClosed -= HandleResultClosed;
VitrinaCatcher.Instance.ResultClosed += HandleResultClosed;
}
(int direction, bool action) CollectInputs()
private void UnsubscribeFromResult()
{
int direction = In.GetAxis(JoystickSide.Both, InteractMode.Discrete).x;
bool action = In.GetAction(JoystickSide.Both, InteractMode.Continious);
return (direction, action);
if (VitrinaCatcher.Instance != null)
VitrinaCatcher.Instance.ResultClosed -= HandleResultClosed;
}
private void HandleResultClosed(bool success)
{
if (!_initialized || !_waitingForResult)
return;
UnsubscribeFromResult();
_initialized = false;
_waitingForResult = false;
gameObject.SetActive(false);
OnComplete?.Invoke(success);
}
}

View File

@ -16,8 +16,10 @@ public class VitrinaView : MonoBehaviour
{
if (instance == null)
{
instance = GameObject.FindAnyObjectByType<VitrinaView>();
instance.Deactivate();
instance = FindFirstObjectByType<VitrinaView>(FindObjectsInactive.Include);
if (instance != null)
instance.Deactivate();
}
return instance;
}

View File

@ -11,6 +11,7 @@ public class SettingFunctions : MonoBehaviour
[SerializeField] private InputTestPanel _inputTestPanelPrefab;
[SerializeField] private SettingsPasswordPanel _passwordPanelPrefab;
[SerializeField] private MachineCountersPanel _countersPanelPrefab;
[SerializeField] private PrizeStockPanel _prizeStockPanelPrefab;
[SerializeField] private QrTestPanel _qrTestPanelPrefab;
[SerializeField] private AudioClip _qrTestUnavailableClip;
[SerializeField] private AudioClip[] _soundTestReplicas;
@ -19,6 +20,7 @@ public class SettingFunctions : MonoBehaviour
private InputTestPanel _activeInputTestPanel;
private SettingsPasswordPanel _activePasswordPanel;
private MachineCountersPanel _activeCountersPanel;
private PrizeStockPanel _activePrizeStockPanel;
private QrTestPanel _activeQrTestPanel;
private void Awake()
@ -45,6 +47,9 @@ public class SettingFunctions : MonoBehaviour
if (_activeCountersPanel != null)
_activeCountersPanel.OnClosed -= CloseCountersPanel;
if (_activePrizeStockPanel != null)
_activePrizeStockPanel.OnClosed -= ClosePrizeStockPanel;
if (_activeQrTestPanel != null)
_activeQrTestPanel.OnClosed -= CloseQrTest;
}
@ -211,7 +216,14 @@ public class SettingFunctions : MonoBehaviour
public void OpenPrizesRest()
{
if (_activePrizeStockPanel != null)
return;
_activePrizeStockPanel = Instantiate(_prizeStockPanelPrefab, _canvas);
_activePrizeStockPanel.OnClosed += ClosePrizeStockPanel;
_settingsScreen.BlockControls();
ControlsPerformer.Instance.SetControlable(_activePrizeStockPanel);
_activePrizeStockPanel.Open();
}
private void ReturnToSettings()
@ -253,6 +265,14 @@ public class SettingFunctions : MonoBehaviour
ReturnToSettings();
}
private void ClosePrizeStockPanel()
{
_activePrizeStockPanel.OnClosed -= ClosePrizeStockPanel;
Destroy(_activePrizeStockPanel.gameObject);
_activePrizeStockPanel = null;
ReturnToSettings();
}
private void CloseQrTest()
{
_activeQrTestPanel.OnClosed -= CloseQrTest;

View File

@ -0,0 +1,290 @@
using System;
using DG.Tweening;
using PrizeSystem;
using TMPro;
using UnityEngine;
public sealed class PrizeStockPanel : MonoBehaviour, IControlable
{
public event Action OnClosed;
[SerializeField] private CanvasGroup _canvasGroup;
[SerializeField] private TextMeshProUGUI _count;
[SerializeField] private MToggle _controlToggle;
[SerializeField] private TextMeshProUGUI[] _options;
[SerializeField] private TextMeshProUGUI _description;
[SerializeField] private TextMeshProUGUI _hint;
[SerializeField] private float _fadeDuration = 0.25f;
[SerializeField] private Color _normalOptionColor = Color.white;
[SerializeField] private Color _selectedOptionColor = new Color(1f, 0.85f, 0.1f, 1f);
[Header("Ускорение числового ввода")]
[SerializeField] private float _accelerationTime = 0.8f;
[SerializeField] private int _maximumStepMultiplier = 10;
[SerializeField] private float _initialRepeatInterval = 0.5f;
[SerializeField] private float _minimumRepeatInterval = 0.03f;
[SerializeField] private AnimationCurve _accelerationCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
private const int ToggleOptionIndex = 0;
private const int AddOptionIndex = 1;
private const int SetOptionIndex = 2;
private const int BackOptionIndex = 3;
private const int DefaultAddAmount = 10;
private const int MaximumCount = 999999;
private Tween _fadeTween;
private int _selectedOptionIndex;
private int _addAmount;
private int _setAmount;
private bool _editingNumber;
private bool _actionReleased;
private bool _isClosing;
private bool _numberInputHeld;
private int _heldNumberDirection;
private int _heldNumberBaseStep;
private float _numberHoldTime;
private float _lastNumberChangeTime;
public bool ControlsEnabled { get; set; }
private void OnDisable()
{
PrizeStockService.StateChanged -= Refresh;
_fadeTween?.Kill();
_fadeTween = null;
ControlsEnabled = false;
ResetNumberInputRepeat();
}
public void Open()
{
_fadeTween?.Kill();
transform.SetAsLastSibling();
_canvasGroup.alpha = 0f;
_canvasGroup.blocksRaycasts = true;
_selectedOptionIndex = ToggleOptionIndex;
_addAmount = DefaultAddAmount;
_setAmount = PrizeStockService.Count;
_editingNumber = false;
_actionReleased = false;
_isClosing = false;
ResetNumberInputRepeat();
ControlsEnabled = true;
PrizeStockService.StateChanged -= Refresh;
PrizeStockService.StateChanged += Refresh;
_controlToggle.DisplayImmediate(PrizeStockService.Enabled);
Refresh();
_fadeTween = _canvasGroup.DOFade(1f, _fadeDuration).SetEase(Ease.OutCubic).SetUpdate(true);
_fadeTween.PlayForward();
}
public void UpdateControls()
{
if (_isClosing)
return;
Vector2Int axis = In.GetAxis(JoystickSide.Both, InteractMode.Discrete);
bool actionPressed = In.GetAction(JoystickSide.Both, InteractMode.Discrete);
bool actionHeld = In.GetAction(JoystickSide.Both, InteractMode.Continious);
if (actionHeld == false)
_actionReleased = true;
if (_editingNumber)
{
UpdateNumberEditing();
if (_actionReleased && actionPressed)
ApplyNumberChange();
return;
}
if (axis.y != 0)
{
int direction = axis.y > 0 ? -1 : 1;
_selectedOptionIndex = (_selectedOptionIndex + direction + _options.Length) % _options.Length;
RefreshSelection();
}
if (_selectedOptionIndex == ToggleOptionIndex && axis.x != 0)
SetControlEnabled(axis.x > 0);
if (_actionReleased && actionPressed)
ExecuteSelectedOption();
}
private void ExecuteSelectedOption()
{
_actionReleased = false;
if (_selectedOptionIndex == ToggleOptionIndex)
{
SetControlEnabled(PrizeStockService.Enabled == false);
return;
}
if (_selectedOptionIndex == AddOptionIndex || _selectedOptionIndex == SetOptionIndex)
{
_editingNumber = true;
ResetNumberInputRepeat();
RefreshSelection();
return;
}
Close();
}
private void UpdateNumberEditing()
{
Vector2Int axis = In.GetAxis(JoystickSide.Both, InteractMode.Continious);
UpdateNumberEditing(axis, Time.unscaledTime, Time.unscaledDeltaTime);
}
private void UpdateNumberEditing(Vector2Int axis, float currentTime, float deltaTime)
{
int direction = axis.y != 0 ? axis.y : axis.x;
int baseStep = axis.y != 0 ? 10 : 1;
if (direction == 0)
{
ResetNumberInputRepeat();
return;
}
if (_numberInputHeld == false
|| direction != _heldNumberDirection
|| baseStep != _heldNumberBaseStep)
{
_numberInputHeld = true;
_heldNumberDirection = direction;
_heldNumberBaseStep = baseStep;
_numberHoldTime = 0f;
_lastNumberChangeTime = currentTime;
ChangeEditedNumber(direction * baseStep);
return;
}
_numberHoldTime += deltaTime;
float progress = Mathf.Clamp01(_numberHoldTime / Mathf.Max(_accelerationTime, 0.01f));
float acceleration = _accelerationCurve.Evaluate(progress);
int maximumStepMultiplier = Mathf.Max(_maximumStepMultiplier, 1);
int stepMultiplier = 1 + Mathf.RoundToInt(acceleration * (maximumStepMultiplier - 1));
float initialRepeatInterval = Mathf.Max(_initialRepeatInterval, 0.01f);
float minimumRepeatInterval = Mathf.Clamp(_minimumRepeatInterval, 0.01f, initialRepeatInterval);
float repeatInterval = Mathf.Lerp(initialRepeatInterval, minimumRepeatInterval, acceleration);
if (currentTime - _lastNumberChangeTime < repeatInterval)
return;
_lastNumberChangeTime = currentTime;
ChangeEditedNumber(direction * baseStep * stepMultiplier);
}
private void ChangeEditedNumber(int delta)
{
if (delta == 0)
return;
if (_selectedOptionIndex == AddOptionIndex)
_addAmount = Mathf.Clamp(_addAmount + delta, 1, MaximumCount);
else
_setAmount = Mathf.Clamp(_setAmount + delta, 0, MaximumCount);
Refresh();
}
private void ApplyNumberChange()
{
_actionReleased = false;
ResetNumberInputRepeat();
if (_selectedOptionIndex == AddOptionIndex)
{
PrizeStockService.Add(_addAmount);
_setAmount = PrizeStockService.Count;
}
else
{
PrizeStockService.SetCount(_setAmount);
}
_editingNumber = false;
Refresh();
}
private void SetControlEnabled(bool enabled)
{
if (PrizeStockService.Enabled == enabled)
return;
PrizeStockService.SetEnabled(enabled);
_controlToggle.SetValue(enabled);
Refresh();
}
private void Refresh()
{
_count.text = PrizeStockService.Count.ToString();
_options[0].text = PrizeStockService.Enabled
? "КОНТРОЛЬ ОСТАТКОВ: ВКЛЮЧЁН"
: "КОНТРОЛЬ ОСТАТКОВ: ВЫКЛЮЧЕН";
_options[1].text = $"ДОБАВИТЬ ПРИЗЫ: +{_addAmount}";
_options[2].text = $"УСТАНОВИТЬ КОЛИЧЕСТВО: {_setAmount}";
RefreshSelection();
}
private void RefreshSelection()
{
for (int i = 0; i < _options.Length; i++)
{
bool selected = i == _selectedOptionIndex;
_options[i].color = selected ? _selectedOptionColor : _normalOptionColor;
_options[i].rectTransform.localScale = selected ? Vector3.one * 1.06f : Vector3.one;
}
if (_editingNumber)
{
_description.text = _selectedOptionIndex == AddOptionIndex
? "Добавляет выбранное число к текущему остатку."
: "Заменяет текущий остаток выбранным значением.";
_hint.text = "УДЕРЖИВАЙТЕ ДЖОЙСТИК ДЛЯ УСКОРЕНИЯ КНОПКА: ПРИМЕНИТЬ";
return;
}
_description.text = _selectedOptionIndex switch
{
ToggleOptionIndex => "При выключении остаток не списывается, а предупреждение в меню скрыто.",
AddOptionIndex => "Увеличивает текущий остаток на выбранное количество.",
SetOptionIndex => "Присваивает счётчику указанное количество призов.",
_ => "Вернуться к списку настроек."
};
_hint.text = "ВВЕРХ / ВНИЗ: ВЫБОР КНОПКА: ОТКРЫТЬ ВЛЕВО / ВПРАВО: ПЕРЕКЛЮЧИТЬ";
}
private void ResetNumberInputRepeat()
{
_numberInputHeld = false;
_heldNumberDirection = 0;
_heldNumberBaseStep = 0;
_numberHoldTime = 0f;
_lastNumberChangeTime = 0f;
}
private void Close()
{
if (_isClosing)
return;
_isClosing = true;
ControlsEnabled = false;
_fadeTween?.Kill();
_fadeTween = _canvasGroup.DOFade(0f, _fadeDuration).SetEase(Ease.InCubic).SetUpdate(true);
_fadeTween.OnComplete(() =>
{
_canvasGroup.blocksRaycasts = false;
OnClosed?.Invoke();
});
_fadeTween.PlayForward();
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4245112e57c290747b0c6dbec1a82da7

View File

@ -10,6 +10,7 @@ public static class SettingsManager
private static string SettingsFileName = "GameSettings.json";
private static string SettingsPath => Application.persistentDataPath;
public static SettingsSchemeDTO currentSchemeDTO { get; private set; }
public static bool Initialized => currentSchemeDTO != null;
// Обновленный метод Initialize с обработкой ошибок загрузки
public static void Initialize(SettingsScheme defaultSchemeAsset)
@ -472,6 +473,24 @@ public static class SettingsManager
public static bool GetBool(string id)
=> GetSetting(id, Setting.SettingType.Bool, s => s.BoolValue);
public static void SetBool(string id, bool value)
{
SettingDTO setting = FindSetting(id);
if (setting == null)
throw new InvalidOperationException($"Настройка {id} не найдена");
if (setting.Type != Setting.SettingType.Bool)
throw new InvalidOperationException($"Настройка {id} не является логической");
if (setting.BoolValue == value)
return;
setting.BoolValue = value;
TriggerSettingsChanged();
SaveSettings();
}
#endregion
#region Private Helpers
@ -504,4 +523,4 @@ public static class SettingsManager
return null;
}
#endregion
}
}

View File

@ -20,6 +20,7 @@ public class TextInput : MonoBehaviour, IControlable
[SerializeField] private float MinSpeedLetterPerSecond = 3;
[SerializeField] private float MaxSpeedTime = 3;
[SerializeField] private float MinSpeedTime = 0.5f;
[SerializeField] private bool _destroyOnComplete = true;
private float switchButtonPressedTime = 0;
private float letterSwitchSpeed = 0;
@ -35,10 +36,16 @@ public class TextInput : MonoBehaviour, IControlable
public void Initialize(int cellsAmount, string alphabet, string initialString = "")
{
ControlsEnabled = true;
completionRequested = false;
completeUnpressed = false;
Generate(cellsAmount, alphabet);
InitializeSlots(initialString);
}
public void SetDestroyOnComplete(bool destroyOnComplete)
=> _destroyOnComplete = destroyOnComplete;
public void UpdateControls()
{
int dir_h = In.GetAxis(JoystickSide.Both, InteractMode.Discrete).x;
@ -58,8 +65,15 @@ public class TextInput : MonoBehaviour, IControlable
private void Complete()
{
string result = CollectText();
if (string.IsNullOrWhiteSpace(result))
return;
ControlsEnabled = false;
OnComplete?.Invoke(result);
Destroy(gameObject);
if (_destroyOnComplete)
Destroy(gameObject);
}
private void Generate(int cellsAmount, string alphabet)
@ -97,7 +111,7 @@ public class TextInput : MonoBehaviour, IControlable
if (dir == 0) return;
ActiveSlot.SetSelected(false);
_slotIndex = Mathf.Clamp(_slotIndex + dir, 0, _slots.Length);
_slotIndex = Mathf.Clamp(_slotIndex + dir, 0, _slots.Length - 1);
ActiveSlot.SetSelected(true);
}

View File

@ -1,4 +1,6 @@
using DG.Tweening;
using CharacterSystem;
using PrizeSystem;
using System.Collections.Generic;
using UnityEngine;
@ -14,16 +16,10 @@ public class PrizesPanel : InfoPanel
[SerializeField] private RectTransform table;
[SerializeField] private PrizeRecord recordPrefab;
[Header("Отладочное")]
[SerializeField] private List<Sprite> icons;
[SerializeField] private List<string> names;
[SerializeField] private List<string> prizes;
[SerializeField] private List<Sprite> prizeicons;
private List<PrizeRecord> records;
private readonly List<PrizeRecord> _records = new();
public override float closeDelay => (Mathf.Max(infoDuration, buttonDuration, titleDuration)
+ records.Count * recordDelay
+ _records.Count * recordDelay
+ titleDuration) / closeSpeedMultiplier;
protected override void InitializeBeforeOpen()
@ -33,15 +29,24 @@ public class PrizesPanel : InfoPanel
{
Destroy(table.GetChild(i).gameObject);
}
records?.Clear();
records = new List<PrizeRecord>();
_records.Clear();
// Сгенерировать призы
for (int i = 0; i < 5; i++)
Character[] characters = CharactersController.GetCharactersArray();
List<PrizeSet> prizeSets = PrizeSystemManager.PrizeContainer?.Sets;
for (int i = 0; i < characters.Length; i++)
{
PrizeRecord record = Instantiate(recordPrefab, table);
record.Init(i + 1, icons[i], names[i], prizes[i], prizeicons[i]);
records.Add(record);
IReadOnlyList<Prize> characterPrizes = i < (prizeSets?.Count ?? 0)
? prizeSets[i]?.Prizes
: null;
record.Init(
i + 1,
characters[i].visualModule.iconSmall,
characters[i].characterName,
characterPrizes);
_records.Add(record);
}
}
@ -59,9 +64,9 @@ public class PrizesPanel : InfoPanel
seq.Insert(t + buttonDuration - timeOffset, info.DOAnchorPosX(infoOpenX, infoDuration).SetEase(Ease.OutBack));
t += recordDelay;
for (int i = records.Count - 1; i >= 0; i--)
for (int i = _records.Count - 1; i >= 0; i--)
{
var image = records[i].movableGroup;
var image = _records[i].movableGroup;
seq.Insert(t, image.DOAnchorPosY(recordOpenY, recordDuration));
t += recordDelay;
}
@ -78,14 +83,11 @@ public class PrizesPanel : InfoPanel
seq.Join(titleSign.DOAnchorPosY(titleCloseY, titleDuration));
float time = 0;
if (records != null)
for (int i = 0; i < _records.Count; i++)
{
for (int i = 0; i < records.Count; i++)
{
var image = records[i].movableGroup;
seq.Insert(time, image.DOAnchorPosY(recordCloseY, recordDuration));
time += recordDelay;
}
var image = _records[i].movableGroup;
seq.Insert(time, image.DOAnchorPosY(recordCloseY, recordDuration));
time += recordDelay;
}
seq.Append(root.DOAnchorPos(position, titleDuration * 4));
@ -97,4 +99,4 @@ public class PrizesPanel : InfoPanel
public override void Initialize()
{
}
}
}

View File

@ -1,25 +1,99 @@
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using PrizeSystem;
using System.Collections.Generic;
using System.Linq;
public class PrizeRecord : MonoBehaviour
{
[SerializeField] TextMeshProUGUI numberLabel;
[SerializeField] Image characterImage;
[SerializeField] TextMeshProUGUI characterNameLabel;
[SerializeField] TextMeshProUGUI prizeLabel;
[SerializeField] Image prizeIconImage;
[SerializeField] CanvasGroup prizeCell;
public RectTransform movableGroup;
public void Init(int number, Sprite iconCharacter, string name, string prize, Sprite iconPrize)
[SerializeField] private TextMeshProUGUI numberLabel;
[SerializeField] private Image characterImage;
[SerializeField] private TextMeshProUGUI characterNameLabel;
[SerializeField] private TextMeshProUGUI prizeLabel;
[SerializeField] private Image prizeIconImage;
[SerializeField] private CanvasGroup prizeCell;
private const float PrizeIconSize = 22f;
private const float PrizeIconSpacing = 3f;
private readonly List<Image> _additionalPrizeIcons = new();
public void Init(int number, Sprite iconCharacter, string name, IReadOnlyList<Prize> prizes)
{
numberLabel.text = number.ToString();
characterImage.sprite = iconCharacter;
characterNameLabel.text = name;
prizeLabel.text = prize;
prizeIconImage.sprite = iconPrize;
prizeCell.alpha = iconPrize != null ? 1 : 0;
List<Prize> availablePrizes = prizes?
.Where(prize => prize != null && prize is not EmptyPrize)
.ToList() ?? new List<Prize>();
prizeLabel.enableAutoSizing = true;
prizeLabel.fontSizeMin = 8f;
prizeLabel.text = availablePrizes.Count > 0
? string.Join("\n", availablePrizes.Select(GetCompactPrizeText))
: "Без приза";
SetupPrizeIcons(availablePrizes);
}
private void SetupPrizeIcons(IReadOnlyList<Prize> prizes)
{
ClearAdditionalPrizeIcons();
bool hasPrizes = prizes.Count > 0;
prizeCell.alpha = hasPrizes ? 1f : 0f;
prizeIconImage.gameObject.SetActive(hasPrizes);
if (hasPrizes == false)
return;
float totalWidth = prizes.Count * PrizeIconSize + (prizes.Count - 1) * PrizeIconSpacing;
float startX = -totalWidth * 0.5f + PrizeIconSize * 0.5f;
for (int i = 0; i < prizes.Count; i++)
{
Image icon = i == 0
? prizeIconImage
: Instantiate(prizeIconImage, prizeCell.transform);
icon.name = $"Prize icon {i + 1}";
icon.sprite = PrizeSystemManager.GetPrizeIcon(prizes[i].PrizeType);
icon.preserveAspect = true;
icon.gameObject.SetActive(true);
RectTransform iconTransform = icon.rectTransform;
iconTransform.anchorMin = new Vector2(0.5f, 0.5f);
iconTransform.anchorMax = new Vector2(0.5f, 0.5f);
iconTransform.pivot = new Vector2(0.5f, 0.5f);
iconTransform.anchoredPosition = new Vector2(startX + i * (PrizeIconSize + PrizeIconSpacing), 0f);
iconTransform.sizeDelta = Vector2.one * PrizeIconSize;
iconTransform.localScale = Vector3.one;
if (i > 0)
_additionalPrizeIcons.Add(icon);
}
}
private void ClearAdditionalPrizeIcons()
{
foreach (Image icon in _additionalPrizeIcons)
{
icon.gameObject.SetActive(false);
Destroy(icon.gameObject);
}
_additionalPrizeIcons.Clear();
}
private static string GetCompactPrizeText(Prize prize)
{
if (prize is VitrinaPrize vitrinaPrize)
return $"Витрина: {string.Join(" | ", vitrinaPrize.Boxes)}";
return PrizeView.GetPrizeText(prize);
}
}

View File

@ -5,6 +5,8 @@ using UnityEngine;
public class VitrinaCatcher : MonoBehaviour
{
public event Action<bool> ResultClosed;
[Header("Ссылки")]
[SerializeField] private RectTransform Panel;
[SerializeField] private CanvasGroup MainCanvasGroup;
@ -23,14 +25,21 @@ public class VitrinaCatcher : MonoBehaviour
[SerializeField] private float DisappearTimeError = 60;
private bool animating = false;
private bool _currentSuccess;
public static VitrinaCatcher Instance { get; private set; }
public void Initialize()
{
Instance = this;
PrizeStandController.OpeningFinished -= DisplayOpenResult;
PrizeStandController.OpeningFinished += DisplayOpenResult;
}
private void OnDestroy()
{
PrizeStandController.OpeningFinished -= DisplayOpenResult;
}
private void Update()
{
if (animating && In.GetAxis(JoystickSide.Both, InteractMode.Discrete).x > 0)
@ -46,6 +55,8 @@ public class VitrinaCatcher : MonoBehaviour
SuccessCanvasGroup.gameObject.SetActive(false);
ErrorCanvasGroup.gameObject.SetActive(false);
animating = false;
gameObject.SetActive(false);
ResultClosed?.Invoke(_currentSuccess);
}
@ -54,6 +65,8 @@ public class VitrinaCatcher : MonoBehaviour
if (success)
PrizeSystem.PrizeSystemManager.ReportPrizeIssued();
_currentSuccess = success;
gameObject.SetActive(true);
InitializeBySuccess(success);
}

File diff suppressed because one or more lines are too long

View File

@ -27,7 +27,7 @@ EditorSettings:
m_UnlockBlockShaders: 0
m_PrefabModeAllowAutoSave: 1
m_EnterPlayModeOptionsEnabled: 1
m_EnterPlayModeOptions: 0
m_EnterPlayModeOptions: 1
m_GameObjectNamingDigits: 1
m_GameObjectNamingScheme: 1
m_AssetNamingUsesSpace: 1

View File

@ -144,8 +144,7 @@ PlayerSettings:
visionOSBundleVersion: 1.0
tvOSBundleVersion: 1.0
bundleVersion: 5
preloadedAssets:
- {fileID: 11400000, guid: 8aa2e28620f30d74db04e88c22398ba0, type: 2}
preloadedAssets: []
metroInputSource: 0
wsaTransparentSwapchain: 0
xboxOneDisableKinectGpuReservation: 1
@ -534,7 +533,10 @@ PlayerSettings:
m_Height: 1024
m_Kind: 4
m_SubKind: App Store
m_BuildTargetBatching: []
m_BuildTargetBatching:
- m_BuildTarget: Standalone
m_StaticBatching: 1
m_DynamicBatching: 0
m_BuildTargetShaderSettings: []
m_BuildTargetGraphicsJobs:
- m_BuildTarget: MacStandaloneSupport

View File

@ -4,16 +4,20 @@
UnityConnectSettings:
m_ObjectHideFlags: 0
serializedVersion: 1
m_Enabled: 1
m_Enabled: 0
m_TestMode: 0
m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events
m_EventUrl: https://cdp.cloud.unity3d.com/v1/events
m_ConfigUrl: https://config.uca.cloud.unity3d.com
m_DashboardUrl: https://dashboard.unity3d.com
m_TestInitMode: 0
CrashReportingSettings:
m_EventUrl: https://perf-events.cloud.unity3d.com
InsightsSettings:
m_EngineDiagnosticsEnabled: 0
m_Enabled: 0
CrashReportingSettings:
serializedVersion: 2
m_EventUrl: https://perf-events.cloud.unity3d.com
m_EnableCloudDiagnosticsReporting: 0
m_LogBufferSize: 10
m_CaptureEditorExceptions: 1
UnityPurchasingSettings: