Merge lp:~widelands-dev/widelands/warehouse_refactor into lp:widelands
- warehouse_refactor
- Merge into trunk
Proposed by
SirVer
Status: | Merged |
---|---|
Merged at revision: | 8368 |
Proposed branch: | lp:~widelands-dev/widelands/warehouse_refactor |
Merge into: | lp:widelands |
Diff against target: |
1066 lines (+339/-307) 19 files modified
src/ai/defaultai_warfare.cc (+3/-3) src/economy/route.h (+4/-2) src/logic/CMakeLists.txt (+2/-2) src/logic/cmd_queue.h (+0/-1) src/logic/findimmovable.cc (+5/-3) src/logic/findimmovable.h (+2/-2) src/logic/map_objects/map_object.h (+2/-2) src/logic/map_objects/tribes/attack_target.h (+43/-56) src/logic/map_objects/tribes/building.cc (+7/-1) src/logic/map_objects/tribes/building.h (+10/-0) src/logic/map_objects/tribes/militarysite.cc (+141/-136) src/logic/map_objects/tribes/militarysite.h (+16/-11) src/logic/map_objects/tribes/soldier.cc (+17/-15) src/logic/map_objects/tribes/soldier.h (+1/-1) src/logic/map_objects/tribes/warehouse.cc (+53/-45) src/logic/map_objects/tribes/warehouse.h (+19/-15) src/logic/player.cc (+4/-4) src/wui/attack_box.h (+0/-1) src/wui/fieldaction.cc (+10/-7) |
To merge this branch: | bzr merge lp:~widelands-dev/widelands/warehouse_refactor |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
GunChleoc | Approve | ||
Review via email: mp+324367@code.launchpad.net |
Commit message
Refactor Attackable.
- Getting rid of some multiple inheritance in Warehouse and MilitarySite by
composing Attackable as a member into Building.
- Rename Attackable -> AttackTarget and change the interface to be cleaner.
- Mild related cleanups.
Description of the change
To post a comment you must log in.
Revision history for this message
SirVer (sirver) wrote : | # |
Revision history for this message
GunChleoc (gunchleoc) wrote : | # |
2 tiny nits, code LGTM otherwise. Not tested yet.
And trading - yay! :)
review:
Approve
Revision history for this message
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2227. State: passed. Details: https:/
Appveyor build 2062. State: success. Details: https:/
Revision history for this message
GunChleoc (gunchleoc) wrote : | # |
Tested now, so you can merge this any time :)
Revision history for this message
SirVer (sirver) : | # |
Revision history for this message
SirVer (sirver) wrote : | # |
@bunnybot merge
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/ai/defaultai_warfare.cc' |
2 | --- src/ai/defaultai_warfare.cc 2017-01-28 14:53:28 +0000 |
3 | +++ src/ai/defaultai_warfare.cc 2017-05-21 11:20:39 +0000 |
4 | @@ -122,7 +122,7 @@ |
5 | // get list of immovable around this our military site |
6 | std::vector<ImmovableFound> immovables; |
7 | map.find_immovables(Area<FCoords>(f, (vision + 3 < 13) ? 13 : vision + 3), &immovables, |
8 | - FindImmovableAttackable()); |
9 | + FindImmovableAttackTarget()); |
10 | |
11 | for (uint32_t j = 0; j < immovables.size(); ++j) { |
12 | if (upcast(MilitarySite const, bld, immovables.at(j).object)) { |
13 | @@ -229,7 +229,7 @@ |
14 | defenders_strength = calculate_strength(defenders); |
15 | |
16 | flag = &bld->base_flag(); |
17 | - if (is_visible && bld->can_attack()) { |
18 | + if (is_visible && bld->attack_target()->can_be_attacked()) { |
19 | is_attackable = true; |
20 | } |
21 | owner_number = bld->owner().player_number(); |
22 | @@ -244,7 +244,7 @@ |
23 | |
24 | flag = &Wh->base_flag(); |
25 | is_warehouse = true; |
26 | - if (is_visible && Wh->can_attack()) { |
27 | + if (is_visible && Wh->attack_target()->can_be_attacked()) { |
28 | is_attackable = true; |
29 | } |
30 | owner_number = Wh->owner().player_number(); |
31 | |
32 | === modified file 'src/economy/route.h' |
33 | --- src/economy/route.h 2017-01-25 18:55:59 +0000 |
34 | +++ src/economy/route.h 2017-05-21 11:20:39 +0000 |
35 | @@ -23,14 +23,16 @@ |
36 | #include <vector> |
37 | |
38 | #include "economy/iroute.h" |
39 | +#include "io/fileread.h" |
40 | +#include "io/filewrite.h" |
41 | #include "logic/map_objects/map_object.h" |
42 | |
43 | namespace Widelands { |
44 | |
45 | +class EditorGameBase; |
46 | +class MapObjectLoader; |
47 | struct Flag; |
48 | -class EditorGameBase; |
49 | struct MapObjectSaver; |
50 | -class MapObjectLoader; |
51 | struct RoutingNode; |
52 | |
53 | /** |
54 | |
55 | === modified file 'src/logic/CMakeLists.txt' |
56 | --- src/logic/CMakeLists.txt 2017-05-14 18:17:16 +0000 |
57 | +++ src/logic/CMakeLists.txt 2017-05-21 11:20:39 +0000 |
58 | @@ -127,14 +127,13 @@ |
59 | save_handler.h |
60 | widelands_geometry_io.cc |
61 | widelands_geometry_io.h |
62 | - map_objects/draw_text.h |
63 | - map_objects/attackable.h |
64 | map_objects/bob.cc |
65 | map_objects/bob.h |
66 | map_objects/buildcost.cc |
67 | map_objects/buildcost.h |
68 | map_objects/checkstep.cc |
69 | map_objects/checkstep.h |
70 | + map_objects/draw_text.h |
71 | map_objects/immovable.cc |
72 | map_objects/immovable.h |
73 | map_objects/immovable_program.h |
74 | @@ -142,6 +141,7 @@ |
75 | map_objects/map_object.h |
76 | map_objects/terrain_affinity.cc |
77 | map_objects/terrain_affinity.h |
78 | + map_objects/tribes/attack_target.h |
79 | map_objects/tribes/battle.cc |
80 | map_objects/tribes/battle.h |
81 | map_objects/tribes/bill_of_materials.h |
82 | |
83 | === modified file 'src/logic/cmd_queue.h' |
84 | --- src/logic/cmd_queue.h 2017-01-25 18:55:59 +0000 |
85 | +++ src/logic/cmd_queue.h 2017-05-21 11:20:39 +0000 |
86 | @@ -27,7 +27,6 @@ |
87 | #include <stdint.h> |
88 | |
89 | #include "logic/queue_cmd_ids.h" |
90 | -#include <stdint.h> |
91 | |
92 | class FileRead; |
93 | class FileWrite; |
94 | |
95 | === modified file 'src/logic/findimmovable.cc' |
96 | --- src/logic/findimmovable.cc 2017-01-25 18:55:59 +0000 |
97 | +++ src/logic/findimmovable.cc 2017-05-21 11:20:39 +0000 |
98 | @@ -21,7 +21,6 @@ |
99 | |
100 | #include "base/macros.h" |
101 | #include "economy/flag.h" |
102 | -#include "logic/map_objects/attackable.h" |
103 | #include "logic/map_objects/immovable.h" |
104 | #include "logic/map_objects/tribes/militarysite.h" |
105 | |
106 | @@ -61,8 +60,11 @@ |
107 | return false; |
108 | } |
109 | |
110 | -bool FindImmovableAttackable::accept(const BaseImmovable& imm) const { |
111 | - return dynamic_cast<Attackable const*>(&imm); |
112 | +bool FindImmovableAttackTarget::accept(const BaseImmovable& imm) const { |
113 | + if (upcast(Building const, b, &imm)) { |
114 | + return b->attack_target() != nullptr; |
115 | + } |
116 | + return false; |
117 | } |
118 | |
119 | bool FindImmovableByDescr::accept(const BaseImmovable& baseimm) const { |
120 | |
121 | === modified file 'src/logic/findimmovable.h' |
122 | --- src/logic/findimmovable.h 2017-01-25 18:55:59 +0000 |
123 | +++ src/logic/findimmovable.h 2017-05-21 11:20:39 +0000 |
124 | @@ -132,8 +132,8 @@ |
125 | |
126 | const Player& player; |
127 | }; |
128 | -struct FindImmovableAttackable { |
129 | - FindImmovableAttackable() { |
130 | +struct FindImmovableAttackTarget { |
131 | + FindImmovableAttackTarget() { |
132 | } |
133 | |
134 | bool accept(const BaseImmovable&) const; |
135 | |
136 | === modified file 'src/logic/map_objects/map_object.h' |
137 | --- src/logic/map_objects/map_object.h 2017-04-23 12:11:19 +0000 |
138 | +++ src/logic/map_objects/map_object.h 2017-05-21 11:20:39 +0000 |
139 | @@ -491,12 +491,12 @@ |
140 | ObjectPointer() { |
141 | serial_ = 0; |
142 | } |
143 | - ObjectPointer(MapObject* const obj) { |
144 | + ObjectPointer(const MapObject* const obj) { |
145 | serial_ = obj ? obj->serial_ : 0; |
146 | } |
147 | // can use standard copy constructor and assignment operator |
148 | |
149 | - ObjectPointer& operator=(MapObject* const obj) { |
150 | + ObjectPointer& operator=(const MapObject* const obj) { |
151 | serial_ = obj ? obj->serial_ : 0; |
152 | return *this; |
153 | } |
154 | |
155 | === renamed file 'src/logic/map_objects/attackable.h' => 'src/logic/map_objects/tribes/attack_target.h' |
156 | --- src/logic/map_objects/attackable.h 2017-01-25 18:55:59 +0000 |
157 | +++ src/logic/map_objects/tribes/attack_target.h 2017-05-21 11:20:39 +0000 |
158 | @@ -17,68 +17,55 @@ |
159 | * |
160 | */ |
161 | |
162 | -#ifndef WL_LOGIC_MAP_OBJECTS_ATTACKABLE_H |
163 | -#define WL_LOGIC_MAP_OBJECTS_ATTACKABLE_H |
164 | +#ifndef WL_LOGIC_MAP_OBJECTS_TRIBES_ATTACK_TARGET_H |
165 | +#define WL_LOGIC_MAP_OBJECTS_TRIBES_ATTACK_TARGET_H |
166 | + |
167 | +#include "base/macros.h" |
168 | |
169 | namespace Widelands { |
170 | |
171 | -class Player; |
172 | class Soldier; |
173 | |
174 | -enum { |
175 | - /** |
176 | - * This is the maximum radius that a military building can protect |
177 | - * in the sense that an enemy soldier that enters the player's territory |
178 | - * will call \ref Attackable::aggressor if it is that close. |
179 | - */ |
180 | - MaxProtectionRadius = 25 |
181 | -}; |
182 | - |
183 | -/** |
184 | - * Buildings can implement this interface to indicate that |
185 | - * they can be attacked. |
186 | - */ |
187 | -struct Attackable { |
188 | - /** |
189 | - * Return the player that owns this attackable. |
190 | - */ |
191 | - virtual Player& owner() const = 0; |
192 | - |
193 | - /** |
194 | - * Determines whether this building can be attacked right now. |
195 | - * |
196 | - * This should only return false for military sites that have not |
197 | - * been occupied yet. |
198 | - */ |
199 | - virtual bool can_attack() = 0; |
200 | - |
201 | - /** |
202 | - * Called by an enemy soldier that enters a node with distance |
203 | - * less than or equal to \ref MaxProtectionRadius from the building. |
204 | - * |
205 | - * This allows the building to send protective forces to intercept |
206 | - * the soldier. |
207 | - */ |
208 | - virtual void aggressor(Soldier&) = 0; |
209 | - |
210 | - /** |
211 | - * Called by a soldier who is standing on the building's flag |
212 | - * to attack the building. |
213 | - * |
214 | - * The building must send a soldier for defense, and return \c true. |
215 | - * Otherwise, i.e. if the building cannot defend itself anymore, |
216 | - * it must destroy itself or turn over to the attacking player, |
217 | - * and return \c false. |
218 | - * |
219 | - * \return \c true if a soldier was launched in defense of the building, |
220 | - * or \c false if the building cannot defend itself any longer. |
221 | - */ |
222 | - virtual bool attack(Soldier&) = 0; |
223 | - |
224 | -protected: |
225 | - virtual ~Attackable() { |
226 | +// This is the maximum radius that a military building can protect in the sense |
227 | +// that an enemy soldier that enters the player's territory will call \ref |
228 | +// AttackTarget::enemy_soldier_approaches if it is that close. |
229 | +constexpr int kMaxProtectionRadius = 25; |
230 | + |
231 | +class AttackTarget { |
232 | +public: |
233 | + AttackTarget() = default; |
234 | + virtual ~AttackTarget() { |
235 | } |
236 | + |
237 | + // Determines whether this building can be attacked right now. |
238 | + virtual bool can_be_attacked() const = 0; |
239 | + |
240 | + // Called by an enemy soldier that enters a node with distance |
241 | + // less than or equal to \ref kMaxProtectionRadius from the building. |
242 | + // |
243 | + // This allows the building to send protective forces to intercept |
244 | + // the soldier. |
245 | + virtual void enemy_soldier_approaches(const Soldier& enemy) const = 0; |
246 | + |
247 | + // Called by a soldier who is standing on the building's flag |
248 | + // to attack the building. |
249 | + // |
250 | + // The building must send a soldier for defense, and return \c true. |
251 | + // Otherwise, i.e. if the building cannot defend itself anymore, |
252 | + // it must destroy itself or turn over to the attacking player, |
253 | + // and return \c false. |
254 | + enum class AttackResult { |
255 | + // Returned when a soldier was launched in defense of the building. |
256 | + DefenderLaunched, |
257 | + |
258 | + // Returned if the building cannot defend itself any longer. |
259 | + Defenseless |
260 | + }; |
261 | + virtual AttackResult attack(Soldier* attacker) const = 0; |
262 | + |
263 | +private: |
264 | + DISALLOW_COPY_AND_ASSIGN(AttackTarget); |
265 | }; |
266 | } |
267 | |
268 | -#endif // end of include guard: WL_LOGIC_MAP_OBJECTS_ATTACKABLE_H |
269 | +#endif // end of include guard: WL_LOGIC_MAP_OBJECTS_TRIBES_ATTACK_TARGET_H |
270 | |
271 | === modified file 'src/logic/map_objects/tribes/building.cc' |
272 | --- src/logic/map_objects/tribes/building.cc 2017-05-09 09:18:14 +0000 |
273 | +++ src/logic/map_objects/tribes/building.cc 2017-05-21 11:20:39 +0000 |
274 | @@ -235,7 +235,8 @@ |
275 | animstart_(0), |
276 | leave_time_(0), |
277 | defeating_player_(0), |
278 | - seeing_(false) { |
279 | + seeing_(false), |
280 | + attack_target_(nullptr) { |
281 | } |
282 | |
283 | void Building::load_finish(EditorGameBase& egbase) { |
284 | @@ -692,6 +693,11 @@ |
285 | Notifications::publish(NoteBuilding(serial(), NoteBuilding::Action::kWorkersChanged)); |
286 | } |
287 | |
288 | +void Building::set_attack_target(AttackTarget* new_attack_target) { |
289 | + assert(attack_target_ == nullptr); |
290 | + attack_target_ = new_attack_target; |
291 | +} |
292 | + |
293 | /** |
294 | * Change whether this building sees its vision range based on workers |
295 | * inside the building. |
296 | |
297 | === modified file 'src/logic/map_objects/tribes/building.h' |
298 | --- src/logic/map_objects/tribes/building.h 2017-05-06 19:21:47 +0000 |
299 | +++ src/logic/map_objects/tribes/building.h 2017-05-21 11:20:39 +0000 |
300 | @@ -30,6 +30,7 @@ |
301 | #include "base/macros.h" |
302 | #include "logic/map_objects/buildcost.h" |
303 | #include "logic/map_objects/immovable.h" |
304 | +#include "logic/map_objects/tribes/attack_target.h" |
305 | #include "logic/map_objects/tribes/bill_of_materials.h" |
306 | #include "logic/map_objects/tribes/wareworker.h" |
307 | #include "logic/map_objects/tribes/workarea_info.h" |
308 | @@ -293,6 +294,13 @@ |
309 | void add_worker(Worker&) override; |
310 | void remove_worker(Worker&) override; |
311 | |
312 | + // Returns the AttackTarget object associated with this building. If the |
313 | + // building can never be attacked (for example productionsites) this will be |
314 | + // nullptr. |
315 | + const AttackTarget* attack_target() const { |
316 | + return attack_target_; |
317 | + } |
318 | + |
319 | void send_message(Game& game, |
320 | const Message::Type msgtype, |
321 | const std::string& title, |
322 | @@ -324,6 +332,7 @@ |
323 | draw_info(TextToDraw draw_text, const Vector2f& point_on_dst, float scale, RenderTarget* dst); |
324 | |
325 | void set_seeing(bool see); |
326 | + void set_attack_target(AttackTarget* new_attack_target); |
327 | |
328 | Coords position_; |
329 | Flag* flag_; |
330 | @@ -350,6 +359,7 @@ |
331 | |
332 | private: |
333 | std::string statistics_string_; |
334 | + AttackTarget* attack_target_; // owned by the base classes, set by 'set_attack_target'. |
335 | }; |
336 | } |
337 | |
338 | |
339 | === modified file 'src/logic/map_objects/tribes/militarysite.cc' |
340 | --- src/logic/map_objects/tribes/militarysite.cc 2017-04-23 12:11:19 +0000 |
341 | +++ src/logic/map_objects/tribes/militarysite.cc 2017-05-21 11:20:39 +0000 |
342 | @@ -43,6 +43,145 @@ |
343 | |
344 | namespace Widelands { |
345 | |
346 | +bool MilitarySite::AttackTarget::can_be_attacked() const { |
347 | + return military_site_->didconquer_; |
348 | +} |
349 | + |
350 | +void MilitarySite::AttackTarget::enemy_soldier_approaches(const Soldier& enemy) const { |
351 | + auto& owner = military_site_->owner(); |
352 | + Game& game = dynamic_cast<Game&>(owner.egbase()); |
353 | + Map& map = game.map(); |
354 | + if (enemy.get_owner() == &owner || enemy.get_battle() || |
355 | + military_site_->descr().get_conquers() <= |
356 | + map.calc_distance(enemy.get_position(), military_site_->get_position())) |
357 | + return; |
358 | + |
359 | + if (map.find_bobs(Area<FCoords>(map.get_fcoords(military_site_->base_flag().get_position()), 2), |
360 | + nullptr, FindBobEnemySoldier(&owner))) |
361 | + return; |
362 | + |
363 | + // We're dealing with a soldier that we might want to keep busy |
364 | + // Now would be the time to implement some player-definable |
365 | + // policy as to how many soldiers are allowed to leave as defenders |
366 | + std::vector<Soldier*> present = military_site_->present_soldiers(); |
367 | + |
368 | + if (1 < present.size()) { |
369 | + for (Soldier* temp_soldier : present) { |
370 | + if (!military_site_->has_soldier_job(*temp_soldier)) { |
371 | + SoldierJob sj; |
372 | + sj.soldier = temp_soldier; |
373 | + sj.enemy = &enemy; |
374 | + sj.stayhome = false; |
375 | + military_site_->soldierjobs_.push_back(sj); |
376 | + temp_soldier->update_task_buildingwork(game); |
377 | + return; |
378 | + } |
379 | + } |
380 | + } |
381 | + |
382 | + // Inform the player, that we are under attack by adding a new entry to the |
383 | + // message queue - a sound will automatically be played. |
384 | + military_site_->notify_player(game, true); |
385 | +} |
386 | + |
387 | +AttackTarget::AttackResult MilitarySite::AttackTarget::attack(Soldier* enemy) const { |
388 | + Game& game = dynamic_cast<Game&>(military_site_->owner().egbase()); |
389 | + |
390 | + std::vector<Soldier*> present = military_site_->present_soldiers(); |
391 | + Soldier* defender = nullptr; |
392 | + |
393 | + if (!present.empty()) { |
394 | + // Find soldier with greatest health |
395 | + uint32_t current_max = 0; |
396 | + for (Soldier* temp_soldier : present) { |
397 | + if (temp_soldier->get_current_health() > current_max) { |
398 | + defender = temp_soldier; |
399 | + current_max = defender->get_current_health(); |
400 | + } |
401 | + } |
402 | + } else { |
403 | + // If one of our stationed soldiers is currently walking into the |
404 | + // building, give us another chance. |
405 | + std::vector<Soldier*> stationed = military_site_->stationed_soldiers(); |
406 | + for (Soldier* temp_soldier : stationed) { |
407 | + if (temp_soldier->get_position() == military_site_->get_position()) { |
408 | + defender = temp_soldier; |
409 | + break; |
410 | + } |
411 | + } |
412 | + } |
413 | + |
414 | + if (defender) { |
415 | + military_site_->pop_soldier_job(defender); // defense overrides all other jobs |
416 | + |
417 | + SoldierJob sj; |
418 | + sj.soldier = defender; |
419 | + sj.enemy = enemy; |
420 | + sj.stayhome = true; |
421 | + military_site_->soldierjobs_.push_back(sj); |
422 | + |
423 | + defender->update_task_buildingwork(game); |
424 | + |
425 | + // Inform the player, that we are under attack by adding a new entry to |
426 | + // the message queue - a sound will automatically be played. |
427 | + military_site_->notify_player(game); |
428 | + |
429 | + return AttackTarget::AttackResult::DefenderLaunched; |
430 | + } |
431 | + |
432 | + // The enemy has defeated our forces, we should inform the player |
433 | + const Coords coords = military_site_->get_position(); |
434 | + { |
435 | + military_site_->send_message(game, Message::Type::kWarfareSiteLost, |
436 | + /** TRANSLATORS: Militarysite lost (taken/destroyed by enemy) */ |
437 | + pgettext("building", "Lost!"), |
438 | + military_site_->descr().icon_filename(), _("Militarysite lost!"), |
439 | + military_site_->descr().defeated_enemy_str_, false); |
440 | + } |
441 | + |
442 | + // Now let's see whether the enemy conquers our militarysite, or whether |
443 | + // we still hold the bigger military presence in that area (e.g. if there |
444 | + // is a fortress one or two points away from our sentry, the fortress has |
445 | + // a higher presence and thus the enemy can just burn down the sentry. |
446 | + if (military_site_->military_presence_kept(game)) { |
447 | + // Okay we still got the higher military presence, so the attacked |
448 | + // militarysite will be destroyed. |
449 | + military_site_->set_defeating_player(enemy->owner().player_number()); |
450 | + military_site_->schedule_destroy(game); |
451 | + return AttackTarget::AttackResult::Defenseless; |
452 | + } |
453 | + |
454 | + // The enemy conquers the building |
455 | + // In fact we do not conquer it, but place a new building of same type at |
456 | + // the old location. |
457 | + |
458 | + Building::FormerBuildings former_buildings = military_site_->old_buildings_; |
459 | + |
460 | + // The enemy conquers the building |
461 | + // In fact we do not conquer it, but place a new building of same type at |
462 | + // the old location. |
463 | + Player* enemyplayer = enemy->get_owner(); |
464 | + |
465 | + // Now we destroy the old building before we place the new one. |
466 | + military_site_->set_defeating_player(enemyplayer->player_number()); |
467 | + military_site_->schedule_destroy(game); |
468 | + |
469 | + enemyplayer->force_building(coords, former_buildings); |
470 | + BaseImmovable* const newimm = game.map()[coords].get_immovable(); |
471 | + upcast(MilitarySite, newsite, newimm); |
472 | + newsite->reinit_after_conqueration(game); |
473 | + |
474 | + // Of course we should inform the victorious player as well |
475 | + newsite->send_message( |
476 | + game, Message::Type::kWarfareSiteDefeated, |
477 | + /** TRANSLATORS: Message title. */ |
478 | + /** TRANSLATORS: If you run out of space, you can also translate this as "Success!" */ |
479 | + _("Enemy Defeated!"), newsite->descr().icon_filename(), _("Enemy at site defeated!"), |
480 | + newsite->descr().defeated_you_str_, true); |
481 | + |
482 | + return AttackTarget::AttackResult::Defenseless; |
483 | +} |
484 | + |
485 | /** |
486 | * The contents of 'table' are documented in |
487 | * /data/tribes/buildings/militarysites/atlanteans/castle/init.lua |
488 | @@ -91,6 +230,7 @@ |
489 | |
490 | MilitarySite::MilitarySite(const MilitarySiteDescr& ms_descr) |
491 | : Building(ms_descr), |
492 | + attack_target_(this), |
493 | didconquer_(false), |
494 | capacity_(ms_descr.get_max_number_of_soldiers()), |
495 | nexthealtime_(0), |
496 | @@ -98,6 +238,7 @@ |
497 | soldier_upgrade_try_(false), |
498 | doing_upgrade_request_(false) { |
499 | next_swap_soldiers_time_ = 0; |
500 | + set_attack_target(&attack_target_); |
501 | } |
502 | |
503 | MilitarySite::~MilitarySite() { |
504 | @@ -684,142 +825,6 @@ |
505 | didconquer_ = true; |
506 | } |
507 | |
508 | -bool MilitarySite::can_attack() { |
509 | - return didconquer_; |
510 | -} |
511 | - |
512 | -void MilitarySite::aggressor(Soldier& enemy) { |
513 | - Game& game = dynamic_cast<Game&>(owner().egbase()); |
514 | - Map& map = game.map(); |
515 | - if (enemy.get_owner() == &owner() || enemy.get_battle() || |
516 | - descr().get_conquers() <= map.calc_distance(enemy.get_position(), get_position())) |
517 | - return; |
518 | - |
519 | - if (map.find_bobs(Area<FCoords>(map.get_fcoords(base_flag().get_position()), 2), nullptr, |
520 | - FindBobEnemySoldier(&owner()))) |
521 | - return; |
522 | - |
523 | - // We're dealing with a soldier that we might want to keep busy |
524 | - // Now would be the time to implement some player-definable |
525 | - // policy as to how many soldiers are allowed to leave as defenders |
526 | - std::vector<Soldier*> present = present_soldiers(); |
527 | - |
528 | - if (1 < present.size()) { |
529 | - for (Soldier* temp_soldier : present) { |
530 | - if (!has_soldier_job(*temp_soldier)) { |
531 | - SoldierJob sj; |
532 | - sj.soldier = temp_soldier; |
533 | - sj.enemy = &enemy; |
534 | - sj.stayhome = false; |
535 | - soldierjobs_.push_back(sj); |
536 | - temp_soldier->update_task_buildingwork(game); |
537 | - return; |
538 | - } |
539 | - } |
540 | - } |
541 | - |
542 | - // Inform the player, that we are under attack by adding a new entry to the |
543 | - // message queue - a sound will automatically be played. |
544 | - notify_player(game, true); |
545 | -} |
546 | - |
547 | -bool MilitarySite::attack(Soldier& enemy) { |
548 | - Game& game = dynamic_cast<Game&>(owner().egbase()); |
549 | - |
550 | - std::vector<Soldier*> present = present_soldiers(); |
551 | - Soldier* defender = nullptr; |
552 | - |
553 | - if (!present.empty()) { |
554 | - // Find soldier with greatest health |
555 | - uint32_t current_max = 0; |
556 | - for (Soldier* temp_soldier : present) { |
557 | - if (temp_soldier->get_current_health() > current_max) { |
558 | - defender = temp_soldier; |
559 | - current_max = defender->get_current_health(); |
560 | - } |
561 | - } |
562 | - } else { |
563 | - // If one of our stationed soldiers is currently walking into the |
564 | - // building, give us another chance. |
565 | - std::vector<Soldier*> stationed = stationed_soldiers(); |
566 | - for (Soldier* temp_soldier : stationed) { |
567 | - if (temp_soldier->get_position() == get_position()) { |
568 | - defender = temp_soldier; |
569 | - break; |
570 | - } |
571 | - } |
572 | - } |
573 | - |
574 | - if (defender) { |
575 | - pop_soldier_job(defender); // defense overrides all other jobs |
576 | - |
577 | - SoldierJob sj; |
578 | - sj.soldier = defender; |
579 | - sj.enemy = &enemy; |
580 | - sj.stayhome = true; |
581 | - soldierjobs_.push_back(sj); |
582 | - |
583 | - defender->update_task_buildingwork(game); |
584 | - |
585 | - // Inform the player, that we are under attack by adding a new entry to |
586 | - // the message queue - a sound will automatically be played. |
587 | - notify_player(game); |
588 | - |
589 | - return true; |
590 | - } else { |
591 | - // The enemy has defeated our forces, we should inform the player |
592 | - const Coords coords = get_position(); |
593 | - { |
594 | - send_message(game, Message::Type::kWarfareSiteLost, |
595 | - /** TRANSLATORS: Militarysite lost (taken/destroyed by enemy) */ |
596 | - pgettext("building", "Lost!"), descr().icon_filename(), |
597 | - _("Militarysite lost!"), descr().defeated_enemy_str_, false); |
598 | - } |
599 | - |
600 | - // Now let's see whether the enemy conquers our militarysite, or whether |
601 | - // we still hold the bigger military presence in that area (e.g. if there |
602 | - // is a fortress one or two points away from our sentry, the fortress has |
603 | - // a higher presence and thus the enemy can just burn down the sentry. |
604 | - if (military_presence_kept(game)) { |
605 | - // Okay we still got the higher military presence, so the attacked |
606 | - // militarysite will be destroyed. |
607 | - set_defeating_player(enemy.owner().player_number()); |
608 | - schedule_destroy(game); |
609 | - return false; |
610 | - } |
611 | - |
612 | - // The enemy conquers the building |
613 | - // In fact we do not conquer it, but place a new building of same type at |
614 | - // the old location. |
615 | - |
616 | - Building::FormerBuildings former_buildings = old_buildings_; |
617 | - |
618 | - // The enemy conquers the building |
619 | - // In fact we do not conquer it, but place a new building of same type at |
620 | - // the old location. |
621 | - Player* enemyplayer = enemy.get_owner(); |
622 | - |
623 | - // Now we destroy the old building before we place the new one. |
624 | - set_defeating_player(enemyplayer->player_number()); |
625 | - schedule_destroy(game); |
626 | - |
627 | - enemyplayer->force_building(coords, former_buildings); |
628 | - BaseImmovable* const newimm = game.map()[coords].get_immovable(); |
629 | - upcast(MilitarySite, newsite, newimm); |
630 | - newsite->reinit_after_conqueration(game); |
631 | - |
632 | - // Of course we should inform the victorious player as well |
633 | - newsite->send_message( |
634 | - game, Message::Type::kWarfareSiteDefeated, |
635 | - /** TRANSLATORS: Message title. */ |
636 | - /** TRANSLATORS: If you run out of space, you can also translate this as "Success!" */ |
637 | - _("Enemy Defeated!"), newsite->descr().icon_filename(), _("Enemy at site defeated!"), |
638 | - newsite->descr().defeated_you_str_, true); |
639 | - |
640 | - return false; |
641 | - } |
642 | -} |
643 | - |
644 | /// Initialises the militarysite after it was "conquered" (the old was replaced) |
645 | void MilitarySite::reinit_after_conqueration(Game& game) { |
646 | clear_requirements(); |
647 | |
648 | === modified file 'src/logic/map_objects/tribes/militarysite.h' |
649 | --- src/logic/map_objects/tribes/militarysite.h 2017-04-23 12:11:19 +0000 |
650 | +++ src/logic/map_objects/tribes/militarysite.h 2017-05-21 11:20:39 +0000 |
651 | @@ -24,7 +24,6 @@ |
652 | |
653 | #include "base/macros.h" |
654 | #include "economy/request.h" |
655 | -#include "logic/map_objects/attackable.h" |
656 | #include "logic/map_objects/tribes/building.h" |
657 | #include "logic/map_objects/tribes/requirements.h" |
658 | #include "logic/map_objects/tribes/soldiercontrol.h" |
659 | @@ -69,7 +68,7 @@ |
660 | DISALLOW_COPY_AND_ASSIGN(MilitarySiteDescr); |
661 | }; |
662 | |
663 | -class MilitarySite : public Building, public SoldierControl, public Attackable { |
664 | +class MilitarySite : public Building, public SoldierControl { |
665 | friend class MapBuildingdataPacket; |
666 | MO_DESCR(MilitarySiteDescr) |
667 | |
668 | @@ -102,15 +101,6 @@ |
669 | void drop_soldier(Soldier&) override; |
670 | int incorporate_soldier(EditorGameBase& game, Soldier& s) override; |
671 | |
672 | - // Begin implementation of Attackable |
673 | - Player& owner() const override { |
674 | - return Building::owner(); |
675 | - } |
676 | - bool can_attack() override; |
677 | - void aggressor(Soldier&) override; |
678 | - bool attack(Soldier&) override; |
679 | - // End implementation of Attackable |
680 | - |
681 | /// Launch the given soldier on an attack towards the given |
682 | /// target building. |
683 | void send_attacker(Soldier&, Building&); |
684 | @@ -153,6 +143,21 @@ |
685 | bool drop_least_suited_soldier(bool new_has_arrived, Soldier* s); |
686 | |
687 | private: |
688 | + // We can be attacked if we have stationed soldiers. |
689 | + class AttackTarget : public Widelands::AttackTarget { |
690 | + public: |
691 | + explicit AttackTarget(MilitarySite* military_site) : military_site_(military_site) { |
692 | + } |
693 | + |
694 | + bool can_be_attacked() const override; |
695 | + void enemy_soldier_approaches(const Soldier&) const override; |
696 | + Widelands::AttackTarget::AttackResult attack(Soldier*) const override; |
697 | + |
698 | + private: |
699 | + MilitarySite* const military_site_; |
700 | + }; |
701 | + |
702 | + AttackTarget attack_target_; |
703 | Requirements soldier_requirements_; // This is used to grab a bunch of soldiers: Anything goes |
704 | RequireAttribute soldier_upgrade_requirements_; // This is used when exchanging soldiers. |
705 | std::unique_ptr<Request> normal_soldier_request_; // filling the site |
706 | |
707 | === modified file 'src/logic/map_objects/tribes/soldier.cc' |
708 | --- src/logic/map_objects/tribes/soldier.cc 2017-05-13 18:48:26 +0000 |
709 | +++ src/logic/map_objects/tribes/soldier.cc 2017-05-21 11:20:39 +0000 |
710 | @@ -42,7 +42,6 @@ |
711 | #include "logic/game.h" |
712 | #include "logic/game_controller.h" |
713 | #include "logic/game_data_error.h" |
714 | -#include "logic/map_objects/attackable.h" |
715 | #include "logic/map_objects/checkstep.h" |
716 | #include "logic/map_objects/tribes/battle.h" |
717 | #include "logic/map_objects/tribes/building.h" |
718 | @@ -602,7 +601,7 @@ |
719 | return false; |
720 | } |
721 | |
722 | -Battle* Soldier::get_battle() { |
723 | +Battle* Soldier::get_battle() const { |
724 | return battle_; |
725 | } |
726 | |
727 | @@ -902,12 +901,14 @@ |
728 | } |
729 | } |
730 | |
731 | - upcast(Attackable, attackable, enemy); |
732 | - assert(attackable); |
733 | + assert(enemy->attack_target() != nullptr); |
734 | |
735 | molog("[attack] attacking target building\n"); |
736 | // give the enemy soldier some time to act |
737 | - schedule_act(game, attackable->attack(*this) ? 1000 : 10); |
738 | + schedule_act( |
739 | + game, enemy->attack_target()->attack(this) == AttackTarget::AttackResult::DefenderLaunched ? |
740 | + 1000 : |
741 | + 10); |
742 | } |
743 | |
744 | void Soldier::attack_pop(Game& game, State&) { |
745 | @@ -1520,20 +1521,21 @@ |
746 | PlayerNumber const land_owner = get_position().field->get_owned_by(); |
747 | // First check if the soldier is standing on someone else's territory |
748 | if (land_owner != owner().player_number()) { |
749 | - // Let's collect all reachable attackable sites in vicinity (militarysites mainly) |
750 | - std::vector<BaseImmovable*> attackables; |
751 | + // Let's collect all reachable attack_target sites in vicinity (militarysites mainly) |
752 | + std::vector<BaseImmovable*> attack_targets; |
753 | game.map().find_reachable_immovables_unique( |
754 | - Area<FCoords>(get_position(), MaxProtectionRadius), attackables, |
755 | - CheckStepWalkOn(descr().movecaps(), false), FindImmovableAttackable()); |
756 | + Area<FCoords>(get_position(), kMaxProtectionRadius), attack_targets, |
757 | + CheckStepWalkOn(descr().movecaps(), false), FindImmovableAttackTarget()); |
758 | |
759 | - for (BaseImmovable* temp_attackable : attackables) { |
760 | - const Player* attackable_player = |
761 | - dynamic_cast<const PlayerImmovable&>(*temp_attackable).get_owner(); |
762 | + for (BaseImmovable* temp_attack_target : attack_targets) { |
763 | + Building* building = dynamic_cast<Building*>(temp_attack_target); |
764 | + assert(building != nullptr && building->attack_target() != nullptr); |
765 | + const Player& attack_target_player = building->owner(); |
766 | // Let's inform the site that this (=enemy) soldier is nearby and within the site's owner's |
767 | // territory |
768 | - if (attackable_player->player_number() == land_owner && |
769 | - attackable_player->is_hostile(*get_owner())) { |
770 | - dynamic_cast<Attackable&>(*temp_attackable).aggressor(*this); |
771 | + if (attack_target_player.player_number() == land_owner && |
772 | + attack_target_player.is_hostile(*get_owner())) { |
773 | + building->attack_target()->enemy_soldier_approaches(*this); |
774 | } |
775 | } |
776 | } |
777 | |
778 | === modified file 'src/logic/map_objects/tribes/soldier.h' |
779 | --- src/logic/map_objects/tribes/soldier.h 2017-05-13 13:14:29 +0000 |
780 | +++ src/logic/map_objects/tribes/soldier.h 2017-05-21 11:20:39 +0000 |
781 | @@ -262,7 +262,7 @@ |
782 | |
783 | bool is_on_battlefield(); |
784 | bool is_attacking_player(Game&, Player&); |
785 | - Battle* get_battle(); |
786 | + Battle* get_battle() const; |
787 | bool can_be_challenged(); |
788 | bool check_node_blocked(Game&, const FCoords&, bool commit) override; |
789 | |
790 | |
791 | === modified file 'src/logic/map_objects/tribes/warehouse.cc' |
792 | --- src/logic/map_objects/tribes/warehouse.cc 2017-05-03 10:34:46 +0000 |
793 | +++ src/logic/map_objects/tribes/warehouse.cc 2017-05-21 11:20:39 +0000 |
794 | @@ -69,6 +69,57 @@ |
795 | |
796 | } // namespace |
797 | |
798 | +bool Warehouse::AttackTarget::can_be_attacked() const { |
799 | + return warehouse_->descr().get_conquers() > 0; |
800 | +} |
801 | + |
802 | +void Warehouse::AttackTarget::enemy_soldier_approaches(const Soldier& enemy) const { |
803 | + if (!warehouse_->descr().get_conquers()) |
804 | + return; |
805 | + |
806 | + Player& owner = warehouse_->owner(); |
807 | + Game& game = dynamic_cast<Game&>(owner.egbase()); |
808 | + Map& map = game.map(); |
809 | + if (enemy.get_owner() == &owner || enemy.get_battle() || |
810 | + warehouse_->descr().get_conquers() <= |
811 | + map.calc_distance(enemy.get_position(), warehouse_->get_position())) |
812 | + return; |
813 | + |
814 | + if (game.map().find_bobs( |
815 | + Area<FCoords>(map.get_fcoords(warehouse_->base_flag().get_position()), 2), nullptr, |
816 | + FindBobEnemySoldier(&owner))) |
817 | + return; |
818 | + |
819 | + DescriptionIndex const soldier_index = owner.tribe().soldier(); |
820 | + Requirements noreq; |
821 | + |
822 | + if (!warehouse_->count_workers(game, soldier_index, noreq, Match::kCompatible)) |
823 | + return; |
824 | + |
825 | + Soldier& defender = |
826 | + dynamic_cast<Soldier&>(warehouse_->launch_worker(game, soldier_index, noreq)); |
827 | + defender.start_task_defense(game, false); |
828 | +} |
829 | + |
830 | +AttackTarget::AttackResult Warehouse::AttackTarget::attack(Soldier* enemy) const { |
831 | + Player& owner = warehouse_->owner(); |
832 | + Game& game = dynamic_cast<Game&>(owner.egbase()); |
833 | + DescriptionIndex const soldier_index = owner.tribe().soldier(); |
834 | + Requirements noreq; |
835 | + |
836 | + if (warehouse_->count_workers(game, soldier_index, noreq, Match::kCompatible)) { |
837 | + Soldier& defender = |
838 | + dynamic_cast<Soldier&>(warehouse_->launch_worker(game, soldier_index, noreq)); |
839 | + defender.start_task_defense(game, true); |
840 | + enemy->send_signal(game, "sleep"); |
841 | + return AttackTarget::AttackResult::DefenderLaunched; |
842 | + } |
843 | + |
844 | + warehouse_->set_defeating_player(enemy->owner().player_number()); |
845 | + warehouse_->schedule_destroy(game); |
846 | + return AttackTarget::AttackResult::Defenseless; |
847 | +} |
848 | + |
849 | WarehouseSupply::~WarehouseSupply() { |
850 | if (economy_) { |
851 | log("WarehouseSupply::~WarehouseSupply: Warehouse %u still belongs to " |
852 | @@ -260,11 +311,13 @@ |
853 | |
854 | Warehouse::Warehouse(const WarehouseDescr& warehouse_descr) |
855 | : Building(warehouse_descr), |
856 | + attack_target_(this), |
857 | supply_(new WarehouseSupply(this)), |
858 | next_military_act_(0), |
859 | portdock_(nullptr) { |
860 | next_stock_remove_act_ = 0; |
861 | cleanup_in_progress_ = false; |
862 | + set_attack_target(&attack_target_); |
863 | } |
864 | |
865 | Warehouse::~Warehouse() { |
866 | @@ -1165,51 +1218,6 @@ |
867 | next_worker_without_cost_spawn_[worker_types_without_cost_index] = never(); |
868 | } |
869 | |
870 | -bool Warehouse::can_attack() { |
871 | - return descr().get_conquers() > 0; |
872 | -} |
873 | - |
874 | -void Warehouse::aggressor(Soldier& enemy) { |
875 | - if (!descr().get_conquers()) |
876 | - return; |
877 | - |
878 | - Game& game = dynamic_cast<Game&>(owner().egbase()); |
879 | - Map& map = game.map(); |
880 | - if (enemy.get_owner() == &owner() || enemy.get_battle() || |
881 | - descr().get_conquers() <= map.calc_distance(enemy.get_position(), get_position())) |
882 | - return; |
883 | - |
884 | - if (game.map().find_bobs(Area<FCoords>(map.get_fcoords(base_flag().get_position()), 2), nullptr, |
885 | - FindBobEnemySoldier(&owner()))) |
886 | - return; |
887 | - |
888 | - DescriptionIndex const soldier_index = owner().tribe().soldier(); |
889 | - Requirements noreq; |
890 | - |
891 | - if (!count_workers(game, soldier_index, noreq, Match::kCompatible)) |
892 | - return; |
893 | - |
894 | - Soldier& defender = dynamic_cast<Soldier&>(launch_worker(game, soldier_index, noreq)); |
895 | - defender.start_task_defense(game, false); |
896 | -} |
897 | - |
898 | -bool Warehouse::attack(Soldier& enemy) { |
899 | - Game& game = dynamic_cast<Game&>(owner().egbase()); |
900 | - DescriptionIndex const soldier_index = owner().tribe().soldier(); |
901 | - Requirements noreq; |
902 | - |
903 | - if (count_workers(game, soldier_index, noreq, Match::kCompatible)) { |
904 | - Soldier& defender = dynamic_cast<Soldier&>(launch_worker(game, soldier_index, noreq)); |
905 | - defender.start_task_defense(game, true); |
906 | - enemy.send_signal(game, "sleep"); |
907 | - return true; |
908 | - } |
909 | - |
910 | - set_defeating_player(enemy.owner().player_number()); |
911 | - schedule_destroy(game); |
912 | - return false; |
913 | -} |
914 | - |
915 | void Warehouse::PlannedWorkers::cleanup() { |
916 | while (!requests.empty()) { |
917 | delete requests.back(); |
918 | |
919 | === modified file 'src/logic/map_objects/tribes/warehouse.h' |
920 | --- src/logic/map_objects/tribes/warehouse.h 2017-04-23 12:11:19 +0000 |
921 | +++ src/logic/map_objects/tribes/warehouse.h 2017-05-21 11:20:39 +0000 |
922 | @@ -24,7 +24,6 @@ |
923 | #include "base/wexception.h" |
924 | #include "economy/request.h" |
925 | #include "economy/wares_queue.h" |
926 | -#include "logic/map_objects/attackable.h" |
927 | #include "logic/map_objects/tribes/building.h" |
928 | #include "logic/map_objects/tribes/soldiercontrol.h" |
929 | #include "logic/map_objects/tribes/wareworker.h" |
930 | @@ -71,7 +70,7 @@ |
931 | DISALLOW_COPY_AND_ASSIGN(WarehouseDescr); |
932 | }; |
933 | |
934 | -class Warehouse : public Building, public Attackable, public SoldierControl { |
935 | +class Warehouse : public Building, public SoldierControl { |
936 | friend class PortDock; |
937 | friend class MapBuildingdataPacket; |
938 | |
939 | @@ -221,15 +220,6 @@ |
940 | void enable_spawn(Game&, uint8_t worker_types_without_cost_index); |
941 | void disable_spawn(uint8_t worker_types_without_cost_index); |
942 | |
943 | - // Begin Attackable implementation |
944 | - Player& owner() const override { |
945 | - return Building::owner(); |
946 | - } |
947 | - bool can_attack() override; |
948 | - void aggressor(Soldier&) override; |
949 | - bool attack(Soldier&) override; |
950 | - // End Attackable implementation |
951 | - |
952 | void receive_ware(Game&, DescriptionIndex ware) override; |
953 | void receive_worker(Game&, Worker& worker) override; |
954 | |
955 | @@ -250,13 +240,26 @@ |
956 | |
957 | void log_general_info(const EditorGameBase&) override; |
958 | |
959 | -protected: |
960 | +private: |
961 | + // A warehouse that conquers space can also be attacked. |
962 | + class AttackTarget : public Widelands::AttackTarget { |
963 | + public: |
964 | + AttackTarget(Warehouse* warehouse) : warehouse_(warehouse) { |
965 | + } |
966 | + |
967 | + bool can_be_attacked() const override; |
968 | + void enemy_soldier_approaches(const Soldier&) const override; |
969 | + Widelands::AttackTarget::AttackResult attack(Soldier*) const override; |
970 | + |
971 | + private: |
972 | + Warehouse* const warehouse_; |
973 | + }; |
974 | + |
975 | + void init_portdock(EditorGameBase& egbase); |
976 | + |
977 | /// Initializes the container sizes for the owner's tribe. |
978 | void init_containers(Player& owner); |
979 | |
980 | -private: |
981 | - void init_portdock(EditorGameBase& egbase); |
982 | - |
983 | /** |
984 | * Plan to produce a certain worker type in this warehouse. This means |
985 | * requesting all the necessary wares, if multiple different wares types are |
986 | @@ -282,6 +285,7 @@ |
987 | void update_planned_workers(Game&, PlannedWorkers& pw); |
988 | void update_all_planned_workers(Game&); |
989 | |
990 | + AttackTarget attack_target_; |
991 | WarehouseSupply* supply_; |
992 | |
993 | std::vector<StockPolicy> ware_policy_; |
994 | |
995 | === modified file 'src/logic/player.cc' |
996 | --- src/logic/player.cc 2017-04-22 05:35:46 +0000 |
997 | +++ src/logic/player.cc 2017-05-21 11:20:39 +0000 |
998 | @@ -452,8 +452,8 @@ |
999 | log("Clearing for road at (%i, %i)\n", c.x, c.y); |
1000 | |
1001 | // Make sure that the player owns the area around. |
1002 | - dynamic_cast<Game&>(egbase()) |
1003 | - .conquer_area_no_building(PlayerArea<Area<FCoords>>(player_number(), Area<FCoords>(c, 1))); |
1004 | + dynamic_cast<Game&>(egbase()).conquer_area_no_building( |
1005 | + PlayerArea<Area<FCoords>>(player_number(), Area<FCoords>(c, 1))); |
1006 | |
1007 | if (BaseImmovable* const immovable = c.field->get_immovable()) { |
1008 | assert(immovable != &start); |
1009 | @@ -869,8 +869,8 @@ |
1010 | log("enemyflagaction: count is 0\n"); |
1011 | else if (is_hostile(flag.owner())) { |
1012 | if (Building* const building = flag.get_building()) { |
1013 | - if (upcast(Attackable, attackable, building)) { |
1014 | - if (attackable->can_attack()) { |
1015 | + if (const AttackTarget* attack_target = building->attack_target()) { |
1016 | + if (attack_target->can_be_attacked()) { |
1017 | std::vector<Soldier*> attackers; |
1018 | find_attack_soldiers(flag, &attackers, count); |
1019 | assert(attackers.size() <= count); |
1020 | |
1021 | === modified file 'src/wui/attack_box.h' |
1022 | --- src/wui/attack_box.h 2017-02-23 17:58:25 +0000 |
1023 | +++ src/wui/attack_box.h 2017-05-21 11:20:39 +0000 |
1024 | @@ -26,7 +26,6 @@ |
1025 | #include "graphic/font_handler1.h" |
1026 | #include "graphic/text/font_set.h" |
1027 | #include "graphic/text_constants.h" |
1028 | -#include "logic/map_objects/attackable.h" |
1029 | #include "logic/map_objects/bob.h" |
1030 | #include "logic/map_objects/tribes/soldier.h" |
1031 | #include "logic/player.h" |
1032 | |
1033 | === modified file 'src/wui/fieldaction.cc' |
1034 | --- src/wui/fieldaction.cc 2017-02-27 19:10:24 +0000 |
1035 | +++ src/wui/fieldaction.cc 2017-05-21 11:20:39 +0000 |
1036 | @@ -26,7 +26,7 @@ |
1037 | #include "economy/road.h" |
1038 | #include "graphic/graphic.h" |
1039 | #include "logic/cmd_queue.h" |
1040 | -#include "logic/map_objects/attackable.h" |
1041 | +#include "logic/map_objects/tribes/attack_target.h" |
1042 | #include "logic/map_objects/tribes/soldier.h" |
1043 | #include "logic/map_objects/tribes/tribe_descr.h" |
1044 | #include "logic/map_objects/tribes/warehouse.h" |
1045 | @@ -380,13 +380,16 @@ |
1046 | void FieldActionWindow::add_buttons_attack() { |
1047 | UI::Box& a_box = *new UI::Box(&tabpanel_, 0, 0, UI::Box::Horizontal); |
1048 | |
1049 | - if (upcast(Widelands::Attackable, attackable, map_->get_immovable(node_))) { |
1050 | - if (player_ && player_->is_hostile(attackable->owner()) && attackable->can_attack()) { |
1051 | - attack_box_ = new AttackBox(&a_box, player_, &node_, 0, 0); |
1052 | - a_box.add(attack_box_); |
1053 | + if (upcast(Widelands::Building, building, map_->get_immovable(node_))) { |
1054 | + if (const Widelands::AttackTarget* attack_target = building->attack_target()) { |
1055 | + if (player_ && player_->is_hostile(building->owner()) && |
1056 | + attack_target->can_be_attacked()) { |
1057 | + attack_box_ = new AttackBox(&a_box, player_, &node_, 0, 0); |
1058 | + a_box.add(attack_box_); |
1059 | |
1060 | - set_fastclick_panel(&add_button( |
1061 | - &a_box, "attack", pic_attack, &FieldActionWindow::act_attack, _("Start attack"))); |
1062 | + set_fastclick_panel(&add_button( |
1063 | + &a_box, "attack", pic_attack, &FieldActionWindow::act_attack, _("Start attack"))); |
1064 | + } |
1065 | } |
1066 | } |
1067 |
I was looking into how Warehouse works - thinking about trading. And the design of this irked me, so I improved it.