From 00462af4c362fe193fe80ef217dda5913ceddce8 Mon Sep 17 00:00:00 2001 From: _sega Date: Thu, 29 Jan 2026 12:18:15 +0300 Subject: [PATCH] =?UTF-8?q?FEAT:=20=D0=94=D0=B5=D0=B9=D1=81=D1=82=D0=B2?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B5=D0=BA?= =?UTF-8?q?=20=D0=B2=D1=8B=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D1=8B=20=D0=BD?= =?UTF-8?q?=D0=B0=20=D1=81=D0=B0=D0=B9=D1=82-=D0=BF=D0=B0=D0=BD=D0=B5?= =?UTF-8?q?=D0=BB=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Fonts/NEW/NexaRegular COOL.asset | 2 +- Assets/Resources/style.txt | 226 +++++++++++++++--- Assets/Scripts/Combinator/SettingsActions.cs | 30 +-- .../GameFSM/EnterPanels/CheckQrState.cs | 23 +- .../EnterPanels/EnterPricePVERevengeState.cs | 7 + .../GameFSM/EnterPanels/EnterPricePVEState.cs | 9 +- .../GameFSM/EnterPanels/EnterPricePVPState.cs | 9 +- .../EnterPanels/EnterPrizesAvailableState.cs | 10 +- .../EnterPrizesForCharactersState.cs | 11 +- .../EnterPulsesToResetDifficulty.cs | 9 +- .../EnterPanels/EnterSpecialPrizeState.cs | 9 +- .../EnterPanels/EnterValutaNameState.cs | 9 +- .../EnterPanels/EnterValutaPerImpulse.cs | 9 +- .../GameFSM/EnterPanels/GetNumberState.cs | 9 +- Assets/Scripts/GameFSM/GameStateMachine.cs | 2 +- Assets/Scripts/GameFSM/IoTestState.cs | 5 +- Assets/Scripts/Server/HtmlBuilder.cs | 140 +++++++++-- Assets/Scripts/Server/WebServer.cs | 79 +++++- Assets/Scripts/Settings/SettingsScreen.cs | 4 +- 19 files changed, 491 insertions(+), 111 deletions(-) diff --git a/Assets/Fonts/NEW/NexaRegular COOL.asset b/Assets/Fonts/NEW/NexaRegular COOL.asset index 675d8c7..b2cb47a 100644 --- a/Assets/Fonts/NEW/NexaRegular COOL.asset +++ b/Assets/Fonts/NEW/NexaRegular COOL.asset @@ -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, g: 1, b: 0.08173752, a: 1} + - _GlowColor: {r: 0, g: 0.72760534, b: 1, 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} diff --git a/Assets/Resources/style.txt b/Assets/Resources/style.txt index 5395c1a..24d8a3c 100644 --- a/Assets/Resources/style.txt +++ b/Assets/Resources/style.txt @@ -1,9 +1,15 @@ -* { +/* ========================= + Base reset + ========================= */ +* { margin: 0; padding: 0; box-sizing: border-box; } +/* ========================= + Page + ========================= */ body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); @@ -22,7 +28,7 @@ h1 { font-size: 3.5rem; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); width: 100%; - max-width: 800px; /* чуть шире, т.к. панелей теперь несколько */ + max-width: 800px; /* ширина под блоки */ } h2 { @@ -31,7 +37,9 @@ h2 { margin-bottom: 14px; } -/* Основная форма (legacy) */ +/* ========================= + Legacy: если где-то ещё используется + ========================= */ .form-root { background: white; padding: 25px; @@ -45,7 +53,65 @@ h2 { } /* ========================= - NEW: Панели (как карточки) + NEW: раскрывающиеся блоки (details) + ========================= */ +.block { + width: 100%; + max-width: 800px; + border-radius: 20px; + background: white; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); + overflow: hidden; + margin-bottom: 18px; +} + +/* убираем стандартный маркер */ +.block > summary::-webkit-details-marker { display: none; } +.block > summary::marker { content: ""; } + +/* заголовок блока */ +.block-title { + padding: 20px 24px; + cursor: pointer; + user-select: none; + font-weight: 700; + color: #2d3748; + font-size: 2rem; + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + background: white; +} + +/* стрелка справа */ +.block-title::after { + content: "›"; + font-size: 2.4rem; + line-height: 1; + transform: rotate(0deg); + transition: transform 0.2s ease; + opacity: 0.85; +} + +.block[open] > .block-title { + border-bottom: 2px solid rgba(0, 0, 0, 0.06); +} + +.block[open] > .block-title::after { + transform: rotate(90deg); +} + +/* тело блока */ +.block-body { + padding: 22px 24px 24px; + display: flex; + flex-direction: column; + gap: 18px; +} + +/* ========================= + Панели (карточки внутри блока) ========================= */ .panel { background: white; @@ -57,10 +123,17 @@ h2 { display: flex; flex-direction: column; gap: 12px; - margin-bottom: 20px; + margin-bottom: 0; /* теперь управляем gap'ом в .block-body */ } -/* Группы элементов формы */ +/* чуть мягче панели внутри раскрывающегося блока (не ломает общий стиль) */ +.block-body .panel { + box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12); +} + +/* ========================= + Группы элементов формы + ========================= */ .form-group { border: 2px solid #e0e0e0 !important; border-radius: 12px !important; @@ -69,7 +142,7 @@ h2 { background: #f8f9fa; } -/* hover оставляем (это не анимация появления, это реакция) */ +/* hover реакция */ .form-group:hover { border-color: #667eea !important; box-shadow: 0 5px 15px rgba(102, 126, 234, 0.1); @@ -91,7 +164,9 @@ h2 { font-size: 2.1rem; } -/* Стили для range input */ +/* ========================= + Range input + ========================= */ input[type="range"] { width: 100%; height: 8px; @@ -120,7 +195,9 @@ input[type="range"]::-webkit-slider-thumb:hover { box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); } -/* Стили для чекбоксов */ +/* ========================= + Checkbox + ========================= */ input[type="checkbox"] { -webkit-appearance: none; appearance: none; @@ -157,7 +234,9 @@ input[type="checkbox"]:hover { box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); } -/* Стили для радиокнопок */ +/* ========================= + Radio + ========================= */ input[type="radio"] { -webkit-appearance: none; appearance: none; @@ -195,8 +274,7 @@ input[type="radio"]:hover { } /* ========================= - CHANGED: радио-строки - (раньше было .form-group > div, но у тебя теперь .radio-row) + Радио-строки (.radio-row) ========================= */ .radio-row { display: flex; @@ -223,7 +301,7 @@ input[type="radio"]:hover { margin-right: 0; } -/* Оставляем старую поддержку на случай других div внутри form-group */ +/* поддержка старой разметки */ .form-group > div { margin-bottom: 10px; padding: 8px 12px; @@ -240,7 +318,9 @@ input[type="radio"]:hover { margin-bottom: 0; } -/* Кнопка отправки (legacy) */ +/* ========================= + Legacy submit + ========================= */ input[type="submit"] { background: linear-gradient(to right, #667eea, #764ba2); color: white; @@ -268,7 +348,7 @@ input[type="submit"]:active { } /* ========================= - NEW: кнопки и поля (монеты + логин) + Buttons / Coins / Login ========================= */ button { font-family: inherit; @@ -326,10 +406,10 @@ button { font-size: 1.05rem; color: #2d3748; opacity: 0.9; - min-height: 1.2em; /* чтобы не прыгало, когда текст появляется/исчезает */ + min-height: 1.2em; /* чтобы не прыгало */ } -/* глобальный статус снизу — визуально мягче */ +/* глобальный статус снизу */ #globalStatus.status { width: 100%; max-width: 800px; @@ -341,7 +421,68 @@ button { } /* ========================= - NEW: логин-страница (в стиле карточек) + NEW: actions (SettingAction) + ========================= */ +.action-group { + border: 2px solid #e0e0e0; + border-radius: 16px; + padding: 18px; + background: #f8f9fa; + transition: all 0.3s ease; +} + +.action-group:hover { + border-color: #667eea; + box-shadow: 0 5px 15px rgba(102, 126, 234, 0.1); + transform: translateY(-2px); +} + +.action-group-title { + color: #2d3748; + font-size: 1.7rem; + font-weight: 700; + margin-bottom: 14px; +} + +.actions-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 12px; +} + +.action-btn { + background: linear-gradient(to right, #667eea, #764ba2); + color: white; + border: none; + padding: 16px 16px; + font-size: 1.1rem; + font-weight: 700; + border-radius: 14px; + cursor: pointer; + transition: all 0.2s ease; + box-shadow: 0 4px 15px rgba(102, 126, 234, 0.22); + text-align: center; + line-height: 1.15; +} + +.action-btn:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(102, 126, 234, 0.35); +} + +.action-btn:active { + transform: translateY(-1px); +} + +.action-btn:disabled { + opacity: 0.65; + cursor: not-allowed; + transform: none !important; + box-shadow: 0 4px 15px rgba(102, 126, 234, 0.18); +} + +/* ========================= + Login page card ========================= */ .login-root { background: white; @@ -356,7 +497,6 @@ button { } .login-root h1 { - /* На логине свой h1 внутри белой карточки, поэтому переопределим */ color: #2d3748; text-shadow: none; font-size: 2.6rem; @@ -371,7 +511,6 @@ button { min-height: 1.2em; } -/* Инпут пароля в стиле coins-input */ .login-root input[type="password"] { width: 100%; border: 2px solid #e0e0e0; @@ -388,29 +527,19 @@ button { outline: none; } -/* Улучшение читаемости на светлом фоне */ -.form-group label[for], -.form-group p { - color: #2d3748; -} - -/* Эффект фокуса для доступности */ -input:focus { +/* ========================= + Focus accessibility + ========================= */ +input:focus, +button:focus, +summary:focus { outline: none; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.3); } /* ========================= - CHANGED: УБРАЛИ анимацию появления + Mobile ========================= */ -/* Удалено: -@keyframes fadeIn {...} -.form-group { animation: ... } -.form-group:nth-child(...) ... -input[type="submit"] { animation: ... } -*/ - -/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { body { padding: 15px; @@ -428,6 +557,20 @@ input[type="submit"] { animation: ... } margin-bottom: 10px; } + .block-title { + font-size: 1.35rem; + padding: 16px 18px; + } + + .block-title::after { + font-size: 1.9rem; + } + + .block-body { + padding: 18px; + gap: 16px; + } + .form-root, .panel { padding: 20px; @@ -445,12 +588,13 @@ input[type="submit"] { animation: ... } margin-bottom: 10px; } - /* кнопки */ .coins-btn, #loginBtn, - input[type="submit"] { + input[type="submit"], + .action-btn { padding: 14px 16px; font-size: 1rem; + border-radius: 12px; } .coins-input, @@ -465,6 +609,10 @@ input[type="submit"] { animation: ... } align-items: stretch; } + .actions-grid { + grid-template-columns: 1fr; + } + /* тач-таргеты */ input[type="checkbox"], input[type="radio"], diff --git a/Assets/Scripts/Combinator/SettingsActions.cs b/Assets/Scripts/Combinator/SettingsActions.cs index 8c09ac0..318807e 100644 --- a/Assets/Scripts/Combinator/SettingsActions.cs +++ b/Assets/Scripts/Combinator/SettingsActions.cs @@ -10,6 +10,8 @@ public class SettingsActions : MonoBehaviour [SerializeField] AudioClip prizingDisabled; private GameManager gm => GameManager.instance; + + private IState CurrentState => GameManager.instance.stateMachine.currentState; private void OnEnable() { SettingsManager.OnSettingsChanged?.AddListener(RefreshUI); @@ -36,22 +38,23 @@ public class SettingsActions : MonoBehaviour RefreshUI(); } - //Панель1 - public void EnterSpecialPrize() => gm.stateMachine.ChangeState(new EnterSpecialPrizeState()); - public void EnterPvePrice() => gm.stateMachine.ChangeState(new EnterPricePVEState()); - public void EnterPveRevengePrice() => gm.stateMachine.ChangeState(new EnterPricePVERevengeState()); - public void EnterPvpPrice() => gm.stateMachine.ChangeState(new EnterPricePVPState()); - public void EnterSupportNumber() => gm.stateMachine.ChangeState(new GetNumberState()); - public void EnterPrizesAvailable() => gm.stateMachine.ChangeState(new EnterPrizesAvailableState()); + //Смены состояний + public void EnterSpecialPrize() => gm.stateMachine.ChangeState(new EnterSpecialPrizeState(CurrentState)); + public void EnterPvePrice() => gm.stateMachine.ChangeState(new EnterPricePVEState(CurrentState)); + public void EnterPveRevengePrice() => gm.stateMachine.ChangeState(new EnterPricePVERevengeState(CurrentState)); + public void EnterPvpPrice() => gm.stateMachine.ChangeState(new EnterPricePVPState(CurrentState)); + public void EnterSupportNumber() => gm.stateMachine.ChangeState(new GetNumberState(CurrentState)); + public void EnterPrizesAvailable() => gm.stateMachine.ChangeState(new EnterPrizesAvailableState(CurrentState)); public void ResetPrizeError() => PlayerPrefs.SetInt(KeyVault.PRIZING_ERROR_REGISTRED, 0); - public void EnterPrizesSettings() => gm.stateMachine.ChangeState(new EnterPrizesAvailableState()); - public void ConfigurePrizes() => gm.stateMachine.ChangeState(new EnterPrizesForCharactersState()); - public void ConfigurePulsesToResetDifficulty() => gm.stateMachine.ChangeState(new EnterPulsesToResetDifficulty()); - public void ConfigureValutaPerPulse() => gm.stateMachine.ChangeState(new EnterValutaPerImpulse()); - public void EnterValutaName() => gm.stateMachine.ChangeState(new EnterValutaNameState()); + public void EnterPrizesSettings() => gm.stateMachine.ChangeState(new EnterPrizesAvailableState(CurrentState)); + public void ConfigurePrizes() => gm.stateMachine.ChangeState(new EnterPrizesForCharactersState(CurrentState)); + public void ConfigurePulsesToResetDifficulty() => gm.stateMachine.ChangeState(new EnterPulsesToResetDifficulty(CurrentState)); + public void ConfigureValutaPerPulse() => gm.stateMachine.ChangeState(new EnterValutaPerImpulse(CurrentState)); + public void EnterValutaName() => gm.stateMachine.ChangeState(new EnterValutaNameState(CurrentState)); + public void QrTest() => gm.stateMachine.ChangeState(new CheckQrState(CurrentState)); + public void EnterIO_test() => gm.stateMachine.ChangeState(new IoTestState(IO_test, CurrentState)); //Панель2 - public void EnterIO_test() => gm.stateMachine.ChangeState(new IoTestState(IO_test)); public void HopperTest() { if(SettingsManager.GetBool("Prizing", false)) @@ -65,7 +68,6 @@ public class SettingsActions : MonoBehaviour } public void SoundTest() => SoundManager.instance.playClip(testSound); public void ScreenTest() => ScreenTester.SetActive(true); - public void QrTest() => gm.stateMachine.ChangeState(new CheckQrState()); public void ResetBalance() { ComWrapper.Balance = 0; diff --git a/Assets/Scripts/GameFSM/EnterPanels/CheckQrState.cs b/Assets/Scripts/GameFSM/EnterPanels/CheckQrState.cs index cfc098a..18046d7 100644 --- a/Assets/Scripts/GameFSM/EnterPanels/CheckQrState.cs +++ b/Assets/Scripts/GameFSM/EnterPanels/CheckQrState.cs @@ -5,27 +5,22 @@ using UnityEngine; public class CheckQrState : IState { float timer = 0; + IState prevState; + + public CheckQrState(IState prevState) + { + this.prevState = prevState; + } + public void enter() => GameManager.instance.checkQrPanel.SetActive(true); public void exit() => GameManager.instance.checkQrPanel.SetActive(false); public void update() { - /*Это был выход по и1 + и2 с таймером 3 сек - timer -= Time.deltaTime; - if (In.GetButton(ButtonPinMapping.startOnePlayer) && In.GetButton(ButtonPinMapping.startTwoPlayers)) - { - timer += Time.deltaTime * 2; - } - - timer = Mathf.Clamp(timer, 0, 3); - if (timer >= 2.9f) - { - GameManager.instance.stateMachine.ChangeState(new SettingsState()); - }*/ - if (In.GetButtonDownAnyPlayer(PlayerAction.down)) { - GameManager.instance.stateMachine.ChangeState(new SettingsState()); + //GameManager.instance.stateMachine.ChangeState(new SettingsState()); + GameManager.instance.stateMachine.ChangeState(prevState); } } } diff --git a/Assets/Scripts/GameFSM/EnterPanels/EnterPricePVERevengeState.cs b/Assets/Scripts/GameFSM/EnterPanels/EnterPricePVERevengeState.cs index b931696..d37336a 100644 --- a/Assets/Scripts/GameFSM/EnterPanels/EnterPricePVERevengeState.cs +++ b/Assets/Scripts/GameFSM/EnterPanels/EnterPricePVERevengeState.cs @@ -6,6 +6,12 @@ public class EnterPricePVERevengeState : IState { NumberInput numberInput; GameManager gm; + IState prevState; + + public EnterPricePVERevengeState(IState prevState) + { + this.prevState = prevState; + } public void enter() { @@ -33,6 +39,7 @@ public class EnterPricePVERevengeState : IState //обратный переход gm.stateMachine.ChangeState(new SettingsState()); + gm.stateMachine.ChangeState(prevState); } diff --git a/Assets/Scripts/GameFSM/EnterPanels/EnterPricePVEState.cs b/Assets/Scripts/GameFSM/EnterPanels/EnterPricePVEState.cs index 602ec82..11e0de8 100644 --- a/Assets/Scripts/GameFSM/EnterPanels/EnterPricePVEState.cs +++ b/Assets/Scripts/GameFSM/EnterPanels/EnterPricePVEState.cs @@ -2,6 +2,12 @@ { NumberInput numberInput; GameManager gm; + IState prevState; + + public EnterPricePVEState(IState prevState) + { + this.prevState = prevState; + } public void enter() { @@ -28,7 +34,8 @@ numberInput.AcceptAndClose(); //обратный переход - gm.stateMachine.ChangeState(new SettingsState()); + //gm.stateMachine.ChangeState(new SettingsState()); + gm.stateMachine.ChangeState(prevState); } diff --git a/Assets/Scripts/GameFSM/EnterPanels/EnterPricePVPState.cs b/Assets/Scripts/GameFSM/EnterPanels/EnterPricePVPState.cs index 9d9c161..026ef0b 100644 --- a/Assets/Scripts/GameFSM/EnterPanels/EnterPricePVPState.cs +++ b/Assets/Scripts/GameFSM/EnterPanels/EnterPricePVPState.cs @@ -6,6 +6,12 @@ public class EnterPricePVPState : IState { NumberInput numberInput; GameManager gm; + IState prevState; + + public EnterPricePVPState(IState prevState) + { + this.prevState = prevState; + } public void enter() { @@ -32,7 +38,8 @@ public class EnterPricePVPState : IState numberInput.AcceptAndClose(); //обратный переход - gm.stateMachine.ChangeState(new SettingsState()); + //gm.stateMachine.ChangeState(new SettingsState()); + gm.stateMachine.ChangeState(prevState); } diff --git a/Assets/Scripts/GameFSM/EnterPanels/EnterPrizesAvailableState.cs b/Assets/Scripts/GameFSM/EnterPanels/EnterPrizesAvailableState.cs index 0ec816a..6803cfe 100644 --- a/Assets/Scripts/GameFSM/EnterPanels/EnterPrizesAvailableState.cs +++ b/Assets/Scripts/GameFSM/EnterPanels/EnterPrizesAvailableState.cs @@ -7,6 +7,13 @@ public class EnterPrizesAvailableState : IState NumberInput numberInput; GameManager gm; + IState prevState; + + public EnterPrizesAvailableState(IState prevState) + { + this.prevState = prevState; + } + public void enter() { gm = GameManager.instance; @@ -32,7 +39,8 @@ public class EnterPrizesAvailableState : IState numberInput.AcceptAndClose(); //обратный переход - gm.stateMachine.ChangeState(new SettingsState()); + //gm.stateMachine.ChangeState(new SettingsState()); + gm.stateMachine.ChangeState(prevState); } } diff --git a/Assets/Scripts/GameFSM/EnterPanels/EnterPrizesForCharactersState.cs b/Assets/Scripts/GameFSM/EnterPanels/EnterPrizesForCharactersState.cs index 389ba5e..8151245 100644 --- a/Assets/Scripts/GameFSM/EnterPanels/EnterPrizesForCharactersState.cs +++ b/Assets/Scripts/GameFSM/EnterPanels/EnterPrizesForCharactersState.cs @@ -6,7 +6,12 @@ public class EnterPrizesForCharactersState : IState { GameObject rootPanel; List prizes; + IState prevState; + public EnterPrizesForCharactersState(IState prevState) + { + this.prevState = prevState; + } public void enter() { @@ -14,7 +19,11 @@ public class EnterPrizesForCharactersState : IState rootPanel.SetActive(true); } - public void exit() => rootPanel.SetActive(false); + public void exit() + { + GameManager.instance.stateMachine.ChangeState(prevState); + rootPanel.SetActive(false); + } public void update() { diff --git a/Assets/Scripts/GameFSM/EnterPanels/EnterPulsesToResetDifficulty.cs b/Assets/Scripts/GameFSM/EnterPanels/EnterPulsesToResetDifficulty.cs index 8168db7..135d8c5 100644 --- a/Assets/Scripts/GameFSM/EnterPanels/EnterPulsesToResetDifficulty.cs +++ b/Assets/Scripts/GameFSM/EnterPanels/EnterPulsesToResetDifficulty.cs @@ -6,6 +6,12 @@ public class EnterPulsesToResetDifficulty : IState { NumberInput numberInput; GameManager gm; + IState prevState; + + public EnterPulsesToResetDifficulty(IState prevState) + { + this.prevState = prevState; + } public void enter() { @@ -32,7 +38,8 @@ public class EnterPulsesToResetDifficulty : IState numberInput.AcceptAndClose(); //обратный переход - gm.stateMachine.ChangeState(new SettingsState()); + //gm.stateMachine.ChangeState(new SettingsState()); + gm.stateMachine.ChangeState(prevState); } } diff --git a/Assets/Scripts/GameFSM/EnterPanels/EnterSpecialPrizeState.cs b/Assets/Scripts/GameFSM/EnterPanels/EnterSpecialPrizeState.cs index 556df0e..cea9ba6 100644 --- a/Assets/Scripts/GameFSM/EnterPanels/EnterSpecialPrizeState.cs +++ b/Assets/Scripts/GameFSM/EnterPanels/EnterSpecialPrizeState.cs @@ -6,6 +6,12 @@ public class EnterSpecialPrizeState : IState { TextInput textInput; GameManager gm; + IState prevState; + + public EnterSpecialPrizeState(IState prevState) + { + this.prevState = prevState; + } public void enter() { @@ -36,7 +42,8 @@ public class EnterSpecialPrizeState : IState PlayerPrefs.SetString("SpecialPrize", textInput.AcceptAndClose()); //переход - gm.stateMachine.ChangeState(new SettingsState()); + //gm.stateMachine.ChangeState(new SettingsState()); + gm.stateMachine.ChangeState(prevState); } diff --git a/Assets/Scripts/GameFSM/EnterPanels/EnterValutaNameState.cs b/Assets/Scripts/GameFSM/EnterPanels/EnterValutaNameState.cs index 0b332a9..8e7893c 100644 --- a/Assets/Scripts/GameFSM/EnterPanels/EnterValutaNameState.cs +++ b/Assets/Scripts/GameFSM/EnterPanels/EnterValutaNameState.cs @@ -6,6 +6,12 @@ public class EnterValutaNameState : IState { TextInput textInput; GameManager gm; + IState prevState; + + public EnterValutaNameState(IState prevState) + { + this.prevState = prevState; + } //private string name; public void enter() @@ -37,7 +43,8 @@ public class EnterValutaNameState : IState PlayerPrefs.SetString("Price", textInput.AcceptAndClose()); //переход - gm.stateMachine.ChangeState(new SettingsState()); + //gm.stateMachine.ChangeState(new SettingsState()); + gm.stateMachine.ChangeState(prevState); } } diff --git a/Assets/Scripts/GameFSM/EnterPanels/EnterValutaPerImpulse.cs b/Assets/Scripts/GameFSM/EnterPanels/EnterValutaPerImpulse.cs index f41cae4..982f9ff 100644 --- a/Assets/Scripts/GameFSM/EnterPanels/EnterValutaPerImpulse.cs +++ b/Assets/Scripts/GameFSM/EnterPanels/EnterValutaPerImpulse.cs @@ -6,6 +6,12 @@ public class EnterValutaPerImpulse : IState { NumberInput numberInput; GameManager gm; + IState prevState; + + public EnterValutaPerImpulse(IState prevState) + { + this.prevState = prevState; + } public void enter() { @@ -32,7 +38,8 @@ public class EnterValutaPerImpulse : IState numberInput.AcceptAndClose(); //обратный переход - gm.stateMachine.ChangeState(new SettingsState()); + //gm.stateMachine.ChangeState(new SettingsState()); + gm.stateMachine.ChangeState(prevState); } } diff --git a/Assets/Scripts/GameFSM/EnterPanels/GetNumberState.cs b/Assets/Scripts/GameFSM/EnterPanels/GetNumberState.cs index d9bfd51..bb8ab49 100644 --- a/Assets/Scripts/GameFSM/EnterPanels/GetNumberState.cs +++ b/Assets/Scripts/GameFSM/EnterPanels/GetNumberState.cs @@ -10,6 +10,7 @@ using UnityEngine.UI; public class GetNumberState : IState { + IState prevState; const string NUMBER_KEY = "Number"; //Иерархия для панели: Панель -> 10 Image -> У каждого tmpro GameObject entryPanel; @@ -25,6 +26,11 @@ public class GetNumberState : IState private int currentSlotIndex = 0; private int length = 11; + public GetNumberState(IState prevState) + { + this.prevState = prevState; + } + public void enter() { //включить панель, получить доступ ко всем слотам @@ -72,7 +78,8 @@ public class GetNumberState : IState //записываем и выходим if (In.GetButtonDown(ButtonPinMapping.down2)) { - GameManager.instance.stateMachine.ChangeState(new SettingsState()); + //GameManager.instance.stateMachine.ChangeState(new SettingsState()); + GameManager.instance.stateMachine.ChangeState(prevState); } } diff --git a/Assets/Scripts/GameFSM/GameStateMachine.cs b/Assets/Scripts/GameFSM/GameStateMachine.cs index cd54284..e4814b4 100644 --- a/Assets/Scripts/GameFSM/GameStateMachine.cs +++ b/Assets/Scripts/GameFSM/GameStateMachine.cs @@ -2,7 +2,7 @@ public class GameStateMachine : MonoBehaviour { - IState currentState; + public IState currentState; public void ChangeState(IState nextState) { diff --git a/Assets/Scripts/GameFSM/IoTestState.cs b/Assets/Scripts/GameFSM/IoTestState.cs index 8b0bd80..75816c1 100644 --- a/Assets/Scripts/GameFSM/IoTestState.cs +++ b/Assets/Scripts/GameFSM/IoTestState.cs @@ -3,10 +3,11 @@ public class IoTestState : IState { GameObject io; - - public IoTestState(GameObject ioPanel) + IState prevState; + public IoTestState(GameObject ioPanel, IState prevState) { io = ioPanel; + this.prevState = prevState; } public void enter() => io.SetActive(true); public void exit() => io.SetActive(false); diff --git a/Assets/Scripts/Server/HtmlBuilder.cs b/Assets/Scripts/Server/HtmlBuilder.cs index 4ab9bab..d03becd 100644 --- a/Assets/Scripts/Server/HtmlBuilder.cs +++ b/Assets/Scripts/Server/HtmlBuilder.cs @@ -1,12 +1,18 @@ using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Text; public class HtmlBuilder { + // старый метод оставим, чтобы ничего не поломать public static string GetSettingsPage(List settings) + => GetSettingsPage(settings, actions: null); + + // новый метод + public static string GetSettingsPage(List settings, List actions) { - return Decorate(GetSettingsString(settings), title: "Настройки Фишки"); + return Decorate(GetSettingsString(settings, actions), title: "Настройки Фишки"); } public static string GetLoginPage() @@ -59,26 +65,38 @@ public class HtmlBuilder return Decorate(content, title: "Вход"); } + // старый метод оставим public static string GetSettingsString(List settings) + => GetSettingsString(settings, actions: null); + + // новый метод: настройки + действия (раздельные блоки) + public static string GetSettingsString(List settings, List actions) { var sb = new StringBuilder(); sb.AppendLine("

Настройки фишки

"); - // блок монет + // === БЛОК: НАСТРОЙКИ === sb.AppendLine(@" -
-

Монеты

-
- - -
-
-
+
+ Настройки +
"); - sb.AppendLine("
"); - sb.AppendLine("

Параметры

"); + // монеты внутри настроек + sb.AppendLine(@" +
+

Монеты

+
+ + +
+
+
+"); + + sb.AppendLine("
"); + sb.AppendLine("

Параметры

"); foreach (Setting setting in settings) { @@ -93,9 +111,25 @@ public class HtmlBuilder sb.AppendLine(view); } - sb.AppendLine("
"); + sb.AppendLine("
"); // panel + sb.AppendLine(@" +
+
+"); - // JS для realtime отправки + // === БЛОК: ДЕЙСТВИЯ === + sb.AppendLine(@" +
+ Действия +
+"); + sb.AppendLine(GetActionsView(actions)); + sb.AppendLine(@" +
+
+"); + + // JS: realtime settings + coins + actions sb.AppendLine(@" @@ -207,6 +272,41 @@ public class HtmlBuilder return sb.ToString(); } + private static string GetActionsView(List actions) + { + var sb = new StringBuilder(); + + if (actions == null || actions.Count == 0) + { + sb.AppendLine("

Нет доступных действий

"); + return sb.ToString(); + } + + // группировка по Group (если пусто — в "Другое") + var groups = actions + .Where(a => a != null && !string.IsNullOrWhiteSpace(a.Name)) + .GroupBy(a => string.IsNullOrWhiteSpace(a.Group) ? "Другое" : a.Group); + + foreach (var g in groups) + { + sb.AppendLine($"
"); + sb.AppendLine($"

{Escape(g.Key)}

"); + sb.AppendLine("
"); + + foreach (var a in g) + { + sb.AppendLine( + $" " + ); + } + + sb.AppendLine("
"); + sb.AppendLine("
"); + } + + return sb.ToString(); + } + private static string GetBoolView(Setting setting) { string name = setting.Id; @@ -294,12 +394,10 @@ public class HtmlBuilder + ""; res += content; - res += ""; return res; } - // минимальная защита от мусора в html private static string Escape(string s) { if (string.IsNullOrEmpty(s)) return ""; diff --git a/Assets/Scripts/Server/WebServer.cs b/Assets/Scripts/Server/WebServer.cs index 5c18482..779f7ef 100644 --- a/Assets/Scripts/Server/WebServer.cs +++ b/Assets/Scripts/Server/WebServer.cs @@ -27,11 +27,16 @@ public class WebServer : MonoBehaviour private string AdminPassword => AuthManager.LoadPasswordOrFallback(adminPasswordFallback, passwordFileName); + + public List settingActions; + + async void Start() { - // SettingsManager.Initialize();//надо будет убирать LoadCss(); + settingActions = GameManager.instance.AdminPanel.GetComponent().settingActions; + await StartWebServerAsync(); } @@ -39,10 +44,6 @@ public class WebServer : MonoBehaviour { listener = new HttpListener(); listener.Prefixes.Add("http://localhost:8080/"); - - //тест - listener.Prefixes.Add("http://fishka:8080/"); - // 4. ВСЕ сетевые интерфейсы (самый важный!) listener.Prefixes.Add("http://*:8080/"); listener.Start(); @@ -126,6 +127,12 @@ public class WebServer : MonoBehaviour return; } + if (method == "POST" && path == "/api/action") + { + await HandlePostActionAsync(context); + return; + } + SendHtmlResponse(context, "

404 Not Found

", 404); } catch (Exception ex) @@ -144,8 +151,10 @@ public class WebServer : MonoBehaviour var stopwatch = System.Diagnostics.Stopwatch.StartNew(); var htmlTask = Task.Run(() => - HtmlBuilder.GetSettingsPage(SettingsManager.Container.Settings) - ); + { + var actionsSnapshot = settingActions != null ? new List(settingActions) : new List(); + return HtmlBuilder.GetSettingsPage(SettingsManager.Container.Settings, actionsSnapshot); + }); if (htmlTask.Wait(TimeSpan.FromSeconds(5))) { @@ -317,6 +326,40 @@ public class WebServer : MonoBehaviour } } + async Task HandlePostActionAsync(HttpListenerContext context) + { + try + { + string body = await ReadBodyAsStringAsync(context.Request); + var dto = JsonUtility.FromJson(body); + + string name = dto?.name ?? ""; + if (string.IsNullOrWhiteSpace(name)) + { + SendJson(context, 400, "{\"ok\":false,\"message\":\"name is empty\"}"); + return; + } + + bool exists = settingActions != null && settingActions.Exists(a => a != null && a.Name == name); + if (!exists) + { + SendJson(context, 404, "{\"ok\":false,\"message\":\"action not found\"}"); + return; + } + + // Важно: UnityEvent только на главном потоке + UnityMainThreadDispatcher.Instance.Enqueue(() => ExecuteSettingAction(name)); + + SendJson(context, 200, "{\"ok\":true}"); + } + catch (Exception ex) + { + Debug.LogError($"POST /api/action error: {ex.Message}\n{ex.StackTrace}"); + SendJson(context, 500, "{\"ok\":false,\"message\":\"Server error\"}"); + } + } + + private void OnAddCoins(int amount) => ComWrapper.AddCreditNoCounters(amount); // ========================= @@ -443,6 +486,20 @@ public class WebServer : MonoBehaviour context.Response.OutputStream.Close(); } + public void ExecuteSettingAction(string name) + { + SettingAction action = settingActions.Find(s => s.Name == name); + if (action != null) + { + action.OnPressed?.Invoke(); + } + else + { + Debug.Log($"Действие с именем {name} не найдено."); + } + } + + void OnDestroy() { isRunning = false; @@ -456,4 +513,12 @@ public class WebServer : MonoBehaviour listener?.Stop(); listener?.Close(); } + + + [Serializable] + private class ActionDto + { + public string name; + } } + diff --git a/Assets/Scripts/Settings/SettingsScreen.cs b/Assets/Scripts/Settings/SettingsScreen.cs index e9696ad..107aa5c 100644 --- a/Assets/Scripts/Settings/SettingsScreen.cs +++ b/Assets/Scripts/Settings/SettingsScreen.cs @@ -7,7 +7,7 @@ using UnityEngine; public class SettingsScreen : MonoBehaviour { [SerializeField] RectTransform pointer; - [SerializeField] List settingActions; + public List settingActions; private List settings; [SerializeField] private List excludedIds; @@ -80,8 +80,6 @@ public class SettingsScreen : MonoBehaviour private void Controls() { int direction = 0; - //if (In.GetButtonDownAnyPlayer(PlayerAction.left)) direction -= 1; - //if (In.GetButtonDownAnyPlayer(PlayerAction.right)) direction += 1; if (In.GetButtonAnyPlayer(PlayerAction.left)) direction -= 1; if (In.GetButtonAnyPlayer(PlayerAction.right)) direction += 1;