1774 lines
47 KiB
Plaintext
1774 lines
47 KiB
Plaintext
using PuzzleDefine;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using Sirenix.OdinInspector;
|
|
using SonicBloom.Koreo;
|
|
using SonicBloom.Koreo.Players;
|
|
using UnityEngine.Video;
|
|
|
|
public class PuzzleGameMode : MonoBehaviour
|
|
{
|
|
public static PuzzleGameMode main;
|
|
|
|
#region Define
|
|
|
|
[System.Serializable]
|
|
public struct HeroConfig
|
|
{
|
|
public HeroID ID;
|
|
public GameObject Prefab;
|
|
}
|
|
|
|
|
|
[System.Serializable]
|
|
public struct GemDamageSheet
|
|
{
|
|
public PuzzleGemMatch GemMatch;
|
|
public int ComboNum;
|
|
}
|
|
|
|
[System.Serializable]
|
|
public struct RandomGemShape
|
|
{
|
|
public List<PuzzleDefine.Position> Positions;
|
|
}
|
|
|
|
public delegate void VoidDelegate();
|
|
|
|
[System.Serializable]
|
|
public struct MatchTiming
|
|
{
|
|
public float Timing;
|
|
|
|
public float FirstRadius;
|
|
public int MatchType;
|
|
public PuzzleDefine.AttributeFix FirstBonusFix;
|
|
|
|
public float SecondRadius;
|
|
public PuzzleDefine.AttributeFix SecondBonusFix;
|
|
public TimingNames TimingName;
|
|
|
|
public bool InFirstRange(float timing)
|
|
{
|
|
return timing >= Timing - FirstRadius && timing <= Timing + FirstRadius;
|
|
}
|
|
public bool InSecondRange(float timing)
|
|
{
|
|
return timing >= Timing - SecondRadius && timing <= Timing + SecondRadius;
|
|
}
|
|
}
|
|
|
|
[System.Serializable]
|
|
public struct DamageComposition
|
|
{
|
|
public PuzzleDefine.Attribute Damage;
|
|
|
|
public PuzzleDefine.AttributeFix GemBonus;
|
|
}
|
|
|
|
public enum TimingNames
|
|
{
|
|
Perfect,
|
|
Good,
|
|
Miss,
|
|
}
|
|
|
|
public enum FillStrategies
|
|
{
|
|
Fall,
|
|
Generate,
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void Awake()
|
|
{
|
|
main = this;
|
|
}
|
|
|
|
#region Events
|
|
|
|
public VoidDelegate OnAfterGameStarted;
|
|
public VoidDelegate OnAfterRoundFinished;
|
|
|
|
#endregion
|
|
|
|
#region Game
|
|
|
|
public bool GameStarted = false;
|
|
public bool GameFinished = false;
|
|
|
|
public List<Board> Boards;
|
|
// public Board Board;
|
|
|
|
public int Round;
|
|
public void SetRound(int round)
|
|
{
|
|
Round = round;
|
|
}
|
|
|
|
public PuzzleGamePlayer GamePlayer;
|
|
public PuzzleMonster Monster;
|
|
|
|
public BattleConfig BattleConfig;
|
|
public List<HeroConfig> HeroConfigs;
|
|
|
|
public int PredictBlocks;
|
|
public int MaxReplaceBlocks;
|
|
public List<RandomGemShape> RandomShapes;
|
|
public PuzzleUXConfig PuzzleUXConfig;
|
|
public PuzzleGameConfig PuzzleGameConfig;
|
|
|
|
private List<SkillConfig> SkillConfigs;
|
|
|
|
public ReplaceBlock GenRandomReplaceBlock()
|
|
{
|
|
ReplaceBlock block = new ReplaceBlock();
|
|
|
|
int rand = Random.Range(0, RandomShapes.Count);
|
|
RandomGemShape shape = RandomShapes[rand];
|
|
block.GemsToReplace = new List<PositionedGem>(shape.Positions.Count);
|
|
|
|
for (int j = 0; j < shape.Positions.Count; j++)
|
|
{
|
|
PositionedGem positionedGem = new PositionedGem()
|
|
{
|
|
Position = shape.Positions[j],
|
|
Gem = GetRandomGem(),
|
|
};
|
|
block.GemsToReplace.Add(positionedGem);
|
|
}
|
|
|
|
return block;
|
|
}
|
|
|
|
public void FillReplaceBlocks()
|
|
{
|
|
var blocks = GamePlayer.ReplaceBlocks;
|
|
|
|
blocks.Clear();
|
|
|
|
for (int i = 0; i < MaxReplaceBlocks; i++)
|
|
{
|
|
blocks[i] = GenRandomReplaceBlock();
|
|
}
|
|
|
|
for (int i = 0; i < PredictBlocks; i++)
|
|
{
|
|
blocks[-1 - i] = GenRandomReplaceBlock();
|
|
}
|
|
|
|
GamePlayer.OnAfterReplaceBlockChagned?.Invoke();
|
|
}
|
|
|
|
public void SlideReplaceBlocks()
|
|
{
|
|
var blocks = GamePlayer.ReplaceBlocks;
|
|
|
|
int lastEmptySlot = MaxReplaceBlocks - 1;
|
|
for (int i = MaxReplaceBlocks - 1; i >= -PredictBlocks; i--)
|
|
{
|
|
if (blocks.ContainsKey(i))
|
|
{
|
|
if (lastEmptySlot == i)
|
|
{
|
|
lastEmptySlot--;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
blocks[lastEmptySlot] = blocks[i];
|
|
blocks.Remove(i);
|
|
lastEmptySlot--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//lastEmptySlot = i;
|
|
}
|
|
}
|
|
|
|
for (int i = -PredictBlocks; i < MaxReplaceBlocks; i++)
|
|
{
|
|
if (blocks.ContainsKey(i))
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
blocks.Add(i, GenRandomReplaceBlock());
|
|
}
|
|
}
|
|
|
|
GamePlayer.OnAfterReplaceBlockChagned?.Invoke();
|
|
}
|
|
|
|
private void Start_Game()
|
|
{
|
|
PuzzlePlayer player = PuzzlePlayer.main;
|
|
|
|
GamePlayer.Heroes = new List<PuzzleHero>();
|
|
PuzzleLeveData currLevelData = LevelLoader.instance.currLevelData;
|
|
SkillConfigs = PuzzleGameConfig.SkillConfigs;
|
|
if (currLevelData != null)
|
|
{
|
|
m_currKoreography = LevelLoader.instance.currLevelData.Koreography;
|
|
m_beatKoreEventID = m_currKoreography.Tracks[0].EventID;
|
|
m_koreAudioClip = m_currKoreography.SourceClip;
|
|
GameObject.Find("KoreMusicPlayer").GetComponent<SimpleMusicPlayer>().LoadSong(m_currKoreography);
|
|
}
|
|
foreach (var item in player.HeroIDs)
|
|
{
|
|
GamePlayer.Heroes.Add(CreateHero(item));
|
|
}
|
|
|
|
Monster.SetHealth(Monster.MaxHealth);
|
|
|
|
Round = 0;
|
|
StartRound();
|
|
|
|
FillReplaceBlocks();
|
|
|
|
GameStarted = true;
|
|
|
|
OnAfterGameStarted?.Invoke();
|
|
for (int i = 0; i < Boards.Count; i++)
|
|
{
|
|
Boards[i].Init();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Combo
|
|
|
|
public int ComboNum = 0;
|
|
|
|
#endregion
|
|
|
|
#region Animation
|
|
|
|
private bool m_duringFallilngAnimation;
|
|
|
|
#endregion
|
|
|
|
#region Connection
|
|
|
|
private static readonly Position[] s_iterateDirections = new Position[]
|
|
{
|
|
new Position()
|
|
{
|
|
X = 0,
|
|
Y = 1,
|
|
},
|
|
new Position()
|
|
{
|
|
X = 0,
|
|
Y = -1,
|
|
},
|
|
new Position()
|
|
{
|
|
X = 1,
|
|
Y = 0,
|
|
},
|
|
new Position()
|
|
{
|
|
X = -1,
|
|
Y = 0,
|
|
},
|
|
};
|
|
|
|
public Vector2 GetGemSize()
|
|
{
|
|
return Vector2.one * PuzzleUXConfig.GemSize;
|
|
}
|
|
|
|
public void CalcConnectionMatch(Position at, HashSet<Position> connected, out Gem outGem)
|
|
{
|
|
PositionedGem? _positionedGem = GetPositionedGem(at);
|
|
if (_positionedGem == null)
|
|
{
|
|
outGem = default;
|
|
return;
|
|
}
|
|
|
|
outGem = _positionedGem.Value.Gem;
|
|
CalcConnectionMatchRec(at, connected, outGem);
|
|
}
|
|
|
|
private void CalcConnectionMatchRec(Position at, HashSet<Position> connected, Gem filterGem)
|
|
{
|
|
PositionedGem? _positionedGem = GetPositionedGem(at);
|
|
if (_positionedGem == null || _positionedGem.Value.Gem != filterGem)
|
|
{
|
|
return;
|
|
}
|
|
|
|
connected.Add(_positionedGem.Value.Position);
|
|
|
|
foreach (var direction in s_iterateDirections)
|
|
{
|
|
Position nextPosition = _positionedGem.Value.Position + direction;
|
|
if (connected.Contains(nextPosition))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
CalcConnectionMatchRec(nextPosition, connected, filterGem);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Music
|
|
|
|
[SerializeField]
|
|
private AudioClip m_koreAudioClip;
|
|
|
|
[EventID]
|
|
[SerializeField]
|
|
private string m_beatKoreEventID;
|
|
[SerializeField]
|
|
private Koreography m_currKoreography;
|
|
|
|
private static float PeekKoreEvents(string clipName, string eventID, List<BeatEvent> outEvents, float seconds, float? startFrom = null)
|
|
{
|
|
int currentTimeSample = Koreographer.Instance.GetMusicSampleTime(clipName);
|
|
int samplingRate = Koreographer.Instance.GetMusicSampleRate(clipName);
|
|
|
|
int peekLength = (int)(samplingRate * seconds);
|
|
int peekOffset = currentTimeSample;
|
|
|
|
if (startFrom != null)
|
|
{
|
|
peekOffset = (int)(samplingRate * startFrom);
|
|
}
|
|
|
|
List<KoreographyEvent> events = new List<KoreographyEvent>();
|
|
Koreographer.Instance.GetAllEventsInRange(clipName, eventID, peekOffset, peekOffset + peekLength, events);
|
|
|
|
foreach (var item in events)
|
|
{
|
|
BeatEvent beatEvent = new BeatEvent()
|
|
{
|
|
StartTime = (float)item.StartSample / (float)samplingRate,
|
|
EndTime = (float)item.EndSample / (float)samplingRate,
|
|
EventID = eventID,
|
|
EventType = item.GetIntValue(),
|
|
};
|
|
outEvents.Add(beatEvent);
|
|
}
|
|
|
|
return (float)peekOffset / (float)samplingRate;
|
|
}
|
|
|
|
private void UpdateBeatTiming()
|
|
{
|
|
List<BeatEvent> koreographyEvents = new List<BeatEvent>();
|
|
float currentTime = PeekKoreEvents(m_koreAudioClip.name, m_beatKoreEventID, koreographyEvents, m_koreAudioClip.length);
|
|
|
|
MatchTimings.Clear();
|
|
MonsterSkillTimings.Clear();
|
|
foreach (var item in koreographyEvents)
|
|
{
|
|
MatchTiming newTiming = TemplateTimng;
|
|
newTiming.Timing = (item.StartTime - currentTime) / SecondsPerBeat;
|
|
newTiming.MatchType = item.EventType;
|
|
newTiming.TimingName = TimingNames.Miss;
|
|
MatchTimings.Add(newTiming);
|
|
if (newTiming.MatchType == 2)
|
|
{
|
|
MonsterSkillTimings.Add(newTiming);
|
|
}
|
|
}
|
|
OnAfterMatchTimingsChanged?.Invoke();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Time Beat
|
|
|
|
public float SecondsPerBeat;
|
|
|
|
private float m_lastBeatTime;
|
|
public VoidDelegate OnAfterLastBeatTimeChanged;
|
|
|
|
public GameObject PuzzleGemMatchPrefab;
|
|
public List<PuzzleGemMatch> GemMatches = new List<PuzzleGemMatch>();
|
|
|
|
public MatchTiming TemplateTimng;
|
|
public List<MatchTiming> MatchTimings;
|
|
public List<MatchTiming> MonsterSkillTimings;
|
|
public VoidDelegate OnAfterMatchTimingsChanged;
|
|
|
|
public HashSet<Position> BeatBlockedPositions = new HashSet<Position>();
|
|
|
|
private int m_placedTimingsInBeat;
|
|
|
|
public struct OnAfterPlayerClickParam
|
|
{
|
|
public float Timing;
|
|
public int PerfectCount;
|
|
public TimingNames TimingName;
|
|
}
|
|
public delegate void OnAfterPlayerClickHandler(OnAfterPlayerClickParam param);
|
|
public OnAfterPlayerClickHandler OnAfterPlayerClick;
|
|
|
|
public float GetCurrentTiming()
|
|
{
|
|
return (Time.time - m_lastBeatTime) / SecondsPerBeat;
|
|
}
|
|
|
|
public float GetLastBeatTime()
|
|
{
|
|
return m_lastBeatTime;
|
|
}
|
|
public void SetLastBeatTime(float value)
|
|
{
|
|
m_lastBeatTime = value;
|
|
OnAfterLastBeatTimeChanged?.Invoke();
|
|
}
|
|
|
|
//private IEnumerable<float> GetCurrentBeatEventsRatio()
|
|
//{
|
|
// foreach (var koreEvent in m_koreEvents)
|
|
// {
|
|
// koreEvent.StartSample
|
|
// }
|
|
//}
|
|
|
|
private void Start_Beat()
|
|
{
|
|
SetLastBeatTime(Time.time);
|
|
UpdateBeatTiming();
|
|
}
|
|
|
|
private void Update_Beat()
|
|
{
|
|
if (GameFinished)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!GameStarted)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (SecondsPerBeat <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// float timeSpan = Time.time - m_lastBeatTime;
|
|
// if (timeSpan > SecondsPerBeat)
|
|
// {
|
|
// DoBeat();
|
|
// }
|
|
|
|
}
|
|
|
|
int currMonsterSkillAnimIndex;
|
|
private void Update_MonsterSkillAnim()
|
|
{
|
|
if (MonsterSkillTimings.Count < 0) return;
|
|
if (MonsterSkillTimings.Count <= currMonsterSkillAnimIndex) return;
|
|
if (GetCurrentTiming() > MonsterSkillTimings[currMonsterSkillAnimIndex].Timing - SkillConfigs[0].SkillPreAnim)
|
|
{
|
|
MonsterPreReleaseSkill(new PreReleaseSkillParam()
|
|
{
|
|
Monster = Monster,
|
|
});
|
|
currMonsterSkillAnimIndex++;
|
|
}
|
|
}
|
|
|
|
|
|
int currMonsterSkillIndex;
|
|
private void Update_MonsterSkillDamage()
|
|
{
|
|
if (MonsterSkillTimings.Count < 0) return;
|
|
if (MonsterSkillTimings.Count <= currMonsterSkillIndex) return;
|
|
if (GetCurrentTiming() > MonsterSkillTimings[currMonsterSkillIndex].Timing + MonsterSkillTimings[currMonsterSkillIndex].SecondRadius)
|
|
{
|
|
if (MonsterSkillTimings[currMonsterSkillIndex].TimingName != TimingNames.Perfect)
|
|
{
|
|
Monster.TookAction();
|
|
// Debug.Log("~~~~~~MonsterSkillDamage");
|
|
}
|
|
currMonsterSkillIndex++;
|
|
}
|
|
}
|
|
|
|
private void AddUnique(List<PuzzleGemMatch> result, PuzzleGemMatch match)
|
|
{
|
|
if (result.Contains(match))
|
|
{
|
|
return;
|
|
}
|
|
|
|
result.Add(match);
|
|
}
|
|
|
|
private List<PuzzleGemMatch> FindGemMatches(List<Item> items)
|
|
{
|
|
List<PuzzleGemMatch> result = new List<PuzzleGemMatch>();
|
|
|
|
foreach (var gemMatch in GemMatches)
|
|
{
|
|
foreach (var item in items)
|
|
{
|
|
PositionedGem? gem = GetItemPositionedGem(item);
|
|
if (gem == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach (var gemInGemMatch in gemMatch.Gems)
|
|
{
|
|
if (gem.Value.Position == gemInGemMatch.Position)
|
|
{
|
|
AddUnique(result, gemMatch);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result.Count > 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private PuzzleGemMatch FindGemMatch(MatchEvent items)
|
|
{
|
|
foreach (var gemMatch in GemMatches)
|
|
{
|
|
foreach (var gem in items.MatchedGems)
|
|
{
|
|
foreach (var gemInGemMatch in gemMatch.Gems)
|
|
{
|
|
if (gem.Position == gemInGemMatch.Position)
|
|
{
|
|
return gemMatch;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private PuzzleGemMatch FindGemMatch(Position position)
|
|
{
|
|
foreach (var gemMatch in GemMatches)
|
|
{
|
|
foreach (var gem in gemMatch.Gems)
|
|
{
|
|
if (gem.Position == position)
|
|
{
|
|
return gemMatch;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private PuzzleGemMatch CreatePuzzleMatch(List<Item> itemMatch)
|
|
{
|
|
PuzzleGemMatch newGemMatch = PuzzleUtils.CreateObject<PuzzleGemMatch>(PuzzleGemMatchPrefab);
|
|
foreach (var item in itemMatch)
|
|
{
|
|
newGemMatch.AddGem(GetItemPositionedGem(item).Value);
|
|
}
|
|
GemMatches.Add(newGemMatch);
|
|
return newGemMatch;
|
|
}
|
|
|
|
private PuzzleGemMatch CreatePuzzleMatch(IEnumerable<PositionedGem> gems)
|
|
{
|
|
PuzzleGemMatch newGemMatch = PuzzleUtils.CreateObject<PuzzleGemMatch>(PuzzleGemMatchPrefab);
|
|
foreach (var item in gems)
|
|
{
|
|
newGemMatch.AddGem(item);
|
|
}
|
|
GemMatches.Add(newGemMatch);
|
|
return newGemMatch;
|
|
}
|
|
|
|
private void RefillPuzzleMatch(PuzzleGemMatch gemMatch, List<Item> itemMatch)
|
|
{
|
|
gemMatch.Clear();
|
|
foreach (var item in itemMatch)
|
|
{
|
|
gemMatch.AddGem(GetItemPositionedGem(item).Value);
|
|
}
|
|
}
|
|
|
|
private void MergePuzzleMatch(PuzzleGemMatch dest, PuzzleGemMatch source)
|
|
{
|
|
dest.AddBonus(source.Bonus);
|
|
GemMatches.Remove(source);
|
|
Destroy(source);
|
|
}
|
|
|
|
int PerfectCount;
|
|
private AttributeFix? GetTimingBonus(float timing, out TimingNames outTimingName)
|
|
{
|
|
foreach (MatchTiming matchTiming in MatchTimings)
|
|
{
|
|
if (matchTiming.InFirstRange(timing))
|
|
{
|
|
if (matchTiming.MatchType == 1)
|
|
{
|
|
outTimingName = TimingNames.Miss;
|
|
PerfectCount = 0;
|
|
return null;
|
|
}
|
|
SetTimingName(matchTiming, TimingNames.Perfect);
|
|
outTimingName = TimingNames.Perfect;
|
|
PerfectCount++;
|
|
return matchTiming.FirstBonusFix;
|
|
}
|
|
else if (matchTiming.InSecondRange(timing))
|
|
{
|
|
if (matchTiming.MatchType == 1)
|
|
{
|
|
outTimingName = TimingNames.Miss;
|
|
PerfectCount = 0;
|
|
return null;
|
|
}
|
|
PerfectCount = 0;
|
|
SetTimingName(matchTiming, TimingNames.Good);
|
|
outTimingName = TimingNames.Good;
|
|
return matchTiming.SecondBonusFix;
|
|
}
|
|
|
|
}
|
|
|
|
PerfectCount = 0;
|
|
outTimingName = TimingNames.Miss;
|
|
return null;
|
|
}
|
|
|
|
void SetTimingName(MatchTiming currTiming, TimingNames currTimingName)
|
|
{
|
|
for (int j = 0; j < MonsterSkillTimings.Count; j++)
|
|
{
|
|
if (MonsterSkillTimings[j].Timing == currTiming.Timing)
|
|
{
|
|
MatchTiming newMatchTiming = new MatchTiming();
|
|
newMatchTiming.Timing = currTiming.Timing;
|
|
newMatchTiming.TimingName = currTimingName;
|
|
newMatchTiming.MatchType = MonsterSkillTimings[j].MatchType;
|
|
Debug.Log("currIndex" + j + "SetTimingName:" + currTimingName);
|
|
MonsterSkillTimings[j] = newMatchTiming;
|
|
}
|
|
}
|
|
}
|
|
|
|
public float GetTimingDis()
|
|
{
|
|
float dis = 0;
|
|
int currAddIndex = 0;
|
|
for (int j = 0; j < MatchTimings.Count; j++)
|
|
{
|
|
float currDis = Mathf.Abs(GetCurrentTiming() - MatchTimings[j].Timing);
|
|
if (MatchTimings[j].MatchType == 1)
|
|
{
|
|
continue;
|
|
}
|
|
else if (currAddIndex == 0)
|
|
{
|
|
dis = currDis;
|
|
currAddIndex++;
|
|
}
|
|
else if (currDis < dis)
|
|
{
|
|
dis = currDis;
|
|
currAddIndex++;
|
|
}
|
|
}
|
|
dis = dis > 0.1f ? 0.1f : dis;
|
|
return dis;
|
|
}
|
|
|
|
private TimingNames GetSkillTimingBonus(float timing)
|
|
{
|
|
foreach (MatchTiming matchTiming in MatchTimings)
|
|
{
|
|
if (matchTiming.InFirstRange(timing))
|
|
{
|
|
if (matchTiming.MatchType == 1)
|
|
{
|
|
return TimingNames.Perfect;
|
|
}
|
|
else if (matchTiming.MatchType == 2)
|
|
{
|
|
SetTimingName(matchTiming, TimingNames.Perfect);
|
|
}
|
|
}
|
|
else if (matchTiming.InSecondRange(timing))
|
|
{
|
|
if (matchTiming.MatchType == 1)
|
|
{
|
|
SetTimingName(matchTiming, TimingNames.Good);
|
|
return TimingNames.Good;
|
|
}
|
|
else if (matchTiming.MatchType == 2)
|
|
{
|
|
SetTimingName(matchTiming, TimingNames.Good);
|
|
}
|
|
}
|
|
}
|
|
return TimingNames.Miss;
|
|
}
|
|
|
|
private bool ItemMatchSame(List<Item> a, List<Item> b)
|
|
{
|
|
if (a.Count != b.Count)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
HashSet<Item> set = new HashSet<Item>(a);
|
|
foreach (var item in b)
|
|
{
|
|
if (!set.Contains(item))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private List<List<Item>> GetItemMatches(int boardIndex)
|
|
{
|
|
List<List<Item>> result = Boards[boardIndex].GetMatches();
|
|
int count = result.Count;
|
|
for (int i = count - 1; i >= 0; i--)
|
|
{
|
|
for (int j = i - 1; j >= 0; j--)
|
|
{
|
|
if (ItemMatchSame(result[i], result[j]))
|
|
{
|
|
result.RemoveAt(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private bool GemMatchContainsPosition(PuzzleGemMatch gemMatch, Position position)
|
|
{
|
|
foreach (var gem in gemMatch.Gems)
|
|
{
|
|
if (gem.Position == position)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private bool GemMatchContainsBlock(PuzzleGemMatch gemMatch, ReplaceBlock placedBlock, Position placedPosition)
|
|
{
|
|
foreach (var gem in placedBlock.GemsToReplace)
|
|
{
|
|
Position finalPosition = gem.Position + placedPosition;
|
|
if (!GemMatchContainsPosition(gemMatch, finalPosition))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool IsRelated(List<Item> itemMatch, ReplaceBlock placedBlock, Position placedPosition)
|
|
{
|
|
foreach (var item in itemMatch)
|
|
{
|
|
PositionedGem? gem = GetItemPositionedGem(item);
|
|
if (gem == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach (var gemItem in placedBlock.GemsToReplace)
|
|
{
|
|
Position finalPosition = placedPosition + gemItem.Position;
|
|
if (finalPosition == gem.Value.Position)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private IEnumerable<PositionedGem> CombineGem(IEnumerable<Position> positions, Gem gem)
|
|
{
|
|
foreach (var item in positions)
|
|
{
|
|
yield return new PositionedGem()
|
|
{
|
|
Position = item,
|
|
Gem = gem,
|
|
};
|
|
}
|
|
}
|
|
|
|
private void UpdateGemMatch(Position clickedPoisition, float timing)
|
|
{
|
|
if (m_placedTimingsInBeat >= MatchTimings.Count)
|
|
{
|
|
Debug.LogWarning($"Trying to choose position but reached timings limite per beat {m_placedTimingsInBeat}/{MatchTimings.Count}");
|
|
return;
|
|
}
|
|
|
|
PuzzleGemMatch findedGemMatch = FindGemMatch(clickedPoisition);
|
|
if (findedGemMatch != null)
|
|
{
|
|
Debug.LogWarning($"Trying to choose position but this place has a gemMatch at {clickedPoisition}");
|
|
return;
|
|
}
|
|
|
|
Gem gem;
|
|
HashSet<Position> connectedGems = new HashSet<Position>();
|
|
CalcConnectionMatch(clickedPoisition, connectedGems, out gem);
|
|
|
|
if (connectedGems.Count <= 1)
|
|
{
|
|
Debug.LogWarning($"Trying to choose position but not enough connected gems at {clickedPoisition} type: {gem} connected: {connectedGems.Count}");
|
|
return;
|
|
}
|
|
|
|
OnAfterPlayerClickParam afterClickParam;
|
|
afterClickParam.Timing = timing;
|
|
|
|
PuzzleDefine.AttributeFix? bonus = GetTimingBonus(timing, out afterClickParam.TimingName);
|
|
afterClickParam.PerfectCount = PerfectCount;
|
|
|
|
PuzzleGemMatch newGemMatch = CreatePuzzleMatch(CombineGem(connectedGems, gem));
|
|
newGemMatch.AddBonus(bonus);
|
|
newGemMatch.LastTimingName = afterClickParam.TimingName;
|
|
|
|
OnAfterPlayerClick?.Invoke(afterClickParam);
|
|
|
|
m_placedTimingsInBeat++;
|
|
|
|
ProcessAllGemMatches();
|
|
|
|
if (Monster.Health.Value <= 0)
|
|
{
|
|
Win(clickedPoisition.BoardIndex);
|
|
return;
|
|
}
|
|
|
|
UpdateBoard(clickedPoisition.BoardIndex);
|
|
}
|
|
|
|
private void UpdateGemMatch(ReplaceBlock placedBlock, Position placedPosition, float timing)
|
|
{
|
|
List<List<Item>> matches = GetItemMatches(placedPosition.BoardIndex);
|
|
TimingNames outTimingName;
|
|
PuzzleDefine.AttributeFix? bonus = GetTimingBonus(timing, out outTimingName);
|
|
|
|
foreach (var itemMatch in matches)
|
|
{
|
|
if (!IsRelated(itemMatch, placedBlock, placedPosition))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
List<PuzzleGemMatch> findedGemMatches = FindGemMatches(itemMatch);
|
|
if (findedGemMatches == null)
|
|
{
|
|
PuzzleGemMatch newGemMatch = CreatePuzzleMatch(itemMatch);
|
|
newGemMatch.AddBonus(bonus);
|
|
}
|
|
else
|
|
{
|
|
if (findedGemMatches.Count > 1)
|
|
{
|
|
// Merge matches
|
|
for (int i = 1; i < findedGemMatches.Count; i++)
|
|
{
|
|
MergePuzzleMatch(findedGemMatches[0], findedGemMatches[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// == 1
|
|
}
|
|
RefillPuzzleMatch(findedGemMatches[0], itemMatch);
|
|
|
|
if (GemMatchContainsBlock(findedGemMatches[0], placedBlock, placedPosition))
|
|
{
|
|
findedGemMatches[0].AddBonus(bonus);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Game Callbacks
|
|
|
|
public void OnPlayerPlaced()
|
|
{
|
|
|
|
}
|
|
|
|
public void OnMapMatchHappened(List<MatchEvent> matchEvents)
|
|
{
|
|
return;
|
|
|
|
//bool processed = false;
|
|
//foreach (var evt in matchEvents)
|
|
//{
|
|
// if (evt.MatchedGems == null || evt.MatchedGems.Count == 0)
|
|
// {
|
|
// continue;
|
|
// }
|
|
// PuzzleGemMatch gemMatch = FindGemMatch(evt);
|
|
|
|
// GemDamageSheet damageSheet = new GemDamageSheet()
|
|
// {
|
|
// GemMatch = gemMatch,
|
|
// ComboNum = ComboNum,
|
|
// };
|
|
// PuzzleDefine.Attribute finalDamage = CalcDamage(Monster, damageSheet);
|
|
// DamageMonster(Monster, finalDamage);
|
|
|
|
// PuzzleDefine.Attribute energyGot = PuzzleDefine.Attribute.Zero;
|
|
// foreach (var item in evt.MatchedGems)
|
|
// {
|
|
// BoostEnergy(GamePlayer, item.Gem.Type, BattleConfig.BaseEneryPerGem);
|
|
// }
|
|
|
|
// processed = true;
|
|
//}
|
|
|
|
//if (processed)
|
|
//{
|
|
// ComboNum++;
|
|
//}
|
|
}
|
|
|
|
private void ProcessAllGemMatches()
|
|
{
|
|
foreach (var gemMatch in GemMatches)
|
|
{
|
|
GemDamageSheet damageSheet = new GemDamageSheet()
|
|
{
|
|
GemMatch = gemMatch,
|
|
ComboNum = ComboNum,
|
|
};
|
|
if (gemMatch.LastTimingName != TimingNames.Perfect && gemMatch.LastTimingName != TimingNames.Good)
|
|
{
|
|
continue;
|
|
}
|
|
DamageComposition finalDamage = CalcDamage(Monster, damageSheet);
|
|
DamageMonster(new DamageMonsterParam()
|
|
{
|
|
Monster = Monster,
|
|
Composition = finalDamage,
|
|
TimingUsed = gemMatch.LastTimingName,
|
|
});
|
|
|
|
PuzzleDefine.Attribute energyGot = PuzzleDefine.Attribute.Zero;
|
|
foreach (var item in gemMatch.Gems)
|
|
{
|
|
Item theItem = GetItem(item.Position);
|
|
if (theItem.puzzleHero == null || theItem.puzzleHero.IsDead)
|
|
{
|
|
continue;
|
|
}
|
|
BoostEnergy(GamePlayer, item.Gem.Type, BattleConfig.BaseEneryPerGem);
|
|
}
|
|
}
|
|
|
|
foreach (var gemMatch in GemMatches)
|
|
{
|
|
foreach (var gem in gemMatch.Gems)
|
|
{
|
|
Item theItem = GetItem(gem.Position);
|
|
theItem.Destroy();
|
|
}
|
|
|
|
Destroy(gemMatch);
|
|
}
|
|
GemMatches.Clear();
|
|
}
|
|
|
|
private MatchEvent m_currentMatchEvent;
|
|
public void OnAfterGemDestroyed(Item item)
|
|
{
|
|
PositionedGem? gem = GetItemPositionedGem(item);
|
|
if (gem == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_currentMatchEvent.MatchedGems == null)
|
|
{
|
|
m_currentMatchEvent.MatchedGems = new List<PositionedGem>();
|
|
}
|
|
|
|
m_currentMatchEvent.MatchedGems.Add(gem.Value);
|
|
}
|
|
|
|
public void OnAftermapFilled()
|
|
{
|
|
if (m_currentMatchEvent.MatchedGems == null || m_currentMatchEvent.MatchedGems.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
OnMapMatchHappened(new List<MatchEvent>()
|
|
{
|
|
m_currentMatchEvent
|
|
});
|
|
m_currentMatchEvent.MatchedGems.Clear();
|
|
}
|
|
|
|
public void OnGemFallingFinished()
|
|
{
|
|
ComboNum = 0;
|
|
m_duringFallilngAnimation = false;
|
|
}
|
|
|
|
public Gem GetRandomGem()
|
|
{
|
|
Gem result;
|
|
result.Type = (ElementTypes)Random.Range(0, (int)ElementTypes.Max);
|
|
return result;
|
|
}
|
|
|
|
public Item GetItem(Position position)
|
|
{
|
|
return Boards[position.BoardIndex].GetNode(position.Y, position.X)?.item;
|
|
}
|
|
|
|
public Gem? GetItemGem(Item item)
|
|
{
|
|
Gem result;
|
|
var elementType = GetElementType(item.type);
|
|
if (elementType == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
result.Type = elementType.Value;
|
|
|
|
return result;
|
|
}
|
|
|
|
public Position GetItemPosition(Item item)
|
|
{
|
|
return new Position()
|
|
{
|
|
X = item.node.j,
|
|
Y = item.node.i,
|
|
BoardIndex = item.node.board.BoardIndex,
|
|
};
|
|
}
|
|
|
|
public PositionedGem? GetItemPositionedGem(Item item)
|
|
{
|
|
if (item == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
PositionedGem result;
|
|
|
|
var elementType = GetElementType(item.type);
|
|
if (elementType == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Gem gem;
|
|
gem.Type = elementType.Value;
|
|
|
|
result.Gem = gem;
|
|
result.Position = GetItemPosition(item);
|
|
|
|
return result;
|
|
}
|
|
|
|
public PositionedGem? GetPositionedGem(Position position)
|
|
{
|
|
return GetItemPositionedGem(GetItem(position));
|
|
}
|
|
|
|
public void SetItemGem(Item item, Gem gem)
|
|
{
|
|
ITEM_TYPE newType = GetItemType(gem.Type);
|
|
int color;
|
|
GameObject prefab;
|
|
Item.GetColorAndPrefab(newType, out color, out prefab);
|
|
item.color = color;
|
|
item.type = newType;
|
|
item.GetComponent<SpriteRenderer>().sprite = prefab.GetComponent<SpriteRenderer>().sprite;
|
|
}
|
|
|
|
public void UpdateBoard(int boardIndex)
|
|
{
|
|
m_duringFallilngAnimation = true;
|
|
|
|
Boards[boardIndex].FindMatches();
|
|
}
|
|
|
|
public static ElementTypes? GetElementType(ITEM_TYPE itemType)
|
|
{
|
|
ITEM_TYPE cookieType = Item.GetCookie(itemType);
|
|
switch (cookieType)
|
|
{
|
|
case ITEM_TYPE.COOKIE_1:
|
|
return ElementTypes.Water;
|
|
case ITEM_TYPE.COOKIE_2:
|
|
return ElementTypes.Wood;
|
|
case ITEM_TYPE.COOKIE_3:
|
|
return ElementTypes.Thunder;
|
|
case ITEM_TYPE.COOKIE_4:
|
|
break;
|
|
case ITEM_TYPE.COOKIE_5:
|
|
return ElementTypes.Fire;
|
|
case ITEM_TYPE.COOKIE_6:
|
|
return ElementTypes.Light;
|
|
case ITEM_TYPE.NONE:
|
|
break;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static ITEM_TYPE GetItemType(ElementTypes? elementType)
|
|
{
|
|
switch (elementType)
|
|
{
|
|
case ElementTypes.Fire:
|
|
return ITEM_TYPE.COOKIE_5;
|
|
case ElementTypes.Water:
|
|
return ITEM_TYPE.COOKIE_1;
|
|
case ElementTypes.Light:
|
|
return ITEM_TYPE.COOKIE_6;
|
|
case ElementTypes.Wood:
|
|
return ITEM_TYPE.COOKIE_2;
|
|
case ElementTypes.Thunder:
|
|
return ITEM_TYPE.COOKIE_3;
|
|
case ElementTypes.Max:
|
|
break;
|
|
}
|
|
|
|
return ITEM_TYPE.NONE;
|
|
}
|
|
|
|
public SkillConfig? GetSkillConfig(int skillID)
|
|
{
|
|
foreach (var item in SkillConfigs)
|
|
{
|
|
if (item.SkillID == skillID)
|
|
{
|
|
return item;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public HeroConfig? GetHeroConfig(HeroID heroID)
|
|
{
|
|
foreach (var item in HeroConfigs)
|
|
{
|
|
if (item.ID.Value == heroID.Value)
|
|
{
|
|
return item;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public bool CheckLoseCondition()
|
|
{
|
|
if (GamePlayer.Heroes.Count == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
foreach (var item in GamePlayer.Heroes)
|
|
{
|
|
if (!item.IsDead)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Round
|
|
|
|
//public int PlayerPlacedTimes = 0;
|
|
|
|
#endregion
|
|
|
|
#region Debug
|
|
|
|
private float m_startSeconds;
|
|
private bool m_debuging = false;
|
|
|
|
private void Start_Debug()
|
|
{
|
|
m_startSeconds = SecondsPerBeat;
|
|
}
|
|
|
|
private void Update_Debug()
|
|
{
|
|
if (Input.GetKeyDown(KeyCode.Space))
|
|
{
|
|
m_debuging = !m_debuging;
|
|
if (m_debuging)
|
|
{
|
|
SecondsPerBeat = 10000;
|
|
}
|
|
else
|
|
{
|
|
SecondsPerBeat = m_startSeconds;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Skill Effect
|
|
|
|
public Transform EffectCenter;
|
|
public GameObject VideoEffectPlayer;
|
|
public GameObject VideoEffectPlane;
|
|
public Camera CanvasCamera;
|
|
|
|
|
|
private Canvas GetParentCanvas(Transform tr)
|
|
{
|
|
Canvas result = tr.GetComponent<Canvas>();
|
|
if (result != null)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (tr.transform.parent != null)
|
|
{
|
|
return GetParentCanvas(tr.transform.parent);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private void OnHeroCastSkill_PlayEffect(PuzzleHero hero, int skillID)
|
|
{
|
|
SkillConfig? skillConfig = GetSkillConfig(skillID);
|
|
if (skillConfig == null || skillConfig.Value.HeroSkillEffect.IsEmpty)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Vector3 playPos;
|
|
|
|
if (skillConfig.Value.HeroSkillEffect.PlayCentered)
|
|
{
|
|
playPos = EffectCenter.position;
|
|
}
|
|
else
|
|
{
|
|
// var rectTransform = hero.Displayer.GetComponent<RectTransform>();
|
|
Vector3 worldPosition = hero.Displayer.transform.position;
|
|
//worldPosition = CanvasCamera.ScreenToWorldPoint(new Vector3(worldPosition.x, worldPosition.y, EffectCenter.transform.position.z));
|
|
worldPosition.z = EffectCenter.transform.position.z;
|
|
playPos = worldPosition;
|
|
//Debug.Log(worldPosition);
|
|
}
|
|
|
|
GameObject newEffect = Instantiate(skillConfig.Value.HeroSkillEffect.Prefab, playPos, Quaternion.identity, null);
|
|
|
|
if (skillConfig.Value.VideoClip != null)
|
|
{
|
|
float length = (float)skillConfig.Value.VideoClip.length;
|
|
GameObject vidEffect = Instantiate(VideoEffectPlayer);
|
|
VideoPlayer vidPlayer = vidEffect.GetComponent<VideoPlayer>();
|
|
vidPlayer.clip = skillConfig.Value.VideoClip;
|
|
vidPlayer.targetCamera = Camera.main;
|
|
Destroy(vidEffect, length);
|
|
StartCoroutine(DelayInActive(VideoEffectPlane, length));
|
|
}
|
|
|
|
if (skillConfig.Value.HeroSkillEffect.Length > 0)
|
|
{
|
|
Destroy(newEffect, skillConfig.Value.HeroSkillEffect.Length);
|
|
}
|
|
}
|
|
|
|
private IEnumerator DelayInActive(GameObject go, float time)
|
|
{
|
|
go.SetActive(true);
|
|
yield return new WaitForSeconds(time);
|
|
go.SetActive(false);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Interface
|
|
|
|
public bool IfPlaceBlockWillMatch(Gem gem, Position position)
|
|
{
|
|
Item item = GetItem(position);
|
|
PositionedGem? oldPositionedGem = GetItemPositionedGem(item);
|
|
if (oldPositionedGem == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SetItemGem(item, gem);
|
|
List<List<Item>> matches = GetItemMatches(position.BoardIndex);
|
|
foreach (List<Item> match in matches)
|
|
{
|
|
foreach (Item matchItem in match)
|
|
{
|
|
if (item == matchItem)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
SetItemGem(item, oldPositionedGem.Value.Gem);
|
|
return false;
|
|
}
|
|
|
|
public void DoBeat()
|
|
{
|
|
//ProcessAllGemMatches();
|
|
|
|
//if (Monster.Health.Value <= 0)
|
|
//{
|
|
// Win();
|
|
// return;
|
|
//}
|
|
|
|
//UpdateBoard();
|
|
m_placedTimingsInBeat = 0;
|
|
BeatBlockedPositions.Clear();
|
|
SetLastBeatTime(Time.time);
|
|
UpdateBeatTiming();
|
|
|
|
EndRound();
|
|
|
|
Monster.TookAction();
|
|
}
|
|
|
|
public DamageComposition CalcDamage(PuzzleMonster monster, GemDamageSheet sheet)
|
|
{
|
|
PuzzleDefine.Attribute result = PuzzleDefine.Attribute.Zero;
|
|
foreach (var item in sheet.GemMatch.Gems)
|
|
{
|
|
Item theItem = GetItem(item.Position);
|
|
if (theItem.puzzleHero == null || theItem.puzzleHero.IsDead)
|
|
{
|
|
continue;
|
|
}
|
|
PuzzleDefine.Attribute baseDamage = BattleConfig.BaseDamagePerGem;
|
|
if (IsWeakness(monster, item.Gem))
|
|
{
|
|
baseDamage = baseDamage * BattleConfig.WeaknessDamageRatio;
|
|
}
|
|
result += baseDamage;
|
|
}
|
|
|
|
PuzzleDefine.AttributeFix comboFix = BattleConfig.ComboDamageRatio * sheet.ComboNum;
|
|
result = result * (sheet.GemMatch.Bonus + comboFix + 1);
|
|
|
|
return new DamageComposition()
|
|
{
|
|
Damage = result,
|
|
GemBonus = sheet.GemMatch.Bonus,
|
|
};
|
|
}
|
|
|
|
public bool IsWeakness(PuzzleMonster monter, Gem gem)
|
|
{
|
|
foreach (var item in Monster.WeaknessTypes)
|
|
{
|
|
if (item == gem.Type)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void EndRound()
|
|
{
|
|
|
|
SetRound(Round + 1);
|
|
|
|
StartRound();
|
|
|
|
|
|
|
|
OnAfterRoundFinished?.Invoke();
|
|
}
|
|
|
|
public void StartRound()
|
|
{
|
|
|
|
}
|
|
|
|
public void PlayerChoosePosition(Position choice)
|
|
{
|
|
UpdateGemMatch(choice, GetCurrentTiming());
|
|
}
|
|
|
|
[Button]
|
|
public void PlayerPlaceBlock(int blockID, Position placedOn)
|
|
{
|
|
ReplaceBlock replaceBlock;
|
|
if (!GamePlayer.ReplaceBlocks.TryGetValue(blockID, out replaceBlock))
|
|
{
|
|
Debug.LogWarning("Trying to place block but ReplaceBlock doesn't exist");
|
|
return;
|
|
}
|
|
|
|
foreach (var item in replaceBlock.GemsToReplace)
|
|
{
|
|
Position finalPosition = placedOn + item.Position;
|
|
|
|
// If has been placed
|
|
if (BeatBlockedPositions.Contains(finalPosition))
|
|
{
|
|
Debug.LogWarning("Trying to place block but this place has been placed before");
|
|
return;
|
|
}
|
|
|
|
if (FindGemMatch(finalPosition) != null)
|
|
{
|
|
Debug.LogWarning($"Trying to place block but this place has a gemMatch at {finalPosition}");
|
|
return;
|
|
}
|
|
|
|
// If same color
|
|
Item positionItem = GetItem(finalPosition);
|
|
PositionedGem? gem = GetItemPositionedGem(positionItem);
|
|
if (gem == null)
|
|
{
|
|
Debug.LogWarning($"Trying to place block but gem is not recognized at {finalPosition}");
|
|
return;
|
|
}
|
|
if (gem.Value.Gem.Type == item.Gem.Type)
|
|
{
|
|
Debug.LogWarning($"Trying to place block but gem is identical at {finalPosition}");
|
|
return;
|
|
}
|
|
|
|
if (!IfPlaceBlockWillMatch(item.Gem, gem.Value.Position))
|
|
{
|
|
Debug.LogWarning($"Trying to place block but placement cannot form new match at{finalPosition}");
|
|
return;
|
|
}
|
|
}
|
|
|
|
GamePlayer.ReplaceBlocks.Remove(blockID);
|
|
|
|
foreach (var item in replaceBlock.GemsToReplace)
|
|
{
|
|
Position finalPosition = placedOn + item.Position;
|
|
BeatBlockedPositions.Add(finalPosition);
|
|
Item gemItem = GetItem(finalPosition);
|
|
SetItemGem(gemItem, item.Gem);
|
|
}
|
|
//UpdateBoard();
|
|
|
|
UpdateGemMatch(replaceBlock, placedOn, GetCurrentTiming());
|
|
SlideReplaceBlocks();
|
|
|
|
GamePlayer.OnAfterReplaceBlockChagned?.Invoke();
|
|
}
|
|
|
|
public PuzzleHero CreateHero(HeroID id)
|
|
{
|
|
HeroConfig? config = GetHeroConfig(id);
|
|
if (config == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
GameObject newHero = Instantiate(config.Value.Prefab);
|
|
PuzzleHero result = newHero.GetComponent<PuzzleHero>();
|
|
result.SetHealth(result.MaxHealth);
|
|
return result;
|
|
}
|
|
|
|
public void PlayerCastSkill(PuzzleHero hero, int skillID, Board board)
|
|
{
|
|
if (hero.SkillEnergy.Value < hero.MaxSkillEnergy.Value)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SkillConfig? config = GetSkillConfig(skillID);
|
|
if (config == null)
|
|
{
|
|
return;
|
|
}
|
|
TimingNames timingNames = GetSkillTimingBonus(GetCurrentTiming());
|
|
hero.SetSkillEnergy(PuzzleDefine.Attribute.Zero);
|
|
hero.IsSkillDisplay = false;
|
|
// DamageMonster(hero,skillID,config,1);
|
|
if (timingNames == TimingNames.Good)
|
|
{
|
|
DamageMonster(hero, skillID, config, 1);
|
|
}
|
|
else if (timingNames == TimingNames.Perfect)
|
|
{
|
|
DamageMonster(hero, skillID, config, 3);
|
|
}
|
|
DealSkillReset(hero, board);
|
|
}
|
|
|
|
void DealSkillReset(PuzzleHero hero, Board board)
|
|
{
|
|
board.ResetSkill(hero);
|
|
}
|
|
|
|
void DamageMonster(PuzzleHero hero, int skillID, SkillConfig? config, int count)
|
|
{
|
|
for (int j = 0; j < count; j++)
|
|
{
|
|
OnHeroCastSkill_PlayEffect(hero, skillID);
|
|
|
|
DamageMonster(new DamageMonsterParam()
|
|
{
|
|
Monster = Monster,
|
|
Composition = new DamageComposition()
|
|
{
|
|
Damage = config.Value.SkillDamage,
|
|
},
|
|
});
|
|
|
|
}
|
|
}
|
|
|
|
public void BoostEnergy(PuzzleGamePlayer player, ElementTypes elementType, PuzzleDefine.Attribute amount)
|
|
{
|
|
foreach (var hero in player.Heroes)
|
|
{
|
|
if (hero.ElementType == elementType)
|
|
{
|
|
PuzzleDefine.Attribute afterEnergy = hero.SkillEnergy + amount;
|
|
if (afterEnergy.Value > hero.MaxSkillEnergy.Value)
|
|
{
|
|
afterEnergy = hero.MaxSkillEnergy;
|
|
}
|
|
bool isShowCard = hero.SetSkillEnergy(afterEnergy);
|
|
if (isShowCard)
|
|
{
|
|
ShowHeroSkillCard(hero);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
public void ShowHeroSkillCard(PuzzleHero hero)
|
|
{
|
|
// List<int> boardIndexList = new List<int>();
|
|
// for(int j=0;j<Boards.Count;j++){
|
|
// if(!Boards[j].heroSkillDisplay || Boards[j].GetHeroSkillCanDisplay(hero)){
|
|
// boardIndexList.Add(j);
|
|
// }
|
|
// }
|
|
// if(boardIndexList.Count>0){
|
|
// int currBoardIndex = Random.Range(0,boardIndexList.Count);
|
|
// Boards[boardIndexList[currBoardIndex]].CreateHeroSkillCard(hero);
|
|
// }
|
|
bool setfirst = false;
|
|
List<CountAndPosData> finalPosAndCount = new List<CountAndPosData>();
|
|
for (int j = 0; j < Boards.Count; j++)
|
|
{
|
|
List<CountAndPosData> currPosAndCount = Boards[j].GetMinEffectPosList(hero);
|
|
if (currPosAndCount.Count > 0 && !setfirst)
|
|
{
|
|
setfirst = true;
|
|
finalPosAndCount = currPosAndCount;
|
|
}
|
|
else if (currPosAndCount.Count > 0)
|
|
{
|
|
if (currPosAndCount[0].Count > finalPosAndCount[0].Count)
|
|
{
|
|
finalPosAndCount = currPosAndCount;
|
|
}
|
|
else if (currPosAndCount[0].Count == finalPosAndCount[0].Count)
|
|
{
|
|
finalPosAndCount.AddRange(currPosAndCount);
|
|
}
|
|
}
|
|
}
|
|
if (finalPosAndCount.Count < 0)
|
|
{
|
|
return;
|
|
}
|
|
int finalIndex = Random.Range(0, finalPosAndCount.Count);
|
|
Boards[finalPosAndCount[finalIndex].BoardIndex].CreateHeroSkillCard(hero, finalPosAndCount[finalIndex].PosData);
|
|
}
|
|
|
|
public void MonsterCastSkill(PuzzleMonster monster, PuzzleHero target, int skillID)
|
|
{
|
|
SkillConfig? config = GetSkillConfig(skillID);
|
|
if (config == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DamageHero(target, config.Value.SkillDamage);
|
|
}
|
|
|
|
public void Win(int boardIndex)
|
|
{
|
|
GameFinished = true;
|
|
|
|
Boards[boardIndex].winPopup.OpenPopup();
|
|
AudioManager.instance.PopupWinAudio();
|
|
Boards[boardIndex].SaveLevelInfo();
|
|
}
|
|
|
|
public void Lose(int boardIndex)
|
|
{
|
|
GameFinished = true;
|
|
|
|
Boards[boardIndex].losePopup.OpenPopup();
|
|
AudioManager.instance.PopupLoseAudio();
|
|
}
|
|
|
|
public void DamageHero(PuzzleHero hero, PuzzleDefine.Attribute damage)
|
|
{
|
|
PuzzleDefine.Attribute afterHealth = hero.Health - damage;
|
|
if (afterHealth.Value < 0)
|
|
{
|
|
afterHealth.Value = 0;
|
|
}
|
|
|
|
hero.SetHealth(afterHealth);
|
|
hero.OnAfterDamaged?.Invoke();
|
|
|
|
Debug.Log($"Hero {hero.name} took {damage.Value} damage.");
|
|
|
|
if (afterHealth.Value == 0)
|
|
{
|
|
hero.SetIsDead(true);
|
|
|
|
if (CheckLoseCondition())
|
|
{
|
|
Lose(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
public delegate void OnDamageEvent(DamageMonsterParam param);
|
|
[System.Serializable]
|
|
public struct DamageMonsterParam
|
|
{
|
|
public PuzzleMonster Monster;
|
|
public DamageComposition Composition;
|
|
public TimingNames TimingUsed;
|
|
}
|
|
public void DamageMonster(DamageMonsterParam param)
|
|
{
|
|
PuzzleDefine.Attribute afterHealth = param.Monster.Health - param.Composition.Damage;
|
|
if (afterHealth.Value < 0)
|
|
{
|
|
afterHealth.Value = 0;
|
|
}
|
|
|
|
param.Monster.SetHealth(afterHealth);
|
|
|
|
param.Monster.OnAfterDamagedEvent?.Invoke(param);
|
|
|
|
Debug.Log($"Monster took {param.Composition.Damage.Value} damage.");
|
|
}
|
|
|
|
public void MonsterPreReleaseSkill(PreReleaseSkillParam param)
|
|
{
|
|
param.Monster.OnAfterPreReleaseSkillEvent?.Invoke(param);
|
|
}
|
|
|
|
public PuzzleHero GetHeroWithItemType(ITEM_TYPE itemType)
|
|
{
|
|
foreach (var item in PuzzleGameMode.main.GamePlayer.Heroes)
|
|
{
|
|
if (item.ElementType == PuzzleGameMode.GetElementType(itemType))
|
|
{
|
|
return item;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public delegate void OnMonsterPreReleaseSkillEvent(PreReleaseSkillParam param);
|
|
[System.Serializable]
|
|
public struct PreReleaseSkillParam
|
|
{
|
|
public PuzzleMonster Monster;
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void Start()
|
|
{
|
|
Start_Game();
|
|
Start_Beat();
|
|
Start_Debug();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
Update_Beat();
|
|
Update_MonsterSkillAnim();
|
|
Update_MonsterSkillDamage();
|
|
Update_Debug();
|
|
if (Input.GetKeyDown(KeyCode.Q))
|
|
{
|
|
ShowHeroSkillCard(GamePlayer.Heroes[3]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[System.Serializable]
|
|
public struct SkillConfig
|
|
{
|
|
public int SkillID;
|
|
public float SkillPreAnim;
|
|
public Vector2Int SkillSize;
|
|
public PuzzleDefine.Attribute SkillDamage;
|
|
public HeroSkillEffect HeroSkillEffect;
|
|
|
|
public UnityEngine.Sprite SkillCardImage;
|
|
public UnityEngine.Video.VideoClip VideoClip;
|
|
}
|