widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #04063
[Merge] lp:~widelands-dev/widelands/trainingsites_and_teams into lp:widelands
TiborB has proposed merging lp:~widelands-dev/widelands/trainingsites_and_teams into lp:widelands.
Requested reviews:
Widelands Developers (widelands-dev)
For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/trainingsites_and_teams/+merge/260517
Another bunch of changes to AI
- better management of trainingsites
- better expansion policy (building farms and other internal paramenters)
- better calculation of military strength of soldiers
- better management of rangers
- multiple smaller changes
--
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/trainingsites_and_teams into lp:widelands.
=== modified file 'src/ai/ai_help_structs.h'
--- src/ai/ai_help_structs.h 2015-05-05 20:00:21 +0000
+++ src/ai/ai_help_structs.h 2015-05-28 19:39:10 +0000
@@ -382,6 +382,15 @@
std::vector<int16_t> inputs_;
std::vector<int16_t> outputs_;
std::vector<Widelands::WareIndex> critical_built_mat_;
+
+ bool upgrade_substitutes_;
+
+ //it seems that fish and meat are subsitutes (for trainingsites), so
+ // when testing ifa trainingsite is supplied enough
+ // we count the wares together
+ std::unordered_set<Widelands::WareIndex> substitute_inputs_;
+ int32_t substitutes_count_;
+
int16_t production_hint_;
int32_t cnt_built_;
@@ -457,8 +466,8 @@
//is stored in the observer
struct EnemySiteObserver {
bool warehouse_;
- uint8_t attack_soldiers;
- uint8_t defenders;
+ int32_t attack_soldiers_strength;
+ int32_t defenders_strength;
uint8_t stationed_soldiers;
uint32_t last_time_attackable;
uint32_t last_tested;
@@ -468,8 +477,8 @@
EnemySiteObserver()
: warehouse_(false),
- attack_soldiers(0),
- defenders(0),
+ attack_soldiers_strength(0),
+ defenders_strength(0),
stationed_soldiers(0),
last_time_attackable(std::numeric_limits<uint32_t>::max()),
last_tested(0),
=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc 2015-05-07 20:46:32 +0000
+++ src/ai/defaultai.cc 2015-05-28 19:39:10 +0000
@@ -44,6 +44,7 @@
#include "logic/playercommand.h"
#include "logic/productionsite.h"
#include "logic/ship.h"
+#include "logic/soldier.h"
#include "logic/trainingsite.h"
#include "logic/tribe.h"
#include "logic/warehouse.h"
@@ -62,7 +63,7 @@
constexpr int kMinMFCheckInterval = 19 * 1000;
constexpr int kShipCheckInterval = 5 * 1000;
constexpr int kMarineDecisionInterval = 20 * 1000;
-constexpr int kTrainingSitesCheckInterval = 5 * 60 * 1000;
+constexpr int kTrainingSitesCheckInterval = 45 * 1000;
// this is intended for map developers, by default should be off
constexpr bool kPrintStats = false;
@@ -90,6 +91,7 @@
num_ports(0),
last_attacked_player_(std::numeric_limits<uint16_t>::max()),
enemysites_check_delay_(60),
+ wood_policy_(WoodPolicy::kStartRangers),
next_ai_think_(0),
next_mine_construction_due_(0),
inhibit_road_building_(0),
@@ -112,7 +114,8 @@
ts_basic_const_count_(0),
ts_advanced_count_(0),
ts_advanced_const_count_(0),
- ts_without_trainers_(0) {
+ ts_without_trainers_(0),
+ scheduler_delay_counter_(0) {
// Subscribe to NoteFieldPossession.
field_possession_subscriber_ =
@@ -152,6 +155,18 @@
});
+ // Subscribe to TrainingSiteSoldierTrained.
+ soldiertrained_subscriber_ = Notifications::subscribe<NoteTrainingSiteSoldierTrained>(
+ [this](const NoteTrainingSiteSoldierTrained& note) {
+ if (note.ts->owner().player_number() != player_->player_number()) {
+ return;
+ }
+
+ soldier_trained(*note.ts);
+
+ });
+
+
// Subscribe to ShipNotes.
shipnotes_subscriber_ =
Notifications::subscribe<NoteShipMessage>([this](const NoteShipMessage& note) {
@@ -253,8 +268,16 @@
if (check_economies()) { // is a must
return;
};
- taskDue[ScheduleTasks::kRoadCheck] = gametime + 400;
- improve_roads(gametime);
+ taskDue[ScheduleTasks::kRoadCheck] = gametime + 1000;
+ //testing 5 roads
+ {int32_t roads_to_check = (roads.size() + 1 < 5) ? roads.size() + 1:5;
+ for (int i = 0; i < roads_to_check; i += 1){
+ if (improve_roads(gametime)){
+ //if significant change takes place do not go on
+ break;
+ };
+ }
+ }
break;
case ScheduleTasks::kUnbuildableFCheck :
taskDue[ScheduleTasks::kUnbuildableFCheck] = gametime + 4000;
@@ -280,8 +303,15 @@
if (check_economies()) { // economies must be consistent
return;
}
- check_productionsites(gametime);
- taskDue[ScheduleTasks::kCheckProductionsites] = gametime + 5000;
+ {int32_t ps_to_check = (productionsites.size()<5)?productionsites.size():5;
+ for (int i = 0; i < ps_to_check; i += 1){
+ if (check_productionsites(gametime)){
+ //if significant change takes place do not go on
+ break;
+ };
+ }
+ }
+ taskDue[ScheduleTasks::kCheckProductionsites] = gametime + 15000;
break;
case ScheduleTasks::kCheckShips :
check_ships(gametime);
@@ -293,8 +323,16 @@
if (check_economies()) { // economies must be consistent
return;
}
- taskDue[ScheduleTasks::kCheckMines] = gametime + 7000; // 7 seconds is enough
- check_mines_(gametime);
+ taskDue[ScheduleTasks::kCheckMines] = gametime + 15000;
+ //checking 3 mines if possible
+ {int32_t mines_to_check = (mines_.size()<5)?mines_.size():5;
+ for (int i = 0; i < mines_to_check; i += 1){
+ if (check_mines_(gametime)){
+ //if significant change takes place do not go on
+ break;
+ };
+ }
+ }
break;
case ScheduleTasks::kCheckMilitarysites :
check_militarysites(gametime);
@@ -304,7 +342,7 @@
break;
case ScheduleTasks::kCountMilitaryVacant :
count_military_vacant_positions();
- taskDue[ScheduleTasks::kCountMilitaryVacant] = gametime + 90 * 1000;
+ taskDue[ScheduleTasks::kCountMilitaryVacant] = gametime + 60 * 1000;
break;
case ScheduleTasks::kWareReview :
if (check_economies()) { // economies must be consistent
@@ -391,6 +429,7 @@
bo.forced_after_ = bh.get_forced_after() * 1000; // value in conf is in seconds
bo.is_port_ = bld.get_isport();
bo.trainingsite_type_ = TrainingSiteType::kNoTS;
+ bo.upgrade_substitutes_ = false;
if (bh.renews_map_resource()) {
bo.production_hint_ = tribe_->safe_ware_index(bh.get_renews_map_resource());
@@ -448,6 +487,38 @@
bo.is_shipyard_ = false;
}
+ // now we find out if the upgrade of the building is a full substitution
+ // (produces all wares as current one)
+ const BuildingIndex enhancement = bld.enhancement();
+ if (enhancement != INVALID_INDEX && bo.type == BuildingObserver::PRODUCTIONSITE) {
+ std::unordered_set<WareIndex> enh_outputs;
+ const ProductionSiteDescr& enh_prod
+ =
+ dynamic_cast<const ProductionSiteDescr&>(*tribe_->get_building_descr(enhancement));
+
+ // collecting wares that are produced in enhanced building
+ for (const WareIndex& ware : enh_prod.output_ware_types()) {
+ enh_outputs.insert(ware);
+ }
+ //now testing outputs of current building
+ //and comparing
+ bo.upgrade_substitutes_ = true;
+ for (WareIndex ware : bo.outputs_) {
+ if (enh_outputs.count(ware) == 0){
+ bo.upgrade_substitutes_ = false;
+ break;
+ }
+ }
+ }
+
+ // plus some manually picked buildings,
+ // see preffered_upgrade list
+ for (const char* pb : preffered_upgrade) {
+ if (strcmp(bld.name().c_str(), pb) == 0) {
+ bo.upgrade_substitutes_ = true;
+ }
+ }
+
continue;
}
@@ -482,7 +553,16 @@
const TrainingSiteDescr& train = dynamic_cast<const TrainingSiteDescr&>(bld);
for (const WareAmount& temp_input : train.inputs()) {
bo.inputs_.push_back(temp_input.first);
+
+ //collecting subsitutes
+ if (tribe_->ware_index("meat") == temp_input.first ||
+ tribe_->ware_index("fish") == temp_input.first ||
+ tribe_->ware_index("smoked_meat") == temp_input.first ||
+ tribe_->ware_index("smoked_fish") == temp_input.first) {
+ bo.substitute_inputs_.insert(temp_input.first);
+ }
}
+
bo.trainingsite_type_ = bh.get_trainingsite_type();
// it would behave badly if no type was set
// make sure all TS have its type set properly in conf files
@@ -496,6 +576,8 @@
}
}
+
+
// atlanteans they consider water as a resource
// (together with mines, stones and wood)
if (tribe_->name() == "atlanteans") {
@@ -592,7 +674,7 @@
taskDue[ScheduleTasks::kCheckShips] = 30 * 1000;
taskDue[ScheduleTasks::kCheckEconomies] = 1000;
taskDue[ScheduleTasks::KMarineDecisions] = 30 * 1000;
- taskDue[ScheduleTasks::kCheckTrainingsites] = 15 * 60 * 1000;
+ taskDue[ScheduleTasks::kCheckTrainingsites] = 2 * 60 * 1000;
taskDue[ScheduleTasks::kBbuildableFieldsCheck] = 1000;
taskDue[ScheduleTasks::kMineableFieldsCheck] = 1000;
taskDue[ScheduleTasks::kUnbuildableFCheck] = 1000;
@@ -870,7 +952,6 @@
if (player_->is_hostile(player_immovable->owner())) {
field.enemy_nearby_ = true;
}
- enemy_last_seen_ = gametime;
continue;
}
@@ -1172,6 +1253,21 @@
new_buildings_stop_ = false;
MilitaryStrategy expansion_mode = MilitaryStrategy::kResourcesOrDefense;
+ // helper variable - we need some proportion of free spots vs productionsites
+ // the proportion depends on size of economy
+ // this proportion defines how dense the buildings will be
+ // it is degressive (allows high density on the beginning)
+ int32_t needed_spots = 0;
+ if (productionsites.size() < 50) {
+ needed_spots = productionsites.size();
+ } else if (productionsites.size() < 100) {
+ needed_spots = 50 + (productionsites.size() - 50) * 5;
+ } else if (productionsites.size() < 200) {
+ needed_spots = 300 + (productionsites.size() - 100) * 10;
+ } else {
+ needed_spots = 1300 + (productionsites.size() - 200) * 20;
+ }
+
// there are many reasons why to stop building production buildings
// (note there are numerous exceptions)
// 1. to not have too many constructionsites
@@ -1179,12 +1275,12 @@
new_buildings_stop_ = true;
}
// 2. to not exhaust all free spots
- if (spots_ * 3 / 2 + 5 < static_cast<int32_t>(productionsites.size())) {
+ if (spots_ < needed_spots) {
new_buildings_stop_ = true;
}
// 3. too keep some proportions production sites vs military sites
if ((num_prod_constructionsites + productionsites.size()) >
- (num_milit_constructionsites + militarysites.size()) * 3) {
+ (num_milit_constructionsites + militarysites.size()) * 5) {
new_buildings_stop_ = true;
}
// 4. if we do not have 3 mines at least
@@ -1192,26 +1288,24 @@
new_buildings_stop_ = true;
}
// BUT if enemy is nearby, we cancel above stop
- if (new_buildings_stop_ && enemy_last_seen_ + 2 * 60 * 1000 > gametime) {
+ if (new_buildings_stop_ && enemy_last_seen_ + 10 * 60 * 1000 > gametime) {
new_buildings_stop_ = false;
}
- // sometimes there is too many military buildings in construction, so we must
- // prevent initialization of further buildings start
- const int32_t vacant_plus_in_construction_minus_prod =
- vacant_mil_positions_ + 2 * num_milit_constructionsites - productionsites.size() / 7;
- if (vacant_plus_in_construction_minus_prod > 20) {
- expansion_mode = MilitaryStrategy::kNoNewMilitary;
- } else if (vacant_plus_in_construction_minus_prod > 13) {
- expansion_mode = MilitaryStrategy::kDefenseOnly;
- } else if (vacant_plus_in_construction_minus_prod > 6) {
- expansion_mode = MilitaryStrategy::kResourcesOrDefense;
+ //we must calculate wood policy
+ const WareIndex wood_index = tribe_->safe_ware_index("log");
+ // the name of variable is not 100% proper
+ const int32_t stocked_wood = get_warehoused_stock(wood_index) -
+ productionsites.size() * 2 -
+ num_prod_constructionsites;
+ if (stocked_wood > 80) {
+ wood_policy_ = WoodPolicy::kDismantleRangers;
+ } else if (stocked_wood > 25) {
+ wood_policy_ = WoodPolicy::kStopRangers;
+ } else if (stocked_wood > 10) {
+ wood_policy_ = WoodPolicy::kStartRangers;
} else {
- if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
- expansion_mode = MilitaryStrategy::kExpansion;
- } else {
- expansion_mode = MilitaryStrategy::kPushExpansion;
- }
+ wood_policy_ = WoodPolicy::kBuildRangers;
}
// we must consider need for mines
@@ -1243,6 +1337,27 @@
}
}
+ // this controls a speed and willingness to expand the teritorry
+ const int32_t vacant_plus_in_construction_minus_prod =
+ vacant_mil_positions_ + 2 * num_milit_constructionsites - productionsites.size() / 7;
+ if (vacant_plus_in_construction_minus_prod > 20) {
+ expansion_mode = MilitaryStrategy::kNoNewMilitary;
+ } else if (vacant_plus_in_construction_minus_prod > 13) {
+ expansion_mode = MilitaryStrategy::kDefenseOnly;
+ } else if (vacant_plus_in_construction_minus_prod > 6) {
+ expansion_mode = MilitaryStrategy::kResourcesOrDefense;
+ } else {
+ //this is intended for initial phase of game when the player has enough soldiers yet
+ //but we still want to force it to follow resources instead for plain expansion
+ if (virtual_mines <= 2 && (unstationed_milit_buildings_ + num_milit_constructionsites) > 2) {
+ expansion_mode = MilitaryStrategy::kResourcesOrDefense;
+ } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
+ expansion_mode = MilitaryStrategy::kExpansion;
+ } else {
+ expansion_mode = MilitaryStrategy::kPushExpansion;
+ }
+ }
+
BuildingObserver* best_building = nullptr;
int32_t proposed_priority = 0;
Coords proposed_coords;
@@ -1559,12 +1674,6 @@
continue;
}
- if (bo.stocklevel_time < game().get_gametime() - 5 * 1000) {
- bo.stocklevel_ =
- get_stocklevel_by_hint(static_cast<size_t>(bo.production_hint_));
- bo.stocklevel_time = game().get_gametime();
- }
-
if (bo.total_count() == 0) {
prio = 200;
}
@@ -1572,9 +1681,10 @@
continue;
}
// we can go above target if there is shortage of logs on stock
- else if (bo.total_count() >= bo.cnt_target_ &&
- bo.stocklevel_ > 40 + productionsites.size() * 2) {
+ else if (bo.total_count() >= bo.cnt_target_) {
+ if (wood_policy_ != WoodPolicy::kBuildRangers) {
continue;
+ }
}
// considering near trees and producers
@@ -1632,8 +1742,19 @@
continue;
}
- if ((bo.cnt_under_construction_ + bo.unoccupied_) > 0) {
- continue;
+ // generally we allow 1 building in construction, but if
+ // preciousness of missing ware is >=10 and it is farm-like building
+ // we allow 2 in construction
+ if (max_needed_preciousness >= 10
+ && bo.inputs_.empty()
+ && gametime > 30 * 60 * 1000) {
+ if ((bo.cnt_under_construction_ + bo.unoccupied_) > 1) {
+ continue;
+ }
+ } else {
+ if ((bo.cnt_under_construction_ + bo.unoccupied_) > 0) {
+ continue;
+ }
}
if (bo.forced_after_ < gametime && (bo.total_count() - bo.unconnected_) == 0) {
@@ -1653,9 +1774,9 @@
prio += kDefaultPrioBoost;
} else if ((bo.cnt_built_ - bo.unconnected_) > 1 && bo.current_stats_ > 97) {
prio -= kDefaultPrioBoost * (new_buildings_stop_);
- } else if (new_buildings_stop_)
+ } else if (new_buildings_stop_){
continue;
-
+ }
// we check separatelly buildings with no inputs and some inputs
if (bo.inputs_.empty()) {
@@ -1683,7 +1804,6 @@
if (!bo.space_consumer_) {
prio -= bf->producers_nearby_.at(bo.outputs_.at(0)) * 20;
} // leave some free space between them
-
}
else if (bo.is_shipyard_) {
@@ -1699,12 +1819,9 @@
}
if ((bo.cnt_built_ - bo.unconnected_) > 0
&&
- //due to very badly designed statistics and the way how
- //productionsites are working we must distinguish how many
- //outputs the site has.
- ((bo.outputs_.size() == 1 && bo.current_stats_ > 75)
- ||
- (bo.outputs_.size() > 1 && bo.current_stats_ > 55))) {
+ is_productionsite_needed(bo.outputs_.size(),
+ bo.current_stats_,
+ PerfEvaluation::kForConstruction)) {
prio += max_needed_preciousness + kDefaultPrioBoost - 3 +
(bo.current_stats_ - 55) / 8;
}
@@ -1714,6 +1831,15 @@
continue;
}
+ //bonus for big buildings if shortage of big fields
+ if (spots_avail.at(BUILDCAPS_BIG) <= 5 && bo.desc->get_size() == 3) {
+ prio += 10;
+ }
+
+ if (spots_avail.at(BUILDCAPS_MEDIUM) <= 5 && bo.desc->get_size() == 2) {
+ prio += 5;
+ }
+
//+1 if any consumers_ are nearby
consumers_nearby_count = 0;
@@ -1723,6 +1849,7 @@
if (consumers_nearby_count > 0) {
prio += 1;
}
+
}
} // production sites done
else if (bo.type == BuildingObserver::MILITARYSITE) {
@@ -2029,14 +2156,14 @@
continue;
}
- // we build one training site for 100 militarysites
+ // we build one basic training site for 50 militarysites
if (bo.trainingsite_type_ == TrainingSiteType::kBasic &&
- militarysites.size() / 100 < static_cast<int32_t>(ts_basic_count_)) {
+ militarysites.size() / 50 < static_cast<int32_t>(ts_basic_count_)) {
continue;
}
- // we build one training site for 100 militarysites
+ // we build one advanced training site for 75 militarysites
if (bo.trainingsite_type_ == TrainingSiteType::kAdvanced &&
- militarysites.size() / 100 < static_cast<int32_t>(ts_advanced_count_)) {
+ militarysites.size() / 75 < static_cast<int32_t>(ts_advanced_count_)) {
continue;
}
@@ -2080,9 +2207,9 @@
}
// Prefer road side fields
- prio += bf->preferred_ ? 1 : 0;
+ prio += bf->preferred_ ? 5 : 0;
// don't waste good land for small huts
- prio -= (maxsize - bo.desc->get_size()) * 5;
+ prio -= (maxsize - bo.desc->get_size()) * 20;
// prefer vicinity of ports (with exemption of warehouses)
if (bf->port_nearby_ && bo.type == BuildingObserver::MILITARYSITE) {
@@ -2148,7 +2275,7 @@
0) {
nearness_penalty = 0;
} else {
- nearness_penalty = 30;
+ nearness_penalty = 40;
}
// bonus score to prefer if too few mines
@@ -2338,12 +2465,13 @@
}
if (inhibit_road_building_ >= gametime) {
- return false;
+ return true;
}
// now we rotate economies and flags to get one flag to go on with
if (economies.empty()) {
- return check_economies();
+ check_economies();
+ return false;
}
if (economies.size() >= 2) { // rotating economies
@@ -2353,7 +2481,8 @@
EconomyObserver* eco = economies.front();
if (eco->flags.empty()) {
- return check_economies();
+ check_economies();
+ return false;
}
if (eco->flags.size() > 1) {
eco->flags.push_back(eco->flags.front());
@@ -2389,9 +2518,11 @@
} else if (flag.current_wares() > 5) {
create_shortcut_road(flag, 9, -2, gametime);
inhibit_road_building_ = gametime + 400;
+ } else {
+ return false;
}
- return false;
+ return true;
}
// the function takes a road (road is smallest section of roads with two flags on the ends)
@@ -2469,6 +2600,27 @@
return false;
}
+//is productionsite needed
+//used for building new buildings or dismantle of old, intended for ones
+//that have inputs
+bool DefaultAI::is_productionsite_needed(int32_t outputs,
+ int32_t performance,
+ PerfEvaluation purpose) {
+ int32_t expected_performance = 0;
+ if (outputs > 0) {
+ expected_performance = 10 + 70 / outputs;
+ } else {
+ expected_performance = 80;
+ }
+ if (purpose == PerfEvaluation::kForDismantle) {
+ expected_performance /= 2;
+ }
+ if (performance > expected_performance) {
+ return true;
+ }
+ return false;
+}
+
// trying to connect the flag to another one, be it from own economy
// or other economy
bool DefaultAI::create_shortcut_road(const Flag& flag,
@@ -2811,7 +2963,6 @@
return false;
}
- bool changed = false;
// Reorder and set new values; - better now because there are multiple returns in the function
productionsites.push_back(productionsites.front());
productionsites.pop_front();
@@ -2850,14 +3001,18 @@
// available, one is to be enhanced
// b) if there are two buildings
// statistics percents are decisive
+ // c) yet there are buildings that might be upgraded, even when
+ // there is no second buiding of the kind (flag upgrade_substitutes_)
+
const BuildingIndex enhancement = site.site->descr().enhancement();
if (connected_to_wh && enhancement != INVALID_INDEX &&
- (site.bo->cnt_built_ - site.bo->unoccupied_) > 1) {
+ ((site.bo->cnt_built_ - site.bo->unoccupied_) > 1 ||
+ site.bo->upgrade_substitutes_)) {
BuildingIndex enbld = INVALID_INDEX; // to get rid of this
// Only enhance buildings that are allowed (scenario mode)
- // do not do decisions to fast
+ // do not do decisions too fast
if (player_->is_building_type_allowed(enhancement)) {
const BuildingDescr& bld = *tribe_->get_building_descr(enhancement);
@@ -2871,7 +3026,7 @@
if (site.site->has_workers(enhancement, game())) {
// forcing first upgrade
- if (en_bo.cnt_built_ == 0 && !mines_.empty()) {
+ if (en_bo.cnt_built_ == 0) {
enbld = enhancement;
bestbld = &en_bo;
}
@@ -3079,9 +3234,12 @@
}
// buildings with inputs_, checking if we can a dismantle some due to low performance
- if (!site.bo->inputs_.empty() && (site.bo->cnt_built_ - site.bo->unoccupied_) >= 3 &&
+ if (!site.bo->inputs_.empty() &&
+ (site.bo->cnt_built_ - site.bo->unoccupied_) >= 3 &&
site.site->can_start_working() &&
- site.site->get_statistics_percent() < 20 && // statistics for the building
+ !is_productionsite_needed(site.bo->outputs_.size(),
+ site.site->get_statistics_percent(),
+ PerfEvaluation::kForDismantle) &&
site.bo->current_stats_ < 30 && // overall statistics
(game().get_gametime() - site.unoccupied_till_) > 10 * 60 * 1000) {
@@ -3117,16 +3275,8 @@
// stop/start them based on stock avaiable
if (site.bo->production_hint_ >= 0) {
- if (site.bo->stocklevel_time < game().get_gametime() - 5 * 1000) {
- site.bo->stocklevel_ = get_stocklevel_by_hint(site.bo->production_hint_);
- site.bo->stocklevel_time = game().get_gametime();
- }
-
- // logs can be stored also in productionsites, they are counted as on stock
- // but are no available for random production site
- int16_t score = site.bo->stocklevel_ - productionsites.size() * 2;
-
- if (score > 200 && site.bo->cnt_built_ > site.bo->cnt_target_) {
+ //dismantling the rangers hut, but only if we have them above a target
+ if (wood_policy_ == WoodPolicy::kDismantleRangers && site.bo->cnt_built_ > site.bo->cnt_target_) {
site.bo->last_dismantle_time_ = game().get_gametime();
flags_to_be_removed.push_back(site.site->base_flag().get_position());
@@ -3138,27 +3288,38 @@
return true;
}
- if (score > 120 && !site.site->is_stopped()) {
+ // stopping a ranger (sometimes the policy can be kDismantleRangers,
+ // but we still preserve some rangers for sure)
+ if ((wood_policy_ == WoodPolicy::kStopRangers
+ ||
+ wood_policy_ == WoodPolicy::kDismantleRangers)
+ &&
+ !site.site->is_stopped()) {
game().send_player_start_stop_building(*site.site);
- return true;
+ return false;
}
+
const uint32_t trees_in_vicinity =
map.find_immovables(Area<FCoords>(map.get_fcoords(site.site->get_position()), 5),
nullptr,
FindImmovableAttribute(MapObjectDescr::get_attribute_id("tree")));
+ //stop ranger if enough trees around regardless of policy
if (trees_in_vicinity > 25) {
if (!site.site->is_stopped()) {
game().send_player_start_stop_building(*site.site);
}
- } else if (score < 80 && site.site->is_stopped()) {
-
- game().send_player_start_stop_building(*site.site);
+ //if not enough trees nearby, we can start them if required
+ } else if ((wood_policy_ == WoodPolicy::kStartRangers ||
+ wood_policy_ == WoodPolicy::kBuildRangers)
+ &&
+ site.site->is_stopped()) {
+ game().send_player_start_stop_building(*site.site);
}
}
- return changed;
+ return false;
}
// This function scans current situation with shipyards, ports, ships, ongoing expeditions
@@ -3562,15 +3723,15 @@
// counting vacant positions
vacant_mil_positions_ = 0;
for (TrainingSiteObserver tso : trainingsites) {
- vacant_mil_positions_ += tso.site->soldier_capacity() - tso.site->stationed_soldiers().size();
+ vacant_mil_positions_ += 10 * (tso.site->soldier_capacity() - tso.site->stationed_soldiers().size());
}
for (MilitarySiteObserver mso : militarysites) {
vacant_mil_positions_ += mso.site->soldier_capacity() - mso.site->stationed_soldiers().size();
}
}
-// this function only manipulates with trainingsites' inputs priority
-// decreases it when too many unoccupied military buildings
+// this function only check with trainingsites
+// manipulates input queues and soldier capacity
bool DefaultAI::check_trainingsites(uint32_t gametime) {
if (taskDue[ScheduleTasks::kCheckTrainingsites] > gametime) {
return false;
@@ -3582,6 +3743,9 @@
return false;
}
+ trainingsites.push_back(trainingsites.front());
+ trainingsites.pop_front();
+
TrainingSite* ts = trainingsites.front().site;
TrainingSiteObserver& tso = trainingsites.front();
@@ -3595,12 +3759,78 @@
}
}
- trainingsites.push_back(trainingsites.front());
- trainingsites.pop_front();
-
- // changing capacity
- if (tso.site->soldier_capacity() != 2) {
- game().send_player_change_soldier_capacity(*ts, 2 - tso.site->soldier_capacity());
+ // changing capacity to 0 - this will happen only once.....
+ if (tso.site->soldier_capacity() > 1) {
+ game().send_player_change_soldier_capacity(*ts, - tso.site->soldier_capacity());
+ return true;
+ }
+
+ //reducing ware quees
+ // - for armours and weapons to 1
+ // - for others to 6
+ std::vector<WaresQueue*> const warequeues1 = tso.site->warequeues();
+ size_t nr_warequeues = warequeues1.size();
+ for (size_t i = 0; i < nr_warequeues; ++i) {
+
+ //if it was decreased yet
+ if (warequeues1[i]->get_max_fill() <= 1) {
+ continue;}
+
+ //now modifying max_fill of armors and weapons
+ for (std::string pattern : armors_and_weapons) {
+
+ if (tribe_->get_ware_descr(warequeues1[i]->get_ware())->name().find(pattern) != std::string::npos) {
+ if (warequeues1[i]->get_max_fill() > 1) {
+ game().send_player_set_ware_max_fill(*ts, warequeues1[i]->get_ware(), 1);
+ continue;
+ }
+ }
+ }
+ }
+
+ //changing priority if basic
+ if (tso.bo->trainingsite_type_ == TrainingSiteType::kBasic) {
+ for (uint32_t k = 0; k < tso.bo->inputs_.size(); ++k) {
+ game().send_player_set_ware_priority(
+ *ts, wwWARE, tso.bo->inputs_.at(k), HIGH_PRIORITY);
+ }
+ }
+
+ //if soldier capacity is set to 0, we need to find out if the site is
+ //suplied enough to incrase the capacity to 1
+ if (tso.site->soldier_capacity() == 0){
+
+ //First subsitute wares
+ //int32_t capacity = 0;
+ int32_t filled = 0;
+ bool supplied_enough = true;
+ std::vector<WaresQueue*> const warequeues2 = tso.site->warequeues();
+ nr_warequeues = warequeues2.size();
+ for (size_t i = 0; i < nr_warequeues; ++i) {
+ if (tso.bo->substitute_inputs_.count(warequeues2[i]->get_ware()) > 0){
+ filled += warequeues2[i]->get_filled();
+ //capacity += warequeues2[i]->get_max_fill();
+ }
+ }
+ if (filled < 5) {
+ supplied_enough = false;
+ }
+
+ //checking non subsitutes
+ for (size_t i = 0; i < nr_warequeues; ++i) {
+ if (tso.bo->substitute_inputs_.count(warequeues2[i]->get_ware()) == 0){
+ const uint32_t required_amount
+ =
+ (warequeues2[i]->get_max_fill()<5) ? warequeues2[i]->get_max_fill() : 5;
+ if (warequeues2[i]->get_filled() < required_amount) {
+ supplied_enough = false;
+ }
+ }
+ }
+
+ if (supplied_enough) {
+ game().send_player_change_soldier_capacity(*ts, 1);
+ }
}
ts_without_trainers_ = 0; // zeroing
@@ -3713,12 +3943,12 @@
int32_t unused1 = 0;
uint16_t unused2 = 0;
- mso.enemies_nearby_ = true;
+ mso.enemies_nearby_ = false;
// yes enemy is nearby, but still we must distinguish whether
// he is accessible (over the land)
if (other_player_accessible(
- vision + 4, &unused1, &unused2, ms->get_position(), WalkSearch::kOtherPlayers)) {
+ vision + 4, &unused1, &unused2, ms->get_position(), WalkSearch::kEnemy)) {
uint32_t const total_capacity = ms->max_soldier_capacity();
uint32_t const target_capacity = ms->soldier_capacity();
@@ -3732,6 +3962,9 @@
*ms, MilitarySite::kPrefersHeroes);
changed = true;
}
+
+ mso.enemies_nearby_ = true;
+ enemy_last_seen_ = gametime;
} else { // otherwise decrease soldiers
uint32_t const j = ms->soldier_capacity();
@@ -3952,6 +4185,23 @@
}
}
+//this is called when soldier left the trainingsite
+//the purpose is to set soldier capacity to 0
+// (AI will then wait till training site is stocked)
+void DefaultAI::soldier_trained(const TrainingSite& site) {
+
+ // we must identify particular training site
+ for (std::list<TrainingSiteObserver>::iterator i = trainingsites.begin(); i != trainingsites.end(); ++i)
+ if (i->site == &site) {
+ if (i->site->soldier_capacity() > 0) {
+ game().send_player_change_soldier_capacity(*i->site, - i->site->soldier_capacity());
+ }
+ return;
+ }
+ log (" %d: Computer player error - trainingsite not found\n",
+ player_number());
+}
+
// walk and search for teritorry controlled by other player
// usually scanning radius is enough but sometimes we must walk to
// verify that an enemy teritory is really accessible by land
@@ -3993,10 +4243,35 @@
// a port location), but when testing (starting from) own military building
// we must ignore own teritory, of course
if (f->get_owned_by() > 0) {
- if (type == WalkSearch::kAnyPlayer ||
- (type == WalkSearch::kOtherPlayers && f->get_owned_by() != pn)) {
- *tested_fields = done.size();
- return true;
+
+ //if field is owned by anybody
+ if (type == WalkSearch::kAnyPlayer){
+ *tested_fields = done.size();
+ return true;
+ }
+
+ //if anybody but not me
+ if (type == WalkSearch::kOtherPlayers && f->get_owned_by() != pn){
+ *tested_fields = done.size();
+ return true;
+ }
+
+ //if owned by enemy
+ if (type == WalkSearch::kEnemy && f->get_owned_by() != pn){
+ //for case I am not member of a team
+ if (player_->team_number() == 0) {
+ *tested_fields = done.size();
+ return true;
+ }
+ //if I am in team, testing if the same team
+ if (player_->team_number() > 0
+ &&
+ player_->team_number()
+ !=
+ game().get_player(f->get_owned_by())->team_number()) {
+ *tested_fields = done.size();
+ return true;
+ }
}
}
@@ -4421,6 +4696,65 @@
return supplied == bo.inputs_.size();
}
+//This calculates strength of vector of soldiers, f.e. soldiers in a building or
+//ones ready to attack
+int32_t DefaultAI::calculate_strength(const std::vector<Widelands::Soldier*> soldiers){
+
+ if (soldiers.empty()) {
+ return 0;
+ }
+
+ enum {BARBARIANS, ATLANTEANS, EMPIRE};
+ uint8_t tribe = std::numeric_limits<uint8_t>::max();
+
+ if (soldiers.at(0)->get_owner()->tribe().name() == "atlanteans"){
+ tribe = ATLANTEANS;
+ } else if (soldiers.at(0)->get_owner()->tribe().name() == "barbarians"){
+ tribe = BARBARIANS;
+ } else if (soldiers.at(0)->get_owner()->tribe().name() == "empire"){
+ tribe = EMPIRE;
+ } else {
+ throw wexception("AI warning: Unable to calculate strenght for player of tribe %s",
+ soldiers.at(0)->get_owner()->tribe().name().c_str());
+ }
+
+ float hp = 0;
+ float al = 0;
+ float dl = 0;
+ float el = 0;
+ float final = 0;
+
+ for (Soldier * soldier : soldiers) {
+ switch (tribe) {
+ case (ATLANTEANS):
+ hp = 135 + 40 * soldier->get_hp_level();
+ al = 14 + 8 * soldier->get_attack_level();
+ dl = static_cast<float>(94 - 8 * soldier->get_defense_level()) / 100;
+ el = static_cast<float>(70 - 17 * soldier->get_evade_level()) / 100;
+ break;
+ case (BARBARIANS):
+ hp += 130 + 28 * soldier->get_hp_level();
+ al += 14 + 7 * soldier->get_attack_level();
+ dl += static_cast<float>(97 - 8 * soldier->get_defense_level()) / 100;
+ el += static_cast<float>(75 - 15 * soldier->get_evade_level()) / 100;
+ break;
+ case (EMPIRE):
+ hp += 130 + 21 * soldier->get_hp_level();
+ al += 14 + 8 * soldier->get_attack_level();
+ dl += static_cast<float>(95 - 8 * soldier->get_defense_level()) / 100;
+ el += static_cast<float>(70 - 16 * soldier->get_evade_level()) / 100;
+ break;
+ default:
+ assert (false);
+ }
+
+ final += (al * hp) / (dl * el);
+ }
+
+ //2500 is aproximate strength of one unpromoted soldier
+ return static_cast<int32_t>(final / 2500);
+}
+
bool DefaultAI::check_enemy_sites(uint32_t const gametime) {
Map& map = game().map();
@@ -4437,6 +4771,27 @@
// receiving games statistics and parsing it (reading latest entry)
const Game::GeneralStatsVector& genstats = game().get_general_statistics();
+ //summing team power, creating team_power std::map of team_number:strength
+ std::map<TeamNumber, uint32_t> team_power;
+ for (uint8_t j = 1; j <= plr_in_game; ++j) {
+ TeamNumber const tm = game().get_player(j)->team_number();
+ if (tm == 0) {
+ continue;
+ }
+ //for case this is new team
+ if (team_power.count(tm) == 0) {
+ //adding this team (number) to vector
+ team_power[tm] = 0;
+ }
+ try {
+ team_power[tm] += genstats.at(j - 1).miltary_strength.back();
+ } catch (const std::out_of_range&) {
+ log("ComputerPlayer(%d): genstats entry missing - size :%d\n",
+ player_number(),
+ static_cast<unsigned int>(genstats.size()));
+ }
+ }
+
// defining treshold ratio of own_strenght/enemy's_strength
uint32_t treshold_ratio = 100;
if (type_ == AGGRESSIVE) {
@@ -4446,25 +4801,49 @@
treshold_ratio = 120;
}
- // now we test all players which one are 'attackable'
+ uint32_t my_power = 0;
+ try {
+ my_power = genstats.at(pn - 1).miltary_strength.back();
+ } catch (const std::out_of_range&) {
+ log("ComputerPlayer(%d): genstats entry missing - size :%d\n",
+ player_number(),
+ static_cast<unsigned int>(genstats.size()));
+ }
+ //adding power of team (minus my power) divided by 2
+ //(if I am a part of a team of course)
+ if (game().get_player(pn)->team_number() > 0) {
+ my_power += (team_power[game().get_player(pn)->team_number()] - my_power) / 2;
+ }
+
+ // now we test all players to identify 'attackable' ones
for (uint8_t j = 1; j <= plr_in_game; ++j) {
- if (pn == j) { // its me
+ //if it's me
+ if (pn == j) {
+ player_attackable[j - 1] = false;
+ continue;
+ }
+ //if we are the same team
+ if (game().get_player(pn)->team_number() > 0 &&
+ game().get_player(pn)->team_number() == game().get_player(j)->team_number()) {
player_attackable[j - 1] = false;
continue;
}
+ //now we compare strength
try {
- // It seems that under some circumstances genstats can be empty.
- // So, to avoid crash, the AI tests its content first.
- if (genstats.at(j - 1).miltary_strength.empty()) {
- log("ComputerPlayer(%d): miltary_strength is empty\n", player_number());
- player_attackable.at(j - 1) = false;
- // Avoid division by zero
- } else if (genstats.at(j - 1).miltary_strength.back() == 0) {
+ //strength of the other player
+ uint32_t players_power = 0;
+ if (!genstats.at(j - 1).miltary_strength.empty()) {
+ players_power += genstats.at(j - 1).miltary_strength.back();
+ }
+ //+power of team (if member of a team)
+ if (game().get_player(j)->team_number() > 0) {
+ players_power += (team_power[game().get_player(j)->team_number()] - players_power) / 2;
+ }
+
+ if (players_power == 0){
player_attackable.at(j - 1) = true;
- // Check threshold
- } else if ((genstats.at(pn - 1).miltary_strength.back() * 100 /
- genstats.at(j - 1).miltary_strength.back()) > treshold_ratio) {
+ } else if (my_power * 100 / players_power > treshold_ratio) {
player_attackable.at(j - 1) = true;
} else {
player_attackable.at(j - 1) = false;
@@ -4532,7 +4911,7 @@
count += 1;
site->second.last_tested = gametime;
- uint8_t defenders = 0;
+ uint8_t defenders_strength = 0;
bool is_warehouse = false;
bool is_attackable = false;
uint16_t onwer_number = 100;
@@ -4544,7 +4923,10 @@
Flag* flag = nullptr;
if (upcast(MilitarySite, bld, f.field->get_immovable())) {
if (player_->is_hostile(bld->owner())) {
- defenders = bld->present_soldiers().size();
+ std::vector<Soldier *> defenders;
+ defenders = bld->present_soldiers();
+ defenders_strength = calculate_strength(defenders);
+
flag = &bld->base_flag();
if (bld->can_attack()) {
is_attackable = true;
@@ -4554,7 +4936,11 @@
}
if (upcast(Warehouse, Wh, f.field->get_immovable())) {
if (player_->is_hostile(Wh->owner())) {
- defenders = Wh->present_soldiers().size();
+
+ std::vector<Soldier *> defenders;
+ defenders = Wh->present_soldiers();
+ defenders_strength = calculate_strength(defenders);
+
flag = &Wh->base_flag();
is_warehouse = true;
if (Wh->can_attack()) {
@@ -4588,31 +4974,42 @@
// can we attack:
if (is_attackable) {
- site->second.attack_soldiers = player_->find_attack_soldiers(*flag);
+ std::vector<Soldier *> attackers;
+ player_->find_attack_soldiers(*flag, &attackers);
+ int32_t strength = calculate_strength(attackers);
+
+ site->second.attack_soldiers_strength = strength;
} else {
- site->second.attack_soldiers = 0;
+ site->second.attack_soldiers_strength = 0;
}
- site->second.defenders = defenders;
-
- if (site->second.attack_soldiers > 0) {
- site->second.score = site->second.attack_soldiers - site->second.defenders / 2;
-
- if (!is_warehouse)
- site->second.score -= 1;
+ site->second.defenders_strength = defenders_strength;
+
+ if (site->second.attack_soldiers_strength > 0
+ &&
+ player_attackable[onwer_number - 1]) {
+ site->second.score = site->second.attack_soldiers_strength - site->second.defenders_strength / 2;
+
+ if (is_warehouse) {
+ site->second.score += 2;
+ } else {
+ site->second.score -= 2;
+ }
// here is some differentiation based on "character" of a player
if (type_ == NORMAL) {
- site->second.score -= 1;
- site->second.score -= vacant_mil_positions_ / 10;
+ site->second.score -= 3;
+ site->second.score -= vacant_mil_positions_ / 8;
} else if (type_ == DEFENSIVE) {
- site->second.score -= 2;
- site->second.score -= vacant_mil_positions_ / 5;
+ site->second.score -= 6;
+ site->second.score -= vacant_mil_positions_ / 4;
} else { //=AGRESSIVE
- site->second.score -= vacant_mil_positions_ / 15;
+ site->second.score -= vacant_mil_positions_ / 16;
}
if (site->second.mines_nearby == ExtendedBool::kFalse) {
site->second.score -= 1;
+ } else {
+ site->second.score += 1;
}
// we dont want to attack multiple players at the same time too eagerly
if (onwer_number != last_attacked_player_) {
@@ -4620,29 +5017,32 @@
}
// if we dont have mines yet
if (mines_.size() <= 2) {
- site->second.score -= 2;
+ site->second.score -= 8;
}
// also we should have at least some training sites
if ((ts_basic_count_ + ts_advanced_count_) == 0) {
- site->second.score -= 2;
+ site->second.score -= 4;
}
// treating no attack score
if (site->second.no_attack_counter < 0) {
+ //we cannot attack yet
site->second.score = 0;
+ //but increase the counter by 1
site->second.no_attack_counter += 1;
}
+
} else {
site->second.score = 0;
} // or the score will remain 0
- if (site->second.score > 0 && player_attackable[onwer_number - 1]) {
+ if (site->second.score > 0) {
if (site->second.score > best_score) {
best_score = site->second.score;
best_target = site->first;
}
}
- if (site->second.attack_soldiers > 0) {
+ if (site->second.attack_soldiers_strength > 0) {
site->second.last_time_attackable = gametime;
}
if (site->second.last_time_attackable + 20 * 60 * 1000 < gametime) {
@@ -4743,6 +5143,19 @@
DueTask = task.first;
}
}
+ if ((gametime - oldestTaskTime) > 5000){
+ scheduler_delay_counter_ += 1;
+ } else {
+ scheduler_delay_counter_ = 0;
+ }
+
+ if (scheduler_delay_counter_ > 10){
+ log(" %d: AI: game speed too high, jobs are too late (now %2d seconds)\n",
+ player_number(),
+ static_cast<int32_t>((gametime - oldestTaskTime) / 1000));
+ scheduler_delay_counter_ = 0;
+ }
+
return DueTask;
}
@@ -4766,6 +5179,7 @@
const std::vector<std::string> materials = {"coal",
"log",
"ironore",
+ "iron",
"marble",
"plank",
"water",
@@ -4791,12 +5205,13 @@
}
summary = summary + materials.at(j) + ", ";
}
- log(" %1d: Buildings: Pr:%3lu, Ml:%3lu, Mi:%2lu, Wh:%2lu, Po:%2u. Missing: %s\n",
+
+ log(" %1d: Buildings: Pr:%3u, Ml:%3u, Mi:%2u, Wh:%2u, Po:%u. Missing: %s\n",
pn,
- productionsites.size(),
- militarysites.size(),
- mines_.size(),
- warehousesites.size() - num_ports,
+ static_cast<uint32_t>(productionsites.size()),
+ static_cast<uint32_t>(militarysites.size()),
+ static_cast<uint32_t>(mines_.size()),
+ static_cast<uint32_t>(warehousesites.size() - num_ports),
num_ports,
summary.c_str());
}
=== modified file 'src/ai/defaultai.h'
--- src/ai/defaultai.h 2015-05-05 20:00:21 +0000
+++ src/ai/defaultai.h 2015-05-28 19:39:10 +0000
@@ -30,6 +30,8 @@
#include "base/i18n.h"
#include "logic/immovable.h"
#include "logic/ship.h"
+#include "logic/soldier.h"
+#include "logic/trainingsite.h"
namespace Widelands {
struct Road;
@@ -79,8 +81,10 @@
DEFENSIVE = 0,
};
- enum class WalkSearch : uint8_t {kAnyPlayer, kOtherPlayers};
+ enum class WalkSearch : uint8_t {kAnyPlayer, kOtherPlayers, kEnemy};
+ enum class WoodPolicy : uint8_t {kDismantleRangers, kStopRangers, kStartRangers, kBuildRangers};
enum class NewShip : uint8_t {kBuilt, kFoundOnLoad};
+ enum class PerfEvaluation : uint8_t {kForConstruction, kForDismantle};
enum class ScheduleTasks : uint8_t {
kBbuildableFieldsCheck,
kMineableFieldsCheck,
@@ -199,6 +203,8 @@
bool check_ships(uint32_t);
bool check_enemy_sites(uint32_t);
void print_stats(uint32_t);
+ //return single number of strength of vector of soldiers
+ int32_t calculate_strength(const std::vector<Widelands::Soldier*>);
uint32_t get_stocklevel_by_hint(size_t);
uint32_t get_stocklevel(BuildingObserver&);
uint32_t get_warehoused_stock(Widelands::WareIndex wt);
@@ -233,6 +239,10 @@
void gain_ship(Widelands::Ship&, NewShip);
void expedition_management(ShipObserver&);
void out_of_resources_site(const Widelands::ProductionSite&);
+ void soldier_trained(const Widelands::TrainingSite&);
+ bool is_productionsite_needed(int32_t outputs,
+ int32_t performance,
+ PerfEvaluation purpose);
bool check_supply(const BuildingObserver&);
@@ -260,6 +270,8 @@
// check ms in this interval - will auto-adjust
uint32_t enemysites_check_delay_;
+ WoodPolicy wood_policy_;
+
std::list<Widelands::FCoords> unusable_fields;
std::list<BuildableField*> buildable_fields;
std::list<BlockedField> blocked_fields;
@@ -318,6 +330,17 @@
uint8_t ts_advanced_const_count_;
uint8_t ts_without_trainers_;
+ //this is helping counter to track how many scheduler tasks are too delayed
+ // the purpose is to print out a warning that the game is pacing too fast
+ int32_t scheduler_delay_counter_;
+
+ //this is bunch of patterns that have to identify weapons and armors for input queues of traininsites
+ std::vector<std::string> const armors_and_weapons =
+ {"ax", "lance", "armor", "helm", "lance", "trident", "tabard", "shield", "mask"};
+ //some buildings can be upgraded even when they are only one
+ //now only microbrewery get this special treatment
+ const char* preffered_upgrade[1] = {"micro-brewery"};
+
enum {kReprioritize, kStopShipyard, kStapShipyard};
std::vector<int16_t> marineTaskQueue_;
@@ -327,6 +350,8 @@
std::unique_ptr<Notifications::Subscriber<Widelands::NoteImmovable>> immovable_subscriber_;
std::unique_ptr<Notifications::Subscriber<Widelands::NoteProductionSiteOutOfResources>>
outofresource_subscriber_;
+ std::unique_ptr<Notifications::Subscriber<Widelands::NoteTrainingSiteSoldierTrained>>
+ soldiertrained_subscriber_;
std::unique_ptr<Notifications::Subscriber<Widelands::NoteShipMessage>> shipnotes_subscriber_;
};
=== modified file 'src/logic/trainingsite.cc'
--- src/logic/trainingsite.cc 2015-05-03 11:39:11 +0000
+++ src/logic/trainingsite.cc 2015-05-28 19:39:10 +0000
@@ -320,8 +320,9 @@
delete m_soldier_request;
m_soldier_request = nullptr;
- while (m_soldiers.size() > m_capacity)
+ while (m_soldiers.size() > m_capacity) {
drop_soldier(**m_soldiers.rbegin());
+ }
}
}
@@ -432,6 +433,7 @@
// Schedule, so that we can call new soldiers on next act()
schedule_act(game, 100);
+ Notifications::publish(NoteTrainingSiteSoldierTrained(this, get_owner()));
}
=== modified file 'src/logic/trainingsite.h'
--- src/logic/trainingsite.h 2014-09-19 12:54:54 +0000
+++ src/logic/trainingsite.h 2015-05-28 19:39:10 +0000
@@ -218,6 +218,24 @@
};
+/**
+ * Note to be published when a soldier is leaving the training center
+ */
+// A note we're using to notify the AI
+struct NoteTrainingSiteSoldierTrained {
+ CAN_BE_SEND_AS_NOTE(NoteId::TrainingSiteSoldierTrained)
+
+ // The trainingsite from where soldier is leaving.
+ TrainingSite* ts;
+
+ // The player that owns the ttraining site.
+ Player * player;
+
+ NoteTrainingSiteSoldierTrained(TrainingSite* const init_ts, Player* init_player)
+ : ts(init_ts), player(init_player) {
+ }
+};
+
}
#endif // end of include guard: WL_LOGIC_TRAININGSITE_H
=== modified file 'src/notifications/note_ids.h'
--- src/notifications/note_ids.h 2015-03-01 09:21:20 +0000
+++ src/notifications/note_ids.h 2015-05-28 19:39:10 +0000
@@ -32,6 +32,7 @@
FieldPossession,
FieldTransformed,
ProductionSiteOutOfResources,
+ TrainingSiteSoldierTrained,
ShipMessage,
GraphicResolutionChanged,
=== modified file 'tribes/atlanteans/fish_breeders_house/conf'
--- tribes/atlanteans/fish_breeders_house/conf 2014-08-02 19:55:23 +0000
+++ tribes/atlanteans/fish_breeders_house/conf 2015-05-28 19:39:10 +0000
@@ -3,7 +3,7 @@
[aihints]
needs_water=true
renews_map_resource=fish
-prohibited_till=900
+prohibited_till=300
[buildcost]
log=1
=== modified file 'tribes/atlanteans/sawmill/conf'
--- tribes/atlanteans/sawmill/conf 2014-08-02 19:55:23 +0000
+++ tribes/atlanteans/sawmill/conf 2015-05-28 19:39:10 +0000
@@ -2,7 +2,7 @@
output=planks
[aihints]
-forced_after=300
+forced_after=120
prohibited_till=60
[buildcost]
=== modified file 'tribes/atlanteans/smokery/conf'
--- tribes/atlanteans/smokery/conf 2014-08-02 19:55:23 +0000
+++ tribes/atlanteans/smokery/conf 2015-05-28 19:39:10 +0000
@@ -4,7 +4,7 @@
[aihints]
forced_after=900
-prohibited_till=60
+prohibited_till=180
[buildcost]
log=1
=== modified file 'tribes/atlanteans/spidercloth/conf'
--- tribes/atlanteans/spidercloth/conf 2014-07-29 20:48:19 +0000
+++ tribes/atlanteans/spidercloth/conf 2015-05-28 19:39:10 +0000
@@ -1,7 +1,7 @@
help=_Spidercloth is made out of spideryarn in a weaving mill. It is used in the toolsmithy and the shipyard. Also some higher developed buildings need spidercloth for their construction.
default_target_quantity=20
-preciousness=5
+preciousness=7
[idle]
pics=idle.png
=== modified file 'tribes/atlanteans/spiderfarm/conf'
--- tribes/atlanteans/spiderfarm/conf 2014-08-02 19:55:23 +0000
+++ tribes/atlanteans/spiderfarm/conf 2015-05-28 19:39:10 +0000
@@ -37,5 +37,5 @@
hotspot=87 75
[aihints]
-forced_after=60
+forced_after=150
prohibited_till=60
=== modified file 'tribes/atlanteans/weaving-mill/conf'
--- tribes/atlanteans/weaving-mill/conf 2014-08-02 19:55:23 +0000
+++ tribes/atlanteans/weaving-mill/conf 2015-05-28 19:39:10 +0000
@@ -4,7 +4,7 @@
output=golden_tabard
[aihints]
-forced_after=60
+forced_after=150
prohibited_till=60
[buildcost]
=== modified file 'tribes/barbarians/farm/conf'
--- tribes/barbarians/farm/conf 2015-02-07 01:51:19 +0000
+++ tribes/barbarians/farm/conf 2015-05-28 19:39:10 +0000
@@ -3,6 +3,7 @@
[aihints]
space_consumer=true
+forced_after=900
[buildcost]
log=4
@@ -60,4 +61,4 @@
[working]
pics=working_??.png # ???
-hotspot=69 76
\ No newline at end of file
+hotspot=69 76
Follow ups
-
Re: [Merge] lp:~widelands-dev/widelands/trainingsites_and_teams into lp:widelands
From: GunChleoc, 2015-07-29
-
Re: [Merge] lp:~widelands-dev/widelands/trainingsites_and_teams into lp:widelands
From: TiborB, 2015-07-28
-
[Merge] lp:~widelands-dev/widelands/trainingsites_and_teams into lp:widelands
From: noreply, 2015-07-28
-
Re: [Merge] lp:~widelands-dev/widelands/trainingsites_and_teams into lp:widelands
From: GunChleoc, 2015-07-28
-
Re: [Merge] lp:~widelands-dev/widelands/trainingsites_and_teams into lp:widelands
From: TiborB, 2015-07-27
-
Re: [Merge] lp:~widelands-dev/widelands/trainingsites_and_teams into lp:widelands
From: GunChleoc, 2015-07-26
-
Re: [Merge] lp:~widelands-dev/widelands/trainingsites_and_teams into lp:widelands
From: TiborB, 2015-07-25
-
Re: [Merge] lp:~widelands-dev/widelands/trainingsites_and_teams into lp:widelands
From: GunChleoc, 2015-07-25
-
Re: [Merge] lp:~widelands-dev/widelands/trainingsites_and_teams into lp:widelands
From: TiborB, 2015-06-29
-
Re: [Merge] lp:~widelands-dev/widelands/trainingsites_and_teams into lp:widelands
From: TiborB, 2015-06-26