using CodeStage.AntiCheat.ObscuredTypes; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using Random = UnityEngine.Random; public static class MyValue { public static string OnestoreURL = "https://onesto.re/0000759501"; public static int UTC = 9; public static int ItemID_Gem = 101, ItemID_Gold = 201, ItemID_Soul = 301, ItemID_Potion = 401, ItemID_Exp = 501, ItemID_EvolutionMaxStone = 601; public static Dictionary dic_NeedEvolution = new Dictionary { { eEquipmentParts.Ring, 1 }, { eEquipmentParts.Necklace, 2 }, { eEquipmentParts.SubWeapon, 3 } }; public static int PCEvolutionMaxLv = 6, SealMixCount = 3; public static float MaxAvoid = 0.9f, MaxCri = 0.9f; public static string OptionKey_UserType = "Option_UserType"; // 0 구글, 1 애플, 2 게스트 public static string OptionKey_UserID = "Option_UserID"; public static string SaveDataFileName = "SaveData"; public static ServerData sdata => ServerInfo.Ins.m_ServerData; public static MyStageData m_MyStageData = new MyStageData(); public static void Set_StageData(int chapter, int stage) { // 정인호 : 난이도, 챕터 정보도 필요 m_MyStageData.m_Diff = 1; m_MyStageData.m_Chapter = chapter; m_MyStageData.m_Stage = stage; m_MyStageData.m_NodeIndex = -1; m_MyStageData.m_GameMode = eGameMode.Stage; } public static ActorStatInfo Get_PCActorStatInfo(int pcid) { var pc = InGameInfo.Ins.m_PCActor; var pctdata = table_pclist.Ins.Get_Data_orNull(pcid); pc.Set(0, pctdata, IngameUIManager.Ins.PC_HUD_HPShield, sdata); return Get_ActorStatInfo(pc, pctdata); } public static ActorStatInfo Get_ActorStatInfo(Actor actor, ActorTableDataBase actordata) { var stat = new ActorStatInfo(actor, true); stat.Set_Stat(eStat.AttackCoolTime, actordata.f_AttackCoolTime); stat.Set_Stat(eStat.Attack_Min, actordata.n_AttackMin); stat.Set_Stat(eStat.Attack_Max, actordata.n_AttackMax); stat.Set_Stat(eStat.Cri, actordata.f_Cri); stat.Set_Stat(eStat.CriDmg, actordata.f_CriDmg); stat.Set_Stat(eStat.HitRate, 100); if (actor.IsRole(eRole.Mob)) { stat.Set_Stat(eStat.HP, actordata.n_HP, true); stat.Set_Stat(eStat.Shield, actordata.n_Shield, true); stat.Set_Stat(eStat.Avoid_Melee, actordata.n_Avoid_Meele * 100); stat.Set_Stat(eStat.Avoid_Range, actordata.n_Avoid_Range * 100); } else { var sdata = actor.Get_ServerData(); var pcid = actor.Get_Data().n_ID; var addstats = new Dictionary { { eStat.Attack_Min, 0 }, { eStat.Attack_Max, 0 }, { eStat.MaxHP, 0 }, { eStat.MaxShield, 0 }, { eStat.HitRate, 0 }, { eStat.Avoid_Melee, 0 }, { eStat.Avoid_Range, 0 }, { eStat.Cri, 0 }, { eStat.CriDmg, 0 }, }; // 2025. 12. 08 : 진화값 추가 var evolution = sdata.Get_PCEvolution(pcid); for (int i = 1; i < evolution; i++) { var evolutionTdata = table_PCEvolution.Ins.Get_Data(pcid, i); addstats[eStat.Attack_Min] += evolutionTdata.n_AttackValue; addstats[eStat.Attack_Max] += evolutionTdata.n_AttackValue; addstats[eStat.MaxHP] += evolutionTdata.n_MaxHPValue; addstats[eStat.MaxShield] += evolutionTdata.n_MaxShieldValue; addstats[eStat.HitRate] += evolutionTdata.n_HitRateValue; } // 2025. 12. 09 : 한계돌파값 추가 foreach (var key in addstats.Keys.ToList()) { var lv = sdata.Get_PCEvolutionMax(pcid, key); if (lv > 0) { var tData = table_PCEvolutionMax.Ins.Get_Data(key); addstats[key] += lv * (tData.e_Stat == eStat.Avoid_Melee || tData.e_Stat == eStat.Avoid_Range ? tData.f_Value * 100 : tData.f_Value); } } // 2025.12.27 : 장비값 추가 var equipitems = sdata.Get_EquipItems(pcid); for (int i = 0; i < equipitems.Count; i++) { var equip = equipitems[i]; if (equip == null || equip.ID == 0) continue; var tdata = table_EquipmentList.Ins.Get_Data(equip.TableID); if (tdata == null) continue; int lv = equip.Lv; // ===== Main Option ===== if (tdata.n_MainOtion > 0) { var mainOpt = table_StatusOptionSet.Ins.Get_Data(tdata.n_MainOtion); if (mainOpt != null) { ApplyStat(stat, addstats, mainOpt.e_Stat1, tdata.Get_Value(eEquipmentStat.Main1, actordata.n_ID, lv)); ApplyStat(stat, addstats, mainOpt.e_Stat2, tdata.Get_Value(eEquipmentStat.Main2, actordata.n_ID, lv)); } } // ===== Sub Options ===== ApplyStat(stat, addstats, tdata, tdata.n_SubOtion1, eEquipmentStat.Sub1, actordata.n_ID, lv); ApplyStat(stat, addstats, tdata, tdata.n_SubOtion2, eEquipmentStat.Sub2, actordata.n_ID, lv); ApplyStat(stat, addstats, tdata, tdata.n_SubOtion3, eEquipmentStat.Sub3, actordata.n_ID, lv); ApplyStat(stat, addstats, tdata, tdata.n_SubOtion4, eEquipmentStat.Sub4, actordata.n_ID, lv); } // 2026. 01. 02 : 장비 세트 옵션값 추가 var setitemData = sdata.Get_EquipSetItemData(actordata.n_ID); if (setitemData != null) foreach (var item in setitemData.dic_Stat) ApplyStat(stat, addstats, item.Key, item.Value); stat.Set_Stat(true, eStat.Attack_Min, addstats[eStat.Attack_Min]); stat.Set_Stat(true, eStat.Attack_Max, addstats[eStat.Attack_Max]); stat.Set_Stat(true, eStat.HitRate, addstats[eStat.HitRate]); stat.Set_Stat(true, eStat.Cri, addstats[eStat.Cri]); stat.Set_Stat(true, eStat.CriDmg, addstats[eStat.CriDmg]); stat.Set_Stat(eStat.HP, actordata.n_HP + addstats[eStat.MaxHP], true); stat.Set_Stat(eStat.Shield, actordata.n_Shield + addstats[eStat.MaxShield], true); stat.Set_Stat(eStat.Avoid_Melee, actordata.n_Avoid_Meele + addstats[eStat.Avoid_Melee]); stat.Set_Stat(eStat.Avoid_Range, actordata.n_Avoid_Range + addstats[eStat.Avoid_Range]); } // 테스트 : 포션 임의 세팅 stat.Set_Stat(eStat.Potion, 5, true); return stat; } static void ApplyStat(ActorStatInfo stat, Dictionary addstats, eStat statType, float value) { if (addstats.ContainsKey(statType)) addstats[statType] += value; else stat.Set_Stat(true, statType, value); } static void ApplyStat(ActorStatInfo stat, Dictionary addstats, EquipmentListTableData tdata, int optionId, eEquipmentStat estat, int pcid, int lv) { if (optionId <= 0) return; var optData = table_StatusOptionSet.Ins.Get_Data(optionId); if (optData == null) return; ApplyStat(stat, addstats, optData.e_Stat1, tdata.Get_Value(estat, pcid, lv)); } public static Dictionary dic_GradeColor = new Dictionary { { eGrade.None, new Color32(161,152,148,255) }, { eGrade.Common, new Color32(161,152,148,255) }, { eGrade.UnCommon, new Color32(158,215,35,255) }, { eGrade.Rare, new Color32(25,197,240,255) }, { eGrade.Hero, new Color32(191,106,219,255) }, { eGrade.Legend, new Color32(252,218,32,255) }, { eGrade.Grade6, new Color32(240,65,65,255) }, }; } public enum eStageEnterType { Normal, Tool, Tool_Mob } public class MyStageData { public eStageEnterType m_EnterType = eStageEnterType.Normal; public eGameMode m_GameMode; ObscuredInt _diff = 1; public int m_Diff { get { return _diff; } set { _diff = value; _diff.RandomizeCryptoKey(); } } ObscuredInt _chapter = 1; public int m_Chapter { get { return _chapter; } set { _chapter = value; _chapter.RandomizeCryptoKey(); } } ObscuredInt _stage; public int m_Stage { get { return _stage; } set { _stage = value; _stage.RandomizeCryptoKey(); } } ObscuredInt _node; public int m_NodeIndex { get { return _node; } set { _node = value; _node.RandomizeCryptoKey(); // 스테이지 진행 상황 UI 갱신 if (_node > -1) IngameUIManager.Ins.m_DungeonInfo.m_DungeonProcess.Set(); } } public IngameStageData m_StageData; StageNodeData m_CurNode; public void Set_CurNodeData() { m_CurNode = m_StageData.list_Nodedata.Count > m_NodeIndex ? m_StageData.list_Nodedata[m_NodeIndex] : null; if (m_CurNode != null && m_CurNode.m_Type == eStageNodeType.Random) { var rdnnodedata = m_CurNode.Get_Data(); var tabledata = table_RandomPatternConfig.Ins.Get_Data(rdnnodedata.RandomID); int pick = -1; int safety = 20; // 무한루프 방지 while (safety-- > 0) { var tmp = DSUtil.WeightedPickIndex(tabledata.list_Weight, x => x); var type = eStageNodeType.Nothing; switch (tmp) { case 1: type = eStageNodeType.Mob; break; case 2: type = eStageNodeType.BuffDebuff; break; case 3: type = eStageNodeType.Merchant; break; case 4: type = eStageNodeType.Treasure; break; case 5: type = eStageNodeType.NPC; break; case 6: type = eStageNodeType.Sanctuary; break; case 7: type = eStageNodeType.TwoWay; break; } // 제한 걸린 타입이면 다시 뽑기 if (m_StageData.Use_RandomNodeLimit(type)) { pick = tmp; break; } } switch (pick) { default: // 없음 m_CurNode.Set_Data(eStageNodeType.Nothing, new StageNodeData_Nothing()); break; case 1: // 몬스터 등장 { var mobnodedata = new StageNodeData_Mob(); var patternindex = DSUtil.WeightedPickIndex(m_StageData.list_PatternData, x => x.m_Weight); var patterndata = m_StageData.list_PatternData[patternindex]; mobnodedata.PatternID = patterndata.m_PatternID; mobnodedata.MobCount = Mathf.Max(1, patterndata.m_MobCount); mobnodedata.list_MobID = new List(); var moblst = m_StageData.Get_MonsterList(false); for (int i = 0; i < mobnodedata.MobCount; i++) { var mobindex = DSUtil.WeightedPickIndex(moblst, x => x.m_Weight); mobnodedata.list_MobID.Add(moblst[mobindex].m_MobID); } m_CurNode.Set_Data(eStageNodeType.Mob, mobnodedata); } break; case 2: // 버프/디버프 { var nodedata = new StageNodeData_BuffDebuff(); nodedata.BuffID = table_BuffPatternConfig.Ins.Get_RandomData().n_BuffOptionID; m_CurNode.Set_Data(eStageNodeType.BuffDebuff, nodedata); } break; case 3: // 암상인 { var nodedata = new StageNodeData_Merchant(); nodedata.MerchantID = table_MerchantConfig.Ins.Get_RandomData().n_MerchantID; m_CurNode.Set_Data(eStageNodeType.Merchant, nodedata); } break; case 4: // 보물 상자 { var nodedata = new StageNodeData_Treasure(); var tdata = table_TreasureBoxConfig.Ins.Get_RandomData(); nodedata.BoxID = tdata.n_TreasureID; nodedata.MimicRate = tdata.f_TreasureMimicRate; m_CurNode.Set_Data(eStageNodeType.Treasure, nodedata); } break; case 5: // NPC { var nodedata = new StageNodeData_NPC(); var tdata = table_NPCConfig.Ins.Get_RandomData(); nodedata.NPCID = tdata.n_NPCID; m_CurNode.Set_Data(eStageNodeType.NPC, nodedata); } break; case 6: // 성소 { var nodedata = new StageNodeData_Mine(); var tdata = table_SanctuaryConfig.Ins.Get_RandomData(); nodedata.SanctuaryID = tdata.n_BuffOptionID; m_CurNode.Set_Data(eStageNodeType.Sanctuary, nodedata); } break; case 7: // 갈림길 { // TODO 정인호 : 갈림길 세부적인 건 추후에... var nodedata = new StageNodeData_TwoWay(); nodedata.MapConfig1 = ""; nodedata.MapConfig2 = ""; nodedata.ChangeRate = 0.2f; m_CurNode.Set_Data(eStageNodeType.TwoWay, nodedata); } break; } } } public StageNodeData Get_CurNodeData() { return m_CurNode; } public int Get_MobID_onStage(eToolMobType type) { List candidates = new List(); if (type == eToolMobType.Melee) { foreach (var temp in m_StageData.list_MobData) { var mobdata = table_monsterlist.Ins.Get_Data_orNull(temp.m_MobID); if (mobdata != null && mobdata.Get_AttackType() == eAttackType.Melee) candidates.Add(temp); } } else if (type == eToolMobType.Range) { foreach (var temp in m_StageData.list_MobData) { var mobdata = table_monsterlist.Ins.Get_Data_orNull(temp.m_MobID); if (mobdata != null && mobdata.Get_AttackType() == eAttackType.Range) candidates.Add(temp); } } if (candidates.Count == 0) return 0; // 없을 때는 0 반환 // 가중치 합계 int totalWeight = 0; foreach (var c in candidates) totalWeight += c.m_Weight; // 랜덤 뽑기 int rand = UnityEngine.Random.Range(1, totalWeight + 1); int cumulative = 0; foreach (var c in candidates) { cumulative += c.m_Weight; if (rand <= cumulative) return c.m_MobID; } return candidates[0].m_MobID; // fallback } } public class ActorStatInfo { public Actor m_Actor; Dictionary dic_StatValue = new Dictionary(); Dictionary dic_StatValue_Buff = new Dictionary(); Dictionary dic_StatValue_Debuff = new Dictionary(); bool bInit = false; public bool IsMainAttack; void Init() { if (!bInit) { bInit = true; for (eStat i = 0; i < eStat.Max; i++) dic_StatValue.Add(i, 0d); } } public void Clear_All() { var keys = new List(dic_StatValue.Keys); foreach (var key in keys) dic_StatValue[key] = 0d; Clear_BuffDebuff(); } public void Clear_BuffDebuff() { dic_StatValue_Buff.Clear(); dic_StatValue_Debuff.Clear(); } public ActorStatInfo(Actor actor, bool mainattack) { Init(); m_Actor = actor; IsMainAttack = mainattack; } public double Get_Stat(eStat key) { if (dic_StatValue.ContainsKey(key)) return dic_StatValue[key].GetDecrypted(); return 0d; } public double Get_TotalStat(eStat stat) { var rtn = Get_Stat(stat) + Get_BuffStat(stat) - Get_DebuffStat(stat); switch (stat) { // 정수 case eStat.HP: case eStat.Shield: case eStat.Potion: case eStat.HitRate: return (int)rtn; case eStat.MaxHP: { rtn += Get_BuffStat(eStat.MaxHP_Add); var mul = Get_BuffStat(eStat.MaxHP_Mul); return (int)(rtn + (rtn * mul)); } case eStat.MaxShield: { rtn += Get_BuffStat(eStat.MaxShield_Add); var mul = Get_BuffStat(eStat.MaxShield_Mul); return (int)(rtn + (rtn * mul)); } case eStat.Attack_Min: { rtn += Get_Stat(eStat.Attack) + Get_BuffStat(eStat.Attack) - Get_DebuffStat(eStat.Attack); var card = m_Actor.Get_CardSkillData_orNull(eCardType.G5_FlatDamageMinMaxEqual); if (card != null) { var comparestat = eStat.Attack_Max; var maxdmg = Get_Stat(comparestat) + Get_BuffStat(comparestat) - Get_DebuffStat(comparestat); rtn = Mathf.Max((int)rtn, (int)maxdmg); } var mul = Get_BuffStat(eStat.DmgMul); return (int)(rtn + (rtn * mul)); } case eStat.Attack_Max: { rtn += Get_Stat(eStat.Attack) + Get_BuffStat(eStat.Attack) - Get_DebuffStat(eStat.Attack); var card = m_Actor.Get_CardSkillData_orNull(eCardType.G5_FlatDamageMinMaxEqual); if (card != null) { var comparestat = eStat.Attack_Min; var maxdmg = Get_Stat(comparestat) + Get_BuffStat(comparestat) - Get_DebuffStat(comparestat); rtn = Mathf.Max((int)rtn, (int)maxdmg); } var mul = Get_BuffStat(eStat.DmgMul); return (int)(rtn + (rtn * mul) + Get_BuffStat(eStat.MaxAttack_byShield)); } // 정수 분기 (캐릭터 실수, 몬스터 정수) case eStat.Avoid_Melee: case eStat.Avoid_Range: if (m_Actor.IsRole(eRole.PC)) { var totalavoid = rtn * 0.01f + Get_BuffStat(eStat.AvoidAll_1Time); totalavoid += Get_BuffStat(eStat.Avoid_G1_MaxDodgeWhenHpBelow); totalavoid += Get_TotalStat(eStat.AllEvasion); Set_Buff(eStat.AvoidAll_1Time, 0); return Math.Min(MyValue.MaxAvoid, totalavoid); } return (int)rtn; // 백분율 case eStat.Cri: if (IsMainAttack) { rtn += Get_BuffStat(eStat.AddCri_1Time) + Get_BuffStat(eStat.AddCri_Once); Set_Buff(eStat.AddCri_1Time, 0); Set_Buff(eStat.AddCri_Once, 0); } rtn = Math.Min(MyValue.MaxCri, rtn); break; case eStat.CriDmg: { rtn += Get_BuffStat(eStat.AddCriDmg_1Time); Set_Buff(eStat.AddCriDmg_1Time, 0); } return rtn; case eStat.AttackCoolTime: { var slow = Get_DebuffStat(eStat.Slow); rtn += rtn * slow; var past = Get_BuffStat(eStat.AttackCoolTimeMul); rtn -= rtn * past; if (rtn <= 0) rtn = 0.1f; } break; } return rtn; } public void Set_Stat(bool _add, eStat key, double _val) { if (key >= eStat.Max) return; if (_add) dic_StatValue[key] += _val; else dic_StatValue[key] -= _val; if (key == eStat.HP) { var maxhp = Get_TotalStat(eStat.MaxHP); if (dic_StatValue[key] > maxhp) dic_StatValue[key] = maxhp; else if (dic_StatValue[key] < 0) dic_StatValue[key] = 0; //if (m_Actor) m_Actor.Set_HUD(); } else if (key == eStat.Shield) { var maxshield = Get_TotalStat(eStat.MaxShield); if (dic_StatValue[key] > maxshield) dic_StatValue[key] = maxshield; else if (dic_StatValue[key] < 0) dic_StatValue[key] = 0; //if (m_Actor) m_Actor.Set_HUD(); } else if (key == eStat.Potion) { var maxpotion = Get_TotalStat(eStat.MaxPotion); if (dic_StatValue[key] > maxpotion) dic_StatValue[key] = maxpotion; else if (dic_StatValue[key] < 0) dic_StatValue[key] = 0; } dic_StatValue[key].RandomizeCryptoKey(); } public void Set_Stat(eStat key, double _val, bool setmax = false) { dic_StatValue[key] = _val; dic_StatValue[key].RandomizeCryptoKey(); if (setmax) { if (key == eStat.HP) { dic_StatValue[eStat.MaxHP] = _val; dic_StatValue[eStat.MaxHP].RandomizeCryptoKey(); //if (m_Actor) m_Actor.Set_HUD(); } else if (key == eStat.Shield) { dic_StatValue[eStat.MaxShield] = _val; dic_StatValue[eStat.MaxShield].RandomizeCryptoKey(); //if (m_Actor) m_Actor.Set_HUD(); } else if (key == eStat.Potion) { dic_StatValue[eStat.MaxPotion] = _val; dic_StatValue[eStat.MaxPotion].RandomizeCryptoKey(); } } } public double Get_BuffStat(eStat stat) { return dic_StatValue_Buff.ContainsKey(stat) ? dic_StatValue_Buff[stat] : 0d; } public void Set_Buff(eStat stat, double v) { if (stat >= eStat.Max) return; if (!dic_StatValue_Buff.ContainsKey(stat)) dic_StatValue_Buff.Add(stat, v); else dic_StatValue_Buff[stat] = v; dic_StatValue_Buff[stat].RandomizeCryptoKey(); } public void Set_Buff(bool add, eStat stat, double v) { if (stat >= eStat.Max) return; if (!dic_StatValue_Buff.ContainsKey(stat)) { if (add) dic_StatValue_Buff.Add(stat, v); } else { if (add) dic_StatValue_Buff[stat] += v; else dic_StatValue_Buff[stat] -= v; } dic_StatValue_Buff[stat].RandomizeCryptoKey(); } public double Get_DebuffStat(eStat stat) { return dic_StatValue_Debuff.ContainsKey(stat) ? dic_StatValue_Debuff[stat] : 0d; } public void Set_Debuff(eStat stat, double v) { if (stat >= eStat.Max) return; if (!dic_StatValue_Debuff.ContainsKey(stat)) dic_StatValue_Debuff.Add(stat, v); else dic_StatValue_Debuff[stat] = v; dic_StatValue_Debuff[stat].RandomizeCryptoKey(); } public void Set_Debuff(bool add, eStat stat, double v) { if (!dic_StatValue_Debuff.ContainsKey(stat)) { if (add) dic_StatValue_Debuff.Add(stat, v); } else { if (add) dic_StatValue_Debuff[stat] += v; else dic_StatValue_Debuff[stat] -= v; } dic_StatValue_Debuff[stat].RandomizeCryptoKey(); } public void Set_BuffDebuff(ActorStatInfo statinfo) { foreach (var item in dic_StatValue_Buff) statinfo.Set_Buff(item.Key, item.Value); foreach (var item in dic_StatValue_Debuff) statinfo.Set_Debuff(item.Key, item.Value); } public int Get_Damage() { var dmg = Random.Range((int)Get_TotalStat(eStat.Attack_Min), (int)Get_TotalStat(eStat.Attack_Max) + 1); return Mathf.Max(dmg, 1); // 최소 데미지 1 보장 } public bool isFullHP() { return Get_TotalStat(eStat.HP) >= Get_TotalStat(eStat.MaxHP); } }