1 /***************************************************************************
2 * Mechanized Assault and Exploration Reloaded Projectfile *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
18 ***************************************************************************/
19
20 #include "ui/graphical/game/widgets/unitdetailshud.h"
21
22 #include "ui/graphical/menu/widgets/label.h"
23 #include "main.h"
24 #include "video.h"
25 #include "game/data/units/unit.h"
26 #include "game/data/player/player.h"
27 #include "game/data/units/building.h"
28 #include "ui/graphical/menu/windows/windowgamesettings/gamesettings.h"
29 #include "utility/drawing.h"
30
31 //------------------------------------------------------------------------------
cUnitDetailsHud(const cBox<cPosition> & area,bool drawLines_)32 cUnitDetailsHud::cUnitDetailsHud (const cBox<cPosition>& area, bool drawLines_) :
33 cWidget (area),
34 drawLines (drawLines_),
35 unit (nullptr)
36 {
37 const auto size = getSize();
38 if (std::size_t (size.y()) < maxRows * rowHeight) resize (cPosition (getSize().x(), maxRows * rowHeight));
39
40 for (size_t i = 0; i < maxRows; ++i)
41 {
42 amountLabels[i] = addChild (std::make_unique<cLabel> (cBox<cPosition> (getPosition() + cPosition (3, 2 + rowHeight * i), getPosition() + cPosition (3 + 35, 2 + rowHeight * i + rowHeight)), "", FONT_LATIN_SMALL_WHITE, toEnumFlag (eAlignmentType::CenterHorizontal) | eAlignmentType::Bottom));
43 nameLabels[i] = addChild (std::make_unique<cLabel> (cBox<cPosition> (getPosition() + cPosition (40, 2 + rowHeight * i), getPosition() + cPosition (40 + 40, 2 + rowHeight * i + rowHeight)), "", FONT_LATIN_SMALL_WHITE, toEnumFlag (eAlignmentType::Left) | eAlignmentType::Bottom));
44 }
45
46 surface = AutoSurface (SDL_CreateRGBSurface (0, size.x(), size.y(), Video.getColDepth(), 0, 0, 0, 0));
47
48 SDL_FillRect (surface.get(), nullptr, 0xFF00FF);
49 SDL_SetColorKey (surface.get(), SDL_TRUE, 0xFF00FF);
50 }
51
52 //------------------------------------------------------------------------------
setUnit(const cUnit * unit_)53 void cUnitDetailsHud::setUnit (const cUnit* unit_)
54 {
55 unit = unit_;
56 }
57
58 //------------------------------------------------------------------------------
setPlayer(const cPlayer * player_)59 void cUnitDetailsHud::setPlayer (const cPlayer* player_)
60 {
61 player = player_;
62 }
63
64 //------------------------------------------------------------------------------
setGameSettings(std::shared_ptr<const cGameSettings> gameSettings_)65 void cUnitDetailsHud::setGameSettings (std::shared_ptr<const cGameSettings> gameSettings_)
66 {
67 gameSettings = std::move (gameSettings_);
68 }
69
70 //------------------------------------------------------------------------------
draw(SDL_Surface & destination,const cBox<cPosition> & clipRect)71 void cUnitDetailsHud::draw (SDL_Surface& destination, const cBox<cPosition>& clipRect)
72 {
73 if (surface != nullptr)
74 {
75 reset();
76
77 blitClipped (*surface, getArea(), destination, clipRect);
78 }
79
80 cWidget::draw (destination, clipRect);
81 }
82
83 //------------------------------------------------------------------------------
reset()84 void cUnitDetailsHud::reset()
85 {
86 SDL_FillRect (surface.get(), nullptr, 0xFF00FF);
87 SDL_SetColorKey (surface.get(), SDL_TRUE, 0xFF00FF);
88
89 for (std::size_t i = 0; i < maxRows; ++i)
90 {
91 if (drawLines)
92 {
93 SDL_Rect lineRect = {2, int (14 + 12 * i), getSize().x(), 1};
94 SDL_FillRect (surface.get(), &lineRect, 0xFF743904);
95 }
96
97 amountLabels[i]->hide();
98 nameLabels[i]->hide();
99 }
100
101 if (unit == nullptr) return;
102
103 const auto& data = unit->data;
104
105 drawRow (0, eUnitDataSymbolType::Hits, data.getHitpoints(), data.getHitpointsMax(), lngPack.i18n ("Text~Others~Hitpoints_7"));
106
107 if (data.getSpeedMax() > 0) drawRow (2, eUnitDataSymbolType::Speed, data.getSpeed() / 4, data.getSpeedMax() / 4, lngPack.i18n ("Text~Others~Speed_7"));
108
109 if (data.canScore)
110 {
111 assert (unit->data.ID.isABuilding()); // currently only buildings can score
112 const auto unitScore = static_cast<const cBuilding*> (unit)->points;
113 const auto totalScore = unit->getOwner()->getScore();
114 const auto goalScore = (gameSettings && gameSettings->getVictoryCondition() == eGameSettingsVictoryCondition::Points) ? gameSettings->getVictoryPoints() : totalScore;
115
116 drawRow (1, eUnitDataSymbolType::Human, unitScore, unitScore, lngPack.i18n ("Text~Others~Score"));
117 drawRow (2, eUnitDataSymbolType::Human, totalScore, goalScore, lngPack.i18n ("Text~Others~Total"));
118 }
119 else if ((data.storeResType != sUnitData::STORE_RES_NONE || data.storageUnitsMax > 0) && unit->getOwner() == player)
120 {
121 if (data.storeResType > 0)
122 {
123 eUnitDataSymbolType symbolType;
124 switch (data.storeResType)
125 {
126 case sUnitData::STORE_RES_METAL:
127 symbolType = eUnitDataSymbolType::Metal;
128 break;
129 case sUnitData::STORE_RES_OIL:
130 symbolType = eUnitDataSymbolType::Oil;
131 break;
132 case sUnitData::STORE_RES_GOLD:
133 symbolType = eUnitDataSymbolType::Gold;
134 break;
135 case sUnitData::STORE_RES_NONE: break;
136 }
137
138 drawRow (1, symbolType, data.getStoredResources(), data.storageResMax, lngPack.i18n ("Text~Others~Cargo_7"));
139
140 if (unit->data.ID.isABuilding())
141 {
142 const auto& building = static_cast<const cBuilding&> (*unit);
143 switch (data.storeResType)
144 {
145 case sUnitData::STORE_RES_METAL:
146 drawRow (2, symbolType, building.SubBase->getMetal(), building.SubBase->MaxMetal, lngPack.i18n ("Text~Others~Total"));
147 break;
148 case sUnitData::STORE_RES_OIL:
149 drawRow (2, symbolType, building.SubBase->getOil(), building.SubBase->MaxOil, lngPack.i18n ("Text~Others~Total"));
150 break;
151 case sUnitData::STORE_RES_GOLD:
152 drawRow (2, symbolType, building.SubBase->getGold(), building.SubBase->MaxGold, lngPack.i18n ("Text~Others~Total"));
153 break;
154 case sUnitData::STORE_RES_NONE: break;
155 }
156 }
157 }
158 else if (data.storeUnitsImageType != sUnitData::STORE_UNIT_IMG_NONE)
159 {
160 eUnitDataSymbolType symbolType;
161 switch (data.storeUnitsImageType)
162 {
163 case sUnitData::STORE_UNIT_IMG_TANK:
164 case sUnitData::STORE_UNIT_IMG_SHIP:
165 symbolType = eUnitDataSymbolType::TransportTank;
166 break;
167 case sUnitData::STORE_UNIT_IMG_PLANE:
168 symbolType = eUnitDataSymbolType::TransportAir;
169 break;
170 case sUnitData::STORE_UNIT_IMG_HUMAN:
171 symbolType = eUnitDataSymbolType::Human;
172 break;
173 case sUnitData::STORE_UNIT_IMG_NONE: break;
174 }
175
176 drawRow (1, symbolType, data.getStoredUnits(), data.storageUnitsMax, lngPack.i18n ("Text~Others~Cargo_7"));
177 }
178 }
179 else if (data.canAttack && !data.explodesOnContact)
180 {
181 if (unit->getOwner() == player) drawRow (1, eUnitDataSymbolType::Ammo, data.getAmmo(), data.getAmmoMax(), lngPack.i18n ("Text~Others~Ammo_7"));
182
183 drawRow (3, eUnitDataSymbolType::Shots, data.getShots(), data.getShotsMax(), lngPack.i18n ("Text~Others~Shots_7"));
184 }
185 else if (data.produceEnergy && unit->data.ID.isABuilding())
186 {
187 const auto& building = static_cast<const cBuilding&> (*unit);
188 drawRow (1, eUnitDataSymbolType::Energy, (building.isUnitWorking() ? data.produceEnergy : 0), data.produceEnergy, lngPack.i18n ("Text~Others~Power"));
189
190 if (unit->getOwner() == player)
191 {
192 drawRow (2, eUnitDataSymbolType::Energy, building.SubBase->EnergyProd, building.SubBase->MaxEnergyProd, lngPack.i18n ("Text~Others~Total"));
193 drawRow (3, eUnitDataSymbolType::Energy, building.SubBase->EnergyNeed, building.SubBase->MaxEnergyNeed, lngPack.i18n ("Text~Others~Usage_7"));
194 }
195 }
196 else if (data.produceHumans && unit->data.ID.isABuilding())
197 {
198 const auto& building = static_cast<const cBuilding&> (*unit);
199 drawRow (1, eUnitDataSymbolType::Human, data.produceHumans, data.produceHumans, lngPack.i18n ("Text~Others~Teams_7"));
200
201 if (unit->getOwner() == player)
202 {
203 drawRow (2, eUnitDataSymbolType::Human, building.SubBase->HumanProd, building.SubBase->HumanProd, lngPack.i18n ("Text~Others~Total"));
204 drawRow (3, eUnitDataSymbolType::Human, building.SubBase->HumanNeed, building.SubBase->MaxHumanNeed, lngPack.i18n ("Text~Others~Usage_7"));
205 }
206 }
207 else if (data.needsHumans && unit->data.ID.isABuilding())
208 {
209 const auto& building = static_cast<const cBuilding&> (*unit);
210 if (building.isUnitWorking()) drawRow (1, eUnitDataSymbolType::Human, data.needsHumans, data.needsHumans, lngPack.i18n ("Text~Others~Usage_7"));
211 else drawRow (1, eUnitDataSymbolType::Human, 0, data.needsHumans, lngPack.i18n ("Text~Others~Usage_7"));
212
213 if (unit->getOwner() == player) drawRow (2, eUnitDataSymbolType::Human, building.SubBase->HumanNeed, building.SubBase->MaxHumanNeed, lngPack.i18n ("Text~Others~Total"));
214 }
215 }
216
217 //------------------------------------------------------------------------------
drawRow(size_t index,eUnitDataSymbolType symbolType,int amount,int maximalAmount,const std::string & name)218 void cUnitDetailsHud::drawRow (size_t index, eUnitDataSymbolType symbolType, int amount, int maximalAmount, const std::string& name)
219 {
220 if (index >= maxRows) return;
221
222 amountLabels[index]->show();
223 nameLabels[index]->show();
224
225 eUnicodeFontType fontType;
226 if (amount > maximalAmount / 2) fontType = FONT_LATIN_SMALL_GREEN;
227 else if (amount > maximalAmount / 4) fontType = FONT_LATIN_SMALL_YELLOW;
228 else fontType = FONT_LATIN_SMALL_RED;
229
230 amountLabels[index]->setFont (fontType);
231 amountLabels[index]->setText (iToStr (amount) + "/" + iToStr (maximalAmount));
232
233 nameLabels[index]->setText (name);
234 drawSmallSymbols (surface.get(), rowHeight, symbolType, cPosition (80, rowHeight * index), amount, maximalAmount);
235 }
236
237 //------------------------------------------------------------------------------
drawSmallSymbols(SDL_Surface * destination,int rowHeight,eUnitDataSymbolType symbolType,const cPosition & position,int value1,int value2)238 void cUnitDetailsHud::drawSmallSymbols (SDL_Surface* destination, int rowHeight, eUnitDataSymbolType symbolType, const cPosition& position, int value1, int value2)
239 {
240 const int maxX = destination->w - position.x() - 5;
241 auto src = getSmallSymbolPosition (symbolType);
242 const cPosition srcSize = src.getSize();
243 int toValue = value2;
244
245 if (symbolType == eUnitDataSymbolType::Hits)
246 {
247 if (value1 <= value2 / 4) // red
248 {
249 src.getMinCorner().x() += srcSize.x() * 4;
250 src.getMaxCorner().x() += srcSize.x() * 4;
251 }
252 else if (value1 <= value2 / 2) // orange
253 {
254 src.getMinCorner().x() += srcSize.x() * 2;
255 src.getMaxCorner().x() += srcSize.x() * 2;
256 }
257 }
258 int offX = srcSize.x();
259 int step = 1;
260
261 while (offX * toValue > maxX)
262 {
263 offX--;
264
265 if (offX < 4)
266 {
267 toValue /= 2;
268 step *= 2;
269 offX = srcSize.x();
270 }
271 }
272
273 SDL_Rect dest = {position.x(), position.y() + 2 + (rowHeight - srcSize.y()) / 2, 0, 0};
274
275 const auto oriSrcMinX = src.getMinCorner().x();
276 const auto oriSrcMaxX = src.getMaxCorner().x();
277 for (int i = 0; i < toValue; i++)
278 {
279 if (value1 <= 0)
280 {
281 src.getMinCorner().x() = oriSrcMinX + srcSize.x();
282 src.getMaxCorner().x() = oriSrcMaxX + srcSize.x();
283 }
284
285 auto srcRect = src.toSdlRect();
286 SDL_BlitSurface (GraphicsData.gfx_hud_stuff.get(), &srcRect, destination, &dest);
287
288 dest.x += offX;
289 value1 -= step;
290 }
291 }
292
293 //------------------------------------------------------------------------------
getSmallSymbolPosition(eUnitDataSymbolType symbolType)294 cBox<cPosition> cUnitDetailsHud::getSmallSymbolPosition (eUnitDataSymbolType symbolType)
295 {
296 cPosition position (0, 98);
297 cPosition size (0, 0);
298
299 switch (symbolType)
300 {
301 case eUnitDataSymbolType::Speed:
302 position.x() = 0;
303 size.x() = 7;
304 size.y() = 7;
305 break;
306 case eUnitDataSymbolType::Hits:
307 position.x() = 14;
308 size.x() = 6;
309 size.y() = 9;
310 break;
311 case eUnitDataSymbolType::Ammo:
312 position.x() = 50;
313 size.x() = 5;
314 size.y() = 7;
315 break;
316 case eUnitDataSymbolType::Shots:
317 position.x() = 88;
318 size.x() = 8;
319 size.y() = 4;
320 break;
321 case eUnitDataSymbolType::Metal:
322 position.x() = 60;
323 size.x() = 7;
324 size.y() = 10;
325 break;
326 case eUnitDataSymbolType::Oil:
327 position.x() = 104;
328 size.x() = 8;
329 size.y() = 9;
330 break;
331 case eUnitDataSymbolType::Gold:
332 position.x() = 120;
333 size.x() = 9;
334 size.y() = 8;
335 break;
336 case eUnitDataSymbolType::Energy:
337 position.x() = 74;
338 size.x() = 7;
339 size.y() = 7;
340 break;
341 case eUnitDataSymbolType::Human:
342 position.x() = 170;
343 size.x() = 8;
344 size.y() = 9;
345 break;
346 case eUnitDataSymbolType::TransportTank:
347 position.x() = 138;
348 size.x() = 16;
349 size.y() = 8;
350 break;
351 case eUnitDataSymbolType::TransportAir:
352 position.x() = 186;
353 size.x() = 21;
354 size.y() = 8;
355 break;
356 case eUnitDataSymbolType::Attack:
357 case eUnitDataSymbolType::Range:
358 case eUnitDataSymbolType::Armor:
359 case eUnitDataSymbolType::Scan:
360 case eUnitDataSymbolType::MetalEmpty:
361 break;
362 }
363
364 return cBox<cPosition> (position, position + size - 1);
365 }
366