The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
side_actions.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2010 - 2025
3 by Gabriel Morin <gabrielmorin (at) gmail (dot) com>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
16/**
17 * @file
18 */
19
20#include <set>
21#include <sstream>
22#include <utility>
23
25
26#include "whiteboard/action.hpp"
27#include "whiteboard/attack.hpp"
29#include "whiteboard/move.hpp"
30#include "whiteboard/recall.hpp"
35
36#include "actions/undo.hpp"
37#include "display.hpp"
39#include "game_state.hpp"
40#include "play_controller.hpp"
41#include "resources.hpp"
42#include "units/unit.hpp"
44
45namespace wb
46{
47
48/** Dumps side_actions on a stream, for debug purposes. */
49std::ostream &operator<<(std::ostream &out, const wb::side_actions& side_actions)
50{
51 out << "Content of side_actions:";
52 for(std::size_t turn = 0; turn < side_actions.num_turns(); ++turn) {
53 out << "\n Turn " << turn;
54
55 int count = 1;
57 out << "\n (" << count++ << ") " << *it;
58 }
59
60 if(side_actions.turn_size(turn) == 0) {
61 out << "\n (empty)";
62 }
63 }
64
65 if(side_actions.empty()) {
66 out << " (empty)";
67 }
68
69 return out;
70}
71
73 : actions_()
74 , turn_beginnings_()
75{
76}
77
78std::size_t side_actions_container::get_turn_impl(std::size_t begin, std::size_t end, const_iterator it) const
79{
80 if(begin+1 >= end) {
81 if(begin+1 != end) {
82 ERR_WB << "get_turn: begin >= end";
83 }
84 else if(it < turn_beginnings_[begin]) {
85 ERR_WB << "get_turn failed";
86 }
87 return begin;
88 }
89 std::size_t mid = (begin+end) / 2;
90 if(it < turn_beginnings_[mid]) {
91 return get_turn_impl(begin, mid, it);
92 } else {
93 return get_turn_impl(mid, end, it);
94 }
95}
96
98{
99 return get_turn_impl(0, num_turns(), it);
100}
101
103{
104 return it - turn_begin( get_turn(it) );
105}
106
108 if(turn_num >= num_turns()) {
109 return end();
110 } else {
111 return turn_beginnings_[turn_num];
112 }
113}
114
116{
117 if(turn_num >= num_turns()) {
118 return end();
119 } else {
120 return turn_beginnings_[turn_num];
121 }
122}
123
125 if(turn_size(turn) == 0) {
126 return queue(turn, action);
127 }
128
129 iterator res = insert(turn_begin(turn), action);
130 if(res != end()) {
131 bool current_turn_unplanned = turn_size(0) == 0;
132 turn_beginnings_[turn] = res;
133
134 if(current_turn_unplanned && turn == 1) {
135 turn_beginnings_.front() = res;
136 }
137 }
138 return res;
139}
140
142{
143 assert(position <= end());
144
145 bool first = position == begin();
146
147 std::pair<iterator,bool> res = actions_.insert(position, action);
148 if(!res.second) {
149 return end();
150 }
151 if(first) {
152 // If we are inserting before the first action, then the inserted action should became the first of turn 0.
153 turn_beginnings_.front() = begin();
154 }
155 return res.first;
156}
157
159{
160 // Are we inserting an action in the future while the current turn is empty?
161 // That is, are we in the sole case where an empty turn can be followed by a non-empty one.
162 bool future_only = turn_num == 1 && num_turns() == 0;
163
164 bool current_turn_unplanned = turn_size(0) == 0;
165
166 //for a little extra safety, since we should never resize by much at a time
167 assert(turn_num <= num_turns() || future_only);
168
169 std::pair<iterator,bool> res = actions_.insert(turn_end(turn_num), action);
170 if(!res.second) {
171 return end();
172 }
173
174 if(future_only) {
175 // No action are planned for the current turn but we are planning an action for turn 1 (the next turn).
176 turn_beginnings_.push_back(res.first);
177 }
178 if(turn_num >= num_turns()) {
179 turn_beginnings_.push_back(res.first);
180 } else if(current_turn_unplanned && turn_num == 0) {
181 // We are planning the first action of the current turn while others actions are planned in the future.
182 turn_beginnings_.front() = res.first;
183 }
184
185 return res.first;
186}
187
189{
190 assert(position > begin());
191 assert(position < end());
192
193 action_ptr rhs = *position;
194 action_ptr lhs = *(position - 1);
195
196 actions_.replace(position, lhs);
197 actions_.replace(position - 1, rhs);
198 return position - 1;
199}
200
205
207{
208 //precondition
209 assert(position < end());
210
211 //prepare
212 iterator next = position + 1;
213 bool deleting_last_element = next == end();
214
215 // pre-processing (check if position is at the beginning of a turn)
216 action_limits::iterator beginning = std::find(turn_beginnings_.begin(), turn_beginnings_.end(), position);
217 if(beginning != turn_beginnings_.end()) {
218 if(deleting_last_element) {
219 if(size() == 1) {
220 // If we are deleting our sole action, we can clear turn_beginnings_ (and we have to if this last action is in turn 1)
221 turn_beginnings_.clear();
222 } else {
223 // Otherwise, we just delete the last turn
224 turn_beginnings_.pop_back();
225 }
226 } else {
227#if 1
228 for(auto& it : turn_beginnings_) {
229 if (it == position) {
230 it = next;
231 }
232 }
233#else
234 std::size_t turn_of_position = std::distance(turn_beginnings_.begin(), beginning);
235 // If we still have action this turn
236 if(get_turn(next) == turn_of_position) {
237 *beginning = next; // We modify the beginning of the turn
238 } else {
239 assert(turn_of_position == 0);
240 *beginning = turn_end(0); // Otherwise, we are emptying the current turn.
241 }
242#endif
243 }
244 }
245
246 //erase!
247 return actions_.erase(position);
248}
249
251 // @todo rewrite using boost::multi_index::erase(iterator,iterator) for efficiency.
252 if(first>=last) {
253 return last;
254 }
255 for(iterator it = last-1; it>first; --it) {
256 it = erase(it);
257 }
258 return erase(first);
259}
260
261
263 : actions_()
264 , team_index_(0)
265 , team_index_defined_(false)
266 , gold_spent_(0)
267 , hidden_(false)
268{
269}
270
271void side_actions::set_team_index(std::size_t team_index)
272{
273 assert(!team_index_defined_);
275 team_index_defined_ = true;
276}
277
279{
280 if(empty()) {
281 return;
282 }
283
284 std::vector<int>& numbers_to_draw = result.numbers_to_draw;
285 std::vector<std::size_t>& team_numbers = result.team_numbers;
286 int& main_number = result.main_number;
287 std::set<std::size_t>& secondary_numbers = result.secondary_numbers;
288 std::shared_ptr<highlighter> hlighter = resources::whiteboard->get_highlighter().lock();
289
290 for(const_iterator it = begin(); it != end(); ++it) {
291 if((*it)->is_numbering_hex(hex)) {
292 //store number corresponding to iterator's position + 1
293 std::size_t number = (it - begin()) + 1;
294 std::size_t index = numbers_to_draw.size();
295 numbers_to_draw.push_back(number);
296 team_numbers.push_back(team_index());
297
298 if(hlighter) {
299 if(hlighter->get_main_highlight().lock() == *it) {
300 main_number = index;
301 }
302
303 for(weak_action_ptr action : hlighter->get_secondary_highlights()) {
304 if(action.lock() == *it) {
305 secondary_numbers.insert(index);
306 }
307 }
308 }
309 }
310 }
311}
312
314{
315 if(!empty()) {
316 return execute(begin());
317 } else { //nothing is executable right now
318 return false;
319 }
320}
321
323{
324 if(resources::whiteboard->has_planned_unit_map()) {
325 ERR_WB << "Modifying action queue while temp modifiers are applied!!!";
326 }
327
328 if(actions_.empty() || position == actions_.end()) {
329 return false;
330 }
331
332 assert(position < turn_end(0)); //can't execute actions from future turns
333
334 LOG_WB << "Before execution, " << *this;
335
336 action_ptr action = *position;
337
338 if(!action->valid()) {
339 LOG_WB << "Invalid action sent to execution, deleting.";
340 synced_erase(position);
341 return true;
342 }
343
344 bool action_successful;
345 // Determines whether action should be deleted. Interrupted moves return action_complete == false.
346 bool action_complete;
347 try {
348 action->execute(action_successful, action_complete);
349 } catch (const return_to_play_side_exception&) {
350 synced_erase(position);
351 LOG_WB << "End turn exception caught during execution, deleting action. " << *this;
352 //validate actions at next map rebuild
353 resources::whiteboard->on_gamestate_change();
354 throw;
355 }
356
357 if(resources::whiteboard->should_clear_undo()) {
358 if(resources::controller->current_team().auto_shroud_updates()) {
360 }
361 else {
362 WRN_WB << "not clearing undo stack because dsu is active";
363 }
364 }
365
366 std::stringstream ss;
367 ss << "After " << (action_successful? "successful": "failed") << " execution ";
368 if(action_complete) {
369 ss << "with deletion, ";
370 synced_erase(position);
371 }
372 else { //action may have revised itself; let's tell our allies.
373 ss << "without deletion, ";
374 resources::whiteboard->queue_net_cmd(team_index_,make_net_cmd_replace(position,*position));
375
376 //Idea that needs refining: move action at the end of the queue if it failed executing:
377 //actions_.erase(position);
378 //actions_.insert(end(), action);
379 }
380 ss << *this << "\n";
381 LOG_WB << ss.str();
382
383 resources::whiteboard->validate_viewer_actions();
384 return action_successful;
385}
386
388{
389 if(hidden_) {
390 return;
391 }
392
393 hidden_ = true;
394
395 for(action_ptr act : *this) {
396 act->hide();
397 }
398}
400{
401 if(!hidden_) {
402 return;
403 }
404
405 hidden_ = false;
406
407 for(action_ptr act : *this) {
408 act->show();
409 }
410}
411
413{
414 if(resources::whiteboard->has_planned_unit_map()) {
415 ERR_WB << "Modifying action queue while temp modifiers are applied!!!";
416 }
417 iterator valid_position = synced_insert(position, action);
418 LOG_WB << "Inserted into turn #" << get_turn(valid_position) << " at position #"
419 << actions_.position_in_turn(valid_position) << " : " << action;
420 resources::whiteboard->validate_viewer_actions();
421 return valid_position;
422}
423
425{
426 if(resources::whiteboard->has_planned_unit_map()) {
427 ERR_WB << "Modifying action queue while temp modifiers are applied!!!";
428 }
429 iterator result = synced_enqueue(turn_num, action);
430 LOG_WB << "Queue into turn #" << turn_num << " : " << action;
431 resources::whiteboard->validate_viewer_actions();
432 return result;
433}
434
435namespace
436{
437 /**
438 * Check whether a move is swapable with a given action.
439 */
440 struct swapable_with_move: public visitor
441 {
442 public:
443 swapable_with_move(side_actions &sa, side_actions::iterator position, move_ptr second): sa_(sa), valid_(false), position_(position), second_(std::move(second)) {}
444 bool valid() const { return valid_; }
445
446 void visit(move_ptr first) {
447 valid_ = second_->get_dest_hex() != first->get_source_hex();
448 }
449
450 void visit(attack_ptr first) {
451 visit(std::static_pointer_cast<move>(first));
452 }
453
454 void visit(recruit_ptr first) {
455 check_recruit_recall(first->get_recruit_hex());
456 }
457
458 void visit(recall_ptr first) {
459 check_recruit_recall(first->get_recall_hex());
460 }
461
462 void visit(suppose_dead_ptr) {
463 valid_ = true;
464 }
465
466 private:
467 side_actions &sa_;
468 bool valid_;
469 side_actions::iterator position_;
470 move_ptr second_;
471
472 void check_recruit_recall(const map_location &loc) {
473 const unit_const_ptr leader = second_->get_unit();
474 if(leader->can_recruit() && dynamic_cast<game_state&>(*resources::filter_con).can_recruit_on(*leader, loc)) {
475 if(const unit_const_ptr backup_leader = find_backup_leader(*leader)) {
476 side_actions::iterator it = sa_.find_first_action_of(*backup_leader);
477 if(!(it == sa_.end() || position_ < it)) {
478 return; //backup leader but he moves before us, refuse bump
479 }
480 } else {
481 return; //no backup leader, refuse bump
482 }
483 }
484 valid_ = true;
485 }
486 };
487}
488
489//move action toward front of queue
491{
492 if(resources::whiteboard->has_planned_unit_map()) {
493 ERR_WB << "Modifying action queue while temp modifiers are applied!!!";
494 }
495
496 assert(position <= end());
497
498 //Don't allow bumping the very first action any earlier, of course.
499 //Also, don't allow bumping an action into a previous turn queue
500 if(actions_.position_in_turn(position) == 0) {
501 return end();
502 }
503
504 side_actions::iterator previous = position - 1;
505
506 //Verify we're not moving an action out-of-order compared to other action of the same unit
507 const unit_const_ptr previous_ptr = (*previous)->get_unit();
508 const unit_const_ptr current_ptr = (*position)->get_unit();
509 if(previous_ptr && current_ptr && previous_ptr.get() == current_ptr.get()) {
510 return end();
511 }
512
513 if(move_ptr second = std::dynamic_pointer_cast<move>(*position)) {
514 swapable_with_move check(*this, position, second);
515 (*previous)->accept(check);
516 if(!check.valid()) {
517 return end();
518 }
519 }
520
521 LOG_WB << "Before bumping earlier, " << *this;
522
523 int turn_number = get_turn(position);
524 int action_number = actions_.position_in_turn(position);
525 int last_position = turn_size(turn_number) - 1;
526 LOG_WB << "In turn #" << turn_number
527 << ", bumping action #" << action_number << "/" << last_position
528 << " to position #" << action_number - 1 << "/" << last_position
529 << ".";
530
531 if (send_to_net) {
532 resources::whiteboard->queue_net_cmd(team_index_, make_net_cmd_bump_later(position - 1));
533 }
534 actions_.bump_earlier(position);
535
536 LOG_WB << "After bumping earlier, " << *this;
537 return position - 1;
538}
539
540//move action toward back of queue
542{
543 assert(position < end());
544
545 ++position;
546 if(position == end()) {
547 return end();
548 }
549 position = bump_earlier(position, send_to_net);
550 if(position == end()) {
551 return end();
552 }
553 return position + 1;
554}
555
557{
558 if(resources::whiteboard->has_planned_unit_map()) {
559 ERR_WB << "Modifying action queue while temp modifiers are applied!!!";
560 }
561
562 assert(position < end());
563
564 LOG_WB << "Erasing action at turn #" << get_turn(position) << " position #" << actions_.position_in_turn(position);
565
566
567 if(resources::gameboard->get_team(team_index_ + 1).is_local()) {
568 position = synced_erase(position);
569 }
570 else {
571 // don't sync actions of sides that we don't control, this would only generate
572 // 'illegal whiteboard data' server wanrings.
573 // it might be better to instead don't even erase the action in this case to keep
574 // the actionlist in sync with the owner client.
575 position = safe_erase(position);
576 }
577
578
579 if(validate_after_delete) {
580 resources::whiteboard->validate_viewer_actions();
581 }
582
583 return position;
584}
585
590
592{
593 return find_first_action_of(actions_.get<container::by_unit>().equal_range(unit_id), start_position, std::less<iterator>());
594}
595
597 return find_first_action_of(actions_.get<container::by_unit>().equal_range(unit_id), start_position, std::greater<iterator>());
598}
599
601{
602 return find_first_action_of(actions_.get<container::by_unit>().equal_range(unit_id), start_position, std::greater<iterator>());
603}
604
606{
607 if(end() == begin()) {
608 return end();
609 }
610 return find_last_action_of(unit_id, end() - 1);
611}
612
614{
615 if(end() == begin()) {
616 return end();
617 }
618 return find_last_action_of(unit_id, end() - 1);
619}
620
622{
623 return find_first_action_of(actions_.get<container::by_unit>().equal_range(unit.underlying_id()), start_position, std::less<iterator>());
624}
625
627 return find_first_action_of(actions_.get<container::by_unit>().equal_range(unit.underlying_id()), start_position, std::greater<iterator>());
628}
629
631{
632 return find_first_action_of(actions_.get<container::by_unit>().equal_range(unit.underlying_id()), start_position, std::greater<iterator>());
633}
634
636{
637 if(end() == begin()) {
638 return end();
639 }
640 return find_last_action_of(unit, end() - 1);
641}
642
644{
645 if(end() == begin()) {
646 return end();
647 }
648 return find_last_action_of(unit, end() - 1);
649}
650
655
657{
659}
660
661std::deque<action_ptr> side_actions::actions_of(const unit& target)
662{
664 std::pair<unit_iterator, unit_iterator> action_its = actions_.get<container::by_unit>().equal_range(target.underlying_id());
665
666 std::deque<action_ptr> actions (action_its.first, action_its.second);
667 return actions;
668}
669
670std::size_t side_actions::get_turn_num_of(const unit& u) const
671{
673 if(itor == end()) {
674 return 0;
675 }
676 return get_turn(itor);
677}
678
680{
681 DBG_WB << "Changing gold spent for side " << (team_index() + 1) << "; old value: "
682 << gold_spent_ << "; new value: " << (gold_spent_ + difference) << "\n";
683 gold_spent_ += difference; assert(gold_spent_ >= 0);
684}
685
687{
688 DBG_WB << "Resetting gold spent for side " << (team_index() + 1) << " to 0.";
689 gold_spent_ = 0;
690}
691
692void side_actions::update_recruited_unit(std::size_t old_id, unit& new_unit)
693{
694 for(const_iterator it = begin(); it != end(); ++it) {
695 if(move_ptr mp = std::dynamic_pointer_cast<move>(*it)) {
696 if(mp->raw_uid() == old_id) {
697 actions_.modify(it, [&](action_ptr& p) {
698 static_cast<move&>(*p).modify_unit(new_unit);
699 });
700 }
701 }
702 }
703}
704
705side_actions::iterator side_actions::safe_insert(std::size_t turn, std::size_t pos, const action_ptr& act)
706{
707 assert(act);
708 if(pos == 0) {
709 return actions_.push_front(turn, act);
710 } else {
711 return actions_.insert(turn_begin(turn) + pos, act);
712 }
713}
714
720
722{
723 resources::whiteboard->queue_net_cmd(team_index_, make_net_cmd_insert(itor, act));
724 return actions_.insert(itor, act);
725}
726
728{
729 //raw_enqueue() creates actions_[turn_num] if it doesn't exist already, so we
730 //have to do it first -- before subsequently calling actions_[turn_num].size().
731 iterator result = actions_.queue(turn_num, act);
732 if(result != end()) {
733 resources::whiteboard->queue_net_cmd(team_index_, make_net_cmd_insert(turn_num, turn_size(turn_num) - 1, act));
734 // The insert position is turn_size(turn_num)-1 since we already inserted the action.
735 }
736 return result;
737}
738
740{
741 action_ptr action = *itor;
742 resources::whiteboard->pre_delete_action(action); //misc cleanup
743 iterator return_itor = actions_.erase(itor);
744 resources::whiteboard->post_delete_action(action);
745 return return_itor;
746}
747side_actions::iterator side_actions::queue_move(std::size_t turn, const unit& mover, const pathfind::marked_route& route, const arrow_ptr& arrow, fake_unit_ptr fake_unit)
748{
749 move_ptr new_move(std::make_shared<move>(team_index(), hidden_, mover, route, arrow, std::move(fake_unit)));
750 return queue_action(turn, new_move);
751}
752
753side_actions::iterator side_actions::queue_attack(std::size_t turn, const unit& mover, const map_location& target_hex, int weapon_choice, const pathfind::marked_route& route, const arrow_ptr& arrow, fake_unit_ptr fake_unit)
754{
755 attack_ptr new_attack(std::make_shared<attack>(team_index(), hidden_, mover, target_hex, weapon_choice, route, arrow, std::move(fake_unit)));
756 return queue_action(turn, new_attack);
757}
758
759side_actions::iterator side_actions::queue_recruit(std::size_t turn, const std::string& unit_name, const map_location& recruit_hex)
760{
761 recruit_ptr new_recruit(std::make_shared<recruit>(team_index(), hidden_, unit_name, recruit_hex));
762 return queue_action(turn, new_recruit);
763}
764
765side_actions::iterator side_actions::queue_recall(std::size_t turn, const unit& unit, const map_location& recall_hex)
766{
767 recall_ptr new_recall(std::make_shared<recall>(team_index(), hidden_, unit, recall_hex));
768 return queue_action(turn, new_recall);
769}
770
772{
773 suppose_dead_ptr new_suppose_dead(std::make_shared<suppose_dead>(team_index(), hidden_, curr_unit, loc));
774 return queue_action(turn, new_suppose_dead);
775}
776
778{
779 std::string type = cmd["type"];
780
781 if(type=="insert") {
782 std::size_t turn = cmd["turn"].to_int();
783 std::size_t pos = cmd["pos"].to_int();
785 if(!act) {
786 ERR_WB << "side_actions::execute_network_command(): received invalid action data!";
787 return;
788 }
789
790 iterator itor = safe_insert(turn, pos, act);
791 if(itor >= end()) {
792 ERR_WB << "side_actions::execute_network_command(): received invalid insertion position!";
793 return;
794 }
795
796 LOG_WB << "Command received: action inserted on turn #" << turn << ", position #" << pos << ": " << act;
797
798 //update numbering hexes as necessary
799 ++itor;
800 for(iterator end_itor = end(); itor != end_itor; ++itor) {
801 display::get_singleton()->invalidate((*itor)->get_numbering_hex());
802 }
803 } else if(type=="replace") {
804 std::size_t turn = cmd["turn"].to_int();
805 std::size_t pos = cmd["pos"].to_int();
807 if(!act) {
808 ERR_WB << "side_actions::execute_network_command(): received invalid action data!";
809 return;
810 }
811
812 iterator itor = turn_begin(turn) + pos;
813 if(itor >= end() || get_turn(itor) != turn) {
814 ERR_WB << "side_actions::execute_network_command(): received invalid pos!";
815 return;
816 }
817
818 if(!actions_.replace(itor, act)){
819 ERR_WB << "side_actions::execute_network_command(): replace failed!";
820 return;
821 }
822
823 LOG_WB << "Command received: action replaced on turn #" << turn << ", position #" << pos << ": " << act;
824 } else if(type=="remove") {
825 std::size_t turn = cmd["turn"].to_int();
826 std::size_t pos = cmd["pos"].to_int();
827
828 iterator itor = turn_begin(turn) + pos;
829 if(itor >= end() || get_turn(itor) != turn) {
830 ERR_WB << "side_actions::execute_network_command(): received invalid pos!";
831 return;
832 }
833
834 itor = safe_erase(itor);
835
836 LOG_WB << "Command received: action removed on turn #" << turn << ", position #" << pos;
837
838 //update numbering hexes as necessary
839 for(iterator end_itor = end(); itor != end_itor; ++itor) {
840 display::get_singleton()->invalidate((*itor)->get_numbering_hex());
841 }
842 } else if(type=="bump_later") {
843 std::size_t turn = cmd["turn"].to_int();
844 std::size_t pos = cmd["pos"].to_int();
845
846 iterator itor = turn_begin(turn) + pos;
847 if(itor+1 >= end() || get_turn(itor) != turn) {
848 ERR_WB << "side_actions::execute_network_command(): received invalid pos!";
849 return;
850 }
851
852 action_ptr first_action = *itor;
853 action_ptr second_action = itor[1];
854 bump_later(itor, false);
855
856 LOG_WB << "Command received: action bumped later from turn #" << turn << ", position #" << pos;
857
858 //update numbering hexes as necessary
859 display::get_singleton()->invalidate(first_action->get_numbering_hex());
860 display::get_singleton()->invalidate(second_action->get_numbering_hex());
861 } else if(type=="clear") {
862 LOG_WB << "Command received: clear";
863 clear();
864 } else if(type=="refresh") {
865 LOG_WB << "Command received: refresh";
866 clear();
867 for(const net_cmd& sub_cmd : cmd.child_range("net_cmd"))
868 execute_net_cmd(sub_cmd);
869 } else {
870 ERR_WB << "side_actions::execute_network_command(): received invalid type!";
871 return;
872 }
873
874 resources::whiteboard->validate_viewer_actions();
875}
876
877side_actions::net_cmd side_actions::make_net_cmd_insert(std::size_t turn_num, std::size_t pos, const action_const_ptr& act) const
878{
879 net_cmd result;
880 result["type"] = "insert";
881 result["turn"] = static_cast<int>(turn_num);
882 result["pos"] = static_cast<int>(pos);
883 result.add_child("action", act->to_config());
884 return result;
885}
887{
888 if(pos == begin()) {
889 return make_net_cmd_insert(0,0,act);
890 } else {
891 const_iterator prec = pos - 1;
892 return make_net_cmd_insert(get_turn(prec), actions_.position_in_turn(prec)+1, act);
893 }
894}
896{
897 net_cmd result;
898 result["type"] = "replace";
899 result["turn"] = static_cast<int>(get_turn(pos));
900 result["pos"] = static_cast<int>(actions_.position_in_turn(pos));
901 result.add_child("action", act->to_config());
902 return result;
903}
905{
906 net_cmd result;
907 result["type"] = "remove";
908 result["turn"] = static_cast<int>(get_turn(pos));
909 result["pos"] = static_cast<int>(actions_.position_in_turn(pos));
910 return result;
911}
913{
914 net_cmd result;
915 result["type"] = "bump_later";
916 result["turn"] = static_cast<int>(get_turn(pos));
917 result["pos"] = static_cast<int>(actions_.position_in_turn(pos));
918 return result;
919}
921{
922 net_cmd result;
923 result["type"] = "clear";
924 return result;
925}
927{
928 net_cmd result;
929 result["type"] = "refresh";
930
931 for(const_iterator itor = begin(), end_itor = end(); itor != end_itor; ++itor) {
932 result.add_child("net_cmd", make_net_cmd_insert(get_turn(itor), actions_.position_in_turn(itor), *itor));
933 }
934
935 return result;
936}
937
939{
940 //find units who still have plans for turn 0 (i.e. were too lazy to finish their jobs)
941 std::set<unit_const_ptr> lazy_units;
942 for(const action_ptr& act : iter_turn(0)) {
943 unit_const_ptr u = act->get_unit();
944 if(u) {
945 lazy_units.insert(u);
946 }
947 }
948
949 //push their plans back one turn
950 std::set<unit_const_ptr>::iterator lazy_end = lazy_units.end();
951 iterator itor = end();
952 while(itor != begin()) {
953 --itor;
954 action_ptr act = *itor;
955
956 if(lazy_units.find(act->get_unit()) != lazy_end) {
957 safe_insert(get_turn(itor)+1, 0, act);
958 itor = actions_.erase(itor);
959 }
960 }
961
962 //push any remaining first-turn plans into the second turn
963 for(iterator act=turn_begin(0), end=turn_end(0); act!=end; ++act) {
964 safe_insert(1, 0, *act);
965 }
966
967 //shift everything forward one turn
970}
971
977
978} //end namespace wb
map_location loc
Definition move.cpp:172
void clear()
Clears the stack of undoable (and redoable) actions.
Definition undo.cpp:131
Arrows destined to be drawn on the map.
Definition arrow.hpp:30
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
config & add_child(std::string_view key)
Definition config.cpp:436
void insert(std::string_view key, T &&value)
Inserts an attribute into the config.
Definition config.hpp:519
child_itors child_range(std::string_view key)
Definition config.cpp:268
config & mandatory_child(std::string_view key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition config.cpp:362
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition display.cpp:2927
static display * get_singleton()
Returns the display object if a display object exists.
Definition display.hpp:102
Holds a temporary unit that can be drawn on the map without being placed in the unit_map.
bool can_recruit_on(const map_location &leader_loc, const map_location &recruit_loc, int side) const
Checks to see if a leader at leader_loc could recruit on recruit_loc.
Exception used to escape form the ai or ui code to playsingle_controller::play_side.
This class represents a single unit of a specific type.
Definition unit.hpp:39
Abstract base class for all the whiteboard planned actions.
Definition action.hpp:34
bool valid()
Returns whether this action is valid or not.
Definition action.hpp:135
static action_ptr from_config(const config &, bool hidden)
Constructs an object of a subclass of wb::action using a config.
Definition action.cpp:60
virtual void execute(bool &success, bool &complete)=0
Output parameters: success: Whether or not to continue an execute-all after this execution complete: ...
A planned move, represented on the map by an arrow and a ghosted unit in the destination hex.
Definition move.hpp:36
void modify_unit(unit &new_unit)
Definition move.cpp:316
bool modify(iterator position, Modifier mod)
iterator turn_end(std::size_t turn_num)
action_set::index< T >::type & get()
Returns a given index.
action_set::index< chronological >::type::iterator iterator
action_limits turn_beginnings_
Contains a list of iterator to the beginning of each turn.
iterator end()
Returns the iterator for the position after the last executed action within the actions queue.
std::size_t get_turn(const_iterator it) const
Returns the turn of a given iterator planned execution.
iterator bump_later(iterator position)
Moves an action later in the execution order.
bool replace(iterator it, action_ptr act)
Replaces the action at a given position with another action.
iterator queue(std::size_t turn_num, const action_ptr &action)
Queues an action to be executed last.
bool empty() const
Indicates whether the action queue is empty.
action_set::index< chronological >::type::const_iterator const_iterator
iterator turn_begin(std::size_t turn_num)
Returns the iterator for the first (executed earlier) action of a given turn within the actions queue...
std::size_t size() const
Returns the number of actions in the action queue.
iterator erase(iterator position)
Deletes the action at the specified position.
iterator begin()
Returns the iterator for the first (executed earlier) action within the actions queue.
iterator bump_earlier(iterator position)
Moves an action earlier in the execution order.
std::size_t turn_size(std::size_t turn_num) const
Returns the number of actions planned for turn turn_num.
iterator insert(iterator position, const action_ptr &action)
Inserts an action at the specified position.
std::size_t get_turn_impl(std::size_t begin, std::size_t end, const_iterator it) const
Binary search to find the occurring turn of the action pointed by an iterator.
iterator push_front(std::size_t turn, const action_ptr &action)
Pushes an action in front of a given turn.
void turn_shift()
Shift turn.
std::size_t position_in_turn(const_iterator it) const
Returns the position of a given iterator in its turn.
std::size_t num_turns() const
Returns the number of turns that have plans.
This internal whiteboard class holds the planned action queues for a team, and offers many utility me...
std::size_t team_index()
Returns the team index this action queue belongs to.
iterator find_first_action_at(map_location hex)
Find the first action occurring at a given hex.
iterator queue_recruit(std::size_t turn_num, const std::string &unit_name, const map_location &recruit_hex)
Queues a recruit to be executed last.
iterator queue_recall(std::size_t turn_num, const unit &unit, const map_location &recall_hex)
Queues a recall to be executed last.
iterator queue_attack(std::size_t turn_num, const unit &mover, const map_location &target_hex, int weapon_choice, const pathfind::marked_route &route, const arrow_ptr &arrow, fake_unit_ptr fake_unit)
Queues an attack or attack-move to be executed last.
net_cmd make_net_cmd_remove(const const_iterator &pos) const
std::size_t num_turns() const
Returns the number of turns that have plans.
iterator find_last_action_of(const unit &unit, iterator start_position)
Finds the last action that belongs to this unit, starting the search backwards from the specified pos...
bool empty() const
Indicates whether the action queue is empty.
iterator queue_action(std::size_t turn_num, const action_ptr &action)
Queues an action to be executed last.
iterator find_first_action_of(std::pair< T, T > between, iterator limit, Compare comp)
Find the (chronologically) first action between the iterators between.first and between....
std::deque< action_ptr > actions_of(const unit &unit)
void hide()
Sets whether or not the contents should be drawn on the screen.
void reset_gold_spent()
Set gold spent back to zero.
iterator turn_begin(std::size_t turn_num)
iterator synced_enqueue(std::size_t turn_num, const action_ptr &to_insert)
iterator queue_move(std::size_t turn_num, const unit &mover, const pathfind::marked_route &route, const arrow_ptr &arrow, fake_unit_ptr fake_unit)
Queues a move to be executed last.
iterator synced_insert(iterator itor, const action_ptr &to_insert)
iterator bump_earlier(iterator position, bool send_to_net=true)
Moves an action earlier in the execution order.
std::size_t get_turn_num_of(const unit &) const
Determines the appropriate turn number for the next action planned for this unit.
std::size_t team_index_
void get_numbers(const map_location &hex, numbers_t &result)
Gets called when display is drawing a hex to determine which numbers to draw on it.
std::size_t turn_size(std::size_t turn_num) const
Returns the number of actions planned for turn turn_num.
void change_gold_spent_by(int difference)
Used to track gold spending by recruits/recalls when building the future unit map.
iterator begin()
Returns the iterator for the first (executed earlier) action within the actions queue.
int gold_spent_
Used to store gold "spent" in planned recruits/recalls when the future unit map is applied.
container::const_iterator const_iterator
iterator insert_action(iterator position, const action_ptr &action)
Inserts an action at the specified position.
net_cmd make_net_cmd_insert(std::size_t turn_num, std::size_t pos, const action_const_ptr &) const
net_cmd make_net_cmd_clear() const
iterator remove_action(iterator position, bool validate_after_delete=true)
Deletes the action at the specified position.
iterator queue_suppose_dead(std::size_t turn_num, const unit &curr_unit, const map_location &loc)
Queues a suppose_dead to be executed last.
bool execute(iterator position)
Executes the specified action, if it exists in the queue.
container::iterator iterator
void update_recruited_unit(std::size_t old_id, unit &new_unit)
After a recruit action was executed the id of the unit was changed so we need to update the unitid of...
net_cmd make_net_cmd_replace(const const_iterator &pos, const action_const_ptr &) const
std::size_t get_turn(const_iterator it) const
Returns the turn of a given iterator planned execution.
iterator bump_later(iterator position, bool send_to_net=true)
Moves an action later in the execution order.
void clear()
Empties the action queue.
iterator synced_erase(iterator itor)
std::size_t count_actions_of(const unit &unit)
iterator safe_erase(const iterator &itor)
bool execute_next()
Executes the first action in the queue, and then deletes it.
iterator end()
Returns the iterator for the position after the last executed action within the actions queue.
bool unit_has_actions(const unit &unit)
void execute_net_cmd(const net_cmd &)
net_cmd make_net_cmd_bump_later(const const_iterator &pos) const
range_t iter_turn(std::size_t turn_num)
Returns an iterator range corresponding to the requested turn.
void set_team_index(std::size_t team_index)
Must be called only once, right after the team that owns this side_actions is added to the teams vect...
net_cmd make_net_cmd_refresh() const
iterator turn_end(std::size_t turn_num)
iterator safe_insert(std::size_t turn_num, std::size_t pos, const action_ptr &to_insert)
Abstract base class for all the visitors (cf GoF Visitor Design Pattern) the whiteboard uses.
Definition visitor.hpp:33
map_display and display: classes which take care of displaying the map and game-data on the screen.
Contains the exception interfaces used to signal completion of a scenario, campaign or turn.
std::size_t underlying_id() const
This unit's unique internal ID.
Definition unit.hpp:298
Main entry points of multiplayer mode.
game_board * gameboard
Definition resources.cpp:20
actions::undo_list * undo_stack
Definition resources.cpp:32
play_controller * controller
Definition resources.cpp:21
filter_context * filter_con
Definition resources.cpp:23
std::shared_ptr< wb::manager > whiteboard
Definition resources.cpp:33
STL namespace.
Definition display.hpp:45
std::shared_ptr< recruit > recruit_ptr
Definition typedefs.hpp:72
std::weak_ptr< action > weak_action_ptr
Definition typedefs.hpp:64
std::shared_ptr< suppose_dead > suppose_dead_ptr
Definition typedefs.hpp:76
std::shared_ptr< move > move_ptr
Definition typedefs.hpp:68
std::shared_ptr< action > action_ptr
Definition typedefs.hpp:62
std::shared_ptr< attack > attack_ptr
Definition typedefs.hpp:70
std::shared_ptr< arrow > arrow_ptr
Definition typedefs.hpp:60
unit_const_ptr find_backup_leader(const unit &leader)
For a given leader on a keep, find another leader on another keep in the same castle.
Definition utility.cpp:55
std::shared_ptr< action const > action_const_ptr
Definition typedefs.hpp:63
std::ostream & operator<<(std::ostream &s, const action_ptr &action)
Definition action.cpp:34
std::shared_ptr< recall > recall_ptr
Definition typedefs.hpp:74
std::shared_ptr< const unit > unit_const_ptr
Definition ptr.hpp:27
std::shared_ptr< attack_type > attack_ptr
Definition ptr.hpp:33
static config unit_name(const unit *u)
Definition reports.cpp:163
Encapsulates the map of the game.
Definition location.hpp:46
Structure which holds a single route and marks for special events.
Definition pathfind.hpp:142
std::set< std::size_t > secondary_numbers
std::vector< int > numbers_to_draw
std::vector< std::size_t > team_numbers
Tag for action_set's hashed_non_unique index.
Tag for action_set's hashed_non_unique index.
mock_party p
#define WRN_WB
Definition typedefs.hpp:26
#define ERR_WB
Definition typedefs.hpp:25
#define DBG_WB
Definition typedefs.hpp:28
#define LOG_WB
Definition typedefs.hpp:27
Various functions that implement the undoing (and redoing) of in-game commands.