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:
m_Elasticity: 0.1
m_Inertia: 1
m_DecelerationRate: 0.135
m_ScrollSensitivity: 1000
m_ScrollSensitivity: 10
m_Viewport: {fileID: 277287946404074816}
m_HorizontalScrollbar: {fileID: 0}
m_VerticalScrollbar: {fileID: 277287947542810653}
......
This diff is collapsed.
......@@ -240,11 +240,16 @@ namespace MDPro3.Duel
#region Mate
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);
if (deck != null && !Config.GetBool("OverrideDeckAppearance", false))
if (deck != null && !overrideDeckAppearance && !mateConfigIsNone)
mateCode = deck.Mate;
if (mateCode != Items.CODE_NONE)
{
var mate = await ABLoader.LoadMateAsync(mateCode);
if (mate != null)
{
......@@ -253,11 +258,12 @@ namespace MDPro3.Duel
mate0.gameObject.SetActive(false);
}
}
}
mateConfig = Config.Get(condition.ToString() + "Mate1", Program.items.mates[0].id.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)
{
mate1 = mate;
......
......@@ -202,10 +202,12 @@ namespace MDPro3
manager.GetElement("Defense").SetActive(true);
manager.GetElement("TextDefense").SetActive(true);
manager.GetElement<Text>("TextDefense").text = data.Defense == -2 ? "?" : data.Defense.ToString();
if (data.Defense > (origin.Defense < 0 ? 0 : origin.Defense))
var defense = Card.NormalizeBattleValue(data.Defense, true);
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;
else if (data.Defense < origin.Defense)
else if (Card.NormalizeBattleValue(defense, false) < originDefense)
manager.GetElement<Text>("TextDefense").color = downColor;
else
manager.GetElement<Text>("TextDefense").color = equalColor;
......@@ -238,10 +240,12 @@ namespace MDPro3
}
}
manager.GetElement<Text>("TextAttack").text = data.Attack == -2 ? "?" : data.Attack.ToString();
if (data.Attack > (origin.Attack < 0 ? 0 : origin.Attack))
var attack = Card.NormalizeBattleValue(data.Attack, true);
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;
else if (data.Attack < origin.Attack)
else if (Card.NormalizeBattleValue(attack, false) < originAttack)
manager.GetElement<Text>("TextAttack").color = downColor;
else
manager.GetElement<Text>("TextAttack").color = equalColor;
......
......@@ -423,10 +423,10 @@ namespace MDPro3
else if (data.Id > 0)
data.CloneTo(lastValidData);
if (d.Attack < 0)
d.Attack = 0;
if (d.Defense < 0)
d.Defense = 0;
d.Attack = Card.NormalizeBattleValue(d.Attack, false);
d.Defense = Card.NormalizeBattleValue(d.Defense, false);
d.rAttack = Card.NormalizeBattleValue(d.rAttack, false);
d.rDefense = Card.NormalizeBattleValue(d.rDefense, false);
if (d.Id != data.Id)
{
......@@ -441,6 +441,8 @@ namespace MDPro3
}
}
data = d;
if (model != null && p.InLocation(CardLocation.Hand) && !inAnimation)
RefreshHandTurnByCode();
RefreshLabel();
UpdateExDeckTop();
}
......@@ -1547,6 +1549,7 @@ namespace MDPro3
Program.instance.ocgcore.SetExDeckTop(this);
ShowFaceDownCardOrNot(NeedShowFaceDownCard());
RefreshHandTurnByCode();
if (p.InLocation(CardLocation.Deck, CardLocation.Extra))
Program.instance.ocgcore.DuelBGManager.ResizeDecks();
......@@ -1554,6 +1557,13 @@ namespace MDPro3
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)
{
if (model == null)
......
......@@ -10,6 +10,9 @@ namespace MDPro3.Duel.YGOSharp
{
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 Ot;
public int Alias;
......@@ -229,15 +232,31 @@ namespace MDPro3.Duel.YGOSharp
public string GetAttackString()
{
return Attack == -2 ? "?" : Attack.ToString();
return FormatBattleValue(Attack);
}
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);
public string GetDescription(bool withSetName = false)
......
......@@ -290,7 +290,10 @@ namespace MDPro3.Duel.YGOSharp
var ydk = GetYDK();
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 dir = Path.GetDirectoryName(path);
if (!Directory.Exists(dir))
......@@ -308,6 +311,25 @@ namespace MDPro3.Duel.YGOSharp
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()
{
var value = deckHint + "\r\n#main";
......
......@@ -28,22 +28,24 @@ namespace MDPro3.Net
private static async UniTask InitializeGenesysLflist()
{
// Load cached/local genesys data immediately so Deck Editor works offline.
ParseGenesysLflist();
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))
{
Program.Debug("Update Genesys Lflist.");
await DownloadGenesysLflist(eTag);
ParseGenesysLflist();
}
else
Program.Debug("Genesys Lflist do not need update.");
}
ParseGenesysLflist();
}
private static bool GenesysRequiresDownload()
{
if (!File.Exists(PATH_GENESYS_LFLIST))
......@@ -74,6 +76,10 @@ namespace MDPro3.Net
private static void ParseGenesysLflist()
{
genesysBannedCards.Clear();
genesysPoints.Clear();
officialGenesysLimit = 100;
if (!File.Exists(PATH_GENESYS_LFLIST))
return;
......@@ -88,6 +94,13 @@ namespace MDPro3.Net
if(string.IsNullOrEmpty(line) || line.StartsWith("#")) continue;
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;
continue;
}
......@@ -242,6 +255,7 @@ namespace MDPro3.Net
public static async UniTask<string> GetETagAsync(string url)
{
using var headRequest = UnityWebRequest.Head(url);
headRequest.timeout = 8;
await headRequest.SendWebRequest();
if(headRequest.result != UnityWebRequest.Result.Success)
......
......@@ -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);
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);
watchFace0 = await Program.items.LoadConcreteItemIconAsync(Config.Get("WatchFace0", 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);
watchFace0Tag = await Program.items.LoadConcreteItemIconAsync(Config.Get("WatchFace0Tag", 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);
replayFace0 = await Program.items.LoadConcreteItemIconAsync(Config.Get("ReplayFace0", 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);
replayFace0Tag = await Program.items.LoadConcreteItemIconAsync(Config.Get("ReplayFace0Tag", 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);
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, 1);
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, 3);
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, 1);
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, 3);
defaultFace0 = await Program.items.LoadConcreteItemIconAsync("1010039", Items.ItemType.Face);
defaultFace1 = await Program.items.LoadConcreteItemIconAsync("1010001", Items.ItemType.Face);
......
......@@ -1948,13 +1948,13 @@ namespace MDPro3.Servant
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);
foreach (var card in monsters)
if ((card.p.position & (uint)CardPosition.FaceUpAttack) > 0)
allAttack += card.GetData().Attack;
allAttack += Card.NormalizeBattleValue(card.GetData().Attack, false);
return allAttack;
}
......
......@@ -165,7 +165,7 @@ namespace MDPro3
MouseMovedEvent();
}
if(MoveInput != Vector2.zero)
if(MoveInput != Vector2.zero && !InputFieldActivating())
{
if (Cursor.lockState == CursorLockMode.None)
{
......@@ -456,9 +456,10 @@ namespace MDPro3
if (current == null) return false;
if (!current.TryGetComponent<Selectable>(out var selectable))
return false;
if(selectable is TMP_InputField inputField)
return inputField.isFocused;
else
if (selectable is TMP_InputField tmpInputField)
return tmpInputField.isFocused;
if (selectable is InputField legacyInputField)
return legacyInputField.isFocused;
return false;
}
......
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace MDPro3.UI
......@@ -17,13 +18,17 @@ namespace MDPro3.UI
{
base.InitializeSelections();
Program.instance.currentServant.returnAction = null;
var selectionButtons = new List<Button>();
for (int i = 1; i < selections.Count; i++)
{
GameObject newSelection = Instantiate(item);
newSelection.transform.SetParent(scrollRect.content, false);
newSelection.transform.GetChild(0).GetChild(0).GetComponent<Text>().text = selections[i];
newSelection.transform.GetChild(0).name = responses[i - 1].ToString();
newSelection.transform.GetChild(0).GetComponent<Button>().onClick.AddListener(() =>
var buttonTransform = newSelection.transform.GetChild(0);
buttonTransform.name = responses[i - 1].ToString();
var button = buttonTransform.GetComponent<Button>();
button.onClick.AddListener(() =>
{
string selected = UnityEngine.EventSystems.EventSystem.current.
currentSelectedGameObject.name;
......@@ -35,14 +40,39 @@ namespace MDPro3.UI
}
Hide();
});
selectionButtons.Add(button);
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);
baseRect.sizeDelta = new Vector2(baseRect.sizeDelta.x,
scrollRect.content.sizeDelta.y + 50 > 800 ?
800 :
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;
using DG.Tweening;
using UnityEngine;
using UnityEngine.EventSystems;
using TMPro;
using YgomSystem.UI;
namespace MDPro3.UI
{
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()
{
ElementsReset();
......@@ -19,6 +26,8 @@ namespace MDPro3.UI
private void ElementsReset()
{
EnsureMainMenuButtonWidth();
// Out
Manager.GetElement<CanvasGroup>("Out").alpha = 1f;
Manager.GetElement<RectTransform>("Line").localScale = Vector3.one;
......@@ -27,7 +36,7 @@ namespace MDPro3.UI
Manager.GetElement<CanvasGroup>("Hover").alpha = 0f;
Manager.GetElement<RectTransform>("PlateTween").localScale = new Vector3(0.5f, 1f, 1f);
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
Manager.GetElement<RectTransform>("Corner").offsetMin = new Vector2(0f, 0f);
......@@ -42,6 +51,7 @@ namespace MDPro3.UI
if (hoverd)
return;
base.HoverOn();
EnsureMainMenuButtonWidth();
Manager.GetElement<CanvasGroup>("Out").alpha = 0f;
......@@ -54,11 +64,73 @@ namespace MDPro3.UI
Manager.GetElement<RectTransform>("HoverTextMask").offsetMax = new Vector2(-340f, 0f);
Manager.GetElement<RectTransform>("HoverTextMask").DOSizeDelta(Vector2.zero, 0.2f);
Manager.GetElement<RectTransform>("Arrow").anchoredPosition = new Vector2(-262f, 0f);
var tween2 = Manager.GetElement<RectTransform>("Arrow").DOAnchorPosX(-5f, 0.33f).SetEase(Ease.OutQuart);
Manager.GetElement<RectTransform>("Arrow").anchoredPosition = new Vector2(ArrowStartX, 0f);
var tween2 = Manager.GetElement<RectTransform>("Arrow").DOAnchorPosX(ArrowRestX, 0.33f).SetEase(Ease.OutQuart);
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)
{
base.HoverOff();
......
......@@ -99,18 +99,32 @@ namespace MDPro3.UI
if (index == 0)
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();
for (int i = 0; i < siblingIndex; i++)
await UniTask.Yield(cancellationToken: destroyCancellationToken);
if (gameObject == null)
if (this == null || gameObject == null)
return;
var sprite = await Program.items.LoadDeckCaseIconAsync(deckCase, "_L_SD");
if (sprite != null)
if (this == null || gameObject == null || sprite == null)
return;
Manager.GetElement<Image>("DeckImage").sprite = sprite;
}
catch (System.OperationCanceledException)
{
}
catch (MissingReferenceException)
{
}
}
protected override async UniTask RefreshAsync()
{
......
......@@ -87,14 +87,32 @@ namespace MDPro3.UI
protected override async UniTask RefreshDeckCaseAsync()
{
await UniTask.WaitWhile(() => Program.instance.deckSelector.inTransition);
for (int i = 0; i < transform.GetSiblingIndex(); i++)
await UniTask.Yield();
try
{
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 (sprite != null)
if (this == null || gameObject == null || sprite == null)
return;
Manager.GetElement<Image>("DeckImage").sprite = sprite;
}
catch (System.OperationCanceledException)
{
}
catch (MissingReferenceException)
{
}
}
protected override void OnClick()
{
......
......@@ -783,7 +783,8 @@ namespace MDPro3.UI
if (condition == Condition.Editable && !deckLoaded) return false;
Deck = FromObjectDeckToCodedDeck();
DeckFileSave();
if (!DeckFileSave())
return false;
SetCondition(Condition.Editable);
return true;
}
......@@ -1293,29 +1294,80 @@ namespace MDPro3.UI
return DeckLocation.All;
}
protected void DeckFileSave()
protected bool DeckFileSave()
{
try
{
var deckName = GetDeckName();
// TODO: 检查违法字符。
var deckName = MDPro3.Duel.YGOSharp.Deck.NormalizeDeckFileName(GetDeckName());
if (!MDPro3.Duel.YGOSharp.Deck.IsValidDeckFileName(deckName))
throw new InvalidOperationException($"Invalid deck name: \"{GetDeckName()}\"");
Deck.type = deckType;
Deck.Save(deckName, DateTime.UtcNow);
if (deckName != deckFileName)
File.Delete(Program.PATH_DECK + this.deckNameWithType + Program.EXPANSION_YDK);
var oldDeckPath = GetDeckFilePath(deckFileName);
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;
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));
Config.SetConfigDeck(deckName, true);
SetDirty(false);
return true;
}
catch (Exception e)
{
MessageManager.Toast(InterString.Get("保存失败!"));
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
private void PrePick()
......
......@@ -311,6 +311,11 @@ namespace MDPro3.UI
public static PlayerPosition GetPlayerPosition(int player)
{
player = GetRoomPlayerIndex(player);
bool spectatorInRoomLobby =
RoomServant.SelfType == 7
&& Program.instance.room != null
&& Program.instance.room.showing
&& !Program.instance.ocgcore.showing;
PlayerPosition position;
if (player < 4)
{
......@@ -326,9 +331,9 @@ namespace MDPro3.UI
else
{
if (player == 0)
position = PlayerPosition.WatchMe;
position = spectatorInRoomLobby ? PlayerPosition.Me : PlayerPosition.WatchMe;
else
position = PlayerPosition.WatchOp;
position = spectatorInRoomLobby ? PlayerPosition.Op : PlayerPosition.WatchOp;
}
}
else
......@@ -350,13 +355,13 @@ namespace MDPro3.UI
else
{
if (player == 0)
position = PlayerPosition.WatchMe;
position = spectatorInRoomLobby ? PlayerPosition.Me : PlayerPosition.WatchMe;
else if (player == 1)
position = PlayerPosition.WatchMyTag;
position = spectatorInRoomLobby ? PlayerPosition.MyTag : PlayerPosition.WatchMyTag;
else if (player == 2)
position = PlayerPosition.WatchOp;
position = spectatorInRoomLobby ? PlayerPosition.Op : PlayerPosition.WatchOp;
else
position = PlayerPosition.WatchOpTag;
position = spectatorInRoomLobby ? PlayerPosition.OpTag : PlayerPosition.WatchOpTag;
}
}
}
......
......@@ -21,6 +21,7 @@ namespace MDPro3.Utility
private static readonly ConcurrentDictionary<int, CacheEntry> cachedCards = new();
private static readonly ConcurrentDictionary<int, Texture2D> cachedCardNames = 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> cardLoadingLocks = new();
......@@ -86,7 +87,7 @@ namespace MDPro3.Utility
Interlocked.Increment(ref entry.ReferenceCount);
entry.IsPersistent |= persistent;
}
else
else if (!HasOverFrameArtFile(code))
Debug.LogError($"Art texture is null for code {code}");
return entry.Texture;
......@@ -111,7 +112,7 @@ namespace MDPro3.Utility
cachedArts.TryRemove(code, out _);
throw ex;
}
if (newEntry.Texture == null)
if (newEntry.Texture == null && !HasOverFrameArtFile(code))
Debug.LogError($"{code} art is null");
newEntry.LoadingTask = null;
......@@ -255,6 +256,7 @@ namespace MDPro3.Utility
cachedCardNames.Clear();
ClearArtVideos();
knownOverFrameArts.Clear();
}
#endregion
......@@ -290,7 +292,7 @@ namespace MDPro3.Utility
}
if (art == null)
{
lastCardFoundArt = false;
lastCardFoundArt = HasOverFrameArtFile(code);
return null;
}
......@@ -354,6 +356,7 @@ namespace MDPro3.Utility
if (art == null)
{
if (!HasOverFrameArtFile(data.Id))
Debug.LogError($"Get null from ArtLoad for Card {data.Id}:");
art = TextureManager.container.unknownArt.texture;
}
......@@ -397,6 +400,7 @@ namespace MDPro3.Utility
if (art == null)
{
if (!HasOverFrameArtFile(data.Id))
Debug.LogError($"Get null from ArtLoad for Card {data.Id}:");
art = TextureManager.container.unknownArt.texture;
}
......@@ -559,6 +563,30 @@ namespace MDPro3.Utility
#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 Dictionary<int, string> artAltFileList = new();
private static bool artFileListInitialized;
......
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