xoreos  0.0.5
charspells.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos 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 xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 #include "src/common/util.h"
26 #include "src/common/strutil.h"
27 
28 #include "src/aurora/talkman.h"
29 #include "src/aurora/2dareg.h"
30 #include "src/aurora/2dafile.h"
31 
32 #include "src/graphics/graphics.h"
33 
36 
38 
44 
45 namespace Engines {
46 
47 namespace NWN {
48 
50  WidgetListItemButton(gui, "ctl_cg_btn_feat", spell.name, spell.icon, kMoveButton | kHelpButton) {
51  // Set maximum width for text.
52  _text->set(spell.name, 230.f);
53 
54  if (!isRight)
56 
57  setTag("Item#" + spell.name);
58 
59  _spell = spell;
60 }
61 
63 }
64 
66  if ((widget.getTag().endsWith("#MoveButtonLeft") || widget.getTag().endsWith("#MoveButtonRight")))
67  dynamic_cast<CharSpells &>(*_gui).moveSpell(this);
68 }
69 
71  dynamic_cast<CharSpells &>(*_gui).showSpellHelp(_spell);
72 }
73 
74 CharSpells::CharSpells(CharGenChoices &choices, Console *console) : CharGenBase(console) {
75  _choices = &choices;
76  load("cg_spells");
77 
79  _abilityLimit = 0;
80 
81  _spellHelp.reset(new CharHelp("cg_spellinfo", console));
82 
83  _availListBox = getListBox("AvailBox", true);
85  _knownListBox = getListBox("KnownBox", true);
87 
88  for (uint32 lvl = 0; lvl < 10; ++lvl) {
89  WidgetButton *spellLvlButton = getButton("SpellLevel" + Common::composeString<uint32>(lvl), true);
90  spellLvlButton->setTooltip(TalkMan.getString(68674 + lvl));
91  spellLvlButton->setTooltipPosition(20.f, -40.f, -100.f);
92 
93  // TODO: The button should be render properly when pressed.
94  spellLvlButton->setMode(WidgetButton::kModeUnchanged);
95  spellLvlButton->setPressed(false);
96  }
97 
99 }
100 
102  _availListBox->lock();
103  _availListBox->clear();
105  _knownListBox->lock();
106  _knownListBox->clear();
108 }
109 
111  makeSpellsList();
112 }
113 
115  if (tag.beginsWith("SpellLevel"))
116  type = kWidgetTypeButton;
117 }
118 
119 void CharSpells::showSpellLevel(size_t spellLevel, bool forceUpdate) {
120  if (_currentSpellLevel == spellLevel && !forceUpdate)
121  return;
122 
123  // Show/hide label about empty spells list.
124  Widget *label = 0;
125  label = _availListBox->getChild("EmptySpellListLabel");
126  if (_availSpells[spellLevel].empty() && label) {
127  label->setInvisible(false);
128  if (_availListBox->isVisible())
129  label->show();
130  } else if (label) {
131  label->setInvisible(true);
132  if (_availListBox->isVisible())
133  label->hide();
134  }
135 
136  // Show/Hide label about ability requirement.
137  label = 0;
138  label = _knownListBox->getChild("CannotLearn");
139  if (spellLevel <= _abilityLimit && label) {
140  label->setInvisible(true);
141  if (_knownListBox->isVisible())
142  label->hide();
143  } else if (label) {
144  label->setInvisible(false);
145  if (_knownListBox->isVisible())
146  label->show();
147  }
148 
149  // Fill available spells.
150  _availListBox->lock();
151  _availListBox->clear();
152  for (std::vector<Spell>::iterator sp = _availSpells[spellLevel].begin(); sp != _availSpells[spellLevel].end(); ++sp) {
153  _availListBox->add(new WidgetListItemSpell(*this, *sp), true);
154  }
156 
157  // Fill known spells.
158  _knownListBox->lock();
159  _knownListBox->clear();
160  for (std::vector<Spell>::iterator sp = _knownSpells[spellLevel].begin(); sp != _knownSpells[spellLevel].end(); ++sp) {
161  _knownListBox->add(new WidgetListItemSpell(*this, *sp, false), true);
162  }
164 
165  _currentSpellLevel = spellLevel;
167 }
168 
170  WidgetListBox *fromListBox = dynamic_cast<WidgetListBox *>(spellItem->_owner);
171  if (!fromListBox)
172  return;
173 
174  WidgetListBox *toListBox = 0;
175 
176  std::vector<Spell> *fromSpellList;
177  std::vector<Spell> *toSpellList;
178 
179  if (fromListBox == _knownListBox) {
180  toListBox = _availListBox;
181  toSpellList = &_availSpells[_currentSpellLevel];
182  fromSpellList = &_knownSpells[_currentSpellLevel];
183 
185 
186  } else {
188  return;
189 
190  toListBox = _knownListBox;
191  toSpellList = &_knownSpells[_currentSpellLevel];
192  fromSpellList = &_availSpells[_currentSpellLevel];
193 
195  }
196 
197  fromListBox->lock();
198  fromListBox->remove(spellItem);
199  fromListBox->sortByTag();
200  fromListBox->unlock();
201 
202  spellItem->changeArrowDirection();
203 
204  toListBox->lock();
205  toListBox->add(spellItem, true);
206  toListBox->sortByTag();
207  toListBox->unlock();
208 
209  for (std::vector<Spell>::iterator it = fromSpellList->begin(); it != fromSpellList->end(); ++it) {
210  if ((*it).spellID == spellItem->_spell.spellID) {
211  toSpellList->push_back(*it);
212  fromSpellList->erase(it);
213  break;
214  }
215  }
216 
218 }
219 
221  if (widget.getTag() == "OkButton") {
222  for (size_t lvl = 0; lvl < _knownSpells.size(); ++lvl) {
223  for (std::vector<Spell>::iterator sp = _knownSpells[lvl].begin(); sp != _knownSpells[lvl].end(); ++sp) {
224  _choices->setSpell(lvl, (*sp).spellID);
225  }
226  }
227  _returnCode = 2;
228  return;
229  }
230 
231  if (widget.getTag() == "CancelButton") {
232  _returnCode = 1;
233  return;
234  }
235 
236  if (widget.getTag() == "RecommendButton") {
237  GfxMan.lockFrame();
239  GfxMan.unlockFrame();
240  return;
241  }
242 
243  if (widget.getTag() == "ResetButton") {
244  GfxMan.lockFrame();
245  reset();
247  GfxMan.unlockFrame();
248  return;
249  }
250 
251  for (uint32 lvl = 0; lvl <= _maxLevel; ++lvl) {
252  if (widget.getTag() == "SpellLevel" + Common::composeString<uint32>(lvl)) {
253  showSpellLevel(lvl);
254  return;
255  }
256  }
257 }
258 
260  // Compute the maximum spell level.
261  const Aurora::TwoDAFile &twodaClasses = TwoDAReg.get2DA("classes");
262  const Aurora::TwoDARow &classRow = twodaClasses.getRow(_choices->getClass());
263  const Common::UString gainTable = classRow.getString("SpellGainTable");
264  const Aurora::TwoDAFile &twodaSpellGain = TwoDAReg.get2DA(gainTable);
265  const Aurora::TwoDARow &spellLevelRow = twodaSpellGain.getRow(_choices->getCharacter().getHitDice());
266 
267  for (size_t lvl = 2; lvl < twodaSpellGain.getColumnCount(); ++lvl) {
268  if (spellLevelRow.empty(lvl)) {
269  _maxLevel = lvl - 3UL;
270  break;
271  }
272  }
273 
274  _knownSpells.clear();
275  _knownSpells.resize(_maxLevel + 1);
276  _availSpells.clear();
277  _availSpells.resize(_maxLevel + 1);
278  _remainingSpells.clear();
279  _remainingSpells.resize(_maxLevel + 1);
280 
281  computeRemainingSpells(classRow);
282 
283  // We need to know the spellCaster type. We can get it from the spellGain table.
284  Common::UString casterName;
285  if (gainTable == "CLS_SPGN_BARD") {
286  casterName = "Bard";
287  } else if (gainTable == "CLS_SPGN_CLER") {
288  casterName = "Cleric";
289  } else if (gainTable == "CLS_SPGN_DRUI") {
290  casterName = "Druid";
291  } else if (gainTable == "CLS_SPGN_PAL") {
292  casterName = "Paladin";
293  } else if (gainTable == "CLS_SPGN_RANG") {
294  casterName = "Ranger";
295  } else if (gainTable == "CLS_SPGN_WIZ" || gainTable == "CLS_SPGN_SORC") {
296  casterName = "Wiz_Sorc";
297  } else {
298  error("Unknown caster name when building spells list: %s", gainTable.c_str());
299  }
300 
301  Common::UString oppositeSchool = "";
302  if (_choices->getSpellSchool() < UINT8_MAX) {
303  const Aurora::TwoDAFile &twodaSpellsSchool = TwoDAReg.get2DA("spellschools");
304  const Aurora::TwoDARow &rowSchool = twodaSpellsSchool.getRow(_choices->getSpellSchool());
305  const Aurora::TwoDARow &rowOppSchool = twodaSpellsSchool.getRow(rowSchool.getInt("Opposition"));
306  oppositeSchool = rowOppSchool.getString("Letter");
307  }
308 
309  // Add spells to available and known list.
310  const Aurora::TwoDAFile &twodaSpells = TwoDAReg.get2DA("spells");
311  for (size_t sp = 0; sp < twodaSpells.getRowCount(); ++sp) {
312  // TODO: Check if character already own the spell.
313  const Aurora::TwoDARow &spellRow = twodaSpells.getRow(sp);
314 
315  if (spellRow.empty("Name"))
316  continue;
317 
318  if (spellRow.empty(casterName))
319  continue;
320 
321  // Check spell level.
322  uint32 spellLevel = static_cast<uint32>(spellRow.getInt(casterName));
323  if (spellLevel > _maxLevel)
324  continue;
325 
326  // Check spell school.
327  if (spellRow.getString("School") == oppositeSchool)
328  continue;
329 
330  // The wizard knows all the level 0 spells.
331  if (gainTable == "CLS_SPGN_WIZ" && spellLevel == 0) {
332  Spell spell;
333  spell.spellID = sp;
334  spell.name = TalkMan.getString(spellRow.getInt("Name"));
335  spell.icon = spellRow.getString("IconResRef");
336  spell.desc = TalkMan.getString(spellRow.getInt("SpellDesc"));
337  _knownSpells[spellLevel].push_back(spell);
338  continue;
339  }
340 
341  Spell spell;
342  spell.spellID = sp;
343  spell.name = TalkMan.getString(spellRow.getInt("Name"));
344  spell.icon = spellRow.getString("IconResRef");
345  spell.desc = TalkMan.getString(spellRow.getInt("SpellDesc"));
346  _availSpells[spellLevel].push_back(spell);
347  }
348 
349  // Add icon to spells levels.
350  for (uint32 lvl = 0; lvl <= _maxLevel; ++lvl) {
351  WidgetButton *spellLvlButton = getButton("SpellLevel" + Common::composeString<uint32>(lvl), true);
352 
353  Common::UString lvlStr = Common::composeString<uint32>(lvl);
354  Common::UString iconName;
355  if (lvl == 0) {
356  iconName = "ir_cantrips";
357  } else if (lvl > 0 && lvl < 7) {
358  iconName = "ir_level" + lvlStr;
359  } else {
360  iconName = "ir_level789";
361  }
362 
363  const std::vector<Common::UString> texture(1, iconName);
364  spellLvlButton->getNode("Plane64")->setTextures(texture);
365  }
366 
367  // Compute spell level limit due to ability.
368  Common::UString abilityStr = classRow.getString("PrimaryAbil");
369  const Aurora::TwoDAFile &twodaAbilities = TwoDAReg.get2DA("iprp_abilities");
370  for (size_t ab = 0; ab < twodaAbilities.getRowCount(); ++ab) {
371  const Aurora::TwoDARow &abilityRow = twodaAbilities.getRow(ab);
372  if (abilityStr.toLower() == abilityRow.getString("Label").toLower()) {
373  _abilityLimit = _choices->getTotalAbility(static_cast<Ability>(ab)) - 10U;
374  break;
375  }
376  }
377 
378  // Show the first spells level that has a level to choose.
379  size_t lvlToShow = 0;
380  for (size_t lvl = _maxLevel; lvl == 0UL; --lvl)
381  if (_remainingSpells[lvl] > 0)
382  lvlToShow = lvl;
383 
384  showSpellLevel(lvlToShow);
386 }
387 
390 
391  // Check if it is a first level wizard.
392  if (classRow.empty("SpellKnownTable") && classLevel == 0) {
394  return;
395  }
396 
397  const Aurora::TwoDAFile &twodaSpellKnown = TwoDAReg.get2DA(classRow.getString("SpellKnownTable"));
398  const Aurora::TwoDARow &knownRow =twodaSpellKnown.getRow(classLevel);
399 
400  // TODO Compute difference between new spells and known ones.
401 
402  for (size_t c = 1; c < twodaSpellKnown.getColumnCount(); ++c) {
403  if (knownRow.empty(c))
404  break;
405 
406  _remainingSpells[c - 1] = static_cast<uint8>(knownRow.getInt(c));
407  }
408 
409 }
410 
412  getLabel("RemainLabel")->setText(Common::composeString<uint8>(_remainingSpells[_currentSpellLevel]));
413 }
414 
416  std::vector<std::vector<uint16> > prefSpells;
417  _choices->getPrefSpells(prefSpells);
418 
419  for (size_t lvl = 0; lvl < _remainingSpells.size(); ++lvl) {
420  bool allSpellsMoved = false;
421  while (_remainingSpells[lvl] != 0 && !allSpellsMoved) {
422  allSpellsMoved = true;
423  for (std::vector<Spell>::iterator s = _availSpells[lvl].begin(); s != _availSpells[lvl].end(); ++s) {
424  if (_remainingSpells[lvl] == 0)
425  break;
426 
427  if ((*s).spellID != prefSpells[lvl].front())
428  continue;
429 
430  allSpellsMoved = false;
431  _knownSpells[lvl].push_back(*s);
432  _availSpells[lvl].erase(s);
433 
434  prefSpells[lvl].erase(prefSpells[lvl].begin());
435  --_remainingSpells[lvl];
436 
437  break;
438  }
439 
440  if (allSpellsMoved && prefSpells[lvl].size() > 0) {
441  prefSpells[lvl].erase(prefSpells[lvl].begin());
442  allSpellsMoved = false;
443  continue;
444  }
445 
446  if (prefSpells[lvl].size() == 0)
447  break;
448  }
449  }
450 
452 }
453 
455  _spellHelp->setContent(spell.name, spell.desc, spell.icon);
456 
457  _spellHelp->show();
458  _spellHelp->run();
459  _spellHelp->hide();
460 }
461 
462 } // End of namespace NWN
463 
464 } // End of namespace Engines
Class to hold the two-dimensional array of a 2DA file.
Definition: 2dafile.h:124
uint8 getHitDice() const
Returns the number of hit dice, which is effectively the total number of levels.
Definition: creature.cpp:1031
Common::ScopedPtr< CharHelp > _spellHelp
Definition: charspells.h:83
The global graphics manager.
virtual void setInvisible(bool invisible)
Make the widget invisible.
Definition: widget.cpp:168
#define TalkMan
Shortcut for accessing the talk manager.
Definition: talkman.h:111
A NWN button widget.
const Common::UString & getString(size_t column) const
Return the contents of a cell as a string.
Definition: 2dafile.cpp:59
uint32 _returnCode
The GUI&#39;s return code.
Definition: gui.h:75
Widget * _owner
The widget&#39;s owner, if any.
Definition: widget.h:113
A class holding an UTF-8 string.
Definition: ustring.h:48
WidgetListBox * _availListBox
Definition: charspells.h:85
WidgetListBox * getListBox(const Common::UString &tag, bool vital=false)
Definition: gui.cpp:318
std::vector< std::vector< Spell > > _availSpells
Definition: charspells.h:88
void remove(WidgetListItem *item)
Definition: listbox.cpp:438
void add(WidgetListItem *item, bool noTag=false)
Definition: listbox.cpp:411
Common::UString icon
Definition: charspells.h:45
CharSpells(CharGenChoices &choices, Engines::Console *console)
Definition: charspells.cpp:74
uint8_t uint8
Definition: types.h:200
bool beginsWith(const UString &with) const
Definition: ustring.cpp:295
const Creature & getCharacter()
Common::UString name
Definition: charspells.h:44
A text object.
void setTag(const Common::UString &tag)
Set the widget&#39;s tag.
bool endsWith(const UString &with) const
Definition: ustring.cpp:315
bool isVisible() const
Is the widget visible?
Definition: widget.cpp:59
void moveSpell(WidgetListItemSpell *spellItem)
Definition: charspells.cpp:169
size_t getRowCount() const
Return the number of rows in the array.
Definition: 2dafile.cpp:400
void showSpellHelp(Spell &spell)
Definition: charspells.cpp:454
uint8 getTotalAbility(Ability ability) const
Utility templates and functions for working with strings and streams.
A NWN listbox widget.
void callbackActive(Widget &widget)
Callback that&#39;s triggered when a widget was activated.
Definition: charspells.cpp:220
std::vector< std::vector< Spell > > _knownSpells
Definition: charspells.h:89
WidgetListItemSpell(::Engines::GUI &gui, Spell spell, bool isRight=true)
Definition: charspells.cpp:49
A NWN button widget.
Definition: button.h:39
WidgetButton * getButton(const Common::UString &tag, bool vital=false)
Definition: gui.cpp:306
A GUI.
Definition: gui.h:43
const char * c_str() const
Return the (utf8 encoded) string data.
Definition: ustring.cpp:249
size_t getColumnCount() const
Return the number of columns in the array.
Definition: 2dafile.cpp:404
Common::UString desc
Definition: charspells.h:46
WidgetListBox * _knownListBox
Definition: charspells.h:86
WidgetLabel * getLabel(const Common::UString &tag, bool vital=false)
Definition: gui.cpp:270
uint16_t uint16
Definition: types.h:202
void setMode(Mode mode)
Definition: button.cpp:82
Utility templates and functions.
void setTooltip(const Common::UString &text)
Definition: nwnwidget.cpp:67
const Common::UString & getTag() const
Get the widget&#39;s tag.
Definition: widget.cpp:45
void setMode(Mode mode)
Definition: listbox.cpp:313
CharGenChoices * _choices
Definition: chargenbase.h:45
Handling BioWare&#39;s 2DAs (two-dimensional array).
int8 getAbilityModifier(Ability ability)
#define UINT8_MAX
Definition: types.h:225
A node within a 3D model.
void computeRemainingSpells(const Aurora::TwoDARow &classRow)
Definition: charspells.cpp:388
uint16 getClassLevel(uint32 classID) const
Get the creature&#39;s level for this class.
Definition: creature.cpp:949
void load(const Common::UString &resref)
Definition: gui.cpp:77
#define TwoDAReg
Shortcut for accessing the 2da registry.
Definition: 2dareg.h:101
A NWN listbox widget.
Definition: listbox.h:116
The global 2DA registry.
int32 getInt(size_t column) const
Return the contents of a cell as an int.
Definition: 2dafile.cpp:75
void subActive(Widget &widget)
A sub-widget was activated.
Definition: charspells.cpp:65
void setSpell(size_t spellLevel, uint16 spell)
void getPrefSpells(std::vector< std::vector< uint16 > > &spells)
virtual void hide()
Hide the widget.
Definition: widget.cpp:90
bool empty(size_t column) const
Check if the cell is empty.
Definition: 2dafile.cpp:107
UString toLower() const
Return a lowercased copy of the string.
Definition: ustring.cpp:481
const TwoDARow & getRow(size_t row) const
Get a row.
Definition: 2dafile.cpp:421
Common::ScopedPtr< Graphics::Aurora::Text > _text
Help popup GUI.
A widget in a GUI.
Definition: widget.h:40
Graphics::Aurora::ModelNode * getNode(const Common::UString &nodeName) const
Definition: modelwidget.cpp:92
The spells selection GUI in CharGen.
virtual void show()
Show the widget.
Definition: widget.cpp:71
Widget * getChild(const Common::UString &childTag)
Get the widget&#39;s child by tag.
Definition: widget.cpp:110
uint32_t uint32
Definition: types.h:204
The global talk manager for Aurora strings.
A creature in a Neverwinter Nights area.
std::vector< uint8 > _remainingSpells
Definition: charspells.h:91
#define SIZE_MAX
Definition: types.h:172
void setTooltipPosition(float x, float y, float z)
Definition: nwnwidget.cpp:74
void setPressed(bool pushed)
Definition: button.cpp:93
void fixWidgetType(const Common::UString &tag, NWN::GUI::WidgetType &type)
Definition: charspells.cpp:114
void showSpellLevel(size_t spellLevel, bool forceUpdate=false)
Definition: charspells.cpp:119
A row within a 2DA file.
Definition: 2dafile.h:61
void NORETURN_PRE error(const char *s,...)
Definition: util.cpp:86
bool empty()
Check if the gui is currently empty.
Definition: gui.cpp:306
#define GfxMan
Shortcut for accessing the graphics manager.
Definition: graphics.h:299
A NWN label widget.
void setText(const Common::UString &text)
Definition: label.cpp:67
void setTextures(const std::vector< Common::UString > &textures)
Set textures to the node.
Definition: modelnode.cpp:297