1 #include <iostream>
2 
3 #include "util/helpers.h"
4 #include "api/evetime.h"
5 
6 #include "character.h"
7 
Character(EveApiAuth const & auth)8 Character::Character (EveApiAuth const& auth)
9 {
10   this->auth = auth;
11 
12   this->cs_fetcher.set_auth(auth);
13   this->sq_fetcher.set_auth(auth);
14 
15   this->cs_fetcher.set_doctype(API_DOCTYPE_CHARSHEET);
16   this->sq_fetcher.set_doctype(API_DOCTYPE_SKILLQUEUE);
17 
18   this->cs = ApiCharSheet::create();
19   this->sq = ApiSkillQueue::create();
20 
21   this->cs_fetcher.signal_done().connect(sigc::mem_fun
22       (*this, &Character::on_cs_available));
23   this->sq_fetcher.signal_done().connect(sigc::mem_fun
24       (*this, &Character::on_sq_available));
25 
26   this->process_api_data();
27 }
28 
29 /* ---------------------------------------------------------------- */
30 
~Character(void)31 Character::~Character (void)
32 {
33   //std::cout << "Removing character" << std::endl;
34 }
35 
36 /* ---------------------------------------------------------------- */
37 
38 void
on_cs_available(EveApiData data)39 Character::on_cs_available (EveApiData data)
40 {
41   if (data.data.get() == 0)
42   {
43     this->sig_request_error.emit(API_DOCTYPE_CHARSHEET, data.exception);
44     return;
45   }
46 
47   bool yet_unnamed = this->cs->name.empty();
48 
49   try
50   {
51     this->cs->set_api_data(data);
52     if (data.locally_cached)
53       this->sig_cached_warning.emit(API_DOCTYPE_CHARSHEET, data.exception);
54   }
55   catch (Exception& e)
56   {
57     this->sig_request_error.emit(API_DOCTYPE_CHARSHEET, e);
58     return;
59   }
60 
61   if (yet_unnamed && !this->cs->name.empty())
62     this->sig_name_available.emit(this->auth.char_id);
63 
64   this->process_api_data();
65   this->sig_char_sheet_updated.emit();
66   this->sig_api_info_changed.emit();
67 }
68 
69 /* ---------------------------------------------------------------- */
70 
71 void
on_sq_available(EveApiData data)72 Character::on_sq_available (EveApiData data)
73 {
74   if (data.data.get() == 0)
75   {
76     this->sig_request_error.emit(API_DOCTYPE_SKILLQUEUE, data.exception);
77     return;
78   }
79 
80   try
81   {
82     this->sq->set_api_data(data);
83     if (data.locally_cached)
84       this->sig_cached_warning.emit(API_DOCTYPE_SKILLQUEUE, data.exception);
85   }
86   catch (Exception& e)
87   {
88     this->sig_request_error.emit(API_DOCTYPE_SKILLQUEUE, e);
89     return;
90   }
91 
92   this->process_api_data();
93   this->sig_skill_queue_updated.emit();
94   this->sig_api_info_changed.emit();
95 }
96 
97 /* ---------------------------------------------------------------- */
98 
99 void
process_api_data(void)100 Character::process_api_data (void)
101 {
102     this->training_skill = 0;
103     this->training_spph = 0;
104     this->training_level_sp = 0;
105     this->training_skill_sp = 0;
106     this->training_level_done = 0.0;
107     this->training_remaining = 0;
108     this->training_info.queue_pos = -1;
109 
110     this->char_base_sp = 0;
111     this->char_live_sp = 0;
112     this->char_group_base_sp = 0;
113 
114     /* Update information related to the character sheet. */
115     if (this->cs->valid)
116     {
117         this->char_base_sp = this->cs->total_sp;
118         this->char_live_sp = this->cs->total_sp;
119     }
120 
121     /* Update information related to the skill queue. */
122     if (this->is_training())
123     {
124         ApiSkillTreePtr tree = ApiSkillTree::request();
125         this->training_info = *this->sq->get_training_skill();
126         this->training_skill = tree->get_skill_for_id(this->training_info.skill_id);
127         this->training_spph = this->sq->get_spph_for_current();
128 
129         if (this->training_skill == 0)
130         {
131             std::cout << "Warning: Skill in training (ID "
132                 << this->training_info.skill_id << ") not found. "
133                 << "Skill tree out of date?" << std::endl;
134         }
135     }
136 
137     /* Update information related to both sheets. */
138     if (this->cs->valid && this->sq->valid)
139     {
140         this->training_cskill = 0;
141         if (this->is_training())
142         {
143             /* Get the character skill in training. */
144             this->training_cskill = this->cs->get_skill_for_id
145                 (this->training_info.skill_id);
146 
147             if (this->training_cskill != 0)
148             {
149                 /* Cache the amount of SP in the active skill group. */
150                 int group_id = this->training_cskill->details->group;
151                 for (std::size_t i = 0; i < this->cs->skills.size(); ++i)
152                 {
153                     ApiCharSheetSkill& cskill = this->cs->skills[i];
154                     if (cskill.details->group == group_id)
155                         this->char_group_base_sp += cskill.points;
156                 }
157             }
158             else
159             {
160                 std::cout << "Warning: Skill in training (ID "
161                     << this->training_info.skill_id
162                     << ") is unknown to " << this->cs->name
163                     << "!" << std::endl;
164             }
165         }
166 
167         /* Update the character from skill queue information. */
168         this->update_from_queue();
169     }
170 
171     /* Update the live info. */
172     this->update_live_info();
173 }
174 
175 /* ---------------------------------------------------------------- */
176 
177 void
update_live_info(void)178 Character::update_live_info (void)
179 {
180   if (this->training_info.queue_pos < 0)
181     return;
182 
183   time_t evetime = EveTime::get_eve_time();
184   time_t finish = this->training_info.end_time_t;
185   time_t diff = finish - evetime;
186 
187   //std::cout << "** Evetime: " << evetime << ", Finish: " << finish
188   //    << ", time diff: " << diff << std::endl;
189 
190   /* Check if the skill is finished. */
191   if (diff < 0)
192   {
193     this->training_info.queue_pos = -1;
194     this->skill_completed();
195     return;
196   }
197 
198   /* Update easy values first to get useful results even in case of errors. */
199   unsigned int level_dest_sp = this->training_info.end_sp;
200   double spps = (double)this->training_spph / 3600.0;
201 
202   this->training_remaining = diff;
203   this->training_skill_sp = level_dest_sp - (unsigned int)((double)diff * spps);
204 
205   /* Check if the skill in training could be determined.
206    * This may be NULL if the skill was not available in the skill tree. */
207   if (this->training_skill == 0)
208     return;
209 
210   /* Update training live values. */
211   unsigned int level_start_sp = ApiCharSheet::calc_start_sp
212       (this->training_info.to_level - 1, this->training_skill->rank);
213   unsigned int level_total_sp = level_dest_sp - level_start_sp;
214 
215   this->training_level_sp = this->training_skill_sp - level_start_sp;
216   this->training_level_done = (double)this->training_level_sp
217       / (double)level_total_sp;
218 
219   //std::cout << "** Start SP: " << level_start_sp << ", Dest SP: "
220   //    << level_dest_sp << ", Level SP: " << level_total_sp << ", SP/s: "
221   //    << spps << std::endl;
222 
223   /* Check if the skill in training is available in the character.
224    * This may be NULL if the skill was not available in the skill tree. */
225   if (!this->cs->valid || this->training_cskill == 0)
226     return;
227 
228   /* Update character live values. */
229   unsigned int basediff_sp = this->training_skill_sp
230       - this->training_cskill->points;
231   this->char_live_sp = this->char_base_sp + basediff_sp;
232   this->char_group_live_sp = this->char_group_base_sp + basediff_sp;
233 }
234 
235 /* ---------------------------------------------------------------- */
236 
237 void
update_from_queue(void)238 Character::update_from_queue (void)
239 {
240     if (!this->sq->valid || !this->cs->valid)
241         return;
242 
243     /* Walk through the skill queue and update the character with already
244      * finished skills and the previous level of the skill in training.
245      */
246     time_t eve_time = EveTime::get_eve_time();
247     for (std::size_t i = 0; i < this->sq->queue.size(); ++i)
248     {
249         ApiSkillQueueItem const& item(this->sq->queue[i]);
250         if (item.start_time_t > eve_time)
251             break;
252         if (item.end_time_t < eve_time)
253             this->cs->add_char_skill(item.skill_id, item.to_level);
254         else
255             this->cs->add_char_skill(item.skill_id, item.to_level - 1);
256     }
257     this->char_base_sp = this->cs->total_sp;
258 }
259 
260 /* ---------------------------------------------------------------- */
261 
262 void
skill_completed(void)263 Character::skill_completed (void)
264 {
265     /* Update the character by registering completed skills. */
266     this->update_from_queue();
267 
268     /* Emit the completed signal to notify listeners. */
269     this->sig_skill_completed.emit();
270 
271     /* Clear old skill info and reprocess API data to update. */
272     this->training_cskill = 0;
273     this->training_skill = 0;
274     this->process_api_data();
275 
276     /* Emit signal to notify about changed skill in training. */
277     this->sig_training_changed.emit();
278 }
279 
280 /* ---------------------------------------------------------------- */
281 
282 std::string
get_char_name(void) const283 Character::get_char_name (void) const
284 {
285   if (this->cs->valid)
286     return this->cs->name;
287   else
288     return this->auth.char_id;
289 }
290 
291 /* ---------------------------------------------------------------- */
292 
293 std::string
get_training_text(void) const294 Character::get_training_text (void) const
295 {
296     if (this->is_training())
297     {
298         int skill_id = this->training_info.skill_id;
299         int to_level = this->training_info.to_level;
300 
301         std::string to_level_str = Helpers::get_roman_from_int(to_level);
302         std::string skill_str;
303         try
304         {
305             ApiSkillTreePtr tree = ApiSkillTree::request();
306             ApiSkill const* skill = tree->get_skill_for_id(skill_id);
307             if (skill == 0)
308                 throw Exception();
309             skill_str = skill->name;
310         }
311         catch (Exception& e)
312         {
313             /* This happens if the ID is not found. */
314             skill_str = Helpers::get_string_from_int(skill_id);
315         }
316 
317         return skill_str + " " + to_level_str;
318     }
319     else
320     {
321         return "No skill in training!";
322     }
323 }
324 
325 /* ---------------------------------------------------------------- */
326 
327 std::string
get_remaining_text(bool slim) const328 Character::get_remaining_text (bool slim) const
329 {
330     if (!this->sq->valid)
331         return "No training information!";
332 
333     if (this->sq->is_paused())
334         return "Training is paused!";
335 
336     if (!this->is_training())
337         return "No skill in training!";
338 
339     return EveTime::get_string_for_timediff(this->training_remaining, slim);
340 }
341 
342 /* ---------------------------------------------------------------- */
343 
344 std::string
get_summary_text(bool detailed)345 Character::get_summary_text (bool detailed)
346 {
347     std::string ret;
348     ret += this->get_char_name();
349     ret += " - ";
350 
351     if (this->is_training())
352     {
353         ret += this->get_remaining_text(true);
354         if (detailed)
355         {
356             ret += " - ";
357             ret += this->get_training_text();
358         }
359     }
360     else
361         ret += "Not training!";
362 
363     return ret;
364 }
365