Mana
Loading...
Searching...
No Matches
skilldialog.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "gui/skilldialog.h"
23
24#include "log.h"
25#include "playerinfo.h"
26#include "configuration.h"
27
28#include "gui/setup.h"
29
30#include "gui/widgets/button.h"
31#include "gui/widgets/label.h"
32#include "gui/widgets/listbox.h"
34#include "gui/widgets/tab.h"
37
38#include "net/net.h"
39#include "net/playerhandler.h"
40
41#include "resources/image.h"
43#include "resources/theme.h"
44
45#include "utils/gettext.h"
46#include "utils/stringutils.h"
47#include "utils/xml.h"
48
49#include <guichan/font.hpp>
50
51#include <string>
52
53#define SKILLS_FILE "skills.xml"
54
55class SkillModel;
56class SkillEntry;
57
59{
60 unsigned short id;
61 std::string name;
64 bool visible;
65 SkillModel *model = nullptr;
66
67 std::string skillLevel;
69
70 std::string skillExp;
71 float progress;
72 gcn::Color color;
73
74 ~SkillInfo() = default;
75
76 void setIcon(const std::string &iconPath)
77 {
79 if (!iconPath.empty())
80 icon = res->getImage(iconPath);
81
82 if (!icon)
84 }
85
86 void update();
87
88 void draw(Graphics *graphics, int y, int width);
89};
90
91class SkillModel : public gcn::ListModel
92{
93public:
94 int getNumberOfElements() override
95 { return mVisibleSkills.size(); }
96
97 SkillInfo *getSkillAt(int i) const
98 { return mVisibleSkills.at(i); }
99
100 std::string getElementAt(int i) override
101 { return getSkillAt(i)->name; }
102
103 void updateVisibilities();
104
105 void addSkill(std::unique_ptr<SkillInfo> info)
106 { mSkills.push_back(std::move(info)); }
107
108private:
109 std::vector<std::unique_ptr<SkillInfo>> mSkills;
110 std::vector<SkillInfo *> mVisibleSkills;
111};
112
113class SkillListBox : public ListBox
114{
115public:
117 : ListBox(model)
118 {}
119
121 {
122 const int selected = getSelected();
123 if (selected < 0 || selected > mListModel->getNumberOfElements())
124 return nullptr;
125
126 return static_cast<SkillModel*>(mListModel)->getSkillAt(selected);
127 }
128
129 void draw(gcn::Graphics *gcnGraphics) override
130 {
131 if (!mListModel)
132 return;
133
134 auto *model = static_cast<SkillModel *>(mListModel);
135 auto *graphics = static_cast<Graphics *>(gcnGraphics);
136
137 graphics->setFont(getFont());
138
139 // Draw filled rectangle around the selected list element
140 if (mSelected >= 0)
141 {
142 auto highlightColor = Theme::getThemeColor(Theme::HIGHLIGHT);
143 highlightColor.a = gui->getTheme()->getGuiAlpha();
144 graphics->setColor(highlightColor);
145 graphics->fillRectangle(gcn::Rectangle(0, getRowHeight() * mSelected,
146 getWidth(), getRowHeight()));
147 }
148
149 // Draw the list elements
150 for (int i = 0, y = 1;
151 i < model->getNumberOfElements();
152 ++i, y += getRowHeight())
153 {
154 if (mSelected == i)
156 else
158
159 if (SkillInfo *e = model->getSkillAt(i))
160 e->draw(graphics, y, getWidth());
161 }
162 }
163
164 unsigned int getRowHeight() const override { return 34; }
165};
166
167class SkillTab : public Tab
168{
169public:
170 SkillTab(const std::string &name, SkillListBox *listBox)
171 : mListBox(listBox)
172 {
173 setCaption(name);
174 }
175
176 ~SkillTab() override
177 {
178 delete mListBox;
179 }
180
182 {
183 return mListBox->getSelectedInfo();
184 }
185
186private:
188};
189
191 Window(_("Skills"))
192{
194
195 setWindowName("Skills");
196 setCloseButton(true);
197 setResizable(true);
198 setSaveVisible(true);
199 setDefaultSize(windowContainer->getWidth() - 280, 30, 275, 425);
200 setMinHeight(113);
201 setMinWidth(240);
203
205 mPointsLabel = new Label("0");
206 mIncreaseButton = new Button(_("Up"), "inc", this);
207
208 place(0, 0, mTabbedArea, 5, 5);
209 place(0, 5, mPointsLabel, 4);
210 place(4, 5, mIncreaseButton);
211
212 center();
214}
215
220
221void SkillDialog::action(const gcn::ActionEvent &event)
222{
223 if (event.getId() == "inc")
224 {
225 auto *tab = static_cast<SkillTab*>(mTabbedArea->getSelectedTab());
226 if (SkillInfo *info = tab->getSelectedInfo())
228 }
229 else if (event.getId() == "close")
230 {
231 setVisible(false);
232 }
233}
234
235std::string SkillDialog::update(int id)
236{
237 auto i = mSkills.find(id);
238 if (i != mSkills.end())
239 {
240 SkillInfo &info = *i->second;
241 info.update();
242 return info.name;
243 }
244
245 return std::string();
246}
247
249{
250 mPointsLabel->setCaption(strprintf(_("Skill points available: %d"),
252 mPointsLabel->adjustSize();
253
254 for (auto &skill : mSkills)
255 skill.second->update();
256}
257
258void SkillDialog::event(Event::Channel channel, const Event &event)
259{
260 if (event.getType() == Event::UpdateAttribute)
261 {
262 if (event.getInt("id") == SKILL_POINTS)
263 {
264 update();
265 }
266 }
267 else if (event.getType() == Event::UpdateStat)
268 {
269 auto it = mSkills.find(event.getInt("id"));
270 if (it != mSkills.end())
271 it->second->update();
272 }
273}
274
276{
277 for (auto &tab : mTabs)
278 mTabbedArea->removeTab(tab.get());
279
280 mTabs.clear();
281 mSkillModels.clear();
282 mSkills.clear();
283}
284
286{
287 clearSkills();
288
290 XML::Node root = doc.rootNode();
291
292 int setCount = 0;
293 std::string setName;
294
295 if (!root || root.name() != "skills")
296 {
297 Log::info("Error loading skills file: %s", SKILLS_FILE);
298
300 {
301 auto model = std::make_unique<SkillModel>();
302 auto skill = std::make_unique<SkillInfo>();
303 skill->id = 1;
304 skill->name = "basic";
305 skill->setIcon(std::string());
306 skill->modifiable = true;
307 skill->visible = true;
308 skill->model = model.get();
309 skill->update();
310
311 mSkills[1] = skill.get();
312
313 model->addSkill(std::move(skill));
314 model->updateVisibilities();
315
316 auto listbox = new SkillListBox(model.get());
317 auto scroll = std::make_unique<ScrollArea>(listbox);
318 scroll->setOpaque(false);
319 scroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER);
320 scroll->setVerticalScrollPolicy(ScrollArea::SHOW_ALWAYS);
321
322 auto tab = std::make_unique<SkillTab>("Skills", listbox);
323 mTabbedArea->addTab(tab.get(), scroll.get());
324
325 mTabs.push_back(std::move(tab));
326 mTabWidgets.push_back(std::move(scroll));
327 mSkillModels.push_back(std::move(model));
328
329 update();
330 }
331 return;
332 }
333
334 for (auto set : root.children())
335 {
336 if (set.name() == "set" ||
337 set.name() == "skill-set")
338 {
339 setCount++;
340 setName = set.getProperty("name", strprintf(_("Skill Set %d"), setCount));
341
342 auto model = std::make_unique<SkillModel>();
343
344 for (auto node : set.children())
345 {
346 if (node.name() == "skill")
347 {
348 int id = atoi(node.getProperty("id", "-1").c_str());
349 std::string name = node.getProperty("name", strprintf(_("Skill %d"), id));
350 std::string icon = node.getProperty("icon", "");
351
352 auto skill = std::make_unique<SkillInfo>();
353 skill->id = id;
354 skill->name = name;
355 skill->setIcon(icon);
356 skill->modifiable = false;
357 skill->visible = false;
358 skill->model = model.get();
359 skill->update();
360
361 mSkills[id] = skill.get();
362
363 model->addSkill(std::move(skill));
364 }
365 }
366
367 model->updateVisibilities();
368
369 auto listbox = new SkillListBox(model.get());
370 auto scroll = std::make_unique<ScrollArea>(listbox);
371 scroll->setOpaque(false);
372 scroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER);
373 scroll->setVerticalScrollPolicy(ScrollArea::SHOW_ALWAYS);
374
375 auto tab = std::make_unique<SkillTab>(setName, listbox);
376
377 mTabbedArea->addTab(tab.get(), scroll.get());
378
379 mTabs.push_back(std::move(tab));
380 mTabWidgets.push_back(std::move(scroll));
381 mSkillModels.push_back(std::move(model));
382 }
383 }
384
385 update();
386}
387
388void SkillDialog::setModifiable(int id, bool modifiable)
389{
390 auto it = mSkills.find(id);
391
392 if (it != mSkills.end())
393 {
394 SkillInfo &info = *it->second;
395 info.modifiable = modifiable;
396 info.update();
397 }
398}
399
401{
402 mVisibleSkills.clear();
403
404 for (auto &skill : mSkills)
405 if (skill->visible)
406 mVisibleSkills.push_back(skill.get());
407}
408
410{
411 int baseLevel = PlayerInfo::getStatBase(id);
412 int effLevel = PlayerInfo::getStatEffective(id);
413
414 std::pair<int, int> exp = PlayerInfo::getStatExperience(id);
415
416 if (!modifiable && baseLevel == 0 && effLevel == 0 && exp.second == 0)
417 {
418 if (visible)
419 {
420 visible = false;
422 }
423
424 return;
425 }
426
427 bool updateVisibility = !visible;
428 visible = true;
429
430 if (effLevel != baseLevel)
431 {
432 skillLevel = strprintf(_("Lvl: %d (%+d)"), baseLevel,
433 effLevel - baseLevel);
434 }
435 else
436 {
437 if (baseLevel == 0)
438 {
439 skillLevel.clear();
440 }
441 else
442 {
443 skillLevel = strprintf(_("Lvl: %d"), baseLevel);
444 }
445 }
446 skillLevelWidth = -1;
447
448 if (exp.second)
449 {
450 progress = exp.second != 0 ? (float) exp.first / exp.second : 0;
451 skillExp = strprintf("%.2f%%", progress * 100);
452 }
453 else
454 {
455 skillExp.clear();
456 progress = 0.0f;
457 }
458
460
461 if (updateVisibility)
462 {
464 }
465}
466
467void SkillInfo::draw(Graphics *graphics, int y, int width)
468{
469 graphics->drawImage(icon, 1, y);
470 graphics->drawText(name, 34, y);
471
472 if (skillLevelWidth < 0)
473 {
474 // Add one for padding
475 skillLevelWidth = graphics->getFont()->getWidth(skillLevel) + 1;
476 }
477
479
480 if (!skillExp.empty())
481 {
482 const gcn::Rectangle rect(33, y + 15, width - 33, 17);
484 }
485}
Button widget.
Definition button.h:38
std::string getStringValue(const std::string &key) const
void listen(Event::Channel channel)
Definition event.h:42
@ UpdateAttribute
Definition event.h:99
@ UpdateStat
Definition event.h:100
Channel
Definition event.h:45
@ AttributesChannel
Definition event.h:47
A central point of control for graphics.
Definition graphics.h:78
gcn::Font * getFont() const
Definition graphics.h:248
void drawText(const std::string &text, int x, int y, gcn::Graphics::Alignment alignment, const gcn::Color &color, gcn::Font *font, bool outline=false, bool shadow=false, const std::optional< gcn::Color > &outlineColor={}, const std::optional< gcn::Color > &shadowColor={})
Definition graphics.cpp:176
void setColor(const gcn::Color &color) override
Definition graphics.h:255
bool drawImage(const Image *image, int x, int y)
Blits an image onto the screen.
Definition graphics.cpp:36
Theme * getTheme() const
The global GUI theme.
Definition gui.h:138
Label widget.
Definition label.h:34
A list box, meant to be used inside a scroll area.
Definition listbox.h:36
virtual void increaseSkill(int skillId)=0
A class for loading and managing resources.
static ResourceManager * getInstance()
Returns an instance of the class, creating one if it does not already exist.
ResourceRef< Image > getImage(const std::string &idPath)
Loads the Image resource found at the given identifier path.
Automatically counting Resource reference.
Definition resource.h:74
void registerWindowForReset(Window *window)
Enables the Reset Windows button.
Definition setup.cpp:108
std::vector< std::unique_ptr< SkillModel > > mSkillModels
Definition skilldialog.h:79
std::map< int, SkillInfo * > mSkills
Definition skilldialog.h:82
~SkillDialog() override
std::vector< std::unique_ptr< gcn::Widget > > mTabWidgets
Definition skilldialog.h:81
void event(Event::Channel channel, const Event &event) override
void clearSkills()
void setModifiable(int id, bool modifiable)
Label * mPointsLabel
Definition skilldialog.h:84
void action(const gcn::ActionEvent &event) override
Called when receiving actions from widget.
TabbedArea * mTabbedArea
Definition skilldialog.h:83
void update()
Update other parts of the display.
std::vector< std::unique_ptr< Tab > > mTabs
Definition skilldialog.h:80
void loadSkills()
Button * mIncreaseButton
Definition skilldialog.h:85
void draw(gcn::Graphics *gcnGraphics) override
unsigned int getRowHeight() const override
SkillListBox(SkillModel *model)
SkillInfo * getSelectedInfo()
std::vector< std::unique_ptr< SkillInfo > > mSkills
void updateVisibilities()
SkillInfo * getSkillAt(int i) const
int getNumberOfElements() override
void addSkill(std::unique_ptr< SkillInfo > info)
std::string getElementAt(int i) override
std::vector< SkillInfo * > mVisibleSkills
SkillTab(const std::string &name, SkillListBox *listBox)
SkillListBox * mListBox
SkillInfo * getSelectedInfo()
~SkillTab() override
A tab, the same as the Guichan tab in 0.8, but extended to allow transparency.
Definition tab.h:33
void setCaption(const std::string &caption)
Sets the caption of the tab.
Definition tab.cpp:49
A tabbed area, the same as the guichan tabbed area in 0.8, but extended.
Definition tabbedarea.h:40
void addTab(gcn::Tab *tab, gcn::Widget *widget) override
Add a tab.
void removeTab(gcn::Tab *tab) override
Override the remove tab function as it's broken in guichan 0.8.
int getGuiAlpha() const
Get the current GUI alpha value.
Definition theme.h:321
static const gcn::Color & getThemeColor(int type)
Gets the color associated with the type in the default palette (0).
Definition theme.cpp:313
static ResourceRef< Image > getImageFromTheme(const std::string &path)
Definition theme.cpp:308
void drawProgressBar(Graphics *graphics, const gcn::Rectangle &area, const gcn::Color &color, float progress, const std::string &text=std::string(), ProgressPalette progressType=ProgressPalette::THEME_PROG_END) const
Definition theme.cpp:378
@ PROG_EXP
Definition theme.h:273
static gcn::Color getProgressColor(int type, float progress)
Definition theme.cpp:318
@ HIGHLIGHT_TEXT
Definition theme.h:232
@ HIGHLIGHT
Definition theme.h:231
@ TEXT
Definition theme.h:214
A window.
Definition window.h:59
void center()
Positions the window in the center of it's parent.
Definition window.cpp:768
virtual void setVisible(bool visible)
Overloads window setVisible by Guichan to allow sticky window handling.
Definition window.cpp:282
void setMinHeight(int height)
Sets the minimum height of the window.
Definition window.cpp:197
void setWindowName(const std::string &name)
Sets the name of the window.
Definition window.h:272
void setResizable(bool resize)
Sets whether or not the window can be resized.
Definition window.cpp:212
void setSaveVisible(bool save)
Sets whether the window will save it's visibility.
Definition window.h:225
LayoutCell & place(int x, int y, gcn::Widget *, int w=1, int h=1)
Adds a widget to the window and sets it at given cell.
Definition window.cpp:737
void setCloseButton(bool flag)
Sets whether or not the window has a close button.
Definition window.cpp:262
void setDefaultSize()
Set the default win pos and size to the current ones.
Definition window.cpp:545
void loadWindowState()
Reads the position (and the size for resizable windows) in the configuration based on the given strin...
Definition window.cpp:467
void setMinWidth(int width)
Sets the minimum width of the window.
Definition window.cpp:192
A helper class for parsing an XML document, which also cleans it up again (RAII).
Definition xml.h:190
Node rootNode() const
Returns the root node of the document (or NULL if there was a load error).
Definition xml.h:213
std::string_view name() const
Definition xml.h:46
Children children() const
Definition xml.h:97
Configuration paths
XML default paths information reader.
Definition client.cpp:99
Graphics * graphics
Definition client.cpp:104
#define _(s)
Definition gettext.h:38
Gui * gui
The GUI system.
Definition gui.cpp:50
void info(const char *log_text,...) LOG_PRINTF_ATTR
ServerType getNetworkType()
Definition net.cpp:200
PlayerHandler * getPlayerHandler()
Definition net.cpp:110
int getStatBase(int id)
Returns the base value of the given stat.
int getStatEffective(int id)
Returns the current effective value of the given stat.
int getAttribute(int id)
Returns the value of the given attribute.
std::pair< int, int > getStatExperience(int id)
Returns the experience of the given stat.
@ SKILL_POINTS
Definition playerinfo.h:36
Setup * setupWindow
Definition setup.cpp:120
#define SKILLS_FILE
std::string strprintf(char const *format,...)
A safe version of sprintf that returns a std::string of the result.
ResourceRef< Image > icon
bool modifiable
int skillLevelWidth
void update()
void setIcon(const std::string &iconPath)
~SkillInfo()=default
std::string skillLevel
unsigned short id
void draw(Graphics *graphics, int y, int width)
gcn::Color color
float progress
SkillModel * model
std::string skillExp
std::string name
WindowContainer * windowContainer