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