1 ////////////////////////////////////////////////////////////////////////////////
2 //            Copyright (C) 2014-2016 by Bertram (Valyria Tear)
3 //                         All Rights Reserved
4 //
5 // This code is licensed under the GNU GPL version 2. It is free software
6 // and you may modify it and/or redistribute it under the terms of this license.
7 // See https://www.gnu.org/copyleft/gpl.html for details.
8 ////////////////////////////////////////////////////////////////////////////////
9 
10 /** ****************************************************************************
11 *** \file    map_status_effects.cpp
12 *** \author  Yohann Ferreira, yohann ferreira orange fr
13 *** \brief   Source file for map active status effects handling.
14 *** ***************************************************************************/
15 
16 #include "modes/map/map_status_effects.h"
17 
18 #include "modes/map/map_mode.h"
19 
20 #include "script/script_read.h"
21 
22 #include "common/global/global.h"
23 #include "common/global/actors/global_character.h"
24 
25 #include "engine/video/video.h"
26 
27 using namespace vt_global;
28 
29 namespace vt_map
30 {
31 
32 namespace private_map
33 {
34 
35 //! \brief Gives the X effect position from the given index
_GetEffectXPositionFromCharacterIndex(uint32_t index)36 float _GetEffectXPositionFromCharacterIndex(uint32_t index)
37 {
38     return 90.0f + (float)(index * 130);
39 }
40 
41 //! \brief Where on screen (on y-axis) the active status effects icon should be displayed
42 const float EFFECTS_Y_POS = 750.0f;
43 
44 ////////////////////////////////////////////////////////////////////////////////
45 // PassiveMapStatusEffect class
46 ////////////////////////////////////////////////////////////////////////////////
47 
PassiveMapStatusEffect(vt_global::GlobalCharacter * character,vt_global::GLOBAL_STATUS type,vt_global::GLOBAL_INTENSITY intensity)48 PassiveMapStatusEffect::PassiveMapStatusEffect(vt_global::GlobalCharacter* character,
49                                                vt_global::GLOBAL_STATUS type,
50                                                vt_global::GLOBAL_INTENSITY intensity):
51     GlobalStatusEffect(type, intensity),
52     _affected_character(character),
53     _icon_image(nullptr)
54 {
55     // Check that the constructor arguments are valid
56     if((type <= GLOBAL_STATUS_INVALID) || (type >= GLOBAL_STATUS_TOTAL)) {
57         PRINT_WARNING << "The constructor received an invalid type argument: " << type << std::endl;
58         return;
59     }
60     if((intensity <= GLOBAL_INTENSITY_INVALID) || (intensity >= GLOBAL_INTENSITY_TOTAL)) {
61         PRINT_WARNING << "The constructor received an invalid intensity argument: " << intensity << std::endl;
62         return;
63     }
64     if(character == nullptr) {
65         PRINT_WARNING << "The constructor received nullptr character argument" << std::endl;
66         return;
67     }
68 
69     // Make sure that a table entry exists for this status element
70     uint32_t table_id = static_cast<uint32_t>(type);
71     vt_script::ReadScriptDescriptor &script_file = vt_global::GlobalManager->GetStatusEffectsScript();
72     if(!script_file.OpenTable(table_id)) {
73         PRINT_WARNING << "Lua definition file contained no entry for status effect: " << table_id << std::endl;
74         return;
75     }
76 
77     // Read in the status effect's property data
78     _name = script_file.ReadString("name");
79 
80     if(script_file.DoesFunctionExist("MapUpdatePassive")) {
81         _update_passive_function = script_file.ReadFunctionPointer("MapUpdatePassive");
82     } else {
83         PRINT_WARNING << "No MapUpdatePassive() function found in Lua definition file for status: " << table_id << std::endl;
84     }
85 
86     script_file.CloseTable(); // table_id
87 
88     if(script_file.IsErrorDetected()) {
89         PRINT_WARNING << "one or more errors occurred while reading status effect data - they are listed below"
90             << std::endl << script_file.GetErrorMessages() << std::endl;
91     }
92 
93     _icon_image = GlobalManager->Media().GetStatusIcon(_type, _intensity);
94 }
95 
96 ////////////////////////////////////////////////////////////////////////////////
97 // ActiveMapStatusEffect class
98 ////////////////////////////////////////////////////////////////////////////////
99 
ActiveMapStatusEffect(vt_global::GlobalCharacter * character,vt_global::GLOBAL_STATUS type,vt_global::GLOBAL_INTENSITY intensity,uint32_t duration)100 ActiveMapStatusEffect::ActiveMapStatusEffect(vt_global::GlobalCharacter* character,
101                                              vt_global::GLOBAL_STATUS type,
102                                              vt_global::GLOBAL_INTENSITY intensity,
103                                              uint32_t duration) :
104     GlobalStatusEffect(type, intensity),
105     _affected_character(character),
106     _timer(0),
107     _icon_image(nullptr),
108     _intensity_changed(false)
109 {
110     // Check that the constructor arguments are valid
111     if((type <= GLOBAL_STATUS_INVALID) || (type >= GLOBAL_STATUS_TOTAL)) {
112         PRINT_WARNING << "The constructor received an invalid type argument: " << type << std::endl;
113         return;
114     }
115     if((intensity <= GLOBAL_INTENSITY_INVALID) || (intensity >= GLOBAL_INTENSITY_TOTAL)) {
116         PRINT_WARNING << "The constructor received an invalid intensity argument: " << intensity << std::endl;
117         return;
118     }
119     if(character == nullptr) {
120         PRINT_WARNING << "The constructor received nullptr character argument" << std::endl;
121         return;
122     }
123 
124     // Make sure that a table entry exists for this status element
125     uint32_t table_id = static_cast<uint32_t>(type);
126     vt_script::ReadScriptDescriptor &script_file = vt_global::GlobalManager->GetStatusEffectsScript();
127     if(!script_file.OpenTable(table_id)) {
128         PRINT_WARNING << "Lua definition file contained no entry for status effect: " << table_id << std::endl;
129         return;
130     }
131 
132     // Read in the status effect's property data
133     _name = script_file.ReadString("name");
134 
135     // Read the fall back duration when none is given.
136     if(duration == 0)
137         duration = script_file.ReadUInt("default_duration");
138     _timer.SetDuration(duration);
139 
140     if(script_file.DoesFunctionExist("MapApply")) {
141         _apply_function = script_file.ReadFunctionPointer("MapApply");
142     } else {
143         PRINT_WARNING << "No MapApply() function found in Lua definition file for status: " << table_id << std::endl;
144     }
145 
146     if(script_file.DoesFunctionExist("MapUpdate")) {
147         _update_function = script_file.ReadFunctionPointer("MapUpdate");
148     } else {
149         PRINT_WARNING << "No MapUpdate() function found in Lua definition file for status: " << table_id << std::endl;
150     }
151 
152     if(script_file.DoesFunctionExist("MapRemove")) {
153         _remove_function = script_file.ReadFunctionPointer("MapRemove");
154     } else {
155         PRINT_WARNING << "No MapRemove() function found in Lua definition file for status: " << table_id << std::endl;
156     }
157     script_file.CloseTable(); // table_id
158 
159     if(script_file.IsErrorDetected()) {
160         PRINT_WARNING << "one or more errors occurred while reading status effect data - they are listed below"
161             << std::endl << script_file.GetErrorMessages() << std::endl;
162     }
163 
164     // Init the effect timer
165     _timer.EnableManualUpdate();
166     _timer.Reset();
167     _timer.Run();
168 
169     _icon_image = GlobalManager->Media().GetStatusIcon(_type, _intensity);
170 }
171 
SetIntensity(vt_global::GLOBAL_INTENSITY intensity)172 void ActiveMapStatusEffect::SetIntensity(vt_global::GLOBAL_INTENSITY intensity)
173 {
174     if((intensity <= GLOBAL_INTENSITY_INVALID) || (intensity >= GLOBAL_INTENSITY_TOTAL)) {
175         PRINT_WARNING << "Attempted to set status effect to invalid intensity: " << intensity << std::endl;
176         return;
177     }
178 
179     bool no_intensity_change = (_intensity == intensity);
180     _intensity = intensity;
181     _ProcessIntensityChange(no_intensity_change);
182 }
183 
IncrementIntensity(uint8_t amount)184 bool ActiveMapStatusEffect::IncrementIntensity(uint8_t amount)
185 {
186     bool change = GlobalStatusEffect::IncrementIntensity(amount);
187     _ProcessIntensityChange(!change);
188     return change;
189 }
190 
DecrementIntensity(uint8_t amount)191 bool ActiveMapStatusEffect::DecrementIntensity(uint8_t amount)
192 {
193     bool change = GlobalStatusEffect::DecrementIntensity(amount);
194     _ProcessIntensityChange(!change);
195     return change;
196 }
197 
_ProcessIntensityChange(bool reset_timer_only)198 void ActiveMapStatusEffect::_ProcessIntensityChange(bool reset_timer_only)
199 {
200     _timer.Reset();
201     _timer.Run();
202 
203     if(reset_timer_only)
204         return;
205 
206     _intensity_changed = true;
207     _icon_image = vt_global::GlobalManager->Media().GetStatusIcon(_type, _intensity);
208 }
209 
210 ////////////////////////////////////////////////////////////////////////////////
211 // CharacterIndication class
212 ////////////////////////////////////////////////////////////////////////////////
213 
CharacterIndication(vt_global::GlobalCharacter * character,float x_position,float y_position)214 CharacterIndication::CharacterIndication(vt_global::GlobalCharacter* character,
215                                          float x_position, float y_position):
216         _position(x_position, y_position),
217         _image_alpha(0.0f),
218         _fade_in(false),
219         _fade_out(false),
220         _display_time(0),
221         _global_character(character)
222 {
223     // Loads a copy of the portrait.
224     if (character)
225         _portrait.Load(character->GetPortrait().GetFilename());
226 
227     if (!_portrait.GetFilename().empty())
228         _portrait.SetHeightKeepRatio(65.0f);
229 }
230 
Update()231 void CharacterIndication::Update()
232 {
233     uint32_t elapsed_time = vt_system::SystemManager->GetUpdateTime();
234     // Apply fading
235     if (_fade_out) {
236         _image_alpha -= 0.005f * (float)elapsed_time;
237         if (_image_alpha <= 0.0f) {
238             _image_alpha = 0.0f;
239             _fade_out = false;
240         }
241     }
242     else if (_fade_in) {
243         _image_alpha += 0.005f * (float)elapsed_time;
244         if (_image_alpha >= 1.0f) {
245             _image_alpha = 1.0f;
246             _fade_in = false;
247         }
248     }
249 
250     // Check time limit
251     if (_display_time <= 0) {
252         _display_time = 0;
253         FadeOut();
254         return;
255     }
256 
257     // Update display time otherwise
258     _display_time -= (int32_t)elapsed_time;
259 }
260 
Draw()261 void CharacterIndication::Draw() {
262     if (_image_alpha <= 0.0f)
263         return;
264 
265     vt_video::VideoManager->SetDrawFlags(vt_video::VIDEO_X_RIGHT, vt_video::VIDEO_Y_BOTTOM, vt_video::VIDEO_BLEND, 0);
266     vt_video::VideoManager->Move(_position.x, _position.y);
267 
268     _portrait.Draw(vt_video::Color(1.0f, 1.0f, 1.0f, _image_alpha));
269 }
270 
271 ////////////////////////////////////////////////////////////////////////////////
272 // MapStatusEffectsSupervisor class
273 ////////////////////////////////////////////////////////////////////////////////
274 
MapStatusEffectsSupervisor()275 MapStatusEffectsSupervisor::MapStatusEffectsSupervisor()
276 {
277     LoadStatusEffects();
278 }
279 
~MapStatusEffectsSupervisor()280 MapStatusEffectsSupervisor::~MapStatusEffectsSupervisor()
281 {
282     SaveActiveStatusEffects();
283 }
284 
LoadStatusEffects()285 void MapStatusEffectsSupervisor::LoadStatusEffects()
286 {
287     // First, wipe out every old data
288     _active_status_effects.clear();
289     _equipment_status_effects.clear();
290     _characters_portraits.clear();
291 
292     std::vector<GlobalCharacter*>* characters = GlobalManager->GetCharacterHandler().GetOrderedCharacters();
293     if (!characters)
294         return;
295 
296     // For each character, we load the status effects data
297     for (uint32_t i = 0; i < characters->size(); ++i) {
298         GlobalCharacter* character = (*characters)[i];
299 
300         if (!character)
301             continue;
302 
303         // Loads or reloads the Character portraits positions (in case the party order has changed)
304         float x_pos = _GetEffectXPositionFromCharacterIndex(i);
305         _characters_portraits.push_back(CharacterIndication(character, x_pos, EFFECTS_Y_POS));
306 
307         // passive effects
308         const std::vector<GLOBAL_INTENSITY>& passives = character->GetEquipementStatusEffects();
309         for (uint32_t j = 0; j < passives.size(); ++j) {
310             GLOBAL_STATUS status = (vt_global::GLOBAL_STATUS)j;
311             GLOBAL_INTENSITY intensity = passives.at(j);
312 
313             if (status < GLOBAL_STATUS_TOTAL
314                     && intensity != GLOBAL_INTENSITY_INVALID
315                     && intensity != GLOBAL_INTENSITY_NEUTRAL) {
316                 _AddPassiveStatusEffect(character, status, intensity);
317             }
318         }
319 
320         // active effects
321         const std::vector<ActiveStatusEffect>& actives = character->GetActiveStatusEffects();
322         for (uint32_t j = 0; j < actives.size(); ++j) {
323             GLOBAL_STATUS status = actives.at(j).GetEffect();
324             GLOBAL_INTENSITY intensity = actives.at(j).GetIntensity();
325 
326             if (status < GLOBAL_STATUS_TOTAL
327                     && intensity != GLOBAL_INTENSITY_INVALID
328                     && intensity != GLOBAL_INTENSITY_NEUTRAL) {
329                 _AddActiveStatusEffect(character, status, intensity,
330                                        actives.at(j).GetEffectTime(),
331                                        actives.at(j).GetElapsedTime());
332             }
333         }
334     }
335 }
336 
SaveActiveStatusEffects()337 void MapStatusEffectsSupervisor::SaveActiveStatusEffects()
338 {
339     // first, we clear the old data from the characters
340     std::vector<GlobalCharacter*>* characters = GlobalManager->GetCharacterHandler().GetOrderedCharacters();
341     if (!characters)
342         return;
343 
344     for (uint32_t i = 0; i < characters->size(); ++i) {
345         GlobalCharacter* character = (*characters)[i];
346 
347         if (!character)
348             continue;
349 
350         character->ResetActiveStatusEffects();
351     }
352 
353     // Then, we copy every active status effects back to the affected character.
354     for (uint32_t i = 0; i < _active_status_effects.size(); ++i) {
355         ActiveMapStatusEffect& effect = _active_status_effects[i];
356         if (effect.GetType() == GLOBAL_STATUS_INVALID)
357             continue;
358 
359         GlobalCharacter* character = effect.GetAffectedCharacter();
360         if (!character)
361             continue;
362 
363         vt_system::SystemTimer* timer = effect.GetTimer();
364         character->SetActiveStatusEffect(effect.GetType(), effect.GetIntensity(),
365                                          timer->GetDuration(), timer->GetTimeExpired());
366     }
367 }
368 
_UpdatePassive()369 void MapStatusEffectsSupervisor::_UpdatePassive()
370 {
371     for(uint32_t i = 0; i < _equipment_status_effects.size(); ++i) {
372         PassiveMapStatusEffect& effect = _equipment_status_effects.at(i);
373 
374         if (!effect.GetUpdatePassiveFunction().is_valid())
375             continue;
376 
377         // Update the update timer if it is running
378         vt_system::SystemTimer *update_timer = effect.GetUpdateTimer();
379         bool use_update_timer = effect.IsUsingUpdateTimer();
380         if (use_update_timer) {
381             uint32_t update_time = vt_system::SystemManager->GetUpdateTime();
382             update_timer->Update(update_time);
383         }
384 
385         if (!use_update_timer || update_timer->IsFinished()) {
386 
387             // Call the update passive function
388             try {
389                 luabind::call_function<void>(effect.GetUpdatePassiveFunction(), effect.GetAffectedCharacter(), effect.GetIntensity());
390             } catch(const luabind::error& e) {
391                 PRINT_ERROR << "Error while loading status effect MapUpdatePassive() function" << std::endl;
392                 vt_script::ScriptManager->HandleLuaError(e);
393             } catch(const luabind::cast_failed& e) {
394                 PRINT_ERROR << "Error while loading status effect MapUpdatePassive() function" << std::endl;
395                 vt_script::ScriptManager->HandleCastError(e);
396             }
397 
398             // Restart the update timer when needed
399             if (use_update_timer) {
400                 update_timer->Reset();
401                 update_timer->Run();
402             }
403         }
404     }
405 }
406 
UpdateEffects()407 void MapStatusEffectsSupervisor::UpdateEffects()
408 {
409     // Update the timers and state for all active status effects
410     std::vector<ActiveMapStatusEffect>::iterator it = _active_status_effects.begin();
411     for(; it != _active_status_effects.end();) {
412         ActiveMapStatusEffect& effect = *it;
413         if(!effect.IsActive()) {
414             // Remove from loop
415             it = _active_status_effects.erase(it);
416             continue;
417         }
418 
419         bool effect_removed = false;
420 
421         vt_system::SystemTimer* effect_timer = effect.GetTimer();
422         vt_system::SystemTimer* update_timer = effect.GetUpdateTimer();
423 
424         // Update the effect time while taking in account the battle speed
425         uint32_t update_time = vt_system::SystemManager->GetUpdateTime();
426         effect_timer->Update(update_time);
427 
428         // Update the update timer if it is running
429         bool use_update_timer = effect.IsUsingUpdateTimer();
430         if (use_update_timer)
431             update_timer->Update(update_time);
432 
433         // Decrease the intensity of the status by one level when its timer expires. This may result in
434         // the status effect being removed from the actor if its intensity changes to the neutral level.
435         if(effect_timer->IsFinished()) {
436             // If the intensity of the effect is at its weakest, the call that follows will remove the effect from the actor
437             effect_removed = (effect.GetIntensity() == GLOBAL_INTENSITY_POS_LESSER
438                               || effect.GetIntensity() == GLOBAL_INTENSITY_NEG_LESSER);
439 
440             // As the effect is fading, we divide the effect duration time per 2, with at least 1 second of duration.
441             // This is done to give more a fading out style onto the effect and not to advantage/disadvantage the target
442             // too much.
443             uint32_t duration = effect_timer->GetDuration() / 2;
444             effect_timer->SetDuration(duration < 1000 ? 1000 : duration);
445 
446             if (effect.GetIntensity() > GLOBAL_INTENSITY_NEUTRAL) {
447                 ChangeActiveStatusEffect(effect, GLOBAL_INTENSITY_NEG_LESSER, duration);
448             }
449             else {
450                 ChangeActiveStatusEffect(effect, GLOBAL_INTENSITY_POS_LESSER, duration);
451             }
452         }
453 
454         if (effect_removed) {
455             // Remove from loop
456             it = _active_status_effects.erase(it);
457             continue;
458         }
459 
460         // Update the effect according to the script function
461         if (!use_update_timer || update_timer->IsFinished()) {
462             if (effect.GetUpdateFunction().is_valid()) {
463 
464                 try {
465                     luabind::call_function<void>(effect.GetUpdateFunction(), effect);
466                 } catch(const luabind::error& e) {
467                     PRINT_ERROR << "Error while loading status effect Update function" << std::endl;
468                     vt_script::ScriptManager->HandleLuaError(e);
469                 } catch(const luabind::cast_failed& e) {
470                     PRINT_ERROR << "Error while loading status effect Update function" << std::endl;
471                     vt_script::ScriptManager->HandleCastError(e);
472                 }
473             }
474             else {
475                 PRINT_WARNING << "No status effect Update function defined." << std::endl;
476             }
477             // If the character has his effects removed because of the effect update (when dying)
478             // The effect doesn't exist anymore, so we have to check this here.
479             if(!effect.IsActive()) {
480                 // Remove from loop
481                 it = _active_status_effects.erase(it);
482                 continue;
483             }
484 
485             effect.ResetIntensityChanged();
486 
487             // Restart the update timer when needed
488             if (use_update_timer) {
489                 update_timer->Reset();
490                 update_timer->Run();
491             }
492         }
493 
494         ++it;
495     }
496 
497     _UpdatePassive();
498 }
499 
UpdatePortraits()500 void MapStatusEffectsSupervisor::UpdatePortraits()
501 {
502     // Update portrait indicators
503     for (uint32_t i = 0; i < _characters_portraits.size(); ++i)
504         _characters_portraits[i].Update();
505 }
506 
Draw()507 void MapStatusEffectsSupervisor::Draw()
508 {
509     // Draw character portraits shown when effects changes are triggered.
510     for (uint32_t i = 0; i < _characters_portraits.size(); ++i)
511         _characters_portraits[i].Draw();
512 }
513 
ChangeActiveStatusEffect(GlobalCharacter * character,vt_global::GLOBAL_STATUS status_type,vt_global::GLOBAL_INTENSITY intensity,uint32_t duration,uint32_t elapsed_time,bool display_change)514 bool MapStatusEffectsSupervisor::ChangeActiveStatusEffect(GlobalCharacter* character,
515                                                           vt_global::GLOBAL_STATUS status_type,
516                                                           vt_global::GLOBAL_INTENSITY intensity,
517                                                           uint32_t duration, uint32_t elapsed_time,
518                                                           bool display_change)
519 {
520     if (!character)
521         return false;
522 
523     if (intensity == GLOBAL_INTENSITY_NEUTRAL
524             || intensity <= GLOBAL_INTENSITY_INVALID
525             || intensity >= GLOBAL_INTENSITY_TOTAL)
526         return false;
527 
528     for (uint32_t i = 0; i < _active_status_effects.size(); ++i) {
529         ActiveMapStatusEffect& active_effect = _active_status_effects[i];
530 
531         if (active_effect.GetType() != status_type)
532             continue;
533         if (active_effect.GetAffectedCharacter() != character)
534             continue;
535 
536         // If the effect is found, we apply the new change on it.
537         return ChangeActiveStatusEffect(active_effect, intensity, duration, elapsed_time, display_change);
538     }
539 
540     // Add a new status effect
541     _AddActiveStatusEffect(character, status_type, intensity, duration);
542 
543     if (!display_change)
544         return true;
545 
546     vt_mode_manager::IndicatorSupervisor& indicator = MapMode::CurrentInstance()->GetIndicatorSupervisor();
547     indicator.AddStatusIndicator(_GetEffectAnimationXPosition(character), EFFECTS_Y_POS,
548                                  status_type, GLOBAL_INTENSITY_NEUTRAL, intensity);
549     _MakeCharacterPortraitAppear(character, vt_mode_manager::INDICATOR_TIME); // default time in milliseconds
550     return true;
551 }
552 
ChangeActiveStatusEffect(ActiveMapStatusEffect & active_effect,GLOBAL_INTENSITY intensity,uint32_t duration,uint32_t elapsed_time,bool display_change)553 bool MapStatusEffectsSupervisor::ChangeActiveStatusEffect(ActiveMapStatusEffect& active_effect,
554                                                           GLOBAL_INTENSITY intensity,
555                                                           uint32_t duration, uint32_t elapsed_time,
556                                                           bool display_change)
557 {
558     if (!active_effect.IsActive())
559         return false;
560 
561     // Determine if we are attempting to increment or decrement the intensity of this status
562     bool increase_intensity = false;
563     if((intensity <= GLOBAL_INTENSITY_POS_EXTREME) && (intensity > GLOBAL_INTENSITY_NEUTRAL))
564         increase_intensity = true;
565 
566     // Holds the unsigned amount of change in intensity in either a positive or negative degree
567     uint8_t intensity_change = abs(static_cast<int8_t>(intensity));
568 
569     // Used to determine the intensity change of the effect.
570     GLOBAL_INTENSITY new_intensity = GLOBAL_INTENSITY_INVALID;
571 
572     // Set the previous status and intensity return values to match the active effect, if one was found to exist
573     GLOBAL_INTENSITY previous_intensity = active_effect.GetIntensity();
574 
575     // Set the coordinates of the status effect next to the corresponding portrait
576     vt_mode_manager::IndicatorSupervisor& indicator = MapMode::CurrentInstance()->GetIndicatorSupervisor();
577     vt_global::GlobalCharacter* character = active_effect.GetAffectedCharacter();
578     float x_pos = _GetEffectAnimationXPosition(character);
579 
580     // Perform status changes according to the previously determined information
581     if(active_effect.IsActive()) {
582         if (increase_intensity)
583             active_effect.IncrementIntensity(intensity_change);
584         else
585             active_effect.DecrementIntensity(intensity_change);
586 
587         new_intensity = active_effect.GetIntensity();
588 
589         // If the status was decremented to the neutral level, this means it is no longer active and should be removed
590         // NOTE: The actual removal of the effect will be done in the Update() loop.
591         if(new_intensity == GLOBAL_INTENSITY_NEUTRAL)
592             _RemoveActiveStatusEffect(active_effect);
593 
594         if (!display_change)
595             return true;
596 
597         indicator.AddStatusIndicator(x_pos, EFFECTS_Y_POS, active_effect.GetType(), previous_intensity, new_intensity);
598         _MakeCharacterPortraitAppear(character, vt_mode_manager::INDICATOR_TIME);
599         return true;
600     }
601     else {
602         _AddActiveStatusEffect(active_effect.GetAffectedCharacter(), active_effect.GetType(), intensity, duration, elapsed_time);
603         new_intensity = intensity;
604 
605         if (!display_change)
606             return true;
607 
608         indicator.AddStatusIndicator(x_pos, EFFECTS_Y_POS, active_effect.GetType(), previous_intensity, new_intensity);
609         _MakeCharacterPortraitAppear(character, vt_mode_manager::INDICATOR_TIME);
610         return true;
611     }
612 
613     return false;
614 } // bool MapStatusEffectsSupervisor::ChangeActiveStatusEffect( ... )
615 
GetActiveStatusEffectIntensity(GlobalCharacter * character,GLOBAL_STATUS status_type) const616 GLOBAL_INTENSITY MapStatusEffectsSupervisor::GetActiveStatusEffectIntensity(GlobalCharacter* character,
617                                                                             GLOBAL_STATUS status_type) const
618 {
619     if (character == nullptr)
620         return GLOBAL_INTENSITY_INVALID;
621 
622     if (status_type == GLOBAL_STATUS_INVALID || status_type == GLOBAL_STATUS_TOTAL)
623         return GLOBAL_INTENSITY_INVALID;
624 
625     for (uint32_t i = 0; i < _active_status_effects.size(); ++i) {
626         const ActiveMapStatusEffect& effect = _active_status_effects[i];
627         if (effect.GetType() != status_type)
628             continue;
629 
630         if (effect.GetAffectedCharacter() != character)
631             continue;
632 
633         return effect.GetIntensity();
634     }
635 
636     return GLOBAL_INTENSITY_NEUTRAL;
637 }
638 
RemoveNegativeActiveStatusEffects()639 void MapStatusEffectsSupervisor::RemoveNegativeActiveStatusEffects()
640 {
641     for (uint32_t i = 0; i < _active_status_effects.size(); ++i) {
642         ActiveMapStatusEffect& effect = _active_status_effects[i];
643 
644         if (effect.GetIntensity() <= GLOBAL_INTENSITY_NEUTRAL)
645             effect.Disable();
646     }
647 }
648 
_AddActiveStatusEffect(GlobalCharacter * character,GLOBAL_STATUS status,GLOBAL_INTENSITY intensity,uint32_t duration,uint32_t elapsed_time)649 void MapStatusEffectsSupervisor::_AddActiveStatusEffect(GlobalCharacter* character,
650                                                         GLOBAL_STATUS status, GLOBAL_INTENSITY intensity,
651                                                         uint32_t duration, uint32_t elapsed_time)
652 {
653     if((status <= GLOBAL_STATUS_INVALID) || (status >= GLOBAL_STATUS_TOTAL)) {
654         PRINT_WARNING << "Function received invalid status argument: " << status << std::endl;
655         return;
656     }
657 
658     if((intensity <= GLOBAL_INTENSITY_INVALID) || (intensity >= GLOBAL_INTENSITY_TOTAL)) {
659         PRINT_WARNING << "Function received invalid intensity argument: " << intensity << std::endl;
660         return;
661     }
662 
663     _active_status_effects.push_back(ActiveMapStatusEffect(character, status, intensity, duration));
664     ActiveMapStatusEffect& new_effect = _active_status_effects.back();
665 
666     // If there is already some elapsed time, we restore it
667     if (elapsed_time > 0 && elapsed_time <= duration)
668         new_effect.GetTimer()->SetTimeExpired(elapsed_time);
669 
670     if (!new_effect.GetApplyFunction().is_valid()) {
671         PRINT_WARNING << "No valid status effect Apply function to call" << std::endl;
672         return;
673     }
674 
675     // Call the apply script function now that this new status is active on the actor
676     try {
677         luabind::call_function<void>(new_effect.GetApplyFunction(), new_effect);
678     } catch(const luabind::error& e) {
679         PRINT_ERROR << "Error while loading status effect Apply function" << std::endl;
680         vt_script::ScriptManager->HandleLuaError(e);
681     } catch(const luabind::cast_failed& e) {
682         PRINT_ERROR << "Error while loading status effect Apply function" << std::endl;
683         vt_script::ScriptManager->HandleCastError(e);
684     }
685 }
686 
_RemoveActiveStatusEffect(ActiveMapStatusEffect & status_effect)687 void MapStatusEffectsSupervisor::_RemoveActiveStatusEffect(ActiveMapStatusEffect& status_effect)
688 {
689     if(!status_effect.IsActive())
690         return;
691 
692     // Remove the status effect from the active effects list if it registered there.
693     if (status_effect.GetRemoveFunction().is_valid()) {
694         try {
695             luabind::call_function<void>(status_effect.GetRemoveFunction(), status_effect);
696         } catch(const luabind::error& e) {
697             PRINT_ERROR << "Error while loading status effect Remove function" << std::endl;
698             vt_script::ScriptManager->HandleLuaError(e);
699         } catch(const luabind::cast_failed& e) {
700             PRINT_ERROR << "Error while loading status effect Remove function" << std::endl;
701             vt_script::ScriptManager->HandleCastError(e);
702         }
703     }
704     else {
705         PRINT_WARNING << "No status effect Remove function defined." << std::endl;
706     }
707 
708     status_effect.Disable();
709 
710 }
711 
_SetActiveStatusEffects(GlobalCharacter * character)712 void MapStatusEffectsSupervisor::_SetActiveStatusEffects(GlobalCharacter* character)
713 {
714     if (!character)
715         return;
716 
717     character->ResetActiveStatusEffects();
718     for(std::vector<ActiveMapStatusEffect>::iterator it = _active_status_effects.begin();
719             it != _active_status_effects.end(); ++it) {
720         ActiveMapStatusEffect& effect = (*it);
721         if (!effect.IsActive())
722             continue;
723 
724         // Copy the active status effect state
725         vt_system::SystemTimer* timer = effect.GetTimer();
726         character->SetActiveStatusEffect(effect.GetType(), effect.GetIntensity(),
727                                          timer->GetDuration(), timer->GetTimeExpired());
728     }
729 }
730 
_AddPassiveStatusEffect(vt_global::GlobalCharacter * character,vt_global::GLOBAL_STATUS status_effect,vt_global::GLOBAL_INTENSITY intensity)731 void MapStatusEffectsSupervisor::_AddPassiveStatusEffect(vt_global::GlobalCharacter* character,
732                                                          vt_global::GLOBAL_STATUS status_effect,
733                                                          vt_global::GLOBAL_INTENSITY intensity)
734 {
735     PassiveMapStatusEffect effect(character, status_effect, intensity);
736     _equipment_status_effects.push_back(effect);
737 }
738 
_MakeCharacterPortraitAppear(vt_global::GlobalCharacter * character,uint32_t time)739 void MapStatusEffectsSupervisor::_MakeCharacterPortraitAppear(vt_global::GlobalCharacter* character, uint32_t time)
740 {
741     for (uint32_t i = 0; i < _characters_portraits.size(); ++i) {
742         if (_characters_portraits[i].GetCharacter() == character) {
743             _characters_portraits[i].FadeIn(time);
744             return;
745         }
746     }
747 }
748 
_GetEffectAnimationXPosition(vt_global::GlobalCharacter * character)749 float MapStatusEffectsSupervisor::_GetEffectAnimationXPosition(vt_global::GlobalCharacter* character)
750 {
751     float x_pos = 90.0; // default position.
752     for (uint32_t i = 0; i < _characters_portraits.size(); ++i) {
753         if (_characters_portraits[i].GetCharacter() == character) {
754             x_pos = _GetEffectXPositionFromCharacterIndex(i) + 5.0f; // We add an offset to avoid cluttering
755             break;
756         }
757     }
758     return x_pos;
759 }
760 
761 } // namespace private_map
762 
763 } // namespace vt_map
764