playersdiagram.cpp
1 // playerdiagram.cpp
2 //------------------------------------------------------------------------------
3 //------------------------------------------------------------------------------
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301 USA
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2010 Braden "Blzut3" Obrzut <admin@maniacsvault.net>
22 //------------------------------------------------------------------------------
23 #include "playersdiagram.h"
24 
25 #include "datapaths.h"
26 #include "log.h"
27 #include "serverapi/playerslist.h"
28 #include "serverapi/server.h"
29 #include "strings.hpp"
30 #include <cassert>
31 #include <QDir>
32 #include <QPainter>
33 #include <QResource>
34 #include <utility>
35 
36 QImage PlayersDiagram::openImage;
37 QImage PlayersDiagram::openSpecImage;
38 QImage PlayersDiagram::botImage;
39 QImage PlayersDiagram::playerImage;
40 QImage PlayersDiagram::spectatorImage;
41 
42 const QString PlayersDiagram::DEFAULT_STYLE = "blocks";
43 QString PlayersDiagram::currentlyLoadedStyle;
44 
45 PlayersDiagram::PlayersDiagram(ServerCPtr server)
46  : server(std::move(server))
47 {
48  if (openImage.isNull())
49  return;
50 
51  obtainPlayerNumbers();
52  draw();
53 }
54 
55 PlayersDiagram::~PlayersDiagram()
56 {
57 }
58 
59 QList<PlayersDiagramStyle> PlayersDiagram::availableSlotStyles()
60 {
61  QList<PlayersDiagramStyle> list;
62 
63  // Built-ins.
64  list << PlayersDiagramStyle("numeric", tr("Numeric"));
65  list << PlayersDiagramStyle("blocks", tr("Blocks"));
66 
67  // Extra.
68  QStringList knownNames;
69  for (QDir dir : stylePaths())
70  {
71  QStringList extraSlots = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
72  for (QString slotsName : extraSlots)
73  {
74  if (!knownNames.contains(slotsName, Qt::CaseInsensitive))
75  {
76  knownNames << slotsName;
77  list << PlayersDiagramStyle(slotsName);
78  }
79  }
80  }
81 
82  return list;
83 }
84 
85 QImage PlayersDiagram::colorizePlayer(QImage image, const QColor &color)
86 {
87  QVector<QRgb> colors = image.colorTable();
88  QColor destinationColor = color.toHsv();
89  for (QRgb &color : colors)
90  {
91  // Cyan has no red so move on if this color has red.
92  if (qRed(color) != 0 || qAlpha(color) == 0)
93  continue;
94 
95  int hue = 0;
96  int saturation = 0;
97  int value = 0;
98  destinationColor.getHsv(&hue, &saturation, &value);
99  destinationColor.setHsv(hue, saturation, QColor(color).toHsv().value());
100  color = destinationColor.rgb();
101  }
102  image.setColorTable(colors);
103 
104  return image;
105 }
106 
107 void PlayersDiagram::draw()
108 {
109  // Don't bother trying to draw an empty image.
110  if (server->maxClients() == 0)
111  return;
112 
113  diagram = QPixmap(server->maxClients() * playerImage.width(), playerImage.height());
114  diagram.fill(Qt::transparent);
115 
116  slotSize = playerImage.width();
117  position = diagram.width() - slotSize;
118  painter = new QPainter(&diagram);
119 
120  for (int team = 0; team < MAX_TEAMS; ++team)
121  {
122  drawTeam(Human, team, numHumansOnTeam[team]);
123  drawTeam(Bot, team, numBotsOnTeam[team]);
124  }
125 
126  drawTeam(Human, Player::TEAM_NONE, numHumansWithoutTeam);
127  drawTeam(Bot, Player::TEAM_NONE, numBotsWithoutTeam);
128 
129  if (numSpectators > 0)
130  drawPictures(spectatorImage, numSpectators);
131 
132  if (numFreeJoinSlots > 0)
133  drawPictures(openImage, numFreeJoinSlots);
134 
135  if (numFreeSpectatorSlots > 0)
136  drawPictures(openSpecImage, numFreeSpectatorSlots);
137 
138  delete painter;
139 }
140 
141 void PlayersDiagram::drawTeam(PlayerType playerType, int team, int howMany)
142 {
143  if (howMany > 0)
144  {
145  QImage baseImage;
146 
147  switch (playerType)
148  {
149  case Bot:
150  baseImage = botImage;
151  break;
152 
153  case Human:
154  baseImage = playerImage;
155  break;
156 
157  default:
158  gLog << "Error inside PlayersDiagram::drawTeam(): unknown PlayerType";
159  return;
160  }
161 
162  const QImage picture = colorizePlayer(baseImage, QColor(server->teamColor(team)));
163  drawPictures(picture, howMany);
164  }
165 }
166 
167 void PlayersDiagram::drawPictures(const QImage &image, int howMany)
168 {
169  for (; howMany > 0; --howMany)
170  {
171  painter->drawImage(position, 0, image);
172  position -= slotSize;
173  }
174 }
175 
176 bool PlayersDiagram::isNumericStyle(const QString &style)
177 {
178  return style == "numeric";
179 }
180 
181 void PlayersDiagram::loadImages(const QString &style)
182 {
183  if (style == currentlyLoadedStyle)
184  return;
185  if (isNumericStyle(style))
186  return;
187 
188  openImage = loadImage(style, "open");
189  openSpecImage = loadImage(style, "specopen");
190  botImage = loadImage(style, "bot");
191  playerImage = loadImage(style, "player");
192  spectatorImage = loadImage(style, "spectator");
193  currentlyLoadedStyle = style;
194 }
195 
196 QImage PlayersDiagram::loadImage(const QString &style, const QString &name)
197 {
198  QImage image;
199  if (style != DEFAULT_STYLE)
200  {
201  QString resourcePath;
202  for (const QString &dir : stylePaths())
203  {
204  image = QImage(Strings::combinePaths(dir, style + "/" + name + ".png"));
205  if (!image.isNull())
206  return image;
207  }
208  }
209  if (image.isNull())
210  image = QImage(":/slots/" + DEFAULT_STYLE + "/" + name);
211  return image;
212 }
213 
214 void PlayersDiagram::obtainPlayerNumbers()
215 {
216  memset(numBotsOnTeam, 0, sizeof(int) * MAX_TEAMS);
217  memset(numHumansOnTeam, 0, sizeof(int) * MAX_TEAMS);
218 
219  const PlayersList &players = server->players();
220 
221  numBotsWithoutTeam = players.numBotsWithoutTeam();
222  numFreeJoinSlots = server->numFreeJoinSlots();
223  numFreeSpectatorSlots = server->numFreeSpectatorSlots();
224  numHumansWithoutTeam = players.numHumansWithoutTeam();
225  numSpectators = players.numSpectators();
226 
227  for (int i = 0; i < MAX_TEAMS; ++i)
228  {
229  numBotsOnTeam[i] = players.numBotsOnTeam(i);
230  numHumansOnTeam[i] = players.numHumansOnTeam(i);
231  }
232 }
233 
234 QStringList PlayersDiagram::stylePaths()
235 {
236  return gDefaultDataPaths->staticDataSearchDirs("theme/slots");
237 }