1/**
2 * The Forgotten Server - a free and open-source MMORPG server emulator
3 * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "otpch.h"
21
22#include "outfit.h"
23
24#include "pugicast.h"
25#include "tools.h"
26
27bool Outfits::loadFromXml() {
28 pugi::xml_document doc;
29 pugi::xml_parse_result result = doc.load_file("data/XML/outfits.xml");
30 if (!result) {
31 printXMLError("Error - Outfits::loadFromXml", "data/XML/outfits.xml",
32 result);
33 return false;
34 }
35
36 for (auto outfitNode : doc.child("outfits").children()) {
37 pugi::xml_attribute attr;
38 if ((attr = outfitNode.attribute("enabled")) && !attr.as_bool()) {
39 continue;
40 }
41
42 if (!(attr = outfitNode.attribute("type"))) {
43 std::cout << "[Warning - Outfits::loadFromXml] Missing outfit type."
44 << std::endl;
45 continue;
46 }
47
48 uint16_t type = pugi::cast<uint16_t>(attr.value());
49 if (type > PLAYERSEX_LAST) {
50 std::cout << "[Warning - Outfits::loadFromXml] Invalid outfit type "
51 << type << "." << std::endl;
52 continue;
53 }
54
55 pugi::xml_attribute lookTypeAttribute = outfitNode.attribute("looktype");
56 if (!lookTypeAttribute) {
57 std::cout
58 << "[Warning - Outfits::loadFromXml] Missing looktype on outfit."
59 << std::endl;
60 continue;
61 }
62
63 uint16_t attackMeleeType = 0;
64 if ((attr = outfitNode.attribute("attackmeleetype"))) {
65 attackMeleeType = pugi::cast<uint16_t>(attr.value());
66 } else if ((attr = outfitNode.attribute("attacktype"))) {
67 attackMeleeType = pugi::cast<uint16_t>(attr.value());
68 }
69
70 uint16_t attackRangeType = 0;
71 if ((attr = outfitNode.attribute("attackrangetype"))) {
72 attackRangeType = pugi::cast<uint16_t>(attr.value());
73 } else {
74 attackRangeType = attackMeleeType;
75 }
76
77 uint8_t attackRange = 1;
78 if ((attr = outfitNode.attribute("attackrange"))) {
79 attackRange = std::max<uint8_t>(
80 1, static_cast<uint8_t>(pugi::cast<uint16_t>(attr.value())));
81 }
82
83 Outfit outfit(outfitNode.attribute("name").as_string(),
84 pugi::cast<uint16_t>(lookTypeAttribute.value()),
85 attackMeleeType, attackRangeType, attackRange,
86 outfitNode.attribute("premium").as_bool(),
87 outfitNode.attribute("unlocked").as_bool(true));
88
89 bool hasOffsets = false;
90 for (auto offsetNode : outfitNode.children("offset")) {
91 int dir = offsetNode.attribute("dir").as_int(-1);
92 if (dir < 0 || dir >= OUTFIT_DIRECTIONS) {
93 std::cout << "[Warning - Outfits::loadFromXml] Invalid offset dir "
94 << dir << " on outfit " << outfit.name << "." << std::endl;
95 continue;
96 }
97 hasOffsets = true;
98 outfit.offsets[dir].lookX = offsetNode.attribute("lookX").as_int(0);
99 outfit.offsets[dir].lookY = offsetNode.attribute("lookY").as_int(0);
100 outfit.offsets[dir].meleeX = offsetNode.attribute("meleeX").as_int(0);
101 outfit.offsets[dir].meleeY = offsetNode.attribute("meleeY").as_int(0);
102 outfit.offsets[dir].rangeX = offsetNode.attribute("rangeX").as_int(0);
103 outfit.offsets[dir].rangeY = offsetNode.attribute("rangeY").as_int(0);
104 }
105
106 outfits[type].push_back(std::move(outfit));
107 }
108 return true;
109}
110
111const Outfit *Outfits::getOutfitByLookType(PlayerSex_t sex,
112 uint16_t lookType) const {
113 for (const Outfit &outfit : outfits[sex]) {
114 if (outfit.lookType == lookType) {
115 return &outfit;
116 }
117 }
118 return nullptr;
119}
120