1 // Copyright (C) 2013, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>
\r
3 // Licensed under the Apache License, Version 2.0 (the "License");
\r
4 // you may not use this file except in compliance with the License.
\r
5 // You may obtain a copy of the License at
\r
7 // http://www.apache.org/licenses/LICENSE-2.0
\r
9 // Unless required by applicable law or agreed to in writing, software
\r
10 // distributed under the License is distributed on an "AS IS" BASIS,
\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
12 // See the License for the specific language governing permissions and
\r
13 // limitations under the License.
\r
16 using System.Collections.Generic;
\r
17 using System.Drawing;
\r
19 using System.Windows.Forms;
\r
20 using System.Xml.Serialization;
\r
21 using KancolleSniffer.Util;
\r
23 namespace KancolleSniffer.Model
\r
25 public class QuestStatus
\r
27 public int Id { get; set; }
\r
28 public int Category { get; set; }
\r
29 public QuestInterval Interval { get; set; }
\r
30 public string Name { get; set; }
\r
31 public string Detail { get; set; }
\r
32 public int[] Material { get; set; }
\r
33 public int Progress { get; set; }
\r
36 public QuestCount Count { get; set; }
\r
39 public Color Color { get; set; }
\r
41 public string ToToolTip() =>
\r
43 (Material == null || Material.All(x => x == 0)
\r
45 : "\r\n" + string.Join(" ",
\r
46 new[] {"燃", "弾", "鋼", "ボ", "建造", "修復", "開発", "改修"}
\r
47 .Zip(Material, (m, num) => num == 0 ? "" : m + num)
\r
48 .Where(s => !string.IsNullOrEmpty(s))));
\r
50 public QuestStatus Clone()
\r
52 var clone = (QuestStatus)MemberwiseClone();
\r
53 clone.Count = Count.Clone();
\r
58 public enum QuestInterval
\r
60 // ReSharper disable once UnusedMember.Global
\r
68 public class QuestInfo : IHaveState
\r
70 private readonly SortedDictionary<int, QuestStatus> _quests = new SortedDictionary<int, QuestStatus>();
\r
71 private readonly QuestCountList _countList = new QuestCountList();
\r
72 private readonly Func<DateTime> _nowFunc = () => DateTime.Now;
\r
73 private DateTime _now;
\r
74 private DateTime _lastReset;
\r
75 private IEnumerable<QuestStatus> _clearedQuest = new List<QuestStatus>();
\r
77 private readonly Color[] _color =
\r
79 Color.FromArgb(60, 141, 76), Color.FromArgb(232, 57, 41), Color.FromArgb(136, 204, 120),
\r
80 Color.FromArgb(52, 147, 185), Color.FromArgb(220, 198, 126), Color.FromArgb(168, 111, 76),
\r
81 Color.FromArgb(200, 148, 231), Color.FromArgb(232, 57, 41)
\r
84 public int AcceptMax { get; set; } = 5;
\r
86 public SortedDictionary<int, QuestStatus> QuestDictionary => _quests;
\r
88 public QuestStatus[] Quests => _quests.Values.ToArray();
\r
90 public QuestInfo(Func<DateTime> nowFunc = null)
\r
92 if (nowFunc != null)
\r
96 public void GetNotifications(out string[] notify, out string[] stop)
\r
98 var cleared = _quests.Values.Where(q => q.Count.Cleared).ToArray();
\r
99 notify = cleared.Except(_clearedQuest, new QuestComparer()).Select(q => q.Name).ToArray();
\r
100 stop = _clearedQuest.Except(cleared, new QuestComparer()).Select(q => q.Name).ToArray();
\r
101 _clearedQuest = cleared;
\r
104 private class QuestComparer : IEqualityComparer<QuestStatus>
\r
106 public bool Equals(QuestStatus x, QuestStatus y)
\r
108 return x?.Id == y?.Id;
\r
111 public int GetHashCode(QuestStatus obj)
\r
117 private readonly QuestInterval[] _intervals =
\r
119 QuestInterval.Daily, QuestInterval.Weekly, QuestInterval.Monthly,
\r
120 QuestInterval.Other, QuestInterval.Quarterly
\r
123 private readonly int[] _progress = {0, 50, 80};
\r
125 public void InspectQuestList(dynamic json)
\r
128 if (json.api_list == null)
\r
130 for (var i = 0; i < 2; i++)
\r
132 foreach (var entry in json.api_list)
\r
134 if (entry is double) // -1の場合がある。
\r
136 var quest = new QuestStatus
\r
138 Id = (int)entry.api_no,
\r
139 Category = (int)entry.api_category,
\r
140 Progress = _progress[(int)entry.api_progress_flag],
\r
141 Interval = _intervals[(int)entry.api_type - 1],
\r
142 Name = (string)entry.api_title,
\r
143 Detail = ((string)entry.api_detail).Replace("<br>", "\r\n"),
\r
144 Material = (int[])entry.api_get_material
\r
146 var state = (int)entry.api_state;
\r
150 if (_quests.Remove(quest.Id))
\r
154 quest.Progress = 100;
\r
157 SetProcessedQuest(quest);
\r
161 if (_quests.Count <= AcceptMax)
\r
164 * ほかのPCで任務を達成した場合、任務が消えずに受領した任務の数がAcceptMaxを超えることがある。
\r
165 * その場合はいったん任務をクリアして、現在のページの任務だけを登録し直す。
\r
171 private void SetProcessedQuest(QuestStatus quest)
\r
173 var count = _countList.GetCount(quest.Id);
\r
174 if (count.AdjustCount(quest.Progress))
\r
176 quest.Material = quest.Material.Concat(count.Spec.Material).ToArray();
\r
177 if (!_quests.ContainsKey(quest.Id))
\r
182 private void SetQuest(QuestStatus quest)
\r
184 quest.Count = _countList.GetCount(quest.Id);
\r
185 quest.Color = quest.Category <= _color.Length ? _color[quest.Category - 1] : Control.DefaultBackColor;
\r
186 _quests[quest.Id] = quest;
\r
189 public void ClearQuests()
\r
194 private void ResetQuests()
\r
197 if (!CrossBoundary(LastMorning))
\r
199 RemoveQuest(QuestInterval.Daily);
\r
200 _countList.Remove(QuestInterval.Daily);
\r
208 private DateTime LastMorning => _now.Date.AddDays(_now.Hour < 5 ? -1 : 0).AddHours(5);
\r
210 private void ResetWeekly()
\r
212 if (!CrossBoundary(LastMonday.AddHours(5)))
\r
214 RemoveQuest(QuestInterval.Weekly);
\r
215 _countList.Remove(QuestInterval.Weekly);
\r
218 private DateTime LastMonday => _now.Date.AddDays(-((6 + (int)_now.DayOfWeek) % 7));
\r
220 private void ResetMonthly()
\r
222 if (!CrossBoundary(new DateTime(_now.Year, _now.Month, 1, 5, 0, 0)))
\r
224 RemoveQuest(QuestInterval.Monthly);
\r
225 _countList.Remove(QuestInterval.Monthly);
\r
228 private void ResetQuarterly()
\r
230 if (!CrossBoundary(QuarterlyBoundary.AddHours(5)))
\r
232 RemoveQuest(QuestInterval.Quarterly);
\r
233 _countList.Remove(QuestInterval.Quarterly);
\r
236 private DateTime QuarterlyBoundary =>
\r
237 _now.Month / 3 == 0
\r
238 ? new DateTime(_now.Year - 1, 12, 1)
\r
239 : new DateTime(_now.Year, _now.Month / 3 * 3, 1);
\r
241 private bool CrossBoundary(DateTime boundary)
\r
243 return _lastReset < boundary && boundary <= _now;
\r
246 private void RemoveQuest(QuestInterval interval)
\r
249 (from kv in _quests
\r
250 where kv.Value.Count.Spec.Interval == interval || // 輸送5と空母3はカウンタを見ないとデイリーにならない
\r
251 kv.Value.Interval == interval
\r
252 select kv.Key).ToArray())
\r
253 _quests.Remove(id);
\r
256 public void InspectStop(string request)
\r
258 var values = HttpUtility.ParseQueryString(request);
\r
259 _quests.Remove(int.Parse(values["api_quest_id"]));
\r
263 public void InspectClearItemGet(string request)
\r
265 var values = HttpUtility.ParseQueryString(request);
\r
266 var id = int.Parse(values["api_quest_id"]);
\r
267 _countList.Remove(id);
\r
268 _quests.Remove(id);
\r
272 public bool NeedSave { get; set; }
\r
274 public void SaveState(Status status)
\r
277 status.QuestLastReset = _lastReset;
\r
278 if (_quests != null)
\r
279 status.QuestList = _quests.Values.ToArray();
\r
280 if (_countList != null)
\r
281 status.QuestCountList = _countList.NonZeroCountList.ToArray();
\r
284 public void LoadState(Status status)
\r
286 _lastReset = status.QuestLastReset;
\r
287 if (status.QuestCountList != null)
\r
288 _countList.SetCountList(status.QuestCountList);
\r
289 if (status.QuestList != null)
\r
292 foreach (var quest in status.QuestList)
\r