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