| 1 |
/* |
| 2 |
* Support for custom units |
| 3 |
* Copyright (C) 2009 The Mana World Development Team |
| 4 |
* |
| 5 |
* This file is part of The Mana World. |
| 6 |
* |
| 7 |
* This program is free software; you can redistribute it and/or modify |
| 8 |
* it under the terms of the GNU General Public License as published by |
| 9 |
* the Free Software Foundation; either version 2 of the License, or |
| 10 |
* any later version. |
| 11 |
* |
| 12 |
* This program is distributed in the hope that it will be useful, |
| 13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 |
* GNU General Public License for more details. |
| 16 |
* |
| 17 |
* You should have received a copy of the GNU General Public License |
| 18 |
* along with this program; if not, write to the Free Software |
| 19 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 20 |
*/ |
| 21 |
|
| 22 |
#include "units.h" |
| 23 |
|
| 24 |
#include "log.h" |
| 25 |
|
| 26 |
#include "utils/stringutils.h" |
| 27 |
#include "utils/xml.h" |
| 28 |
|
| 29 |
#include <cmath> |
| 30 |
#include <climits> |
| 31 |
#include <vector> |
| 32 |
|
| 33 |
struct UnitLevel { |
| 34 |
std::string symbol; |
| 35 |
int count; |
| 36 |
int round; |
| 37 |
}; |
| 38 |
|
| 39 |
struct UnitDescription { |
| 40 |
std::vector<struct UnitLevel> levels; |
| 41 |
double conversion; |
| 42 |
bool mix; |
| 43 |
}; |
| 44 |
|
| 45 |
enum UnitType { |
| 46 |
UNIT_WEIGHT = 0, |
| 47 |
UNIT_CURRENCY = 1, |
| 48 |
UNIT_END |
| 49 |
}; |
| 50 |
|
| 51 |
struct UnitDescription units[UNIT_END]; |
| 52 |
|
| 53 |
void Units::loadUnits() |
| 54 |
{ |
| 55 |
{ // Setup default weight |
| 56 |
struct UnitDescription ud; |
| 57 |
|
| 58 |
ud.conversion = 1.0; |
| 59 |
ud.mix = false; |
| 60 |
|
| 61 |
struct UnitLevel bu; |
| 62 |
bu.symbol = "g"; |
| 63 |
bu.count = 1; |
| 64 |
bu.round = 0; |
| 65 |
|
| 66 |
ud.levels.push_back(bu); |
| 67 |
|
| 68 |
struct UnitLevel ul; |
| 69 |
ul.symbol = "kg"; |
| 70 |
ul.count = 1000; |
| 71 |
ul.round = 2; |
| 72 |
|
| 73 |
ud.levels.push_back(ul); |
| 74 |
|
| 75 |
units[UNIT_WEIGHT] = ud; |
| 76 |
} |
| 77 |
|
| 78 |
{ // Setup default currency |
| 79 |
struct UnitDescription ud; |
| 80 |
|
| 81 |
ud.conversion = 1.0; |
| 82 |
ud.mix = false; |
| 83 |
|
| 84 |
struct UnitLevel bu; |
| 85 |
bu.symbol = "¤"; |
| 86 |
bu.count = 1; |
| 87 |
bu.round = 0; |
| 88 |
|
| 89 |
ud.levels.push_back(bu); |
| 90 |
|
| 91 |
units[UNIT_CURRENCY] = ud; |
| 92 |
} |
| 93 |
|
| 94 |
XML::Document doc("units.xml"); |
| 95 |
xmlNodePtr root = doc.rootNode(); |
| 96 |
|
| 97 |
if (!root || !xmlStrEqual(root->name, BAD_CAST "units")) |
| 98 |
{ |
| 99 |
logger->log("Error loading unit definition file: units.xml"); |
| 100 |
return; |
| 101 |
} |
| 102 |
|
| 103 |
for_each_xml_child_node(node, root) |
| 104 |
{ |
| 105 |
if (xmlStrEqual(node->name, BAD_CAST "unit")) |
| 106 |
{ |
| 107 |
struct UnitDescription ud; |
| 108 |
int level = 1; |
| 109 |
const std::string type = XML::getProperty(node, "type", ""); |
| 110 |
ud.conversion = XML::getProperty(node, "conversion", 1.0); |
| 111 |
ud.mix = XML::getProperty(node, "mix", "no") == "yes"; |
| 112 |
|
| 113 |
struct UnitLevel bu; |
| 114 |
bu.symbol = XML::getProperty(node, "base", "¤"); |
| 115 |
bu.count = 1; |
| 116 |
bu.round = XML::getProperty(node, "round", 2); |
| 117 |
|
| 118 |
ud.levels.push_back(bu); |
| 119 |
|
| 120 |
for_each_xml_child_node(uLevel, node) |
| 121 |
{ |
| 122 |
if (xmlStrEqual(uLevel->name, BAD_CAST "level")) |
| 123 |
{ |
| 124 |
struct UnitLevel ul; |
| 125 |
ul.symbol = XML::getProperty(uLevel, "symbol", |
| 126 |
strprintf("¤%d",level)); |
| 127 |
ul.count = XML::getProperty(uLevel, "count", -1); |
| 128 |
ul.round = XML::getProperty(uLevel, "round", bu.round); |
| 129 |
|
| 130 |
if (ul.count > 0) |
| 131 |
{ |
| 132 |
ud.levels.push_back(ul); |
| 133 |
level++; |
| 134 |
} |
| 135 |
else |
| 136 |
{ |
| 137 |
logger->log("Error bad unit count: %d for %s in %s", |
| 138 |
ul.count, ul.symbol.c_str(), bu.symbol.c_str()); |
| 139 |
} |
| 140 |
} |
| 141 |
} |
| 142 |
|
| 143 |
// Add one more level for saftey |
| 144 |
struct UnitLevel ll; |
| 145 |
ll.symbol = ""; |
| 146 |
ll.count = INT_MAX; |
| 147 |
ll.round = 0; |
| 148 |
|
| 149 |
ud.levels.push_back(ll); |
| 150 |
|
| 151 |
if (type == "weight") |
| 152 |
units[UNIT_WEIGHT] = ud; |
| 153 |
else if (type == "currency") |
| 154 |
units[UNIT_CURRENCY] = ud; |
| 155 |
else |
| 156 |
logger->log("Error unknown unit type: %s", type.c_str()); |
| 157 |
} |
| 158 |
} |
| 159 |
} |
| 160 |
|
| 161 |
std::string formatUnit(int value, int type) |
| 162 |
{ |
| 163 |
struct UnitDescription ud = units[type]; |
| 164 |
struct UnitLevel ul; |
| 165 |
|
| 166 |
// Shortcut for 0; do the same for values less than 0 (for now) |
| 167 |
if (value <= 0) { |
| 168 |
ul = ud.levels[0]; |
| 169 |
return strprintf("0%s", ul.symbol.c_str()); |
| 170 |
} else { |
| 171 |
double amount = ud.conversion * value; |
| 172 |
|
| 173 |
// If only the first level is needed, act like mix if false |
| 174 |
if (ud.mix && ud.levels.size() > 0 && ud.levels[1].count < amount) |
| 175 |
{ |
| 176 |
std::string output; |
| 177 |
struct UnitLevel pl = ud.levels[0]; |
| 178 |
ul = ud.levels[1]; |
| 179 |
int levelAmount = (int) amount; |
| 180 |
int nextAmount; |
| 181 |
|
| 182 |
levelAmount /= ul.count; |
| 183 |
|
| 184 |
amount -= levelAmount * ul.count; |
| 185 |
|
| 186 |
if (amount > 0) { |
| 187 |
output = strprintf("%.*f%s", pl.round, amount, |
| 188 |
pl.symbol.c_str()); |
| 189 |
} |
| 190 |
|
| 191 |
for (unsigned int i = 2; i < ud.levels.size(); i++) |
| 192 |
{ |
| 193 |
pl = ul; |
| 194 |
ul = ud.levels[i]; |
| 195 |
|
| 196 |
nextAmount = levelAmount / ul.count; |
| 197 |
levelAmount %= ul.count; |
| 198 |
|
| 199 |
if (levelAmount > 0) output = strprintf("%d%s", |
| 200 |
levelAmount, pl.symbol.c_str()) + output; |
| 201 |
|
| 202 |
if (!nextAmount) break; |
| 203 |
levelAmount = nextAmount; |
| 204 |
} |
| 205 |
|
| 206 |
return output; |
| 207 |
} |
| 208 |
else |
| 209 |
{ |
| 210 |
for (unsigned int i = 0; i < ud.levels.size(); i++) |
| 211 |
{ |
| 212 |
ul = ud.levels[i]; |
| 213 |
if (amount < ul.count && ul.count > 0) { |
| 214 |
ul = ud.levels[i - 1]; |
| 215 |
break; |
| 216 |
} |
| 217 |
amount /= ul.count; |
| 218 |
} |
| 219 |
|
| 220 |
return strprintf("%.*f%s", ul.round, amount, ul.symbol.c_str()); |
| 221 |
} |
| 222 |
} |
| 223 |
} |
| 224 |
|
| 225 |
std::string Units::formatCurrency(int value) |
| 226 |
{ |
| 227 |
return formatUnit(value, UNIT_CURRENCY); |
| 228 |
} |
| 229 |
|
| 230 |
std::string Units::formatWeight(int value) |
| 231 |
{ |
| 232 |
return formatUnit(value, UNIT_WEIGHT); |
| 233 |
} |