The Battle for Wesnoth  1.19.0+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"
19 #include "resources.hpp"
20 #include "variable.hpp" // vconfig
21 #include "game_data.hpp"
22 #include "units/unit.hpp"
23 #include "sound.hpp"
24 
25 #include <cassert>
26 #include <iterator>
27 #include <algorithm>
28 
29 namespace actions
30 {
31 
32 undo_event::undo_event(int fcn_idx, const config& args, const game_events::queued_event& ctx)
33  : lua_idx(fcn_idx)
34  , commands(args)
35  , data(ctx.data)
36  , loc1(ctx.loc1)
37  , loc2(ctx.loc2)
38  , filter_loc1(ctx.loc1.filter_loc())
39  , filter_loc2(ctx.loc2.filter_loc())
40  , uid1(), uid2()
41 {
42  unit_const_ptr u1 = ctx.loc1.get_unit(), u2 = ctx.loc2.get_unit();
43  if(u1) {
44  id1 = u1->id();
45  uid1 = u1->underlying_id();
46  }
47  if(u2) {
48  id2 = u2->id();
49  uid2 = u2->underlying_id();
50  }
51 }
52 
54  : commands(cmds)
55  , data(ctx.data)
56  , loc1(ctx.loc1)
57  , loc2(ctx.loc2)
58  , filter_loc1(ctx.loc1.filter_loc())
59  , filter_loc2(ctx.loc2.filter_loc())
60  , uid1(), uid2()
61 {
62  unit_const_ptr u1 = ctx.loc1.get_unit(), u2 = ctx.loc2.get_unit();
63  if(u1) {
64  id1 = u1->id();
65  uid1 = u1->underlying_id();
66  }
67  if(u2) {
68  id2 = u2->id();
69  uid2 = u2->underlying_id();
70  }
71 }
72 
73 undo_event::undo_event(const config& first, const config& second, const config& weapons, const config& cmds)
74  : commands(cmds)
75  , data(weapons)
76  , loc1(first["x"], first["y"], wml_loc())
77  , loc2(second["x"], second["y"], wml_loc())
78  , filter_loc1(first["filter_x"], first["filter_y"], wml_loc())
79  , filter_loc2(second["filter_x"], second["filter_y"], wml_loc())
80  , uid1(first["underlying_id"])
81  , uid2(second["underlying_id"])
82  , id1(first["id"])
83  , id2(second["id"])
84 {
85 }
86 
89  , unit_id_diff(synced_context::get_unit_id_diff())
90 {
92  auto command_transformer = [](const synced_context::event_info& p) {
93  if(p.lua_.has_value()) {
94  return undo_event(*p.lua_, p.cmds_, p.evt_);
95  } else {
96  return undo_event(p.cmds_, p.evt_);
97  }
98  };
99  std::transform(undo.begin(), undo.end(), std::back_inserter(umc_commands_undo), command_transformer);
100  undo.clear();
101 }
102 
104  : undo_action_base()
105  , unit_id_diff(cfg["unit_id_diff"])
106 {
107  read_event_vector(umc_commands_undo, cfg, "undo_actions");
108 }
109 
110 namespace {
111  unit_ptr get_unit(std::size_t uid, const std::string& id) {
112  assert(resources::gameboard);
113  auto iter = resources::gameboard->units().find(uid);
114  if(!iter.valid() || iter->id() != id) {
115  return nullptr;
116  }
117  return iter.get_shared_ptr();
118  }
119  void execute_event(const undo_event& e, std::string tag) {
120  assert(resources::lua_kernel);
121  assert(resources::gamedata);
122 
127  int oldx1 = x1, oldy1 = y1, oldx2 = x2, oldy2 = y2;
128  x1 = e.filter_loc1.wml_x(); y1 = e.filter_loc1.wml_y();
129  x2 = e.filter_loc2.wml_x(); y2 = e.filter_loc2.wml_y();
130 
131  std::unique_ptr<scoped_xy_unit> u1, u2;
132  if(unit_ptr who = get_unit(e.uid1, e.id1)) {
133  u1.reset(new scoped_xy_unit("unit", who->get_location(), resources::gameboard->units()));
134  }
135  if(unit_ptr who = get_unit(e.uid2, e.id2)) {
136  u2.reset(new scoped_xy_unit("unit", who->get_location(), resources::gameboard->units()));
137  }
138 
139  scoped_weapon_info w1("weapon", e.data.optional_child("first"));
140  scoped_weapon_info w2("second_weapon", e.data.optional_child("second"));
141 
142  game_events::queued_event q(tag, "", map_location(x1, y1, wml_loc()), map_location(x2, y2, wml_loc()), e.data);
143  if(e.lua_idx.has_value()) {
144  resources::lua_kernel->run_wml_event(*e.lua_idx, vconfig(e.commands), q);
145  } else {
146  resources::lua_kernel->run_wml_action("command", vconfig(e.commands), q);
147  }
149 
150  x1 = oldx1; y1 = oldy1;
151  x2 = oldx2; y2 = oldy2;
152  }
153 }
154 
156 {
157  for(const undo_event& e : umc_commands_undo)
158  {
159  execute_event(e, "undo");
160  }
161 }
162 
163 
164 void undo_action::write(config & cfg) const
165 {
166  cfg["unit_id_diff"] = unit_id_diff;
167  write_event_vector(umc_commands_undo, cfg, "undo_actions");
169 }
170 
171 void undo_action::read_event_vector(event_vector& vec, const config& cfg, const std::string& tag)
172 {
173  for(auto c : cfg.child_range(tag)) {
174  vec.emplace_back(c.child_or_empty("filter"), c.child_or_empty("filter_second"), c.child_or_empty("data"), c.child_or_empty("command"));
175  }
176 }
177 
178 void undo_action::write_event_vector(const event_vector& vec, config& cfg, const std::string& tag)
179 {
180  for(const auto& evt : vec)
181  {
182  if(evt.lua_idx.has_value()) {
183  // TODO: Log warning that this cannot be serialized
184  continue;
185  }
186  config& entry = cfg.add_child(tag);
187  config& first = entry.add_child("filter");
188  config& second = entry.add_child("filter_second");
189  entry.add_child("data", evt.data);
190  entry.add_child("command", evt.commands);
191  // First location
192  first["filter_x"] = evt.filter_loc1.wml_x();
193  first["filter_y"] = evt.filter_loc1.wml_y();
194  first["underlying_id"] = evt.uid1;
195  first["id"] = evt.id1;
196  first["x"] = evt.loc1.wml_x();
197  first["y"] = evt.loc1.wml_y();
198  // Second location
199  second["filter_x"] = evt.filter_loc2.wml_x();
200  second["filter_y"] = evt.filter_loc2.wml_y();
201  second["underlying_id"] = evt.uid2;
202  second["id"] = evt.id2;
203  second["x"] = evt.loc2.wml_x();
204  second["y"] = evt.loc2.wml_y();
205  }
206 }
207 
208 }
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
child_itors child_range(config_key_type key)
Definition: config.cpp:273
config & add_child(config_key_type key)
Definition: config.cpp:441
virtual const unit_map & units() const override
Definition: game_board.hpp:106
config::attribute_value & get_variable(const std::string &varname)
throws invalid_variablename_exception if varname is no valid variable name.
Definition: game_data.cpp:66
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.
static event_list & get_undo_commands()
unit_iterator find(std::size_t id)
Definition: map.cpp:302
A variable-expanding proxy for the config class.
Definition: variable.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:835
std::string_view data
Definition: picture.cpp:194
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:26
Records information to be able to undo an action.
Definition: undo_action.hpp:42
virtual void write(config &cfg) const
Writes this into the provided config.
Definition: undo_action.hpp:56
event_vector umc_commands_undo
Definition: undo_action.hpp:92
virtual void write(config &cfg) const
Writes this into the provided config.
std::vector< undo_event > event_vector
actions wml (specified by wml) that should be executed when undoing this command.
Definition: undo_action.hpp:91
undo_action()
Default constructor.
Definition: undo_action.cpp:87
static void read_event_vector(event_vector &vec, const config &cfg, const std::string &tag)
virtual bool undo(int side)=0
Undoes this action.
static void write_event_vector(const event_vector &vec, config &cfg, const std::string &tag)
int unit_id_diff
the difference in the unit ids TODO: does it really make sense to allow undoing if the unit id counte...
Definition: undo_action.hpp:89
undo_event(int fcn_idx, const config &args, const game_events::queued_event &ctx)
Definition: undo_action.cpp:32
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:38
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
mock_char c
mock_party p
#define e