FEAT: Действия настроек выведены на сайт-панель
This commit is contained in:
parent
56a9e2464b
commit
00462af4c3
@ -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}
|
||||
|
||||
@ -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"],
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,7 +6,12 @@ public class EnterPrizesForCharactersState : IState
|
||||
{
|
||||
GameObject rootPanel;
|
||||
List<Prize> 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()
|
||||
{
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
public class GameStateMachine : MonoBehaviour
|
||||
{
|
||||
IState currentState;
|
||||
public IState currentState;
|
||||
|
||||
public void ChangeState(IState nextState)
|
||||
{
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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<Setting> settings)
|
||||
=> GetSettingsPage(settings, actions: null);
|
||||
|
||||
// новый метод
|
||||
public static string GetSettingsPage(List<Setting> settings, List<SettingAction> 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<Setting> settings)
|
||||
=> GetSettingsString(settings, actions: null);
|
||||
|
||||
// новый метод: настройки + действия (раздельные блоки)
|
||||
public static string GetSettingsString(List<Setting> settings, List<SettingAction> actions)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine("<h1>Настройки фишки</h1>");
|
||||
|
||||
// блок монет
|
||||
// === БЛОК: НАСТРОЙКИ ===
|
||||
sb.AppendLine(@"
|
||||
<div class='panel'>
|
||||
<h2>Монеты</h2>
|
||||
<div class='coins-row'>
|
||||
<input id='coinsAmount' type='number' value='10' min='1' step='1' class='coins-input'/>
|
||||
<button id='addCoinsBtn' type='button' class='coins-btn'>Добавить монеты</button>
|
||||
</div>
|
||||
<div id='coinsStatus' class='status'></div>
|
||||
</div>
|
||||
<details class='block' open>
|
||||
<summary class='block-title'>Настройки</summary>
|
||||
<div class='block-body'>
|
||||
");
|
||||
|
||||
sb.AppendLine("<div class='panel'>");
|
||||
sb.AppendLine("<h2>Параметры</h2>");
|
||||
// монеты внутри настроек
|
||||
sb.AppendLine(@"
|
||||
<div class='panel'>
|
||||
<h2>Монеты</h2>
|
||||
<div class='coins-row'>
|
||||
<input id='coinsAmount' type='number' value='10' min='1' step='1' class='coins-input'/>
|
||||
<button id='addCoinsBtn' type='button' class='coins-btn'>Добавить монеты</button>
|
||||
</div>
|
||||
<div id='coinsStatus' class='status'></div>
|
||||
</div>
|
||||
");
|
||||
|
||||
sb.AppendLine(" <div class='panel'>");
|
||||
sb.AppendLine(" <h2>Параметры</h2>");
|
||||
|
||||
foreach (Setting setting in settings)
|
||||
{
|
||||
@ -93,9 +111,25 @@ public class HtmlBuilder
|
||||
sb.AppendLine(view);
|
||||
}
|
||||
|
||||
sb.AppendLine("</div>");
|
||||
sb.AppendLine(" </div>"); // panel
|
||||
sb.AppendLine(@"
|
||||
</div>
|
||||
</details>
|
||||
");
|
||||
|
||||
// JS для realtime отправки
|
||||
// === БЛОК: ДЕЙСТВИЯ ===
|
||||
sb.AppendLine(@"
|
||||
<details class='block'>
|
||||
<summary class='block-title'>Действия</summary>
|
||||
<div class='block-body'>
|
||||
");
|
||||
sb.AppendLine(GetActionsView(actions));
|
||||
sb.AppendLine(@"
|
||||
</div>
|
||||
</details>
|
||||
");
|
||||
|
||||
// JS: realtime settings + coins + actions
|
||||
sb.AppendLine(@"
|
||||
<script>
|
||||
(function(){
|
||||
@ -129,7 +163,7 @@ public class HtmlBuilder
|
||||
}
|
||||
}
|
||||
|
||||
// Все элементы настроек
|
||||
// === Настройки ===
|
||||
document.querySelectorAll('[data-setting-id]').forEach(el=>{
|
||||
const id = el.getAttribute('data-setting-id');
|
||||
|
||||
@ -146,7 +180,6 @@ public class HtmlBuilder
|
||||
|
||||
const apply = ()=>{
|
||||
if(valueSpan) valueSpan.textContent = el.value;
|
||||
// range шлём с небольшой задержкой, чтобы не заспамить
|
||||
debounce('rng_'+id, ()=>sendSetting(id, el.value), 120);
|
||||
};
|
||||
|
||||
@ -162,18 +195,18 @@ public class HtmlBuilder
|
||||
return;
|
||||
}
|
||||
|
||||
// number/text (int)
|
||||
el.addEventListener('change', ()=>{
|
||||
sendSetting(id, el.value);
|
||||
});
|
||||
});
|
||||
|
||||
// Монеты
|
||||
// === Монеты ===
|
||||
const coinsAmount = document.getElementById('coinsAmount');
|
||||
const addBtn = document.getElementById('addCoinsBtn');
|
||||
const coinsStatus = document.getElementById('coinsStatus');
|
||||
|
||||
async function addCoins(){
|
||||
if(!coinsAmount || !addBtn || !coinsStatus) return;
|
||||
coinsStatus.textContent = '';
|
||||
const v = parseInt(coinsAmount.value || '0', 10);
|
||||
if(!Number.isFinite(v) || v <= 0){
|
||||
@ -197,7 +230,39 @@ public class HtmlBuilder
|
||||
}
|
||||
}
|
||||
|
||||
addBtn.addEventListener('click', addCoins);
|
||||
if(addBtn) addBtn.addEventListener('click', addCoins);
|
||||
|
||||
// === Действия ===
|
||||
document.querySelectorAll('[data-action-name]').forEach(btn=>{
|
||||
btn.addEventListener('click', async ()=>{
|
||||
const name = btn.getAttribute('data-action-name') || '';
|
||||
if(!name) return;
|
||||
|
||||
try{
|
||||
btn.disabled = true;
|
||||
const r = await fetch('/api/action', {
|
||||
method:'POST',
|
||||
headers:{'Content-Type':'application/json'},
|
||||
body: JSON.stringify({ name })
|
||||
});
|
||||
const t = await r.text();
|
||||
if(!r.ok){
|
||||
status('Ошибка действия');
|
||||
try{
|
||||
const j = JSON.parse(t);
|
||||
if(j && j.message) status(j.message);
|
||||
}catch(e){}
|
||||
return;
|
||||
}
|
||||
status('Ок');
|
||||
}catch(e){
|
||||
status('Сервер недоступен');
|
||||
}finally{
|
||||
setTimeout(()=>{ btn.disabled = false; }, 200);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
})();
|
||||
</script>
|
||||
|
||||
@ -207,6 +272,41 @@ public class HtmlBuilder
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string GetActionsView(List<SettingAction> actions)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
if (actions == null || actions.Count == 0)
|
||||
{
|
||||
sb.AppendLine("<p class='muted'>Нет доступных действий</p>");
|
||||
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($"<div class='action-group'>");
|
||||
sb.AppendLine($" <h3 class='action-group-title'>{Escape(g.Key)}</h3>");
|
||||
sb.AppendLine(" <div class='actions-grid'>");
|
||||
|
||||
foreach (var a in g)
|
||||
{
|
||||
sb.AppendLine(
|
||||
$" <button type='button' class='action-btn' data-action-name='{EscapeAttr(a.Name)}'>{Escape(a.Name)}</button>"
|
||||
);
|
||||
}
|
||||
|
||||
sb.AppendLine(" </div>");
|
||||
sb.AppendLine("</div>");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string GetBoolView(Setting setting)
|
||||
{
|
||||
string name = setting.Id;
|
||||
@ -294,12 +394,10 @@ public class HtmlBuilder
|
||||
+ "<body>";
|
||||
|
||||
res += content;
|
||||
|
||||
res += "</body></html>";
|
||||
return res;
|
||||
}
|
||||
|
||||
// минимальная защита от мусора в html
|
||||
private static string Escape(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s)) return "";
|
||||
|
||||
@ -27,11 +27,16 @@ public class WebServer : MonoBehaviour
|
||||
|
||||
private string AdminPassword => AuthManager.LoadPasswordOrFallback(adminPasswordFallback, passwordFileName);
|
||||
|
||||
|
||||
public List<SettingAction> settingActions;
|
||||
|
||||
|
||||
async void Start()
|
||||
{
|
||||
// SettingsManager.Initialize();//надо будет убирать
|
||||
LoadCss();
|
||||
|
||||
settingActions = GameManager.instance.AdminPanel.GetComponent<SettingsScreen>().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, "<h1>404 Not Found</h1>", 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<SettingAction>(settingActions) : new List<SettingAction>();
|
||||
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<ActionDto>(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ using UnityEngine;
|
||||
public class SettingsScreen : MonoBehaviour
|
||||
{
|
||||
[SerializeField] RectTransform pointer;
|
||||
[SerializeField] List<SettingAction> settingActions;
|
||||
public List<SettingAction> settingActions;
|
||||
private List<Setting> settings;
|
||||
|
||||
[SerializeField] private List<string> 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;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user