1 ////////////////////////////////////////////////////////////////////////////////
2 // Copyright (C) 2004-2011 by The Allacrost Project
3 // Copyright (C) 2012-2016 by Bertram (Valyria Tear)
4 // All Rights Reserved
5 //
6 // This code is licensed under the GNU GPL version 2. It is free software and
7 // you may modify it and/or redistribute it under the terms of this license.
8 // See http://www.gnu.org/copyleft/gpl.html for details.
9 ////////////////////////////////////////////////////////////////////////////////
10
11 /** ****************************************************************************
12 *** \file indicator_supervisor.h
13 *** \author Tyler Olsen, roots@allacrost.org
14 *** \author Yohann Ferreira, yohann ferreira orange fr
15 *** \brief Source file for battle indicator displays.
16 *** ***************************************************************************/
17
18 #include "indicator_supervisor.h"
19
20 #include "engine/system.h"
21 #include "engine/video/video.h"
22
23 #include "common/global/global.h"
24 #include "common/message_window.h"
25
26 #include "utils/utils_random.h"
27 #include "utils/utils_strings.h"
28
29 using namespace vt_video;
30
31 namespace vt_mode_manager
32 {
33
34 //! \brief The amount of time (in milliseconds) that indicator elements fade at the beginning of the display sequence
35 const uint32_t INDICATOR_FADEIN_TIME = 500;
36
37 //! \brief The amount of time (in milliseconds) that indicator elements fade at the end of the display sequence
38 const uint32_t INDICATOR_FADEOUT_TIME = 1000;
39
40 //! \brief Represents the initial y (up) force applied on the indicator effect
41 const float INITIAL_FORCE = 12.0f;
42
43 ////////////////////////////////////////////////////////////////////////////////
44 // IndicatorElement class
45 ////////////////////////////////////////////////////////////////////////////////
46
IndicatorElement(float x_position,float y_position,INDICATOR_TYPE indicator_type)47 IndicatorElement::IndicatorElement(float x_position, float y_position, INDICATOR_TYPE indicator_type) :
48 _timer(INDICATOR_TIME),
49 _alpha_color(1.0f, 1.0f, 1.0f, 0.0f),
50 _force(0.0f, INITIAL_FORCE),
51 _origin_position(x_position, y_position),
52 _relative_position(0.0f, 0.0f),
53 _use_parallax(false),
54 _indicator_type(indicator_type)
55 {
56 }
57
58
Start()59 void IndicatorElement::Start()
60 {
61 if(!_timer.IsInitial())
62 _timer.Reset();
63 _timer.Run();
64
65 // Reinit the indicator push
66 if (vt_utils::RandomFloat(0.0f, 100.0f) >= 50.0f)
67 _force.x = vt_utils::RandomFloat(30.0f, 60.0f);
68 else
69 _force.x = vt_utils::RandomFloat(-60.0f, -30.0f);
70 _force.y = INITIAL_FORCE;
71 }
72
Update()73 void IndicatorElement::Update()
74 {
75 _timer.Update();
76 _UpdateDrawPosition();
77 _ComputeDrawAlpha();
78 }
79
_UpdateDrawPosition()80 void IndicatorElement::_UpdateDrawPosition()
81 {
82 // the time passed since the last call in ms.
83 float elapsed_ms = static_cast<float>(vt_system::SystemManager->GetUpdateTime());
84 // Use only rational values:
85 // Prevent high jumps after pausing the battle
86 // Prevents too slow animation and cap the movement to the equivalent of a 30 FPS one.
87 const float elapsed_ms_cap = 32.0f;
88 if (elapsed_ms <= 0.0f || elapsed_ms > elapsed_ms_cap)
89 elapsed_ms = elapsed_ms_cap;
90
91 switch(_indicator_type) {
92 case DAMAGE_INDICATOR: {
93 // Indicator gravity appliance in pixels / seconds
94 const float INDICATOR_WEIGHT = 26.0f;
95
96 _force.y -= elapsed_ms / 1000 * INDICATOR_WEIGHT;
97
98 // Compute a potential maximum fall speed
99 if(_force.y < -15.0f)
100 _force.y = -15.0f;
101
102 _relative_position.y += _force.y;
103
104 // Resolve a ground collision
105 if(_relative_position.y <= 0.0f) {
106 _relative_position.y = 0.0f;
107
108 // If the force is very low, the bouncing is over
109 if(std::abs(_force.y) <= INDICATOR_WEIGHT / 10.0f) {
110 _force.y = 0.0f;
111 } else {
112 // Make the object bounce
113 _force.y = -(_force.y * 0.6f);
114 }
115 }
116
117 // Make the object advance only if it still can.
118 if(_relative_position.y > 0.0f)
119 _relative_position.x += (_force.x / 1000 * elapsed_ms);
120 }
121 break;
122
123 default:
124 case HEALING_INDICATOR:
125 case POSITIVE_STATUS_EFFECT_INDICATOR:
126 _relative_position.y += 5.0f / 1000 * elapsed_ms;
127 break;
128 case ITEM_INDICATOR:
129 case NEGATIVE_STATUS_EFFECT_INDICATOR:
130 _relative_position.y -= 5.0f / 1000 * elapsed_ms;
131 break;
132 case TEXT_INDICATOR:
133 // Move vertically
134 _relative_position.x -= 5.0f / 1000 * elapsed_ms;
135 break;
136 }
137 }
138
_ComputeDrawAlpha()139 void IndicatorElement::_ComputeDrawAlpha()
140 {
141 // Timer is not running nor paused so indicator should not be drawn
142 if((_timer.GetState() == vt_system::SYSTEM_TIMER_RUNNING) && (_timer.GetState() == vt_system::SYSTEM_TIMER_PAUSED)) {
143 _alpha_color.SetAlpha(0.0f);
144 }
145 // Timer is in beginning stage and indicator graphic is fading in
146 else if(_timer.GetTimeExpired() < INDICATOR_FADEIN_TIME) {
147 _alpha_color.SetAlpha(static_cast<float>(_timer.GetTimeExpired()) / static_cast<float>(INDICATOR_FADEIN_TIME));
148 }
149 // Timer is in final stage and indicator graphic is fading out
150 else if(_timer.TimeLeft() < INDICATOR_FADEOUT_TIME) {
151 _alpha_color.SetAlpha(static_cast<float>(_timer.TimeLeft()) / static_cast<float>(INDICATOR_FADEOUT_TIME));
152 }
153 // Timer is in middle stage and indicator graphic should be drawn with no transparency
154 else {
155 _alpha_color.SetAlpha(1.0f);
156 }
157 }
158
159 ////////////////////////////////////////////////////////////////////////////////
160 // IndicatorText class
161 ////////////////////////////////////////////////////////////////////////////////
162
IndicatorText(float x_position,float y_position,const std::string & text,const vt_video::TextStyle & style,INDICATOR_TYPE indicator_type)163 IndicatorText::IndicatorText(float x_position, float y_position,
164 const std::string& text, const vt_video::TextStyle& style,
165 INDICATOR_TYPE indicator_type) :
166 IndicatorElement(x_position, y_position, indicator_type),
167 _text_image(text, style)
168 {}
169
170
171
Draw()172 void IndicatorText::Draw()
173 {
174 VideoManager->SetDrawFlags(VIDEO_X_RIGHT, VIDEO_Y_BOTTOM, VIDEO_BLEND, 0);
175 VideoManager->Move(
176 _origin_position.x + _relative_position.x + _text_image.GetWidth() / 2,
177 _origin_position.y - _relative_position.y);
178
179 _text_image.Draw(_alpha_color);
180 }
181
182 ////////////////////////////////////////////////////////////////////////////////
183 // IndicatorImage class
184 ////////////////////////////////////////////////////////////////////////////////
185
IndicatorImage(float x_position,float y_position,const std::string & filename,INDICATOR_TYPE indicator_type)186 IndicatorImage::IndicatorImage(float x_position, float y_position, const std::string &filename,
187 INDICATOR_TYPE indicator_type) :
188 IndicatorElement(x_position, y_position, indicator_type)
189 {
190 if(!_image.Load(filename))
191 PRINT_WARNING << "Failed to load indicator image: " << filename << std::endl;
192 }
193
194
195
IndicatorImage(float x_position,float y_position,const StillImage & image,INDICATOR_TYPE indicator_type)196 IndicatorImage::IndicatorImage(float x_position, float y_position, const StillImage& image,
197 INDICATOR_TYPE indicator_type) :
198 IndicatorElement(x_position, y_position, indicator_type),
199 _image(image)
200 {
201 if (_image.GetFilename().empty())
202 PRINT_WARNING << "Invalid indicator image." << std::endl;
203 }
204
205
Draw()206 void IndicatorImage::Draw()
207 {
208 VideoManager->SetDrawFlags(VIDEO_X_RIGHT, VIDEO_Y_BOTTOM, VIDEO_BLEND, 0);
209 VideoManager->Move(_origin_position.x + _relative_position.x,
210 _origin_position.y - _relative_position.y);
211
212 _image.Draw(_alpha_color);
213 }
214
215 ////////////////////////////////////////////////////////////////////////////////
216 // IndicatorBlendedImage class
217 ////////////////////////////////////////////////////////////////////////////////
218
IndicatorBlendedImage(float x_position,float y_position,const std::string & first_filename,const std::string & second_filename,INDICATOR_TYPE indicator_type)219 IndicatorBlendedImage::IndicatorBlendedImage(float x_position, float y_position,
220 const std::string& first_filename,
221 const std::string& second_filename,
222 INDICATOR_TYPE indicator_type) :
223 IndicatorElement(x_position, y_position, indicator_type),
224 _second_alpha_color(1.0f, 1.0f, 1.0f, 0.0f)
225 {
226 if(!_first_image.Load(first_filename))
227 PRINT_WARNING << "Invalid first indicator image." << std::endl;
228 if(!_second_image.Load(second_filename))
229 PRINT_WARNING << "Invalid second indicator image." << std::endl;
230 }
231
232
233
IndicatorBlendedImage(float x_position,float y_position,const StillImage & first_image,const StillImage & second_image,INDICATOR_TYPE indicator_type)234 IndicatorBlendedImage::IndicatorBlendedImage(float x_position, float y_position,
235 const StillImage& first_image,
236 const StillImage& second_image,
237 INDICATOR_TYPE indicator_type) :
238 IndicatorElement(x_position, y_position, indicator_type),
239 _first_image(first_image),
240 _second_image(second_image),
241 _second_alpha_color(1.0f, 1.0f, 1.0f, 0.0f)
242 {
243 if(_first_image.GetFilename().empty())
244 PRINT_WARNING << "Invalid first indicator image." << std::endl;
245 if(_second_image.GetFilename().empty())
246 PRINT_WARNING << "Invalid first indicator image." << std::endl;
247 }
248
249
250
Draw()251 void IndicatorBlendedImage::Draw()
252 {
253 VideoManager->SetDrawFlags(VIDEO_X_RIGHT, VIDEO_Y_BOTTOM, VIDEO_BLEND, 0);
254 VideoManager->Move(_origin_position.x + _relative_position.x,
255 _origin_position.y - _relative_position.y);
256
257 // Initial fade in of first image
258 if(_timer.GetTimeExpired() <= INDICATOR_FADEIN_TIME) {
259 _first_image.Draw(_alpha_color);
260 }
261 // Opaque draw of first image
262 else if(_timer.GetTimeExpired() <= INDICATOR_TIME / 4) {
263 _first_image.Draw();
264 }
265 // Blended draw of first and second images
266 else if(_timer.GetTimeExpired() <= INDICATOR_TIME / 2) {
267 _alpha_color.SetAlpha(static_cast<float>((INDICATOR_TIME / 2) - _timer.GetTimeExpired())
268 / static_cast<float>(1000));
269 _second_alpha_color.SetAlpha(1.0f - _alpha_color.GetAlpha());
270 _first_image.Draw(_alpha_color);
271 _second_image.Draw(_second_alpha_color);
272 }
273 // Opaque draw of second image
274 else if(_timer.GetTimeExpired() <= INDICATOR_TIME / 3 * 2) {
275 _second_image.Draw();
276 }
277 // Final fade out of second image
278 else { // <= end
279 _second_image.Draw(_alpha_color);
280 }
281 }
282
283 ////////////////////////////////////////////////////////////////////////////////
284 // IndicatorSupervisor class
285 ////////////////////////////////////////////////////////////////////////////////
286
~IndicatorSupervisor()287 IndicatorSupervisor::~IndicatorSupervisor()
288 {
289 for(uint32_t i = 0; i < _wait_queue.size(); ++i)
290 delete _wait_queue[i];
291 _wait_queue.clear();
292
293 for(uint32_t i = 0; i < _active_queue.size(); ++i)
294 delete _active_queue[i];
295 _active_queue.clear();
296
297 for(uint32_t i = 0; i < _short_notices.size(); ++i)
298 delete _short_notices[i];
299 _short_notices.clear();
300 }
301
IndicatorCompare(IndicatorElement * one,IndicatorElement * another)302 static bool IndicatorCompare(IndicatorElement *one, IndicatorElement *another)
303 {
304 return (one->GetXOrigin() > another->GetXOrigin());
305 }
306
Update()307 void IndicatorSupervisor::Update()
308 {
309 // Update all active elements
310 for(uint32_t i = 0; i < _active_queue.size(); i++)
311 _active_queue[i]->Update();
312
313 // Remove all expired elements from the active queue
314 while(_active_queue.empty() == false) {
315 if(_active_queue.front()->IsExpired()) {
316 delete _active_queue.front();
317 _active_queue.pop_front();
318 } else {
319 // If the front element is not expired, no other elements should be expired either
320 break;
321 }
322 }
323
324 bool must_sort = false;
325 while(!_wait_queue.empty()) {
326
327 // Update the element position if it is overlapping another one.
328 _wait_queue.front()->Start(); // Setup the indicator's coords
329 while(_FixPotentialIndicatorOverlapping(_wait_queue.front()))
330 {}
331
332 _active_queue.push_back(_wait_queue.front());
333 _wait_queue.pop_front();
334 must_sort = true;
335 }
336
337 // Sort the indicator display in that case
338 if(must_sort)
339 std::sort(_active_queue.begin(), _active_queue.end(), IndicatorCompare);
340
341 if (_short_notices.empty())
342 return;
343
344 // Update only the first ShortNoticeWindow
345 vt_common::ShortNoticeWindow* msg_win = _short_notices.front();
346 msg_win->Update(vt_system::SystemManager->GetUpdateTime());
347
348 // and delete it if it was hidden.
349 if (!msg_win->IsVisible()) {
350 delete msg_win;
351 _short_notices.pop_front();
352
353 // Show the next timed message window
354 if (!_short_notices.empty())
355 _short_notices.front()->Show();
356 }
357 }
358
_FixPotentialIndicatorOverlapping(IndicatorElement * element)359 bool IndicatorSupervisor::_FixPotentialIndicatorOverlapping(IndicatorElement* element)
360 {
361 if(!element)
362 return false; // No overlapping
363
364 IndicatorElement *overlapped_element = 0;
365
366 // Get potential overlapped indicators
367 for(std::deque<IndicatorElement *>::iterator it = _active_queue.begin(),
368 it_end = _active_queue.end(); it != it_end; ++it) {
369 if((*it)->GetXOrigin() == element->GetXOrigin() &&
370 (*it)->GetYOrigin() == element->GetYOrigin() &&
371 (*it) != element) {
372 overlapped_element = *it;
373 break;
374 }
375 }
376
377 if(!overlapped_element)
378 return false; // No overlapping
379
380 // Move the next indicator a bit depending on its type
381 if(element->GetType() == DAMAGE_INDICATOR) {
382 element->SetXOrigin(element->GetXOrigin() + 1.0f);
383 }
384 else if (element->GetType() == HEALING_INDICATOR) {
385 element->SetXOrigin(element->GetXOrigin() + 15.0f);
386 element->SetYOrigin(element->GetYOrigin() + 15.0f);
387 }
388 else {
389 element->SetXOrigin(element->GetXOrigin() + 15.0f);
390 }
391 return true;
392 }
393
Draw()394 void IndicatorSupervisor::Draw()
395 {
396 for(uint32_t i = 0; i < _active_queue.size(); i++)
397 _active_queue[i]->Draw();
398
399 if (_short_notices.empty())
400 return;
401 _short_notices.front()->Draw();
402 }
403
AddDamageIndicator(float x_position,float y_position,uint32_t amount,const TextStyle & style,bool use_parallax)404 void IndicatorSupervisor::AddDamageIndicator(float x_position, float y_position,
405 uint32_t amount, const TextStyle& style, bool use_parallax)
406 {
407 if (amount == 0)
408 return;
409
410 std::string text = vt_utils::NumberToString(amount);
411
412 IndicatorText* indicator = new IndicatorText(x_position, y_position, text, style, DAMAGE_INDICATOR);
413 indicator->SetUseParallax(use_parallax);
414
415 _wait_queue.push_back(indicator);
416 }
417
418
419
AddHealingIndicator(float x_position,float y_position,uint32_t amount,const TextStyle & style,bool use_parallax)420 void IndicatorSupervisor::AddHealingIndicator(float x_position, float y_position,
421 uint32_t amount, const TextStyle& style, bool use_parallax)
422 {
423 if(amount == 0)
424 return;
425
426 std::string text = vt_utils::NumberToString(amount);
427
428 IndicatorText* indicator = new IndicatorText(x_position, y_position, text, style, HEALING_INDICATOR);
429 indicator->SetUseParallax(use_parallax);
430
431 _wait_queue.push_back(indicator);
432 }
433
AddMissIndicator(float x_position,float y_position)434 void IndicatorSupervisor::AddMissIndicator(float x_position, float y_position)
435 {
436 std::string text = vt_system::Translate("Miss");
437 TextStyle style("text24", Color::white);
438 _wait_queue.push_back(new IndicatorText(x_position, y_position, text, style, TEXT_INDICATOR));
439 }
440
AddStatusIndicator(float x_position,float y_position,vt_global::GLOBAL_STATUS status,vt_global::GLOBAL_INTENSITY old_intensity,vt_global::GLOBAL_INTENSITY new_intensity)441 void IndicatorSupervisor::AddStatusIndicator(float x_position, float y_position,
442 vt_global::GLOBAL_STATUS status, vt_global::GLOBAL_INTENSITY old_intensity,
443 vt_global::GLOBAL_INTENSITY new_intensity)
444 {
445 // If the status and intensity has not changed, only a single status icon needs to be used
446 if(old_intensity == new_intensity) {
447 StillImage *image = vt_global::GlobalManager->Media().GetStatusIcon(status, new_intensity);
448 _wait_queue.push_back(new IndicatorImage(x_position, y_position, *image, POSITIVE_STATUS_EFFECT_INDICATOR));
449 }
450 // Otherwise two status icons need to be used in the indicator image
451 else {
452 StillImage *first_image = vt_global::GlobalManager->Media().GetStatusIcon(status, old_intensity);
453 StillImage *second_image = vt_global::GlobalManager->Media().GetStatusIcon(status, new_intensity);
454 INDICATOR_TYPE indicator_type = (old_intensity <= new_intensity) ?
455 POSITIVE_STATUS_EFFECT_INDICATOR : NEGATIVE_STATUS_EFFECT_INDICATOR;
456 _wait_queue.push_back(new IndicatorBlendedImage(x_position, y_position, *first_image, *second_image, indicator_type));
457 }
458 }
459
AddItemIndicator(float x_position,float y_position,const vt_global::GlobalItem & item)460 void IndicatorSupervisor::AddItemIndicator(float x_position, float y_position, const vt_global::GlobalItem& item)
461 {
462 _wait_queue.push_back(new IndicatorImage(x_position, y_position,
463 item.GetIconImage(),
464 ITEM_INDICATOR));
465 }
466
AddParallax(float x_parallax,float y_parallax)467 void IndicatorSupervisor::AddParallax(float x_parallax, float y_parallax)
468 {
469 for(std::deque<IndicatorElement *>::iterator it = _active_queue.begin(),
470 it_end = _active_queue.end(); it != it_end; ++it) {
471 IndicatorElement* element = *it;
472 if (!element->UseParallax())
473 continue;
474 element->SetXOrigin(element->GetXOrigin() + x_parallax);
475 element->SetYOrigin(element->GetYOrigin() + y_parallax);
476 }
477 }
478
AddShortNotice(const vt_utils::ustring & message,const std::string & icon_image_filename,uint32_t display_time)479 void IndicatorSupervisor::AddShortNotice(const vt_utils::ustring& message,
480 const std::string& icon_image_filename,
481 uint32_t display_time)
482 {
483 vt_common::ShortNoticeWindow* msg_win = nullptr;
484 msg_win = new vt_common::ShortNoticeWindow(message, icon_image_filename, display_time);
485 _short_notices.push_back(msg_win);
486 }
487
488 } // namespace vt_mode_manager
489