295 lines
9.0 KiB
C#
295 lines
9.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using AYellowpaper.SerializedCollections;
|
|
using BriarQueen.Data.Identifiers;
|
|
using BriarQueen.Data.IO.Saves;
|
|
using BriarQueen.Framework.Events.UI;
|
|
using BriarQueen.Framework.Managers.Hints.Data;
|
|
using BriarQueen.Framework.Managers.Levels.Data;
|
|
using BriarQueen.Framework.Services.Puzzles.Base;
|
|
using BriarQueen.Game.Levels.ChapterOne.LaxleyHouse.FireplaceLockbox;
|
|
using Cysharp.Threading.Tasks;
|
|
using MemoryPack;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using Random = UnityEngine.Random;
|
|
|
|
namespace BriarQueen.Game.Levels.ChapterOne.LaxleyHouse
|
|
{
|
|
[Serializable]
|
|
[MemoryPackable]
|
|
public partial struct FireplaceLockboxPuzzleState
|
|
{
|
|
public int SolutionSlotOne;
|
|
public int SolutionSlotTwo;
|
|
public int SolutionSlotThree;
|
|
|
|
public int SlotOneValue;
|
|
public int SlotTwoValue;
|
|
public int SlotThreeValue;
|
|
}
|
|
|
|
public class FireplaceLockboxBasePuzzle : BasePuzzle, IPuzzleStateful
|
|
{
|
|
private const int MinDigit = 1;
|
|
private const int MaxDigit = 9;
|
|
|
|
public override string PuzzleID => PuzzleIdentifiers.AllPuzzles[PuzzleKey.FireplaceLockboxPuzzle];
|
|
public override string LevelName => "Fireplace Lockbox";
|
|
public override Dictionary<int, BaseHint> Hints { get; }
|
|
|
|
[Header("Components")]
|
|
[SerializeField]
|
|
[SerializedDictionary(keyName: "Lockbox Number", valueName: "Number Sprite")]
|
|
private SerializedDictionary<int, Sprite> _numberSprites = new();
|
|
|
|
[Header("Puzzle Slots")]
|
|
[SerializeField]
|
|
private List<LockboxSlot> _lockboxSlots = new();
|
|
|
|
[Header("Level State")]
|
|
[SerializeField]
|
|
private Image _backgroundImage;
|
|
|
|
[SerializeField]
|
|
private Sprite _lockBoxOpenSprite;
|
|
|
|
[SerializeField]
|
|
private List<BaseItem> _lockboxItems = new();
|
|
|
|
private int _solutionSlotOne = -1;
|
|
private int _solutionSlotTwo = -1;
|
|
private int _solutionSlotThree = -1;
|
|
|
|
public bool IsCompleted { get; private set; }
|
|
|
|
private void Awake()
|
|
{
|
|
InitializeSlots();
|
|
}
|
|
|
|
protected override async UniTask PostLoadInternal()
|
|
{
|
|
if (SaveManager.GetLevelFlag(LevelFlag.LaxleyLockboxOpened))
|
|
{
|
|
DisableSlots();
|
|
await OpenLockbox();
|
|
}
|
|
}
|
|
|
|
public override async UniTask CompletePuzzle()
|
|
{
|
|
if (SaveManager.GetLevelFlag(LevelFlag.LaxleyLockboxOpened))
|
|
return;
|
|
|
|
IsCompleted = true;
|
|
|
|
SaveManager.SetLevelFlag(LevelFlag.LaxleyLockboxOpened, true);
|
|
SaveManager.SetPuzzleCompleted(PuzzleKey.FireplaceLockboxPuzzle, true);
|
|
|
|
DisableSlots();
|
|
|
|
EventCoordinator.Publish(new FadeEvent(false, 0.5f));
|
|
AudioManager.Play(AudioNameIdentifiers.Get(SFXKey.LockboxOpening));
|
|
|
|
await OpenLockbox();
|
|
|
|
EventCoordinator.Publish(new FadeEvent(true, 0.5f));
|
|
}
|
|
|
|
private async UniTask OpenLockbox()
|
|
{
|
|
if (_backgroundImage != null)
|
|
_backgroundImage.sprite = _lockBoxOpenSprite;
|
|
|
|
await GenerateLockboxItems();
|
|
}
|
|
|
|
private async UniTask GenerateLockboxItems()
|
|
{
|
|
foreach (var item in _lockboxItems)
|
|
{
|
|
if (item == null)
|
|
continue;
|
|
|
|
if (SaveManager.CurrentSave.CollectedItems.All(x => x.UniqueIdentifier != item.ItemData.UniqueID))
|
|
{
|
|
item.CanvasGroup.alpha = 1f;
|
|
item.CanvasGroup.blocksRaycasts = true;
|
|
item.CanvasGroup.interactable = true;
|
|
}
|
|
else
|
|
{
|
|
await DestructionService.Destroy(item.gameObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
public UniTask<byte[]> CaptureState()
|
|
{
|
|
var state = new FireplaceLockboxPuzzleState
|
|
{
|
|
SolutionSlotOne = _solutionSlotOne,
|
|
SolutionSlotTwo = _solutionSlotTwo,
|
|
SolutionSlotThree = _solutionSlotThree,
|
|
SlotOneValue = _lockboxSlots.Count > 0 ? _lockboxSlots[0].CurrentValue : MinDigit,
|
|
SlotTwoValue = _lockboxSlots.Count > 1 ? _lockboxSlots[1].CurrentValue : MinDigit,
|
|
SlotThreeValue = _lockboxSlots.Count > 2 ? _lockboxSlots[2].CurrentValue : MinDigit
|
|
};
|
|
|
|
return UniTask.FromResult(MemoryPackSerializer.Serialize(state));
|
|
}
|
|
|
|
public UniTask RestoreState(byte[] state)
|
|
{
|
|
if (state == null || state.Length == 0)
|
|
{
|
|
GenerateSolutionIfNeeded();
|
|
ApplyRandomDefaultSlotValues();
|
|
ApplySolutionToCompletionCheck();
|
|
|
|
if (SaveManager.GetLevelFlag(LevelFlag.LaxleyLockboxOpened))
|
|
DisableSlots();
|
|
|
|
return UniTask.CompletedTask;
|
|
}
|
|
|
|
var restored = MemoryPackSerializer.Deserialize<FireplaceLockboxPuzzleState>(state);
|
|
|
|
_solutionSlotOne = WrapDigit(restored.SolutionSlotOne);
|
|
_solutionSlotTwo = WrapDigit(restored.SolutionSlotTwo);
|
|
_solutionSlotThree = WrapDigit(restored.SolutionSlotThree);
|
|
|
|
if (_lockboxSlots.Count > 0)
|
|
_lockboxSlots[0].ApplyValueImmediate(restored.SlotOneValue);
|
|
|
|
if (_lockboxSlots.Count > 1)
|
|
_lockboxSlots[1].ApplyValueImmediate(restored.SlotTwoValue);
|
|
|
|
if (_lockboxSlots.Count > 2)
|
|
_lockboxSlots[2].ApplyValueImmediate(restored.SlotThreeValue);
|
|
|
|
ApplySolutionToCompletionCheck();
|
|
|
|
if (SaveManager.GetLevelFlag(LevelFlag.LaxleyLockboxOpened))
|
|
DisableSlots();
|
|
|
|
return UniTask.CompletedTask;
|
|
}
|
|
|
|
private void InitializeSlots()
|
|
{
|
|
if (_lockboxSlots == null)
|
|
return;
|
|
|
|
foreach (var slot in _lockboxSlots)
|
|
{
|
|
if (slot == null)
|
|
continue;
|
|
|
|
slot.Initialize(GetNumberSprite, OnSlotValueChanged);
|
|
}
|
|
}
|
|
|
|
private void DisableSlots()
|
|
{
|
|
if (_lockboxSlots == null)
|
|
return;
|
|
|
|
foreach (var slot in _lockboxSlots)
|
|
{
|
|
if (slot == null)
|
|
continue;
|
|
|
|
slot.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
|
|
private void ApplyRandomDefaultSlotValues()
|
|
{
|
|
if (_lockboxSlots.Count > 0)
|
|
_lockboxSlots[0].ApplyValueImmediate(GetRandomDigit());
|
|
|
|
if (_lockboxSlots.Count > 1)
|
|
_lockboxSlots[1].ApplyValueImmediate(GetRandomDigit());
|
|
|
|
if (_lockboxSlots.Count > 2)
|
|
_lockboxSlots[2].ApplyValueImmediate(GetRandomDigit());
|
|
}
|
|
|
|
private int GetRandomDigit()
|
|
{
|
|
return Random.Range(MinDigit, MaxDigit + 1);
|
|
}
|
|
|
|
private Sprite GetNumberSprite(int value)
|
|
{
|
|
value = WrapDigit(value);
|
|
return _numberSprites.TryGetValue(value, out var sprite) ? sprite : null;
|
|
}
|
|
|
|
private void OnSlotValueChanged()
|
|
{
|
|
ApplySolutionToCompletionCheck();
|
|
|
|
if (!SaveManager.GetLevelFlag(LevelFlag.LaxleyLockboxOpened) && IsCurrentInputCorrect())
|
|
CompletePuzzle().Forget();
|
|
}
|
|
|
|
private void ApplySolutionToCompletionCheck()
|
|
{
|
|
IsCompleted = IsCurrentInputCorrect();
|
|
}
|
|
|
|
private bool IsCurrentInputCorrect()
|
|
{
|
|
if (_lockboxSlots == null || _lockboxSlots.Count < 3)
|
|
return false;
|
|
|
|
return _lockboxSlots[0].CurrentValue == _solutionSlotOne &&
|
|
_lockboxSlots[1].CurrentValue == _solutionSlotTwo &&
|
|
_lockboxSlots[2].CurrentValue == _solutionSlotThree;
|
|
}
|
|
|
|
private void GenerateSolutionIfNeeded()
|
|
{
|
|
if (HasValidSolution())
|
|
return;
|
|
|
|
GenerateSolutionVariation();
|
|
}
|
|
|
|
private bool HasValidSolution()
|
|
{
|
|
return _solutionSlotOne is >= MinDigit and <= MaxDigit &&
|
|
_solutionSlotTwo is >= MinDigit and <= MaxDigit &&
|
|
_solutionSlotThree is >= MinDigit and <= MaxDigit;
|
|
}
|
|
|
|
private void GenerateSolutionVariation()
|
|
{
|
|
int[] digits = { 8, 4, 7 };
|
|
|
|
for (int i = digits.Length - 1; i > 0; i--)
|
|
{
|
|
int swapIndex = Random.Range(0, i + 1);
|
|
(digits[i], digits[swapIndex]) = (digits[swapIndex], digits[i]);
|
|
}
|
|
|
|
_solutionSlotOne = digits[0];
|
|
_solutionSlotTwo = digits[1];
|
|
_solutionSlotThree = digits[2];
|
|
}
|
|
|
|
private int WrapDigit(int value)
|
|
{
|
|
value %= MaxDigit;
|
|
|
|
if (value <= 0)
|
|
value += MaxDigit;
|
|
|
|
return value;
|
|
}
|
|
}
|
|
} |