chip-gameV3/Assets/Scripts/RulesPrizePanel.cs
2026-07-14 11:14:13 +03:00

480 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using PrizeSystem;
public class RulesPrizePanel : MonoBehaviour
{
[SerializeField] RectTransform ScaleContainer;
[SerializeField] GameObject IconTemplate;
[SerializeField] GameObject PointNumberTemplate;
[SerializeField] float IconSize = 100;
[SerializeField] float PointSize = 30;
[SerializeField] float SubpointSize = 15;
[SerializeField] float PointOffsetX = 100;
[SerializeField] float LinkWidth = 5;
[SerializeField] float StrokeWidth = 15;
[SerializeField] float StrokeGap = 15;
[SerializeField] float prizetextFontsize = 30;
[SerializeField] float instructionFontsize = 24;
[SerializeField] float giftImageSizeMultiplier = 1;
[SerializeField] Sprite circle;
[SerializeField] Sprite square;
[SerializeField] Sprite giftSprite;
[SerializeField] Sprite hopperSprite;
[SerializeField] Sprite marblesSprite;
[SerializeField] Sprite ticketSprite;
[SerializeField] Sprite vitrinaSprite;
[SerializeField] Sprite qrSprite;
[SerializeField] Color ScaleColor = Color.gray;
[SerializeField] Color ScaleColorHighlighted = new Color(1, .7f, 0);
private Character[] characters;
private List<Prize> prizes;
List<GameObject> generatedObjects;
List<RectTransform> generatedIcons;
List<RectTransform> generatedPoints;
List<RectTransform> generatedLinks;
List<RectTransform> generatedSubpoints;
[SerializeField] RectTransform InstructionContainer;
private void OnEnable() => GenerateScale();
private void OnDisable() => Clear();
private Sprite SpriteByType(PrizeType prizeType)
{
return prizeType switch
{
PrizeType.Hopper => hopperSprite,
PrizeType.Hopper2 => hopperSprite,
PrizeType.Ticket => ticketSprite,
PrizeType.Marbles => marblesSprite,
PrizeType.Qr => qrSprite,
PrizeType.VitrinaBox => vitrinaSprite,
_ => giftSprite
};
}
private void Clear()
{
for (int i = generatedObjects.Count - 1; i >= 0; i--)
{
Destroy(generatedObjects[i]);
}
generatedObjects.Clear();
}
private void GenerateScale()
{
characters = CharacterController.I.characters;
prizes = PrizeSerializer.LoadPrizes();
generatedObjects = new();
GenerateIcons();
GeneratePoints();
GenerateLinks();
GenerateNumbers();
GenerateSubpoints();
GenerateInstructions();
}
private void GenerateIcons()
{
generatedIcons = new();
for (int i = 0; i < characters.Length; i++)
{
RectTransform icon = Instantiate(IconTemplate, ScaleContainer).GetComponent<RectTransform>();
icon.sizeDelta = new Vector2(IconSize, IconSize);
icon.anchoredPosition = new Vector2(IconSize / 2, -IconSize / 2 - IconSize * i);
icon.GetComponent<Image>().sprite = characters[i].IconSmall;
generatedObjects.Add(icon.gameObject);
generatedIcons.Add(icon);
}
}
private void GeneratePoints()
{
List<Vector2> positions = new();
//собираем список точек куда спавнить кружки
foreach (RectTransform icon in generatedIcons)
{
positions.Add((Vector2)icon.localPosition + Vector2.right * PointOffsetX);
}
//
generatedPoints = new();
foreach (Vector2 position in positions)
{
RectTransform point = Instantiate(IconTemplate, ScaleContainer).GetComponent<RectTransform>();
point.sizeDelta = new Vector2(PointSize, PointSize);
point.localPosition = position;
Image image = point.GetComponent<Image>();
image.sprite = circle;
image.color = ScaleColor;
generatedObjects.Add(point.gameObject);
generatedPoints.Add(point);
}
}
private void GenerateLinks()
{
generatedLinks = new();
List<Vector2> LinkPositions = new();
//собираем точки
for (int i = 0; i < generatedPoints.Count - 1; i++)
{
float y = (generatedPoints[i].transform.localPosition.y + generatedPoints[i + 1].transform.localPosition.y) / 2;
LinkPositions.Add(new Vector2(generatedPoints[0].localPosition.x, y));
}
//последний уникальный
LinkPositions.Add(new Vector2(generatedPoints[^1].localPosition.x, generatedPoints[^1].transform.localPosition.y));
foreach (Vector2 position in LinkPositions)
{
RectTransform link = Instantiate(IconTemplate, ScaleContainer).GetComponent<RectTransform>();
link.sizeDelta = new Vector2(LinkWidth, IconSize);
link.localPosition = position;
Image image = link.GetComponent<Image>();
image.sprite = square;
image.color = ScaleColor;
generatedObjects.Add(link.gameObject);
generatedLinks.Add(link);
}
}
private void GenerateNumbers()
{
int i = 1;
foreach (RectTransform point in generatedPoints)
{
TextMeshProUGUI label = Instantiate(PointNumberTemplate, ScaleContainer).GetComponent<TextMeshProUGUI>();
label.text = i.ToString();
label.transform.localPosition = point.localPosition;
generatedObjects.Add(label.gameObject);
i++;
}
}
private void GenerateSubpoints()
{
generatedSubpoints = new List<RectTransform>();
List<Vector2> positions = new List<Vector2>();
foreach (RectTransform point in generatedPoints)
{
positions.Add((Vector2)point.localPosition + Vector2.down * IconSize / 2);
}
int index = 0;
foreach (Vector2 position in positions)
{
RectTransform subpoint = Instantiate(IconTemplate, ScaleContainer).GetComponent<RectTransform>();
subpoint.localPosition = position;
subpoint.sizeDelta = Vector2.one * SubpointSize;
Image image = subpoint.GetComponent<Image>();
image.sprite = circle;
bool prizedIndex = prizes[index].PrizeType != PrizeType.None;
Color color = prizedIndex ? ScaleColorHighlighted : ScaleColor;
image.color = color;
if (prizedIndex) GeneratePrizeView(position, prizes[index]);
generatedObjects.Add(subpoint.gameObject);
generatedSubpoints.Add(subpoint);
index++;
}
}
//private void GeneratePrizeView(Vector2 startPoint, Prize prize)
//{
// int strokeAmount = 5;
// int offsetX = 15;
// //генерация штрихов
// for (int i = 0; i < strokeAmount; i++)
// {
// RectTransform stroke = Instantiate(IconTemplate, ScaleContainer).GetComponent<RectTransform>();
// float x = startPoint.x + offsetX + StrokeWidth / 2 + (StrokeGap + StrokeWidth) * i;
// float y = startPoint.y;
// Vector2 position = new Vector2(x, y);
// stroke.localPosition = position;
// stroke.sizeDelta = new Vector2(StrokeWidth, LinkWidth);
// Image image = stroke.GetComponent<Image>();
// image.sprite = square;
// image.color = ScaleColor;
// generatedObjects.Add(stroke.gameObject);
// }
// //генерация текста
// Vector2 lastStrokePosition = new Vector2(x: startPoint.x + offsetX + StrokeWidth / 2 + (StrokeGap + StrokeWidth) * (strokeAmount - 1),
// y: startPoint.y);
// TextMeshProUGUI label = Instantiate(PointNumberTemplate, ScaleContainer).GetComponent<TextMeshProUGUI>();
// label.fontSize = prizetextFontsize;
// label.alignment = TextAlignmentOptions.MidlineLeft;
// //string prizeText = GetPrizeText(prize);
// string prizeText = GetPrizeText(prize);
// label.text = prizeText;
// float prizetextWidth = label.GetPreferredValues(prizeText).x;
// RectTransform rect = label.GetComponent<RectTransform>();
// Vector2 labelPosition = new Vector2(x: lastStrokePosition.x + StrokeWidth / 2 + StrokeGap + prizetextWidth / 2,
// y: lastStrokePosition.y);
// rect.localPosition = labelPosition;
// rect.sizeDelta = new Vector2(prizetextWidth, IconSize);
// generatedObjects.Add(label.gameObject);
// //генерация картинки подарка
// Vector2 giftPosition = labelPosition + Vector2.right * (IconSize + prizetextWidth) / 2;
// RectTransform gift = Instantiate(IconTemplate, ScaleContainer).GetComponent<RectTransform>();
// gift.localPosition = giftPosition;
// gift.sizeDelta = Vector2.one * IconSize;
// gift.GetComponent<Image>().sprite = giftSprite;
// generatedObjects.Add(gift.gameObject);
//}
private void GeneratePrizeView(Vector2 startPoint, Prize prize)
{
int strokeAmount = 5;
int offsetX = 15;
//генерация штрихов
for (int i = 0; i < strokeAmount; i++)
{
RectTransform stroke = Instantiate(IconTemplate, ScaleContainer).GetComponent<RectTransform>();
float x = startPoint.x + offsetX + StrokeWidth / 2 + (StrokeGap + StrokeWidth) * i;
float y = startPoint.y;
Vector2 position = new Vector2(x, y);
stroke.localPosition = position;
stroke.sizeDelta = new Vector2(StrokeWidth, LinkWidth);
Image image = stroke.GetComponent<Image>();
image.sprite = square;
image.color = ScaleColor;
generatedObjects.Add(stroke.gameObject);
}
// ПОЗИЦИЯ ПОСЛЕДНЕГО ШТРИХА
Vector2 lastStrokePosition = new Vector2(
x: startPoint.x + offsetX + StrokeWidth / 2 + (StrokeGap + StrokeWidth) * (strokeAmount - 1),
y: startPoint.y
);
// 1. СНАЧАЛА ГЕНЕРИРУЕМ ИКОНКУ ПОДАРКА (ТЕПЕРЬ СЛЕВА)
float iconX = lastStrokePosition.x + StrokeWidth / 2 + StrokeGap + IconSize / 2;
Vector2 iconPosition = new Vector2(iconX, lastStrokePosition.y);
RectTransform gift = Instantiate(IconTemplate, ScaleContainer).GetComponent<RectTransform>();
gift.localPosition = iconPosition;
gift.sizeDelta = Vector2.one * IconSize * giftImageSizeMultiplier;
gift.GetComponent<Image>().sprite = SpriteByType(prize.PrizeType);
generatedObjects.Add(gift.gameObject);
// 2. ТЕПЕРЬ ГЕНЕРИРУЕМ ТЕКСТ (СПРАВА ОТ ИКОНКИ)
TextMeshProUGUI label = Instantiate(PointNumberTemplate, ScaleContainer).GetComponent<TextMeshProUGUI>();
label.fontSize = prizetextFontsize;
label.alignment = TextAlignmentOptions.MidlineLeft; // Оставляем выравнивание влево
string prizeText = GetPrizeText(prize);
label.text = prizeText;
// Получаем ширину текста
float prizetextWidth = label.GetPreferredValues(prizeText).x;
// Рассчитываем позицию текста СПРАВА от иконки
// Текст начинается сразу после иконки + отступ
float labelX = iconPosition.x + IconSize / 2 + StrokeGap + prizetextWidth / 2;
Vector2 labelPosition = new Vector2(labelX, lastStrokePosition.y);
RectTransform rect = label.GetComponent<RectTransform>();
rect.localPosition = labelPosition;
rect.sizeDelta = new Vector2(prizetextWidth, IconSize);
generatedObjects.Add(label.gameObject);
}
private void GenerateInstructions()
{
//заполняем призы которые надо проинструктировать
HashSet<CountablePrizeType> countableTypes = new();
bool hasQr = false;
bool hasVitrina = false;
foreach (var prize in prizes)
{
if (prize is CountablePrize cp) countableTypes.Add(cp.Type);
if (prize is QrPrize) hasQr = true;
if (prize is VitrinaPrize) hasVitrina = true;
}
if (countableTypes.Count > 0)
{
string prefix = string.Join(", ", countableToString(countableTypes));
InsertBlock($"<b>{prefix}</b><br>Забирать в окне выдачи");
}
if (hasQr)
{
InsertBlock($"<b>Qr-приз</b>" +
"<br>1. После победы откроется окно с Qr-кодом" +
"<br>2. Сфотографировать окно" +
"<br>3. Сканировать Qr-код" +
"<br>4. Отправить сгенерированное сообщение администратору в мессенджере");
}
if (hasVitrina)
{
InsertBlock($"<b>Витрина</b>" +
"<br>1. После победы откроется окно выбором ячейки" +
"<br>2. Выбрать ячейку и ожидать выдачу");
}
void InsertBlock(string text)
{
RectTransform block = Instantiate(PointNumberTemplate, InstructionContainer).GetComponent<RectTransform>();
TextMeshProUGUI label = block.GetComponent<TextMeshProUGUI>();
label.text = text;
label.alignment = TextAlignmentOptions.TopLeft;
label.fontSize = instructionFontsize;
generatedObjects.Add(block.gameObject);
}
}
string[] countableToString(IEnumerable<CountablePrizeType> values)
{
List<string> result = new List<string>();
foreach (CountablePrizeType type in values)
{
if (type == CountablePrizeType.Hopper || type == CountablePrizeType.Hopper2)
{
result.Add("Маленький приз");
}
else if (type == CountablePrizeType.Marbles)
{
result.Add("Марблс");
}
else if (type == CountablePrizeType.Ticket)
{
result.Add("Билет");
}
}
return result.ToArray();
}
public static string GetPrizeText(Prize prize)
{
if (prize == null || prize is EmptyPrize)
{
return "";
}
else if (prize is CountablePrize cp)
{
return prize.PrizeType switch
{
PrizeType.Hopper => $"{cp.Amount} {Pluralize(cp.Amount, "маленький приз", "маленьких приза", "маленьких призов")}",
PrizeType.Hopper2 => $"{cp.Amount} {Pluralize(cp.Amount, "маленький приз", "маленьких приза", "маленьких призов")}",
PrizeType.Ticket => $"{cp.Amount} {Pluralize(cp.Amount, "билет", "билета", "билетов")}",
PrizeType.Marbles => $"{cp.Amount} марблс",
_ => "Приз"
};
}
else if (prize is VitrinaPrize vp)
{
return $"Витрина ячейки: {string.Join(" | ", vp.Boxes)}";
}
else if (prize is QrPrize qp)
{
return $"Qr: {qp.PrizeText}</b>";
}
return "";
}
/// <summary>
/// Возвращает правильную форму существительного для русского языка в зависимости от числа
/// </summary>
/// <param name="count">Количество предметов</param>
/// <param name="form1">Форма для 1 (именительный падеж, ед.ч.): "яблоко"</param>
/// <param name="form2">Форма для 2-4 (родительный падеж, ед.ч.): "яблока"</param>
/// <param name="form3">Форма для 5+ (родительный падеж, мн.ч.): "яблок"</param>
/// <returns>Правильная форма существительного</returns>
public static string Pluralize(int count, string form1, string form2, string form3)
{
// Приводим число к положительному виду для корректной работы с остатками
int absCount = Mathf.Abs(count);
// Определяем остатки для проверки правил
long lastTwoDigits = absCount % 100;
long lastDigit = absCount % 10;
// Особый случай: числа от 11 до 19 всегда используют форму 3
if (lastTwoDigits >= 11 && lastTwoDigits <= 19)
{
return form3;
}
// Обычные правила склонения
switch (lastDigit)
{
case 1:
return form1;
case 2:
case 3:
case 4:
return form2;
default:
return form3;
}
}
}