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

Proposed by TiborB
Status: Merged
Merged at revision: 7495
Proposed branch: lp:~widelands-dev/widelands/trainingsites_and_teams
Merge into: lp:widelands
Diff against target: 1798 lines (+748/-292)
13 files modified
src/ai/ai_help_structs.h (+19/-10)
src/ai/defaultai.cc (+667/-273)
src/ai/defaultai.h (+33/-2)
src/logic/trainingsite.cc (+3/-1)
src/logic/trainingsite.h (+18/-0)
src/notifications/note_ids.h (+1/-0)
tribes/atlanteans/fish_breeders_house/conf (+1/-1)
tribes/atlanteans/sawmill/conf (+1/-1)
tribes/atlanteans/smokery/conf (+1/-1)
tribes/atlanteans/spidercloth/conf (+1/-1)
tribes/atlanteans/spiderfarm/conf (+1/-1)
tribes/atlanteans/weaving-mill/conf (+1/-1)
tribes/barbarians/farm/conf (+1/-0)
To merge this branch: bzr merge lp:~widelands-dev/widelands/trainingsites_and_teams
Reviewer Review Type Date Requested Status
GunChleoc Approve
Review via email: mp+260517@code.launchpad.net

Description of the change

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

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

oooops, I found small problem in defaultai.cc (duplicite sections of code), I am going to fix it...

Revision history for this message
TiborB (tiborb95) wrote :

I fixed what I wanted (7477). I tried regression tests but "test cancel when port space was reached two ships" failed - obviously the same problem as in the other branch.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I have gone through the code and added my usual nitpicking. I haven't gotten around to any testing yet.

Revision history for this message
TiborB (tiborb95) wrote :

I will incorporate your comments of course, thanks for review, and I added one answer to your comment in the diff...

Revision history for this message
GunChleoc (gunchleoc) wrote :

I have added a comment to your comment ;)

The regression tests now also will go through in trunk (SirVer fixed it) but not here - maybe merging trunk will fix this.

Revision history for this message
TiborB (tiborb95) wrote :

I incorporated the comments, I belive all of them, though I was unsure about iterators, there is a lot of places where iterators are used here... Maybe it will be reworked gradually.

regression tests passed OK....

Revision history for this message
GunChleoc (gunchleoc) wrote :

Thanks for fixing - this is good to go.

There are still iterators all over the code from before we switched to C++11. We should get rid of as many as them as possible, because this makes the code easier to read. So, I'm trying to avoid adding new ones. I actually once had a go at removing the iterators from the AI, but I screwed something up, so I need to start from scratch.

review: Approve
Revision history for this message
TiborB (tiborb95) wrote :

Thanks, I am glad it is done :)

Revision history for this message
GunChleoc (gunchleoc) wrote :

Yes, it took me too long to find the time for a review :(

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/ai/ai_help_structs.h'
2--- src/ai/ai_help_structs.h 2015-05-05 20:00:21 +0000
3+++ src/ai/ai_help_structs.h 2015-07-28 19:26:44 +0000
4@@ -382,6 +382,15 @@
5 std::vector<int16_t> inputs_;
6 std::vector<int16_t> outputs_;
7 std::vector<Widelands::WareIndex> critical_built_mat_;
8+
9+ bool upgrade_substitutes_;
10+
11+ // It seems that fish and meat are subsitutes (for trainingsites), so
12+ // when testing if a trainingsite is supplied enough
13+ // we count the wares together
14+ std::unordered_set<Widelands::WareIndex> substitute_inputs_;
15+ int32_t substitutes_count_;
16+
17 int16_t production_hint_;
18
19 int32_t cnt_built_;
20@@ -449,16 +458,16 @@
21 uint8_t preciousness_;
22 };
23
24-//Computer player does not get notification messages about enemy militarysites
25-//and warehouses, so following is collected based on observation
26-//It is conventient to have some information preserved, like nearby minefields,
27-//when it was attacked, whether it is warehouse and so on
28-//Also AI test more such targets when considering attack and calculated score is
29-//is stored in the observer
30+// Computer player does not get notification messages about enemy militarysites
31+// and warehouses, so following is collected based on observation
32+// It is conventient to have some information preserved, like nearby minefields,
33+// when it was attacked, whether it is warehouse and so on
34+// Also AI test more such targets when considering attack and calculated score is
35+// is stored in the observer
36 struct EnemySiteObserver {
37 bool warehouse_;
38- uint8_t attack_soldiers;
39- uint8_t defenders;
40+ int32_t attack_soldiers_strength;
41+ int32_t defenders_strength;
42 uint8_t stationed_soldiers;
43 uint32_t last_time_attackable;
44 uint32_t last_tested;
45@@ -468,8 +477,8 @@
46
47 EnemySiteObserver()
48 : warehouse_(false),
49- attack_soldiers(0),
50- defenders(0),
51+ attack_soldiers_strength(0),
52+ defenders_strength(0),
53 stationed_soldiers(0),
54 last_time_attackable(std::numeric_limits<uint32_t>::max()),
55 last_tested(0),
56
57=== modified file 'src/ai/defaultai.cc'
58--- src/ai/defaultai.cc 2015-05-07 20:46:32 +0000
59+++ src/ai/defaultai.cc 2015-07-28 19:26:44 +0000
60@@ -44,6 +44,7 @@
61 #include "logic/playercommand.h"
62 #include "logic/productionsite.h"
63 #include "logic/ship.h"
64+#include "logic/soldier.h"
65 #include "logic/trainingsite.h"
66 #include "logic/tribe.h"
67 #include "logic/warehouse.h"
68@@ -62,7 +63,7 @@
69 constexpr int kMinMFCheckInterval = 19 * 1000;
70 constexpr int kShipCheckInterval = 5 * 1000;
71 constexpr int kMarineDecisionInterval = 20 * 1000;
72-constexpr int kTrainingSitesCheckInterval = 5 * 60 * 1000;
73+constexpr int kTrainingSitesCheckInterval = 45 * 1000;
74
75 // this is intended for map developers, by default should be off
76 constexpr bool kPrintStats = false;
77@@ -90,6 +91,7 @@
78 num_ports(0),
79 last_attacked_player_(std::numeric_limits<uint16_t>::max()),
80 enemysites_check_delay_(60),
81+ wood_policy_(WoodPolicy::kStartRangers),
82 next_ai_think_(0),
83 next_mine_construction_due_(0),
84 inhibit_road_building_(0),
85@@ -112,7 +114,8 @@
86 ts_basic_const_count_(0),
87 ts_advanced_count_(0),
88 ts_advanced_const_count_(0),
89- ts_without_trainers_(0) {
90+ ts_without_trainers_(0),
91+ scheduler_delay_counter_(0) {
92
93 // Subscribe to NoteFieldPossession.
94 field_possession_subscriber_ =
95@@ -152,6 +155,18 @@
96
97 });
98
99+ // Subscribe to TrainingSiteSoldierTrained.
100+ soldiertrained_subscriber_ = Notifications::subscribe<NoteTrainingSiteSoldierTrained>(
101+ [this](const NoteTrainingSiteSoldierTrained& note) {
102+ if (note.ts->owner().player_number() != player_->player_number()) {
103+ return;
104+ }
105+
106+ soldier_trained(*note.ts);
107+
108+ });
109+
110+
111 // Subscribe to ShipNotes.
112 shipnotes_subscriber_ =
113 Notifications::subscribe<NoteShipMessage>([this](const NoteShipMessage& note) {
114@@ -253,8 +268,18 @@
115 if (check_economies()) { // is a must
116 return;
117 };
118- taskDue[ScheduleTasks::kRoadCheck] = gametime + 400;
119- improve_roads(gametime);
120+ taskDue[ScheduleTasks::kRoadCheck] = gametime + 1000;
121+ // testing 5 roads
122+ {
123+ const int32_t roads_to_check = (roads.size() + 1 < 5) ? roads.size() + 1 : 5;
124+ for (int i = 0; i < roads_to_check; i += 1) {
125+ // improve_roads function will test one road and rotate roads vector
126+ if (improve_roads(gametime)) {
127+ // if significant change takes place do not go on
128+ break;
129+ };
130+ }
131+ }
132 break;
133 case ScheduleTasks::kUnbuildableFCheck :
134 taskDue[ScheduleTasks::kUnbuildableFCheck] = gametime + 4000;
135@@ -280,8 +305,18 @@
136 if (check_economies()) { // economies must be consistent
137 return;
138 }
139- check_productionsites(gametime);
140- taskDue[ScheduleTasks::kCheckProductionsites] = gametime + 5000;
141+ {
142+ // testing 5 productionsites (if there are 5 of them)
143+ int32_t ps_to_check = (productionsites.size() < 5) ? productionsites.size() : 5;
144+ for (int i = 0; i < ps_to_check; i += 1) {
145+ // one productionsite per one check_productionsites() call
146+ if (check_productionsites(gametime)) {
147+ // if significant change takes place do not go on
148+ break;
149+ };
150+ }
151+ }
152+ taskDue[ScheduleTasks::kCheckProductionsites] = gametime + 15000;
153 break;
154 case ScheduleTasks::kCheckShips :
155 check_ships(gametime);
156@@ -293,8 +328,18 @@
157 if (check_economies()) { // economies must be consistent
158 return;
159 }
160- taskDue[ScheduleTasks::kCheckMines] = gametime + 7000; // 7 seconds is enough
161- check_mines_(gametime);
162+ taskDue[ScheduleTasks::kCheckMines] = gametime + 15000;
163+ // checking 3 mines if possible
164+ {
165+ int32_t mines_to_check = (mines_.size() < 5) ? mines_.size() : 5;
166+ for (int i = 0; i < mines_to_check; i += 1) {
167+ // every run of check_mines_() checks one mine
168+ if (check_mines_(gametime)) {
169+ // if significant change takes place do not go on
170+ break;
171+ };
172+ }
173+ }
174 break;
175 case ScheduleTasks::kCheckMilitarysites :
176 check_militarysites(gametime);
177@@ -304,7 +349,7 @@
178 break;
179 case ScheduleTasks::kCountMilitaryVacant :
180 count_military_vacant_positions();
181- taskDue[ScheduleTasks::kCountMilitaryVacant] = gametime + 90 * 1000;
182+ taskDue[ScheduleTasks::kCountMilitaryVacant] = gametime + 60 * 1000;
183 break;
184 case ScheduleTasks::kWareReview :
185 if (check_economies()) { // economies must be consistent
186@@ -391,6 +436,7 @@
187 bo.forced_after_ = bh.get_forced_after() * 1000; // value in conf is in seconds
188 bo.is_port_ = bld.get_isport();
189 bo.trainingsite_type_ = TrainingSiteType::kNoTS;
190+ bo.upgrade_substitutes_ = false;
191
192 if (bh.renews_map_resource()) {
193 bo.production_hint_ = tribe_->safe_ware_index(bh.get_renews_map_resource());
194@@ -448,6 +494,38 @@
195 bo.is_shipyard_ = false;
196 }
197
198+ // now we find out if the upgrade of the building is a full substitution
199+ // (produces all wares as current one)
200+ const BuildingIndex enhancement = bld.enhancement();
201+ if (enhancement != INVALID_INDEX && bo.type == BuildingObserver::PRODUCTIONSITE) {
202+ std::unordered_set<WareIndex> enh_outputs;
203+ const ProductionSiteDescr& enh_prod
204+ =
205+ dynamic_cast<const ProductionSiteDescr&>(*tribe_->get_building_descr(enhancement));
206+
207+ // collecting wares that are produced in enhanced building
208+ for (const WareIndex& ware : enh_prod.output_ware_types()) {
209+ enh_outputs.insert(ware);
210+ }
211+ // now testing outputs of current building
212+ // and comparing
213+ bo.upgrade_substitutes_ = true;
214+ for (WareIndex ware : bo.outputs_) {
215+ if (enh_outputs.count(ware) == 0) {
216+ bo.upgrade_substitutes_ = false;
217+ break;
218+ }
219+ }
220+ }
221+
222+ // plus some manually picked buildings,
223+ // see preferred_upgrade list
224+ for (const char* pb : preferred_upgrade) {
225+ if (strcmp(bld.name().c_str(), pb) == 0) {
226+ bo.upgrade_substitutes_ = true;
227+ }
228+ }
229+
230 continue;
231 }
232
233@@ -482,7 +560,16 @@
234 const TrainingSiteDescr& train = dynamic_cast<const TrainingSiteDescr&>(bld);
235 for (const WareAmount& temp_input : train.inputs()) {
236 bo.inputs_.push_back(temp_input.first);
237+
238+ // collecting subsitutes
239+ if (tribe_->ware_index("meat") == temp_input.first ||
240+ tribe_->ware_index("fish") == temp_input.first ||
241+ tribe_->ware_index("smoked_meat") == temp_input.first ||
242+ tribe_->ware_index("smoked_fish") == temp_input.first) {
243+ bo.substitute_inputs_.insert(temp_input.first);
244+ }
245 }
246+
247 bo.trainingsite_type_ = bh.get_trainingsite_type();
248 // it would behave badly if no type was set
249 // make sure all TS have its type set properly in conf files
250@@ -496,6 +583,8 @@
251 }
252 }
253
254+
255+
256 // atlanteans they consider water as a resource
257 // (together with mines, stones and wood)
258 if (tribe_->name() == "atlanteans") {
259@@ -592,7 +681,7 @@
260 taskDue[ScheduleTasks::kCheckShips] = 30 * 1000;
261 taskDue[ScheduleTasks::kCheckEconomies] = 1000;
262 taskDue[ScheduleTasks::KMarineDecisions] = 30 * 1000;
263- taskDue[ScheduleTasks::kCheckTrainingsites] = 15 * 60 * 1000;
264+ taskDue[ScheduleTasks::kCheckTrainingsites] = 2 * 60 * 1000;
265 taskDue[ScheduleTasks::kBbuildableFieldsCheck] = 1000;
266 taskDue[ScheduleTasks::kMineableFieldsCheck] = 1000;
267 taskDue[ScheduleTasks::kUnbuildableFCheck] = 1000;
268@@ -870,7 +959,6 @@
269 if (player_->is_hostile(player_immovable->owner())) {
270 field.enemy_nearby_ = true;
271 }
272- enemy_last_seen_ = gametime;
273
274 continue;
275 }
276@@ -1132,23 +1220,23 @@
277 // scores every combination and one with highest and positive score
278 // is built.
279 // * Buildings are split into categories
280-// * The logic is complex but aproximatelly:
281-// - buildings producing building material are preffered
282-// - buildings identified as basic are preffered
283-// - first bulding of a type is preffered
284-// - buildings identified as 'direct food supplier' as built after 15 min.
285+// * The logic is complex but approximately:
286+// - buildings producing building material are preferred
287+// - buildings identified as basic are preferred
288+// - first bulding of a type is preferred
289+// - buildings identified as 'direct food supplier' are built after 15 min.
290 // from game start
291-// - if a bulding is upgradeable, second building is also preffered
292+// - if a building is upgradeable, second building is also preferred
293 // (there should be no upgrade when there are not two buildings of the same type)
294-// - algorigthm is trying to take into account actual utlization of buildings
295+// - algorithm is trying to take into account actual utlization of buildings
296 // (the one shown in GUI/game is not reliable, it calculates own statistics)
297 // * military buildings have own strategy, split into two situations:
298 // - there is no enemy
299 // - there is an enemy
300-// Currently more military buildings are built then needed
301-// and "optimalization" (dismantling not needed buildings) is done afterwards
302+// Currently more military buildings are built than needed
303+// and "optimization" (dismantling not needed buildings) is done afterwards
304 bool DefaultAI::construct_building(uint32_t gametime) {
305- // Just used for easy checking whether a mine or something else was built.
306+ // Just used for easy checking whether a mine or something else was built.
307 bool mine = false;
308 bool field_blocked = false;
309 uint32_t consumers_nearby_count = 0;
310@@ -1172,6 +1260,21 @@
311 new_buildings_stop_ = false;
312 MilitaryStrategy expansion_mode = MilitaryStrategy::kResourcesOrDefense;
313
314+ // helper variable - we need some proportion of free spots vs productionsites
315+ // the proportion depends on size of economy
316+ // this proportion defines how dense the buildings will be
317+ // it is degressive (allows high density on the beginning)
318+ int32_t needed_spots = 0;
319+ if (productionsites.size() < 50) {
320+ needed_spots = productionsites.size();
321+ } else if (productionsites.size() < 100) {
322+ needed_spots = 50 + (productionsites.size() - 50) * 5;
323+ } else if (productionsites.size() < 200) {
324+ needed_spots = 300 + (productionsites.size() - 100) * 10;
325+ } else {
326+ needed_spots = 1300 + (productionsites.size() - 200) * 20;
327+ }
328+
329 // there are many reasons why to stop building production buildings
330 // (note there are numerous exceptions)
331 // 1. to not have too many constructionsites
332@@ -1179,12 +1282,12 @@
333 new_buildings_stop_ = true;
334 }
335 // 2. to not exhaust all free spots
336- if (spots_ * 3 / 2 + 5 < static_cast<int32_t>(productionsites.size())) {
337+ if (spots_ < needed_spots) {
338 new_buildings_stop_ = true;
339 }
340 // 3. too keep some proportions production sites vs military sites
341 if ((num_prod_constructionsites + productionsites.size()) >
342- (num_milit_constructionsites + militarysites.size()) * 3) {
343+ (num_milit_constructionsites + militarysites.size()) * 5) {
344 new_buildings_stop_ = true;
345 }
346 // 4. if we do not have 3 mines at least
347@@ -1192,26 +1295,26 @@
348 new_buildings_stop_ = true;
349 }
350 // BUT if enemy is nearby, we cancel above stop
351- if (new_buildings_stop_ && enemy_last_seen_ + 2 * 60 * 1000 > gametime) {
352+ if (new_buildings_stop_ && enemy_last_seen_ + 10 * 60 * 1000 > gametime) {
353 new_buildings_stop_ = false;
354 }
355
356- // sometimes there is too many military buildings in construction, so we must
357- // prevent initialization of further buildings start
358- const int32_t vacant_plus_in_construction_minus_prod =
359- vacant_mil_positions_ + 2 * num_milit_constructionsites - productionsites.size() / 7;
360- if (vacant_plus_in_construction_minus_prod > 20) {
361- expansion_mode = MilitaryStrategy::kNoNewMilitary;
362- } else if (vacant_plus_in_construction_minus_prod > 13) {
363- expansion_mode = MilitaryStrategy::kDefenseOnly;
364- } else if (vacant_plus_in_construction_minus_prod > 6) {
365- expansion_mode = MilitaryStrategy::kResourcesOrDefense;
366+ // we must calculate wood policy
367+ const WareIndex wood_index = tribe_->safe_ware_index("log");
368+ // stocked wood is to be in some propotion to productionsites and
369+ // constructionsites (this proportion is bit artifical, or we can say
370+ // it is proportion to the size of economy). Plus some positive 'margin'
371+ const int32_t stocked_wood_margin = get_warehoused_stock(wood_index) -
372+ productionsites.size() * 2 -
373+ num_prod_constructionsites;
374+ if (stocked_wood_margin > 80) {
375+ wood_policy_ = WoodPolicy::kDismantleRangers;
376+ } else if (stocked_wood_margin > 25) {
377+ wood_policy_ = WoodPolicy::kStopRangers;
378+ } else if (stocked_wood_margin > 10) {
379+ wood_policy_ = WoodPolicy::kStartRangers;
380 } else {
381- if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
382- expansion_mode = MilitaryStrategy::kExpansion;
383- } else {
384- expansion_mode = MilitaryStrategy::kPushExpansion;
385- }
386+ wood_policy_ = WoodPolicy::kBuildRangers;
387 }
388
389 // we must consider need for mines
390@@ -1243,6 +1346,27 @@
391 }
392 }
393
394+ // this controls a speed and willingness to expand the teritorry
395+ const int32_t vacant_plus_in_construction_minus_prod =
396+ vacant_mil_positions_ + 2 * num_milit_constructionsites - productionsites.size() / 7;
397+ if (vacant_plus_in_construction_minus_prod > 20) {
398+ expansion_mode = MilitaryStrategy::kNoNewMilitary;
399+ } else if (vacant_plus_in_construction_minus_prod > 13) {
400+ expansion_mode = MilitaryStrategy::kDefenseOnly;
401+ } else if (vacant_plus_in_construction_minus_prod > 6) {
402+ expansion_mode = MilitaryStrategy::kResourcesOrDefense;
403+ } else {
404+ // this is intended for initial phase of game when the player still has enough soldiers
405+ // but we still want to force it to follow resources instead for plain expansion
406+ if (virtual_mines <= 2 && (unstationed_milit_buildings_ + num_milit_constructionsites) > 2) {
407+ expansion_mode = MilitaryStrategy::kResourcesOrDefense;
408+ } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
409+ expansion_mode = MilitaryStrategy::kExpansion;
410+ } else {
411+ expansion_mode = MilitaryStrategy::kPushExpansion;
412+ }
413+ }
414+
415 BuildingObserver* best_building = nullptr;
416 int32_t proposed_priority = 0;
417 Coords proposed_coords;
418@@ -1559,12 +1683,6 @@
419 continue;
420 }
421
422- if (bo.stocklevel_time < game().get_gametime() - 5 * 1000) {
423- bo.stocklevel_ =
424- get_stocklevel_by_hint(static_cast<size_t>(bo.production_hint_));
425- bo.stocklevel_time = game().get_gametime();
426- }
427-
428 if (bo.total_count() == 0) {
429 prio = 200;
430 }
431@@ -1572,9 +1690,10 @@
432 continue;
433 }
434 // we can go above target if there is shortage of logs on stock
435- else if (bo.total_count() >= bo.cnt_target_ &&
436- bo.stocklevel_ > 40 + productionsites.size() * 2) {
437- continue;
438+ else if (bo.total_count() >= bo.cnt_target_) {
439+ if (wood_policy_ != WoodPolicy::kBuildRangers) {
440+ continue;
441+ }
442 }
443
444 // considering near trees and producers
445@@ -1625,15 +1744,29 @@
446 // this will depend on number of mines_ and productionsites
447 if (static_cast<int32_t>((productionsites.size() + mines_.size()) / 30) >
448 bo.total_count() &&
449- bo.cnt_under_construction_ == 0)
450- prio = 4 + kDefaultPrioBoost;
451+ (bo.cnt_under_construction_ + bo.unoccupied_) == 0 &&
452+ // but only if current buildings are utilized enough
453+ (bo.total_count() == 0 || bo.current_stats_ > 60)) {
454+ prio = 4 + kDefaultPrioBoost;
455+ }
456 } else { // finally normal productionsites
457 if (bo.production_hint_ >= 0) {
458 continue;
459 }
460
461- if ((bo.cnt_under_construction_ + bo.unoccupied_) > 0) {
462- continue;
463+ // generally we allow 1 building in construction, but if
464+ // preciousness of missing ware is >=10 and it is farm-like building
465+ // we allow 2 in construction
466+ if (max_needed_preciousness >= 10
467+ && bo.inputs_.empty()
468+ && gametime > 30 * 60 * 1000) {
469+ if ((bo.cnt_under_construction_ + bo.unoccupied_) > 1) {
470+ continue;
471+ }
472+ } else {
473+ if ((bo.cnt_under_construction_ + bo.unoccupied_) > 0) {
474+ continue;
475+ }
476 }
477
478 if (bo.forced_after_ < gametime && (bo.total_count() - bo.unconnected_) == 0) {
479@@ -1653,9 +1786,9 @@
480 prio += kDefaultPrioBoost;
481 } else if ((bo.cnt_built_ - bo.unconnected_) > 1 && bo.current_stats_ > 97) {
482 prio -= kDefaultPrioBoost * (new_buildings_stop_);
483- } else if (new_buildings_stop_)
484+ } else if (new_buildings_stop_) {
485 continue;
486-
487+ }
488 // we check separatelly buildings with no inputs and some inputs
489 if (bo.inputs_.empty()) {
490
491@@ -1683,7 +1816,6 @@
492 if (!bo.space_consumer_) {
493 prio -= bf->producers_nearby_.at(bo.outputs_.at(0)) * 20;
494 } // leave some free space between them
495-
496 }
497
498 else if (bo.is_shipyard_) {
499@@ -1699,12 +1831,9 @@
500 }
501 if ((bo.cnt_built_ - bo.unconnected_) > 0
502 &&
503- //due to very badly designed statistics and the way how
504- //productionsites are working we must distinguish how many
505- //outputs the site has.
506- ((bo.outputs_.size() == 1 && bo.current_stats_ > 75)
507- ||
508- (bo.outputs_.size() > 1 && bo.current_stats_ > 55))) {
509+ is_productionsite_needed(bo.outputs_.size(),
510+ bo.current_stats_,
511+ PerfEvaluation::kForConstruction)) {
512 prio += max_needed_preciousness + kDefaultPrioBoost - 3 +
513 (bo.current_stats_ - 55) / 8;
514 }
515@@ -1714,6 +1843,15 @@
516 continue;
517 }
518
519+ // bonus for big buildings if shortage of big fields
520+ if (spots_avail.at(BUILDCAPS_BIG) <= 5 && bo.desc->get_size() == 3) {
521+ prio += 10;
522+ }
523+
524+ if (spots_avail.at(BUILDCAPS_MEDIUM) <= 5 && bo.desc->get_size() == 2) {
525+ prio += 5;
526+ }
527+
528 //+1 if any consumers_ are nearby
529 consumers_nearby_count = 0;
530
531@@ -1724,6 +1862,21 @@
532 prio += 1;
533 }
534 }
535+
536+ // consider borders (for medium + big buildings and ones with input)
537+ // =>decreasing the score
538+ // but only if we have enough free spots to built on
539+ // otherwise it will slow down the expansion - small buildings would be preferred
540+ if (spots_avail.at(BUILDCAPS_MEDIUM) > 40
541+ &&
542+ spots_avail.at(BUILDCAPS_BIG) > 20
543+ &&
544+ (bo.desc->get_size() == 2 ||
545+ bo.desc->get_size() == 3 ||
546+ !bo.inputs_.empty())) {
547+ prio = recalc_with_border_range(*bf, prio);
548+ }
549+
550 } // production sites done
551 else if (bo.type == BuildingObserver::MILITARYSITE) {
552
553@@ -1901,115 +2054,62 @@
554 // Militarysites are slightly important as well, to have a bigger
555 // chance for a warehouses (containing waiting soldiers or wares
556 // needed for soldier training) near the frontier.
557- if ((static_cast<int32_t>(productionsites.size() + mines_.size()) + 20) / 35 >
558- static_cast<int32_t>(numof_warehouses_) &&
559- bo.cnt_under_construction_ == 0) {
560- prio = 20;
561- warehouse_needed = true;
562- }
563-
564- // but generally we prefer ports
565- if (bo.is_port_) {
566- prio += 10;
567- }
568-
569- // special boost for first port
570- if (bo.is_port_ && bo.total_count() == 0 && productionsites.size() > 5 &&
571- !bf->enemy_nearby_ && bf->is_portspace_ && seafaring_economy) {
572- prio += kDefaultPrioBoost + productionsites.size();
573- warehouse_needed = true;
574- }
575-
576- if (!warehouse_needed) {
577- continue;
578- }
579-
580- // iterating over current warehouses and testing a distance
581- // getting distance to nearest warehouse and adding it to a score
582- uint16_t nearest_distance = std::numeric_limits<uint16_t>::max();
583- for (const WarehouseSiteObserver& wh_obs : warehousesites) {
584- const uint16_t actual_distance =
585- map.calc_distance(bf->coords, wh_obs.site->get_position());
586- nearest_distance = std::min(nearest_distance, actual_distance);
587- }
588- // but limit to 15
589- const uint16_t max_distance_considered = 15;
590- nearest_distance = std::min(nearest_distance, max_distance_considered);
591- prio += nearest_distance;
592-
593- // take care about and enemies
594- if (bf->enemy_nearby_) {
595- prio /= 4;
596- }
597-
598- if (bf->unowned_land_nearby_ && !bo.is_port_) {
599- prio /= 2;
600- }
601-
602- } else if (bo.type == BuildingObserver::WAREHOUSE) {
603-
604- // exclude spots on border
605- if (bf->near_border_ && !bo.is_port_) {
606- continue;
607- }
608-
609- if (!bf->is_portspace_ && bo.is_port_) {
610- continue;
611- }
612-
613- if (bo.cnt_under_construction_ > 0) {
614- continue;
615- }
616-
617- bool warehouse_needed = false;
618-
619- // Build one warehouse for ~every 35 productionsites and mines_.
620- // Militarysites are slightly important as well, to have a bigger
621- // chance for a warehouses (containing waiting soldiers or wares
622- // needed for soldier training) near the frontier.
623- if ((static_cast<int32_t>(productionsites.size() + mines_.size()) + 20) / 35 >
624- static_cast<int32_t>(numof_warehouses_) &&
625- bo.cnt_under_construction_ == 0) {
626- prio = 20;
627- warehouse_needed = true;
628- }
629-
630- // but generally we prefer ports
631- if (bo.is_port_) {
632- prio += 10;
633- }
634-
635- // special boost for first port
636- if (bo.is_port_ && bo.total_count() == 0 && productionsites.size() > 5 &&
637- !bf->enemy_nearby_ && bf->is_portspace_ && seafaring_economy) {
638- prio += kDefaultPrioBoost + productionsites.size();
639- warehouse_needed = true;
640- }
641-
642- if (!warehouse_needed) {
643- continue;
644- }
645-
646- // iterating over current warehouses and testing a distance
647- // getting distance to nearest warehouse and adding it to a score
648- uint16_t nearest_distance = std::numeric_limits<uint16_t>::max();
649- for (const WarehouseSiteObserver& wh_obs : warehousesites) {
650- const uint16_t actual_distance =
651- map.calc_distance(bf->coords, wh_obs.site->get_position());
652- nearest_distance = std::min(nearest_distance, actual_distance);
653- }
654- // but limit to 15
655- const uint16_t max_distance_considered = 15;
656- nearest_distance = std::min(nearest_distance, max_distance_considered);
657- prio += nearest_distance;
658-
659- // take care about and enemies
660- if (bf->enemy_nearby_) {
661- prio /= 4;
662- }
663-
664- if (bf->unowned_land_nearby_ && !bo.is_port_) {
665- prio /= 2;
666+ prio = static_cast<int32_t>(productionsites.size() + mines_.size()) + 20
667+ -
668+ 35 * static_cast<int32_t>(numof_warehouses_);
669+ if (prio > 0) {
670+ warehouse_needed = true;
671+ } else {
672+ prio = 0;
673+ }
674+
675+ // But we still can built a port if it is first one
676+ if (bo.is_port_ && bo.total_count() == 0 && productionsites.size() > 5 &&
677+ !bf->enemy_nearby_ && bf->is_portspace_ && seafaring_economy) {
678+ prio += kDefaultPrioBoost + productionsites.size();
679+ warehouse_needed = true;
680+ }
681+
682+ if (!warehouse_needed) {
683+ continue;
684+ }
685+
686+ // we prefer ports to a normal warehouse
687+ if (bo.is_port_) {
688+ prio += 15;
689+ }
690+
691+ // it is good to have more then 1 warehouse
692+ if (numof_warehouses_ == 1) {
693+ prio += 10;
694+ }
695+
696+ // iterating over current warehouses and testing a distance
697+ // getting distance to nearest warehouse and adding it to a score
698+ uint16_t nearest_distance = std::numeric_limits<uint16_t>::max();
699+ for (const WarehouseSiteObserver& wh_obs : warehousesites) {
700+ const uint16_t actual_distance =
701+ map.calc_distance(bf->coords, wh_obs.site->get_position());
702+ nearest_distance = std::min(nearest_distance, actual_distance);
703+ }
704+ // but limit to 30
705+ const uint16_t max_distance_considered = 30;
706+ nearest_distance = std::min(nearest_distance, max_distance_considered);
707+ prio += nearest_distance - 10;
708+
709+ // dont be close to enemies
710+ if (bf->enemy_nearby_){
711+ if (bo.is_port_) {
712+ prio -= 10;
713+ } else {
714+ prio -= 40;
715+ }
716+ }
717+
718+ // being too close to a border is not good either
719+ if (bf->unowned_land_nearby_ && !bo.is_port_ && prio > 0) {
720+ prio /= 2;
721+ prio -= 10;
722 }
723
724 } else if (bo.type == BuildingObserver::TRAININGSITE) {
725@@ -2029,14 +2129,14 @@
726 continue;
727 }
728
729- // we build one training site for 100 militarysites
730+ // we build one basic training site for 50 militarysites
731 if (bo.trainingsite_type_ == TrainingSiteType::kBasic &&
732- militarysites.size() / 100 < static_cast<int32_t>(ts_basic_count_)) {
733+ militarysites.size() / 50 < static_cast<int32_t>(ts_basic_count_)) {
734 continue;
735 }
736- // we build one training site for 100 militarysites
737+ // we build one advanced training site for 75 militarysites
738 if (bo.trainingsite_type_ == TrainingSiteType::kAdvanced &&
739- militarysites.size() / 100 < static_cast<int32_t>(ts_advanced_count_)) {
740+ militarysites.size() / 75 < static_cast<int32_t>(ts_advanced_count_)) {
741 continue;
742 }
743
744@@ -2080,9 +2180,9 @@
745 }
746
747 // Prefer road side fields
748- prio += bf->preferred_ ? 1 : 0;
749+ prio += bf->preferred_ ? 5 : 0;
750 // don't waste good land for small huts
751- prio -= (maxsize - bo.desc->get_size()) * 5;
752+ prio -= (maxsize - bo.desc->get_size()) * 20;
753
754 // prefer vicinity of ports (with exemption of warehouses)
755 if (bf->port_nearby_ && bo.type == BuildingObserver::MILITARYSITE) {
756@@ -2148,7 +2248,7 @@
757 0) {
758 nearness_penalty = 0;
759 } else {
760- nearness_penalty = 30;
761+ nearness_penalty = 40;
762 }
763
764 // bonus score to prefer if too few mines
765@@ -2338,12 +2438,13 @@
766 }
767
768 if (inhibit_road_building_ >= gametime) {
769- return false;
770+ return true;
771 }
772
773 // now we rotate economies and flags to get one flag to go on with
774 if (economies.empty()) {
775- return check_economies();
776+ check_economies();
777+ return false;
778 }
779
780 if (economies.size() >= 2) { // rotating economies
781@@ -2353,7 +2454,8 @@
782
783 EconomyObserver* eco = economies.front();
784 if (eco->flags.empty()) {
785- return check_economies();
786+ check_economies();
787+ return false;
788 }
789 if (eco->flags.size() > 1) {
790 eco->flags.push_back(eco->flags.front());
791@@ -2389,9 +2491,11 @@
792 } else if (flag.current_wares() > 5) {
793 create_shortcut_road(flag, 9, -2, gametime);
794 inhibit_road_building_ = gametime + 400;
795+ } else {
796+ return false;
797 }
798
799- return false;
800+ return true;
801 }
802
803 // the function takes a road (road is smallest section of roads with two flags on the ends)
804@@ -2469,6 +2573,27 @@
805 return false;
806 }
807
808+// is productionsite needed
809+// used for building new buildings or dismantle of old, intended for ones
810+// that have inputs
811+bool DefaultAI::is_productionsite_needed(int32_t outputs,
812+ int32_t performance,
813+ PerfEvaluation purpose) {
814+ int32_t expected_performance = 0;
815+ if (outputs > 0) {
816+ expected_performance = 10 + 70 / outputs;
817+ } else {
818+ expected_performance = 80;
819+ }
820+ if (purpose == PerfEvaluation::kForDismantle) {
821+ expected_performance /= 2;
822+ }
823+ if (performance > expected_performance) {
824+ return true;
825+ }
826+ return false;
827+}
828+
829 // trying to connect the flag to another one, be it from own economy
830 // or other economy
831 bool DefaultAI::create_shortcut_road(const Flag& flag,
832@@ -2811,7 +2936,6 @@
833 return false;
834 }
835
836- bool changed = false;
837 // Reorder and set new values; - better now because there are multiple returns in the function
838 productionsites.push_back(productionsites.front());
839 productionsites.pop_front();
840@@ -2850,14 +2974,18 @@
841 // available, one is to be enhanced
842 // b) if there are two buildings
843 // statistics percents are decisive
844+ // c) yet there are buildings that might be upgraded, even when
845+ // there is no second buiding of the kind (flag upgrade_substitutes_)
846+
847 const BuildingIndex enhancement = site.site->descr().enhancement();
848 if (connected_to_wh && enhancement != INVALID_INDEX &&
849- (site.bo->cnt_built_ - site.bo->unoccupied_) > 1) {
850+ ((site.bo->cnt_built_ - site.bo->unoccupied_) > 1 ||
851+ site.bo->upgrade_substitutes_)) {
852
853 BuildingIndex enbld = INVALID_INDEX; // to get rid of this
854
855 // Only enhance buildings that are allowed (scenario mode)
856- // do not do decisions to fast
857+ // do not do decisions too fast
858 if (player_->is_building_type_allowed(enhancement)) {
859
860 const BuildingDescr& bld = *tribe_->get_building_descr(enhancement);
861@@ -2871,7 +2999,7 @@
862 if (site.site->has_workers(enhancement, game())) {
863
864 // forcing first upgrade
865- if (en_bo.cnt_built_ == 0 && !mines_.empty()) {
866+ if (en_bo.cnt_built_ == 0) {
867 enbld = enhancement;
868 bestbld = &en_bo;
869 }
870@@ -3079,9 +3207,12 @@
871 }
872
873 // buildings with inputs_, checking if we can a dismantle some due to low performance
874- if (!site.bo->inputs_.empty() && (site.bo->cnt_built_ - site.bo->unoccupied_) >= 3 &&
875+ if (!site.bo->inputs_.empty() &&
876+ (site.bo->cnt_built_ - site.bo->unoccupied_) >= 3 &&
877 site.site->can_start_working() &&
878- site.site->get_statistics_percent() < 20 && // statistics for the building
879+ !is_productionsite_needed(site.bo->outputs_.size(),
880+ site.site->get_statistics_percent(),
881+ PerfEvaluation::kForDismantle) &&
882 site.bo->current_stats_ < 30 && // overall statistics
883 (game().get_gametime() - site.unoccupied_till_) > 10 * 60 * 1000) {
884
885@@ -3117,16 +3248,8 @@
886 // stop/start them based on stock avaiable
887 if (site.bo->production_hint_ >= 0) {
888
889- if (site.bo->stocklevel_time < game().get_gametime() - 5 * 1000) {
890- site.bo->stocklevel_ = get_stocklevel_by_hint(site.bo->production_hint_);
891- site.bo->stocklevel_time = game().get_gametime();
892- }
893-
894- // logs can be stored also in productionsites, they are counted as on stock
895- // but are no available for random production site
896- int16_t score = site.bo->stocklevel_ - productionsites.size() * 2;
897-
898- if (score > 200 && site.bo->cnt_built_ > site.bo->cnt_target_) {
899+ // dismantling the rangers hut, but only if we have them above a target
900+ if (wood_policy_ == WoodPolicy::kDismantleRangers && site.bo->cnt_built_ > site.bo->cnt_target_) {
901
902 site.bo->last_dismantle_time_ = game().get_gametime();
903 flags_to_be_removed.push_back(site.site->base_flag().get_position());
904@@ -3138,27 +3261,38 @@
905 return true;
906 }
907
908- if (score > 120 && !site.site->is_stopped()) {
909+ // stopping a ranger (sometimes the policy can be kDismantleRangers,
910+ // but we still preserve some rangers for sure)
911+ if ((wood_policy_ == WoodPolicy::kStopRangers
912+ ||
913+ wood_policy_ == WoodPolicy::kDismantleRangers)
914+ &&
915+ !site.site->is_stopped()) {
916
917 game().send_player_start_stop_building(*site.site);
918- return true;
919+ return false;
920 }
921+
922 const uint32_t trees_in_vicinity =
923 map.find_immovables(Area<FCoords>(map.get_fcoords(site.site->get_position()), 5),
924 nullptr,
925 FindImmovableAttribute(MapObjectDescr::get_attribute_id("tree")));
926
927+ // stop ranger if enough trees around regardless of policy
928 if (trees_in_vicinity > 25) {
929 if (!site.site->is_stopped()) {
930 game().send_player_start_stop_building(*site.site);
931 }
932- } else if (score < 80 && site.site->is_stopped()) {
933-
934- game().send_player_start_stop_building(*site.site);
935+ // if not enough trees nearby, we can start them if required
936+ } else if ((wood_policy_ == WoodPolicy::kStartRangers ||
937+ wood_policy_ == WoodPolicy::kBuildRangers)
938+ &&
939+ site.site->is_stopped()) {
940+ game().send_player_start_stop_building(*site.site);
941 }
942 }
943
944- return changed;
945+ return false;
946 }
947
948 // This function scans current situation with shipyards, ports, ships, ongoing expeditions
949@@ -3562,15 +3696,15 @@
950 // counting vacant positions
951 vacant_mil_positions_ = 0;
952 for (TrainingSiteObserver tso : trainingsites) {
953- vacant_mil_positions_ += tso.site->soldier_capacity() - tso.site->stationed_soldiers().size();
954+ vacant_mil_positions_ += 10 * (tso.site->soldier_capacity() - tso.site->stationed_soldiers().size());
955 }
956 for (MilitarySiteObserver mso : militarysites) {
957 vacant_mil_positions_ += mso.site->soldier_capacity() - mso.site->stationed_soldiers().size();
958 }
959 }
960
961-// this function only manipulates with trainingsites' inputs priority
962-// decreases it when too many unoccupied military buildings
963+// this function only check with trainingsites
964+// manipulates input queues and soldier capacity
965 bool DefaultAI::check_trainingsites(uint32_t gametime) {
966 if (taskDue[ScheduleTasks::kCheckTrainingsites] > gametime) {
967 return false;
968@@ -3582,6 +3716,9 @@
969 return false;
970 }
971
972+ trainingsites.push_back(trainingsites.front());
973+ trainingsites.pop_front();
974+
975 TrainingSite* ts = trainingsites.front().site;
976 TrainingSiteObserver& tso = trainingsites.front();
977
978@@ -3595,12 +3732,76 @@
979 }
980 }
981
982- trainingsites.push_back(trainingsites.front());
983- trainingsites.pop_front();
984-
985- // changing capacity
986- if (tso.site->soldier_capacity() != 2) {
987- game().send_player_change_soldier_capacity(*ts, 2 - tso.site->soldier_capacity());
988+ // changing capacity to 0 - this will happen only once.....
989+ if (tso.site->soldier_capacity() > 1) {
990+ game().send_player_change_soldier_capacity(*ts, - tso.site->soldier_capacity());
991+ return true;
992+ }
993+
994+ // reducing ware queues
995+ // - for armours and weapons to 1
996+ // - for others to 6
997+ std::vector<WaresQueue*> const warequeues1 = tso.site->warequeues();
998+ size_t nr_warequeues = warequeues1.size();
999+ for (size_t i = 0; i < nr_warequeues; ++i) {
1000+
1001+ // if it was decreased yet
1002+ if (warequeues1[i]->get_max_fill() <= 1) {
1003+ continue;}
1004+
1005+ // now modifying max_fill of armors and weapons
1006+ for (std::string pattern : armors_and_weapons) {
1007+
1008+ if (tribe_->get_ware_descr(warequeues1[i]->get_ware())->name().find(pattern) != std::string::npos) {
1009+ if (warequeues1[i]->get_max_fill() > 1) {
1010+ game().send_player_set_ware_max_fill(*ts, warequeues1[i]->get_ware(), 1);
1011+ continue;
1012+ }
1013+ }
1014+ }
1015+ }
1016+
1017+ // changing priority if basic
1018+ if (tso.bo->trainingsite_type_ == TrainingSiteType::kBasic) {
1019+ for (uint32_t k = 0; k < tso.bo->inputs_.size(); ++k) {
1020+ game().send_player_set_ware_priority(
1021+ *ts, wwWARE, tso.bo->inputs_.at(k), HIGH_PRIORITY);
1022+ }
1023+ }
1024+
1025+ // if soldier capacity is set to 0, we need to find out if the site is
1026+ // supplied enough to incrase the capacity to 1
1027+ if (tso.site->soldier_capacity() == 0) {
1028+
1029+ // First subsitute wares
1030+ int32_t filled = 0;
1031+ bool supplied_enough = true;
1032+ std::vector<WaresQueue*> const warequeues2 = tso.site->warequeues();
1033+ nr_warequeues = warequeues2.size();
1034+ for (size_t i = 0; i < nr_warequeues; ++i) {
1035+ if (tso.bo->substitute_inputs_.count(warequeues2[i]->get_ware()) > 0) {
1036+ filled += warequeues2[i]->get_filled();
1037+ }
1038+ }
1039+ if (filled < 5) {
1040+ supplied_enough = false;
1041+ }
1042+
1043+ // checking non subsitutes
1044+ for (size_t i = 0; i < nr_warequeues; ++i) {
1045+ if (tso.bo->substitute_inputs_.count(warequeues2[i]->get_ware()) == 0) {
1046+ const uint32_t required_amount
1047+ =
1048+ (warequeues2[i]->get_max_fill() < 5) ? warequeues2[i]->get_max_fill() : 5;
1049+ if (warequeues2[i]->get_filled() < required_amount) {
1050+ supplied_enough = false;
1051+ }
1052+ }
1053+ }
1054+
1055+ if (supplied_enough) {
1056+ game().send_player_change_soldier_capacity(*ts, 1);
1057+ }
1058 }
1059
1060 ts_without_trainers_ = 0; // zeroing
1061@@ -3713,12 +3914,12 @@
1062 int32_t unused1 = 0;
1063 uint16_t unused2 = 0;
1064
1065- mso.enemies_nearby_ = true;
1066+ mso.enemies_nearby_ = false;
1067
1068 // yes enemy is nearby, but still we must distinguish whether
1069 // he is accessible (over the land)
1070 if (other_player_accessible(
1071- vision + 4, &unused1, &unused2, ms->get_position(), WalkSearch::kOtherPlayers)) {
1072+ vision + 4, &unused1, &unused2, ms->get_position(), WalkSearch::kEnemy)) {
1073
1074 uint32_t const total_capacity = ms->max_soldier_capacity();
1075 uint32_t const target_capacity = ms->soldier_capacity();
1076@@ -3732,6 +3933,9 @@
1077 *ms, MilitarySite::kPrefersHeroes);
1078 changed = true;
1079 }
1080+
1081+ mso.enemies_nearby_ = true;
1082+ enemy_last_seen_ = gametime;
1083 } else { // otherwise decrease soldiers
1084 uint32_t const j = ms->soldier_capacity();
1085
1086@@ -3753,8 +3957,8 @@
1087
1088 /**
1089 * This function takes care about the unowned and opposing territory and
1090- * recalculates the priority for none military buildings depending on the
1091- * initialisation type of a defaultAI
1092+ * recalculates the priority for non-military buildings
1093+ * The goal is to minimize losses when teritory is lost
1094 *
1095 * \arg bf = BuildableField to be checked
1096 * \arg prio = priority until now.
1097@@ -3762,18 +3966,26 @@
1098 * \returns the recalculated priority
1099 */
1100 int32_t DefaultAI::recalc_with_border_range(const BuildableField& bf, int32_t prio) {
1101- // Prefer building space in the inner land.
1102-
1103+
1104+ // no change when priority is not positive number
1105+ if (prio <= 0) {
1106+ return prio;
1107+ }
1108+
1109+ // in unowned teritory, decreasing to 2/3
1110 if (bf.unowned_land_nearby_ > 15) {
1111- prio -= (bf.unowned_land_nearby_ - 15);
1112- }
1113-
1114- // Especially places near the frontier to the enemies are unlikely
1115- // NOTE take care about the type of computer player_. The more
1116- // NOTE aggressive a computer player_ is, the more important is
1117- // NOTE this check. So we add \var type as bonus.
1118- if (bf.enemy_nearby_ && prio > 0) {
1119- prio /= (3 + type_);
1120+ prio *= 2;
1121+ prio /= 3;
1122+ }
1123+
1124+ // to preserve positive score
1125+ if (prio == 0) {
1126+ prio = 1;
1127+ }
1128+
1129+ // Further decrease the score if enemy nearby
1130+ if (bf.enemy_nearby_) {
1131+ prio /= 2;
1132 }
1133
1134 return prio;
1135@@ -3952,6 +4164,25 @@
1136 }
1137 }
1138
1139+// This is called when soldier left the trainingsite
1140+// the purpose is to set soldier capacity to 0
1141+// (AI will then wait till training site is stocked)
1142+void DefaultAI::soldier_trained(const TrainingSite& site) {
1143+
1144+ for (TrainingSiteObserver & trainingsite_obs : trainingsites) {
1145+ if (trainingsite_obs.site == &site) {
1146+ if (trainingsite_obs.site->soldier_capacity() > 0) {
1147+ game().send_player_change_soldier_capacity(*trainingsite_obs.site,
1148+ - trainingsite_obs.site->soldier_capacity());
1149+ }
1150+ return;
1151+ }
1152+ }
1153+
1154+ log (" %d: Computer player error - trainingsite not found\n",
1155+ player_number());
1156+}
1157+
1158 // walk and search for teritorry controlled by other player
1159 // usually scanning radius is enough but sometimes we must walk to
1160 // verify that an enemy teritory is really accessible by land
1161@@ -3993,10 +4224,34 @@
1162 // a port location), but when testing (starting from) own military building
1163 // we must ignore own teritory, of course
1164 if (f->get_owned_by() > 0) {
1165- if (type == WalkSearch::kAnyPlayer ||
1166- (type == WalkSearch::kOtherPlayers && f->get_owned_by() != pn)) {
1167- *tested_fields = done.size();
1168- return true;
1169+
1170+ // if field is owned by anybody
1171+ if (type == WalkSearch::kAnyPlayer) {
1172+ *tested_fields = done.size();
1173+ return true;
1174+ }
1175+
1176+ // if anybody but not me
1177+ if (type == WalkSearch::kOtherPlayers && f->get_owned_by() != pn) {
1178+ *tested_fields = done.size();
1179+ return true;
1180+ }
1181+
1182+ // if owned by enemy
1183+ if (type == WalkSearch::kEnemy && f->get_owned_by() != pn) {
1184+ // for case I am not member of a team
1185+ if (player_->team_number() == 0) {
1186+ *tested_fields = done.size();
1187+ return true;
1188+ // if I am in team, testing if the same team
1189+ } else if (player_->team_number() > 0
1190+ &&
1191+ player_->team_number()
1192+ !=
1193+ game().get_player(f->get_owned_by())->team_number()) {
1194+ *tested_fields = done.size();
1195+ return true;
1196+ }
1197 }
1198 }
1199
1200@@ -4421,6 +4676,64 @@
1201 return supplied == bo.inputs_.size();
1202 }
1203
1204+// This calculates strength of vector of soldiers, f.e. soldiers in a building or
1205+// ones ready to attack
1206+int32_t DefaultAI::calculate_strength(const std::vector<Widelands::Soldier*> soldiers) {
1207+
1208+ if (soldiers.empty()) {
1209+ return 0;
1210+ }
1211+
1212+ Tribes tribe = Tribes::kNone;
1213+
1214+ if (soldiers.at(0)->get_owner()->tribe().name() == "atlanteans") {
1215+ tribe = Tribes::kAtlanteans;
1216+ } else if (soldiers.at(0)->get_owner()->tribe().name() == "barbarians") {
1217+ tribe = Tribes::kBarbarians;
1218+ } else if (soldiers.at(0)->get_owner()->tribe().name() == "empire") {
1219+ tribe = Tribes::kEmpire;
1220+ } else {
1221+ throw wexception("AI warning: Unable to calculate strenght for player of tribe %s",
1222+ soldiers.at(0)->get_owner()->tribe().name().c_str());
1223+ }
1224+
1225+ float hp = 0;
1226+ float al = 0;
1227+ float dl = 0;
1228+ float el = 0;
1229+ float final = 0;
1230+
1231+ for (Soldier * soldier : soldiers) {
1232+ switch (tribe) {
1233+ case (Tribes::kAtlanteans):
1234+ hp = 135 + 40 * soldier->get_hp_level();
1235+ al = 14 + 8 * soldier->get_attack_level();
1236+ dl = static_cast<float>(94 - 8 * soldier->get_defense_level()) / 100;
1237+ el = static_cast<float>(70 - 17 * soldier->get_evade_level()) / 100;
1238+ break;
1239+ case (Tribes::kBarbarians):
1240+ hp += 130 + 28 * soldier->get_hp_level();
1241+ al += 14 + 7 * soldier->get_attack_level();
1242+ dl += static_cast<float>(97 - 8 * soldier->get_defense_level()) / 100;
1243+ el += static_cast<float>(75 - 15 * soldier->get_evade_level()) / 100;
1244+ break;
1245+ case (Tribes::kEmpire):
1246+ hp += 130 + 21 * soldier->get_hp_level();
1247+ al += 14 + 8 * soldier->get_attack_level();
1248+ dl += static_cast<float>(95 - 8 * soldier->get_defense_level()) / 100;
1249+ el += static_cast<float>(70 - 16 * soldier->get_evade_level()) / 100;
1250+ break;
1251+ default:
1252+ assert (false);
1253+ }
1254+
1255+ final += (al * hp) / (dl * el);
1256+ }
1257+
1258+ // 2500 is aproximate strength of one unpromoted soldier
1259+ return static_cast<int32_t>(final / 2500);
1260+}
1261+
1262 bool DefaultAI::check_enemy_sites(uint32_t const gametime) {
1263
1264 Map& map = game().map();
1265@@ -4437,6 +4750,27 @@
1266 // receiving games statistics and parsing it (reading latest entry)
1267 const Game::GeneralStatsVector& genstats = game().get_general_statistics();
1268
1269+ // summing team power, creating team_power std::map of team_number:strength
1270+ std::map<TeamNumber, uint32_t> team_power;
1271+ for (uint8_t j = 1; j <= plr_in_game; ++j) {
1272+ TeamNumber const tm = game().get_player(j)->team_number();
1273+ if (tm == 0) {
1274+ continue;
1275+ }
1276+ // for case this is new team
1277+ if (team_power.count(tm) == 0) {
1278+ // adding this team (number) to vector
1279+ team_power[tm] = 0;
1280+ }
1281+ try {
1282+ team_power[tm] += genstats.at(j - 1).miltary_strength.back();
1283+ } catch (const std::out_of_range&) {
1284+ log("ComputerPlayer(%d): genstats entry missing - size :%d\n",
1285+ player_number(),
1286+ static_cast<unsigned int>(genstats.size()));
1287+ }
1288+ }
1289+
1290 // defining treshold ratio of own_strenght/enemy's_strength
1291 uint32_t treshold_ratio = 100;
1292 if (type_ == AGGRESSIVE) {
1293@@ -4446,25 +4780,49 @@
1294 treshold_ratio = 120;
1295 }
1296
1297- // now we test all players which one are 'attackable'
1298+ uint32_t my_power = 0;
1299+ try {
1300+ my_power = genstats.at(pn - 1).miltary_strength.back();
1301+ } catch (const std::out_of_range&) {
1302+ log("ComputerPlayer(%d): genstats entry missing - size :%d\n",
1303+ player_number(),
1304+ static_cast<unsigned int>(genstats.size()));
1305+ }
1306+ // adding power of team (minus my power) divided by 2
1307+ // (if I am a part of a team of course)
1308+ if (game().get_player(pn)->team_number() > 0) {
1309+ my_power += (team_power[game().get_player(pn)->team_number()] - my_power) / 2;
1310+ }
1311+
1312+ // now we test all players to identify 'attackable' ones
1313 for (uint8_t j = 1; j <= plr_in_game; ++j) {
1314- if (pn == j) { // its me
1315+ // if it's me
1316+ if (pn == j) {
1317+ player_attackable[j - 1] = false;
1318+ continue;
1319+ }
1320+ // if we are the same team
1321+ if (game().get_player(pn)->team_number() > 0 &&
1322+ game().get_player(pn)->team_number() == game().get_player(j)->team_number()) {
1323 player_attackable[j - 1] = false;
1324 continue;
1325 }
1326
1327+ // now we compare strength
1328 try {
1329- // It seems that under some circumstances genstats can be empty.
1330- // So, to avoid crash, the AI tests its content first.
1331- if (genstats.at(j - 1).miltary_strength.empty()) {
1332- log("ComputerPlayer(%d): miltary_strength is empty\n", player_number());
1333- player_attackable.at(j - 1) = false;
1334- // Avoid division by zero
1335- } else if (genstats.at(j - 1).miltary_strength.back() == 0) {
1336+ // strength of the other player
1337+ uint32_t players_power = 0;
1338+ if (!genstats.at(j - 1).miltary_strength.empty()) {
1339+ players_power += genstats.at(j - 1).miltary_strength.back();
1340+ }
1341+ // +power of team (if member of a team)
1342+ if (game().get_player(j)->team_number() > 0) {
1343+ players_power += (team_power[game().get_player(j)->team_number()] - players_power) / 2;
1344+ }
1345+
1346+ if (players_power == 0) {
1347 player_attackable.at(j - 1) = true;
1348- // Check threshold
1349- } else if ((genstats.at(pn - 1).miltary_strength.back() * 100 /
1350- genstats.at(j - 1).miltary_strength.back()) > treshold_ratio) {
1351+ } else if (my_power * 100 / players_power > treshold_ratio) {
1352 player_attackable.at(j - 1) = true;
1353 } else {
1354 player_attackable.at(j - 1) = false;
1355@@ -4532,10 +4890,10 @@
1356 count += 1;
1357
1358 site->second.last_tested = gametime;
1359- uint8_t defenders = 0;
1360+ uint8_t defenders_strength = 0;
1361 bool is_warehouse = false;
1362 bool is_attackable = false;
1363- uint16_t onwer_number = 100;
1364+ uint16_t owner_number = 100;
1365
1366 // testing if we can attack the building - result is a flag
1367 // if we dont get a flag, we remove the building from observers list
1368@@ -4544,23 +4902,30 @@
1369 Flag* flag = nullptr;
1370 if (upcast(MilitarySite, bld, f.field->get_immovable())) {
1371 if (player_->is_hostile(bld->owner())) {
1372- defenders = bld->present_soldiers().size();
1373+ std::vector<Soldier *> defenders;
1374+ defenders = bld->present_soldiers();
1375+ defenders_strength = calculate_strength(defenders);
1376+
1377 flag = &bld->base_flag();
1378 if (bld->can_attack()) {
1379 is_attackable = true;
1380 }
1381- onwer_number = bld->owner().player_number();
1382+ owner_number = bld->owner().player_number();
1383 }
1384 }
1385 if (upcast(Warehouse, Wh, f.field->get_immovable())) {
1386 if (player_->is_hostile(Wh->owner())) {
1387- defenders = Wh->present_soldiers().size();
1388+
1389+ std::vector<Soldier *> defenders;
1390+ defenders = Wh->present_soldiers();
1391+ defenders_strength = calculate_strength(defenders);
1392+
1393 flag = &Wh->base_flag();
1394 is_warehouse = true;
1395 if (Wh->can_attack()) {
1396 is_attackable = true;
1397 }
1398- onwer_number = Wh->owner().player_number();
1399+ owner_number = Wh->owner().player_number();
1400 }
1401 }
1402
1403@@ -4588,61 +4953,75 @@
1404
1405 // can we attack:
1406 if (is_attackable) {
1407- site->second.attack_soldiers = player_->find_attack_soldiers(*flag);
1408+ std::vector<Soldier *> attackers;
1409+ player_->find_attack_soldiers(*flag, &attackers);
1410+ int32_t strength = calculate_strength(attackers);
1411+
1412+ site->second.attack_soldiers_strength = strength;
1413 } else {
1414- site->second.attack_soldiers = 0;
1415+ site->second.attack_soldiers_strength = 0;
1416 }
1417
1418- site->second.defenders = defenders;
1419-
1420- if (site->second.attack_soldiers > 0) {
1421- site->second.score = site->second.attack_soldiers - site->second.defenders / 2;
1422-
1423- if (!is_warehouse)
1424- site->second.score -= 1;
1425+ site->second.defenders_strength = defenders_strength;
1426+
1427+ if (site->second.attack_soldiers_strength > 0
1428+ &&
1429+ player_attackable[owner_number - 1]) {
1430+ site->second.score = site->second.attack_soldiers_strength - site->second.defenders_strength / 2;
1431+
1432+ if (is_warehouse) {
1433+ site->second.score += 2;
1434+ } else {
1435+ site->second.score -= 2;
1436+ }
1437
1438 // here is some differentiation based on "character" of a player
1439 if (type_ == NORMAL) {
1440- site->second.score -= 1;
1441- site->second.score -= vacant_mil_positions_ / 10;
1442+ site->second.score -= 3;
1443+ site->second.score -= vacant_mil_positions_ / 8;
1444 } else if (type_ == DEFENSIVE) {
1445- site->second.score -= 2;
1446- site->second.score -= vacant_mil_positions_ / 5;
1447+ site->second.score -= 6;
1448+ site->second.score -= vacant_mil_positions_ / 4;
1449 } else { //=AGRESSIVE
1450- site->second.score -= vacant_mil_positions_ / 15;
1451+ site->second.score -= vacant_mil_positions_ / 16;
1452 }
1453 if (site->second.mines_nearby == ExtendedBool::kFalse) {
1454 site->second.score -= 1;
1455+ } else {
1456+ site->second.score += 1;
1457 }
1458 // we dont want to attack multiple players at the same time too eagerly
1459- if (onwer_number != last_attacked_player_) {
1460+ if (owner_number != last_attacked_player_) {
1461 site->second.score -= 3;
1462 }
1463 // if we dont have mines yet
1464 if (mines_.size() <= 2) {
1465- site->second.score -= 2;
1466+ site->second.score -= 8;
1467 }
1468 // also we should have at least some training sites
1469 if ((ts_basic_count_ + ts_advanced_count_) == 0) {
1470- site->second.score -= 2;
1471+ site->second.score -= 4;
1472 }
1473 // treating no attack score
1474 if (site->second.no_attack_counter < 0) {
1475+ // we cannot attack yet
1476 site->second.score = 0;
1477+ // but increase the counter by 1
1478 site->second.no_attack_counter += 1;
1479 }
1480+
1481 } else {
1482 site->second.score = 0;
1483 } // or the score will remain 0
1484
1485- if (site->second.score > 0 && player_attackable[onwer_number - 1]) {
1486+ if (site->second.score > 0) {
1487 if (site->second.score > best_score) {
1488 best_score = site->second.score;
1489 best_target = site->first;
1490 }
1491 }
1492
1493- if (site->second.attack_soldiers > 0) {
1494+ if (site->second.attack_soldiers_strength > 0) {
1495 site->second.last_time_attackable = gametime;
1496 }
1497 if (site->second.last_time_attackable + 20 * 60 * 1000 < gametime) {
1498@@ -4743,6 +5122,19 @@
1499 DueTask = task.first;
1500 }
1501 }
1502+ if ((gametime - oldestTaskTime) > 5000) {
1503+ scheduler_delay_counter_ += 1;
1504+ } else {
1505+ scheduler_delay_counter_ = 0;
1506+ }
1507+
1508+ if (scheduler_delay_counter_ > 10) {
1509+ log(" %d: AI: game speed too high, jobs are too late (now %2d seconds)\n",
1510+ player_number(),
1511+ static_cast<int32_t>((gametime - oldestTaskTime) / 1000));
1512+ scheduler_delay_counter_ = 0;
1513+ }
1514+
1515 return DueTask;
1516 }
1517
1518@@ -4766,6 +5158,7 @@
1519 const std::vector<std::string> materials = {"coal",
1520 "log",
1521 "ironore",
1522+ "iron",
1523 "marble",
1524 "plank",
1525 "water",
1526@@ -4791,12 +5184,13 @@
1527 }
1528 summary = summary + materials.at(j) + ", ";
1529 }
1530- log(" %1d: Buildings: Pr:%3lu, Ml:%3lu, Mi:%2lu, Wh:%2lu, Po:%2u. Missing: %s\n",
1531+
1532+ log(" %1d: Buildings: Pr:%3u, Ml:%3u, Mi:%2u, Wh:%2u, Po:%u. Missing: %s\n",
1533 pn,
1534- productionsites.size(),
1535- militarysites.size(),
1536- mines_.size(),
1537- warehousesites.size() - num_ports,
1538+ static_cast<uint32_t>(productionsites.size()),
1539+ static_cast<uint32_t>(militarysites.size()),
1540+ static_cast<uint32_t>(mines_.size()),
1541+ static_cast<uint32_t>(warehousesites.size() - num_ports),
1542 num_ports,
1543 summary.c_str());
1544 }
1545
1546=== modified file 'src/ai/defaultai.h'
1547--- src/ai/defaultai.h 2015-05-05 20:00:21 +0000
1548+++ src/ai/defaultai.h 2015-07-28 19:26:44 +0000
1549@@ -30,6 +30,8 @@
1550 #include "base/i18n.h"
1551 #include "logic/immovable.h"
1552 #include "logic/ship.h"
1553+#include "logic/soldier.h"
1554+#include "logic/trainingsite.h"
1555
1556 namespace Widelands {
1557 struct Road;
1558@@ -79,8 +81,10 @@
1559 DEFENSIVE = 0,
1560 };
1561
1562- enum class WalkSearch : uint8_t {kAnyPlayer, kOtherPlayers};
1563+ enum class WalkSearch : uint8_t {kAnyPlayer, kOtherPlayers, kEnemy};
1564+ enum class WoodPolicy : uint8_t {kDismantleRangers, kStopRangers, kStartRangers, kBuildRangers};
1565 enum class NewShip : uint8_t {kBuilt, kFoundOnLoad};
1566+ enum class PerfEvaluation : uint8_t {kForConstruction, kForDismantle};
1567 enum class ScheduleTasks : uint8_t {
1568 kBbuildableFieldsCheck,
1569 kMineableFieldsCheck,
1570@@ -108,6 +112,12 @@
1571 kExpansion,
1572 kPushExpansion
1573 };
1574+ enum class Tribes : uint8_t {
1575+ kNone,
1576+ kBarbarians,
1577+ kAtlanteans,
1578+ kEmpire
1579+ };
1580
1581 /// Implementation for Aggressive
1582 struct AggressiveImpl : public ComputerPlayer::Implementation {
1583@@ -199,6 +209,8 @@
1584 bool check_ships(uint32_t);
1585 bool check_enemy_sites(uint32_t);
1586 void print_stats(uint32_t);
1587+ // return single number of strength of vector of soldiers
1588+ int32_t calculate_strength(const std::vector<Widelands::Soldier*>);
1589 uint32_t get_stocklevel_by_hint(size_t);
1590 uint32_t get_stocklevel(BuildingObserver&);
1591 uint32_t get_warehoused_stock(Widelands::WareIndex wt);
1592@@ -233,6 +245,10 @@
1593 void gain_ship(Widelands::Ship&, NewShip);
1594 void expedition_management(ShipObserver&);
1595 void out_of_resources_site(const Widelands::ProductionSite&);
1596+ void soldier_trained(const Widelands::TrainingSite&);
1597+ bool is_productionsite_needed(int32_t outputs,
1598+ int32_t performance,
1599+ PerfEvaluation purpose);
1600
1601 bool check_supply(const BuildingObserver&);
1602
1603@@ -260,6 +276,8 @@
1604 // check ms in this interval - will auto-adjust
1605 uint32_t enemysites_check_delay_;
1606
1607+ WoodPolicy wood_policy_;
1608+
1609 std::list<Widelands::FCoords> unusable_fields;
1610 std::list<BuildableField*> buildable_fields;
1611 std::list<BlockedField> blocked_fields;
1612@@ -311,13 +329,24 @@
1613 // it decreases with failed scans
1614 int32_t spots_; // sum of buildable fields
1615 int32_t vacant_mil_positions_; // sum of vacant positions in militarysites and training sites
1616- //statistics for training sites per type
1617+ // statistics for training sites per type
1618 uint8_t ts_basic_count_;
1619 uint8_t ts_basic_const_count_;
1620 uint8_t ts_advanced_count_;
1621 uint8_t ts_advanced_const_count_;
1622 uint8_t ts_without_trainers_;
1623
1624+ // this is helping counter to track how many scheduler tasks are too delayed
1625+ // the purpose is to print out a warning that the game is pacing too fast
1626+ int32_t scheduler_delay_counter_;
1627+
1628+ // this is a bunch of patterns that have to identify weapons and armors for input queues of trainingsites
1629+ std::vector<std::string> const armors_and_weapons =
1630+ {"ax", "lance", "armor", "helm", "lance", "trident", "tabard", "shield", "mask"};
1631+ // some buildings can be upgraded even when they are only one
1632+ // now only microbrewery get this special treatment
1633+ const char* preferred_upgrade[1] = {"micro-brewery"};
1634+
1635 enum {kReprioritize, kStopShipyard, kStapShipyard};
1636
1637 std::vector<int16_t> marineTaskQueue_;
1638@@ -327,6 +356,8 @@
1639 std::unique_ptr<Notifications::Subscriber<Widelands::NoteImmovable>> immovable_subscriber_;
1640 std::unique_ptr<Notifications::Subscriber<Widelands::NoteProductionSiteOutOfResources>>
1641 outofresource_subscriber_;
1642+ std::unique_ptr<Notifications::Subscriber<Widelands::NoteTrainingSiteSoldierTrained>>
1643+ soldiertrained_subscriber_;
1644 std::unique_ptr<Notifications::Subscriber<Widelands::NoteShipMessage>> shipnotes_subscriber_;
1645 };
1646
1647
1648=== modified file 'src/logic/trainingsite.cc'
1649--- src/logic/trainingsite.cc 2015-05-03 11:39:11 +0000
1650+++ src/logic/trainingsite.cc 2015-07-28 19:26:44 +0000
1651@@ -320,8 +320,9 @@
1652 delete m_soldier_request;
1653 m_soldier_request = nullptr;
1654
1655- while (m_soldiers.size() > m_capacity)
1656+ while (m_soldiers.size() > m_capacity) {
1657 drop_soldier(**m_soldiers.rbegin());
1658+ }
1659 }
1660 }
1661
1662@@ -432,6 +433,7 @@
1663
1664 // Schedule, so that we can call new soldiers on next act()
1665 schedule_act(game, 100);
1666+ Notifications::publish(NoteTrainingSiteSoldierTrained(this, get_owner()));
1667 }
1668
1669
1670
1671=== modified file 'src/logic/trainingsite.h'
1672--- src/logic/trainingsite.h 2014-09-19 12:54:54 +0000
1673+++ src/logic/trainingsite.h 2015-07-28 19:26:44 +0000
1674@@ -218,6 +218,24 @@
1675
1676 };
1677
1678+/**
1679+ * Note to be published when a soldier is leaving the training center
1680+ */
1681+// A note we're using to notify the AI
1682+struct NoteTrainingSiteSoldierTrained {
1683+ CAN_BE_SENT_AS_NOTE(NoteId::TrainingSiteSoldierTrained)
1684+
1685+ // The trainingsite from where soldier is leaving.
1686+ TrainingSite* ts;
1687+
1688+ // The player that owns the ttraining site.
1689+ Player * player;
1690+
1691+ NoteTrainingSiteSoldierTrained(TrainingSite* const init_ts, Player* init_player)
1692+ : ts(init_ts), player(init_player) {
1693+ }
1694+};
1695+
1696 }
1697
1698 #endif // end of include guard: WL_LOGIC_TRAININGSITE_H
1699
1700=== modified file 'src/notifications/note_ids.h'
1701--- src/notifications/note_ids.h 2015-03-01 09:21:20 +0000
1702+++ src/notifications/note_ids.h 2015-07-28 19:26:44 +0000
1703@@ -32,6 +32,7 @@
1704 FieldPossession,
1705 FieldTransformed,
1706 ProductionSiteOutOfResources,
1707+ TrainingSiteSoldierTrained,
1708 ShipMessage,
1709 GraphicResolutionChanged,
1710
1711
1712=== modified file 'tribes/atlanteans/fish_breeders_house/conf'
1713--- tribes/atlanteans/fish_breeders_house/conf 2014-08-02 19:55:23 +0000
1714+++ tribes/atlanteans/fish_breeders_house/conf 2015-07-28 19:26:44 +0000
1715@@ -3,7 +3,7 @@
1716 [aihints]
1717 needs_water=true
1718 renews_map_resource=fish
1719-prohibited_till=900
1720+prohibited_till=300
1721
1722 [buildcost]
1723 log=1
1724
1725=== modified file 'tribes/atlanteans/sawmill/conf'
1726--- tribes/atlanteans/sawmill/conf 2014-08-02 19:55:23 +0000
1727+++ tribes/atlanteans/sawmill/conf 2015-07-28 19:26:44 +0000
1728@@ -2,7 +2,7 @@
1729 output=planks
1730
1731 [aihints]
1732-forced_after=300
1733+forced_after=120
1734 prohibited_till=60
1735
1736 [buildcost]
1737
1738=== modified file 'tribes/atlanteans/smokery/conf'
1739--- tribes/atlanteans/smokery/conf 2014-08-02 19:55:23 +0000
1740+++ tribes/atlanteans/smokery/conf 2015-07-28 19:26:44 +0000
1741@@ -4,7 +4,7 @@
1742
1743 [aihints]
1744 forced_after=900
1745-prohibited_till=60
1746+prohibited_till=180
1747
1748 [buildcost]
1749 log=1
1750
1751=== modified file 'tribes/atlanteans/spidercloth/conf'
1752--- tribes/atlanteans/spidercloth/conf 2014-07-29 20:48:19 +0000
1753+++ tribes/atlanteans/spidercloth/conf 2015-07-28 19:26:44 +0000
1754@@ -1,7 +1,7 @@
1755 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.
1756
1757 default_target_quantity=20
1758-preciousness=5
1759+preciousness=7
1760
1761 [idle]
1762 pics=idle.png
1763
1764=== modified file 'tribes/atlanteans/spiderfarm/conf'
1765--- tribes/atlanteans/spiderfarm/conf 2014-08-02 19:55:23 +0000
1766+++ tribes/atlanteans/spiderfarm/conf 2015-07-28 19:26:44 +0000
1767@@ -37,5 +37,5 @@
1768 hotspot=87 75
1769
1770 [aihints]
1771-forced_after=60
1772+forced_after=150
1773 prohibited_till=60
1774
1775=== modified file 'tribes/atlanteans/weaving-mill/conf'
1776--- tribes/atlanteans/weaving-mill/conf 2014-08-02 19:55:23 +0000
1777+++ tribes/atlanteans/weaving-mill/conf 2015-07-28 19:26:44 +0000
1778@@ -4,7 +4,7 @@
1779 output=golden_tabard
1780
1781 [aihints]
1782-forced_after=60
1783+forced_after=150
1784 prohibited_till=60
1785
1786 [buildcost]
1787
1788=== modified file 'tribes/barbarians/farm/conf'
1789--- tribes/barbarians/farm/conf 2015-06-10 07:00:10 +0000
1790+++ tribes/barbarians/farm/conf 2015-07-28 19:26:44 +0000
1791@@ -3,6 +3,7 @@
1792
1793 [aihints]
1794 space_consumer=true
1795+forced_after=900
1796
1797 [buildcost]
1798 log=4

Subscribers

People subscribed via source and target branches

to status/vote changes: