Additional Levels added. Mostly just base artwork so far.

This commit is contained in:
2026-03-26 21:31:29 +00:00
parent 644b56282e
commit b3b569e98f
131 changed files with 6151 additions and 6008 deletions

View File

@@ -1,7 +1,295 @@
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
{
public class FireplaceLockbox
[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;
}
}
}