36 using namespace std::chrono_literals;
40 return cfg[
"healed_sound"].
empty() ?
"heal.wav" :
cfg[
"healed_sound"].str();
47 config result = attributes;
56 std::vector<config::const_all_children_iterator>
children;
64 : itors(
cfg.all_children_range()), branches(1), parent(nullptr)
66 branches.back().attributes.merge_attributes(
cfg);
70 : itors(
cfg.all_children_range()), branches(
p->branches), parent(
p)
76 bool previously_hits_set =
false;
77 bool previously_direction_set =
false;
78 bool previously_terrain_set =
false;
79 bool previously_value_set =
false;
80 bool previously_value_2nd_set =
false;
82 const std::string s_cfg_hits =
cfg[
"hits"];
83 const std::string s_cfg_direction =
cfg[
"direction"];
84 const std::string s_cfg_terrain =
cfg[
"terrain_types"];
85 const std::string s_cfg_value =
cfg[
"value"];
86 const std::string s_cfg_value_2nd =
cfg[
"value_2nd"];
88 for(
const auto& branch : branches) {
89 const std::string s_branch_hits = branch.attributes[
"hits"];
90 const std::string s_branch_direction = branch.attributes[
"direction"];
91 const std::string s_branch_terrain = branch.attributes[
"terrain_types"];
92 const std::string s_branch_value = branch.attributes[
"value"];
93 const std::string s_branch_value_2nd = branch.attributes[
"value_second"];
95 if(!s_branch_hits.empty() && s_branch_hits == s_cfg_hits) {
96 previously_hits_set =
true;
99 if(!s_branch_direction.empty() && s_branch_direction == s_cfg_direction) {
100 previously_direction_set =
true;
103 if(!s_branch_terrain.empty() && s_branch_terrain == s_cfg_terrain) {
104 previously_terrain_set =
true;
107 if(!s_branch_value.empty() && s_branch_value == s_cfg_value) {
108 previously_value_set =
true;
111 if(!s_branch_value_2nd.empty() && s_branch_value_2nd == s_cfg_value_2nd) {
112 previously_value_2nd_set =
true;
118 for(
auto iter = branches.begin(); iter != branches.end(); ) {
119 const std::string s_branch_hits = (*iter).attributes[
"hits"];
120 const std::string s_branch_direction = (*iter).attributes[
"direction"];
121 const std::string s_branch_terrain = (*iter).attributes[
"terrain_types"];
122 const std::string s_branch_value = (*iter).attributes[
"value"];
123 const std::string s_branch_value_2nd = (*iter).attributes[
"value_second"];
125 const bool hits_match = (previously_hits_set && s_branch_hits != s_cfg_hits);
126 const bool direction_match = (previously_direction_set && s_branch_direction != s_cfg_direction);
127 const bool terrain_match = (previously_terrain_set && s_branch_terrain != s_cfg_terrain);
128 const bool value_match = (previously_value_set && s_branch_value != s_cfg_value);
129 const bool value_2nd_match = (previously_value_2nd_set && s_branch_value_2nd != s_cfg_value_2nd);
131 if((!previously_hits_set || hits_match) &&
132 (!previously_direction_set || direction_match) &&
133 (!previously_terrain_set || terrain_match) &&
134 (!previously_value_set || value_match) &&
135 (!previously_value_2nd_set || value_2nd_match) &&
136 (hits_match || direction_match || terrain_match || value_match || value_2nd_match))
138 branches.
erase(iter++);
140 (*iter).attributes.merge_attributes(
cfg);
147 for(
auto iter = parent->branches.begin(); iter != parent->branches.end(); ) {
148 const std::string s_branch_hits = (*iter).attributes[
"hits"];
149 const std::string s_branch_direction = (*iter).attributes[
"direction"];
150 const std::string s_branch_terrain = (*iter).attributes[
"terrain_types"];
151 const std::string s_branch_value = (*iter).attributes[
"value"];
152 const std::string s_branch_value_2nd = (*iter).attributes[
"value_second"];
154 const bool hits_match = (previously_hits_set && s_branch_hits == s_cfg_hits);
155 const bool direction_match = (previously_direction_set && s_branch_direction == s_cfg_direction);
156 const bool terrain_match = (previously_terrain_set && s_branch_terrain == s_cfg_terrain);
157 const bool value_match = (previously_value_set && s_branch_value == s_cfg_value);
158 const bool value_2nd_match = (previously_value_2nd_set && s_branch_value_2nd == s_cfg_value_2nd);
160 if((!previously_hits_set || hits_match) &&
161 (!previously_direction_set || direction_match) &&
162 (!previously_terrain_set || terrain_match) &&
163 (!previously_value_set || value_match) &&
164 (!previously_value_2nd_set || value_2nd_match) &&
165 (hits_match || direction_match || terrain_match || value_match || value_2nd_match))
167 parent->branches.erase(iter++);
187 std::list<animation_cursor> anim_cursors;
188 anim_cursors.emplace_back(anim_cfg);
190 while(!anim_cursors.empty()) {
194 if(ac.
itors.empty()) {
199 anim_cursors.pop_back();
203 if(ac.
itors.front().key !=
"if") {
209 ac.
itors.pop_front();
217 anim_cursors.emplace_back(ac.
itors.front().cfg, &ac);
218 ac.
itors.pop_front();
220 }
while (!ac.
itors.empty() && ac.
itors.front().key ==
"else");
231 std::cout <<
"--branch--\n" << ab.attributes;
233 std::cout <<
"--branchcfg--\n" << ci->cfg;
240 assert(anim_cursors.size() == 1);
252 return expanded_animations;
259 , secondary_unit_filter_()
262 , base_score_(variation)
265 , primary_attack_filter_()
266 , secondary_attack_filter_()
270 , unit_anim_(start_time,builder)
273 , invalidated_(false)
274 , play_offscreen_(true)
283 , secondary_unit_filter_()
285 , frequency_(
cfg[
"frequency"].to_int())
286 , base_score_(
cfg[
"base_score"].to_int())
289 , primary_attack_filter_()
290 , secondary_attack_filter_()
294 , unit_anim_(
cfg,frame_string)
297 , invalidated_(false)
298 , play_offscreen_(true)
304 if(key == frame_string) {
308 if(key.find(
"_frame", key.size() - 6) == std::string::npos) {
321 const std::vector<std::string>& my_directions =
utils::split(
cfg[
"direction"]);
322 for(
const auto& direction : my_directions) {
347 value_.push_back(utils::from_chars<int>(v).value_or(0));
351 if(
h ==
"yes" ||
h == strike_result::hit) {
352 hits_.push_back(strike_result::type::hit);
355 if(
h ==
"no" ||
h == strike_result::miss) {
356 hits_.push_back(strike_result::type::miss);
359 if(
h ==
"yes" ||
h == strike_result::kill ) {
360 hits_.push_back(strike_result::type::kill);
365 value2_.push_back(utils::from_chars<int>(v2).value_or(0));
386 if(!event.empty() && !
event_.empty()) {
468 if(!attack->matches_filter(iter))
return MATCH_FAIL;
479 if(!second_attack->matches_filter(iter))
return MATCH_FAIL;
490 std::vector<unit_animation> animation_base;
491 for(
const auto& anim : animations) {
493 animation_base.push_back(anim);
495 animation_base.back().event_.clear();
500 std::string default_image;
504 default_image = (*df_cfg)[
"image"].str();
508 if (default_image.empty()) {
509 default_image =
cfg[
"image"].str();
514 if(animation_base.empty()) {
519 animations.push_back(
unit_animation(0ms, default_frame_builder,
"_disabled_", 0));
521 selected_builder.
duration(300ms).
blend(
"0.0~0.3:100,0.3~0.0:200", { 255,255,255 });
522 animations.push_back(
unit_animation(0ms, selected_builder,
"_disabled_selected_", 0));
524 for(
const auto& base : animation_base) {
525 animations.push_back(base);
526 animations.back().event_ = {
"standing" };
527 animations.back().play_offscreen_ =
false;
529 animations.push_back(base);
530 animations.back().event_ = {
"_ghosted_" };
531 animations.back().unit_anim_.override(0ms, animations.back().unit_anim_.get_animation_duration(),
particle::UNSET,
"0.9",
"", {0,0,0},
"",
"",
"~GS()");
533 animations.push_back(base);
534 animations.back().event_ = {
"_disabled_ghosted_" };
535 animations.back().unit_anim_.override(0ms, 1ms,
particle::UNSET,
"0.4",
"", {0,0,0},
"",
"",
"~GS()");
537 animations.push_back(base);
538 animations.back().event_ = {
"selected" };
539 animations.back().unit_anim_.override(0ms, 300ms,
particle::UNSET,
"",
"0.0~0.3:100,0.3~0.0:200", {255,255,255});
541 animations.push_back(base);
542 animations.back().event_ = {
"recruited" };
545 animations.push_back(base);
546 animations.back().event_ = {
"levelin" };
547 animations.back().unit_anim_.override(0ms, 600ms,
particle::NO_CYCLE,
"",
"1~0:600", {255,255,255});
549 animations.push_back(base);
550 animations.back().event_ = {
"levelout" };
551 animations.back().unit_anim_.override(0ms, 600ms,
particle::NO_CYCLE,
"",
"0~1:600,1", {255,255,255});
553 animations.push_back(base);
554 animations.back().event_ = {
"pre_movement" };
557 animations.push_back(base);
558 animations.back().event_ = {
"post_movement" };
561 animations.push_back(base);
562 animations.back().event_ = {
"movement" };
563 animations.back().unit_anim_.override(0ms, 200ms,
566 animations.push_back(base);
567 animations.back().event_ = {
"defend" };
568 animations.back().unit_anim_.override(0ms, animations.back().unit_anim_.get_animation_duration(),
570 animations.back().hits_.push_back(strike_result::type::hit);
571 animations.back().hits_.push_back(strike_result::type::kill);
573 animations.push_back(base);
574 animations.back().event_ = {
"defend" };
576 animations.push_back(base);
577 animations.back().event_ = {
"attack" };
578 animations.back().unit_anim_.override(-150ms, 300ms,
particle::NO_CYCLE,
"",
"", {0,0,0},
"0~0.6:150,0.6~0:150",
std::to_string(
get_abs_frame_layer(
drawing_layer::unit_move_default)));
579 animations.back().primary_attack_filter_.emplace_back(
"range",
"melee");
581 animations.push_back(base);
582 animations.back().event_ = {
"attack" };
584 animations.back().primary_attack_filter_.emplace_back(
"range",
"ranged");
586 animations.push_back(base);
587 animations.back().event_ = {
"death" };
589 animations.back().sub_anims_[
"_death_sound"] =
particle();
590 animations.back().sub_anims_[
"_death_sound"].add_frame(1ms,
frame_builder().
sound(
cfg[
"die_sound"]),
true);
592 animations.push_back(base);
593 animations.back().event_ = {
"victory" };
594 animations.back().unit_anim_.override(0ms, animations.back().unit_anim_.get_animation_duration(),
particle::CYCLE);
596 animations.push_back(base);
598 animations.back().event_ = {
"pre_teleport" };
600 animations.push_back(base);
602 animations.back().event_ = {
"post_teleport" };
604 animations.push_back(base);
605 animations.back().event_ = {
"healing" };
607 animations.push_back(base);
608 animations.back().event_ = {
"healed" };
609 animations.back().unit_anim_.override(0ms, 300ms,
particle::NO_CYCLE,
"",
"0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30", {255,255,255});
613 animations.back().sub_anims_[
"_healed_sound"].add_frame(1ms,
frame_builder().
sound(healed_sound),
true);
615 animations.push_back(base);
616 animations.back().event_ = {
"poisoned" };
617 animations.back().unit_anim_.override(0ms, 300ms,
particle::NO_CYCLE,
"",
"0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30", {0,255,0});
618 animations.back().sub_anims_[
"_poison_sound"] =
particle();
624 const config&
cfg,
char const* tag_name,
char const* apply_to,
626 bool offscreen =
true)
630 anim[
"apply_to"] = apply_to;
634 if(v.
empty()) v =
false;
640 animations.emplace_back(anim);
647 animations.emplace_back(ab.merge());
665 anim[
"apply_to"] =
"standing";
666 anim[
"cycles"] =
true;
670 std::string sub_frame_name = ci->key;
671 std::size_t pos = sub_frame_name.find(
"_frame");
672 if(pos != std::string::npos) {
673 anim[sub_frame_name.substr(0, pos) +
"_cycles"] =
true;
677 if(anim[
"layer"].empty()) {
678 anim[
"layer"] = default_layer;
681 if(anim[
"offscreen"].empty()) {
682 anim[
"offscreen"] =
false;
685 animations.emplace_back(anim);
691 anim[
"apply_to"] =
"default";
692 anim[
"cycles"] =
true;
695 std::string sub_frame_name = ci->key;
696 std::size_t pos = sub_frame_name.find(
"_frame");
697 if(pos != std::string::npos) {
698 anim[sub_frame_name.substr(0, pos) +
"_cycles"] =
true;
702 if(anim[
"layer"].empty()) {
703 anim[
"layer"] = default_layer;
706 if(anim[
"offscreen"].empty()) {
707 anim[
"offscreen"] =
false;
710 animations.emplace_back(anim);
715 anim[
"apply_to"] =
"healing";
716 anim[
"value"] = anim[
"damage"];
718 if(anim[
"layer"].empty()) {
719 anim[
"layer"] = default_layer;
722 animations.emplace_back(anim);
727 anim[
"apply_to"] =
"healed";
728 anim[
"value"] = anim[
"healing"];
730 if(anim[
"layer"].empty()) {
731 anim[
"layer"] = default_layer;
734 animations.emplace_back(anim);
735 animations.back().sub_anims_[
"_healed_sound"] =
particle();
738 animations.back().sub_anims_[
"_healed_sound"].add_frame(1ms,
frame_builder().
sound(healed_sound),
true);
743 anim[
"apply_to"] =
"poisoned";
744 anim[
"value"] = anim[
"damage"];
746 if(anim[
"layer"].empty()) {
747 anim[
"layer"] = default_layer;
750 animations.emplace_back(anim);
751 animations.back().sub_anims_[
"_poison_sound"] =
particle();
759 anim[
"apply_to"] =
"movement";
761 if(anim[
"offset"].empty()) {
762 anim[
"offset"] =
"0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,";
765 if(anim[
"layer"].empty()) {
766 anim[
"layer"] = move_layer;
769 animations.emplace_back(anim);
776 anim[
"apply_to"] =
"defend";
778 if(anim[
"layer"].empty()) {
779 anim[
"layer"] = default_layer;
782 if(!anim[
"damage"].empty() && anim[
"value"].empty()) {
783 anim[
"value"] = anim[
"damage"];
786 if(anim[
"hits"].empty()) {
787 anim[
"hits"] =
false;
788 animations.emplace_back(anim);
789 animations.back().base_score_--;
792 animations.emplace_back(anim);
793 animations.back().base_score_--;
795 image::locator image_loc = animations.back().get_last_frame().end_parameters().image;
799 .blend(
"0.0,0.5:75,0.0:75,0.5:75,0.0", {255,0,0}));
801 for(
const std::string& hit_type :
utils::split(anim[
"hits"])) {
803 tmp[
"hits"] = hit_type;
805 animations.emplace_back(tmp);
807 image::locator image_loc = animations.back().get_last_frame().end_parameters().image;
808 if(hit_type ==
"yes" || hit_type == strike_result::hit || hit_type == strike_result::kill) {
812 .blend(
"0.0,0.5:75,0.0:75,0.5:75,0.0", {255,0,0}));
823 anim[
"apply_to"] =
"attack";
825 if(anim[
"layer"].empty()) {
826 anim[
"layer"] = move_layer;
830 if(anim[
"offset"].empty() && missile_fs.
empty()) {
831 anim[
"offset"] =
"0~0.6,0.6~0";
834 if(!missile_fs.empty()) {
835 if(anim[
"missile_offset"].empty()) {
836 anim[
"missile_offset"] =
"0~0.8";
839 if(anim[
"missile_layer"].empty()) {
840 anim[
"missile_layer"] = missile_layer;
850 animations.emplace_back(anim);
855 anim[
"apply_to"] =
"death";
857 if(anim[
"layer"].empty()) {
858 anim[
"layer"] = default_layer;
861 animations.emplace_back(anim);
862 image::locator image_loc = animations.back().get_last_frame().end_parameters().image;
867 .highlight(
"1~0:600"));
869 if(!
cfg[
"die_sound"].empty()) {
870 animations.back().sub_anims_[
"_death_sound"] =
particle();
871 animations.back().sub_anims_[
"_death_sound"].add_frame(1ms,
frame_builder().
sound(
cfg[
"die_sound"]),
true);
879 anim[
"apply_to"] = anim[
"flag"];
881 if(anim[
"layer"].empty()) {
882 anim[
"layer"] = default_layer;
885 animations.emplace_back(anim);
890 if(anim[
"layer"].empty()) {
891 anim[
"layer"] = default_layer;
894 anim[
"apply_to"] =
"pre_teleport";
895 animations.emplace_back(anim);
896 animations.back().unit_anim_.set_end_time(0ms);
898 anim[
"apply_to"] =
"post_teleport";
899 animations.emplace_back(anim);
900 animations.back().unit_anim_.remove_frames_until(0ms);
905 ,
const std::chrono::milliseconds& duration
907 ,
const std::string& highlight
908 ,
const std::string& blend_ratio
910 ,
const std::string& offset
911 ,
const std::string& layer
912 ,
const std::string& modifiers)
934 if(parameters_.need_update())
return true;
947 , last_frame_begin_time_(0)
955 if(!range.empty() &&
cfg[frame_string +
"start_time"].
empty()) {
956 for(
const config& frame : range) {
963 for(
const config& frame : range) {
968 cycles_ =
cfg[frame_string +
"cycles"].to_bool(
false);
980 if(anim.second.need_update())
return true;
995 if(anim.second.need_minimal_update())
return true;
1005 if(!anim.second.animation_finished())
return false;
1018 if(anim.second.need_update()) {
1019 anim.second.advance_to_current_frame();
1028 result = std::max(result, anim.second.get_end_time());
1038 result = std::min(result, anim.second.get_begin_time());
1047 ,
const std::string& text
1049 ,
const bool accelerate)
1066 anim.second.start_animation(start_time);
1081 anim.second.pause_animation();
1090 anim.second.resume_animation();
1104 anim.second.redraw(value,
src_,
dst_, halo_man);
1113 anim.second.clear_halo();
1125 if(complete_redraw) {
1131 std::set<map_location> tmp = anim.second.get_overlaped_hex(value,
src_,
dst_);
1141 if(complete_redraw) {
1163 std::ostringstream outstream;
1165 return outstream.str();
1171 outstream <<
"[" << events_string <<
"]\n";
1173 outstream <<
"\tstart_time=" << u_animation.
get_begin_time().count() <<
'\n';
1175 if(u_animation.
hits_.size() > 0) {
1176 std::vector<std::string> hits;
1178 outstream <<
"\thits=" <<
utils::join(hits) <<
'\n';
1182 std::vector<std::string> dirs;
1184 outstream <<
"\tdirections=" <<
utils::join(dirs) <<
'\n';
1194 outstream <<
"[filter]\n";
1199 outstream <<
"[/filter]\n";
1203 outstream <<
"[filter_second]\n";
1208 outstream <<
"[/filter_second]\n";
1212 outstream <<
"[filter_attack]\n";
1217 outstream <<
"[/filter_attack]\n";
1221 outstream <<
"[filter_second_attack]\n";
1226 outstream <<
"[/filter_second_attack]\n";
1230 outstream <<
"\t[frame]\n";
1232 outstream <<
"\t\t" << frame_string <<
"\n";
1234 outstream <<
"\t[/frame]\n";
1237 for(std::pair<std::string, unit_animation::particle>
p : u_animation.
sub_anims_) {
1238 for(std::size_t
i = 0;
i <
p.second.get_frames_count();
i++) {
1239 std::string sub_frame_name =
p.first;
1240 std::size_t pos = sub_frame_name.find(
"_frame");
1241 if(pos != std::string::npos) sub_frame_name = sub_frame_name.substr(0, pos);
1243 outstream <<
"\t" << sub_frame_name <<
"_start_time=" <<
p.second.get_begin_time().count() <<
'\n';
1244 outstream <<
"\t[" <<
p.first <<
"]\n";
1246 for(
const std::string& frame_string :
p.second.get_frame(
i).debug_strings()) {
1247 outstream <<
"\t\t" << frame_string <<
'\n';
1250 outstream <<
"\t[/" <<
p.first <<
"]\n";
1254 outstream <<
"[/" << events_string <<
"]\n";
1260 const unit_frame& current_frame = get_current_frame();
1267 if(animation_time > get_current_frame_end_time()) in_scope_of_frame =
false;
1276 current_frame.
redraw(get_time_in_current_frame(),
true, in_scope_of_frame,
src,
dst, halo_id_, halo_man, default_val, value);
1278 current_frame.
redraw(get_time_in_current_frame(),
false, in_scope_of_frame,
src,
dst, halo_id_, halo_man, default_val, value);
1289 const unit_frame& current_frame = get_current_frame();
1302 parameters_.override(get_animation_duration());
1308 ,
const std::string& event
1313 ,
const std::string& text
1320 if(!animated_unit)
return;
1323 animated_unit->anim_comp().choose_animation(
src, event,
dst, value, hit_type, attack, second_attack, value2);
1327 animated_units_.AGGREGATE_EMPLACE(std::move(animated_unit), anim, text, text_color,
src, with_bars);
1334 ,
const std::string& text
1337 if(!animated_unit || !anim)
return;
1340 animated_units_.AGGREGATE_EMPLACE(std::move(animated_unit), anim, text, text_color,
src, with_bars);
1344 ,
const std::string& event
1353 return (animated_unit && animated_unit->anim_comp().choose_animation(
src, event,
dst, value, hit_type, attack, second_attack, value2));
1357 ,
const std::string& event
1362 ,
const std::string& text
1369 if(!animated_unit)
return;
1371 if(animated_unit->anim_comp().get_animation() &&
1372 !animated_unit->anim_comp().get_animation()->animation_finished() &&
1373 animated_unit->anim_comp().get_animation()->matches(
1376 animated_units_.AGGREGATE_EMPLACE(animated_unit,
nullptr, text, text_color,
src, with_bars);
1378 add_animation(animated_unit,event,
src,
dst,value,with_bars,text,text_color,hit_type,attack,second_attack,value2);
1384 auto begin_time = std::chrono::milliseconds::max();
1386 for(
const auto& anim : animated_units_) {
1387 if(anim.my_unit->anim_comp().get_animation()) {
1388 if(anim.animation) {
1389 begin_time = std::min(begin_time, anim.animation->get_begin_time());
1391 begin_time = std::min(begin_time, anim.my_unit->anim_comp().get_animation()->get_begin_time());
1396 for(
auto& anim : animated_units_) {
1397 if(anim.animation) {
1398 anim.my_unit->anim_comp().start_animation(begin_time, anim.animation, anim.with_bars, anim.text, anim.text_color);
1399 anim.animation =
nullptr;
1401 anim.my_unit->anim_comp().get_animation()->update_parameters(anim.src, anim.src.get_direction(anim.my_unit->facing()));
1408 bool finished =
true;
1409 for(
const auto& anim : animated_units_) {
1410 finished &= anim.my_unit->anim_comp().get_animation()->animation_finished();
1418 if(animated_units_.empty()) {
1423 animated_units_[0].my_unit->anim_comp().get_animation()->set_duration_limit(animation_time);
1432 if(current_anim_time >= animation_time) {
1435 auto remaining_ms = animation_time - current_anim_time;
1436 auto wait_ms = std::chrono::duration_cast<std::chrono::milliseconds>(remaining_ms / speed);
1439 auto sleep_slice = std::clamp(wait_ms, 1ms, 20ms);
1440 std::this_thread::sleep_for(sleep_slice);
1445 animated_units_[0].my_unit->anim_comp().get_animation()->set_duration_limit(0ms);
1450 bool finished =
false;
1454 std::this_thread::sleep_for(10ms);
1457 for(
const auto& anim : animated_units_) {
1458 finished &= anim.my_unit->anim_comp().get_animation()->animation_finished();
1465 if(animated_units_.empty()) {
1468 return animated_units_[0].my_unit->anim_comp().get_animation()->get_elapsed_time() ;
1473 auto end_time = std::chrono::milliseconds::min();
1474 for(
const auto& anim : animated_units_) {
1475 if(anim.my_unit->anim_comp().get_animation()) {
1476 end_time = std::max(end_time, anim.my_unit->anim_comp().get_animation()->get_end_time());
1485 for(
const auto& anim : animated_units_) {
1486 if(anim.my_unit->anim_comp().get_animation()) {
1487 anim.my_unit->anim_comp().get_animation()->pause_animation();
1494 for(
const auto& anim : animated_units_) {
1495 if(anim.my_unit->anim_comp().get_animation()) {
1496 anim.my_unit->anim_comp().get_animation()->resume_animation();
1503 for(
const auto& anim : animated_units_) {
1504 anim.my_unit->anim_comp().set_standing();
void update_animation_timers(double acceleration)
Updates both animation timelines.
std::list< animation_branch > animation_branches
static void prepare_single_animation(const config &anim_cfg, animation_branches &expanded_anims)
static std::string get_heal_sound(const config &cfg)
static void add_simple_anim(std::vector< unit_animation > &animations, const config &cfg, char const *tag_name, char const *apply_to, drawing_layer layer=drawing_layer::unit_default, bool offscreen=true)
static animation_branches prepare_animation(const config &cfg, const std::string &animation_tag)
std::unique_ptr< PangoAttribute, void(*)(PangoAttribute *)> value_
const T & get_frame(std::size_t n) const
void resume_animation()
Shifts the timeline forward by the time spent paused, then continues playback.
std::chrono::milliseconds get_begin_time() const
Animation-time offset of the first frame's start.
void set_uses_acceleration(bool uses_accel)
Selects which of the two global timelines get_current_animation_tick() reads for this animation: acce...
void add_frame(const std::chrono::milliseconds &duration, const unit_frame &value, bool force_change=false)
Appends a frame, starting where the previous one ends (or at start_time, if first).
void pause_animation()
Freezes the timeline in place.
void start_animation(const std::chrono::milliseconds &start_time_offset, bool cycles=false)
Anchors the timeline to the current tick, then begins playback.
void set_end_time(const std::chrono::milliseconds &ending_time)
Reduces or extends the frame list so get_end_time() becomes ending_time: drops frames after the cut a...
const unit_frame & get_last_frame() const
void force_change()
Clears the flag returned by is_static().
bool animation_finished() const
True if playback has run past the last frame, is not yet started, or has no frames.
std::chrono::milliseconds starting_frame_time_
std::size_t get_frames_count() const
void set_begin_time(const std::chrono::milliseconds &new_begin_time)
Rebases the timeline onto a new begin time, offsetting every frame's start_time_ by the difference so...
std::chrono::milliseconds get_animation_duration() const
get_end_time() minus get_begin_time(): how long one pass through all frames takes.
std::chrono::milliseconds get_end_time() const
Animation-time offset where the last frame ends.
bool cycles() const
True if the animation loops back to the first frame instead of stopping at the last.
void advance_to_current_frame()
Moves current_frame_index_ forward to match the current tick, one frame at a time (or in whole cycles...
Variant for storing WML attributes.
bool empty() const
Tests for an attribute that either was never set or was set to "".
A config object defines a single node in a WML file, with access to child nodes.
config & add_child(std::string_view key)
all_children_iterator erase(const all_children_iterator &i)
boost::iterator_range< const_all_children_iterator > const_all_children_itors
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
auto all_children_view() const
In-order iteration over all children.
child_itors child_range(std::string_view key)
std::string debug() const
config & add_child_at(std::string_view key, const config &val, std::size_t index)
boost::iterator_range< const_child_iterator > const_child_itors
virtual void play_slice()
virtual const gamemap & map() const =0
virtual const unit_map & units() const =0
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
double turbo_speed() const
bool propagate_invalidation(const std::set< map_location > &locs)
If this set is partially invalidated, invalidate all its hexes.
bool tile_nearly_on_screen(const map_location &loc) const
Checks if location loc or one of the adjacent tiles is visible on screen.
const display_context & context() const
static display * get_singleton()
Returns the display object if a display object exists.
Easily build frame parameters with the serialized constructors.
frame_builder & blend(const std::string &blend_ratio, const color_t blend_color)
frame_builder & image(const std::string &image, const std::string &image_mod="")
frame_builder & duration(const std::chrono::milliseconds &duration)
Allow easy chained modifications.
Keep most parameters in a separate class to simplify the handling of the large number of parameters b...
void override(const std::chrono::milliseconds &duration, const std::string &highlight="", const std::string &blend_ratio="", color_t blend_color={0, 0, 0}, const std::string &offset="", const std::string &layer="", const std::string &modifiers="")
bool does_not_change() const
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Generic locator abstracting the location of an image.
const std::string & get_filename() const
const std::string & get_modifications() const
static rng & default_instance()
std::set< map_location > get_overlaped_hex(const frame_parameters &value, const map_location &src, const map_location &dst)
void start_animation(const std::chrono::milliseconds &start_time)
frame_parsed_parameters parameters_
bool need_minimal_update() const
particle(const std::chrono::milliseconds &start_time=std::chrono::milliseconds{0}, const frame_builder &builder=frame_builder())
void override(const std::chrono::milliseconds &start_time, const std::chrono::milliseconds &duration, const cycle_state cycles, const std::string &highlight="", const std::string &blend_ratio="", color_t blend_color={0, 0, 0}, const std::string &offset="", const std::string &layer="", const std::string &modifiers="")
void redraw(const frame_parameters &value, const map_location &src, const map_location &dst, halo::manager &halo_man)
bool invalidate(frame_parameters &value)
std::vector< config > primary_attack_filter_
std::vector< map_location::direction > directions_
std::vector< int > value2_
bool need_minimal_update() const
std::vector< config > secondary_unit_filter_
void try_advance_to_current_frame()
t_translation::ter_list terrain_types_
void add_frame(const std::chrono::milliseconds &duration, const unit_frame &value, bool force_change=false)
void start_animation(const std::chrono::milliseconds &start_time, const map_location &src=map_location::null_location(), const map_location &dst=map_location::null_location(), const std::string &text="", const color_t text_color={0, 0, 0}, const bool accelerate=true)
std::chrono::milliseconds get_begin_time() const
std::vector< std::string > event_
int matches(const map_location &loc, const map_location &second_loc, const unit_const_ptr &my_unit, const std::string &event="", const int value=0, strike_result::type hit=strike_result::type::invalid, const const_attack_ptr &attack=nullptr, const const_attack_ptr &second_attack=nullptr, int value2=0) const
std::vector< int > value_
std::string debug() const
std::map< std::string, particle > sub_anims_
void update_parameters(const map_location &src, const map_location &dst)
std::chrono::milliseconds get_end_time() const
auto get_current_frame_begin_time() const
auto get_elapsed_time() const
void redraw(frame_parameters &value, halo::manager &halo_man)
std::set< map_location > overlaped_hex_
friend std::ostream & operator<<(std::ostream &outstream, const unit_animation &u_animation)
static void fill_initial_animations(std::vector< unit_animation > &animations, const config &cfg)
std::vector< config > secondary_attack_filter_
std::vector< config > unit_filter_
static void add_anims(std::vector< unit_animation > &animations, const config &cfg)
std::vector< strike_result::type > hits_
bool animation_finished() const
std::chrono::milliseconds get_elapsed_time() const
bool has_animation(const unit_const_ptr &animated_unit, const std::string &event, const map_location &src=map_location::null_location(), const map_location &dst=map_location::null_location(), const int value=0, const strike_result::type hit_type=strike_result::type::invalid, const const_attack_ptr &attack=nullptr, const const_attack_ptr &second_attack=nullptr, int value2=0) const
has_animation : return an boolean value if animated unit present and have animation specified,...
void wait_for_end() const
void replace_anim_if_invalid(const unit_const_ptr &animated_unit, const std::string &event, const map_location &src=map_location::null_location(), const map_location &dst=map_location::null_location(), const int value=0, bool with_bars=false, const std::string &text="", const color_t text_color={0, 0, 0}, const strike_result::type hit_type=strike_result::type::invalid, const const_attack_ptr &attack=nullptr, const const_attack_ptr &second_attack=nullptr, int value2=0)
void add_animation(unit_const_ptr animated_unit, const unit_animation *animation, const map_location &src=map_location::null_location(), bool with_bars=false, const std::string &text="", const color_t text_color={0, 0, 0})
std::chrono::milliseconds get_end_time() const
void wait_until(const std::chrono::milliseconds &animation_time) const
Describes a unit's animation sequence.
void redraw(const std::chrono::milliseconds &frame_time, bool on_start_time, bool in_scope_of_frame, const map_location &src, const map_location &dst, halo::handle &halo_id, halo::manager &halo_man, const frame_parameters &animation_val, const frame_parameters &engine_val) const
const std::chrono::milliseconds & duration() const
bool does_not_change() const
std::vector< std::string > debug_strings() const
std::set< map_location > get_overlaped_hex(const std::chrono::milliseconds &frame_time, const map_location &src, const map_location &dst, const frame_parameters &animation_val, const frame_parameters &engine_val) const
unit_iterator find(std::size_t id)
This class represents a single unit of a specific type.
A variable-expanding proxy for the config class.
map_display and display: classes which take care of displaying the map and game-data on the screen.
@ unit_missile_default
Default layer for missile frames.
@ unit_default
Default layer for drawing units.
@ unit_move_default
Default layer for drawing moving units.
constexpr int get_abs_frame_layer(drawing_layer layer)
Functions to load and save images from/to disk.
play_controller * controller
Audio output for sound and music.
bool terrain_matches(const terrain_code &src, const terrain_code &dest)
Tests whether a specific terrain matches an expression, for matching rules see above.
ter_list read_list(std::string_view str, const ter_layer filler)
Reads a list of terrains from a string, when reading the.
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
std::string join(const Range &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::vector< std::string > split(const config_attribute_value &val)
std::string to_string(const Range &range, const Func &op)
std::shared_ptr< const unit > unit_const_ptr
std::shared_ptr< const attack_type > const_attack_ptr
rect dst
Location on the final composed sheet.
rect src
Non-transparent portion of the surface to compose.
std::vector< config::const_all_children_iterator > children
animation_branches branches
animation_cursor(const config &cfg)
animation_cursor(const config &cfg, animation_cursor *p)
config::const_all_children_itors itors
animation_cursor * parent
The basic class for representing 8-bit RGB or RGBA colour values.
All parameters from a frame at a given instant.
boost::tribool primary_frame
Encapsulates the map of the game.
static std::string write_direction(direction dir)
direction
Valid directions which can be moved in our hexagonal world.
static direction parse_direction(const std::string &str)
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.