1 #include <cmath>
2 #include <iostream>
3 #include <libxml/xmlmemory.h>
4 #include <libxml/parser.h>
5
6 #include "util/exception.h"
7 #include "util/helpers.h"
8 #include "xml.h"
9 #include "apibase.h"
10 #include "apiskilltree.h"
11 #include "apicerttree.h"
12 #include "apicharsheet.h"
13
14 void
set_api_data(EveApiData const & data)15 ApiCharSheet::set_api_data (EveApiData const& data)
16 {
17 this->valid = false;
18 this->ApiBase::set_api_data(data);
19
20 /* Reset values. */
21 this->implant = 0.0;
22 this->total_sp = 0;
23 for (int i = 0; i < 6; ++i)
24 this->skills_at[i] = 0;
25
26 /* Parse the data. */
27 this->parse_xml();
28
29 /* Force the sheet to have at least 30 minutes cache time. */
30 this->enforce_cache_time(API_CHAR_SHEET_MIN_CACHE_TIME);
31
32 /* Find bonus attributes for skills. */
33 this->total = this->base + this->implant;
34
35 /* Calculate start SP, destination SP and completed. */
36 ApiSkillTreePtr stree = ApiSkillTree::request();
37 for (std::size_t i = 0; i < this->skills.size(); ++i)
38 {
39 ApiCharSheetSkill& cskill = this->skills[i];
40 ApiSkill const* skill = stree->get_skill_for_id(cskill.id);
41 if (skill == 0)
42 {
43 std::cout << "Warning: Ignoring unknown skill (ID " << cskill.id
44 << "). Update SkillTree.xml." << std::endl;
45 this->skills.erase(this->skills.begin() + i);
46 i -= 1;
47 continue;
48 }
49
50 /* Update a few fields of the char sheet skill. */
51 cskill.details = skill;
52 cskill.points_start = ApiCharSheet::calc_start_sp
53 (skills[i].level, skill->rank);
54 cskill.points_dest = ApiCharSheet::calc_dest_sp
55 (skills[i].level, skill->rank);
56 cskill.points_max = ApiCharSheet::calc_dest_sp
57 (4, skill->rank);
58
59 cskill.completed = (double)(skills[i].points - skills[i].points_start)
60 / (double)(skills[i].points_dest - skills[i].points_start);
61
62 /* Sum up the total amount of SP for the character. */
63 this->total_sp += cskill.points;
64 this->skills_at[cskill.level] += 1;
65 }
66
67 /* Update certificate field "details". */
68 ApiCertTreePtr ctree = ApiCertTree::request();
69 for (unsigned int i = 0; i < this->certs.size(); ++i)
70 {
71 ApiCharSheetCert& ccert = this->certs[i];
72 ApiCert const* cert = ctree->get_certificate_for_id(ccert.id);
73 if (cert == 0)
74 {
75 std::cout << "Warning: Ignoring unknown certificate (ID " << ccert.id
76 << "). Update GtkEveMon or CertificateTree.xml." << std::endl;
77 this->certs.erase(this->certs.begin() + i);
78 i -= 1;
79 continue;
80 }
81
82 ccert.details = cert;
83 }
84
85 //this->debug_dump();
86 this->valid = true;
87 }
88
89 /* ---------------------------------------------------------------- */
90
91 void
parse_xml(void)92 ApiCharSheet::parse_xml (void)
93 {
94 this->skills.clear();
95
96 std::cout << "Parsing XML: CharacterSheet.xml ..." << std::endl;
97 XmlDocumentPtr xml = XmlDocument::create
98 (&this->http_data->data[0], this->http_data->data.size());
99 xmlNodePtr root = xml->get_root_element();
100 this->parse_eveapi_tag(root);
101 }
102
103 /* ---------------------------------------------------------------- */
104
105 void
parse_eveapi_tag(xmlNodePtr node)106 ApiCharSheet::parse_eveapi_tag (xmlNodePtr node)
107 {
108 if (node->type != XML_ELEMENT_NODE
109 || xmlStrcmp(node->name, (xmlChar const*)"eveapi"))
110 throw Exception("Invalid XML root. Expecting <eveapi> node.");
111
112 for (node = node->children; node != 0; node = node->next)
113 {
114 /* Let the base class know of some fields. */
115 this->check_node(node);
116
117 if (node->type == XML_ELEMENT_NODE
118 && !xmlStrcmp(node->name, (xmlChar const*)"result"))
119 {
120 //std::cout << "Found <result> tag" << std::endl;
121 this->parse_result_tag(node->children);
122 }
123 }
124 }
125
126 /* ---------------------------------------------------------------- */
127
128 void
parse_result_tag(xmlNodePtr node)129 ApiCharSheet::parse_result_tag (xmlNodePtr node)
130 {
131 for (; node != 0; node = node->next)
132 {
133 if (node->type != XML_ELEMENT_NODE)
134 continue;
135
136 this->set_string_if_node_text(node, "characterID", this->char_id);
137 this->set_string_if_node_text(node, "name", this->name);
138 this->set_string_if_node_text(node, "race", this->race);
139 this->set_string_if_node_text(node, "bloodLine", this->bloodline);
140 this->set_string_if_node_text(node, "gender", this->gender);
141 this->set_string_if_node_text(node, "corporationName", this->corp);
142 this->set_string_if_node_text(node, "balance", this->balance);
143
144 this->set_string_if_node_text(node, "cloneName", this->clone_name);
145 this->set_uint_if_node_text(node, "cloneSkillPoints", this->clone_sp);
146 this->set_uint_if_node_text(node, "freeSkillPoints", this->free_sp);
147 this->set_string_if_node_text(node, "lastRespecDate", this->last_respec);
148 this->set_string_if_node_text(node, "lastTimedRespec", this->last_timed_respec);
149 this->set_uint_if_node_text(node, "freeRespecs", this->free_respecs);
150 this->set_string_if_node_text(node, "cloneJumpDate", this->last_clone_jump);
151
152 if (!xmlStrcmp(node->name, (xmlChar const*)"attributes"))
153 this->parse_attribute_tag(node->children);
154
155 if (!xmlStrcmp(node->name, (xmlChar const*)"attributeEnhancers"))
156 this->parse_attrib_enhancers_tag(node->children);
157
158 if (!xmlStrcmp(node->name, (xmlChar const*)"rowset"))
159 {
160 std::string name = this->get_property(node, "name");
161 if (name == "skills")
162 this->parse_skills_tag(node->children);
163 else if (name == "certificates")
164 this->parse_certificates_tag(node->children);
165 }
166 }
167 }
168
169 /* ---------------------------------------------------------------- */
170
171 void
parse_attribute_tag(xmlNodePtr node)172 ApiCharSheet::parse_attribute_tag (xmlNodePtr node)
173 {
174 for (; node != 0; node = node->next)
175 {
176 if (node->type != XML_ELEMENT_NODE)
177 continue;
178
179 this->set_double_if_node_text(node, "intelligence", this->base.intl);
180 this->set_double_if_node_text(node, "memory", this->base.mem);
181 this->set_double_if_node_text(node, "charisma", this->base.cha);
182 this->set_double_if_node_text(node, "perception", this->base.per);
183 this->set_double_if_node_text(node, "willpower", this->base.wil);
184 }
185 }
186
187 /* ---------------------------------------------------------------- */
188
189 void
parse_attrib_enhancers_tag(xmlNodePtr node)190 ApiCharSheet::parse_attrib_enhancers_tag (xmlNodePtr node)
191 {
192 for (; node != 0; node = node->next)
193 {
194 if (node->type != XML_ELEMENT_NODE)
195 continue;
196
197 this->find_implant_bonus(node, "memoryBonus", this->implant.mem);
198 this->find_implant_bonus(node, "willpowerBonus", this->implant.wil);
199 this->find_implant_bonus(node, "perceptionBonus", this->implant.per);
200 this->find_implant_bonus(node, "intelligenceBonus", this->implant.intl);
201 this->find_implant_bonus(node, "charismaBonus", this->implant.cha);
202 }
203 }
204
205 /* ---------------------------------------------------------------- */
206
207 void
parse_skills_tag(xmlNodePtr node)208 ApiCharSheet::parse_skills_tag (xmlNodePtr node)
209 {
210 for (; node != 0; node = node->next)
211 {
212 if (node->type != XML_ELEMENT_NODE)
213 continue;
214
215 if (!xmlStrcmp(node->name, (xmlChar const*)"row"))
216 {
217 /* Prepare a new skill to append. */
218 ApiCharSheetSkill skill;
219
220 /* Fetch these fields. If they are not there, it's an error. */
221 try
222 {
223 skill.id = Helpers::get_int_from_string
224 (this->get_property(node, "typeID"));
225 skill.points = Helpers::get_int_from_string
226 (this->get_property(node, "skillpoints"));
227 }
228 catch (Exception& e)
229 {
230 throw Exception(e + " in element \"row\"");
231 }
232
233 /* Fetch the level field. If it's not there, ignore the skill.
234 * This might happen if the skill in "unpublished" (removed?). */
235 try
236 {
237 skill.level = Helpers::get_int_from_string
238 (this->get_property(node, "level"));
239
240 /* Add skill to list. */
241 this->skills.push_back(skill);
242 }
243 catch (Exception& e)
244 {
245 std::cout << "Warning: Ignoring skill without "
246 "\"level\" attribute: " << skill.id << std::endl;
247 }
248 }
249 }
250 }
251
252 /* ---------------------------------------------------------------- */
253
254 void
parse_certificates_tag(xmlNodePtr node)255 ApiCharSheet::parse_certificates_tag (xmlNodePtr node)
256 {
257 for (; node != 0; node = node->next)
258 {
259 if (node->type != XML_ELEMENT_NODE)
260 continue;
261
262 if (!xmlStrcmp(node->name, (xmlChar const*)"row"))
263 {
264 ApiCharSheetCert cert;
265
266 try
267 {
268 cert.id = Helpers::get_int_from_string
269 (this->get_property(node, "certificateID"));
270 this->certs.push_back(cert);
271 }
272 catch (Exception& e)
273 {
274 throw Exception(e + " in element \"row\"");
275 }
276 }
277 }
278 }
279
280 /* ---------------------------------------------------------------- */
281
282 void
find_implant_bonus(xmlNodePtr node,char const * name,double & v)283 ApiCharSheet::find_implant_bonus (xmlNodePtr node, char const* name, double& v)
284 {
285 if (node->type == XML_ELEMENT_NODE
286 && !xmlStrcmp(node->name, (xmlChar const*)name))
287 {
288 node = node->children;
289 while (node != 0)
290 {
291 this->set_double_if_node_text(node, "augmentatorValue", v);
292 node = node->next;
293 }
294 }
295 }
296
297 /* ---------------------------------------------------------------- */
298
299 void
add_char_skill(int skill_id,int level)300 ApiCharSheet::add_char_skill (int skill_id, int level)
301 {
302 /* Bail out if we get an invalid level. */
303 if (level < 0 || level > 5)
304 return;
305
306 ApiCharSheetSkill* cskill = this->get_skill_for_id(skill_id);
307
308 /* Bail out if the character has the skill and level. */
309 if (cskill != 0 && cskill->level >= level)
310 return;
311
312 /* Create skill if the character does not have it, update otherwise. */
313 if (cskill == 0)
314 {
315 /* Create new skill. */
316 ApiSkillTreePtr tree = ApiSkillTree::request();
317 ApiSkill const* skill = tree->get_skill_for_id(skill_id);
318
319 if (skill == 0)
320 {
321 std::cout << "Warning: Cannot add skill (ID " << skill_id
322 << ") to " << this->name << ", skill not available!" << std::endl;
323 return;
324 }
325
326 int skill_start_sp = ApiCharSheet::calc_start_sp(level, skill->rank);
327 int skill_dest_sp = ApiCharSheet::calc_dest_sp(level, skill->rank);
328 int skill_max_points = ApiCharSheet::calc_dest_sp(4, skill->rank);
329
330 ApiCharSheetSkill new_cskill;
331 new_cskill.id = skill->id;
332 new_cskill.level = level;
333 new_cskill.points = skill_start_sp;
334 new_cskill.points_max = skill_max_points;
335 new_cskill.points_start = skill_start_sp;
336 new_cskill.points_dest = skill_dest_sp;
337 new_cskill.completed = 0.0;
338 new_cskill.details = skill;
339
340 this->skills.push_back(new_cskill);
341 this->skills_at[level] += 1;
342 this->total_sp += skill_start_sp;
343 }
344 else
345 {
346 /* Update existing skill. */
347 ApiSkill const* skill = cskill->details;
348
349 int old_level = cskill->level;
350 int old_points = cskill->points;
351
352 int skill_start_sp = ApiCharSheet::calc_start_sp(level, skill->rank);
353 int skill_dest_sp = ApiCharSheet::calc_dest_sp(level, skill->rank);
354 int skill_max_points = ApiCharSheet::calc_dest_sp(4, skill->rank);
355
356 cskill->level = level;
357 cskill->points = skill_start_sp;
358 cskill->points_max = skill_max_points;
359 cskill->points_start = skill_start_sp;
360 cskill->points_dest = skill_dest_sp;
361 cskill->completed = 0.0;
362
363 this->skills_at[old_level] -= 1;
364 this->skills_at[level] += 1;
365 this->total_sp -= old_points;
366 this->total_sp += skill_start_sp;
367 }
368 }
369
370 /* ---------------------------------------------------------------- */
371
372 int
get_level_for_skill(int id) const373 ApiCharSheet::get_level_for_skill (int id) const
374 {
375 for (std::size_t i = 0; i < this->skills.size(); ++i)
376 if (this->skills[i].id == id)
377 return this->skills[i].level;
378
379 /* Return level 0 if skill is not in the list. */
380 return 0;
381 }
382
383 /* ---------------------------------------------------------------- */
384
385 ApiCharSheetSkill*
get_skill_for_id(int id)386 ApiCharSheet::get_skill_for_id (int id)
387 {
388 for (std::size_t i = 0; i < this->skills.size(); ++i)
389 if (this->skills[i].id == id)
390 return &this->skills[i];
391
392 return 0;
393 }
394
395 /* ---------------------------------------------------------------- */
396
397 ApiCharSheetCert*
get_cert_for_id(int id)398 ApiCharSheet::get_cert_for_id (int id)
399 {
400 for (std::size_t i = 0; i < this->certs.size(); ++i)
401 if (this->certs[i].id == id)
402 return &this->certs[i];
403
404 return 0;
405 }
406
407 /* ---------------------------------------------------------------- */
408
409 int
get_grade_for_class(int class_id) const410 ApiCharSheet::get_grade_for_class (int class_id) const
411 {
412 int grade = 0;
413 for (std::size_t i = 0; i < this->certs.size(); ++i)
414 {
415 ApiCert const* cert = this->certs[i].details;
416 ApiCertClass const* cclass = cert->class_details;
417 if (cclass->id == class_id && cert->grade > grade)
418 grade = this->certs[i].details->grade;
419 }
420
421 return grade;
422 }
423
424 /* ---------------------------------------------------------------- */
425
426 unsigned int
get_spph_for_skill(ApiSkill const * skill,ApiCharAttribs const & attribs)427 ApiCharSheet::get_spph_for_skill (ApiSkill const* skill,
428 ApiCharAttribs const& attribs)
429 {
430 double pri;
431 double sec;
432
433 if (skill == 0)
434 return 0;
435
436 switch (skill->primary)
437 {
438 case API_ATTRIB_INTELLIGENCE: pri = attribs.intl; break;
439 case API_ATTRIB_MEMORY: pri = attribs.mem; break;
440 case API_ATTRIB_CHARISMA: pri = attribs.cha; break;
441 case API_ATTRIB_PERCEPTION: pri = attribs.per; break;
442 case API_ATTRIB_WILLPOWER: pri = attribs.wil; break;
443 default: pri = 0.0;
444 }
445
446 switch (skill->secondary)
447 {
448 case API_ATTRIB_INTELLIGENCE: sec = attribs.intl; break;
449 case API_ATTRIB_MEMORY: sec = attribs.mem; break;
450 case API_ATTRIB_CHARISMA: sec = attribs.cha; break;
451 case API_ATTRIB_PERCEPTION: sec = attribs.per; break;
452 case API_ATTRIB_WILLPOWER: sec = attribs.wil; break;
453 default: sec = 0.0;
454 }
455
456 double sppm = (pri + sec / 2.0);
457 return (unsigned int)(sppm * 60.0 + 0.5);
458 }
459
460 /* ---------------------------------------------------------------- */
461
462 int
calc_start_sp(int level,int rank)463 ApiCharSheet::calc_start_sp (int level, int rank)
464 {
465 if (level <= 0)
466 return 0;
467
468 return ApiCharSheet::calc_dest_sp(level - 1, rank);
469 }
470
471 /* ---------------------------------------------------------------- */
472
473 int
calc_dest_sp(int level,int rank)474 ApiCharSheet::calc_dest_sp (int level, int rank)
475 {
476 return (int)::ceil(250.0 * rank * ::pow(32.0, level / 2.0));
477 }
478
479 /* ---------------------------------------------------------------- */
480
481 void
debug_dump(void)482 ApiCharSheet::debug_dump (void)
483 {
484 std::cout << "Character dump for " << this->name << std::endl;
485 std::cout << "List of all skills:" << std::endl;
486
487 for (std::size_t i = 0; i < this->skills.size(); ++i)
488 {
489 std::cout << " " << this->skills[i].details->name
490 << " (" << this->skills[i].id << ")" << std::endl;
491 }
492
493 std::cout << "List of all certs:" << std::endl;
494 for (std::size_t i = 0; i < this->certs.size(); ++i)
495 {
496 std::cout << " " << /*this->certs[i].details->name*/ "Unnamed yet"
497 << " (" << this->certs[i].id << ")" << std::endl;
498 }
499 }
500
501 /* ---------------------------------------------------------------- */
502
503 bool
is_skill_known(int id)504 ApiCharSheet::is_skill_known (int id)
505 {
506 for (size_t i = 0; i < skills.size(); i++)
507 {
508 if (skills[i].id == id)
509 return true;
510 }
511 return false;
512 }
513
514 /* ---------------------------------------------------------------- */
515
516 ApiCharAttribs
operator +(ApiCharAttribs const & atts) const517 ApiCharAttribs::operator+ (ApiCharAttribs const& atts) const
518 {
519 ApiCharAttribs result;
520 result.cha = this->cha + atts.cha;
521 result.intl = this->intl + atts.intl;
522 result.mem = this->mem + atts.mem;
523 result.per = this->per + atts.per;
524 result.wil = this->wil + atts.wil;
525 return result;
526 }
527
528 /* ---------------------------------------------------------------- */
529
530 ApiCharAttribs
operator +(double const & value) const531 ApiCharAttribs::operator+ (double const& value) const
532 {
533 ApiCharAttribs result;
534 result.cha = this->cha + value;
535 result.intl = this->intl + value;
536 result.mem = this->mem + value;
537 result.per = this->per + value;
538 result.wil = this->wil + value;
539 return result;
540 }
541
542 /* ---------------------------------------------------------------- */
543
544 ApiCharAttribs&
operator +=(ApiCharAttribs const & atts)545 ApiCharAttribs::operator+= (ApiCharAttribs const& atts)
546 {
547 this->cha += atts.cha;
548 this->intl += atts.intl;
549 this->mem += atts.mem;
550 this->per += atts.per;
551 this->wil += atts.wil;
552 return *this;
553 }
554
555 /* ---------------------------------------------------------------- */
556
557 ApiCharAttribs
operator -(ApiCharAttribs const & atts) const558 ApiCharAttribs::operator- (ApiCharAttribs const& atts) const
559 {
560 ApiCharAttribs result;
561 result.cha = this->cha - atts.cha;
562 result.intl = this->intl - atts.intl;
563 result.mem = this->mem - atts.mem;
564 result.per = this->per - atts.per;
565 result.wil = this->wil - atts.wil;
566 return result;
567 }
568
569 /* ---------------------------------------------------------------- */
570
571 ApiCharAttribs
operator -(double const & value) const572 ApiCharAttribs::operator- (double const& value) const
573 {
574 ApiCharAttribs result;
575 result.cha = this->cha - value;
576 result.intl = this->intl - value;
577 result.mem = this->mem - value;
578 result.per = this->per - value;
579 result.wil = this->wil - value;
580 return result;
581 }
582
583 /* ---------------------------------------------------------------- */
584
585 ApiCharAttribs&
operator -=(ApiCharAttribs const & atts)586 ApiCharAttribs::operator-= (ApiCharAttribs const& atts)
587 {
588 this->cha -= atts.cha;
589 this->intl -= atts.intl;
590 this->mem -= atts.mem;
591 this->per -= atts.per;
592 this->wil -= atts.wil;
593 return *this;
594 }
595
596 /* ---------------------------------------------------------------- */
597
598 ApiCharAttribs
operator *(ApiCharAttribs const & atts) const599 ApiCharAttribs::operator* (ApiCharAttribs const& atts) const
600 {
601 ApiCharAttribs result;
602 result.cha = this->cha * atts.cha;
603 result.intl = this->intl * atts.intl;
604 result.mem = this->mem * atts.mem;
605 result.per = this->per * atts.per;
606 result.wil = this->wil * atts.wil;
607 return result;
608 }
609
610 /* ---------------------------------------------------------------- */
611
612 ApiCharAttribs
operator *(double const & value) const613 ApiCharAttribs::operator* (double const& value) const
614 {
615 ApiCharAttribs result;
616 result.cha = this->cha * value;
617 result.intl = this->intl * value;
618 result.mem = this->mem * value;
619 result.per = this->per * value;
620 result.wil = this->wil * value;
621 return result;
622 }
623
624 /* ---------------------------------------------------------------- */
625
626 ApiCharAttribs
operator /(ApiCharAttribs const & atts) const627 ApiCharAttribs::operator/ (ApiCharAttribs const& atts) const
628 {
629 ApiCharAttribs result;
630 result.cha = this->cha / atts.cha;
631 result.intl = this->intl / atts.intl;
632 result.mem = this->mem / atts.mem;
633 result.per = this->per / atts.per;
634 result.wil = this->wil / atts.wil;
635 return result;
636 }
637
638 /* ---------------------------------------------------------------- */
639
640 ApiCharAttribs
operator /(double const & value) const641 ApiCharAttribs::operator/ (double const& value) const
642 {
643 ApiCharAttribs result;
644 result.cha = this->cha / value;
645 result.intl = this->intl / value;
646 result.mem = this->mem / value;
647 result.per = this->per / value;
648 result.wil = this->wil / value;
649 return result;
650 }
651
652 /* ---------------------------------------------------------------- */
653
654 ApiCharAttribs&
operator =(double const & value)655 ApiCharAttribs::operator= (double const& value)
656 {
657 this->cha = value;
658 this->intl = value;
659 this->mem = value;
660 this->per = value;
661 this->wil = value;
662 return *this;
663 }
664