WXMC/.svn/pristine/cb/cbadb09572c7ee75f9ea949cd48c1a5de3850fb8.svn-base

1774 lines
47 KiB
Plaintext
Raw Permalink Normal View History

2024-12-04 16:18:46 +08:00
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;
}