Merge lp:~widelands-dev/widelands/choose-attack-soldiers into lp:widelands

Proposed by Benedikt Straub
Status: Merged
Merged at revision: 9103
Proposed branch: lp:~widelands-dev/widelands/choose-attack-soldiers
Merge into: lp:widelands
Diff against target: 805 lines (+440/-92)
11 files modified
src/ai/defaultai_warfare.cc (+8/-2)
src/logic/game.cc (+4/-3)
src/logic/game.h (+1/-1)
src/logic/map_objects/tribes/militarysite.cc (+1/-0)
src/logic/player.cc (+7/-10)
src/logic/player.h (+1/-1)
src/logic/playercommand.cc (+43/-11)
src/logic/playercommand.h (+4/-4)
src/wui/attack_box.cc (+287/-51)
src/wui/attack_box.h (+79/-3)
src/wui/fieldaction.cc (+5/-6)
To merge this branch: bzr merge lp:~widelands-dev/widelands/choose-attack-soldiers
Reviewer Review Type Date Requested Status
GunChleoc Approve
Toni Förster Approve
Review via email: mp+367041@code.launchpad.net

Commit message

Allow the player to choose the soldiers to send in the attack box

Description of the change

The attack box contains two lists of soldiers: One for the attacking soldiers and one for the rest. Click on a soldier to move him to the other list. Ctrl-Click to move all soldiers.

If you just want to attack quickly and don´t care about soldier choice, you can still use the slider or the more/less buttons.

Note that one soldier will always remain in every militarysite. Currently the engine decides which soldier this is, and he will not be shown in the attack box. Ideally, all available soldiers should be shown, grouped by their building, and the player can then choose which soldier(s) remain(s) behind, but this would clutter up the interface too much in my opinion if there are many own militarysites, especially small ones, nearby.

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

Continuous integration builds have changed state:

Travis build 4908. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/529279191.
Appveyor build 4689. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_choose_attack_soldiers-4689.

Revision history for this message
GunChleoc (gunchleoc) :
Revision history for this message
Toni Förster (stonerl) wrote :
Revision history for this message
Benedikt Straub (nordfriese) wrote :

Formatted the tooltips and replied to diff comments.

> Did you remove the changes from this branch? […]

No, I didn´t even touch the file where that code is located?

Revision history for this message
Toni Förster (stonerl) wrote :

Sorry my mistake. But I do have a request. Could you add the CTRL-key behaviour to set the soldiers to max or minimum?

This is the only window left where this isn't possible.

Revision history for this message
Benedikt Straub (nordfriese) wrote :

OK, I´ll add this :)
By the way, you can also Ctrl-click one of the ListOfSoldiers to put all soldiers on the other list.

Revision history for this message
Toni Förster (stonerl) wrote :

Tested 9101 and it works perfect.

Just one more thing. There are only 8 Soldiers shown per row. Which means there is a huge space below the "Start Attack" button. Is it somehow possible to use this space as well and show 9 soldiers per row?

Revision history for this message
GunChleoc (gunchleoc) wrote :

How about removing the slider and replacing it with some buttons next to the lists? I hate fiddling with my mouse to position it over that tiny thing.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Another idea: Add soldier levels to the tooltips. Good idea/bad idea?

Revision history for this message
Benedikt Straub (nordfriese) wrote :

I´m in favour of keeping the slider. When you have a massive army of similar-strength soldiers and you want to attack with about half of them, the slider is the most efficient way to do this. A spinner, even with big-step buttons, would be too slow IMHO if you have really huge armies.

Since the tooltip already contains the description what happens on click and Ctrl-click, the soldier level shouldn´t be shown there as well. But I can add it to a textbox below the list like in militarysites…

Revision history for this message
Toni Förster (stonerl) wrote :

> How about removing the slider and replacing it with some buttons next to the
> lists? I hate fiddling with my mouse to position it over that tiny thing.

Why not just make the slider bigger?

Revision history for this message
GunChleoc (gunchleoc) wrote :

How about using Shift-click for adding all soldiers from the start of the list to the one that you are clicking?

> But I can add it to a textbox below the list like in militarysites…

Sounds good :)

Revision history for this message
Toni Förster (stonerl) wrote :

Some changes I'd like to propose.

Do we need the word "soldiers" left to the slider? Shouldn't this be
obvious? We could get more space by removing text. Secondly make the
slider buttons as big as the attack button.

Here is how it could look like:

https://fosuta.org/pics/attack.png

I can attach a diff to the bug report if you want me to.

Is it possible to move the "Start Attack" Button into the attack_box?
We would then get rid of the space below.

Revision history for this message
Toni Förster (stonerl) wrote :
Revision history for this message
Toni Förster (stonerl) wrote :

Sorry for spamming, but I think this would be the ideal solution, IMHO.

https://fosuta.org/pics/attack2.png

Revision history for this message
Benedikt Straub (nordfriese) wrote :

Un nu de Weddervörhersaag: Een bannig dichter Hagel vun Vörslagens ;)
How do you like the one I implemented now?

Revision history for this message
Toni Förster (stonerl) wrote :

> Un nu de Weddervörhersaag: Een bannig dichter Hagel vun Vörslagens ;)

:D

> How do you like the one I implemented now?

Almost awesome. :)

Some nits though:

The tooltips for the lists hide the line were the soldier’s strength is shown.

If you could change the values to the ones in the diff-comments and add the
space it would look perfect.

See the screenshot:

https://fosuta.org/pics/attack3.png

And don't forget to remove or comment out this line in fieldaction.cc:

static const char* const pic_attack = "images/wui/buildings/menu_attack.png";

Revision history for this message
Toni Förster (stonerl) wrote :

Oh I forgot, If you could make the slider 17 pix high instead of 20 it would look more elegant, IMHO.

Revision history for this message
Benedikt Straub (nordfriese) wrote :

Implemented the changes as you suggested :)

> The tooltips for the lists hide the line were the soldier’s strength is shown.

I´m not sure how to solve this best: putting the textarea with the info on top wouldn´t look good IMHO, and the tooltip is long enough and shouldn´t contain this as well.
Perhaps I could swap the contents of the textarea and the tooltip, so the explanation what happens when clicking is displayed in the bottom line, and the info of the soldier under the mouse is shown as a tooltip that contains nothing else. What do you think?

Revision history for this message
Toni Förster (stonerl) wrote :

Sorry to bother you once more :(

Could you make the slider 210px wide, please? Then we would have a maximum of 10 soldiers per row, which looks even better. :)

Regarding the tooltip. Putting them in the text area where the soldier's info are shown is too intrusive, IMHO. Can text areas also hold tooltips?

On the other hand these tooltips are quite massive. Why don't we add them to the controls-list in the encyclopedia?

Revision history for this message
Benedikt Straub (nordfriese) wrote :

> Sorry to bother you once more :(
 No problem :)

> Could you make the slider 210px wide, please
 Done

> Can text areas also hold tooltips?
 Every UI element can have a tooltip, so it could be moved to the bottom line. Problem with this is that nobody has a reason to check out a textarea´s tooltip, and since the behaviour of Shift-click is not obvious, people would notice this feature only by chance.

> On the other hand these tooltips are quite massive.
 No more than those on the inputqueue buttons ;)

> Why don't we add them to the controls-list in the encyclopedia?
 How many players actually read that frequently? ;) Again there´s the danger that many people won´t notice if it isn´t explained in the attackbox. Besides, this would be inconsistent as Ctrl-/Shift modifiers are explained in a tooltip for all other controls.

Revision history for this message
Toni Förster (stonerl) wrote :

Hmmm, what if we added them to the "Attackers" and "Not attacking" fields? Players would definitely hover over these areas.

Revision history for this message
Benedikt Straub (nordfriese) wrote :

Good idea, implemented it like this then :)

Revision history for this message
Toni Förster (stonerl) wrote :

Looks good

review: Approve
Revision history for this message
Toni Förster (stonerl) wrote :

a/this

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4932. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/530921209.
Appveyor build 4713. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_choose_attack_soldiers-4713.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I have pushed a commit with i18n fixes. Please merge this branch if you agree with them.

I replaced "x / y soldiers" with "x soldiers", because the total is already on the button next to the slider, and we don't need the information twice.

review: Approve
Revision history for this message
Benedikt Straub (nordfriese) wrote :

Thanks for the fixes :)

@bunnybot merge

Revision history for this message
Toni Förster (stonerl) wrote :

src/wui/attack_box.cc:109:75: warning: unused parameter 'max_attackers' [-Wunused-parameter]
static inline std::string slider_heading(uint32_t num_attackers, uint32_t max_attackers)

Revision history for this message
GunChleoc (gunchleoc) wrote :

Oops. I have cleaned this up now.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/ai/defaultai_warfare.cc'
--- src/ai/defaultai_warfare.cc 2019-04-09 16:43:49 +0000
+++ src/ai/defaultai_warfare.cc 2019-05-11 10:49:50 +0000
@@ -481,7 +481,8 @@
481 }481 }
482482
483 // how many attack soldiers we can send?483 // how many attack soldiers we can send?
484 int32_t attackers = player_->find_attack_soldiers(*flag);484 std::vector<Soldier*> soldiers;
485 int32_t attackers = player_->find_attack_soldiers(*flag, &soldiers);
485 assert(attackers < 500);486 assert(attackers < 500);
486487
487 if (attackers > 5) {488 if (attackers > 5) {
@@ -499,7 +500,12 @@
499 player_number(), flag->get_position().x, flag->get_position().y, best_score, attackers,500 player_number(), flag->get_position().x, flag->get_position().y, best_score, attackers,
500 enemy_sites[best_target].attack_counter + 1,501 enemy_sites[best_target].attack_counter + 1,
501 (gametime - enemy_sites[best_target].last_time_attacked) / 1000);502 (gametime - enemy_sites[best_target].last_time_attacked) / 1000);
502 game().send_player_enemyflagaction(*flag, player_number(), static_cast<uint16_t>(attackers));503 std::vector<Serial> attacking_soldiers;
504 for (int a = 0; a < attackers; ++a) {
505 // TODO(Nordfriese): We could now choose the soldiers we want to send
506 attacking_soldiers.push_back(soldiers[a]->serial());
507 }
508 game().send_player_enemyflagaction(*flag, player_number(), attacking_soldiers);
503 assert(1 <509 assert(1 <
504 player_->vision(Map::get_index(flag->get_building()->get_position(), map.get_width())));510 player_->vision(Map::get_index(flag->get_building()->get_position(), map.get_width())));
505 attackers_count_ += attackers;511 attackers_count_ += attackers;
506512
=== modified file 'src/logic/game.cc'
--- src/logic/game.cc 2019-05-05 18:53:14 +0000
+++ src/logic/game.cc 2019-05-11 10:49:50 +0000
@@ -820,10 +820,11 @@
820820
821void Game::send_player_enemyflagaction(const Flag& flag,821void Game::send_player_enemyflagaction(const Flag& flag,
822 PlayerNumber const who_attacks,822 PlayerNumber const who_attacks,
823 uint32_t const num_soldiers) {823 const std::vector<Serial>& soldiers) {
824 if (1 < player(who_attacks)824 if (1 < player(who_attacks)
825 .vision(Map::get_index(flag.get_building()->get_position(), map().get_width())))825 .vision(Map::get_index(flag.get_building()->get_position(), map().get_width()))) {
826 send_player_command(*new CmdEnemyFlagAction(get_gametime(), who_attacks, flag, num_soldiers));826 send_player_command(*new CmdEnemyFlagAction(get_gametime(), who_attacks, flag, soldiers));
827 }
827}828}
828829
829void Game::send_player_ship_scouting_direction(Ship& ship, WalkingDir direction) {830void Game::send_player_ship_scouting_direction(Ship& ship, WalkingDir direction) {
830831
=== modified file 'src/logic/game.h'
--- src/logic/game.h 2019-03-01 16:24:48 +0000
+++ src/logic/game.h 2019-05-11 10:49:50 +0000
@@ -273,7 +273,7 @@
273 void send_player_change_training_options(TrainingSite&, TrainingAttribute, int32_t);273 void send_player_change_training_options(TrainingSite&, TrainingAttribute, int32_t);
274 void send_player_drop_soldier(Building&, int32_t);274 void send_player_drop_soldier(Building&, int32_t);
275 void send_player_change_soldier_capacity(Building&, int32_t);275 void send_player_change_soldier_capacity(Building&, int32_t);
276 void send_player_enemyflagaction(const Flag&, PlayerNumber, uint32_t count);276 void send_player_enemyflagaction(const Flag&, PlayerNumber, const std::vector<Serial>&);
277277
278 void send_player_ship_scouting_direction(Ship&, WalkingDir);278 void send_player_ship_scouting_direction(Ship&, WalkingDir);
279 void send_player_ship_construct_port(Ship&, Coords);279 void send_player_ship_construct_port(Ship&, Coords);
280280
=== modified file 'src/logic/map_objects/tribes/militarysite.cc'
--- src/logic/map_objects/tribes/militarysite.cc 2019-03-01 04:19:53 +0000
+++ src/logic/map_objects/tribes/militarysite.cc 2019-05-11 10:49:50 +0000
@@ -374,6 +374,7 @@
374 stationed % (capacity_ - stationed))374 stationed % (capacity_ - stationed))
375 .str();375 .str();
376 } else {376 } else {
377 /** TRANSLATORS: Number of soldiers stationed at a militarysite. */
377 *s = (boost::format(ngettext("%u soldier", "%u soldiers", stationed)) % stationed).str();378 *s = (boost::format(ngettext("%u soldier", "%u soldiers", stationed)) % stationed).str();
378 }379 }
379 } else {380 } else {
380381
=== modified file 'src/logic/player.cc'
--- src/logic/player.cc 2019-05-04 10:47:44 +0000
+++ src/logic/player.cc 2019-05-11 10:49:50 +0000
@@ -950,20 +950,17 @@
950950
951// TODO(unknown): Clean this mess up. The only action we really have right now is951// TODO(unknown): Clean this mess up. The only action we really have right now is
952// to attack, so pretending we have more types is pointless.952// to attack, so pretending we have more types is pointless.
953void Player::enemyflagaction(Flag& flag, PlayerNumber const attacker, uint32_t const count) {953void Player::enemyflagaction(Flag& flag, PlayerNumber const attacker,
954 if (attacker != player_number())954 const std::vector<Widelands::Soldier*>& soldiers) {
955 if (attacker != player_number()) {
955 log("Player (%d) is not the sender of an attack (%d)\n", attacker, player_number());956 log("Player (%d) is not the sender of an attack (%d)\n", attacker, player_number());
956 else if (count == 0)957 } else if (soldiers.empty()) {
957 log("enemyflagaction: count is 0\n");958 log("enemyflagaction: no soldiers given\n");
958 else if (is_hostile(flag.owner())) {959 } else if (is_hostile(flag.owner())) {
959 if (Building* const building = flag.get_building()) {960 if (Building* const building = flag.get_building()) {
960 if (const AttackTarget* attack_target = building->attack_target()) {961 if (const AttackTarget* attack_target = building->attack_target()) {
961 if (attack_target->can_be_attacked()) {962 if (attack_target->can_be_attacked()) {
962 std::vector<Soldier*> attackers;963 for (Soldier* temp_attacker : soldiers) {
963 find_attack_soldiers(flag, &attackers, count);
964 assert(attackers.size() <= count);
965
966 for (Soldier* temp_attacker : attackers) {
967 upcast(MilitarySite, ms, temp_attacker->get_location(egbase()));964 upcast(MilitarySite, ms, temp_attacker->get_location(egbase()));
968 ms->send_attacker(*temp_attacker, *building);965 ms->send_attacker(*temp_attacker, *building);
969 }966 }
970967
=== modified file 'src/logic/player.h'
--- src/logic/player.h 2019-04-26 16:52:39 +0000
+++ src/logic/player.h 2019-05-11 10:49:50 +0000
@@ -537,7 +537,7 @@
537 uint32_t find_attack_soldiers(Flag&,537 uint32_t find_attack_soldiers(Flag&,
538 std::vector<Soldier*>* soldiers = nullptr,538 std::vector<Soldier*>* soldiers = nullptr,
539 uint32_t max = std::numeric_limits<uint32_t>::max());539 uint32_t max = std::numeric_limits<uint32_t>::max());
540 void enemyflagaction(Flag&, PlayerNumber attacker, uint32_t count);540 void enemyflagaction(Flag&, PlayerNumber attacker, const std::vector<Widelands::Soldier*>&);
541541
542 uint32_t casualties() const {542 uint32_t casualties() const {
543 return casualties_;543 return casualties_;
544544
=== modified file 'src/logic/playercommand.cc'
--- src/logic/playercommand.cc 2019-03-09 08:58:52 +0000
+++ src/logic/playercommand.cc 2019-05-11 10:49:50 +0000
@@ -1589,7 +1589,11 @@
1589 des.unsigned_8();1589 des.unsigned_8();
1590 serial = des.unsigned_32();1590 serial = des.unsigned_32();
1591 des.unsigned_8();1591 des.unsigned_8();
1592 number = des.unsigned_8();1592 const uint32_t number = des.unsigned_32();
1593 soldiers.clear();
1594 for (uint32_t i = 0; i < number; ++i) {
1595 soldiers.push_back(des.unsigned_32());
1596 }
1593}1597}
15941598
1595void CmdEnemyFlagAction::execute(Game& game) {1599void CmdEnemyFlagAction::execute(Game& game) {
@@ -1597,16 +1601,21 @@
15971601
1598 if (upcast(Flag, flag, game.objects().get_object(serial))) {1602 if (upcast(Flag, flag, game.objects().get_object(serial))) {
1599 log("Cmd_EnemyFlagAction::execute player(%u): flag->owner(%d) "1603 log("Cmd_EnemyFlagAction::execute player(%u): flag->owner(%d) "
1600 "number=%u\n",1604 "number=%" PRIuS "\n",
1601 player->player_number(), flag->owner().player_number(), number);1605 player->player_number(), flag->owner().player_number(), soldiers.size());
16021606
1603 if (const Building* const building = flag->get_building()) {1607 if (const Building* const building = flag->get_building()) {
1604 if (player->is_hostile(flag->owner()) &&1608 if (player->is_hostile(flag->owner()) &&
1605 1 < player->vision(Map::get_index(building->get_position(), game.map().get_width())))1609 1 < player->vision(Map::get_index(building->get_position(), game.map().get_width()))) {
1606 player->enemyflagaction(*flag, sender(), number);1610 std::vector<Soldier*> result;
1607 else1611 for (Serial s : soldiers) {
1612 result.push_back(dynamic_cast<Soldier*>(game.objects().get_object(s)));
1613 }
1614 player->enemyflagaction(*flag, sender(), result);
1615 } else {
1608 log("Cmd_EnemyFlagAction::execute: ERROR: wrong player target not "1616 log("Cmd_EnemyFlagAction::execute: ERROR: wrong player target not "
1609 "seen or not hostile.\n");1617 "seen or not hostile.\n");
1618 }
1610 }1619 }
1611 }1620 }
1612}1621}
@@ -1617,20 +1626,40 @@
1617 ser.unsigned_8(1);1626 ser.unsigned_8(1);
1618 ser.unsigned_32(serial);1627 ser.unsigned_32(serial);
1619 ser.unsigned_8(sender());1628 ser.unsigned_8(sender());
1620 ser.unsigned_8(number);1629 ser.unsigned_32(soldiers.size());
1630 for (Serial s : soldiers) {
1631 ser.unsigned_32(s);
1632 }
1621}1633}
16221634
1623constexpr uint16_t kCurrentPacketVersionCmdEnemyFlagAction = 3;1635constexpr uint16_t kCurrentPacketVersionCmdEnemyFlagAction = 4;
16241636
1625void CmdEnemyFlagAction::read(FileRead& fr, EditorGameBase& egbase, MapObjectLoader& mol) {1637void CmdEnemyFlagAction::read(FileRead& fr, EditorGameBase& egbase, MapObjectLoader& mol) {
1626 try {1638 try {
1627 const uint16_t packet_version = fr.unsigned_16();1639 const uint16_t packet_version = fr.unsigned_16();
1628 if (packet_version == kCurrentPacketVersionCmdEnemyFlagAction) {1640 if (packet_version <= kCurrentPacketVersionCmdEnemyFlagAction && packet_version >= 3) {
1629 PlayerCommand::read(fr, egbase, mol);1641 PlayerCommand::read(fr, egbase, mol);
1630 fr.unsigned_8();1642 fr.unsigned_8();
1631 serial = get_object_serial_or_zero<Flag>(fr.unsigned_32(), mol);1643 serial = get_object_serial_or_zero<Flag>(fr.unsigned_32(), mol);
1632 fr.unsigned_8();1644 fr.unsigned_8();
1633 number = fr.unsigned_8();1645
1646 soldiers.clear();
1647 if (packet_version == kCurrentPacketVersionCmdEnemyFlagAction) {
1648 const uint32_t number = fr.unsigned_32();
1649 for (uint32_t i = 0; i < number; ++i) {
1650 soldiers.push_back(mol.get<Soldier>(fr.unsigned_32()).serial());
1651 }
1652 } else {
1653 const uint8_t number = fr.unsigned_8();
1654 upcast(Flag, flag, egbase.objects().get_object(serial));
1655 assert(flag);
1656 std::vector<Soldier*> result;
1657 egbase.get_player(sender())->find_attack_soldiers(*flag, &result, number);
1658 assert(result.size() == number);
1659 for (const auto& s : result) {
1660 soldiers.push_back(s->serial());
1661 }
1662 }
1634 } else {1663 } else {
1635 throw UnhandledVersionError(1664 throw UnhandledVersionError(
1636 "CmdEnemyFlagAction", packet_version, kCurrentPacketVersionCmdEnemyFlagAction);1665 "CmdEnemyFlagAction", packet_version, kCurrentPacketVersionCmdEnemyFlagAction);
@@ -1653,7 +1682,10 @@
16531682
1654 // Now param1683 // Now param
1655 fw.unsigned_8(sender());1684 fw.unsigned_8(sender());
1656 fw.unsigned_8(number);1685 fw.unsigned_32(soldiers.size());
1686 for (Serial s : soldiers) {
1687 fw.unsigned_32(mos.get_object_file_index(*egbase.objects().get_object(s)));
1688 }
1657}1689}
16581690
1659/*** struct PlayerMessageCommand ***/1691/*** struct PlayerMessageCommand ***/
16601692
=== modified file 'src/logic/playercommand.h'
--- src/logic/playercommand.h 2019-02-23 11:00:49 +0000
+++ src/logic/playercommand.h 2019-05-11 10:49:50 +0000
@@ -733,10 +733,10 @@
733};733};
734734
735struct CmdEnemyFlagAction : public PlayerCommand {735struct CmdEnemyFlagAction : public PlayerCommand {
736 CmdEnemyFlagAction() : PlayerCommand(), serial(0), number(0) {736 CmdEnemyFlagAction() : PlayerCommand(), serial(0) {
737 } // For savegame loading737 } // For savegame loading
738 CmdEnemyFlagAction(uint32_t t, int32_t p, const Flag& f, uint32_t num)738 CmdEnemyFlagAction(uint32_t t, int32_t p, const Flag& f, const std::vector<Serial>& s)
739 : PlayerCommand(t, p), serial(f.serial()), number(num) {739 : PlayerCommand(t, p), serial(f.serial()), soldiers(s) {
740 }740 }
741741
742 // Write these commands to a file (for savegames)742 // Write these commands to a file (for savegames)
@@ -754,7 +754,7 @@
754754
755private:755private:
756 Serial serial;756 Serial serial;
757 uint8_t number;757 std::vector<Serial> soldiers;
758};758};
759759
760/// Abstract base for commands about a message.760/// Abstract base for commands about a message.
761761
=== modified file 'src/wui/attack_box.cc'
--- src/wui/attack_box.cc 2019-02-23 11:00:49 +0000
+++ src/wui/attack_box.cc 2019-05-11 10:49:50 +0000
@@ -30,7 +30,7 @@
30#include "graphic/text_constants.h"30#include "graphic/text_constants.h"
31#include "logic/map_objects/tribes/soldier.h"31#include "logic/map_objects/tribes/soldier.h"
3232
33constexpr int32_t kUpdateTimeInGametimeMs = 1000; // 1 second, gametime33constexpr int32_t kUpdateTimeInGametimeMs = 500; // half a second, gametime
3434
35AttackBox::AttackBox(UI::Panel* parent,35AttackBox::AttackBox(UI::Panel* parent,
36 Widelands::Player* player,36 Widelands::Player* player,
@@ -45,19 +45,24 @@
45 init();45 init();
46}46}
4747
48uint32_t AttackBox::get_max_attackers() {48std::vector<Widelands::Soldier*> AttackBox::get_max_attackers() {
49 assert(player_);49 assert(player_);
5050
51 if (upcast(Building, building, map_.get_immovable(*node_coordinates_))) {51 if (upcast(Building, building, map_.get_immovable(*node_coordinates_))) {
52 if (player_->vision(map_.get_index(building->get_position(), map_.get_width())) <= 1) {52 if (player_->vision(map_.get_index(building->get_position(), map_.get_width())) > 1) {
53 // Player can't see the buildings door, so it can't be attacked53 std::vector<Widelands::Soldier*> v;
54 // This is the same check as done later on in send_player_enemyflagaction()54 // TODO(Nordfriese): This method decides by itself which soldier remains in the building.
55 return 0;55 // This soldier will not show up in the result vector. Perhaps we should show all
56 // available soldiers, grouped by building, so the player can choose between all soldiers
57 // knowing that at least one of each group will have to stay at home. However, this
58 // could clutter up the screen a lot. Especially if you have many small buildings.
59 player_->find_attack_soldiers(building->base_flag(), &v);
60 return v;
56 }61 }
5762 // Player can't see the buildings door, so it can't be attacked
58 return player_->find_attack_soldiers(building->base_flag());63 // This is the same check as done later on in send_player_enemyflagaction()
59 }64 }
60 return 0;65 return std::vector<Widelands::Soldier*>();
61}66}
6267
63std::unique_ptr<UI::HorizontalSlider> AttackBox::add_slider(UI::Box& parent,68std::unique_ptr<UI::HorizontalSlider> AttackBox::add_slider(UI::Box& parent,
@@ -86,7 +91,7 @@
86 void (AttackBox::*fn)(),91 void (AttackBox::*fn)(),
87 const std::string& tooltip_text) {92 const std::string& tooltip_text) {
88 std::unique_ptr<UI::Button> button(new UI::Button(93 std::unique_ptr<UI::Button> button(new UI::Button(
89 &parent, text, 8, 8, 26, 26, UI::ButtonStyle::kWuiPrimary, text, tooltip_text));94 &parent, text, 8, 8, 34, 34, UI::ButtonStyle::kWuiPrimary, text, tooltip_text));
90 button->sigclicked.connect(boost::bind(fn, boost::ref(*this)));95 button->sigclicked.connect(boost::bind(fn, boost::ref(*this)));
91 parent.add(button.get());96 parent.add(button.get());
92 return button;97 return button;
@@ -96,31 +101,78 @@
96 * Update available soldiers101 * Update available soldiers
97 */102 */
98void AttackBox::think() {103void AttackBox::think() {
99 const int32_t gametime = player_->egbase().get_gametime();104 if ((player_->egbase().get_gametime() - lastupdate_) > kUpdateTimeInGametimeMs) {
100 if ((gametime - lastupdate_) > kUpdateTimeInGametimeMs) {105 update_attack(false);
101 update_attack();
102 lastupdate_ = gametime;
103 }106 }
104}107}
105108
106void AttackBox::update_attack() {109static inline std::string slider_heading(uint32_t num_attackers) {
110 /** TRANSLATORS: Number of soldiers that should attack. Used in Attack box. */
111 return (boost::format(ngettext("%u soldier", "%u soldiers", num_attackers)) % num_attackers).str();
112}
113
114void AttackBox::update_attack(bool action_on_panel) {
115 lastupdate_ = player_->egbase().get_gametime();
116
107 assert(soldiers_slider_.get());117 assert(soldiers_slider_.get());
108 assert(soldiers_text_.get());118 assert(soldiers_text_.get());
109 assert(less_soldiers_.get());119 assert(less_soldiers_.get());
110 assert(more_soldiers_.get());120 assert(more_soldiers_.get());
111121 assert(attacking_soldiers_.get());
112 int32_t max_attackers = get_max_attackers();122 assert(remaining_soldiers_.get());
113123
124 std::vector<Widelands::Soldier*> all_attackers = get_max_attackers();
125 const int max_attackers = all_attackers.size();
126
127 // Update number of available soldiers
114 if (soldiers_slider_->get_max_value() != max_attackers) {128 if (soldiers_slider_->get_max_value() != max_attackers) {
115 soldiers_slider_->set_max_value(max_attackers);129 soldiers_slider_->set_max_value(max_attackers);
116 }130 }
117131
132 // Add new soldiers and remove missing soldiers to/from the list
133 for (const auto& s : all_attackers) {
134 if (!attacking_soldiers_->contains(s) && !remaining_soldiers_->contains(s)) {
135 remaining_soldiers_->add(s);
136 }
137 }
138 for (const auto& s : remaining_soldiers_->get_soldiers()) {
139 if (std::find(all_attackers.begin(), all_attackers.end(), s) == all_attackers.end()) {
140 remaining_soldiers_->remove(s);
141 }
142 }
143 for (const auto& s : attacking_soldiers_->get_soldiers()) {
144 if (std::find(all_attackers.begin(), all_attackers.end(), s) == all_attackers.end()) {
145 attacking_soldiers_->remove(s);
146 }
147 }
148
149 if (action_on_panel) {
150 // The player clicked on soldiers in the list – update slider
151 soldiers_slider_->set_value(attacking_soldiers_->count_soldiers());
152 } else {
153 // The slider was moved or we were called from think() – shift lacking/extra soldiers between the lists
154 const int32_t lacking = soldiers_slider_->get_value() - attacking_soldiers_->count_soldiers();
155 if (lacking > 0) {
156 for (int32_t i = 0; i < lacking; ++i) {
157 const Widelands::Soldier* s = remaining_soldiers_->get_soldier();
158 remaining_soldiers_->remove(s);
159 attacking_soldiers_->add(s);
160 }
161 } else if (lacking < 0) {
162 for (int32_t i = 0; i > lacking; --i) {
163 const Widelands::Soldier* s = attacking_soldiers_->get_soldier();
164 attacking_soldiers_->remove(s);
165 remaining_soldiers_->add(s);
166 }
167 }
168 }
169
170 // Update slider, buttons and texts
118 soldiers_slider_->set_enabled(max_attackers > 0);171 soldiers_slider_->set_enabled(max_attackers > 0);
119 more_soldiers_->set_enabled(max_attackers > soldiers_slider_->get_value());172 more_soldiers_->set_enabled(max_attackers > soldiers_slider_->get_value());
120 less_soldiers_->set_enabled(soldiers_slider_->get_value() > 0);173 less_soldiers_->set_enabled(soldiers_slider_->get_value() > 0);
121 soldiers_text_->set_text(174
122 /** TRANSLATORS: %1% of %2% soldiers. Used in Attack box. */175 soldiers_text_->set_text(slider_heading(soldiers_slider_->get_value()));
123 (boost::format(_("%1% / %2%")) % soldiers_slider_->get_value() % max_attackers).str());
124176
125 more_soldiers_->set_title(std::to_string(max_attackers));177 more_soldiers_->set_title(std::to_string(max_attackers));
126}178}
@@ -128,48 +180,232 @@
128void AttackBox::init() {180void AttackBox::init() {
129 assert(node_coordinates_);181 assert(node_coordinates_);
130182
131 uint32_t max_attackers = get_max_attackers();183 std::vector<Widelands::Soldier*> all_attackers = get_max_attackers();
132184 const size_t max_attackers = all_attackers.size();
133 UI::Box& linebox = *new UI::Box(this, 0, 0, UI::Box::Horizontal);185
134 add(&linebox);186 UI::Box& mainbox = *new UI::Box(this, 0, 0, UI::Box::Vertical);
135 add_text(linebox, _("Soldiers:"));187 add(&mainbox);
136 linebox.add_space(8);188
137189 UI::Box& linebox = *new UI::Box(&mainbox, 0, 0, UI::Box::Horizontal);
138 less_soldiers_ =190 mainbox.add(&linebox);
139 add_button(linebox, "0", &AttackBox::send_less_soldiers, _("Send less soldiers"));191
140192 less_soldiers_ = add_button(linebox, "0", &AttackBox::send_less_soldiers,
141 // Spliter of soldiers193 _("Send less soldiers. Hold down Ctrl to send no soldiers"));
194
142 UI::Box& columnbox = *new UI::Box(&linebox, 0, 0, UI::Box::Vertical);195 UI::Box& columnbox = *new UI::Box(&linebox, 0, 0, UI::Box::Vertical);
143 linebox.add(&columnbox);196 linebox.add(&columnbox);
144197
145 const std::string attack_string =198 soldiers_text_.reset(&add_text(columnbox, slider_heading(max_attackers > 0 ? 1 : 0),
146 (boost::format(_("%1% / %2%")) % (max_attackers > 0 ? 1 : 0) % max_attackers).str();199 UI::Align::kCenter, UI_FONT_SIZE_ULTRASMALL));
147
148 soldiers_text_.reset(
149 &add_text(columnbox, attack_string, UI::Align::kCenter, UI_FONT_SIZE_ULTRASMALL));
150200
151 soldiers_slider_ = add_slider(201 soldiers_slider_ = add_slider(
152 columnbox, 100, 10, 0, max_attackers, max_attackers > 0 ? 1 : 0, _("Number of soldiers"));202 columnbox, 210, 17, 0, max_attackers, max_attackers > 0 ? 1 : 0, _("Number of soldiers"));
153203 soldiers_slider_->changed.connect([this]() { update_attack(false); });
154 soldiers_slider_->changed.connect(boost::bind(&AttackBox::update_attack, this));204
155 more_soldiers_ = add_button(linebox, std::to_string(max_attackers),205 more_soldiers_ = add_button(linebox, std::to_string(max_attackers), &AttackBox::send_more_soldiers,
156 &AttackBox::send_more_soldiers, _("Send more soldiers"));206 _("Send more soldiers. Hold down Ctrl to send as many soldiers as possible"));
207 linebox.add_space(8);
208
209 attack_button_.reset(new UI::Button(&linebox, "attack", 8, 8, 34, 34, UI::ButtonStyle::kWuiPrimary,
210 g_gr->images().get("images/wui/buildings/menu_attack.png"), _("Start attack")));
211 linebox.add(attack_button_.get());
212
213 attacking_soldiers_.reset(new ListOfSoldiers(&mainbox, this, 0, 0, 30, 30));
214 remaining_soldiers_.reset(new ListOfSoldiers(&mainbox, this, 0, 0, 30, 30));
215 attacking_soldiers_->set_complement(remaining_soldiers_.get());
216 remaining_soldiers_->set_complement(attacking_soldiers_.get());
217 for (const auto& s : all_attackers) {
218 remaining_soldiers_->add(s);
219 }
220
221 boost::format tooltip_format("%s<br><p><font size=%d bold=0>%s<br>%s</font></p>");
222 {
223 UI::Textarea& txt = add_text(mainbox, _("Attackers:"));
224 // Needed so we can get tooltips
225 txt.set_handle_mouse(true);
226 txt.set_tooltip((tooltip_format
227 % _("Click on a soldier to remove him from the list of attackers")
228 % UI_FONT_SIZE_MESSAGE
229 % _("Hold down Ctrl to remove all soldiers from the list")
230 % _("Hold down Shift to remove all soldiers up to the one you’re pointing at"))
231 .str());
232 mainbox.add(attacking_soldiers_.get(), UI::Box::Resizing::kFullSize);
233 }
234
235 {
236 UI::Textarea& txt = add_text(mainbox, _("Not attacking:"));
237 txt.set_handle_mouse(true);
238 txt.set_tooltip((tooltip_format
239 % _("Click on a soldier to add him to the list of attackers")
240 % UI_FONT_SIZE_MESSAGE
241 % _("Hold down Ctrl to add all soldiers to the list")
242 % _("Hold down Shift to add all soldiers up to the one you’re pointing at"))
243 .str());
244 mainbox.add(remaining_soldiers_.get(), UI::Box::Resizing::kFullSize);
245 }
246
247 current_soldier_stats_.reset(new UI::Textarea(&mainbox, "", UI::Align::kCenter));
248 mainbox.add(current_soldier_stats_.get(), UI::Box::Resizing::kFullSize, UI::Align::kCenter);
157249
158 soldiers_slider_->set_enabled(max_attackers > 0);250 soldiers_slider_->set_enabled(max_attackers > 0);
159 more_soldiers_->set_enabled(max_attackers > 0);251 more_soldiers_->set_enabled(max_attackers > 0);
160 less_soldiers_->set_enabled(max_attackers > 0);
161}252}
162253
163void AttackBox::send_less_soldiers() {254void AttackBox::send_less_soldiers() {
164 assert(soldiers_slider_.get());255 assert(soldiers_slider_.get());
165 soldiers_slider_->set_value(soldiers_slider_->get_value() - 1);256 soldiers_slider_->set_value((SDL_GetModState() & KMOD_CTRL) ? 0 : soldiers_slider_->get_value() - 1);
166}257}
167258
168void AttackBox::send_more_soldiers() {259void AttackBox::send_more_soldiers() {
169 soldiers_slider_->set_value(soldiers_slider_->get_value() + 1);260 soldiers_slider_->set_value((SDL_GetModState() & KMOD_CTRL) ? soldiers_slider_->get_max_value() :
170}261 soldiers_slider_->get_value() + 1);
171262}
172uint32_t AttackBox::soldiers() const {263
173 assert(soldiers_slider_.get());264size_t AttackBox::count_soldiers() const {
174 return soldiers_slider_->get_value();265 return attacking_soldiers_->count_soldiers();
175}266}
267
268std::vector<Widelands::Serial> AttackBox::soldiers() const {
269 std::vector<Widelands::Serial> result;
270 for (const auto& s : attacking_soldiers_->get_soldiers()) {
271 result.push_back(s->serial());
272 }
273 return result;
274}
275
276constexpr int kSoldierIconWidth = 32;
277constexpr int kSoldierIconHeight = 30;
278
279AttackBox::ListOfSoldiers::ListOfSoldiers(UI::Panel* const parent,
280 AttackBox* parent_box,
281 int32_t const x,
282 int32_t const y,
283 int const w,
284 int const h,
285 bool restrict_rows)
286 : UI::Panel(parent, x, y, w, h),
287 restricted_row_number_(restrict_rows),
288 attack_box_(parent_box) {
289 update_desired_size();
290}
291
292bool AttackBox::ListOfSoldiers::handle_mousepress(uint8_t btn, int32_t x, int32_t y) {
293 if (btn != SDL_BUTTON_LEFT || !other_) {
294 return UI::Panel::handle_mousepress(btn, x, y);
295 }
296 if (SDL_GetModState() & KMOD_CTRL) {
297 for (const auto& s : get_soldiers()) {
298 remove(s);
299 other_->add(s);
300 }
301 } else {
302 const Widelands::Soldier* soldier = soldier_at(x, y);
303 if (!soldier) {
304 return UI::Panel::handle_mousepress(btn, x, y);
305 }
306 if (SDL_GetModState() & KMOD_SHIFT) {
307 for (const auto& s : get_soldiers()) {
308 remove(s);
309 other_->add(s);
310 if (s == soldier) {
311 break;
312 }
313 }
314 } else {
315 remove(soldier);
316 other_->add(soldier);
317 }
318 }
319 attack_box_->update_attack(true);
320 return true;
321}
322
323void AttackBox::ListOfSoldiers::handle_mousein(bool) {
324 attack_box_->set_soldier_info_text();
325}
326
327bool AttackBox::ListOfSoldiers::handle_mousemove(uint8_t, int32_t x, int32_t y, int32_t, int32_t) {
328 if (const Widelands::Soldier* soldier = soldier_at(x, y)) {
329 attack_box_->set_soldier_info_text(
330 (boost::format(_("HP: %1$u/%2$u AT: %3$u/%4$u DE: %5$u/%6$u EV: %7$u/%8$u")) %
331 soldier->get_health_level() % soldier->descr().get_max_health_level() %
332 soldier->get_attack_level() % soldier->descr().get_max_attack_level() %
333 soldier->get_defense_level() % soldier->descr().get_max_defense_level() %
334 soldier->get_evade_level() % soldier->descr().get_max_evade_level())
335 .str());
336 } else {
337 attack_box_->set_soldier_info_text();
338 }
339 return true;
340}
341
342Widelands::Extent AttackBox::ListOfSoldiers::size() const {
343 const size_t nr_soldiers = count_soldiers();
344 uint32_t rows = nr_soldiers / current_size_;
345 if (nr_soldiers == 0 || rows * current_size_ < nr_soldiers) {
346 ++rows;
347 }
348 if (restricted_row_number_) {
349 return Widelands::Extent(rows, current_size_);
350 } else {
351 return Widelands::Extent(current_size_, rows);
352 }
353}
354
355void AttackBox::ListOfSoldiers::update_desired_size() {
356 current_size_ = std::max(1, restricted_row_number_ ? get_h() / kSoldierIconHeight : get_w() / kSoldierIconWidth);
357 const Widelands::Extent e = size();
358 set_desired_size(e.w * kSoldierIconWidth, e.h * kSoldierIconHeight);
359}
360
361const Widelands::Soldier* AttackBox::ListOfSoldiers::soldier_at(int32_t x, int32_t y) const {
362 if (x < 0 || y < 0 || soldiers_.empty()) {
363 return nullptr;
364 }
365 const int32_t col = x / kSoldierIconWidth;
366 const int32_t row = y / kSoldierIconHeight;
367 assert(col >= 0);
368 assert(row >= 0);
369 if ((restricted_row_number_ ? row : col) >= current_size_) {
370 return nullptr;
371 }
372 const int index = restricted_row_number_ ? current_size_ * col + row : current_size_ * row + col;
373 assert(index >= 0);
374 return static_cast<unsigned int>(index) < soldiers_.size() ? soldiers_[index] : nullptr;
375}
376
377void AttackBox::ListOfSoldiers::add(const Widelands::Soldier* s) {
378 soldiers_.push_back(s);
379 update_desired_size();
380}
381
382void AttackBox::ListOfSoldiers::remove(const Widelands::Soldier* s) {
383 const auto it = std::find(soldiers_.begin(), soldiers_.end(), s);
384 assert(it != soldiers_.end());
385 soldiers_.erase(it);
386 update_desired_size();
387}
388
389void AttackBox::ListOfSoldiers::draw(RenderTarget& dst) {
390 const size_t nr_soldiers = soldiers_.size();
391 int32_t column = 0;
392 int32_t row = 0;
393 for (uint32_t i = 0; i < nr_soldiers; ++i) {
394 Vector2i location(column * kSoldierIconWidth, row * kSoldierIconHeight);
395 soldiers_[i]->draw_info_icon(location, 1.0f, false, &dst);
396 if (restricted_row_number_) {
397 ++row;
398 if (row >= current_size_) {
399 row = 0;
400 ++column;
401 }
402 } else {
403 ++column;
404 if (column >= current_size_) {
405 column = 0;
406 ++row;
407 }
408 }
409 }
410}
411
176412
=== modified file 'src/wui/attack_box.h'
--- src/wui/attack_box.h 2019-02-23 11:00:49 +0000
+++ src/wui/attack_box.h 2019-05-11 10:49:50 +0000
@@ -22,6 +22,8 @@
2222
23#include <list>23#include <list>
24#include <memory>24#include <memory>
25#include <set>
26#include <vector>
2527
26#include "graphic/font_handler.h"28#include "graphic/font_handler.h"
27#include "graphic/text/font_set.h"29#include "graphic/text/font_set.h"
@@ -51,10 +53,18 @@
5153
52 void init();54 void init();
5355
54 uint32_t soldiers() const;56 size_t count_soldiers() const;
57 std::vector<Widelands::Serial> soldiers() const;
58 void set_soldier_info_text(std::string text = "") {
59 current_soldier_stats_->set_text(text);
60 }
61
62 UI::Button* get_attack_button() const {
63 return attack_button_.get();
64 }
5565
56private:66private:
57 uint32_t get_max_attackers();67 std::vector<Widelands::Soldier*> get_max_attackers();
58 std::unique_ptr<UI::HorizontalSlider> add_slider(UI::Box& parent,68 std::unique_ptr<UI::HorizontalSlider> add_slider(UI::Box& parent,
59 uint32_t width,69 uint32_t width,
60 uint32_t height,70 uint32_t height,
@@ -73,7 +83,7 @@
73 const std::string& tooltip_text);83 const std::string& tooltip_text);
7484
75 void think() override;85 void think() override;
76 void update_attack();86 void update_attack(bool);
77 void send_less_soldiers();87 void send_less_soldiers();
78 void send_more_soldiers();88 void send_more_soldiers();
7989
@@ -88,6 +98,72 @@
88 std::unique_ptr<UI::Button> less_soldiers_;98 std::unique_ptr<UI::Button> less_soldiers_;
89 std::unique_ptr<UI::Button> more_soldiers_;99 std::unique_ptr<UI::Button> more_soldiers_;
90100
101 // A SoldierPanel is not applicable here as it's keyed to a building and thinks too much
102 struct ListOfSoldiers : public UI::Panel {
103 ListOfSoldiers(UI::Panel* const parent,
104 AttackBox* parent_box,
105 int32_t const x,
106 int32_t const y,
107 int const w,
108 int const h,
109 bool restrict_rows = false);
110
111 bool handle_mousepress(uint8_t btn, int32_t x, int32_t y) override;
112 void handle_mousein(bool) override;
113 bool handle_mousemove(uint8_t, int32_t, int32_t, int32_t, int32_t) override;
114
115 const Widelands::Soldier* soldier_at(int32_t x, int32_t y) const;
116 void add(const Widelands::Soldier*);
117 void remove(const Widelands::Soldier*);
118 bool contains(const Widelands::Soldier* soldier) const {
119 for (const auto& s : soldiers_) {
120 if (s == soldier) {
121 return true;
122 }
123 }
124 return false;
125 }
126
127 std::vector<const Widelands::Soldier*> get_soldiers() const {
128 return soldiers_;
129 }
130 const Widelands::Soldier* get_soldier() const {
131 return soldiers_.back();
132 }
133
134 size_t count_soldiers() const {
135 return soldiers_.size();
136 }
137 Widelands::Extent size() const;
138 bool row_number_restricted() const {
139 return restricted_row_number_;
140 }
141 void set_row_number_restricted(bool r) {
142 restricted_row_number_ = r;
143 }
144
145 void draw(RenderTarget& dst) override;
146
147 void set_complement(ListOfSoldiers* o) {
148 other_ = o;
149 }
150
151 private:
152 bool restricted_row_number_;
153 uint16_t current_size_; // Current number of rows or columns
154 std::vector<const Widelands::Soldier*> soldiers_;
155
156 ListOfSoldiers* other_;
157 AttackBox* attack_box_;
158
159 void update_desired_size() override;
160 };
161
162 std::unique_ptr<ListOfSoldiers> attacking_soldiers_;
163 std::unique_ptr<ListOfSoldiers> remaining_soldiers_;
164 std::unique_ptr<UI::Textarea> current_soldier_stats_;
165 std::unique_ptr<UI::Button> attack_button_;
166
91 /// The last time the information in this Panel got updated167 /// The last time the information in this Panel got updated
92 uint32_t lastupdate_;168 uint32_t lastupdate_;
93};169};
94170
=== modified file 'src/wui/fieldaction.cc'
--- src/wui/fieldaction.cc 2019-02-23 11:00:49 +0000
+++ src/wui/fieldaction.cc 2019-05-11 10:49:50 +0000
@@ -232,7 +232,6 @@
232static const char* const pic_geologist = "images/wui/fieldaction/menu_geologist.png";232static const char* const pic_geologist = "images/wui/fieldaction/menu_geologist.png";
233233
234static const char* const pic_tab_attack = "images/wui/fieldaction/menu_tab_attack.png";234static const char* const pic_tab_attack = "images/wui/fieldaction/menu_tab_attack.png";
235static const char* const pic_attack = "images/wui/buildings/menu_attack.png";
236235
237/*236/*
238===============237===============
@@ -382,8 +381,9 @@
382 attack_box_ = new AttackBox(&a_box, player_, &node_, 0, 0);381 attack_box_ = new AttackBox(&a_box, player_, &node_, 0, 0);
383 a_box.add(attack_box_);382 a_box.add(attack_box_);
384383
385 set_fastclick_panel(&add_button(384 UI::Button* attack_button = attack_box_->get_attack_button();
386 &a_box, "attack", pic_attack, &FieldActionWindow::act_attack, _("Start attack")));385 attack_button->sigclicked.connect(boost::bind(&FieldActionWindow::act_attack, this));
386 set_fastclick_panel(attack_button);
387 }387 }
388 }388 }
389 }389 }
@@ -720,10 +720,9 @@
720 assert(attack_box_);720 assert(attack_box_);
721 upcast(Game, game, &ibase().egbase());721 upcast(Game, game, &ibase().egbase());
722 if (upcast(Building, building, game->map().get_immovable(node_)))722 if (upcast(Building, building, game->map().get_immovable(node_)))
723 if (attack_box_->soldiers() > 0) {723 if (attack_box_->count_soldiers() > 0) {
724 upcast(InteractivePlayer const, iaplayer, &ibase());724 upcast(InteractivePlayer const, iaplayer, &ibase());
725 game->send_player_enemyflagaction(building->base_flag(), iaplayer->player_number(),725 game->send_player_enemyflagaction(building->base_flag(), iaplayer->player_number(), attack_box_->soldiers());
726 attack_box_->soldiers() /* number of soldiers */);
727 }726 }
728 reset_mouse_and_die();727 reset_mouse_and_die();
729}728}

Subscribers

People subscribed via source and target branches

to status/vote changes: