Add guard choices and target 1920x1080 layout
CI / test (push) Successful in 2m34s Details

This commit is contained in:
MpakobecEx 2026-10-23 16:20:31 +05:00
parent 30aed3b25d
commit a3d2157925
7 changed files with 96 additions and 7 deletions

View File

@ -30,6 +30,8 @@ namespace Vanguard.App
Instance = this;
DontDestroyOnLoad(gameObject);
Application.targetFrameRate = 60;
if (!Application.isEditor)
Screen.SetResolution(1920, 1080, FullScreenMode.FullScreenWindow);
Loc.Initialize();
var config = new AppConfig();

View File

@ -1247,6 +1247,54 @@ namespace Vanguard.UI
UiKit.SetPreferredHeight(button, 46f);
}
private void AddGuardButton(IReadOnlyList<GuardIntent> guards)
{
if (guards == null || guards.Count == 0)
return;
var button = UiKit.CreateButton(
_intents,
"GuardAction",
"Защититься",
() => OpenGuardSelector(guards),
16);
button.interactable = !_inputLock.IsLocked;
UiKit.SetPreferredHeight(button, 46f);
}
private void OpenGuardSelector(IReadOnlyList<GuardIntent> guards)
{
var groups = guards
.GroupBy(guard => guard.PrintingId)
.ToList();
if (groups.Count == 0)
return;
if (groups.Count == 1)
{
var variants = groups[0].ToList();
if (variants.Count > 1)
ShowGuardDiscardChoice(variants);
else
OnIntentClicked(variants[0]);
return;
}
var items = groups
.Select(group => new CardActionMenuItem(
"Защититься: " + CardName(group.Key),
() =>
{
var variants = group.ToList();
if (variants.Count > 1)
ShowGuardDiscardChoice(variants);
else
OnIntentClicked(variants[0]);
}))
.ToList();
ShowChoiceMenu("Защита", items);
}
private void OpenAbilitySelector(IReadOnlyList<ActivateAbilityIntent> abilities)
{
var groups = abilities
@ -2155,8 +2203,11 @@ namespace Vanguard.UI
var legal = view.LegalIntents ?? new List<IntentPayload>();
var playable = legal.Where(intent => intent is not ConcedeIntent).ToList();
var abilities = playable.OfType<ActivateAbilityIntent>().ToList();
var guards = playable.OfType<GuardIntent>().ToList();
var board = playable
.Where(intent => IsBoardIntent(intent) && intent is not ActivateAbilityIntent)
.Where(intent => IsBoardIntent(intent)
&& intent is not ActivateAbilityIntent
&& intent is not GuardIntent)
.ToList();
var global = playable
.Where(intent => !IsBoardIntent(intent)
@ -2271,6 +2322,9 @@ namespace Vanguard.UI
if (abilities.Count > 0)
AddAbilityButton(abilities);
if (guards.Count > 0)
AddGuardButton(guards);
foreach (var intent in global)
AddIntentButton(intent);
@ -2374,7 +2428,7 @@ namespace Vanguard.UI
+ (attack.BoosterSlot >= 0 ? " + boost" : string.Empty);
case GuardIntent guard: return "Guard: " + CardName(guard.PrintingId);
case InterceptIntent intercept: return "Intercept: " + SlotName(intercept.Slot);
case PassGuardIntent _: return "Пропустить Guard";
case PassGuardIntent _: return "Не защищаться";
case ResolveTriggerIntent trigger:
return "Триггер: сила " + SlotName(trigger.PowerTarget)
+ ", эффект " + SlotName(trigger.EffectTarget);

View File

@ -70,6 +70,10 @@ namespace Vanguard.UI
["Без сброса"] = "No discard",
["Guard без сброса"] = "Guard without discard",
["Perfect Guard: сбросить "] = "Perfect Guard: discard ",
["Защититься"] = "Guard",
["Защититься: "] = "Guard: ",
["Защита"] = "Guard",
["Не защищаться"] = "Do not guard",
["Действия"] = "Actions",
["Переместить"] = "Move",
["Подробнее"] = "Details",

View File

@ -123,6 +123,26 @@ namespace Vanguard.Tests
Object.DestroyImmediate(go);
}
[Test]
public void Canvas_Reference_Resolution_Is_1920x1080()
{
var canvas = UiKit.CreateCanvas("Reference Resolution Canvas");
try
{
var scaler = canvas.GetComponent<CanvasScaler>();
Assert.IsNotNull(scaler);
Assert.AreEqual(1920f, scaler.referenceResolution.x);
Assert.AreEqual(1080f, scaler.referenceResolution.y);
}
finally
{
Object.DestroyImmediate(canvas.gameObject);
var eventSystem = Object.FindAnyObjectByType<EventSystem>();
if (eventSystem != null)
Object.DestroyImmediate(eventSystem.gameObject);
}
}
[Test]
public void Battle_Layout_Fits_Reference_Resolution()
{

View File

@ -85,7 +85,7 @@ PlayerSettings:
androidAutoRotationBehavior: 1
androidPredictiveBackSupport: 1
androidApplicationEntry: 1
defaultIsNativeResolution: 1
defaultIsNativeResolution: 0
macRetinaSupport: 1
runInBackground: 0
callOnDisableOnAssetBundleUnload: 1

View File

@ -959,7 +959,7 @@ Battle; Boot и общие сервисы остаются персистент
- Добавлен `Battle_Layout_Fits_Reference_Resolution`: проверяет блоки зон,
панель фаз и боевую плашку на 1920×1080 и отсутствие пересечений; при
другом разрешении CanvasScaler масштабирует reference-раскладку.
- Проверки: клиент EditMode **29/29**, `SCENE_SMOKE success=True`,
- Проверки: клиент EditMode **30/30**, `SCENE_SMOKE success=True`,
`PLAYMAT_SMOKE success=True cards=25 hand=5/5 fieldVisible=True`.
- Дальше: только 3D-модели/наполнение сцен, анимации retire/ghost и
декбилдер; prefab-оверлеи и раскладка закрыты.
@ -992,8 +992,12 @@ Battle; Boot и общие сервисы остаются персистент
кнопка «Активировать способность». Если целей/вариантов несколько —
отдельный overlay, затем выбор карты для сброса (без duplicate-кнопок).
- Guard: варианты Perfect Guard/сброса показываются отдельным overlay;
случайный первый интент больше не выбирается.
случайный первый интент больше не выбирается. В фазе защиты есть
«Защититься» (при наличии допустимых карт) и «Не защищаться».
- Камера боя: `(0, 4.2, -8)` → цель `(0, 0, 0)`, только наклон по X;
поле по центру, без бокового смещения.
- UI: reference-разрешение **1920×1080**, CanvasScaler `matchWidthOrHeight
= 0.5`; standalone принудительно `1920×1080 FullScreenWindow`,
`defaultIsNativeResolution = 0`.
- Проверки: сервер **141** правил + **45** серверных, `validate` и
`translations check` OK; клиент EditMode **29/29**.
`translations check` OK; клиент EditMode **30/30**.

View File

@ -36,6 +36,9 @@
- Переходы — `SceneFlow`; очередь/онбординг — оверлеи **Lobby**.
- 3D-окружение сейчас из примитивов (`SceneEnvironment`): камера, свет,
пол, тематические кубы. UI — code-generated uGUI, пока gray-box.
- Интерфейс рассчитан на reference **1920×1080** (`CanvasScaler`,
`matchWidthOrHeight = 0.5`); standalone принудительно открывается в
1920×1080 fullscreen, `defaultIsNativeResolution` выключен.
- Login: тёмный неон + 3D-карточный стол, форма по центру на
полупрозрачной панели.
- Сервисы: `ApiClient`, `AuthService`, `MetaService`, `TokenStore`,
@ -85,6 +88,8 @@
нескольких вариантах открывается overlay выбора, затем — выбор карты
для сброса. Perfect Guard также спрашивает карту для сброса отдельным
overlay, а не выбирает случайную.
- В фазе защиты доступны «Защититься» (если есть допустимые карты) и
«Не защищаться»; выбор карты защиты и сброса идёт через overlay.
- Статистика карт — отдельное UI-наложение: своя панель снизу слева,
противника — сверху справа. Блоки `Рука/Колода/Дроп/Душа/Урон/Защита/Триггер`
кликабельны и открывают общий горизонтальный `CardListView`; из списка
@ -181,7 +186,7 @@ Standalone Windows:
-executeMethod Vanguard.Editor.SceneSmoke.RunPlaymat
```
Клиентские проверки: **29/29** EditMode (сборка всех экранов, импорт арта,
Клиентские проверки: **30/30** EditMode (сборка всех экранов, импорт арта,
перевод статических/динамических строк, live-переключение языка, рендер
3D-доски с уроном и отдыхающей картой, геометрия поля, правила
перемещения, рука в поле зрения камеры, обмен колоды/дропа, триггер-зона,