Commit 2a369a96 authored by Senator John's avatar Senator John 💬

Merge branch 'patch' into 'master'

Bugs Fixed

See merge request !27
parents 58147aea fc945e10
...@@ -384,7 +384,7 @@ MonoBehaviour: ...@@ -384,7 +384,7 @@ MonoBehaviour:
m_Elasticity: 0.1 m_Elasticity: 0.1
m_Inertia: 1 m_Inertia: 1
m_DecelerationRate: 0.135 m_DecelerationRate: 0.135
m_ScrollSensitivity: 1000 m_ScrollSensitivity: 10
m_Viewport: {fileID: 277287946404074816} m_Viewport: {fileID: 277287946404074816}
m_HorizontalScrollbar: {fileID: 0} m_HorizontalScrollbar: {fileID: 0}
m_VerticalScrollbar: {fileID: 277287947542810653} m_VerticalScrollbar: {fileID: 277287947542810653}
......
This diff is collapsed.
...@@ -240,24 +240,30 @@ namespace MDPro3.Duel ...@@ -240,24 +240,30 @@ namespace MDPro3.Duel
#region Mate #region Mate
var mateConfig = Config.Get(condition.ToString() + "Mate0", Program.items.mates[0].id.ToString()); var mateConfig = Config.Get(condition.ToString() + "Mate0", Program.items.mates[0].id.ToString());
if (mateConfig != Items.CODE_NONE.ToString() || deck != null) var overrideDeckAppearance = Config.GetBool("OverrideDeckAppearance", false);
var mateConfigIsNone = mateConfig == Items.CODE_NONE.ToString();
if (!mateConfigIsNone || (deck != null && !overrideDeckAppearance))
{ {
int mateCode = int.Parse(mateConfig); int mateCode = int.Parse(mateConfig);
if (deck != null && !Config.GetBool("OverrideDeckAppearance", false)) if (deck != null && !overrideDeckAppearance && !mateConfigIsNone)
mateCode = deck.Mate; mateCode = deck.Mate;
var mate = await ABLoader.LoadMateAsync(mateCode);
if (mate != null) if (mateCode != Items.CODE_NONE)
{ {
mate0 = mate; var mate = await ABLoader.LoadMateAsync(mateCode);
mate0.parent = pos_Avatar_near; if (mate != null)
mate0.gameObject.SetActive(false); {
mate0 = mate;
mate0.parent = pos_Avatar_near;
mate0.gameObject.SetActive(false);
}
} }
} }
mateConfig = Config.Get(condition.ToString() + "Mate1", Program.items.mates[0].id.ToString()); mateConfig = Config.Get(condition.ToString() + "Mate1", Program.items.mates[0].id.ToString());
if (mateConfig != Items.CODE_NONE.ToString()) if (mateConfig != Items.CODE_NONE.ToString())
{ {
var mate = await ABLoader.LoadMateAsync(int.Parse(Config.Get(condition.ToString() + "Mate1", Program.items.mates[0].id.ToString()))); var mate = await ABLoader.LoadMateAsync(int.Parse(mateConfig));
if (mate != null) if (mate != null)
{ {
mate1 = mate; mate1 = mate;
...@@ -3295,4 +3301,4 @@ namespace MDPro3.Duel ...@@ -3295,4 +3301,4 @@ namespace MDPro3.Duel
#endregion #endregion
} }
} }
\ No newline at end of file
...@@ -202,10 +202,12 @@ namespace MDPro3 ...@@ -202,10 +202,12 @@ namespace MDPro3
manager.GetElement("Defense").SetActive(true); manager.GetElement("Defense").SetActive(true);
manager.GetElement("TextDefense").SetActive(true); manager.GetElement("TextDefense").SetActive(true);
manager.GetElement<Text>("TextDefense").text = data.Defense == -2 ? "?" : data.Defense.ToString(); var defense = Card.NormalizeBattleValue(data.Defense, true);
if (data.Defense > (origin.Defense < 0 ? 0 : origin.Defense)) var originDefense = Card.NormalizeBattleValue(origin.Defense, false);
manager.GetElement<Text>("TextDefense").text = Card.FormatBattleValue(defense);
if (Card.NormalizeBattleValue(defense, false) > originDefense)
manager.GetElement<Text>("TextDefense").color = upColor; manager.GetElement<Text>("TextDefense").color = upColor;
else if (data.Defense < origin.Defense) else if (Card.NormalizeBattleValue(defense, false) < originDefense)
manager.GetElement<Text>("TextDefense").color = downColor; manager.GetElement<Text>("TextDefense").color = downColor;
else else
manager.GetElement<Text>("TextDefense").color = equalColor; manager.GetElement<Text>("TextDefense").color = equalColor;
...@@ -238,10 +240,12 @@ namespace MDPro3 ...@@ -238,10 +240,12 @@ namespace MDPro3
} }
} }
manager.GetElement<Text>("TextAttack").text = data.Attack == -2 ? "?" : data.Attack.ToString(); var attack = Card.NormalizeBattleValue(data.Attack, true);
if (data.Attack > (origin.Attack < 0 ? 0 : origin.Attack)) var originAttack = Card.NormalizeBattleValue(origin.Attack, false);
manager.GetElement<Text>("TextAttack").text = Card.FormatBattleValue(attack);
if (Card.NormalizeBattleValue(attack, false) > originAttack)
manager.GetElement<Text>("TextAttack").color = upColor; manager.GetElement<Text>("TextAttack").color = upColor;
else if (data.Attack < origin.Attack) else if (Card.NormalizeBattleValue(attack, false) < originAttack)
manager.GetElement<Text>("TextAttack").color = downColor; manager.GetElement<Text>("TextAttack").color = downColor;
else else
manager.GetElement<Text>("TextAttack").color = equalColor; manager.GetElement<Text>("TextAttack").color = equalColor;
...@@ -527,4 +531,4 @@ namespace MDPro3 ...@@ -527,4 +531,4 @@ namespace MDPro3
} }
} }
} }
\ No newline at end of file
...@@ -423,10 +423,10 @@ namespace MDPro3 ...@@ -423,10 +423,10 @@ namespace MDPro3
else if (data.Id > 0) else if (data.Id > 0)
data.CloneTo(lastValidData); data.CloneTo(lastValidData);
if (d.Attack < 0) d.Attack = Card.NormalizeBattleValue(d.Attack, false);
d.Attack = 0; d.Defense = Card.NormalizeBattleValue(d.Defense, false);
if (d.Defense < 0) d.rAttack = Card.NormalizeBattleValue(d.rAttack, false);
d.Defense = 0; d.rDefense = Card.NormalizeBattleValue(d.rDefense, false);
if (d.Id != data.Id) if (d.Id != data.Id)
{ {
...@@ -441,6 +441,8 @@ namespace MDPro3 ...@@ -441,6 +441,8 @@ namespace MDPro3
} }
} }
data = d; data = d;
if (model != null && p.InLocation(CardLocation.Hand) && !inAnimation)
RefreshHandTurnByCode();
RefreshLabel(); RefreshLabel();
UpdateExDeckTop(); UpdateExDeckTop();
} }
...@@ -1547,6 +1549,7 @@ namespace MDPro3 ...@@ -1547,6 +1549,7 @@ namespace MDPro3
Program.instance.ocgcore.SetExDeckTop(this); Program.instance.ocgcore.SetExDeckTop(this);
ShowFaceDownCardOrNot(NeedShowFaceDownCard()); ShowFaceDownCardOrNot(NeedShowFaceDownCard());
RefreshHandTurnByCode();
if (p.InLocation(CardLocation.Deck, CardLocation.Extra)) if (p.InLocation(CardLocation.Deck, CardLocation.Extra))
Program.instance.ocgcore.DuelBGManager.ResizeDecks(); Program.instance.ocgcore.DuelBGManager.ResizeDecks();
...@@ -1554,6 +1557,13 @@ namespace MDPro3 ...@@ -1554,6 +1557,13 @@ namespace MDPro3
Program.instance.ocgcore.DuelBGManager.RefreshGravesState(); Program.instance.ocgcore.DuelBGManager.RefreshGravesState();
} }
private void RefreshHandTurnByCode()
{
if (model == null || !p.InLocation(CardLocation.Hand))
return;
manager.GetElement<Transform>("Turn").localEulerAngles = new Vector3(0, 0, data.Id == 0 ? 180 : 0);
}
public Sequence StartCardSequence(Vector3 fromPosition, Vector3 fromRotation, float interval = 0f) public Sequence StartCardSequence(Vector3 fromPosition, Vector3 fromRotation, float interval = 0f)
{ {
if (model == null) if (model == null)
......
...@@ -10,6 +10,9 @@ namespace MDPro3.Duel.YGOSharp ...@@ -10,6 +10,9 @@ namespace MDPro3.Duel.YGOSharp
{ {
public class Card public class Card
{ {
public const int MAX_BATTLE_POWER_DISPLAY = 99_999_999;
public const int UNKNOWN_BATTLE_VALUE = -2;
public int Id; public int Id;
public int Ot; public int Ot;
public int Alias; public int Alias;
...@@ -229,15 +232,31 @@ namespace MDPro3.Duel.YGOSharp ...@@ -229,15 +232,31 @@ namespace MDPro3.Duel.YGOSharp
public string GetAttackString() public string GetAttackString()
{ {
return Attack == -2 ? "?" : Attack.ToString(); return FormatBattleValue(Attack);
} }
public string GetDefenseString() public string GetDefenseString()
{ {
return Defense == -2 ? "?" : Defense.ToString(); return FormatBattleValue(Defense);
}
public static int NormalizeBattleValue(int value, bool keepUnknown = true)
{
if (value == UNKNOWN_BATTLE_VALUE)
return keepUnknown ? UNKNOWN_BATTLE_VALUE : 0;
if (value < 0)
return MAX_BATTLE_POWER_DISPLAY;
if (value > MAX_BATTLE_POWER_DISPLAY)
return MAX_BATTLE_POWER_DISPLAY;
return value;
}
public static string FormatBattleValue(int value)
{
var normalized = NormalizeBattleValue(value, true);
return normalized == UNKNOWN_BATTLE_VALUE ? "?" : normalized.ToString();
} }
// Put this near other fields/helpers inside Card class:
private static readonly string PendulumSeparatorLine = new string('─', 14); private static readonly string PendulumSeparatorLine = new string('─', 14);
public string GetDescription(bool withSetName = false) public string GetDescription(bool withSetName = false)
...@@ -614,4 +633,4 @@ namespace MDPro3.Duel.YGOSharp ...@@ -614,4 +633,4 @@ namespace MDPro3.Duel.YGOSharp
#endregion #endregion
} }
} }
\ No newline at end of file
...@@ -290,7 +290,10 @@ namespace MDPro3.Duel.YGOSharp ...@@ -290,7 +290,10 @@ namespace MDPro3.Duel.YGOSharp
var ydk = GetYDK(); var ydk = GetYDK();
try try
{ {
deckName = Path.GetFileNameWithoutExtension(deckName); deckName = NormalizeDeckFileName(deckName);
if (!IsValidDeckFileName(deckName))
return false;
var path = Program.PATH_DECK + (type == string.Empty ? string.Empty : $"{type}/") + deckName + Program.EXPANSION_YDK; var path = Program.PATH_DECK + (type == string.Empty ? string.Empty : $"{type}/") + deckName + Program.EXPANSION_YDK;
var dir = Path.GetDirectoryName(path); var dir = Path.GetDirectoryName(path);
if (!Directory.Exists(dir)) if (!Directory.Exists(dir))
...@@ -308,6 +311,25 @@ namespace MDPro3.Duel.YGOSharp ...@@ -308,6 +311,25 @@ namespace MDPro3.Duel.YGOSharp
return true; return true;
} }
public static string NormalizeDeckFileName(string deckName)
{
deckName = deckName?.Trim() ?? string.Empty;
if (deckName.EndsWith(Program.EXPANSION_YDK, StringComparison.OrdinalIgnoreCase))
deckName = deckName[..^Program.EXPANSION_YDK.Length];
return deckName;
}
public static bool IsValidDeckFileName(string deckName)
{
if (string.IsNullOrWhiteSpace(deckName))
return false;
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
if (deckName.EndsWith(" ", StringComparison.Ordinal) || deckName.EndsWith(".", StringComparison.Ordinal))
return false;
#endif
return deckName.IndexOfAny(Path.GetInvalidFileNameChars()) < 0;
}
public string GetYDK() public string GetYDK()
{ {
var value = deckHint + "\r\n#main"; var value = deckHint + "\r\n#main";
......
...@@ -28,20 +28,22 @@ namespace MDPro3.Net ...@@ -28,20 +28,22 @@ namespace MDPro3.Net
private static async UniTask InitializeGenesysLflist() private static async UniTask InitializeGenesysLflist()
{ {
// Load cached/local genesys data immediately so Deck Editor works offline.
ParseGenesysLflist();
var eTag = await GetETagAsync(URL_GENESYS_LFLIST); var eTag = await GetETagAsync(URL_GENESYS_LFLIST);
if (!string.IsNullOrEmpty(eTag)) if (string.IsNullOrEmpty(eTag))
return;
var configTag = Config.Get(GetLocalETagKey(URL_GENESYS_LFLIST), Config.EMPTY_STRING);
if(!string.Equals(eTag, configTag, StringComparison.Ordinal))
{ {
var configTag = Config.Get(GetLocalETagKey(URL_GENESYS_LFLIST), Config.EMPTY_STRING); Program.Debug("Update Genesys Lflist.");
if(!string.Equals(eTag, configTag, StringComparison.Ordinal)) await DownloadGenesysLflist(eTag);
{ ParseGenesysLflist();
Program.Debug("Update Genesys Lflist.");
await DownloadGenesysLflist(eTag);
}
else
Program.Debug("Genesys Lflist do not need update.");
} }
else
ParseGenesysLflist(); Program.Debug("Genesys Lflist do not need update.");
} }
private static bool GenesysRequiresDownload() private static bool GenesysRequiresDownload()
...@@ -74,6 +76,10 @@ namespace MDPro3.Net ...@@ -74,6 +76,10 @@ namespace MDPro3.Net
private static void ParseGenesysLflist() private static void ParseGenesysLflist()
{ {
genesysBannedCards.Clear();
genesysPoints.Clear();
officialGenesysLimit = 100;
if (!File.Exists(PATH_GENESYS_LFLIST)) if (!File.Exists(PATH_GENESYS_LFLIST))
return; return;
...@@ -88,6 +94,13 @@ namespace MDPro3.Net ...@@ -88,6 +94,13 @@ namespace MDPro3.Net
if(string.IsNullOrEmpty(line) || line.StartsWith("#")) continue; if(string.IsNullOrEmpty(line) || line.StartsWith("#")) continue;
if (line.StartsWith("$")) if (line.StartsWith("$"))
{ {
// e.g. "$genesys 100"
if (line.StartsWith("$genesys", StringComparison.OrdinalIgnoreCase))
{
var limitParts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (limitParts.Length >= 2 && int.TryParse(limitParts[1], out var limit))
officialGenesysLimit = limit;
}
currentType = line; currentType = line;
continue; continue;
} }
...@@ -242,6 +255,7 @@ namespace MDPro3.Net ...@@ -242,6 +255,7 @@ namespace MDPro3.Net
public static async UniTask<string> GetETagAsync(string url) public static async UniTask<string> GetETagAsync(string url)
{ {
using var headRequest = UnityWebRequest.Head(url); using var headRequest = UnityWebRequest.Head(url);
headRequest.timeout = 8;
await headRequest.SendWebRequest(); await headRequest.SendWebRequest();
if(headRequest.result != UnityWebRequest.Result.Success) if(headRequest.result != UnityWebRequest.Result.Success)
...@@ -269,4 +283,4 @@ namespace MDPro3.Net ...@@ -269,4 +283,4 @@ namespace MDPro3.Net
public string banType; public string banType;
public int point; public int point;
} }
} }
\ No newline at end of file
...@@ -193,14 +193,14 @@ namespace MDPro3.Servant ...@@ -193,14 +193,14 @@ namespace MDPro3.Servant
duelFace1 = await Program.items.LoadConcreteItemIconAsync(Config.Get("DuelFace1", Program.items.faces[0].id.ToString()), Items.ItemType.Face, 1); duelFace1 = await Program.items.LoadConcreteItemIconAsync(Config.Get("DuelFace1", Program.items.faces[0].id.ToString()), Items.ItemType.Face, 1);
duelFace0Tag = await Program.items.LoadConcreteItemIconAsync(Config.Get("DuelFace0Tag", Program.items.faces[0].id.ToString()), Items.ItemType.Face, 2); duelFace0Tag = await Program.items.LoadConcreteItemIconAsync(Config.Get("DuelFace0Tag", Program.items.faces[0].id.ToString()), Items.ItemType.Face, 2);
duelFace1Tag = await Program.items.LoadConcreteItemIconAsync(Config.Get("DuelFace1Tag", Program.items.faces[0].id.ToString()), Items.ItemType.Face, 3); duelFace1Tag = await Program.items.LoadConcreteItemIconAsync(Config.Get("DuelFace1Tag", Program.items.faces[0].id.ToString()), Items.ItemType.Face, 3);
watchFace0 = await Program.items.LoadConcreteItemIconAsync(Config.Get("WatchFace0", Program.items.faces[0].id.ToString()), Items.ItemType.Face); watchFace0 = await Program.items.LoadConcreteItemIconAsync(Config.Get("WatchFace0", Program.items.faces[0].id.ToString()), Items.ItemType.Face, 0);
watchFace1 = await Program.items.LoadConcreteItemIconAsync(Config.Get("WatchFace1", Program.items.faces[0].id.ToString()), Items.ItemType.Face); watchFace1 = await Program.items.LoadConcreteItemIconAsync(Config.Get("WatchFace1", Program.items.faces[0].id.ToString()), Items.ItemType.Face, 1);
watchFace0Tag = await Program.items.LoadConcreteItemIconAsync(Config.Get("WatchFace0Tag", Program.items.faces[0].id.ToString()), Items.ItemType.Face); watchFace0Tag = await Program.items.LoadConcreteItemIconAsync(Config.Get("WatchFace0Tag", Program.items.faces[0].id.ToString()), Items.ItemType.Face, 2);
watchFace1Tag = await Program.items.LoadConcreteItemIconAsync(Config.Get("WatchFace1Tag", Program.items.faces[0].id.ToString()), Items.ItemType.Face); watchFace1Tag = await Program.items.LoadConcreteItemIconAsync(Config.Get("WatchFace1Tag", Program.items.faces[0].id.ToString()), Items.ItemType.Face, 3);
replayFace0 = await Program.items.LoadConcreteItemIconAsync(Config.Get("ReplayFace0", Program.items.faces[0].id.ToString()), Items.ItemType.Face); replayFace0 = await Program.items.LoadConcreteItemIconAsync(Config.Get("ReplayFace0", Program.items.faces[0].id.ToString()), Items.ItemType.Face, 0);
replayFace1 = await Program.items.LoadConcreteItemIconAsync(Config.Get("ReplayFace1", Program.items.faces[0].id.ToString()), Items.ItemType.Face); replayFace1 = await Program.items.LoadConcreteItemIconAsync(Config.Get("ReplayFace1", Program.items.faces[0].id.ToString()), Items.ItemType.Face, 1);
replayFace0Tag = await Program.items.LoadConcreteItemIconAsync(Config.Get("ReplayFace0Tag", Program.items.faces[0].id.ToString()), Items.ItemType.Face); replayFace0Tag = await Program.items.LoadConcreteItemIconAsync(Config.Get("ReplayFace0Tag", Program.items.faces[0].id.ToString()), Items.ItemType.Face, 2);
replayFace1Tag = await Program.items.LoadConcreteItemIconAsync(Config.Get("ReplayFace1Tag", Program.items.faces[0].id.ToString()), Items.ItemType.Face); replayFace1Tag = await Program.items.LoadConcreteItemIconAsync(Config.Get("ReplayFace1Tag", Program.items.faces[0].id.ToString()), Items.ItemType.Face, 3);
defaultFace0 = await Program.items.LoadConcreteItemIconAsync("1010039", Items.ItemType.Face); defaultFace0 = await Program.items.LoadConcreteItemIconAsync("1010039", Items.ItemType.Face);
defaultFace1 = await Program.items.LoadConcreteItemIconAsync("1010001", Items.ItemType.Face); defaultFace1 = await Program.items.LoadConcreteItemIconAsync("1010001", Items.ItemType.Face);
......
...@@ -1948,13 +1948,13 @@ namespace MDPro3.Servant ...@@ -1948,13 +1948,13 @@ namespace MDPro3.Servant
DuelBGManager.PlayGraveEffect(p, isIn); DuelBGManager.PlayGraveEffect(p, isIn);
} }
public int GetAllAtk(bool mySide) public long GetAllAtk(bool mySide)
{ {
int allAttack = 0; long allAttack = 0;
var monsters = GCS_GetLocationCards(mySide ? 0 : 1, (int)CardLocation.MonsterZone); var monsters = GCS_GetLocationCards(mySide ? 0 : 1, (int)CardLocation.MonsterZone);
foreach (var card in monsters) foreach (var card in monsters)
if ((card.p.position & (uint)CardPosition.FaceUpAttack) > 0) if ((card.p.position & (uint)CardPosition.FaceUpAttack) > 0)
allAttack += card.GetData().Attack; allAttack += Card.NormalizeBattleValue(card.GetData().Attack, false);
return allAttack; return allAttack;
} }
......
...@@ -165,7 +165,7 @@ namespace MDPro3 ...@@ -165,7 +165,7 @@ namespace MDPro3
MouseMovedEvent(); MouseMovedEvent();
} }
if(MoveInput != Vector2.zero) if(MoveInput != Vector2.zero && !InputFieldActivating())
{ {
if (Cursor.lockState == CursorLockMode.None) if (Cursor.lockState == CursorLockMode.None)
{ {
...@@ -456,10 +456,11 @@ namespace MDPro3 ...@@ -456,10 +456,11 @@ namespace MDPro3
if (current == null) return false; if (current == null) return false;
if (!current.TryGetComponent<Selectable>(out var selectable)) if (!current.TryGetComponent<Selectable>(out var selectable))
return false; return false;
if(selectable is TMP_InputField inputField) if (selectable is TMP_InputField tmpInputField)
return inputField.isFocused; return tmpInputField.isFocused;
else if (selectable is InputField legacyInputField)
return false; return legacyInputField.isFocused;
return false;
} }
#endregion #endregion
......
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI; using UnityEngine.UI;
namespace MDPro3.UI namespace MDPro3.UI
...@@ -17,13 +18,17 @@ namespace MDPro3.UI ...@@ -17,13 +18,17 @@ namespace MDPro3.UI
{ {
base.InitializeSelections(); base.InitializeSelections();
Program.instance.currentServant.returnAction = null; Program.instance.currentServant.returnAction = null;
var selectionButtons = new List<Button>();
for (int i = 1; i < selections.Count; i++) for (int i = 1; i < selections.Count; i++)
{ {
GameObject newSelection = Instantiate(item); GameObject newSelection = Instantiate(item);
newSelection.transform.SetParent(scrollRect.content, false); newSelection.transform.SetParent(scrollRect.content, false);
newSelection.transform.GetChild(0).GetChild(0).GetComponent<Text>().text = selections[i]; newSelection.transform.GetChild(0).GetChild(0).GetComponent<Text>().text = selections[i];
newSelection.transform.GetChild(0).name = responses[i - 1].ToString(); var buttonTransform = newSelection.transform.GetChild(0);
newSelection.transform.GetChild(0).GetComponent<Button>().onClick.AddListener(() => buttonTransform.name = responses[i - 1].ToString();
var button = buttonTransform.GetComponent<Button>();
button.onClick.AddListener(() =>
{ {
string selected = UnityEngine.EventSystems.EventSystem.current. string selected = UnityEngine.EventSystems.EventSystem.current.
currentSelectedGameObject.name; currentSelectedGameObject.name;
...@@ -35,14 +40,39 @@ namespace MDPro3.UI ...@@ -35,14 +40,39 @@ namespace MDPro3.UI
} }
Hide(); Hide();
}); });
selectionButtons.Add(button);
newSelection.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, -20 - 90 * (i - 1)); newSelection.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, -20 - 90 * (i - 1));
} }
for (int i = 0; i < selectionButtons.Count; i++)
{
var button = selectionButtons[i];
var navigation = button.navigation;
navigation.mode = Navigation.Mode.Explicit;
navigation.selectOnUp = selectionButtons[Mathf.Max(0, i - 1)];
navigation.selectOnDown = selectionButtons[Mathf.Min(selectionButtons.Count - 1, i + 1)];
navigation.selectOnLeft = button;
navigation.selectOnRight = button;
button.navigation = navigation;
}
if (scrollRect.verticalScrollbar != null)
{
var scrollbarNavigation = scrollRect.verticalScrollbar.navigation;
scrollbarNavigation.mode = Navigation.Mode.None;
scrollRect.verticalScrollbar.navigation = scrollbarNavigation;
}
scrollRect.content.sizeDelta = new Vector2(scrollRect.content.sizeDelta.x, 25 + (selections.Count - 1) * 90); scrollRect.content.sizeDelta = new Vector2(scrollRect.content.sizeDelta.x, 25 + (selections.Count - 1) * 90);
baseRect.sizeDelta = new Vector2(baseRect.sizeDelta.x, baseRect.sizeDelta = new Vector2(baseRect.sizeDelta.x,
scrollRect.content.sizeDelta.y + 50 > 800 ? scrollRect.content.sizeDelta.y + 50 > 800 ?
800 : 800 :
scrollRect.content.sizeDelta.y + 50 scrollRect.content.sizeDelta.y + 50
); );
scrollRect.verticalNormalizedPosition = 1f;
if (selectionButtons.Count > 0)
EventSystem.current.SetSelectedGameObject(selectionButtons[0].gameObject);
} }
} }
......
...@@ -2,12 +2,19 @@ using System.Collections.Generic; ...@@ -2,12 +2,19 @@ using System.Collections.Generic;
using DG.Tweening; using DG.Tweening;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using TMPro;
using YgomSystem.UI; using YgomSystem.UI;
namespace MDPro3.UI namespace MDPro3.UI
{ {
public class SelectionButton_MainMenu : SelectionButton public class SelectionButton_MainMenu : SelectionButton
{ {
private const float ArrowRestX = -5f;
private const float ArrowStartX = -262f;
private const float ArrowTextGap = 30f;
private static float s_BaseButtonWidth = -1f;
private static float s_BaseTextWidth = -1f;
protected override void Awake() protected override void Awake()
{ {
ElementsReset(); ElementsReset();
...@@ -19,6 +26,8 @@ namespace MDPro3.UI ...@@ -19,6 +26,8 @@ namespace MDPro3.UI
private void ElementsReset() private void ElementsReset()
{ {
EnsureMainMenuButtonWidth();
// Out // Out
Manager.GetElement<CanvasGroup>("Out").alpha = 1f; Manager.GetElement<CanvasGroup>("Out").alpha = 1f;
Manager.GetElement<RectTransform>("Line").localScale = Vector3.one; Manager.GetElement<RectTransform>("Line").localScale = Vector3.one;
...@@ -27,7 +36,7 @@ namespace MDPro3.UI ...@@ -27,7 +36,7 @@ namespace MDPro3.UI
Manager.GetElement<CanvasGroup>("Hover").alpha = 0f; Manager.GetElement<CanvasGroup>("Hover").alpha = 0f;
Manager.GetElement<RectTransform>("PlateTween").localScale = new Vector3(0.5f, 1f, 1f); Manager.GetElement<RectTransform>("PlateTween").localScale = new Vector3(0.5f, 1f, 1f);
Manager.GetElement<RectTransform>("HoverTextMask").offsetMax = new Vector2(-340f, 0f); Manager.GetElement<RectTransform>("HoverTextMask").offsetMax = new Vector2(-340f, 0f);
Manager.GetElement<RectTransform>("Arrow").localPosition = new Vector2(-5f, 0f); Manager.GetElement<RectTransform>("Arrow").localPosition = new Vector2(ArrowRestX, 0f);
// Cursor // Cursor
Manager.GetElement<RectTransform>("Corner").offsetMin = new Vector2(0f, 0f); Manager.GetElement<RectTransform>("Corner").offsetMin = new Vector2(0f, 0f);
...@@ -42,6 +51,7 @@ namespace MDPro3.UI ...@@ -42,6 +51,7 @@ namespace MDPro3.UI
if (hoverd) if (hoverd)
return; return;
base.HoverOn(); base.HoverOn();
EnsureMainMenuButtonWidth();
Manager.GetElement<CanvasGroup>("Out").alpha = 0f; Manager.GetElement<CanvasGroup>("Out").alpha = 0f;
...@@ -54,11 +64,73 @@ namespace MDPro3.UI ...@@ -54,11 +64,73 @@ namespace MDPro3.UI
Manager.GetElement<RectTransform>("HoverTextMask").offsetMax = new Vector2(-340f, 0f); Manager.GetElement<RectTransform>("HoverTextMask").offsetMax = new Vector2(-340f, 0f);
Manager.GetElement<RectTransform>("HoverTextMask").DOSizeDelta(Vector2.zero, 0.2f); Manager.GetElement<RectTransform>("HoverTextMask").DOSizeDelta(Vector2.zero, 0.2f);
Manager.GetElement<RectTransform>("Arrow").anchoredPosition = new Vector2(-262f, 0f); Manager.GetElement<RectTransform>("Arrow").anchoredPosition = new Vector2(ArrowStartX, 0f);
var tween2 = Manager.GetElement<RectTransform>("Arrow").DOAnchorPosX(-5f, 0.33f).SetEase(Ease.OutQuart); var tween2 = Manager.GetElement<RectTransform>("Arrow").DOAnchorPosX(ArrowRestX, 0.33f).SetEase(Ease.OutQuart);
hoverOnTweens.Add(tween2); hoverOnTweens.Add(tween2);
} }
private void EnsureMainMenuButtonWidth()
{
var selfRect = transform as RectTransform;
if (selfRect == null || selfRect.parent == null)
return;
var selfTextRect = Manager.GetElement<RectTransform>("Text");
if (s_BaseButtonWidth < 0f)
s_BaseButtonWidth = selfRect.sizeDelta.x;
if (s_BaseTextWidth < 0f && selfTextRect != null)
s_BaseTextWidth = selfTextRect.rect.width;
if (s_BaseButtonWidth <= 0f || s_BaseTextWidth <= 0f)
return;
var buttons = selfRect.parent.GetComponentsInChildren<SelectionButton_MainMenu>(true);
var maxTargetWidth = s_BaseButtonWidth;
foreach (var button in buttons)
{
if (button == null)
continue;
var buttonRect = button.transform as RectTransform;
if (buttonRect == null)
continue;
var text = button.Manager.GetElement<TextMeshProUGUI>("Text");
var textOver = button.Manager.GetElement<TextMeshProUGUI>("TextOver");
var textRect = button.Manager.GetElement<RectTransform>("Text");
if (text == null || textRect == null)
continue;
text.ForceMeshUpdate();
textOver?.ForceMeshUpdate();
var maxLabelWidth = text.preferredWidth;
if (textOver != null && textOver.preferredWidth > maxLabelWidth)
maxLabelWidth = textOver.preferredWidth;
var neededTextWidth = maxLabelWidth + ArrowTextGap;
var extraWidth = Mathf.Max(0f, neededTextWidth - s_BaseTextWidth);
var targetWidth = s_BaseButtonWidth + extraWidth;
if (targetWidth > maxTargetWidth)
maxTargetWidth = targetWidth;
}
var resolvedWidth = Mathf.Ceil(maxTargetWidth);
foreach (var button in buttons)
{
if (button == null)
continue;
var buttonRect = button.transform as RectTransform;
if (buttonRect == null)
continue;
if (Mathf.Abs(buttonRect.sizeDelta.x - resolvedWidth) < 0.01f)
continue;
buttonRect.sizeDelta = new Vector2(resolvedWidth, buttonRect.sizeDelta.y);
}
}
protected override void HoverOff(bool force = false) protected override void HoverOff(bool force = false)
{ {
base.HoverOff(); base.HoverOff();
......
...@@ -99,17 +99,31 @@ namespace MDPro3.UI ...@@ -99,17 +99,31 @@ namespace MDPro3.UI
if (index == 0) if (index == 0)
return; return;
await UniTask.WaitWhile(() => Program.instance.deckSelector.inTransition); try
{
await UniTask.WaitWhile(
() => Program.instance.deckSelector.inTransition,
cancellationToken: destroyCancellationToken);
for (int i = 0; i < transform.GetSiblingIndex(); i++) int siblingIndex = transform.GetSiblingIndex();
await UniTask.Yield(cancellationToken: destroyCancellationToken); for (int i = 0; i < siblingIndex; i++)
await UniTask.Yield(cancellationToken: destroyCancellationToken);
if (gameObject == null) if (this == null || gameObject == null)
return; return;
var sprite = await Program.items.LoadDeckCaseIconAsync(deckCase, "_L_SD");
if (this == null || gameObject == null || sprite == null)
return;
var sprite = await Program.items.LoadDeckCaseIconAsync(deckCase, "_L_SD");
if (sprite != null)
Manager.GetElement<Image>("DeckImage").sprite = sprite; Manager.GetElement<Image>("DeckImage").sprite = sprite;
}
catch (System.OperationCanceledException)
{
}
catch (MissingReferenceException)
{
}
} }
protected override async UniTask RefreshAsync() protected override async UniTask RefreshAsync()
......
...@@ -87,13 +87,31 @@ namespace MDPro3.UI ...@@ -87,13 +87,31 @@ namespace MDPro3.UI
protected override async UniTask RefreshDeckCaseAsync() protected override async UniTask RefreshDeckCaseAsync()
{ {
await UniTask.WaitWhile(() => Program.instance.deckSelector.inTransition); try
for (int i = 0; i < transform.GetSiblingIndex(); i++) {
await UniTask.Yield(); await UniTask.WaitWhile(
() => Program.instance.deckSelector.inTransition,
cancellationToken: destroyCancellationToken);
int siblingIndex = transform.GetSiblingIndex();
for (int i = 0; i < siblingIndex; i++)
await UniTask.Yield(cancellationToken: destroyCancellationToken);
if (this == null || gameObject == null)
return;
var sprite = await Program.items.LoadDeckCaseIconAsync(deckCase, "_L_SD");
if (this == null || gameObject == null || sprite == null)
return;
var sprite = await Program.items.LoadDeckCaseIconAsync(deckCase, "_L_SD");
if (sprite != null)
Manager.GetElement<Image>("DeckImage").sprite = sprite; Manager.GetElement<Image>("DeckImage").sprite = sprite;
}
catch (System.OperationCanceledException)
{
}
catch (MissingReferenceException)
{
}
} }
protected override void OnClick() protected override void OnClick()
......
...@@ -783,7 +783,8 @@ namespace MDPro3.UI ...@@ -783,7 +783,8 @@ namespace MDPro3.UI
if (condition == Condition.Editable && !deckLoaded) return false; if (condition == Condition.Editable && !deckLoaded) return false;
Deck = FromObjectDeckToCodedDeck(); Deck = FromObjectDeckToCodedDeck();
DeckFileSave(); if (!DeckFileSave())
return false;
SetCondition(Condition.Editable); SetCondition(Condition.Editable);
return true; return true;
} }
...@@ -1293,29 +1294,80 @@ namespace MDPro3.UI ...@@ -1293,29 +1294,80 @@ namespace MDPro3.UI
return DeckLocation.All; return DeckLocation.All;
} }
protected void DeckFileSave() protected bool DeckFileSave()
{ {
try try
{ {
var deckName = GetDeckName(); var deckName = MDPro3.Duel.YGOSharp.Deck.NormalizeDeckFileName(GetDeckName());
// TODO: 检查违法字符。 if (!MDPro3.Duel.YGOSharp.Deck.IsValidDeckFileName(deckName))
throw new InvalidOperationException($"Invalid deck name: \"{GetDeckName()}\"");
Deck.type = deckType; Deck.type = deckType;
Deck.Save(deckName, DateTime.UtcNow);
if (deckName != deckFileName) var oldDeckPath = GetDeckFilePath(deckFileName);
File.Delete(Program.PATH_DECK + this.deckNameWithType + Program.EXPANSION_YDK); if (!Deck.Save(deckName, DateTime.UtcNow))
throw new IOException($"Failed to save deck \"{deckName}\".");
var newDeckPath = GetDeckFilePath(deckName);
if (IsSameDeckPath(oldDeckPath, newDeckPath))
ApplyCaseOnlyRename(oldDeckPath, newDeckPath);
else if (File.Exists(oldDeckPath))
File.Delete(oldDeckPath);
deckFileName = deckName; deckFileName = deckName;
deckNameWithType = deckType == string.Empty ? string.Empty : $"/{deckType}" + deckName; deckNameWithType = deckType == string.Empty ? deckName : $"{deckType}/{deckName}";
InputDeckName.text = deckFileName;
TextDeckName.text = deckFileName;
MessageManager.Toast(InterString.Get("本地卡组「[?]」已保存。", deckName)); MessageManager.Toast(InterString.Get("本地卡组「[?]」已保存。", deckName));
Config.SetConfigDeck(deckName, true); Config.SetConfigDeck(deckName, true);
SetDirty(false); SetDirty(false);
return true;
} }
catch (Exception e) catch (Exception e)
{ {
MessageManager.Toast(InterString.Get("保存失败!")); MessageManager.Toast(InterString.Get("保存失败!"));
MessageManager.Cast(e.Message); MessageManager.Cast(e.Message);
return false;
} }
} }
private string GetDeckFilePath(string deckName)
{
return Program.PATH_DECK + (deckType == string.Empty ? string.Empty : $"{deckType}/")
+ deckName + Program.EXPANSION_YDK;
}
private static bool IsSameDeckPath(string leftPath, string rightPath)
{
var fullLeftPath = Path.GetFullPath(leftPath);
var fullRightPath = Path.GetFullPath(rightPath);
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
return string.Equals(fullLeftPath, fullRightPath, StringComparison.OrdinalIgnoreCase);
#else
return string.Equals(fullLeftPath, fullRightPath, StringComparison.Ordinal);
#endif
}
private static void ApplyCaseOnlyRename(string oldPath, string newPath)
{
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
var fullOldPath = Path.GetFullPath(oldPath);
var fullNewPath = Path.GetFullPath(newPath);
if (string.Equals(fullOldPath, fullNewPath, StringComparison.Ordinal))
return;
if (!string.Equals(fullOldPath, fullNewPath, StringComparison.OrdinalIgnoreCase))
return;
if (!File.Exists(fullOldPath))
return;
var directory = Path.GetDirectoryName(fullOldPath);
var tempPath = Path.Combine(directory ?? Program.PATH_DECK, $"__mdpro3_casefix_{Guid.NewGuid():N}.tmp");
File.Move(fullOldPath, tempPath);
File.Move(tempPath, fullNewPath);
#endif
}
#region Pickup #region Pickup
private void PrePick() private void PrePick()
......
...@@ -311,6 +311,11 @@ namespace MDPro3.UI ...@@ -311,6 +311,11 @@ namespace MDPro3.UI
public static PlayerPosition GetPlayerPosition(int player) public static PlayerPosition GetPlayerPosition(int player)
{ {
player = GetRoomPlayerIndex(player); player = GetRoomPlayerIndex(player);
bool spectatorInRoomLobby =
RoomServant.SelfType == 7
&& Program.instance.room != null
&& Program.instance.room.showing
&& !Program.instance.ocgcore.showing;
PlayerPosition position; PlayerPosition position;
if (player < 4) if (player < 4)
{ {
...@@ -326,9 +331,9 @@ namespace MDPro3.UI ...@@ -326,9 +331,9 @@ namespace MDPro3.UI
else else
{ {
if (player == 0) if (player == 0)
position = PlayerPosition.WatchMe; position = spectatorInRoomLobby ? PlayerPosition.Me : PlayerPosition.WatchMe;
else else
position = PlayerPosition.WatchOp; position = spectatorInRoomLobby ? PlayerPosition.Op : PlayerPosition.WatchOp;
} }
} }
else else
...@@ -350,13 +355,13 @@ namespace MDPro3.UI ...@@ -350,13 +355,13 @@ namespace MDPro3.UI
else else
{ {
if (player == 0) if (player == 0)
position = PlayerPosition.WatchMe; position = spectatorInRoomLobby ? PlayerPosition.Me : PlayerPosition.WatchMe;
else if (player == 1) else if (player == 1)
position = PlayerPosition.WatchMyTag; position = spectatorInRoomLobby ? PlayerPosition.MyTag : PlayerPosition.WatchMyTag;
else if (player == 2) else if (player == 2)
position = PlayerPosition.WatchOp; position = spectatorInRoomLobby ? PlayerPosition.Op : PlayerPosition.WatchOp;
else else
position = PlayerPosition.WatchOpTag; position = spectatorInRoomLobby ? PlayerPosition.OpTag : PlayerPosition.WatchOpTag;
} }
} }
} }
......
...@@ -21,6 +21,7 @@ namespace MDPro3.Utility ...@@ -21,6 +21,7 @@ namespace MDPro3.Utility
private static readonly ConcurrentDictionary<int, CacheEntry> cachedCards = new(); private static readonly ConcurrentDictionary<int, CacheEntry> cachedCards = new();
private static readonly ConcurrentDictionary<int, Texture2D> cachedCardNames = new(); private static readonly ConcurrentDictionary<int, Texture2D> cachedCardNames = new();
private static readonly ConcurrentDictionary<int, Texture> cachedVideoCards = new(); private static readonly ConcurrentDictionary<int, Texture> cachedVideoCards = new();
private static readonly ConcurrentDictionary<int, byte> knownOverFrameArts = new();
private static readonly ConcurrentDictionary<int, SemaphoreSlim> artLoadingLocks = new(); private static readonly ConcurrentDictionary<int, SemaphoreSlim> artLoadingLocks = new();
private static readonly ConcurrentDictionary<int, SemaphoreSlim> cardLoadingLocks = new(); private static readonly ConcurrentDictionary<int, SemaphoreSlim> cardLoadingLocks = new();
...@@ -86,7 +87,7 @@ namespace MDPro3.Utility ...@@ -86,7 +87,7 @@ namespace MDPro3.Utility
Interlocked.Increment(ref entry.ReferenceCount); Interlocked.Increment(ref entry.ReferenceCount);
entry.IsPersistent |= persistent; entry.IsPersistent |= persistent;
} }
else else if (!HasOverFrameArtFile(code))
Debug.LogError($"Art texture is null for code {code}"); Debug.LogError($"Art texture is null for code {code}");
return entry.Texture; return entry.Texture;
...@@ -111,7 +112,7 @@ namespace MDPro3.Utility ...@@ -111,7 +112,7 @@ namespace MDPro3.Utility
cachedArts.TryRemove(code, out _); cachedArts.TryRemove(code, out _);
throw ex; throw ex;
} }
if (newEntry.Texture == null) if (newEntry.Texture == null && !HasOverFrameArtFile(code))
Debug.LogError($"{code} art is null"); Debug.LogError($"{code} art is null");
newEntry.LoadingTask = null; newEntry.LoadingTask = null;
...@@ -255,6 +256,7 @@ namespace MDPro3.Utility ...@@ -255,6 +256,7 @@ namespace MDPro3.Utility
cachedCardNames.Clear(); cachedCardNames.Clear();
ClearArtVideos(); ClearArtVideos();
knownOverFrameArts.Clear();
} }
#endregion #endregion
...@@ -290,7 +292,7 @@ namespace MDPro3.Utility ...@@ -290,7 +292,7 @@ namespace MDPro3.Utility
} }
if (art == null) if (art == null)
{ {
lastCardFoundArt = false; lastCardFoundArt = HasOverFrameArtFile(code);
return null; return null;
} }
...@@ -354,7 +356,8 @@ namespace MDPro3.Utility ...@@ -354,7 +356,8 @@ namespace MDPro3.Utility
if (art == null) if (art == null)
{ {
Debug.LogError($"Get null from ArtLoad for Card {data.Id}:"); if (!HasOverFrameArtFile(data.Id))
Debug.LogError($"Get null from ArtLoad for Card {data.Id}:");
art = TextureManager.container.unknownArt.texture; art = TextureManager.container.unknownArt.texture;
} }
if (!Program.instance.cardRenderer.RenderCard(code, art)) if (!Program.instance.cardRenderer.RenderCard(code, art))
...@@ -397,7 +400,8 @@ namespace MDPro3.Utility ...@@ -397,7 +400,8 @@ namespace MDPro3.Utility
if (art == null) if (art == null)
{ {
Debug.LogError($"Get null from ArtLoad for Card {data.Id}:"); if (!HasOverFrameArtFile(data.Id))
Debug.LogError($"Get null from ArtLoad for Card {data.Id}:");
art = TextureManager.container.unknownArt.texture; art = TextureManager.container.unknownArt.texture;
} }
...@@ -559,6 +563,30 @@ namespace MDPro3.Utility ...@@ -559,6 +563,30 @@ namespace MDPro3.Utility
#region Art File List Cache #region Art File List Cache
private static bool HasOverFrameArtFile(int code)
{
if (knownOverFrameArts.ContainsKey(code))
return true;
var fileName = code + Program.EXPANSION_PNG;
#if !UNITY_EDITOR && (UNITY_ANDROID || UNITY_IOS)
var overFramePath = Path.Combine(Application.persistentDataPath, "Picture", "OverFrame", fileName);
var overframePath = Path.Combine(Application.persistentDataPath, "Picture", "Overframe", fileName);
#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_STANDALONE_LINUX
var overFramePath = Path.Combine(Environment.CurrentDirectory, "Picture", "OverFrame", fileName);
var overframePath = Path.Combine(Environment.CurrentDirectory, "Picture", "Overframe", fileName);
#else
var overFramePath = Path.Combine(Environment.CurrentDirectory, "Picture", "OverFrame", fileName);
var overframePath = Path.Combine(Environment.CurrentDirectory, "Picture", "Overframe", fileName);
#endif
var exists = File.Exists(overFramePath) || File.Exists(overframePath);
if (exists)
knownOverFrameArts.TryAdd(code, 0);
return exists;
}
private static readonly List<int> artFileList = new(); private static readonly List<int> artFileList = new();
private static readonly Dictionary<int, string> artAltFileList = new(); private static readonly Dictionary<int, string> artAltFileList = new();
private static bool artFileListInitialized; private static bool artFileListInitialized;
...@@ -688,4 +716,4 @@ namespace MDPro3.Utility ...@@ -688,4 +716,4 @@ namespace MDPro3.Utility
#endregion #endregion
} }
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment