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
}