ManaPlus
avatarlistbox.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2010 The Mana Developers
4  * Copyright (C) 2011-2017 The ManaPlus Developers
5  *
6  * This file is part of The ManaPlus 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 
23 
24 #include "actormanager.h"
25 #include "configuration.h"
26 
27 #include "being/localplayer.h"
28 
30 
31 #include "gui/gui.h"
32 #include "gui/popupmanager.h"
33 #include "gui/skin.h"
34 #include "gui/viewport.h"
35 
36 #include "gui/fonts/font.h"
37 
38 #include "gui/popups/popupmenu.h"
39 
41 
42 #include "gui/windows/chatwindow.h"
43 
45 
46 #include "resources/image/image.h"
47 
48 #include "resources/map/map.h"
49 
50 #include "debug.h"
51 
55 
57  AvatarListModel *const model) :
58  ListBox(widget, model, "avatarlistbox.xml"),
59  mImagePadding(mSkin != nullptr ? mSkin->getOption("imagePadding") : 0),
60  mShowGender(config.getBoolValue("showgender")),
61  mShowLevel(config.getBoolValue("showlevel"))
62 {
63  instances++;
64 
65  if (instances == 1)
66  {
67  onlineIcon = Theme::getImageFromThemeXml("circle-on.xml", "");
68  offlineIcon = Theme::getImageFromThemeXml("circle-off.xml", "");
69  }
70 
71  setWidth(200);
72 
73  config.addListener("showgender", this);
74  config.addListener("showlevel", this);
75 
78 }
79 
81 {
82  config.removeListeners(this);
84 
85  instances--;
86 
87  if (instances == 0)
88  {
89  if (onlineIcon != nullptr)
90  {
91  onlineIcon->decRef();
92  onlineIcon = nullptr;
93  }
94  if (offlineIcon != nullptr)
95  {
97  offlineIcon = nullptr;
98  }
99  }
100 }
101 
102 void AvatarListBox::draw(Graphics *const graphics)
103 {
104  BLOCK_START("AvatarListBox::draw")
105  if ((mListModel == nullptr) || (localPlayer == nullptr))
106  {
107  BLOCK_END("AvatarListBox::draw")
108  return;
109  }
110 
111  const Widget *const parent = mParent;
112  if (parent == nullptr)
113  return;
114 
115  AvatarListModel *const model = static_cast<AvatarListModel *>(
116  mListModel);
117  updateAlpha();
118 
119  Font *const font = getFont();
120  const int fontHeight = font->getHeight();
121  const int parentWidth = parent->getWidth();
122  const std::string &name = localPlayer->getName();
123 
124  // Draw the list elements
125  ImageCollection vertexes;
126  const int num = model->getNumberOfElements();
127  for (int i = 0, y = 0;
128  i < num;
129  ++i, y += fontHeight)
130  {
131  const Avatar *const a = model->getAvatarAt(i);
132  if (a == nullptr)
133  continue;
134 
135  const MapItemType::Type type = static_cast<MapItemType::Type>(
136  a->getType());
137  if (type != MapItemType::SEPARATOR)
138  {
139  // Draw online status
140  const Image *const icon = a->getOnline()
142  if (icon != nullptr)
143  {
144  graphics->calcTileCollection(&vertexes, icon,
145  mImagePadding, y + mPadding);
146  }
147  }
148  }
149 
150  graphics->finalize(&vertexes);
151  graphics->drawTileCollection(&vertexes);
152 
153  for (int i = 0, y = 0;
154  i < num;
155  ++i, y += fontHeight)
156  {
157  const Avatar *const a = model->getAvatarAt(i);
158  if (a == nullptr)
159  continue;
160 
161  const MapItemType::Type type = static_cast<MapItemType::Type>(
162  a->getType());
163  std::string text;
164 
165  if (a->getMaxHp() > 0)
166  {
167  if (mShowLevel && a->getLevel() > 1)
168  {
169  text = strprintf("%s %d/%d (%d)", a->getComplexName().c_str(),
170  a->getHp(), a->getMaxHp(), a->getLevel());
171  }
172  else
173  {
174  text = strprintf("%s %d/%d", a->getComplexName().c_str(),
175  a->getHp(), a->getMaxHp());
176  }
177  const bool isPoison = a->getPoison();
178  if (a->getMaxHp() != 0)
179  {
180  const ProgressColorIdT themeColor = (isPoison
184  themeColor, static_cast<float>(a->getHp())
185  / static_cast<float>(a->getMaxHp()));
186  color.a = 80;
187  graphics->setColor(color);
188  graphics->fillRectangle(Rect(mPadding, y + mPadding,
189  parentWidth * a->getHp() / a->getMaxHp()
190  - 2 * mPadding, fontHeight));
191  }
192  }
193  else if (a->getDamageHp() != 0 && a->getName() != name)
194  {
195  if (mShowLevel && a->getLevel() > 1)
196  {
197  text = strprintf("%s -%d (%d)", a->getComplexName().c_str(),
198  a->getDamageHp(), a->getLevel());
199  }
200  else
201  {
202  text = strprintf("%s -%d", a->getComplexName().c_str(),
203  a->getDamageHp());
204  }
205 
206  const ProgressColorIdT themeColor = (a->getPoison()
208  Color color = Theme::getProgressColor(themeColor, 1);
209  color.a = 80;
210  graphics->setColor(color);
211  graphics->fillRectangle(Rect(mPadding, y + mPadding,
212  parentWidth * a->getDamageHp() / 1024
213  - 2 * mPadding, fontHeight));
214 
215  if (a->getLevel() > 1)
216  {
217  graphics->setColor(mForegroundColor);
218  int minHp = 40 + ((a->getLevel() - 1) * 5);
219  if (minHp < 0)
220  minHp = 40;
221 
222  graphics->drawLine(parentWidth * minHp / 1024
223  + mPadding, y + mPadding,
224  parentWidth * minHp / 1024, y + fontHeight);
225  }
226  }
227  else
228  {
229  if (mShowLevel && a->getLevel() > 1)
230  {
231  text = strprintf("%s (%d)", a->getComplexName().c_str(),
232  a->getLevel());
233  }
234  else
235  {
236  text = a->getComplexName();
237  }
238  }
239 
240  if (!a->getMap().empty())
241  {
242  if (a->getX() != -1)
243  {
244  text.append(strprintf(" [%d,%d %s]", a->getX(), a->getY(),
245  a->getMap().c_str()));
246  }
247  else
248  {
249  text.append(strprintf(" [%s]", a->getMap().c_str()));
250  }
251  }
252 
253  if (graphics->getSecure())
254  {
255  if (mShowGender)
256  {
257  switch (a->getGender())
258  {
259  case Gender::FEMALE:
260  text.append(" \u2640 ");
261  break;
262  case Gender::MALE:
263  text.append(" \u2642 ");
264  break;
265  default:
266  case Gender::UNSPECIFIED:
267  case Gender::OTHER:
268  break;
269  }
270  }
271  }
272  else
273  {
274  if (mShowGender)
275  {
276  switch (a->getGender())
277  {
278  case Gender::FEMALE:
279  text.append(strprintf(" \u2640 %s",
280  a->getAdditionString().c_str()));
281  break;
282  case Gender::MALE:
283  text.append(strprintf(" \u2642 %s",
284  a->getAdditionString().c_str()));
285  break;
286  default:
287  case Gender::UNSPECIFIED:
288  case Gender::OTHER:
289  break;
290  }
291  }
292  else
293  {
294  text.append(a->getAdditionString());
295  }
296  }
297 
298  // Draw Name
299  if (a->getDisplayBold())
300  {
301  if (type == MapItemType::SEPARATOR)
302  {
303  boldFont->drawString(graphics,
306  text,
308  y + mPadding);
309  }
310  else
311  {
312  boldFont->drawString(graphics,
315  text,
316  15 + mImagePadding + mPadding,
317  y + mPadding);
318  }
319  }
320  else
321  {
322  if (type == MapItemType::SEPARATOR)
323  {
324  font->drawString(graphics,
327  text,
329  y + mPadding);
330  }
331  else
332  {
333  font->drawString(graphics,
336  text,
337  15 + mImagePadding + mPadding,
338  y + mPadding);
339  }
340  }
341  }
342 
343  setWidth(parentWidth - 10);
344  BLOCK_END("AvatarListBox::draw")
345 }
346 
347 void AvatarListBox::safeDraw(Graphics *const graphics)
348 {
349  BLOCK_START("AvatarListBox::draw")
350  if ((mListModel == nullptr) || (localPlayer == nullptr))
351  {
352  BLOCK_END("AvatarListBox::draw")
353  return;
354  }
355 
356  const Widget *const parent = mParent;
357  if (parent == nullptr)
358  return;
359 
360  AvatarListModel *const model = static_cast<AvatarListModel *>(
361  mListModel);
362  updateAlpha();
363 
364  Font *const font = getFont();
365  const int fontHeight = font->getHeight();
366  const int parentWidth = parent->getWidth();
367  const std::string &name = localPlayer->getName();
368 
369  // Draw the list elements
370  const int num = model->getNumberOfElements();
371  for (int i = 0, y = 0;
372  i < num;
373  ++i, y += fontHeight)
374  {
375  const Avatar *const a = model->getAvatarAt(i);
376  if (a == nullptr)
377  continue;
378 
379  const MapItemType::Type type = static_cast<MapItemType::Type>(
380  a->getType());
381  if (type != MapItemType::SEPARATOR)
382  {
383  // Draw online status
384  const Image *const icon = a->getOnline()
386  if (icon != nullptr)
387  graphics->drawImage(icon, mImagePadding, y + mPadding);
388  }
389  }
390 
391  for (int i = 0, y = 0;
392  i < model->getNumberOfElements();
393  ++i, y += fontHeight)
394  {
395  const Avatar *const a = model->getAvatarAt(i);
396  if (a == nullptr)
397  continue;
398 
399  const MapItemType::Type type = static_cast<MapItemType::Type>(
400  a->getType());
401  std::string text;
402 
403  if (a->getMaxHp() > 0)
404  {
405  if (mShowLevel && a->getLevel() > 1)
406  {
407  text = strprintf("%s %d/%d (%d)", a->getComplexName().c_str(),
408  a->getHp(), a->getMaxHp(), a->getLevel());
409  }
410  else
411  {
412  text = strprintf("%s %d/%d", a->getComplexName().c_str(),
413  a->getHp(), a->getMaxHp());
414  }
415  const bool isPoison = a->getPoison();
416  if (a->getMaxHp() != 0)
417  {
418  const ProgressColorIdT themeColor = (isPoison
422  themeColor, static_cast<float>(a->getHp())
423  / static_cast<float>(a->getMaxHp()));
424  color.a = 80;
425  graphics->setColor(color);
426  graphics->fillRectangle(Rect(mPadding, y + mPadding,
427  parentWidth * a->getHp() / a->getMaxHp()
428  - 2 * mPadding, fontHeight));
429  }
430  }
431  else if (a->getDamageHp() != 0 && a->getName() != name)
432  {
433  if (mShowLevel && a->getLevel() > 1)
434  {
435  text = strprintf("%s -%d (%d)", a->getComplexName().c_str(),
436  a->getDamageHp(), a->getLevel());
437  }
438  else
439  {
440  text = strprintf("%s -%d", a->getComplexName().c_str(),
441  a->getDamageHp());
442  }
443 
444  const ProgressColorIdT themeColor = (a->getPoison()
446  Color color = Theme::getProgressColor(themeColor, 1);
447  color.a = 80;
448  graphics->setColor(color);
449  graphics->fillRectangle(Rect(mPadding, y + mPadding,
450  parentWidth * a->getDamageHp() / 1024
451  - 2 * mPadding, fontHeight));
452 
453  if (a->getLevel() > 1)
454  {
455  graphics->setColor(mForegroundColor);
456  int minHp = 40 + ((a->getLevel() - 1) * 5);
457  if (minHp < 0)
458  minHp = 40;
459 
460  graphics->drawLine(parentWidth * minHp / 1024
461  + mPadding, y + mPadding,
462  parentWidth * minHp / 1024, y + fontHeight);
463  }
464  }
465  else
466  {
467  if (mShowLevel && a->getLevel() > 1)
468  {
469  text = strprintf("%s (%d)", a->getComplexName().c_str(),
470  a->getLevel());
471  }
472  else
473  {
474  text = a->getComplexName();
475  }
476  }
477 
478  if (!a->getMap().empty())
479  {
480  if (a->getX() != -1)
481  {
482  text.append(strprintf(" [%d,%d %s]", a->getX(), a->getY(),
483  a->getMap().c_str()));
484  }
485  else
486  {
487  text.append(strprintf(" [%s]", a->getMap().c_str()));
488  }
489  }
490 
491  if (graphics->getSecure())
492  {
493  if (mShowGender)
494  {
495  switch (a->getGender())
496  {
497  case Gender::FEMALE:
498  text.append(" \u2640 ");
499  break;
500  case Gender::MALE:
501  text.append(" \u2642 ");
502  break;
503  default:
504  case Gender::UNSPECIFIED:
505  case Gender::OTHER:
506  break;
507  }
508  }
509  }
510  else
511  {
512  if (mShowGender)
513  {
514  switch (a->getGender())
515  {
516  case Gender::FEMALE:
517  text.append(strprintf(" \u2640 %s",
518  a->getAdditionString().c_str()));
519  break;
520  case Gender::MALE:
521  text.append(strprintf(" \u2642 %s",
522  a->getAdditionString().c_str()));
523  break;
524  default:
525  case Gender::UNSPECIFIED:
526  case Gender::OTHER:
527  break;
528  }
529  }
530  else
531  {
532  text.append(a->getAdditionString());
533  }
534  }
535 
536  // Draw Name
537  if (a->getDisplayBold())
538  {
539  if (type == MapItemType::SEPARATOR)
540  {
541  boldFont->drawString(graphics,
544  text,
546  y + mPadding);
547  }
548  else
549  {
550  boldFont->drawString(graphics,
553  text,
554  15 + mImagePadding + mPadding,
555  y + mPadding);
556  }
557  }
558  else
559  {
560  if (type == MapItemType::SEPARATOR)
561  {
562  font->drawString(graphics,
565  text,
567  y + mPadding);
568  }
569  else
570  {
571  font->drawString(graphics,
574  text,
575  15 + mImagePadding + mPadding,
576  y + mPadding);
577  }
578  }
579  }
580 
581  setWidth(parentWidth - 10);
582  BLOCK_END("AvatarListBox::draw")
583 }
584 
586 {
587  if (actorManager == nullptr ||
588  localPlayer == nullptr ||
589  popupManager == nullptr)
590  {
591  return;
592  }
593 
594  const int height = getFont()->getHeight();
595  if (height == 0)
596  return;
597 
598  const int y = (event.getY() - mPadding) / height;
599  if ((mListModel == nullptr) || y > mListModel->getNumberOfElements())
600  return;
601 
602  setSelected(y);
604  const int selected = getSelected();
605  AvatarListModel *const model = static_cast<AvatarListModel *>(
606  mListModel);
607  if (model == nullptr)
608  return;
609  const Avatar *ava = model->getAvatarAt(selected);
610  if (ava == nullptr)
611  return;
612 
613  const MapItemType::Type type = static_cast<MapItemType::Type>(
614  ava->getType());
615 
616  event.consume();
617  const MouseButtonT eventButton = event.getButton();
618  if (eventButton == MouseButton::LEFT)
619  {
620 #ifdef TMWA_SUPPORT
621  if (type == MapItemType::EMPTY)
622  {
623  const Being *const being = actorManager->findBeingByName(
624  ava->getName(), ActorType::Player);
625  if (being != nullptr)
626  actorManager->heal(being);
627  }
628  else
629 #endif // TMWA_SUPPORT
630  {
631  localPlayer->navigateTo(ava->getX(), ava->getY());
632  }
633  }
634  else if (eventButton == MouseButton::RIGHT)
635  {
636  switch (type)
637  {
638  case MapItemType::EMPTY:
639  {
640  const Avatar *const avatar = model->getAvatarAt(selected);
641  if (avatar != nullptr)
642  {
643  const Being *const being = actorManager->findBeingByName(
644  avatar->getName(), ActorType::Player);
645  if (being != nullptr)
646  {
648  viewport->mMouseY,
649  being);
650  }
651  else
652  {
653  popupMenu->showPlayerPopup(avatar->getName());
654  }
655  }
656  break;
657  }
658  case MapItemType::ATTACK:
661  {
662  std::string name;
663  if (model->getAvatarAt(selected)->getLevel() == 0)
664  name.clear();
665  else
666  name = model->getAvatarAt(selected)->getName();
667 
669  viewport->mMouseY,
670  name,
671  model->getAvatarAt(selected)->getType());
672  break;
673  }
674  case MapItemType::PICKUP:
676  {
677  std::string name;
678  if (model->getAvatarAt(selected)->getLevel() == 0)
679  name.clear();
680  else
681  name = model->getAvatarAt(selected)->getName();
682 
684  viewport->mMouseY,
685  name);
686  break;
687  }
688  case MapItemType::HOME:
689  case MapItemType::ROAD:
690  case MapItemType::CROSS:
695  case MapItemType::PORTAL:
696  case MapItemType::MUSIC:
698  {
699  break;
700  }
701  default:
702  {
703  const Map *const map = viewport->getMap();
704  ava = model->getAvatarAt(selected);
705  if ((map != nullptr) && (ava != nullptr))
706  {
707  MapItem *const mapItem = map->findPortalXY(
708  ava->getX(), ava->getY());
710  viewport->mMouseY,
711  mapItem);
712  }
713  break;
714  }
715  }
716  }
717  else if (eventButton == MouseButton::MIDDLE)
718  {
719  if (type == MapItemType::EMPTY && (chatWindow != nullptr))
720  {
721  const std::string &name = model->getAvatarAt(selected)->getName();
722  const WhisperTab *const tab = chatWindow->addWhisperTab(
723  name, name, true);
724  if (tab != nullptr)
726  }
727  }
728 }
729 
731 {
732 }
733 
734 void AvatarListBox::optionChanged(const std::string &value)
735 {
736  if (value == "showgender")
737  mShowGender = config.getBoolValue("showgender");
738  else if (value == "showlevel")
739  mShowLevel = config.getBoolValue("showlevel");
740 }
Font * getFont() const
Definition: widget.cpp:330
ChatWindow * chatWindow
Definition: chatwindow.cpp:88
std::string getName() const
Definition: avatar.h:52
ProgressColorId ::T ProgressColorIdT
void setSelected(const int selected)
Definition: listbox.cpp:392
int mPadding
Definition: listbox.h:286
int getWidth() const
Definition: widget.h:220
Font * boldFont
Definition: gui.cpp:111
bool getOnline() const
Definition: avatar.h:80
virtual void drawTileCollection(const ImageCollection *const vertCol)=0
void setWidth(const int width)
Definition: widget.cpp:132
Being * findBeingByName(const std::string &name, const ActorTypeT type=ActorType::Unknown) const
Definition: font.h:88
unsigned int a
Definition: color.h:250
const Color & getThemeColor(const ThemeColorIdT type, const unsigned int alpha=255U) const A_INLINE
Definition: widget2.h:44
bool getDisplayBold() const
Definition: avatar.h:107
static int instances
Definition: avatarlistbox.h:59
void safeDraw(Graphics *gcnGraphics)
static Image * getImageFromThemeXml(const std::string &name, const std::string &name2)
Definition: theme.cpp:1129
Definition: rect.h:72
#define BLOCK_START(name)
Definition: perfomance.h:78
Configuration config
PopupManager * popupManager
void showAttackMonsterPopup(const int x, const int y, const std::string &name, const int type)
Definition: popupmenu.cpp:1999
#define BLOCK_END(name)
Definition: perfomance.h:79
virtual int getNumberOfElements()=0
int mMouseX
Definition: viewport.h:153
WhisperTab * addWhisperTab(const std::string &caption, const std::string &nick, const bool switchTo=false)
int getLevel() const
Definition: avatar.h:113
Color mForegroundColor
Definition: widget.h:1085
ListModel * mListModel
Definition: listbox.h:260
virtual std::string getAdditionString() const
Definition: avatar.cpp:61
void addListener(const std::string &key, ConfigListener *const listener)
void showPickupItemPopup(const int x, const int y, const std::string &name)
Definition: popupmenu.cpp:2083
virtual void finalize(ImageCollection *const col)
Definition: graphics.h:464
void distributeActionEvent()
Definition: widget.cpp:492
bool getBoolValue(const std::string &key) const
virtual void setColor(const Color &color)
Definition: graphics.h:319
bool getSecure() const
Definition: graphics.h:292
std::string strprintf(const char *const format,...)
Definition: stringutils.cpp:99
Widget * mParent
Definition: widget.h:1127
void saveState() const
static Image * onlineIcon
Definition: avatarlistbox.h:60
LocalPlayer * localPlayer
PopupMenu * popupMenu
Definition: popupmenu.cpp:102
static Image * offlineIcon
Definition: avatarlistbox.h:61
void drawString(Graphics *const graphics, Color col, const Color &col2, const std::string &text, const int x, const int y)
Definition: font.cpp:253
void updateAlpha()
Definition: listbox.cpp:147
int getSelected() const
Definition: listbox.h:167
int getDamageHp() const
Definition: avatar.h:101
int getHp() const
Definition: avatar.h:89
#define nullptr
Definition: localconsts.h:44
int getX() const
Definition: avatar.h:125
bool navigateTo(const int x, const int y)
void mousePressed(MouseEvent &event)
MapItem * findPortalXY(const int x, const int y) const
Definition: map.cpp:1315
const std::string & getName() const
Definition: being.h:233
Definition: avatar.h:39
Definition: map.h:68
int mMouseY
Definition: viewport.h:154
virtual void fillRectangle(const Rect &rectangle)=0
#define A_UNUSED
Definition: localconsts.h:171
Color mForegroundColor2
Definition: widget2.h:112
virtual void drawImage(const Image *const image, int dstX, int dstY)=0
Definition: widget.h:97
virtual void drawLine(int x1, int y1, int x2, int y2)=0
void removeListeners(ConfigListener *const listener)
void draw(Graphics *gcnGraphics)
AvatarListBox(const Widget2 *const widget, AvatarListModel *const model)
Definition: image.h:61
virtual void calcTileCollection(ImageCollection *const vertCol, const Image *const image, int x, int y)=0
GenderT getGender() const
Definition: avatar.h:161
Definition: color.h:74
static Color getProgressColor(const ProgressColorIdT type, const float progress)
Definition: theme.cpp:154
#define CHECKLISTENERS
Definition: localconsts.h:305
Map * getMap() const
Definition: viewport.h:134
int getHeight() const
Definition: font.cpp:360
void heal(const Being *const target) const
std::string getMap() const
Definition: avatar.h:119
void showPlayerPopup(const std::string &nick)
Definition: popupmenu.cpp:598
MouseButton ::T MouseButtonT
Definition: mousebutton.h:77
Definition: being.h:93
void mouseReleased(MouseEvent &event) A_CONST
virtual Avatar * getAvatarAt(const int i)=0
void decRef()
Definition: image.cpp:521
void optionChanged(const std::string &value)
std::string getComplexName() const
Definition: avatar.cpp:50
void showPopup(const int x, const int y, const Being *const being)
Definition: popupmenu.cpp:201
int getMaxHp() const
Definition: avatar.h:95
ActorManager * actorManager
int getType() const
Definition: avatar.h:137
Viewport * viewport
Definition: viewport.cpp:35
int getY() const
Definition: avatar.h:131
bool getPoison() const
Definition: avatar.h:179