using System.Globalization; using System.Numerics; using System; using IVDataFormat; using IVServerFormat; using System.Collections.Generic; [Serializable] public class DispatchMissionProperty { public enum MissionType { Normal, Battle, } public uint id; public MissionType type; public ItemProp[] rewardItemCandidates; } [Serializable] public class DispatchMissionPlayerProperty { public uint id = 0; public bool gotReward = true; public bool needGetResult = false; public bool hasResult = false; public bool isSucceed = false; public string recommendBattlePower = "0"; public uint[] recommendNationalities = new uint[0]; public uint[] bonusRewardTargets = new uint[0]; public string endTime = DateTime.MaxValue.ToString(FormatString.DateTimeParse, CultureInfo.InvariantCulture); } public class DispatchMissionData { DispatchData.Area area; DispatchMissionProperty property; DispatchMissionPlayerProperty playerProperty; int requestCount = 0; Action, bool> onDoneGetReward; public DispatchMissionProperty.MissionType Type => property.type; public uint ID => property.id; public bool GotReward => playerProperty.gotReward; public bool HasResult => playerProperty.hasResult; public bool IsSucceed => playerProperty.isSucceed; public bool NeedGetResult => playerProperty.needGetResult; public float TotalProgressTime => area.MissionProgressTime + (area.MissionMoveTime * 2); public DateTime EndTime => DateTime.ParseExact(playerProperty.endTime, FormatString.DateTimeParse, CultureInfo.InvariantCulture); public BigInteger RecommendBattlePower => BigInteger.Parse(playerProperty.recommendBattlePower); public string ContentStringKey => FormatString.StringFormat("dispatch_content_{0}_{1}", area.AreaType, ID); public string ContentIconAtlasPath => GameProperty.Instance.MissionContentIconAtlasName; public string ContentIconNameInAtlas => ContentStringKey; public Nationality[] RecommendNationalities { get; set; } public nGoods[] DefaultRewards { get; private set; } public nGoods[] TotalRewards { get { var employees = new SlotGroup(area.EmployeeIDs.Count); for (int i = 0; i < area.EmployeeIDs.Count; i++) { if (MateDataGroup.Instance.TryGetMateData(area.EmployeeIDs[i], out var mate)) employees[i] = mate; } var result = new nGoods[DefaultRewards.Length]; for (int i = 0; i < DefaultRewards.Length; i++) { result[i] = DefaultRewards[i].Clone() as nGoods; } var dispatchLvBonus = bonusRewardByDispatchLv; if (result.TryFind(dispatchLvBonus.IsSameGoods, out var goods)) goods.propertyCount += dispatchLvBonus.propertyCount; var nationalityBonus = GetBonusRewardByNationality(employees); if (result.TryFind(nationalityBonus.IsSameGoods, out goods)) goods.propertyCount = nationalityBonus.propertyCount; var employeeCountBonus = GetBonusRewardByEmployeeCount(employees); if (result.TryFind(employeeCountBonus.IsSameGoods, out goods)) goods.propertyCount = employeeCountBonus.propertyCount; return result; } } public nGoods bonusRewardByDispatchLv => property.rewardItemCandidates[playerProperty.bonusRewardTargets[0]].ConvertTo(area.DispatchData.DispatchLevel * 10); public DispatchMissionData(DispatchData.Area area, DispatchMissionProperty property, DispatchMissionPlayerProperty playerProperty) { this.area = area; this.property = property; this.playerProperty = playerProperty; RecommendNationalities = new Nationality[playerProperty.recommendNationalities.Length]; for (int i = 0; i < playerProperty.recommendNationalities.Length; i++) { RecommendNationalities[i] = new Nationality { kind = (Nationality.Kind)playerProperty.recommendNationalities[i] }; } DefaultRewards = new nGoods[property.rewardItemCandidates.Length]; for (int i = 0; i < property.rewardItemCandidates.Length; i++) { //TODO : temp reward amount DefaultRewards[i] = property.rewardItemCandidates[i].ConvertTo(area.DispatchData.DispatchLevel * 10); } } public void Start(DateTime now) { playerProperty.needGetResult = true; playerProperty.hasResult = false; playerProperty.gotReward = false; //미션 소요 시간 : 수행 시간 + 지역 왕복 시간 float totalProgressTime = area.MissionProgressTime + (area.MissionMoveTime * 2); playerProperty.endTime = now.AddSeconds(totalProgressTime).ToString(FormatString.DateTimeParse, CultureInfo.InvariantCulture); } public nGoods GetBonusRewardByNationality(SlotGroup mates) { int[] remainCounts = new int[(int)Nationality.Kind.Count]; for (int i = 0; i < RecommendNationalities.Length; i++) { remainCounts[(int)RecommendNationalities[i].kind]++; } int matchCount = 0; mates.LoopValidSlot((mate) => { int idx = (int)mate.Nationality.kind; if (remainCounts[idx] > 0) { remainCounts[idx]--; matchCount++; } return true; }); return property.rewardItemCandidates[playerProperty.bonusRewardTargets[1]].ConvertTo(matchCount * 10); } public nGoods GetBonusRewardByEmployeeCount(SlotGroup mates) { int count = 0; mates.LoopValidSlot((mate) => { count++; return true; }); return property.rewardItemCandidates[playerProperty.bonusRewardTargets[2]].ConvertTo(count * 10); } public bool InProgress(DateTime now) => now < EndTime; public bool IsAvailableGetResult(DateTime now) { //미션 지역 도착 + 수행 시간 이후 결과 확인 가능. return EndTime.AddSeconds(-area.MissionMoveTime) <= now; } public void SetResult(BigInteger battlePower) { if (!playerProperty.needGetResult) return; var scRate = GetSuccessRate(battlePower); var rand = UnityEngine.Random.Range(0.0f, 1.0f); playerProperty.isSucceed = rand <= scRate; playerProperty.needGetResult = false; playerProperty.hasResult = true; } public void RemoveResult() { playerProperty.hasResult = false; playerProperty.isSucceed = false; } public void TryGetReward(Action, bool> onRequestDone) { if(requestCount > 0) return; requestCount = DefaultRewards.Length; onDoneGetReward = onRequestDone; foreach (var reward in TotalRewards) { SvConnectManager.Instance.RequestSvPost(true, 0, UrlApi.GetUrl(UrlApi.Property), typeof(nGoodsGet), OnGetRewardSuccess, OnGetRewardFail, reward, true); } } private void OnGetRewardSuccess(object result, object request) { if(--requestCount <= 0) { playerProperty.gotReward = true; nGoodsGet data = result as nGoodsGet; var totalReward = TotalRewards; DataHandler.AddGoods(totalReward, data.playCurrency, true); onDoneGetReward?.Invoke(totalReward, true); } } private void OnGetRewardFail(SvError error, object request) => onDoneGetReward?.Invoke(new nGoods[0], false); public int GetSuccessRateLevel(BigInteger battlePower) { if (Type == DispatchMissionProperty.MissionType.Normal) return 5; //-50미만 : 매우낮음 //-50이상 ~ -10미만 : 낮음 //-10이상 ~ 10미만 : 보통 //10이상 ~ 50미만 : 높음 //50이상 : 매우높음 var bpBoundaries = new BigInteger[] { RecommendBattlePower - (RecommendBattlePower / 100) * 50, RecommendBattlePower - (RecommendBattlePower / 100) * 10, RecommendBattlePower + (RecommendBattlePower / 100) * 10, RecommendBattlePower + (RecommendBattlePower / 100) * 50, }; for (int i = 0; i < bpBoundaries.Length; i++) { if (battlePower < bpBoundaries[i]) return i + 1; } return 5; } public float GetSuccessRate(BigInteger battlePower) { int scLevel = GetSuccessRateLevel(battlePower); if (scLevel <= 1) return 0.05f; else if (scLevel == 2) return 0.3f; else if (scLevel == 3) return 0.7f; else if (scLevel == 4) return 0.9f; else return 1.0f; } public uint CalculateExp() { //TODO : this is temp code const uint normalBaseExp = 5; const uint normalExpPerLevel = 1; const uint battleBaseExp = 10; const uint battleExpPerLevel = 1; switch (Type) { case DispatchMissionProperty.MissionType.Normal: return normalBaseExp + (area.DispatchData.DispatchLevel - 1) * normalExpPerLevel; case DispatchMissionProperty.MissionType.Battle: return battleBaseExp + (area.DispatchData.DispatchLevel - 1) * battleExpPerLevel; } return 0; } }