using System.Collections.Generic; using System.Globalization; using System; [Serializable] public class DispatchProperty { [Serializable] public class AreaProperty { public WorldArea areaType; public float[] rndMissionPosMoveTime = new float[] { 10, 20, 30, 40, 50, 60 }; public DispatchMissionProperty[] missionProperties; public int MaxRndMissionPosCnt => rndMissionPosMoveTime.Length; } public uint maxLevel = 100; public uint maxEmployeeCount = 3; public float missionProgressTimeSec = 5; public float missionRefreshTimeSec = 60; public AreaProperty[] areas = new AreaProperty[] { new AreaProperty { areaType = WorldArea.East }, new AreaProperty { areaType = WorldArea.West }, new AreaProperty { areaType = WorldArea.South }, new AreaProperty { areaType = WorldArea.North }, }; } [Serializable] public class DispatchPlayerProperty { [Serializable] public class AreaPlayerProperty { public WorldArea areaType; public bool needRefreshMission; public bool needClearEmployee; public string refreshEndTime = DateTime.MaxValue.ToString(FormatString.DateTimeParse, CultureInfo.InvariantCulture); public int missionPositionIndex; public uint[] employeeIDs = new uint[0]; public DispatchMissionPlayerProperty mission; } public uint dispatchExp; public AreaPlayerProperty[] areas = new AreaPlayerProperty[] { new AreaPlayerProperty { areaType = WorldArea.East }, new AreaPlayerProperty { areaType = WorldArea.West }, new AreaPlayerProperty { areaType = WorldArea.South }, new AreaPlayerProperty { areaType = WorldArea.North }, }; } public partial class DispatchData { DispatchProperty property; DispatchPlayerProperty playerProperty; Area[] areas; public uint MaxLevel => property.maxLevel; public uint MaxEmployeeCount => property.maxEmployeeCount; public float MissionRefreshTimeSec => property.missionRefreshTimeSec; public uint DispatchLevel { get; private set; } public uint DispatchExp => playerProperty.dispatchExp; public IList Areas => areas; public DispatchData(DispatchProperty property, DispatchPlayerProperty playerProperty) { this.property = property; this.playerProperty = playerProperty; areas = new Area[playerProperty.areas.Length]; for (int i = 0; i < areas.Length; ++i) { areas[i] = new Area(this, property.areas[i], playerProperty.areas[i]); } DispatchLevel = GetDispatchLevelByExp(playerProperty.dispatchExp); } public bool AddDispatchExp(uint exp) { playerProperty.dispatchExp += exp; uint prevLevel = DispatchLevel; DispatchLevel = GetDispatchLevelByExp(DispatchExp, DispatchLevel); return prevLevel != DispatchLevel; } public uint GetDispatchLevelByExp(uint exp, uint offset = 2) { if (offset < 2) offset = 2; for (uint i = offset; i <= MaxLevel; i++) { if(exp < GetTotalExpToLevel(i)) return i - 1; } return MaxLevel; } public uint GetTotalExpToLevel(uint level) { //TODO : this is temp code const uint baseExp = 100; const uint expPerLevel = 10; if(level < 2) return 0; uint a = level - 1; uint b = a - 1; return baseExp * a + expPerLevel * Utility.PermutationAdd(b); } public bool TryGetArea(WorldArea areaType, out Area result) { result = null; for (int i = 0; i < areas.Length; ++i) { if (areas[i].AreaType == areaType) { result = areas[i]; break; } } return result != null; } } public partial class DispatchData { [Serializable] public class Area { DispatchProperty.AreaProperty property; DispatchPlayerProperty.AreaPlayerProperty playerProperty; public DispatchData DispatchData { get; private set; } public DispatchMissionData Mission { get; private set; } public WorldArea AreaType => playerProperty.areaType; public DateTime RefreshEndTime => DateTime.ParseExact(playerProperty.refreshEndTime, FormatString.DateTimeParse, CultureInfo.InvariantCulture); public int MissionPositionIndex => playerProperty.missionPositionIndex; public IList EmployeeIDs => playerProperty.employeeIDs; public float MissionMoveTime => property.rndMissionPosMoveTime[MissionPositionIndex]; public float MissionProgressTime => DispatchData.property.missionProgressTimeSec; public bool NeedRefreshMission => playerProperty.needRefreshMission; public bool NeedClearEmployee => playerProperty.needClearEmployee; public string ImageAtlasPath => GameProperty.Instance.AreaImageAtlasName; public string ImageNameInAtlas => AreaType.ToString(); public event Action OnRefreshMission; public Area(DispatchData dispatchData, DispatchProperty.AreaProperty areaProperty, DispatchPlayerProperty.AreaPlayerProperty areaPlayerProperty) { property = areaProperty; playerProperty = areaPlayerProperty; DispatchData = dispatchData; if (TryGetAreaMissionProperty(out var missionProp)) Mission = new DispatchMissionData(this, missionProp, playerProperty.mission); else RefreshMission(TimeUtils.Now()); } private bool TryGetAreaMissionProperty(out DispatchMissionProperty result) { for (int j = 0; j < property.missionProperties.Length; ++j) { var missionProp = property.missionProperties[j]; if (missionProp.id == playerProperty.mission.id) { result = missionProp; return true; } } result = null; return false; } public void SetEmployeeID(int slotIndex, uint id) { if (slotIndex >= playerProperty.employeeIDs.Length) Array.Resize(ref playerProperty.employeeIDs, slotIndex + 1); playerProperty.employeeIDs[slotIndex] = id; } public void ClearEmployeeID() { playerProperty.employeeIDs = new uint[DispatchData.MaxEmployeeCount]; } public void StartRefreshTime(DateTime now) { playerProperty.needRefreshMission = true; playerProperty.refreshEndTime = (now.AddSeconds(DispatchData.MissionRefreshTimeSec)).ToString(FormatString.DateTimeParse, CultureInfo.InvariantCulture); } public bool IsInRefreshTime(DateTime now) => now < RefreshEndTime; public void StartMission(DateTime now) { playerProperty.needClearEmployee = true; Mission.Start(now); } public void RefreshMission(DateTime now) { if (!property.missionProperties.TryGetRandomValue(out var missionProp)) { Logger.LogError("Can't find mission property for area type: " + AreaType.ToString()); return; } playerProperty.needRefreshMission = false; playerProperty.needClearEmployee = false; playerProperty.refreshEndTime = now.ToString(FormatString.DateTimeParse, CultureInfo.InvariantCulture); playerProperty.missionPositionIndex = UnityEngine.Random.Range(0, property.MaxRndMissionPosCnt); ClearEmployeeID(); playerProperty.mission.id = missionProp.id; playerProperty.mission.gotReward = true; playerProperty.mission.needGetResult = false; playerProperty.mission.hasResult = false; playerProperty.mission.isSucceed = false; playerProperty.mission.recommendNationalities = GetRecommendNationalities(); playerProperty.mission.bonusRewardTargets = GetRandomBonusRewards(missionProp); playerProperty.mission.endTime = now.ToString(FormatString.DateTimeParse, CultureInfo.InvariantCulture); Mission = new DispatchMissionData(this, missionProp, playerProperty.mission); OnRefreshMission?.Invoke(); } private uint[] GetRecommendNationalities() { //TODO : get max count by dispatch level const int maxCount = 3; var result = new uint[maxCount]; for (int i = 0; i < maxCount; ++i) { result[i] = (uint)UnityEngine.Random.Range(0, (int)Nationality.Kind.Count); } return result; } private uint[] GetRandomBonusRewards(DispatchMissionProperty missionProp) { //TODO : get random bonus rewards const int maxCount = 3; if(missionProp.rewardCandidates.Length < maxCount) throw new Exception("Not enough reward candidates"); var result = new uint[maxCount]; var candidates = new HashSet(); for(uint i = 0; i < missionProp.rewardCandidates.Length; i++) { candidates.Add(i); } int curCount = 0; while(curCount < maxCount) { uint rnd = (uint)UnityEngine.Random.Range(0, missionProp.rewardCandidates.Length); if(candidates.Contains(rnd)) { result[curCount++] = rnd; candidates.Remove(rnd); } } return result; } } }