OSDN Git Service

資材の増減で連続する遠征結果を積算する
[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                     case 33: // 大型飛行艇\r
178                         return Color.FromArgb(142, 203, 152);\r
179                     case 34: // 戦闘糧食\r
180                         return Color.FromArgb(254, 254, 254);\r
181                     default:\r
182                         return SystemColors.Control;\r
183                 }\r
184             }\r
185         }\r
186     }\r
187 \r
188     public class ItemStatus\r
189     {\r
190         public ItemSpec Spec { get; set; }\r
191         public int Level { get; set; }\r
192         public int Alv { get; set; }\r
193         public ShipStatus Ship { get; set; }\r
194 \r
195         public ItemStatus()\r
196         {\r
197             Spec = new ItemSpec();\r
198             Ship = new ShipStatus();\r
199         }\r
200     }\r
201 \r
202     public class ItemInfo : IHaveState\r
203     {\r
204         private int _nowShips, _nowEquips;\r
205         private readonly Dictionary<int, ItemSpec> _itemSpecs = new Dictionary<int, ItemSpec>();\r
206         private readonly Dictionary<int, ItemStatus> _itemInfo = new Dictionary<int, ItemStatus>();\r
207         private bool _inPort;\r
208         private DateTime _lastMission;\r
209 \r
210         public int MaxShips { get; private set; }\r
211         public int MarginShips { get; set; }\r
212         public bool RingShips { get; set; }\r
213         public int MaxEquips { get; private set; }\r
214         public int MarginEquips { get; set; }\r
215         public bool RingEquips { get; set; }\r
216         public MaterialCount[] MaterialHistory { get; }\r
217         public int[] PrevPort { get; }\r
218 \r
219         public bool NeedSave\r
220         {\r
221             get { return MaterialHistory.Any(m => m.NeedSave); }\r
222             private set\r
223             {\r
224                 foreach (var m in MaterialHistory)\r
225                     m.NeedSave = value;\r
226             }\r
227         }\r
228 \r
229         public int NowShips\r
230         {\r
231             get { return _nowShips; }\r
232             set\r
233             {\r
234                 if (MaxShips != 0)\r
235                 {\r
236                     var limit = MaxShips - MarginShips;\r
237                     RingShips = _nowShips < limit && value >= limit;\r
238                 }\r
239                 _nowShips = value;\r
240             }\r
241         }\r
242 \r
243         public bool TooManyShips => MaxShips != 0 && NowShips >= MaxShips - MarginShips;\r
244 \r
245         public int NowEquips\r
246         {\r
247             get { return _nowEquips; }\r
248             private set\r
249             {\r
250                 if (MaxEquips != 0)\r
251                 {\r
252                     var limit = MaxEquips - MarginEquips;\r
253                     RingEquips = _nowEquips < limit && value >= limit;\r
254                 }\r
255                 _nowEquips = value;\r
256             }\r
257         }\r
258 \r
259         public bool TooManyEquips => MaxEquips != 0 && NowEquips >= MaxEquips - MarginEquips;\r
260 \r
261         public ItemInfo()\r
262         {\r
263             var n = Enum.GetValues(typeof(Material)).Length;\r
264             MaterialHistory = new MaterialCount[n];\r
265             for (var i = 0; i < n; i++)\r
266                 MaterialHistory[i] = new MaterialCount();\r
267             PrevPort = new int[n];\r
268             MarginShips = 4;\r
269             MarginEquips = 10;\r
270         }\r
271 \r
272         public void InspectBasic(dynamic json)\r
273         {\r
274             MaxShips = (int)json.api_max_chara;\r
275             MaxEquips = (int)json.api_max_slotitem;\r
276         }\r
277 \r
278         public void InspectMaterial(dynamic json, bool port = false)\r
279         {\r
280             if (!port)\r
281                 UpdatePrevPort();\r
282             foreach (var entry in json)\r
283             {\r
284                 var i = (int)entry.api_id - 1;\r
285                 var v = (int)entry.api_value;\r
286                 MaterialHistory[i].Now = v;\r
287             }\r
288             if (!port)\r
289                 return;\r
290             _inPort = true;\r
291             if (PrevPort[0] != 0)\r
292                 return;\r
293             for (var i = 0; i < MaterialHistory.Length; i++)\r
294                 PrevPort[i] = MaterialHistory[i].Now;\r
295         }\r
296 \r
297         private void UpdatePrevPort()\r
298         {\r
299             if (!_inPort)\r
300                 return;\r
301             for (var i = 0; i < MaterialHistory.Length; i++)\r
302                 PrevPort[i] = MaterialHistory[i].Now;\r
303             _inPort = false;\r
304         }\r
305 \r
306         public void SetMaterials(int[] material)\r
307         {\r
308             UpdatePrevPort();\r
309             for (var i = 0; i < material.Length; i++)\r
310                 MaterialHistory[i].Now = material[i];\r
311         }\r
312 \r
313         public void AddMaterials(int[] v)\r
314         {\r
315             UpdatePrevPort();\r
316             for (var i = 0; i < v.Length; i++)\r
317                 MaterialHistory[i].Now += v[i];\r
318         }\r
319 \r
320         public void SubMaterial(Material m, int v)\r
321         {\r
322             UpdatePrevPort();\r
323             MaterialHistory[(int)m].Now -= v;\r
324         }\r
325 \r
326         public void InspectMaster(dynamic json)\r
327         {\r
328             var dict = new Dictionary<int, string>();\r
329             foreach (var entry in json.api_mst_slotitem_equiptype)\r
330                 dict[(int)entry.api_id] = entry.api_name;\r
331             foreach (var entry in json.api_mst_slotitem)\r
332             {\r
333                 _itemSpecs[(int)entry.api_id] = new ItemSpec\r
334                 {\r
335                     Id = (int)entry.api_id,\r
336                     Name = (string)entry.api_name,\r
337                     Type = (int)entry.api_type[2],\r
338                     TypeName = dict[(int)entry.api_type[2]],\r
339                     IconType = (int)entry.api_type[3],\r
340                     AntiAir = (int)entry.api_tyku,\r
341                     LoS = (int)entry.api_saku,\r
342                     AntiSubmarine = (int)entry.api_tais,\r
343                     Torpedo = (int)entry.api_raig,\r
344                     Bomber = (int)entry.api_baku\r
345                 };\r
346             }\r
347             _itemSpecs[-1] = new ItemSpec();\r
348         }\r
349 \r
350         public void InspectSlotItem(dynamic json, bool full = false)\r
351         {\r
352             if (!json.IsArray)\r
353                 json = new[] {json};\r
354             if (full)\r
355             {\r
356                 _itemInfo.Clear();\r
357                 _itemInfo[-1] = new ItemStatus();\r
358             }\r
359             foreach (var entry in json)\r
360             {\r
361                 _itemInfo[(int)entry.api_id] = new ItemStatus\r
362                 {\r
363                     Spec = _itemSpecs[(int)entry.api_slotitem_id],\r
364                     Level = entry.api_level() ? (int)entry.api_level : 0,\r
365                     Alv = entry.api_alv() ? (int)entry.api_alv : 0\r
366                 };\r
367             }\r
368             NowEquips = _itemInfo.Count - 1;\r
369         }\r
370 \r
371         public void InspectCreateItem(dynamic json)\r
372         {\r
373             SetMaterials((int[])json.api_material);\r
374             if (!json.IsDefined("api_slot_item"))\r
375                 return;\r
376             InspectSlotItem(json.api_slot_item);\r
377         }\r
378 \r
379         public void InspectGetShip(dynamic json)\r
380         {\r
381             NowShips += 1;\r
382             if (json.api_slotitem == null) // まるゆにはスロットがない\r
383                 return;\r
384             InspectSlotItem(json.api_slotitem);\r
385         }\r
386 \r
387         public void InspectDestroyItem(string request, dynamic json)\r
388         {\r
389             var values = HttpUtility.ParseQueryString(request);\r
390             DeleteItems(values["api_slotitem_ids"].Split(',').Select(int.Parse).ToArray());\r
391             AddMaterials((int[])json.api_get_material);\r
392         }\r
393 \r
394         public void InspectRemodelSlot(dynamic json)\r
395         {\r
396             var after = (int[])json.api_after_material;\r
397             SetMaterials(after);\r
398             if (json.api_after_slot())\r
399                 InspectSlotItem(json.api_after_slot);\r
400             if (!json.api_use_slot_id())\r
401                 return;\r
402             DeleteItems(((int[])json.api_use_slot_id));\r
403         }\r
404 \r
405         public void DeleteItems(int[] ids)\r
406         {\r
407             foreach (var id in ids.Where(id => id != -1))\r
408             {\r
409                 _itemInfo.Remove(id);\r
410                 NowEquips--;\r
411             }\r
412         }\r
413 \r
414         public void CountNewItems(int[] ids)\r
415         {\r
416             foreach (var id in ids.Where(id => id != -1 && !_itemInfo.ContainsKey(id)))\r
417             {\r
418                 _itemInfo[id] = new ItemStatus();\r
419                 NowEquips++;\r
420             }\r
421         }\r
422 \r
423         public void InspectMissionResult(dynamic json)\r
424         {\r
425             if ((int)json.api_clear_result == 0) // 失敗\r
426                 return;\r
427             if (DateTime.Now - _lastMission < TimeSpan.FromMinutes(1))\r
428                 _inPort = false;\r
429             _lastMission = DateTime.Now;\r
430             AddMaterials((int[])json.api_get_material);\r
431         }\r
432 \r
433         public ItemSpec this[int id] => GetSpecById(id);\r
434 \r
435         public ItemSpec GetSpecById(int id) => _itemInfo[id].Spec;\r
436 \r
437         public ItemSpec GetSpecByItemId(int id) => _itemSpecs[id];\r
438 \r
439         public Dictionary<int, ItemStatus> ItemDict => _itemInfo;\r
440 \r
441         public void SetItemOwner(ShipStatus[] shipList)\r
442         {\r
443             foreach (var e in _itemInfo)\r
444                 e.Value.Ship = new ShipStatus();\r
445             foreach (var s in shipList)\r
446             {\r
447                 foreach (var id in s.Slot)\r
448                     _itemInfo[id].Ship = s;\r
449                 if (s.SlotEx != 0)\r
450                     _itemInfo[s.SlotEx].Ship = s;\r
451             }\r
452         }\r
453 \r
454         public void SaveState(Status status)\r
455         {\r
456             NeedSave = false;\r
457             status.MatreialHistory = MaterialHistory;\r
458         }\r
459 \r
460         public void LoadState(Status status)\r
461         {\r
462             status.MatreialHistory?.CopyTo(MaterialHistory, 0);\r
463         }\r
464     }\r
465 \r
466     public enum Material\r
467     {\r
468         Fuel,\r
469         Bullet,\r
470         Steal,\r
471         Bouxite,\r
472         Burner,\r
473         Bucket,\r
474         Development,\r
475         Screw,\r
476     }\r
477 \r
478     public class MaterialCount\r
479     {\r
480         private int _now;\r
481 \r
482         public int BegOfDay { get; set; }\r
483         public int BegOfWeek { get; set; }\r
484         public DateTime LastSet { get; set; }\r
485         public bool NeedSave { get; set; }\r
486 \r
487         public int Now\r
488         {\r
489             get { return _now; }\r
490             set\r
491             {\r
492                 var prev = _now;\r
493                 _now = value;\r
494                 if (Status.Restoring) // JSONから値を復旧するときは履歴に触らない\r
495                     return;\r
496                 if (_now != prev)\r
497                     NeedSave = true;\r
498                 if (LastSet == DateTime.MinValue)\r
499                 {\r
500                     BegOfDay = BegOfWeek = value;\r
501                     LastSet = DateTime.Now;\r
502                     return;\r
503                 }\r
504                 UpdateHistory(prev);\r
505                 LastSet = DateTime.Now;\r
506             }\r
507         }\r
508 \r
509         private void UpdateHistory(int prev)\r
510         {\r
511             var morning = DateTime.Today.AddHours(5);\r
512             var dow = (int)morning.DayOfWeek;\r
513             var monday = morning.AddDays(dow == 0 ? -6 : -dow + 1);\r
514             if (DateTime.Now >= morning && LastSet < morning)\r
515                 BegOfDay = prev;\r
516             if (DateTime.Now >= monday && LastSet < monday)\r
517                 BegOfWeek = prev;\r
518         }\r
519     }\r
520 }