1 /*
2 Copyright (C) 2017-2018 the Battle for Wesnoth Project https://www.wesnoth.org/
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY.
10
11 See the COPYING file for more details.
12 */
13
14 #include "actions/undo_action.hpp"
15 #include "game_board.hpp"
16 #include "scripting/game_lua_kernel.hpp"
17 #include "resources.hpp"
18 #include "variable.hpp" // vconfig
19 #include "game_data.hpp"
20 #include "units/unit.hpp"
21 #include "sound.hpp"
22
23 #include <cassert>
24 #include <iterator>
25 #include <algorithm>
26
27 namespace actions
28 {
29
undo_event(const config & cmds,const game_events::queued_event & ctx)30 undo_event::undo_event(const config& cmds, const game_events::queued_event& ctx)
31 : commands(cmds)
32 , data(ctx.data)
33 , loc1(ctx.loc1)
34 , loc2(ctx.loc2)
35 , filter_loc1(ctx.loc1.filter_loc())
36 , filter_loc2(ctx.loc2.filter_loc())
37 , uid1(), uid2()
38 {
39 unit_const_ptr u1 = ctx.loc1.get_unit(), u2 = ctx.loc2.get_unit();
40 if(u1) {
41 id1 = u1->id();
42 uid1 = u1->underlying_id();
43 }
44 if(u2) {
45 id2 = u2->id();
46 uid2 = u2->underlying_id();
47 }
48 }
49
undo_event(const config & first,const config & second,const config & weapons,const config & cmds)50 undo_event::undo_event(const config& first, const config& second, const config& weapons, const config& cmds)
51 : commands(cmds)
52 , data(weapons)
53 , loc1(first["x"], first["y"], wml_loc())
54 , loc2(second["x"], second["y"], wml_loc())
55 , filter_loc1(first["filter_x"], first["filter_y"], wml_loc())
56 , filter_loc2(second["filter_x"], second["filter_y"], wml_loc())
57 , uid1(first["underlying_id"])
58 , uid2(second["underlying_id"])
59 , id1(first["id"])
60 , id2(second["id"])
61 {
62 }
63
undo_action()64 undo_action::undo_action()
65 : undo_action_base()
66 , unit_id_diff(synced_context::get_unit_id_diff())
67 {
68 auto& undo = synced_context::get_undo_commands();
69 auto command_transformer = [](const std::pair<config, game_events::queued_event>& p) {
70 return undo_event(p.first, p.second);
71 };
72 std::transform(undo.begin(), undo.end(), std::back_inserter(umc_commands_undo), command_transformer);
73 undo.clear();
74 }
75
undo_action(const config & cfg)76 undo_action::undo_action(const config& cfg)
77 : undo_action_base()
78 , unit_id_diff(cfg["unit_id_diff"])
79 {
80 read_event_vector(umc_commands_undo, cfg, "undo_actions");
81 }
82
83 namespace {
get_unit(size_t uid,const std::string & id)84 unit_ptr get_unit(size_t uid, const std::string& id) {
85 assert(resources::gameboard);
86 auto iter = resources::gameboard->units().find(uid);
87 if(!iter.valid() || iter->id() != id) {
88 return nullptr;
89 }
90 return iter.get_shared_ptr();
91 }
execute_event(const undo_event & e,std::string tag)92 void execute_event(const undo_event& e, std::string tag) {
93 assert(resources::lua_kernel);
94 assert(resources::gamedata);
95
96 config::attribute_value& x1 = resources::gamedata->get_variable("x1");
97 config::attribute_value& y1 = resources::gamedata->get_variable("y1");
98 config::attribute_value& x2 = resources::gamedata->get_variable("x2");
99 config::attribute_value& y2 = resources::gamedata->get_variable("y2");
100 int oldx1 = x1, oldy1 = y1, oldx2 = x2, oldy2 = y2;
101 x1 = e.filter_loc1.wml_x(); y1 = e.filter_loc1.wml_y();
102 x2 = e.filter_loc2.wml_x(); y2 = e.filter_loc2.wml_y();
103
104 std::unique_ptr<scoped_xy_unit> u1, u2;
105 if(unit_ptr who = get_unit(e.uid1, e.id1)) {
106 u1.reset(new scoped_xy_unit("unit", who->get_location(), resources::gameboard->units()));
107 }
108 if(unit_ptr who = get_unit(e.uid2, e.id2)) {
109 u2.reset(new scoped_xy_unit("unit", who->get_location(), resources::gameboard->units()));
110 }
111
112 scoped_weapon_info w1("weapon", e.data.child("first"));
113 scoped_weapon_info w2("second_weapon", e.data.child("second"));
114
115 game_events::queued_event q(tag, "", map_location(x1, y1, wml_loc()), map_location(x2, y2, wml_loc()), e.data);
116 resources::lua_kernel->run_wml_action("command", vconfig(e.commands), q);
117 sound::commit_music_changes();
118
119 x1 = oldx1; y1 = oldy1;
120 x2 = oldx2; y2 = oldy2;
121 }
122 }
123
execute_undo_umc_wml()124 void undo_action::execute_undo_umc_wml()
125 {
126 for(const undo_event& e : umc_commands_undo)
127 {
128 execute_event(e, "undo");
129 }
130 }
131
132
write(config & cfg) const133 void undo_action::write(config & cfg) const
134 {
135 cfg["unit_id_diff"] = unit_id_diff;
136 write_event_vector(umc_commands_undo, cfg, "undo_actions");
137 undo_action_base::write(cfg);
138 }
139
read_event_vector(event_vector & vec,const config & cfg,const std::string & tag)140 void undo_action::read_event_vector(event_vector& vec, const config& cfg, const std::string& tag)
141 {
142 for(auto c : cfg.child_range(tag)) {
143 vec.emplace_back(c.child("filter"), c.child("filter_second"), c.child("filter_weapons"), c.child("commands"));
144 }
145 }
146
write_event_vector(const event_vector & vec,config & cfg,const std::string & tag)147 void undo_action::write_event_vector(const event_vector& vec, config& cfg, const std::string& tag)
148 {
149 for(const auto& evt : vec)
150 {
151 config& entry = cfg.add_child(tag);
152 config& first = entry.add_child("filter");
153 config& second = entry.add_child("filter_second");
154 entry.add_child("filter_weapons", evt.data);
155 entry.add_child("command", evt.commands);
156 // First location
157 first["filter_x"] = evt.filter_loc1.wml_x();
158 first["filter_y"] = evt.filter_loc1.wml_y();
159 first["underlying_id"] = evt.uid1;
160 first["id"] = evt.id1;
161 first["x"] = evt.loc1.wml_x();
162 first["y"] = evt.loc1.wml_y();
163 // Second location
164 second["filter_x"] = evt.filter_loc2.wml_x();
165 second["filter_y"] = evt.filter_loc2.wml_y();
166 second["underlying_id"] = evt.uid2;
167 second["id"] = evt.id2;
168 second["x"] = evt.loc2.wml_x();
169 second["y"] = evt.loc2.wml_y();
170 }
171 }
172
173 }
174