Merge lp:~widelands-dev/widelands/warehouse_refactor into lp:widelands

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
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.

To post a comment you must log in.
Revision history for this message
SirVer (sirver) wrote :

I was looking into how Warehouse works - thinking about trading. And the design of this irked me, so I improved it.

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://travis-ci.org/widelands/widelands/builds/234476861.
Appveyor build 2062. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_warehouse_refactor-2062.

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

Subscribers

People subscribed via source and target branches

to status/vote changes: