OSDN Git Service

C# 6.0の記法を使用する
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / ItemInfo.cs
1 // Copyright (C) 2013, 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 // \r
3 // This program is part of KancolleSniffer.\r
4 //\r
5 // KancolleSniffer is free software: you can redistribute it and/or modify\r
6 // it under the terms of the GNU General Public License as published by\r
7 // the Free Software Foundation, either version 3 of the License, or\r
8 // (at your option) any later version.\r
9 //\r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 //\r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, see <http://www.gnu.org/licenses/>.\r
17 \r
18 using System;\r
19 using System.Collections.Generic;\r
20 using System.Drawing;\r
21 using System.Linq;\r
22 using System.Web;\r
23 \r
24 namespace KancolleSniffer\r
25 {\r
26     public class ItemSpec\r
27     {\r
28         public int Id;\r
29         public string Name;\r
30         public int Type;\r
31         public string TypeName;\r
32         public int IconType;\r
33         public int AntiAir;\r
34         public int LoS;\r
35         public int AntiSubmarine;\r
36         public int Torpedo;\r
37         public int Bomber;\r
38 \r
39         public ItemSpec()\r
40         {\r
41             Id = -1;\r
42             Name = "";\r
43         }\r
44 \r
45         public bool CanAirCombat\r
46         {\r
47             get\r
48             {\r
49                 switch (Type)\r
50                 {\r
51                     case 6: // 艦戦\r
52                     case 7: // 艦爆\r
53                     case 8: // 艦攻\r
54                     case 11: // 水爆\r
55                         return true;\r
56                 }\r
57                 return false;\r
58             }\r
59         }\r
60 \r
61         // http://ch.nicovideo.jp/biikame/blomaga/ar663428\r
62         public double LoSScaleFactor()\r
63         {\r
64             switch (Type)\r
65             {\r
66                 case 7: // 艦爆\r
67                     return 1.0376255;\r
68                 case 8: // 艦攻\r
69                     return 1.3677954;\r
70                 case 9: // 艦偵\r
71                     return 1.6592780;\r
72                 case 10: // 水偵\r
73                     return 2.0000000;\r
74                 case 11: // 水爆\r
75                     return 1.7787282;\r
76                 case 12: // 小型電探\r
77                     return 1.0045358;\r
78                 case 13: // 大型電探\r
79                     return 0.9906638;\r
80             }\r
81             if (Name == "探照灯")\r
82                 return 0.9067950;\r
83             return 0;\r
84         }\r
85 \r
86         public bool IsAircraft\r
87         {\r
88             get\r
89             {\r
90                 switch (Type)\r
91                 {\r
92                     case 6:\r
93                     case 7:\r
94                     case 8:\r
95                     case 9:\r
96                     case 10:\r
97                     case 11:\r
98                     case 25: // オートジャイロ\r
99                     case 26: // 対潜哨戒機\r
100                         return true;\r
101                 }\r
102                 return false;\r
103             }\r
104         }\r
105 \r
106         public bool IsSonar => Type == 14 || // ソナー\r
107                                Type == 40; // 大型ソナー\r
108 \r
109         public bool IsDepthCharge => Type == 15;\r
110 \r
111         public bool IsReconSeaplane => Type == 10;\r
112 \r
113         public Color Color\r
114         {\r
115             get\r
116             {\r
117                 switch (IconType)\r
118                 {\r
119                     case 1:\r
120                     case 2:\r
121                     case 3: // 主砲\r
122                     case 13: // 徹甲弾\r
123                         return Color.FromArgb(209, 89, 89);\r
124                     case 4: // 副砲\r
125                         return Color.FromArgb(253, 233, 0);\r
126                     case 5: // 魚雷\r
127                         return Color.FromArgb(88, 134, 170);\r
128                     case 6: // 艦戦\r
129                         return Color.FromArgb(93, 179, 108);\r
130                     case 7: // 艦爆\r
131                         return Color.FromArgb(223, 102, 102);\r
132                     case 8: // 艦攻\r
133                         return Color.FromArgb(95, 173, 234);\r
134                     case 9: // 艦偵\r
135                         return Color.FromArgb(254, 191, 0);\r
136                     case 10: // 水上機\r
137                         return Color.FromArgb(142, 203, 152);\r
138                     case 11: // 電探\r
139                         return Color.FromArgb(231, 153, 53);\r
140                     case 12: // 三式弾\r
141                         return Color.FromArgb(69, 175, 88);\r
142                     case 14: // 応急修理要員\r
143                         return Color.FromArgb(254, 254, 254);\r
144                     case 15: // 機銃\r
145                     case 16: // 高角砲\r
146                         return Color.FromArgb(102, 204, 118);\r
147                     case 17: // 爆雷\r
148                     case 18: // ソナー\r
149                         return Color.FromArgb(126, 203, 215);\r
150                     case 19: // 缶\r
151                         return Color.FromArgb(254, 195, 77);\r
152                     case 20: // 大発\r
153                         return Color.FromArgb(154, 163, 90);\r
154                     case 21: // オートジャイロ\r
155                         return Color.FromArgb(99, 203, 115);\r
156                     case 22: // 対潜哨戒機\r
157                         return Color.FromArgb(125, 205, 217);\r
158                     case 23: // 追加装甲\r
159                         return Color.FromArgb(152, 124, 172);\r
160                     case 24: // 探照灯\r
161                     case 27: // 照明弾\r
162                         return Color.FromArgb(254, 155, 0);\r
163                     case 25: // ドラム缶\r
164                         return Color.FromArgb(161, 161, 160);\r
165                     case 26: // 艦艇修理施設\r
166                         return Color.FromArgb(175, 156, 126);\r
167                     case 28: // 司令部施設\r
168                         return Color.FromArgb(204, 172, 252);\r
169                     case 29: // 航空要員\r
170                         return Color.FromArgb(206, 166, 108);\r
171                     case 30: // 高射装置\r
172                         return Color.FromArgb(137, 153, 77);\r
173                     case 31: // 対地装備\r
174                         return Color.FromArgb(253, 49, 49);\r
175                     case 32: // 水上艦要員\r
176                         return Color.FromArgb(188, 238, 155);\r
177                     default:\r
178                         return SystemColors.Control;\r
179                 }\r
180             }\r
181         }\r
182     }\r
183 \r
184     public class ItemStatus\r
185     {\r
186         public ItemSpec Spec { get; set; }\r
187         public int Level { get; set; }\r
188         public ShipStatus Ship { get; set; }\r
189 \r
190         public ItemStatus()\r
191         {\r
192             Spec = new ItemSpec();\r
193             Ship = new ShipStatus();\r
194         }\r
195     }\r
196 \r
197     public class ItemInfo : IHaveState\r
198     {\r
199         private int _nowShips, _nowEquips;\r
200         private readonly Dictionary<int, ItemSpec> _itemSpecs = new Dictionary<int, ItemSpec>();\r
201         private readonly Dictionary<int, ItemStatus> _itemInfo = new Dictionary<int, ItemStatus>();\r
202 \r
203         public int MaxShips { get; private set; }\r
204         public int MarginShips { get; set; }\r
205         public bool RingShips { get; set; }\r
206         public int MaxEquips { get; private set; }\r
207         public int MarginEquips { get; set; }\r
208         public bool RingEquips { get; set; }\r
209         public MaterialCount[] MaterialHistory { get; }\r
210 \r
211         public bool NeedSave\r
212         {\r
213             get { return MaterialHistory.Any(m => m.NeedSave); }\r
214             private set\r
215             {\r
216                 foreach (var m in MaterialHistory)\r
217                     m.NeedSave = value;\r
218             }\r
219         }\r
220 \r
221         public int NowShips\r
222         {\r
223             get { return _nowShips; }\r
224             set\r
225             {\r
226                 if (MaxShips != 0)\r
227                 {\r
228                     var limit = MaxShips - MarginShips;\r
229                     RingShips = _nowShips < limit && value >= limit;\r
230                 }\r
231                 _nowShips = value;\r
232             }\r
233         }\r
234 \r
235         public bool TooManyShips => MaxShips != 0 && NowShips >= MaxShips - MarginShips;\r
236 \r
237         public int NowEquips\r
238         {\r
239             get { return _nowEquips; }\r
240             private set\r
241             {\r
242                 if (MaxEquips != 0)\r
243                 {\r
244                     var limit = MaxEquips - MarginEquips;\r
245                     RingEquips = _nowEquips < limit && value >= limit;\r
246                 }\r
247                 _nowEquips = value;\r
248             }\r
249         }\r
250 \r
251         public bool TooManyEquips => MaxEquips != 0 && NowEquips >= MaxEquips - MarginEquips;\r
252 \r
253         public ItemInfo()\r
254         {\r
255             MaterialHistory = new MaterialCount[Enum.GetValues(typeof(Material)).Length];\r
256             foreach (Material m in Enum.GetValues(typeof(Material)))\r
257                 MaterialHistory[(int)m] = new MaterialCount();\r
258             MarginShips = 4;\r
259             MarginEquips = 10;\r
260         }\r
261 \r
262         public void InspectBasic(dynamic json)\r
263         {\r
264             MaxShips = (int)json.api_max_chara;\r
265             MaxEquips = (int)json.api_max_slotitem;\r
266         }\r
267 \r
268         public void InspectMaterial(dynamic json)\r
269         {\r
270             foreach (var entry in json)\r
271                 MaterialHistory[(int)entry.api_id - 1].Now = (int)entry.api_value;\r
272         }\r
273 \r
274         public void InspectMaster(dynamic json)\r
275         {\r
276             var dict = new Dictionary<int, string>();\r
277             foreach (var entry in json.api_mst_slotitem_equiptype)\r
278                 dict[(int)entry.api_id] = entry.api_name;\r
279             foreach (var entry in json.api_mst_slotitem)\r
280             {\r
281                 _itemSpecs[(int)entry.api_id] = new ItemSpec\r
282                 {\r
283                     Id = (int)entry.api_id,\r
284                     Name = (string)entry.api_name,\r
285                     Type = (int)entry.api_type[2],\r
286                     TypeName = dict[(int)entry.api_type[2]],\r
287                     IconType = (int)entry.api_type[3],\r
288                     AntiAir = (int)entry.api_tyku,\r
289                     LoS = (int)entry.api_saku,\r
290                     AntiSubmarine = (int)entry.api_tais,\r
291                     Torpedo = (int)entry.api_raig,\r
292                     Bomber = (int)entry.api_baku\r
293                 };\r
294             }\r
295             _itemSpecs[-1] = new ItemSpec();\r
296         }\r
297 \r
298         public void InspectSlotItem(dynamic json, bool full = false)\r
299         {\r
300             if (!json.IsArray)\r
301                 json = new[] {json};\r
302             if (full)\r
303             {\r
304                 _itemInfo.Clear();\r
305                 _itemInfo[-1] = new ItemStatus();\r
306             }\r
307             foreach (var entry in json)\r
308             {\r
309                 _itemInfo[(int)entry.api_id] = new ItemStatus\r
310                 {\r
311                     Spec = _itemSpecs[(int)entry.api_slotitem_id],\r
312                     Level = entry.api_level() ? (int)entry.api_level : 0\r
313                 };\r
314             }\r
315             NowEquips = _itemInfo.Count - 1;\r
316         }\r
317 \r
318         public void InspectCreateItem(dynamic json)\r
319         {\r
320             var m = (dynamic[])json.api_material;\r
321             for (var i = 0; i < m.Length; i++)\r
322                 MaterialHistory[i].Now = (int)m[i];\r
323             if (!json.IsDefined("api_slot_item"))\r
324                 return;\r
325             InspectSlotItem(json.api_slot_item);\r
326         }\r
327 \r
328         public void InspectGetShip(dynamic json)\r
329         {\r
330             NowShips += 1;\r
331             if (json.api_slotitem == null) // まるゆにはスロットがない\r
332                 return;\r
333             InspectSlotItem(json.api_slotitem);\r
334         }\r
335 \r
336         public void InspectDestroyItem(string request, dynamic json)\r
337         {\r
338             var values = HttpUtility.ParseQueryString(request);\r
339             DeleteItems(values["api_slotitem_ids"].Split(',').Select(int.Parse).ToArray());\r
340             var get = (int[])json.api_get_material;\r
341             for (var i = 0; i < get.Length; i++)\r
342                 MaterialHistory[i].Now += get[i];\r
343         }\r
344 \r
345         public void InspectRemodelSlot(dynamic json)\r
346         {\r
347             var after = (int[])json.api_after_material;\r
348             for (var i = 0; i < after.Length; i++)\r
349                 MaterialHistory[i].Now = after[i];\r
350             if (json.api_after_slot())\r
351                 InspectSlotItem(json.api_after_slot);\r
352             if (!json.api_use_slot_id())\r
353                 return;\r
354             DeleteItems(((int[])json.api_use_slot_id));\r
355         }\r
356 \r
357         public void DeleteItems(int[] ids)\r
358         {\r
359             foreach (var id in ids.Where(id => id != -1))\r
360             {\r
361                 _itemInfo.Remove(id);\r
362                 NowEquips--;\r
363             }\r
364         }\r
365 \r
366         public void CountNewItems(int[] ids)\r
367         {\r
368             foreach (var id in ids.Where(id => id != -1 && !_itemInfo.ContainsKey(id)))\r
369             {\r
370                 _itemInfo[id] = new ItemStatus();\r
371                 NowEquips++;\r
372             }\r
373         }\r
374 \r
375         public void InspectMissionResult(dynamic json)\r
376         {\r
377             if ((int)json.api_clear_result == 0) // 失敗\r
378                 return;\r
379             var get = (int[])json.api_get_material;\r
380             for (var i = 0; i < get.Length; i++)\r
381                 MaterialHistory[i].Now += get[i];\r
382         }\r
383 \r
384         public ItemSpec this[int id] => GetSpecById(id);\r
385 \r
386         public ItemSpec GetSpecById(int id) => _itemInfo[id].Spec;\r
387 \r
388         public ItemSpec GetSpecByItemId(int id) => _itemSpecs[id];\r
389 \r
390         public Dictionary<int, ItemStatus> ItemDict => _itemInfo;\r
391 \r
392         public void SetItemOwner(ShipStatus[] shipList)\r
393         {\r
394             foreach (var e in _itemInfo)\r
395                 e.Value.Ship = new ShipStatus();\r
396             foreach (var s in shipList)\r
397             {\r
398                 foreach (var id in s.Slot)\r
399                     _itemInfo[id].Ship = s;\r
400             }\r
401         }\r
402 \r
403         public void SaveState(Status status)\r
404         {\r
405             NeedSave = false;\r
406             status.MatreialHistory = MaterialHistory;\r
407         }\r
408 \r
409         public void LoadState(Status status)\r
410         {\r
411             status.MatreialHistory?.CopyTo(MaterialHistory, 0);\r
412         }\r
413     }\r
414 \r
415     public enum Material\r
416     {\r
417         Fuel,\r
418         Bullet,\r
419         Steal,\r
420         Bouxite,\r
421         Burner,\r
422         Bucket,\r
423         Development,\r
424         Screw,\r
425     }\r
426 \r
427     public class MaterialCount\r
428     {\r
429         private int _now;\r
430 \r
431         public int BegOfDay { get; set; }\r
432         public int BegOfWeek { get; set; }\r
433         public DateTime LastSet { get; set; }\r
434         public bool NeedSave { get; set; }\r
435 \r
436         public int Now\r
437         {\r
438             get { return _now; }\r
439             set\r
440             {\r
441                 var prev = _now;\r
442                 _now = value;\r
443                 if (Status.Restoring) // JSONから値を復旧するときは履歴に触らない\r
444                     return;\r
445                 if (_now != prev)\r
446                     NeedSave = true;\r
447                 if (LastSet == DateTime.MinValue)\r
448                 {\r
449                     BegOfDay = BegOfWeek = value;\r
450                     LastSet = DateTime.Now;\r
451                     return;\r
452                 }\r
453                 UpdateHistory(prev);\r
454                 LastSet = DateTime.Now;\r
455             }\r
456         }\r
457 \r
458         private void UpdateHistory(int prev)\r
459         {\r
460             var morning = DateTime.Today.AddHours(5);\r
461             var dow = (int)morning.DayOfWeek;\r
462             var monday = morning.AddDays(dow == 0 ? -6 : -dow + 1);\r
463             if (DateTime.Now >= morning && LastSet < morning)\r
464                 BegOfDay = prev;\r
465             if (DateTime.Now >= monday && LastSet < monday)\r
466                 BegOfWeek = prev;\r
467         }\r
468     }\r
469 }