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