The Battle for Wesnoth  1.19.7+dev
undo_action.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2017 - 2024
3  by David White <dave@whitevine.net>
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 #include "actions/undo_action.hpp"
17 #include "game_board.hpp"
18 #include "log.hpp" // for LOG_STREAM, logger, etc
20 #include "resources.hpp"
21 #include "variable.hpp" // vconfig
22 #include "game_data.hpp"
23 #include "units/unit.hpp"
24 #include "utils/ranges.hpp"
25 #include "sound.hpp"
26 
27 #include <cassert>
28 #include <iterator>
29 #include <algorithm>
30 
31 static lg::log_domain log_engine("engine");
32 #define ERR_NG LOG_STREAM(err, log_engine)
33 #define LOG_NG LOG_STREAM(info, log_engine)
34 
35 
36 namespace actions
37 {
38 
39 
41  : steps_()
42  , unit_id_diff_(0)
43 {
44 }
45 
47 {
48  int last_unit_id = resources::gameboard->unit_id_manager().get_save_id();
49  for(auto& p_step : steps_ | utils::views::reverse) {
50  p_step->undo(side);
51  }
52  if(last_unit_id - unit_id_diff_ < 0) {
53  ERR_NG << "Next unit id is below 0 after undoing";
54  }
56  return true;
57 }
58 
60 {
61  steps_.emplace_back(std::move(action));
62 }
63 
64 
66 {
67  for(const config& step : cfg.child_range("step")) {
68  auto& factory = get_factories()[step["type"]];
69  add(factory(step));
70  }
71 }
73 {
74  for(auto& p_step : steps_) {
75  p_step->write(cfg.add_child("step"));
76  }
77 }
78 
80 {
81  static t_factory_map res;
82  return res;
83 }
84 
85 
86 
87 
88 
89 
90 undo_event::undo_event(int fcn_idx, const config& args, const game_events::queued_event& ctx)
91  : lua_idx(fcn_idx)
92  , commands(args)
93  , data(ctx.data)
94  , loc1(ctx.loc1)
95  , loc2(ctx.loc2)
96  , filter_loc1(ctx.loc1.filter_loc())
97  , filter_loc2(ctx.loc2.filter_loc())
98  , uid1(), uid2()
99 {
100  unit_const_ptr u1 = ctx.loc1.get_unit(), u2 = ctx.loc2.get_unit();
101  if(u1) {
102  id1 = u1->id();
103  uid1 = u1->underlying_id();
104  }
105  if(u2) {
106  id2 = u2->id();
107  uid2 = u2->underlying_id();
108  }
109 }
110 
112  : commands(cmds)
113  , data(ctx.data)
114  , loc1(ctx.loc1)
115  , loc2(ctx.loc2)
116  , filter_loc1(ctx.loc1.filter_loc())
117  , filter_loc2(ctx.loc2.filter_loc())
118  , uid1(), uid2()
119 {
120  unit_const_ptr u1 = ctx.loc1.get_unit(), u2 = ctx.loc2.get_unit();
121  if(u1) {
122  id1 = u1->id();
123  uid1 = u1->underlying_id();
124  }
125  if(u2) {
126  id2 = u2->id();
127  uid2 = u2->underlying_id();
128  }
129 }
130 
131 undo_event::undo_event(const config& first, const config& second, const config& weapons, const config& cmds)
132  : commands(cmds)
133  , data(weapons)
134  , loc1(first["x"], first["y"], wml_loc())
135  , loc2(second["x"], second["y"], wml_loc())
136  , filter_loc1(first["filter_x"], first["filter_y"], wml_loc())
137  , filter_loc2(second["filter_x"], second["filter_y"], wml_loc())
138  , uid1(first["underlying_id"].to_size_t())
139  , uid2(second["underlying_id"].to_size_t())
140  , id1(first["id"])
141  , id2(second["id"])
142 {
143 }
144 
146  : undo_event(cfg.child_or_empty("filter"),
147  cfg.child_or_empty("filter_second"),
148  cfg.child_or_empty("data"),
149  cfg.child_or_empty("command"))
150 {
151 }
152 
153 
154 namespace
155 {
156 unit_ptr get_unit(std::size_t uid, const std::string& id)
157 {
158  assert(resources::gameboard);
159  auto iter = resources::gameboard->units().find(uid);
160  if(!iter.valid() || iter->id() != id) {
161  return nullptr;
162  }
163  return iter.get_shared_ptr();
164 }
165 } // namespace
166 
168 {
169  undo_event& e = *this;
170  std::string tag = "undo";
171  assert(resources::lua_kernel);
172  assert(resources::gamedata);
173 
178  std::swap(x1, resources::gamedata->get_variable("x1"));
179  std::swap(y1, resources::gamedata->get_variable("y1"));
180  std::swap(x2, resources::gamedata->get_variable("x2"));
181  std::swap(y2, resources::gamedata->get_variable("y2"));
182 
183  std::unique_ptr<scoped_xy_unit> u1, u2;
184  if(unit_ptr who = get_unit(e.uid1, e.id1)) {
185  u1.reset(new scoped_xy_unit("unit", who->get_location(), resources::gameboard->units()));
186  }
187  if(unit_ptr who = get_unit(e.uid2, e.id2)) {
188  u2.reset(new scoped_xy_unit("unit", who->get_location(), resources::gameboard->units()));
189  }
190 
191  scoped_weapon_info w1("weapon", e.data.optional_child("first"));
192  scoped_weapon_info w2("second_weapon", e.data.optional_child("second"));
193 
194  game_events::queued_event q(tag, "", map_location(x1, y1, wml_loc()), map_location(x2, y2, wml_loc()), e.data);
195  if(e.lua_idx.has_value()) {
196  resources::lua_kernel->run_wml_event(*e.lua_idx, vconfig(e.commands), q);
197  } else {
198  resources::lua_kernel->run_wml_action("command", vconfig(e.commands), q);
199  }
201 
202  std::swap(x1, resources::gamedata->get_variable("x1"));
203  std::swap(y1, resources::gamedata->get_variable("y1"));
204  std::swap(x2, resources::gamedata->get_variable("x2"));
205  std::swap(y2, resources::gamedata->get_variable("y2"));
206  return true;
207 }
208 
209 void undo_event::write(config& cfg) const
210 {
211  undo_action::write(cfg);
212  auto& evt = *this;
213  if(evt.lua_idx.has_value()) {
214  // TODO: Log warning that this cannot be serialized
215  return;
216  }
217  config& entry = cfg;
218  config& first = entry.add_child("filter");
219  config& second = entry.add_child("filter_second");
220  entry.add_child("data", evt.data);
221  entry.add_child("command", evt.commands);
222  // First location
223  first["filter_x"] = evt.filter_loc1.wml_x();
224  first["filter_y"] = evt.filter_loc1.wml_y();
225  first["underlying_id"] = evt.uid1;
226  first["id"] = evt.id1;
227  first["x"] = evt.loc1.wml_x();
228  first["y"] = evt.loc1.wml_y();
229  // Second location
230  second["filter_x"] = evt.filter_loc2.wml_x();
231  second["filter_y"] = evt.filter_loc2.wml_y();
232  second["underlying_id"] = evt.uid2;
233  second["id"] = evt.id2;
234  second["x"] = evt.loc2.wml_x();
235  second["y"] = evt.loc2.wml_y();
236 }
237 
238 
240 
241 } // namespace actions
std::unique_ptr< undo_action > t_step_ptr
Definition: undo_action.hpp:32
void read(const config &cfg)
Creates the list of undo steps based on a config.
Definition: undo_action.cpp:65
std::map< std::string, t_factory > t_factory_map
Definition: undo_action.hpp:58
void add(t_step_ptr &&action)
Definition: undo_action.cpp:59
static t_factory_map & get_factories()
Definition: undo_action.cpp:79
undo_event(int fcn_idx, const config &args, const game_events::queued_event &ctx)
Definition: undo_action.cpp:90
virtual bool undo(int side)
Undoes this action.
virtual void write(config &cfg) const
Writes this into the provided config.
Variant for storing WML attributes.
static config_attribute_value create(const T val)
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
child_itors child_range(config_key_type key)
Definition: config.cpp:272
config & add_child(config_key_type key)
Definition: config.cpp:440
n_unit::id_manager & unit_id_manager()
Definition: game_board.hpp:74
virtual const unit_map & units() const override
Definition: game_board.hpp:107
bool run_wml_event(int ref, const vconfig &args, const game_events::queued_event &ev, bool *out=nullptr)
Run a WML stored in the Lua registry.
bool run_wml_action(const std::string &, const vconfig &, const game_events::queued_event &)
Runs a command from an event handler.
std::size_t get_save_id() const
Used for saving id to savegame.
Definition: id.cpp:42
void set_save_id(std::size_t)
Definition: id.cpp:47
unit_iterator find(std::size_t id)
Definition: map.cpp:302
A variable-expanding proxy for the config class.
Definition: variable.hpp:45
void swap(config &lhs, config &rhs)
Implement non-member swap function for std::swap (calls config::swap).
Definition: config.cpp:1343
Standard logging facilities (interface).
static auto red_undo_event
std::string tag(const std::string &tag_name, Args &&... contents)
Definition: markup.hpp:45
game_board * gameboard
Definition: resources.cpp:20
game_data * gamedata
Definition: resources.cpp:22
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
void commit_music_changes()
Definition: sound.cpp:843
constexpr auto reverse
Definition: ranges.hpp:40
std::string_view data
Definition: picture.cpp:178
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:26
virtual void write(config &cfg) const
Writes this into the provided config.
Definition: undo_action.hpp:91
unit_const_ptr get_unit() const
entity_location loc1
Definition: pump.hpp:65
entity_location loc2
Definition: pump.hpp:66
Encapsulates the map of the game.
Definition: location.hpp:45
pointer get_shared_ptr() const
This is exactly the same as operator-> but it's slightly more readable, and can replace &*iter syntax...
Definition: map.hpp:217
static lg::log_domain log_engine("engine")
#define ERR_NG
Definition: undo_action.cpp:32
#define e