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