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 <algorithm>
21
22 #include "ui/graphical/game/temp/unitdrawingengine.h"
23 #include "ui/graphical/game/animations/animationtimer.h"
24 #include "ui/graphical/game/unitselection.h"
25 #include "video.h"
26 #include "game/data/units/building.h"
27 #include "game/data/units/vehicle.h"
28 #include "game/data/player/player.h"
29 #include "game/data/map/map.h"
30 #include "utility/random.h"
31 #include "utility/drawing.h"
32 #include "utility/box.h"
33
34 //--------------------------------------------------------------------------
cUnitDrawingEngine(std::shared_ptr<cAnimationTimer> animationTimer_,std::shared_ptr<const cFrameCounter> frameCounter)35 cUnitDrawingEngine::cUnitDrawingEngine (std::shared_ptr<cAnimationTimer> animationTimer_, std::shared_ptr<const cFrameCounter> frameCounter) :
36 animationTimer (std::move (animationTimer_)),
37 drawingCache (frameCounter),
38 blinkColor (cRgbColor::white()),
39 shouldDrawHits (false),
40 shouldDrawStatus (false),
41 shouldDrawAmmo (false),
42 shouldDrawColor (false)
43 {
44 signalConnectionManager.connect (animationTimer->triggered100ms, std::bind (&cUnitDrawingEngine::rotateBlinkColor, this));
45 }
46
47 //--------------------------------------------------------------------------
setDrawHits(bool drawHits)48 void cUnitDrawingEngine::setDrawHits (bool drawHits)
49 {
50 shouldDrawHits = drawHits;
51 }
52
53 //--------------------------------------------------------------------------
setDrawStatus(bool drawStatus)54 void cUnitDrawingEngine::setDrawStatus (bool drawStatus)
55 {
56 shouldDrawStatus = drawStatus;
57 }
58
59 //--------------------------------------------------------------------------
setDrawAmmo(bool drawAmmo)60 void cUnitDrawingEngine::setDrawAmmo (bool drawAmmo)
61 {
62 shouldDrawAmmo = drawAmmo;
63 }
64
65 //--------------------------------------------------------------------------
setDrawColor(bool drawColor)66 void cUnitDrawingEngine::setDrawColor (bool drawColor)
67 {
68 shouldDrawColor = drawColor;
69 }
70
71 //--------------------------------------------------------------------------
drawUnit(const cBuilding & building,SDL_Rect destination,float zoomFactor,const cUnitSelection * unitSelection,const cPlayer * player)72 void cUnitDrawingEngine::drawUnit (const cBuilding& building, SDL_Rect destination, float zoomFactor, const cUnitSelection* unitSelection, const cPlayer* player)
73 {
74 unsigned long long animationTime = animationTimer->getAnimationTime(); //call getAnimationTime only once in this method and save the result,
75 //to avoid a changing time within this method
76
77 SDL_Rect dest = {0, 0, 0, 0};
78 bool bDraw = false;
79 SDL_Surface* drawingSurface = drawingCache.getCachedImage (building, zoomFactor, animationTime);
80 if (drawingSurface == nullptr)
81 {
82 // no cached image found. building needs to be redrawn.
83 bDraw = true;
84 drawingSurface = drawingCache.createNewEntry (building, zoomFactor, animationTime);
85 }
86
87 if (drawingSurface == nullptr)
88 {
89 // image will not be cached. So blitt directly to the screen buffer.
90 dest = destination;
91 drawingSurface = cVideo::buffer;
92 }
93
94 if (bDraw)
95 {
96 building.render (animationTime, drawingSurface, dest, zoomFactor, cSettings::getInstance().isShadows(), true);
97 }
98
99 // now check, whether the image has to be blitted to screen buffer
100 if (drawingSurface != cVideo::buffer)
101 {
102 dest = destination;
103 SDL_BlitSurface (drawingSurface, nullptr, cVideo::buffer, &dest);
104
105 // all following graphic operations are drawn directly to buffer
106 dest = destination;
107 }
108
109 if (!building.getOwner()) return;
110
111 // draw the effect if necessary
112 if (building.data.powerOnGraphic && cSettings::getInstance().isAnimations() && (building.isUnitWorking() || !building.data.canWork))
113 {
114 SDL_Rect tmp = dest;
115 SDL_SetSurfaceAlphaMod (building.uiData->eff.get(), building.effectAlpha);
116
117 CHECK_SCALING (*building.uiData->eff, *building.uiData->eff_org, zoomFactor);
118 SDL_BlitSurface (building.uiData->eff.get(), nullptr, cVideo::buffer, &tmp);
119 }
120
121 // draw the mark, when a build order is finished
122 if (building.getOwner() == player && ((!building.isBuildListEmpty() && !building.isUnitWorking() && building.getBuildListItem (0).getRemainingMetal() <= 0) ||
123 (building.data.canResearch && building.getOwner()->isCurrentTurnResearchAreaFinished (building.getResearchArea()))))
124 {
125 const cRgbColor finishedMarkColor = cRgbColor::green();
126 const cBox<cPosition> d (cPosition (dest.x + 2, dest.y + 2), cPosition (dest.x + 2 + (building.data.isBig ? 2 * destination.w - 3 : destination.w - 3), dest.y + 2 + (building.data.isBig ? 2 * destination.h - 3 : destination.h - 3)));
127
128 drawRectangle (*cVideo::buffer, d, finishedMarkColor.exchangeGreen (255 - 16 * (animationTime % 0x8)), 3);
129 }
130
131 #if 0
132 // disabled color-frame for buildings
133 // => now it's original game behavior - see ticket #542 (GER) = FIXED
134 // but maybe as setting interresting
135 // => ticket #784 (ENG) (so I just commented it) = TODO
136
137 // draw a colored frame if necessary
138 if (shouldDrawColor)
139 {
140 const Uint32 color = 0xFF000000 | *static_cast<Uint32*> (building.owner->getColorSurface()->pixels);
141 SDL_Rect d = {Sint16 (dest.x + 1), Sint16 (dest.y + 1), building.data.isBig ? 2 * destination.w - 1 : destination.w - 1, building.data.isBig ? 2 * destination.h - 1 : destination.h - 1};
142
143 DrawRectangle (cVideo::buffer, d, color, 1);
144 }
145 #endif
146 // draw the seleted-unit-flash-frame for bulidings
147 if (unitSelection && &building == unitSelection->getSelectedBuilding())
148 {
149 Uint16 maxX = building.data.isBig ? destination.w * 2 : destination.w;
150 Uint16 maxY = building.data.isBig ? destination.h * 2 : destination.h;
151 const int len = maxX / 4;
152 maxX -= 3;
153 maxY -= 3;
154 const cBox<cPosition> d (cPosition (dest.x + 2, dest.y + 2), cPosition (dest.x + 2 + maxX, dest.y + 2 + maxY));
155
156 drawSelectionCorner (*cVideo::buffer, d, blinkColor, len);
157 }
158
159 // draw health bar
160 if (shouldDrawHits)
161 {
162 drawHealthBar (building, destination);
163 }
164
165 // draw ammo bar
166 if (shouldDrawAmmo && (!player || building.getOwner() == player) && building.data.canAttack && building.data.getAmmoMax() > 0)
167 {
168 drawMunBar (building, destination);
169 }
170
171 // draw status
172 if (shouldDrawStatus)
173 {
174 drawStatus (building, destination);
175 }
176 }
177
178 //--------------------------------------------------------------------------
drawUnit(const cVehicle & vehicle,SDL_Rect destination,float zoomFactor,const cMap & map,const cUnitSelection * unitSelection,const cPlayer * player)179 void cUnitDrawingEngine::drawUnit (const cVehicle& vehicle, SDL_Rect destination, float zoomFactor, const cMap& map, const cUnitSelection* unitSelection, const cPlayer* player)
180 {
181 unsigned long long animationTime = animationTimer->getAnimationTime(); //call getAnimationTime only once in this method and save the result,
182 //to avoid a changing time within this method
183
184 // calculate screen position
185 int ox = (int) (vehicle.getMovementOffset().x() * zoomFactor);
186 int oy = (int) (vehicle.getMovementOffset().y() * zoomFactor);
187
188 destination.x += ox;
189 destination.y += oy;
190
191 if (vehicle.getFlightHeight() > 0)
192 {
193 destination.x += vehicle.ditherX;
194 destination.y += vehicle.ditherY;
195 }
196
197 SDL_Rect dest;
198 dest.x = dest.y = 0;
199 bool bDraw = false;
200 SDL_Surface* drawingSurface = drawingCache.getCachedImage (vehicle, zoomFactor, map, animationTime);
201 if (drawingSurface == nullptr)
202 {
203 // no cached image found. building needs to be redrawn.
204 bDraw = true;
205 drawingSurface = drawingCache.createNewEntry (vehicle, zoomFactor, map, animationTime);
206 }
207
208 if (drawingSurface == nullptr)
209 {
210 // image will not be cached. So blitt directly to the screen buffer.
211 dest = destination;
212 drawingSurface = cVideo::buffer;
213 }
214
215 if (bDraw)
216 {
217 vehicle.render (&map, animationTime, player, drawingSurface, dest, zoomFactor, cSettings::getInstance().isShadows());
218 }
219
220 // now check, whether the image has to be blitted to screen buffer
221 if (drawingSurface != cVideo::buffer)
222 {
223 dest = destination;
224 SDL_BlitSurface (drawingSurface, nullptr, cVideo::buffer, &dest);
225 }
226
227 // draw overlay if necessary:
228 vehicle.drawOverlayAnimation (animationTime, cVideo::buffer, destination, zoomFactor);
229
230 // remove the dithering for the following operations
231 if (vehicle.getFlightHeight() > 0)
232 {
233 destination.x -= vehicle.ditherX;
234 destination.y -= vehicle.ditherY;
235 }
236
237 // remove movement offset for working units
238 if (vehicle.isUnitBuildingABuilding() || vehicle.isUnitClearing())
239 {
240 destination.x -= ox;
241 destination.y -= oy;
242 }
243
244 // draw indication, when building is complete
245 if (vehicle.isUnitBuildingABuilding() && vehicle.getBuildTurns() == 0 && vehicle.getOwner() == player && !vehicle.BuildPath)
246 {
247 const cRgbColor finishedMarkColor = cRgbColor::green();
248 const cBox<cPosition> d (cPosition (destination.x + 2, destination.y + 2), cPosition (destination.x + 2 + (vehicle.data.isBig ? 2 * destination.w - 3 : destination.w - 3), destination.y + 2 + (vehicle.data.isBig ? 2 * destination.h - 3 : destination.h - 3)));
249
250 drawRectangle (*cVideo::buffer, d, finishedMarkColor.exchangeGreen (255 - 16 * (animationTime % 0x8)), 3);
251 }
252
253 // Draw the colored frame if necessary
254 if (shouldDrawColor)
255 {
256 const cBox<cPosition> d (cPosition (destination.x + 1, destination.y + 1), cPosition (destination.x + 1 + (vehicle.data.isBig ? 2 * destination.w - 1 : destination.w - 1), destination.y + 1 + (vehicle.data.isBig ? 2 * destination.h - 1 : destination.h - 1)));
257
258 drawRectangle (*cVideo::buffer, d, vehicle.getOwner()->getColor().getColor());
259 }
260
261 // draw the group selected frame if necessary
262 if (unitSelection && unitSelection->getSelectedUnitsCount() > 1 && unitSelection->isSelected (vehicle))
263 {
264 const cRgbColor groupSelectionColor = cRgbColor::yellow();
265 const cBox<cPosition> d (cPosition (destination.x + 2, destination.y + 2), cPosition (destination.x + 2 + (vehicle.data.isBig ? 2 * destination.w - 3 : destination.w - 3), destination.y + 2 + (vehicle.data.isBig ? 2 * destination.h - 3 : destination.h - 3)));
266
267 drawRectangle (*cVideo::buffer, d, groupSelectionColor, 1);
268 }
269 // draw the seleted-unit-flash-frame for vehicles
270 if (unitSelection && &vehicle == unitSelection->getSelectedVehicle())
271 {
272 Uint16 maxX = vehicle.data.isBig ? destination.w * 2 : destination.w;
273 Uint16 maxY = vehicle.data.isBig ? destination.h * 2 : destination.h;
274 const int len = maxX / 4;
275 maxX -= 3;
276 maxY -= 3;
277 const cBox<cPosition> d (cPosition (destination.x + 2, destination.y + 2), cPosition (destination.x + 2 + maxX, destination.y + 2 + maxY));
278
279 drawSelectionCorner (*cVideo::buffer, d, blinkColor, len);
280 }
281
282 // draw health bar
283 if (shouldDrawHits)
284 {
285 drawHealthBar (vehicle, destination);
286 }
287
288 // draw ammo bar
289 if (shouldDrawAmmo && (!player || vehicle.getOwner() == player) && vehicle.data.canAttack)
290 {
291 drawMunBar (vehicle, destination);
292 }
293
294 // draw status info
295 if (shouldDrawStatus)
296 {
297 drawStatus (vehicle, destination);
298 }
299
300 }
301
302 //--------------------------------------------------------------------------
drawHealthBar(const cUnit & unit,SDL_Rect destination)303 void cUnitDrawingEngine::drawHealthBar (const cUnit& unit, SDL_Rect destination)
304 {
305 SDL_Rect r1;
306 r1.x = destination.x + destination.w / 10 + 1;
307 r1.y = destination.y + destination.h / 10;
308 r1.w = destination.w * 8 / 10;
309 r1.h = destination.h / 8;
310
311 if (unit.data.isBig)
312 {
313 r1.w += destination.w;
314 r1.h *= 2;
315 }
316
317 if (r1.h <= 2)
318 r1.h = 3;
319
320 SDL_Rect r2;
321 r2.x = r1.x + 1;
322 r2.y = r1.y + 1;
323 r2.w = (int) (((float) (r1.w - 2) / unit.data.getHitpointsMax()) * unit.data.getHitpoints());
324 r2.h = r1.h - 2;
325
326 SDL_FillRect (cVideo::buffer, &r1, 0xFF000000);
327
328 Uint32 color;
329 if (unit.data.getHitpoints() > unit.data.getHitpointsMax() / 2)
330 color = 0xFF04AE04; // green
331 else if (unit.data.getHitpoints() > unit.data.getHitpointsMax() / 4)
332 color = 0xFFDBDE00; // orange
333 else
334 color = 0xFFE60000; // red
335 SDL_FillRect (cVideo::buffer, &r2, color);
336 }
337
338 //--------------------------------------------------------------------------
drawMunBar(const cUnit & unit,SDL_Rect destination)339 void cUnitDrawingEngine::drawMunBar (const cUnit& unit, SDL_Rect destination)
340 {
341 SDL_Rect r1;
342 r1.x = destination.x + destination.w / 10 + 1;
343 r1.y = destination.y + destination.h / 10 + destination.h / 8;
344 r1.w = destination.w * 8 / 10;
345 r1.h = destination.h / 8;
346
347 if (r1.h <= 2)
348 {
349 r1.y += 1;
350 r1.h = 3;
351 }
352
353 SDL_Rect r2;
354 r2.x = r1.x + 1;
355 r2.y = r1.y + 1;
356 r2.w = (int) (((float) (r1.w - 2) / unit.data.getAmmoMax()) * unit.data.getAmmo());
357 r2.h = r1.h - 2;
358
359 SDL_FillRect (cVideo::buffer, &r1, 0xFF000000);
360
361 if (unit.data.getAmmo() > unit.data.getAmmoMax() / 2)
362 SDL_FillRect (cVideo::buffer, &r2, 0xFF04AE04);
363 else if (unit.data.getAmmo() > unit.data.getAmmoMax() / 4)
364 SDL_FillRect (cVideo::buffer, &r2, 0xFFDBDE00);
365 else
366 SDL_FillRect (cVideo::buffer, &r2, 0xFFE60000);
367 }
368
369 //--------------------------------------------------------------------------
drawStatus(const cUnit & unit,SDL_Rect destination)370 void cUnitDrawingEngine::drawStatus (const cUnit& unit, SDL_Rect destination)
371 {
372 SDL_Rect speedSymbol = {244, 97, 8, 10};
373 SDL_Rect shotsSymbol = {254, 97, 5, 10};
374 SDL_Rect disabledSymbol = {150, 109, 25, 25};
375 SDL_Rect dest;
376
377 if (unit.isDisabled())
378 {
379 if (destination.w < 25)
380 return;
381 dest.x = destination.x + destination.w / 2 - 12;
382 dest.y = destination.y + destination.h / 2 - 12;
383 if (unit.data.isBig)
384 {
385 dest.y += (destination.h / 2);
386 dest.x += (destination.w / 2);
387 }
388 SDL_BlitSurface (GraphicsData.gfx_hud_stuff.get(), &disabledSymbol, cVideo::buffer, &dest);
389 }
390 else
391 {
392 dest.y = destination.y + destination.h - 11;
393 dest.x = destination.x + destination.w / 2 - 4;
394 if (unit.data.isBig)
395 {
396 dest.y += (destination.h / 2);
397 dest.x += (destination.w / 2);
398 }
399 if (unit.data.getSpeed() >= 4)
400 {
401 if (unit.data.getShots())
402 dest.x -= destination.w / 4;
403
404 SDL_Rect destCopy = dest;
405 SDL_BlitSurface (GraphicsData.gfx_hud_stuff.get(), &speedSymbol, cVideo::buffer, &destCopy);
406 }
407
408 dest.x = destination.x + destination.w / 2 - 4;
409 if (unit.data.getShots())
410 {
411 if (unit.data.getSpeed())
412 dest.x += destination.w / 4;
413 SDL_BlitSurface (GraphicsData.gfx_hud_stuff.get(), &shotsSymbol, cVideo::buffer, &dest);
414 }
415 }
416 }
417
418 //--------------------------------------------------------------------------
rotateBlinkColor()419 void cUnitDrawingEngine::rotateBlinkColor()
420 {
421 static bool dec = true;
422 if (dec)
423 {
424 blinkColor.r -= 0x0A;
425 blinkColor.g -= 0x0A;
426 blinkColor.b -= 0x0A;
427 if (blinkColor.r <= 0xA0) dec = false;
428 }
429 else
430 {
431 blinkColor.r += 0x0A;
432 blinkColor.g += 0x0A;
433 blinkColor.b += 0x0A;
434 if (blinkColor.r >= (0xFF - 0x0A)) dec = true;
435 }
436 }
437