using System; using System.Numerics; using IVDataFormat; using Spine.Unity; [Serializable] public struct MateGlobalProperty { public uint mateMaxLevel; public float mateDefaultMoveSpeed; } [Serializable] public struct MatePlayerProperty { public static readonly byte _invalidSlotIndex = 0xff; public uint id; public string levelExp; public bool isUnlocked; public byte settedSlotIndex; public MatePlayerProperty(uint id) { this.id = id; levelExp = BigInteger.Zero.ToString(); isUnlocked = false; settedSlotIndex = _invalidSlotIndex; } } public class MateData { MateProperty property; MatePlayerProperty playerProperty; ValueModifier skillDmgModifierPerLevel; public MateProperty Property => property; public uint ID => property.id; public uint Grade => property.grade; public IVMate Prefab => property.matePrefab; public SkeletonDataAsset SkeletonDataAsset => property.skeletonDataAsset; public float DefaultMoveSpeed => MateDataGroup.Instance.GlobalProperty.mateDefaultMoveSpeed; public SkillData Skill { get; private set; } public string NameKey => FormatString.StringFormat("mate_name_{0}", ID); public string GradeLetterImgAtlasPath => GameProperty.Instance.GradeLetterIconDefaultName; public string GradeLetterImgNameInAtlas => Grade.ToString(); public string GradeImgAtlasPath => GameProperty.Instance.GradeIconAtlasName; public string GradeImgNameInAtlas => Grade.ToString(); public string Img2DPath => FormatString.StringFormat("{0}_{1}", GameProperty.Instance.Mate2DImageDefaultName, ID); public string ImgFull2DPath => FormatString.StringFormat("{0}_{1}_full", GameProperty.Instance.Mate2DImageDefaultName, ID); public string SDImgAtlasPath => GameProperty.Instance.MateSDImageAtlasName; public string SDImgNameInAtlas => ID.ToString(); public static uint MaxLevel => MateDataGroup.Instance.GlobalProperty.mateMaxLevel; public uint Level { get; private set; } public BigInteger LevelExp { get; private set; } public Nationality Nationality => property.nationality; public bool IsUnlocked { get => playerProperty.isUnlocked; set { if (playerProperty.isUnlocked == value) return; playerProperty.isUnlocked = value; } } public bool IsSetted => playerProperty.settedSlotIndex != MatePlayerProperty._invalidSlotIndex; public int SettedSlotIndex => playerProperty.settedSlotIndex; public MateData(MateProperty property, MatePlayerProperty playerProperty) { this.property = property; this.playerProperty = playerProperty; LevelExp = BigInteger.Parse(playerProperty.levelExp); Level = GetLevelByExp(LevelExp, Grade); if (property.skillProperty != null) { Skill = new SkillData(property.skillProperty); var buffGroup = GamePlayBuffMgr.Instance.BuffGroup; if (Skill.TryGetStat(StatID.SkillDamage, out var skillDamage)) { skillDmgModifierPerLevel = new ValueModifier(ValueModifier.Type.Flat, 20 * (Level - 1)); skillDmgModifierPerLevel.From = this; skillDamage.AddModifier(skillDmgModifierPerLevel); } foreach (var buff in buffGroup.GetBuffs(eEffectType.IncMateSkillDmg)) OnChangeBuff(buff, true); foreach (var buff in buffGroup.GetBuffs(eEffectType.DecMateSkillCool)) OnChangeBuff(buff, true); buffGroup.OnChanged += OnChangeBuff; } } private void OnChangeBuff(BuffData buff, bool added) { if(!buff.SubKey.Match(Property)) return; StatData targetStat = null; if (buff.Key == eEffectType.DecMateSkillCool) Skill.TryGetStat(StatID.CoolTime, out targetStat); else if(buff.Key == eEffectType.IncMateSkillDmg) Skill.TryGetStat(StatID.SkillDamage, out targetStat); if(targetStat != null) { if (added) targetStat.AddModifier(buff.Value); else targetStat.RemoveModifier(buff.Value); } } public bool AddLevelExp(BigInteger exp) { LevelExp += exp; playerProperty.levelExp = LevelExp.ToString(); uint prevLevel = Level; Level = GetLevelByExp(LevelExp, Grade, Level); if (prevLevel != Level) { if (Skill.TryGetStat(StatID.SkillDamage, out var skillDamage)) { skillDamage.RemoveModifier(skillDmgModifierPerLevel); skillDmgModifierPerLevel = new ValueModifier(ValueModifier.Type.Flat, 20 * (Level - 1)); skillDmgModifierPerLevel.From = this; skillDamage.AddModifier(skillDmgModifierPerLevel); } return true; } return false; } public BigInteger GetRequireExpToNextLv() => GetTotalExpToLevel(Level + 1, Grade) - GetTotalExpToLevel(Level, Grade); public void SetSlotIndex(int slotIndex) { if (slotIndex >= GameProperty.Instance.MateMaxCount) return; playerProperty.settedSlotIndex = (byte)slotIndex; } public void RemoveSlotIndex() { playerProperty.settedSlotIndex = MatePlayerProperty._invalidSlotIndex; } public static uint GetLevelByExp(BigInteger exp, uint grade, uint offset = 2) { if (offset < 2) offset = 2; for (uint i = offset; i <= MaxLevel; i++) { if (exp < GetTotalExpToLevel(i, grade)) return i - 1; } return MaxLevel; } public static BigInteger GetTotalExpToLevel(uint level, uint grade) { //TODO : this is temp code const uint baseExp = 10000; const uint expPerLevel = 100000; if (level < 2) return 0; float mulRatePerGrade = 2.5f; if (grade <= 1) mulRatePerGrade = 1.0f; else if (grade == 2) mulRatePerGrade = 1.5f; else if (grade == 3) mulRatePerGrade = 2.0f; else if (grade == 4) mulRatePerGrade = 2.5f; else if (grade == 5) mulRatePerGrade = 3.0f; //require exp for next level //baseExp + (expPerLevel * (level - 2) * mulRatePerGrade) uint a = level - 1; uint b = a - 1; return (BigInteger)baseExp * a + (BigInteger)(Utility.PermutationAdd(b) * expPerLevel * mulRatePerGrade); } public static int CompareByID(MateData a, MateData b) => a.ID.CompareTo(b.ID); public static int CompareByGrade(MateData a, MateData b) => a.Grade.CompareTo(b.Grade); public static int CompareBySettedSlotIndex(MateData a, MateData b) => a.playerProperty.settedSlotIndex.CompareTo(b.playerProperty.settedSlotIndex); }