Merge lp:~widelands-dev/widelands/ai-military-changes into lp:widelands
- ai-military-changes
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 7436 |
Proposed branch: | lp:~widelands-dev/widelands/ai-military-changes |
Merge into: | lp:widelands |
Diff against target: |
2528 lines (+937/-492) 13 files modified
src/ai/ai_help_structs.h (+76/-16) src/ai/ai_hints.cc (+14/-0) src/ai/ai_hints.h (+8/-0) src/ai/defaultai.cc (+795/-460) src/ai/defaultai.h (+28/-13) tribes/atlanteans/dungeon/conf (+3/-0) tribes/atlanteans/labyrinth/conf (+1/-1) tribes/barbarians/battlearena/conf (+3/-0) tribes/barbarians/trainingcamp/conf (+1/-1) tribes/empire/arena/conf (+3/-0) tribes/empire/colosseum/conf (+3/-0) tribes/empire/piggery/conf (+1/-0) tribes/empire/trainingcamp/conf (+1/-1) |
To merge this branch: | bzr merge lp:~widelands-dev/widelands/ai-military-changes |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
SirVer | Approve | ||
Review via email: mp+253881@code.launchpad.net |
Commit message
Description of the change
Another bunch of AI changes, this time only files within src/ai/ were changed. Reworked were:
- attacking
- training sites
- mines
- further small things
TiborB (tiborb95) wrote : | # |
re type of trainingsites: type 1 needs less resources (to build and to operate) and type 2 needs more (including gold, weapons), so type 1 can and should be built sooner. But I can rename this classification to "basic" and "advanced", though this is not ideal neither.
GunChleoc (gunchleoc) wrote : | # |
How about "needs_few_wares" and "needs_
TiborB (tiborb95) wrote : | # |
@GunChleoc - you undestand that this will be completely hidden form everybody except handful of developers who will ever look at code. And perhaps few other peoples who will tinker with config files...
I already reworked this so I am not eager much to rework this once more, but if you really want this... :)
GunChleoc (gunchleoc) wrote : | # |
Just imagine that you get hit by a bus tomorrow and that in 10 year's time another developer is trying to understand your code. So yes, just a handful of developers is important, which is why I posted my idea of how they might be named ;)
TiborB (tiborb95) wrote : | # |
:) let make an agreement: If there will be another requests to change something I will change also this, can be?
SirVer (sirver) wrote : | # |
> :) let make an agreement: If there will be another requests to change something I will change also this, can be?
Pfffff. I changed ts_type to trainingsite_type - I took the time, it took me 36 seconds. Should not have taken you much longer than typing this comment.
I think basic and advanced is fine - it allows for other types to be added in non-awkwardly later and it gives more possibility to grep for it through the source code which will make it easier for future devs to pick up on it.
lgtm.
TiborB (tiborb95) wrote : | # |
> I changed ts_type to trainingsite_type - I took the time, it took me 36 seconds.
ts_type is definitely "safe" string to use sed (or other tool) to replace all occurrences, but to use sed to replace strings 'basic' and 'advanced' - it would be quite different
Preview Diff
1 | === modified file 'src/ai/ai_help_structs.h' |
2 | --- src/ai/ai_help_structs.h 2015-03-05 20:57:07 +0000 |
3 | +++ src/ai/ai_help_structs.h 2015-03-26 20:42:28 +0000 |
4 | @@ -23,6 +23,7 @@ |
5 | #include <list> |
6 | #include <unordered_set> |
7 | |
8 | +#include "ai/ai_hints.h" |
9 | #include "economy/flag.h" |
10 | #include "economy/road.h" |
11 | #include "logic/checkstep.h" |
12 | @@ -40,7 +41,7 @@ |
13 | class ProductionSite; |
14 | class MilitarySite; |
15 | |
16 | -enum class ExtendedBool : uint8_t {kUnset, kTrue, kFalse }; |
17 | +enum class ExtendedBool : uint8_t {kUnset, kTrue, kFalse}; |
18 | |
19 | struct CheckStepRoadAI { |
20 | CheckStepRoadAI(Player* const pl, uint8_t const mc, bool const oe) |
21 | @@ -110,6 +111,22 @@ |
22 | } |
23 | }; |
24 | |
25 | +// Sometimes we need to know how many nodes our allies owns |
26 | +struct FindNodeAllyOwned { |
27 | + bool accept(const Map&, const FCoords& fc) const { |
28 | + return (fc.field->nodecaps() & MOVECAPS_WALK) && (fc.field->get_owned_by() != 0) && |
29 | + (fc.field->get_owned_by() != pn) && |
30 | + !player_->is_hostile(*game.get_player(fc.field->get_owned_by())); |
31 | + } |
32 | + |
33 | + Player* player_; |
34 | + Game& game; |
35 | + PlayerNumber pn; |
36 | + |
37 | + FindNodeAllyOwned(Player* p, Game& g, PlayerNumber n) : player_(p), game(g), pn(n) { |
38 | + } |
39 | +}; |
40 | + |
41 | // When looking for unowned terrain to acquire, we must |
42 | // pay speciall attention to fields where mines can be built. |
43 | // Fields should be completely unowned |
44 | @@ -148,8 +165,9 @@ |
45 | |
46 | bool accept(const Map& /* map */, const FCoords& coord) const { |
47 | return (world_.terrain_descr(coord.field->terrain_d()).get_is() & |
48 | - TerrainDescription::Type::kWater) || |
49 | - (world_.terrain_descr(coord.field->terrain_r()).get_is() & TerrainDescription::Type::kWater); |
50 | + TerrainDescription::Type::kWater) || |
51 | + (world_.terrain_descr(coord.field->terrain_r()).get_is() & |
52 | + TerrainDescription::Type::kWater); |
53 | } |
54 | |
55 | private: |
56 | @@ -213,7 +231,7 @@ |
57 | struct BuildableField { |
58 | Widelands::FCoords coords; |
59 | |
60 | - uint32_t next_update_due_; |
61 | + uint32_t field_info_expiration_; |
62 | |
63 | bool preferred_; |
64 | bool enemy_nearby_; |
65 | @@ -232,7 +250,7 @@ |
66 | uint8_t space_consumers_nearby_; |
67 | // to manage the military better following variables exists: |
68 | // capacity of nearby buildings: |
69 | - int16_t military_capacity_; |
70 | + int16_t area_military_capacity_; |
71 | // distance to near buldings: |
72 | int16_t military_loneliness_; |
73 | // count of military buildings in construction |
74 | @@ -241,7 +259,7 @@ |
75 | // are construction sites that will change this once they are built |
76 | int16_t military_in_constr_nearby_; |
77 | // actual count of soldiers in nearby buldings |
78 | - int16_t military_presence_; |
79 | + int16_t area_military_presence_; |
80 | // stationed (manned) military buildings nearby |
81 | int16_t military_stationed_; |
82 | // stationed (manned) military buildings nearby |
83 | @@ -256,7 +274,7 @@ |
84 | |
85 | BuildableField(const Widelands::FCoords& fc) |
86 | : coords(fc), |
87 | - next_update_due_(0), |
88 | + field_info_expiration_(20000), |
89 | preferred_(false), |
90 | enemy_nearby_(0), |
91 | unowned_land_nearby_(0), |
92 | @@ -277,10 +295,10 @@ |
93 | critters_nearby_(-1), |
94 | ground_water_(1), |
95 | space_consumers_nearby_(0), |
96 | - military_capacity_(0), |
97 | + area_military_capacity_(0), |
98 | military_loneliness_(1000), |
99 | military_in_constr_nearby_(0), |
100 | - military_presence_(0), |
101 | + area_military_presence_(0), |
102 | military_stationed_(0), |
103 | military_unstationed_(0), |
104 | is_portspace_(false), |
105 | @@ -292,7 +310,7 @@ |
106 | struct MineableField { |
107 | Widelands::FCoords coords; |
108 | |
109 | - uint32_t next_update_due_; |
110 | + uint32_t field_info_expiration_; |
111 | |
112 | bool preferred_; |
113 | |
114 | @@ -302,7 +320,7 @@ |
115 | |
116 | MineableField(const Widelands::FCoords& fc) |
117 | : coords(fc), |
118 | - next_update_due_(0), |
119 | + field_info_expiration_(20000), |
120 | preferred_(false), |
121 | mines_nearby_(0), |
122 | same_mine_fields_nearby_(0) { |
123 | @@ -346,14 +364,16 @@ |
124 | bool is_fisher_; // need to identify fishers |
125 | bool is_port_; |
126 | bool is_shipyard_; |
127 | - bool space_consumer_; // farm, vineyard... = true |
128 | - bool expansion_type_; // military building used that can be used to control area |
129 | - bool fighting_type_; // military building built near enemies |
130 | - bool mountain_conqueror_; // military building built near mountains |
131 | + bool space_consumer_; // farm, vineyard... = true |
132 | + bool expansion_type_; // military building used that can be used to control area |
133 | + bool fighting_type_; // military building built near enemies |
134 | + bool mountain_conqueror_; // military building built near mountains |
135 | uint32_t prohibited_till_; // do not build before (ms) |
136 | uint32_t forced_after_; // do not wait until ware is needed |
137 | + TrainingSiteType trainingsite_type_; |
138 | |
139 | - bool unoccupied_; // |
140 | + bool unoccupied_; |
141 | + uint16_t unconnected_; // to any warehouse (count of such buildings) |
142 | |
143 | int32_t mines_; // type of resource it mines_ |
144 | uint16_t mines_percent_; // % of res it can mine |
145 | @@ -429,4 +449,44 @@ |
146 | uint8_t preciousness_; |
147 | }; |
148 | |
149 | +//Computer player does not get notification messages about enemy militarysites |
150 | +//and warehouses, so following is collected based on observation |
151 | +//It is conventient to have some information preserved, like nearby minefields, |
152 | +//when it was attacked, whether it is warehouse and so on |
153 | +//Also AI test more such targets when considering attack and calculated score is |
154 | +//is stored in the observer |
155 | +struct EnemySiteObserver { |
156 | + bool warehouse_; |
157 | + uint8_t attack_soldiers; |
158 | + uint8_t defenders; |
159 | + uint8_t stationed_soldiers; |
160 | + uint32_t last_time_attackable; |
161 | + uint32_t last_tested; |
162 | + int16_t score; |
163 | + bool warehouse; |
164 | + Widelands::ExtendedBool mines_nearby; |
165 | + int16_t no_attack_counter; |
166 | + |
167 | + EnemySiteObserver() |
168 | + : warehouse_(false), |
169 | + attack_soldiers(0), |
170 | + stationed_soldiers(0), |
171 | + last_time_attackable(std::numeric_limits<uint32_t>::max()), |
172 | + last_tested(0), |
173 | + score(0), |
174 | + mines_nearby(Widelands::ExtendedBool::kUnset), |
175 | + no_attack_counter(0) { |
176 | + } |
177 | +}; |
178 | + |
179 | +// as all mines have 3 levels, AI does not know total count of mines per mined material |
180 | +// so this observer will be used for this |
181 | +struct MineTypesObserver { |
182 | + uint16_t in_construction; |
183 | + uint16_t finished; |
184 | + |
185 | + MineTypesObserver() : in_construction(0), finished(0) { |
186 | + } |
187 | +}; |
188 | + |
189 | #endif // end of include guard: WL_AI_AI_HELP_STRUCTS_H |
190 | |
191 | === modified file 'src/ai/ai_hints.cc' |
192 | --- src/ai/ai_hints.cc 2015-03-05 20:57:07 +0000 |
193 | +++ src/ai/ai_hints.cc 2015-03-26 20:42:28 +0000 |
194 | @@ -34,6 +34,7 @@ |
195 | prohibited_till_(section ? section->get_natural("prohibited_till", 0) : 0), |
196 | forced_after_(section ? section->get_natural("forced_after", 864000) : 0), // 10 days default |
197 | mines_percent_(section ? section->get_int("mines_percent", 100) : 0) |
198 | + |
199 | { |
200 | if (section) { |
201 | if (section->has_val("renews_map_resource")) |
202 | @@ -41,4 +42,17 @@ |
203 | if (section->has_val("mines")) |
204 | mines_ = section->get_string("mines"); |
205 | } |
206 | + if (section) { |
207 | + if (!section->has_val("trainingsite_type")) { |
208 | + trainingsite_type_ = TrainingSiteType::kNoTS; |
209 | + } else { |
210 | + if (!strcmp(section ? section->get_string("trainingsite_type", "basic") : "basic", "basic")) { |
211 | + trainingsite_type_ = TrainingSiteType::kBasic; |
212 | + } else if (!strcmp(section ? section->get_string("trainingsite_type", "basic") : "basic", "advanced")) { |
213 | + trainingsite_type_ = TrainingSiteType::kAdvanced; |
214 | + } else { |
215 | + trainingsite_type_ = TrainingSiteType::kNoTS; |
216 | + } |
217 | + } |
218 | + } |
219 | } |
220 | |
221 | === modified file 'src/ai/ai_hints.h' |
222 | --- src/ai/ai_hints.h 2015-03-05 20:57:07 +0000 |
223 | +++ src/ai/ai_hints.h 2015-03-26 20:42:28 +0000 |
224 | @@ -24,9 +24,12 @@ |
225 | #include <string> |
226 | |
227 | #include "base/macros.h" |
228 | +#include "base/log.h" |
229 | |
230 | class Section; |
231 | |
232 | +enum class TrainingSiteType : uint8_t {kNoTS = 0, kBasic = 1, kAdvanced = 2}; |
233 | + |
234 | /// This struct is used to read out the data given in [aihints] section of a |
235 | /// buildings conf file. It is used to tell the computer player about the |
236 | /// special properties of a building. |
237 | @@ -94,6 +97,10 @@ |
238 | return mines_percent_; |
239 | } |
240 | |
241 | + TrainingSiteType get_trainingsite_type() const { |
242 | + return trainingsite_type_; |
243 | + } |
244 | + |
245 | private: |
246 | std::string renews_map_resource_; |
247 | std::string mines_; |
248 | @@ -109,6 +116,7 @@ |
249 | int32_t prohibited_till_; |
250 | int32_t forced_after_; |
251 | uint8_t mines_percent_; |
252 | + TrainingSiteType trainingsite_type_; |
253 | |
254 | DISALLOW_COPY_AND_ASSIGN(BuildingHints); |
255 | }; |
256 | |
257 | === modified file 'src/ai/defaultai.cc' |
258 | --- src/ai/defaultai.cc 2015-03-06 07:36:40 +0000 |
259 | +++ src/ai/defaultai.cc 2015-03-26 20:42:28 +0000 |
260 | @@ -51,12 +51,15 @@ |
261 | #include "profile/profile.h" |
262 | |
263 | // following is in miliseconds (widelands counts time in ms) |
264 | -constexpr int kFieldUpdateInterval = 2000; |
265 | -constexpr int kIdleMineUpdateInterval = 22000; |
266 | +// constexpr int kFieldUpdateInterval = 2000; |
267 | +constexpr int kFieldInfoExpiration = 12 * 1000; |
268 | +constexpr int kMineFieldInfoExpiration = 20 * 1000; |
269 | +constexpr int kNewMineConstInterval = 19000; |
270 | constexpr int kBusyMineUpdateInterval = 2000; |
271 | // building of the same building can be started after 25s at earliest |
272 | constexpr int kBuildingMinInterval = 25 * 1000; |
273 | constexpr int kMinBFCheckInterval = 5 * 1000; |
274 | +constexpr int kMinMFCheckInterval = 19 * 1000; |
275 | constexpr int kShipCheckInterval = 5 * 1000; |
276 | constexpr int kMarineDecisionInterval = 20 * 1000; |
277 | constexpr int kTrainingSitesCheckInterval = 5 * 60 * 1000; |
278 | @@ -85,6 +88,8 @@ |
279 | num_milit_constructionsites(0), |
280 | num_prod_constructionsites(0), |
281 | num_ports(0), |
282 | + last_attacked_player_(std::numeric_limits<uint16_t>::max()), |
283 | + enemysites_check_delay_(60), |
284 | next_ai_think_(0), |
285 | next_mine_construction_due_(0), |
286 | inhibit_road_building_(0), |
287 | @@ -92,20 +97,22 @@ |
288 | enemy_last_seen_(0), |
289 | numof_warehouses_(0), |
290 | new_buildings_stop_(false), |
291 | - resource_necessity_territory_(255), |
292 | - resource_necessity_mines_(255), |
293 | - resource_necessity_stones_(255), |
294 | + resource_necessity_territory_(100), |
295 | + resource_necessity_mines_(100), |
296 | resource_necessity_water_(0), |
297 | resource_necessity_water_needed_(false), |
298 | unstationed_milit_buildings_(0), |
299 | military_last_dismantle_(0), |
300 | military_last_build_(0), |
301 | - last_attack_target_( |
302 | - std::numeric_limits<uint16_t>::max(), std::numeric_limits<uint16_t>::max()), |
303 | - next_attack_waittime_(10), |
304 | seafaring_economy(false), |
305 | colony_scan_area_(35), |
306 | - spots_(0) { |
307 | + spots_(0), |
308 | + vacant_mil_positions_(0), |
309 | + ts_basic_count_(0), |
310 | + ts_basic_const_count_(0), |
311 | + ts_advanced_count_(0), |
312 | + ts_advanced_const_count_(0), |
313 | + ts_without_trainers_(0) { |
314 | |
315 | // Subscribe to NoteFieldPossession. |
316 | field_possession_subscriber_ = |
317 | @@ -166,23 +173,23 @@ |
318 | break; |
319 | |
320 | case NoteShipMessage::Message::kLost: |
321 | - for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) { |
322 | + for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) { |
323 | if (i->ship == note.ship) { |
324 | allships.erase(i); |
325 | break; |
326 | } |
327 | } |
328 | - break; |
329 | + break; |
330 | |
331 | case NoteShipMessage::Message::kWaitingForCommand: |
332 | - for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) { |
333 | + for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) { |
334 | if (i->ship == note.ship) { |
335 | i->waiting_for_command_ = true; |
336 | break; |
337 | } |
338 | } |
339 | - break; |
340 | - default: |
341 | + break; |
342 | + default: |
343 | ; |
344 | } |
345 | }); |
346 | @@ -233,65 +240,94 @@ |
347 | // (only one but some of them needs to run check_economies() to |
348 | // guarantee consistency) |
349 | // job names are selfexplanatory |
350 | - if (DueTask == ScheduleTasks::kBbuildableFieldsCheck) { |
351 | - update_all_buildable_fields(gametime); |
352 | - taskDue[ScheduleTasks::kBbuildableFieldsCheck] = gametime + kMinBFCheckInterval; |
353 | - } else if (DueTask == ScheduleTasks::kRoadCheck) { |
354 | - if (check_economies()) { // is a must |
355 | - return; |
356 | - }; |
357 | - taskDue[ScheduleTasks::kRoadCheck] = gametime + 400; |
358 | - improve_roads(gametime); |
359 | - } else if (DueTask == ScheduleTasks::kUnbuildableFCheck) { |
360 | - taskDue[ScheduleTasks::kUnbuildableFCheck] = gametime + 4000; |
361 | - update_all_not_buildable_fields(); |
362 | - } else if (DueTask == ScheduleTasks::kConsiderAttack) { |
363 | - consider_attack(gametime); |
364 | - } else if (DueTask == ScheduleTasks::kCheckEconomies) { |
365 | - check_economies(); |
366 | - taskDue[ScheduleTasks::kCheckEconomies] = gametime + 8000; |
367 | - } else if (DueTask == ScheduleTasks::kProductionsitesStats) { |
368 | - update_productionsite_stats(gametime); |
369 | - } else if (DueTask == ScheduleTasks::kConstructBuilding) { |
370 | - if (check_economies()) { // economies must be consistent |
371 | - return; |
372 | - } |
373 | - taskDue[ScheduleTasks::kConstructBuilding] = gametime + 6000; |
374 | - if (construct_building(gametime)) { |
375 | - time_of_last_construction_ = gametime; |
376 | - } |
377 | - } else if (DueTask == ScheduleTasks::kCheckProductionsites) { |
378 | - if (check_economies()) { // economies must be consistent |
379 | - return; |
380 | - } |
381 | - check_productionsites(gametime); |
382 | - taskDue[ScheduleTasks::kCheckProductionsites] = gametime + 5000; |
383 | - } else if (DueTask == ScheduleTasks::kCheckShips) { |
384 | - check_ships(gametime); |
385 | - } else if (DueTask == ScheduleTasks::KMarineDecisions) { |
386 | - marine_main_decisions(gametime); |
387 | - } else if (DueTask == ScheduleTasks::kCheckMines) { |
388 | - if (check_economies()) { // economies must be consistent |
389 | - return; |
390 | - } |
391 | - taskDue[ScheduleTasks::kCheckMines] = gametime + 7000; // 7 seconds is enough |
392 | - check_mines_(gametime); |
393 | - } else if (DueTask == ScheduleTasks::kCheckMilitarysites) { |
394 | - check_militarysites(gametime); |
395 | - } else if (DueTask == ScheduleTasks::kCheckTrainingsites) { |
396 | - check_trainingsites(gametime); |
397 | - } else if (DueTask == ScheduleTasks::kWareReview) { |
398 | - if (check_economies()) { // economies must be consistent |
399 | - return; |
400 | - } |
401 | - taskDue[ScheduleTasks::kWareReview] = gametime + 15 * 60 * 1000; |
402 | - review_wares_targets(gametime); |
403 | - } else if (DueTask == ScheduleTasks::kPrintStats) { |
404 | - if (check_economies()) { // economies must be consistent |
405 | - return; |
406 | - } |
407 | - print_stats(gametime); |
408 | - } |
409 | + switch (DueTask) { |
410 | + case ScheduleTasks::kBbuildableFieldsCheck : |
411 | + update_all_buildable_fields(gametime); |
412 | + taskDue[ScheduleTasks::kBbuildableFieldsCheck] = gametime + kMinBFCheckInterval; |
413 | + break; |
414 | + case ScheduleTasks::kMineableFieldsCheck : |
415 | + update_all_mineable_fields(gametime); |
416 | + taskDue[ScheduleTasks::kMineableFieldsCheck] = gametime + kMinMFCheckInterval; |
417 | + break; |
418 | + case ScheduleTasks::kRoadCheck : |
419 | + if (check_economies()) { // is a must |
420 | + return; |
421 | + }; |
422 | + taskDue[ScheduleTasks::kRoadCheck] = gametime + 400; |
423 | + improve_roads(gametime); |
424 | + break; |
425 | + case ScheduleTasks::kUnbuildableFCheck : |
426 | + taskDue[ScheduleTasks::kUnbuildableFCheck] = gametime + 4000; |
427 | + update_all_not_buildable_fields(); |
428 | + break; |
429 | + case ScheduleTasks::kCheckEconomies : |
430 | + check_economies(); |
431 | + taskDue[ScheduleTasks::kCheckEconomies] = gametime + 8000; |
432 | + break; |
433 | + case ScheduleTasks::kProductionsitesStats : |
434 | + update_productionsite_stats(gametime); |
435 | + break; |
436 | + case ScheduleTasks::kConstructBuilding : |
437 | + if (check_economies()) { // economies must be consistent |
438 | + return; |
439 | + } |
440 | + taskDue[ScheduleTasks::kConstructBuilding] = gametime + 6000; |
441 | + if (construct_building(gametime)) { |
442 | + time_of_last_construction_ = gametime; |
443 | + } |
444 | + break; |
445 | + case ScheduleTasks::kCheckProductionsites : |
446 | + if (check_economies()) { // economies must be consistent |
447 | + return; |
448 | + } |
449 | + check_productionsites(gametime); |
450 | + taskDue[ScheduleTasks::kCheckProductionsites] = gametime + 5000; |
451 | + break; |
452 | + case ScheduleTasks::kCheckShips : |
453 | + check_ships(gametime); |
454 | + break; |
455 | + case ScheduleTasks::KMarineDecisions : |
456 | + marine_main_decisions(gametime); |
457 | + break; |
458 | + case ScheduleTasks::kCheckMines : |
459 | + if (check_economies()) { // economies must be consistent |
460 | + return; |
461 | + } |
462 | + taskDue[ScheduleTasks::kCheckMines] = gametime + 7000; // 7 seconds is enough |
463 | + check_mines_(gametime); |
464 | + break; |
465 | + case ScheduleTasks::kCheckMilitarysites : |
466 | + check_militarysites(gametime); |
467 | + break; |
468 | + case ScheduleTasks::kCheckTrainingsites : |
469 | + check_trainingsites(gametime); |
470 | + break; |
471 | + case ScheduleTasks::kCountMilitaryVacant : |
472 | + count_military_vacant_positions(); |
473 | + taskDue[ScheduleTasks::kCountMilitaryVacant] = gametime + 90 * 1000; |
474 | + break; |
475 | + case ScheduleTasks::kWareReview : |
476 | + if (check_economies()) { // economies must be consistent |
477 | + return; |
478 | + } |
479 | + taskDue[ScheduleTasks::kWareReview] = gametime + 15 * 60 * 1000; |
480 | + review_wares_targets(gametime); |
481 | + break; |
482 | + case ScheduleTasks::kPrintStats : |
483 | + if (check_economies()) { // economies must be consistent |
484 | + return; |
485 | + } |
486 | + print_stats(gametime); |
487 | + break; |
488 | + case ScheduleTasks::kCheckEnemySites : |
489 | + check_enemy_sites(gametime); |
490 | + taskDue[ScheduleTasks::kCheckEnemySites] = gametime + 19 * 1000; |
491 | + break; |
492 | + case ScheduleTasks::kIdle : |
493 | + break; |
494 | + default: |
495 | + ; |
496 | + } |
497 | } |
498 | |
499 | /** |
500 | @@ -340,6 +376,7 @@ |
501 | bo.production_hint_ = -1; |
502 | bo.current_stats_ = 0; |
503 | bo.unoccupied_ = false; |
504 | + bo.unconnected_ = 0; |
505 | bo.is_buildable_ = bld.is_buildable(); |
506 | bo.need_trees_ = bh.is_logproducer(); |
507 | bo.need_stones_ = bh.is_stoneproducer(); |
508 | @@ -353,6 +390,8 @@ |
509 | bo.prohibited_till_ = bh.get_prohibited_till() * 1000; // value in conf is in seconds |
510 | bo.forced_after_ = bh.get_forced_after() * 1000; // value in conf is in seconds |
511 | bo.is_port_ = bld.get_isport(); |
512 | + bo.trainingsite_type_ = TrainingSiteType::kNoTS; |
513 | + |
514 | if (bh.renews_map_resource()) { |
515 | bo.production_hint_ = tribe_->safe_ware_index(bh.get_renews_map_resource()); |
516 | } |
517 | @@ -382,6 +421,11 @@ |
518 | } |
519 | |
520 | bo.mines_percent_ = bh.get_mines_percent(); |
521 | + |
522 | + // populating mines_per_type map |
523 | + if (mines_per_type.count(bo.mines_) == 0) { |
524 | + mines_per_type[bo.mines_] = MineTypesObserver(); |
525 | + } |
526 | } |
527 | |
528 | // here we identify hunters |
529 | @@ -439,6 +483,10 @@ |
530 | for (const WareAmount& temp_input : train.inputs()) { |
531 | bo.inputs_.push_back(temp_input.first); |
532 | } |
533 | + bo.trainingsite_type_ = bh.get_trainingsite_type(); |
534 | + // it would behave badly if no type was set |
535 | + // make sure all TS have its type set properly in conf files |
536 | + assert(bo.trainingsite_type_ != TrainingSiteType::kNoTS); |
537 | continue; |
538 | } |
539 | |
540 | @@ -544,12 +592,14 @@ |
541 | taskDue[ScheduleTasks::kCheckShips] = 30 * 1000; |
542 | taskDue[ScheduleTasks::kCheckEconomies] = 1000; |
543 | taskDue[ScheduleTasks::KMarineDecisions] = 30 * 1000; |
544 | - taskDue[ScheduleTasks::kConsiderAttack] = 300000; |
545 | taskDue[ScheduleTasks::kCheckTrainingsites] = 15 * 60 * 1000; |
546 | taskDue[ScheduleTasks::kBbuildableFieldsCheck] = 1000; |
547 | + taskDue[ScheduleTasks::kMineableFieldsCheck] = 1000; |
548 | taskDue[ScheduleTasks::kUnbuildableFCheck] = 1000; |
549 | taskDue[ScheduleTasks::kWareReview] = 15 * 60 * 1000; |
550 | taskDue[ScheduleTasks::kPrintStats] = 30 * 60 * 1000; |
551 | + taskDue[ScheduleTasks::kCountMilitaryVacant] = 10 * 60 * 1000; |
552 | + taskDue[ScheduleTasks::kCheckEnemySites] = 10 * 60 * 1000; |
553 | } |
554 | |
555 | /** |
556 | @@ -562,7 +612,10 @@ |
557 | |
558 | uint16_t i = 0; |
559 | |
560 | - while (!buildable_fields.empty() && buildable_fields.front()->next_update_due_ <= gametime && |
561 | + // we test 40 fields that were update more than 1 seconds ago |
562 | + while (!buildable_fields.empty() && |
563 | + (buildable_fields.front()->field_info_expiration_ - kFieldInfoExpiration + 1000) <= |
564 | + gametime && |
565 | i < 40) { |
566 | BuildableField& bf = *buildable_fields.front(); |
567 | |
568 | @@ -582,7 +635,7 @@ |
569 | } |
570 | |
571 | update_buildable_field(bf); |
572 | - bf.next_update_due_ = gametime + kFieldUpdateInterval; |
573 | + bf.field_info_expiration_ = gametime + kFieldInfoExpiration; |
574 | buildable_fields.push_back(&bf); |
575 | buildable_fields.pop_front(); |
576 | |
577 | @@ -600,8 +653,12 @@ |
578 | |
579 | uint16_t i = 0; // counter, used to track # of checked fields |
580 | |
581 | - while (!mineable_fields.empty() && mineable_fields.front()->next_update_due_ <= gametime && |
582 | - i < 40) { |
583 | + // we test 30 fields that were updated more than 1 seconds ago |
584 | + // to avoid re-test of the same field twice |
585 | + while (!mineable_fields.empty() && |
586 | + (mineable_fields.front()->field_info_expiration_ - kMineFieldInfoExpiration + 1000) <= |
587 | + gametime && |
588 | + i < 30) { |
589 | MineableField* mf = mineable_fields.front(); |
590 | |
591 | // check whether we lost ownership of the node |
592 | @@ -620,7 +677,7 @@ |
593 | } |
594 | |
595 | update_mineable_field(*mf); |
596 | - mf->next_update_due_ = gametime + kFieldUpdateInterval; // in fact this has very small effect |
597 | + mf->field_info_expiration_ = gametime + kMineFieldInfoExpiration; |
598 | mineable_fields.push_back(mf); |
599 | mineable_fields.pop_front(); |
600 | |
601 | @@ -672,32 +729,38 @@ |
602 | void DefaultAI::update_buildable_field(BuildableField& field, uint16_t range, bool military) { |
603 | // look if there is any unowned land nearby |
604 | Map& map = game().map(); |
605 | + const int32_t gametime = game().get_gametime(); |
606 | FindNodeUnowned find_unowned(player_, game()); |
607 | FindNodeUnownedMineable find_unowned_mines_pots(player_, game()); |
608 | PlayerNumber const pn = player_->player_number(); |
609 | const World& world = game().world(); |
610 | field.unowned_land_nearby_ = |
611 | map.find_fields(Area<FCoords>(field.coords, range), nullptr, find_unowned); |
612 | + FindNodeAllyOwned find_ally(player_, game(), player_number()); |
613 | + const int32_t AllyOwnedFields = |
614 | + map.find_fields(Area<FCoords>(field.coords, 3), nullptr, find_ally); |
615 | |
616 | field.near_border_ = false; |
617 | - if (field.unowned_land_nearby_ > 0) { |
618 | + if (AllyOwnedFields > 0) { |
619 | + field.near_border_ = true; |
620 | + } else if (field.unowned_land_nearby_ > 0) { |
621 | if (map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_unowned) > 0) { |
622 | field.near_border_ = true; |
623 | } |
624 | } |
625 | |
626 | // to save some CPU |
627 | - if ((mines_.size() > 8 && game().get_gametime() % 3 > 0) || field.unowned_land_nearby_ == 0) { |
628 | + if ((mines_.size() > 8 && gametime % 3 > 0) || field.unowned_land_nearby_ == 0) { |
629 | field.unowned_mines_pots_nearby_ = 0; |
630 | } else { |
631 | uint32_t close_mines = |
632 | map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_unowned_mines_pots); |
633 | uint32_t distant_mines = |
634 | - map.find_fields(Area<FCoords>(field.coords, (range + 6 < 12) ? 12 : range + 6), |
635 | + map.find_fields(Area<FCoords>(field.coords, (range + 6 < 14) ? 14 : range + 6), |
636 | nullptr, |
637 | find_unowned_mines_pots); |
638 | distant_mines = distant_mines - close_mines; |
639 | - field.unowned_mines_pots_nearby_ = 3 * close_mines + distant_mines / 2; |
640 | + field.unowned_mines_pots_nearby_ = 4 * close_mines + distant_mines / 2; |
641 | if (distant_mines > 0) { |
642 | field.unowned_mines_pots_nearby_ += 15; |
643 | } |
644 | @@ -710,7 +773,7 @@ |
645 | } |
646 | } |
647 | |
648 | - // testing for near porspaces |
649 | + // testing for near portspaces |
650 | if (field.portspace_nearby_ == Widelands::ExtendedBool::kUnset) { |
651 | field.portspace_nearby_ = ExtendedBool::kFalse; |
652 | MapRegion<Area<FCoords>> mr(map, Area<FCoords>(field.coords, 2)); |
653 | @@ -745,9 +808,9 @@ |
654 | int32_t const tree_attr = MapObjectDescr::get_attribute_id("tree"); |
655 | field.preferred_ = false; |
656 | field.enemy_nearby_ = false; |
657 | - field.military_capacity_ = 0; |
658 | + field.area_military_capacity_ = 0; |
659 | field.military_loneliness_ = 1000; // instead of floats(v- |
660 | - field.military_presence_ = 0; |
661 | + field.area_military_presence_ = 0; |
662 | field.military_stationed_ = 0; |
663 | field.trees_nearby_ = 0; |
664 | field.space_consumers_nearby_ = 0; |
665 | @@ -772,8 +835,7 @@ |
666 | } |
667 | |
668 | // counting fields with fish |
669 | - if (field.water_nearby_ > 0 && |
670 | - (field.fish_nearby_ == -1 || game().get_gametime() % 10 == 0)) { |
671 | + if (field.water_nearby_ > 0 && (field.fish_nearby_ == -1 || gametime % 10 == 0)) { |
672 | map.find_fields(Area<FCoords>(field.coords, 6), |
673 | &resource_list, |
674 | FindNodeResource(world.get_resource("fish"))); |
675 | @@ -782,7 +844,7 @@ |
676 | |
677 | // counting fields with critters (game) |
678 | // not doing this always, this does not change fast |
679 | - if (game().get_gametime() % 10 == 0) { |
680 | + if (gametime % 10 == 0) { |
681 | map.find_bobs(Area<FCoords>(field.coords, 6), &critters_list, FindBobCritter()); |
682 | field.critters_nearby_ = critters_list.size(); |
683 | } |
684 | @@ -808,7 +870,7 @@ |
685 | if (player_->is_hostile(player_immovable->owner())) { |
686 | field.enemy_nearby_ = true; |
687 | } |
688 | - enemy_last_seen_ = game().get_gametime(); |
689 | + enemy_last_seen_ = gametime; |
690 | |
691 | continue; |
692 | } |
693 | @@ -840,7 +902,7 @@ |
694 | } |
695 | |
696 | // stones are not renewable, we will count them only if previous state si nonzero |
697 | - if (field.stones_nearby_ > 0) { |
698 | + if (field.stones_nearby_ > 0 && gametime % 3 == 0) { |
699 | |
700 | int32_t const stone_attr = MapObjectDescr::get_attribute_id("granite"); |
701 | field.stones_nearby_ = 0; |
702 | @@ -863,13 +925,13 @@ |
703 | |
704 | // we get immovables with higher radius |
705 | immovables.clear(); |
706 | - map.find_immovables(Area<FCoords>(field.coords, (range < 10) ? 10 : range), &immovables); |
707 | + map.find_immovables(Area<FCoords>(field.coords, (range < 11) ? 11 : range), &immovables); |
708 | field.military_stationed_ = 0; |
709 | field.military_unstationed_ = 0; |
710 | field.military_in_constr_nearby_ = 0; |
711 | - field.military_capacity_ = 0; |
712 | + field.area_military_capacity_ = 0; |
713 | field.military_loneliness_ = 1000; |
714 | - field.military_presence_ = 0; |
715 | + field.area_military_presence_ = 0; |
716 | |
717 | for (uint32_t i = 0; i < immovables.size(); ++i) { |
718 | |
719 | @@ -890,6 +952,7 @@ |
720 | } |
721 | } |
722 | |
723 | + // if we are here, immovable is ours |
724 | if (upcast(Building const, building, &base_immovable)) { |
725 | if (upcast(ConstructionSite const, constructionsite, building)) { |
726 | const BuildingDescr& target_descr = constructionsite->building(); |
727 | @@ -899,7 +962,8 @@ |
728 | const int32_t radius = target_ms_d->get_conquers() + 4; |
729 | |
730 | if (radius > dist) { |
731 | - field.military_capacity_ += target_ms_d->get_max_number_of_soldiers() / 2 + 1; |
732 | + field.area_military_capacity_ += |
733 | + target_ms_d->get_max_number_of_soldiers() / 2 + 1; |
734 | field.military_loneliness_ *= static_cast<double_t>(dist) / radius; |
735 | field.military_in_constr_nearby_ += 1; |
736 | } |
737 | @@ -912,8 +976,8 @@ |
738 | |
739 | if (radius > dist) { |
740 | |
741 | - field.military_capacity_ += militarysite->max_soldier_capacity(); |
742 | - field.military_presence_ += militarysite->stationed_soldiers().size(); |
743 | + field.area_military_capacity_ += militarysite->max_soldier_capacity(); |
744 | + field.area_military_presence_ += militarysite->stationed_soldiers().size(); |
745 | |
746 | if (militarysite->stationed_soldiers().empty()) { |
747 | field.military_unstationed_ += 1; |
748 | @@ -935,7 +999,7 @@ |
749 | Map& map = game().map(); |
750 | map.find_immovables(Area<FCoords>(field.coords, 5), &immovables); |
751 | field.preferred_ = false; |
752 | - field.mines_nearby_ = 1; |
753 | + field.mines_nearby_ = 0; |
754 | FCoords fse; |
755 | map.get_brn(field.coords, &fse); |
756 | |
757 | @@ -948,11 +1012,20 @@ |
758 | |
759 | for (const ImmovableFound& temp_immovable : immovables) { |
760 | if (upcast(Building const, bld, temp_immovable.object)) { |
761 | + if (player_number() != bld->owner().player_number()) { |
762 | + continue; |
763 | + } |
764 | if (bld->descr().get_ismine()) { |
765 | - ++field.mines_nearby_; |
766 | + if (get_building_observer(bld->descr().name().c_str()).mines_ == |
767 | + field.coords.field->get_resources()) { |
768 | + ++field.mines_nearby_; |
769 | + } |
770 | } else if (upcast(ConstructionSite const, cs, bld)) { |
771 | if (cs->building().get_ismine()) { |
772 | - ++field.mines_nearby_; |
773 | + if (get_building_observer(cs->building().name().c_str()).mines_ == |
774 | + field.coords.field->get_resources()) { |
775 | + ++field.mines_nearby_; |
776 | + } |
777 | } |
778 | } |
779 | } |
780 | @@ -976,23 +1049,34 @@ |
781 | for (uint32_t i = 0; i < buildings_.size(); ++i) { |
782 | buildings_.at(i).current_stats_ = 0; |
783 | buildings_.at(i).unoccupied_ = false; |
784 | + buildings_.at(i).unconnected_ = 0; |
785 | } |
786 | |
787 | // Check all available productionsites |
788 | for (uint32_t i = 0; i < productionsites.size(); ++i) { |
789 | assert(productionsites.front().bo->cnt_built_ > 0); |
790 | - // Add statistics value |
791 | - productionsites.front().bo->current_stats_ += |
792 | - productionsites.front().site->get_crude_statistics(); |
793 | - |
794 | - // counting fishers |
795 | - if (productionsites.front().bo->is_fisher_) { |
796 | - fishers_count += 1; |
797 | + // is connected |
798 | + const bool connected_to_wh = |
799 | + !productionsites.front().site->get_economy()->warehouses().empty(); |
800 | + |
801 | + // unconnected buildings are excluded from statistics review |
802 | + if (connected_to_wh) { |
803 | + // Add statistics value |
804 | + productionsites.front().bo->current_stats_ += |
805 | + productionsites.front().site->get_crude_statistics(); |
806 | + |
807 | + // counting fishers |
808 | + if (productionsites.front().bo->is_fisher_) { |
809 | + fishers_count += 1; |
810 | + } |
811 | + |
812 | + // Check whether this building is completely occupied |
813 | + productionsites.front().bo->unoccupied_ |= |
814 | + !productionsites.front().site->can_start_working(); |
815 | + } else { |
816 | + productionsites.front().bo->unconnected_ += 1; |
817 | } |
818 | |
819 | - // Check whether this building is completely occupied |
820 | - productionsites.front().bo->unoccupied_ |= !productionsites.front().site->can_start_working(); |
821 | - |
822 | // Now reorder the buildings |
823 | productionsites.push_back(productionsites.front()); |
824 | productionsites.pop_front(); |
825 | @@ -1000,11 +1084,11 @@ |
826 | |
827 | if (resource_necessity_water_needed_) { |
828 | if (fishers_count == 0) { |
829 | - resource_necessity_water_ = 255; |
830 | + resource_necessity_water_ = 100; |
831 | } else if (fishers_count == 1) { |
832 | - resource_necessity_water_ = 150; |
833 | + resource_necessity_water_ = 50; |
834 | } else { |
835 | - resource_necessity_water_ = 18; |
836 | + resource_necessity_water_ = 10; |
837 | } |
838 | } |
839 | |
840 | @@ -1012,10 +1096,20 @@ |
841 | // Check all available mines |
842 | for (uint32_t i = 0; i < mines_.size(); ++i) { |
843 | assert(mines_.front().bo->cnt_built_ > 0); |
844 | - // Add statistics value |
845 | - mines_.front().bo->current_stats_ += mines_.front().site->get_statistics_percent(); |
846 | - // Check whether this building is completely occupied |
847 | - mines_.front().bo->unoccupied_ |= !mines_.front().site->can_start_working(); |
848 | + |
849 | + const bool connected_to_wh = |
850 | + !productionsites.front().site->get_economy()->warehouses().empty(); |
851 | + |
852 | + // unconnected mines are excluded from statistics review |
853 | + if (connected_to_wh) { |
854 | + // Add statistics value |
855 | + mines_.front().bo->current_stats_ += mines_.front().site->get_statistics_percent(); |
856 | + // Check whether this building is completely occupied |
857 | + mines_.front().bo->unoccupied_ |= !mines_.front().site->can_start_working(); |
858 | + } else { |
859 | + buildings_.at(i).unconnected_ += 1; |
860 | + } |
861 | + |
862 | // Now reorder the buildings |
863 | mines_.push_back(mines_.front()); |
864 | mines_.pop_front(); |
865 | @@ -1023,8 +1117,9 @@ |
866 | |
867 | // Scale statistics down |
868 | for (uint32_t i = 0; i < buildings_.size(); ++i) { |
869 | - if (buildings_.at(i).cnt_built_ > 0) { |
870 | - buildings_.at(i).current_stats_ /= buildings_.at(i).cnt_built_; |
871 | + if ((buildings_.at(i).cnt_built_ - buildings_.at(i).unconnected_) > 0) { |
872 | + buildings_.at(i).current_stats_ /= |
873 | + (buildings_.at(i).cnt_built_ - buildings_.at(i).unconnected_); |
874 | } |
875 | } |
876 | } |
877 | @@ -1100,54 +1195,48 @@ |
878 | |
879 | // sometimes there is too many military buildings in construction, so we must |
880 | // prevent initialization of further buildings start |
881 | - const uint32_t treshold = militarysites.size() / 40 + 2; |
882 | - |
883 | - if (unstationed_milit_buildings_ + num_milit_constructionsites > 3 * treshold) { |
884 | + const int32_t vacant_plus_in_construction_minus_prod = |
885 | + vacant_mil_positions_ + 2 * num_milit_constructionsites - productionsites.size() / 15; |
886 | + if (vacant_plus_in_construction_minus_prod > 20) { |
887 | expansion_mode = MilitaryStrategy::kNoNewMilitary; |
888 | - } else if (unstationed_milit_buildings_ + num_milit_constructionsites > 2 * treshold) { |
889 | + } else if (vacant_plus_in_construction_minus_prod > 13) { |
890 | expansion_mode = MilitaryStrategy::kDefenseOnly; |
891 | - } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= treshold - 1) { |
892 | + } else if (vacant_plus_in_construction_minus_prod > 6) { |
893 | expansion_mode = MilitaryStrategy::kResourcesOrDefense; |
894 | - } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) { |
895 | - expansion_mode = MilitaryStrategy::kExpansion; |
896 | } else { |
897 | - expansion_mode = MilitaryStrategy::kPushExpansion; |
898 | + if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) { |
899 | + expansion_mode = MilitaryStrategy::kExpansion; |
900 | + } else { |
901 | + expansion_mode = MilitaryStrategy::kPushExpansion; |
902 | + } |
903 | } |
904 | |
905 | // we must consider need for mines |
906 | // set necessity for mines |
907 | // we use 'virtual mines', because also mine spots can be changed |
908 | // to mines when AI decides so |
909 | - const int32_t virtual_mines = mines_.size() + mineable_fields.size() / 15; |
910 | - if (virtual_mines <= 7) { |
911 | - resource_necessity_mines_ = std::numeric_limits<uint8_t>::max(); |
912 | - } else if (virtual_mines > 19) { |
913 | - resource_necessity_mines_ = 50; |
914 | + const int32_t virtual_mines = |
915 | + mines_.size() + mineable_fields.size() / 15 - productionsites.size() / 25; |
916 | + resource_necessity_mines_ = 100 * (15 - virtual_mines) / 15; |
917 | + resource_necessity_mines_ = (resource_necessity_mines_ > 100) ? 100 : resource_necessity_mines_; |
918 | + resource_necessity_mines_ = (resource_necessity_mines_ < 20) ? 10 : resource_necessity_mines_; |
919 | + |
920 | + // here we calculate how badly we need to expand, result is number (0-100) |
921 | + // like a percent |
922 | + if (spots_ == 0) { |
923 | + resource_necessity_territory_ = 100; |
924 | } else { |
925 | - const uint32_t tmp = (24 - virtual_mines) * 10; |
926 | - resource_necessity_mines_ = tmp; |
927 | - } |
928 | - |
929 | - // here we calculate a need for expansion and reduce necessity for new land |
930 | - // the game has two stages: |
931 | - // First: virtual mines<=5 - stage of building the economics |
932 | - // Second: virtual mines>5 - teritorial expansion |
933 | - if (virtual_mines <= 5) { |
934 | - if (spots_avail.at(BUILDCAPS_BIG) <= 4) { |
935 | - resource_necessity_territory_ = 255; |
936 | - } else { |
937 | - resource_necessity_territory_ = 0; |
938 | + resource_necessity_territory_ = 100 * 5 * (productionsites.size() + 5) / spots_; |
939 | + resource_necessity_territory_ = |
940 | + (resource_necessity_territory_ > 100) ? 100 : resource_necessity_territory_; |
941 | + resource_necessity_territory_ = |
942 | + (resource_necessity_territory_ < 10) ? 10 : resource_necessity_territory_; |
943 | + // alse we need at lest 4 big spots |
944 | + if (spots_avail.at(BUILDCAPS_BIG) < 2) { |
945 | + resource_necessity_territory_ = 100; |
946 | } |
947 | - } else { // or we have enough mines and regulate speed of expansion |
948 | - if (spots_ == 0) { |
949 | - resource_necessity_territory_ = 255; |
950 | - } else { |
951 | - const uint32_t tmp = 255 * 4 * productionsites.size() / spots_; |
952 | - if (tmp > 255) { |
953 | - resource_necessity_territory_ = 255; |
954 | - } else { |
955 | - resource_necessity_territory_ = tmp; |
956 | - } |
957 | + if (spots_avail.at(BUILDCAPS_MEDIUM) < 4) { |
958 | + resource_necessity_territory_ = 100; |
959 | } |
960 | } |
961 | |
962 | @@ -1205,7 +1294,7 @@ |
963 | ++i) { |
964 | BuildableField* const bf = *i; |
965 | |
966 | - if (bf->next_update_due_ < gametime - 10000) { |
967 | + if (bf->field_info_expiration_ < gametime) { |
968 | continue; |
969 | } |
970 | |
971 | @@ -1369,7 +1458,7 @@ |
972 | continue; |
973 | } |
974 | |
975 | - if (bo.total_count() == 0) { |
976 | + if (bo.total_count() - bo.unconnected_ == 0) { |
977 | prio += 150; |
978 | } |
979 | |
980 | @@ -1394,12 +1483,15 @@ |
981 | continue; |
982 | } |
983 | |
984 | - if (new_buildings_stop_) { |
985 | + if (gametime > 5 * 60 * 1000 && |
986 | + (bo.total_count() - bo.unconnected_ == 0)) { |
987 | + prio += 10; |
988 | + } else if (new_buildings_stop_) { |
989 | continue; |
990 | } |
991 | |
992 | prio += |
993 | - (bf->critters_nearby_ * 2) - 8 - 5 * bf->producers_nearby_.at(bo.outputs_.at(0)); |
994 | + (bf->critters_nearby_ * 3) - 8 - 5 * bf->producers_nearby_.at(bo.outputs_.at(0)); |
995 | |
996 | } else if (bo.is_fisher_) { // fisher |
997 | |
998 | @@ -1490,7 +1582,7 @@ |
999 | prio -= bf->space_consumers_nearby_ * 5; |
1000 | |
1001 | } else { // FISH BREEDERS and GAME KEEPERS |
1002 | - if (new_buildings_stop_ && bo.total_count() > 0) { |
1003 | + if (new_buildings_stop_ && (bo.total_count() - bo.unconnected_) > 0) { |
1004 | continue; |
1005 | } |
1006 | |
1007 | @@ -1502,7 +1594,7 @@ |
1008 | prio += bf->water_nearby_ / 5; |
1009 | } |
1010 | |
1011 | - if (bo.total_count() > bo.cnt_target_) { |
1012 | + if ((bo.total_count() - bo.unconnected_) > bo.cnt_target_) { |
1013 | continue; |
1014 | } |
1015 | |
1016 | @@ -1543,9 +1635,10 @@ |
1017 | continue; |
1018 | } |
1019 | |
1020 | - if (bo.forced_after_ < gametime && bo.total_count() == 0) { |
1021 | + if (bo.forced_after_ < gametime && (bo.total_count() - bo.unconnected_) == 0) { |
1022 | prio += 150; |
1023 | - } else if (bo.cnt_built_ == 1 && game().get_gametime() > 40 * 60 * 1000 && |
1024 | + } else if ((bo.cnt_built_ - bo.unconnected_) == 1 && |
1025 | + game().get_gametime() > 40 * 60 * 1000 && |
1026 | bo.desc->enhancement() != INVALID_INDEX && !mines_.empty()) { |
1027 | prio += 10; |
1028 | } else if (bo.is_shipyard_) { |
1029 | @@ -1554,9 +1647,10 @@ |
1030 | } |
1031 | } else if (!output_is_needed) { |
1032 | continue; |
1033 | - } else if (bo.cnt_built_ == 0 && game().get_gametime() > 40 * 60 * 1000) { |
1034 | + } else if ((bo.cnt_built_ - bo.unconnected_) == 0 && |
1035 | + game().get_gametime() > 40 * 60 * 1000) { |
1036 | prio += kDefaultPrioBoost; |
1037 | - } else if (bo.cnt_built_ > 1 && bo.current_stats_ > 97) { |
1038 | + } else if ((bo.cnt_built_ - bo.unconnected_) > 1 && bo.current_stats_ > 97) { |
1039 | prio -= kDefaultPrioBoost * (new_buildings_stop_); |
1040 | } else if (new_buildings_stop_) |
1041 | continue; |
1042 | @@ -1593,17 +1687,25 @@ |
1043 | |
1044 | else if (bo.is_shipyard_) { |
1045 | // for now AI builds only one shipyard |
1046 | - if (bf->water_nearby_ > 3 && bo.total_count() == 0 && seafaring_economy) { |
1047 | + if (bf->water_nearby_ > 3 && (bo.total_count() - bo.unconnected_) == 0 && |
1048 | + seafaring_economy) { |
1049 | prio += kDefaultPrioBoost + productionsites.size() * 5 + bf->water_nearby_; |
1050 | } |
1051 | |
1052 | } else if (!bo.inputs_.empty()) { |
1053 | - if (bo.total_count() == 0) { |
1054 | + if ((bo.total_count() - bo.unconnected_ == 0)) { |
1055 | prio += max_needed_preciousness + kDefaultPrioBoost; |
1056 | } |
1057 | - if (bo.cnt_built_ > 0 && bo.current_stats_ > 70) { |
1058 | + if ((bo.cnt_built_ - bo.unconnected_) > 0 |
1059 | + && |
1060 | + //due to very badly designed statistics and the way how |
1061 | + //productionsites are working we must distinguish how many |
1062 | + //outputs the site has. |
1063 | + ((bo.outputs_.size() == 1 && bo.current_stats_ > 75) |
1064 | + || |
1065 | + (bo.outputs_.size() > 1 && bo.current_stats_ > 55))) { |
1066 | prio += max_needed_preciousness + kDefaultPrioBoost - 3 + |
1067 | - (bo.current_stats_ - 70) / 5; |
1068 | + (bo.current_stats_ - 55) / 8; |
1069 | } |
1070 | } |
1071 | |
1072 | @@ -1623,7 +1725,6 @@ |
1073 | } |
1074 | } // production sites done |
1075 | else if (bo.type == BuildingObserver::MILITARYSITE) { |
1076 | - |
1077 | // we allow 1 exemption from big buildings prohibition |
1078 | if (bo.build_material_shortage_ && |
1079 | (bo.cnt_under_construction_ > 0 || !(bf->enemy_nearby_))) { |
1080 | @@ -1631,6 +1732,7 @@ |
1081 | } |
1082 | |
1083 | if (!bf->unowned_land_nearby_) { |
1084 | + |
1085 | continue; |
1086 | } |
1087 | |
1088 | @@ -1648,8 +1750,7 @@ |
1089 | |
1090 | if (expansion_mode == MilitaryStrategy::kResourcesOrDefense && |
1091 | !(bf->unowned_mines_pots_nearby_ || bf->stones_nearby_ || bf->water_nearby_ || |
1092 | - (bf->distant_water_ && resource_necessity_water_needed_) || |
1093 | - bf->enemy_nearby_)) { |
1094 | + (bf->distant_water_ && resource_necessity_water_needed_) || bf->enemy_nearby_)) { |
1095 | continue; |
1096 | } |
1097 | |
1098 | @@ -1667,31 +1768,40 @@ |
1099 | if (bo.desc->get_size() == 3 && gametime % 3 >= 1) { |
1100 | continue; |
1101 | }; |
1102 | - } |
1103 | - else { |
1104 | + } else { |
1105 | continue; |
1106 | } // the building is not suitable for situation |
1107 | - |
1108 | // not to build so many military buildings nearby |
1109 | if (!bf->enemy_nearby_ && |
1110 | (bf->military_in_constr_nearby_ + bf->military_unstationed_) > 0) { |
1111 | continue; |
1112 | } |
1113 | - |
1114 | // a boost to prevent an expansion halt |
1115 | int32_t local_boost = 0; |
1116 | if (expansion_mode == MilitaryStrategy::kPushExpansion) { |
1117 | local_boost = 200; |
1118 | } |
1119 | |
1120 | - prio = ((bf->unowned_land_nearby_ * 2 * resource_necessity_territory_) / 255 + |
1121 | - (bf->unowned_mines_pots_nearby_ * resource_necessity_mines_) / 255 + |
1122 | - bf->stones_nearby_ / 2 + bf->military_loneliness_ / 10 - 60 + local_boost + |
1123 | - (bf->water_nearby_ * resource_necessity_water_) / 255); |
1124 | + // priority based on basic resources |
1125 | + prio = ((bf->unowned_mines_pots_nearby_ * resource_necessity_mines_) / 100 + |
1126 | + bf->stones_nearby_ + bf->military_loneliness_ / 10 - 40 + local_boost + |
1127 | + (bf->water_nearby_ * resource_necessity_water_) / 100); |
1128 | + |
1129 | + // Depending on wheter resource only are considered or no |
1130 | + if (expansion_mode == MilitaryStrategy::kResourcesOrDefense) { |
1131 | + prio *= 2; |
1132 | + prio += bf->unowned_land_nearby_ * resource_necessity_territory_ / 100 / 2; |
1133 | + } else { // addding score for territory |
1134 | + prio += (bf->unowned_land_nearby_ * resource_necessity_territory_) / 100 * 3 / 2; |
1135 | + } |
1136 | + |
1137 | + // adding score for distance to other military sites |
1138 | + prio += bf->military_loneliness_ / 10 - 40; |
1139 | |
1140 | // special bonus due to remote water for atlanteans |
1141 | - if (resource_necessity_water_needed_) |
1142 | - prio += (bf->distant_water_ * resource_necessity_water_) / 255; |
1143 | + if (resource_necessity_water_needed_) { |
1144 | + prio += (bf->distant_water_ * resource_necessity_water_) / 100 / 2; |
1145 | + } |
1146 | |
1147 | // special bonus if a portspace is close |
1148 | if (bf->portspace_nearby_ == ExtendedBool::kTrue) { |
1149 | @@ -1702,6 +1812,11 @@ |
1150 | } |
1151 | } |
1152 | |
1153 | + //special bonus for bigger buildings if enemy is nearby |
1154 | + if (bf->enemy_nearby_) { |
1155 | + prio += (bo.desc->get_size() - 1) * 25; |
1156 | + } |
1157 | + |
1158 | if (bo.desc->get_size() < maxsize) { |
1159 | prio = prio - 5; |
1160 | } // penalty |
1161 | @@ -1713,12 +1828,12 @@ |
1162 | // for expansion) |
1163 | const int16_t bottom_treshold = |
1164 | 15 - ((virtual_mines <= 4) ? (5 - virtual_mines) * 2 : 0); |
1165 | - if (bf->enemy_nearby_ && bf->military_capacity_ < bottom_treshold) { |
1166 | - prio += 50 + (bottom_treshold - bf->military_capacity_) * 20; |
1167 | + if (bf->enemy_nearby_ && bf->area_military_capacity_ < bottom_treshold) { |
1168 | + prio += 50 + (bottom_treshold - bf->area_military_capacity_) * 20; |
1169 | } |
1170 | |
1171 | - if (bf->enemy_nearby_ && bf->military_capacity_ > bottom_treshold + 4) { |
1172 | - prio -= (bf->military_capacity_ - (bottom_treshold + 4)) * 5; |
1173 | + if (bf->enemy_nearby_ && bf->area_military_capacity_ > bottom_treshold + 4) { |
1174 | + prio -= (bf->area_military_capacity_ - (bottom_treshold + 4)) * 5; |
1175 | } |
1176 | |
1177 | } else if (bo.type == BuildingObserver::WAREHOUSE) { |
1178 | @@ -1780,7 +1895,7 @@ |
1179 | |
1180 | // take care about and enemies |
1181 | if (bf->enemy_nearby_) { |
1182 | - prio /= 2; |
1183 | + prio /= 4; |
1184 | } |
1185 | |
1186 | if (bf->unowned_land_nearby_ && !bo.is_port_) { |
1187 | @@ -1789,24 +1904,43 @@ |
1188 | |
1189 | } else if (bo.type == BuildingObserver::TRAININGSITE) { |
1190 | |
1191 | - if (virtual_mines < 5) { |
1192 | - continue; |
1193 | - } |
1194 | - |
1195 | // exclude spots on border |
1196 | if (bf->near_border_) { |
1197 | continue; |
1198 | } |
1199 | |
1200 | - if (virtual_mines < 3) { |
1201 | - continue; |
1202 | - } |
1203 | - |
1204 | - // build after 20 production sites and then after each 50 production site |
1205 | - if (static_cast<int32_t>((productionsites.size() + 40) / 60) > bo.total_count() && |
1206 | - bo.cnt_under_construction_ == 0) { |
1207 | - prio = 4 + kDefaultPrioBoost; |
1208 | - } |
1209 | + // it is a bit difficult to get a new trainer..... |
1210 | + if (ts_without_trainers_) { |
1211 | + continue; |
1212 | + } |
1213 | + |
1214 | + // target is only one for both types |
1215 | + if ((ts_basic_const_count_ + ts_advanced_const_count_) > 0) { |
1216 | + continue; |
1217 | + } |
1218 | + |
1219 | + // we build one training site for 100 militarysites |
1220 | + if (bo.trainingsite_type_ == TrainingSiteType::kBasic && |
1221 | + militarysites.size() / 100 < static_cast<int32_t>(ts_basic_count_)) { |
1222 | + continue; |
1223 | + } |
1224 | + // we build one training site for 100 militarysites |
1225 | + if (bo.trainingsite_type_ == TrainingSiteType::kAdvanced && |
1226 | + militarysites.size() / 100 < static_cast<int32_t>(ts_advanced_count_)) { |
1227 | + continue; |
1228 | + } |
1229 | + |
1230 | + // for type1 we need 15 productionsties |
1231 | + if (bo.trainingsite_type_ == TrainingSiteType::kBasic && productionsites.size() < 15) { |
1232 | + continue; |
1233 | + } |
1234 | + |
1235 | + // for type2 we need 4 mines |
1236 | + if (bo.trainingsite_type_ == TrainingSiteType::kAdvanced && virtual_mines < 4) { |
1237 | + continue; |
1238 | + } |
1239 | + |
1240 | + prio = 4 + kDefaultPrioBoost; |
1241 | |
1242 | // take care about borders and enemies |
1243 | if (bf->enemy_nearby_) { |
1244 | @@ -1854,8 +1988,9 @@ |
1245 | // then try all mines_ - as soon as basic economy is build up. |
1246 | if (gametime > next_mine_construction_due_) { |
1247 | |
1248 | - update_all_mineable_fields(gametime); |
1249 | - next_mine_construction_due_ = gametime + kIdleMineUpdateInterval; |
1250 | + // not done here |
1251 | + // update_all_mineable_fields(gametime); |
1252 | + next_mine_construction_due_ = gametime + kNewMineConstInterval; |
1253 | |
1254 | if (!mineable_fields.empty()) { |
1255 | |
1256 | @@ -1885,22 +2020,33 @@ |
1257 | check_ware_necessity( |
1258 | bo, &output_is_needed, &max_preciousness, &max_needed_preciousness); |
1259 | |
1260 | - if (!output_is_needed && bo.total_count() > 0) { |
1261 | + if (!output_is_needed && (bo.total_count() - bo.unconnected_) > 0) { |
1262 | continue; |
1263 | } |
1264 | |
1265 | // if current one(s) are performing badly |
1266 | - if (bo.total_count() >= 1 && bo.current_stats_ < 50) { |
1267 | + if ((bo.total_count() - bo.unconnected_) >= 1 && bo.current_stats_ < 50) { |
1268 | continue; |
1269 | } |
1270 | |
1271 | // this is penalty if there are existing mines too close |
1272 | // it is treated as multiplicator for count of near mines |
1273 | uint32_t nearness_penalty = 0; |
1274 | - if ((bo.cnt_built_ + bo.cnt_under_construction_) == 0) { |
1275 | + if ((mines_per_type[bo.mines_].in_construction + mines_per_type[bo.mines_].finished) == |
1276 | + 0) { |
1277 | nearness_penalty = 0; |
1278 | } else { |
1279 | - nearness_penalty = 10; |
1280 | + nearness_penalty = 30; |
1281 | + } |
1282 | + |
1283 | + // bonus score to prefer if too few mines |
1284 | + uint32_t bonus_score = 0; |
1285 | + if ((mines_per_type[bo.mines_].in_construction + mines_per_type[bo.mines_].finished) == |
1286 | + 0) { |
1287 | + bonus_score = 15; |
1288 | + } else if ((mines_per_type[bo.mines_].in_construction + |
1289 | + mines_per_type[bo.mines_].finished) == 1) { |
1290 | + bonus_score = 5; |
1291 | } |
1292 | |
1293 | // iterating over fields |
1294 | @@ -1908,26 +2054,46 @@ |
1295 | j != mineable_fields.end(); |
1296 | ++j) { |
1297 | |
1298 | - if ((*j)->coords.field->get_resources() != bo.mines_) { |
1299 | - continue; |
1300 | - } |
1301 | - |
1302 | - int32_t prio = (*j)->coords.field->get_resources_amount(); |
1303 | + MineableField* const mf = *j; |
1304 | + |
1305 | + if (mf->field_info_expiration_ <= gametime) { |
1306 | + continue; |
1307 | + } |
1308 | + |
1309 | + if (mf->coords.field->get_resources() != bo.mines_) { |
1310 | + continue; |
1311 | + } |
1312 | + |
1313 | + int32_t prio = 0; |
1314 | + MapRegion<Area<FCoords>> mr(map, Area<FCoords>(mf->coords, 2)); |
1315 | + do { |
1316 | + if (bo.mines_ == mr.location().field->get_resources()) { |
1317 | + prio += mr.location().field->get_resources_amount(); |
1318 | + } |
1319 | + } while (mr.advance(map)); |
1320 | + |
1321 | + prio /= 10; |
1322 | + |
1323 | + // Only build mines_ on locations where some material can be mined |
1324 | + if (prio < 1) { |
1325 | + continue; |
1326 | + } |
1327 | |
1328 | // applying nearnes penalty |
1329 | - prio = prio - (*j)->mines_nearby_ * nearness_penalty; |
1330 | - |
1331 | - // Only build mines_ on locations where some material can be mined |
1332 | - if (prio < 2) { |
1333 | - continue; |
1334 | - } |
1335 | + prio -= mf->mines_nearby_ * nearness_penalty; |
1336 | + |
1337 | + // applying bonus score |
1338 | + prio += bonus_score; |
1339 | + |
1340 | + // applying max needed |
1341 | + prio += max_needed_preciousness * 3; |
1342 | |
1343 | // prefer mines in the middle of mine fields of the |
1344 | // same type, so we add a small bonus here |
1345 | // depending on count of same mines nearby, |
1346 | // though this does not reflects how many resources |
1347 | // are (left) in nearby mines |
1348 | - prio += (*j)->same_mine_fields_nearby_ / 3; |
1349 | + prio += mf->same_mine_fields_nearby_; |
1350 | |
1351 | // Continue if field is blocked at the moment |
1352 | bool blocked = false; |
1353 | @@ -1946,13 +2112,12 @@ |
1354 | } |
1355 | |
1356 | // Prefer road side fields |
1357 | - prio += (*j)->preferred_ ? 1 : 0; |
1358 | + prio += mf->preferred_ ? 1 : 0; |
1359 | |
1360 | if (prio > proposed_priority) { |
1361 | - // proposed_building = bo.id; |
1362 | best_building = &bo; |
1363 | proposed_priority = prio; |
1364 | - proposed_coords = (*j)->coords; |
1365 | + proposed_coords = mf->coords; |
1366 | mine = true; |
1367 | } |
1368 | } // end of evaluation of field |
1369 | @@ -2257,11 +2422,11 @@ |
1370 | |
1371 | if (occupied_military_) { |
1372 | eco->dismantle_grace_time_ = |
1373 | - (gametime + 20 * 60 * 1000) + (eco->flags.size() * 20 * 1000); |
1374 | + (gametime + 90 * 60 * 1000) + (eco->flags.size() * 20 * 1000); |
1375 | |
1376 | } else { // for other normal buildings |
1377 | eco->dismantle_grace_time_ = |
1378 | - gametime + (5 * 60 * 1000) + (eco->flags.size() * 20 * 1000); |
1379 | + gametime + (45 * 60 * 1000) + (eco->flags.size() * 20 * 1000); |
1380 | } |
1381 | } |
1382 | |
1383 | @@ -2547,6 +2712,9 @@ |
1384 | site.unoccupied_till_ = game().get_gametime(); |
1385 | } |
1386 | |
1387 | + // is it connected to wh at all? |
1388 | + const bool connected_to_wh = !site.site->get_economy()->warehouses().empty(); |
1389 | + |
1390 | // do not dismantle or upgrade the same type of building too soon - to give some time to update |
1391 | // statistics |
1392 | if (site.bo->last_dismantle_time_ > game().get_gametime() - 30 * 1000) { |
1393 | @@ -2571,7 +2739,8 @@ |
1394 | // b) if there are two buildings |
1395 | // statistics percents are decisive |
1396 | const BuildingIndex enhancement = site.site->descr().enhancement(); |
1397 | - if (enhancement != INVALID_INDEX && (site.bo->cnt_built_ - site.bo->unoccupied_) > 1) { |
1398 | + if (connected_to_wh && enhancement != INVALID_INDEX && |
1399 | + (site.bo->cnt_built_ - site.bo->unoccupied_) > 1) { |
1400 | |
1401 | BuildingIndex enbld = INVALID_INDEX; // to get rid of this |
1402 | |
1403 | @@ -2664,7 +2833,11 @@ |
1404 | // so finally we dismantle the lumberjac |
1405 | site.bo->last_dismantle_time_ = game().get_gametime(); |
1406 | flags_to_be_removed.push_back(site.site->base_flag().get_position()); |
1407 | - game().send_player_dismantle(*site.site); |
1408 | + if (connected_to_wh) { |
1409 | + game().send_player_dismantle(*site.site); |
1410 | + } else { |
1411 | + game().send_player_bulldoze(*site.site); |
1412 | + } |
1413 | |
1414 | return true; |
1415 | } |
1416 | @@ -2675,7 +2848,11 @@ |
1417 | site.site->get_statistics_percent() == 0) { |
1418 | site.bo->last_dismantle_time_ = gametime; |
1419 | flags_to_be_removed.push_back(site.site->base_flag().get_position()); |
1420 | - game().send_player_dismantle(*site.site); |
1421 | + if (connected_to_wh) { |
1422 | + game().send_player_dismantle(*site.site); |
1423 | + } else { |
1424 | + game().send_player_bulldoze(*site.site); |
1425 | + } |
1426 | |
1427 | return true; |
1428 | } |
1429 | @@ -2695,7 +2872,11 @@ |
1430 | if (site.bo->stocklevel_ > 250 + productionsites.size() * 5) { // dismantle |
1431 | site.bo->last_dismantle_time_ = game().get_gametime(); |
1432 | flags_to_be_removed.push_back(site.site->base_flag().get_position()); |
1433 | - game().send_player_dismantle(*site.site); |
1434 | + if (connected_to_wh) { |
1435 | + game().send_player_dismantle(*site.site); |
1436 | + } else { |
1437 | + game().send_player_bulldoze(*site.site); |
1438 | + } |
1439 | return true; |
1440 | } |
1441 | |
1442 | @@ -2706,7 +2887,7 @@ |
1443 | if (site.bo->need_stones_) { |
1444 | |
1445 | if (map.find_immovables( |
1446 | - Area<FCoords>(map.get_fcoords(site.site->get_position()), radius), |
1447 | + Area<FCoords>(map.get_fcoords(site.site->get_position()), 6), |
1448 | nullptr, |
1449 | |
1450 | FindImmovableAttribute(MapObjectDescr::get_attribute_id("granite"))) == 0) { |
1451 | @@ -2714,7 +2895,11 @@ |
1452 | // the destruction of the flag avoids that defaultAI will have too many |
1453 | // unused roads - if needed the road will be rebuild directly. |
1454 | flags_to_be_removed.push_back(site.site->base_flag().get_position()); |
1455 | - game().send_player_dismantle(*site.site); |
1456 | + if (connected_to_wh) { |
1457 | + game().send_player_dismantle(*site.site); |
1458 | + } else { |
1459 | + game().send_player_bulldoze(*site.site); |
1460 | + } |
1461 | return true; |
1462 | } |
1463 | |
1464 | @@ -2723,7 +2908,11 @@ |
1465 | // it is possible that there are stones but quary is not able to mine them |
1466 | site.bo->last_dismantle_time_ = game().get_gametime(); |
1467 | flags_to_be_removed.push_back(site.site->base_flag().get_position()); |
1468 | - game().send_player_dismantle(*site.site); |
1469 | + if (connected_to_wh) { |
1470 | + game().send_player_dismantle(*site.site); |
1471 | + } else { |
1472 | + game().send_player_bulldoze(*site.site); |
1473 | + } |
1474 | |
1475 | return true; |
1476 | } |
1477 | @@ -2743,7 +2932,7 @@ |
1478 | site.bo->space_consumer_ && !site.bo->plants_trees_) { |
1479 | |
1480 | // if we have more buildings then target |
1481 | - if (site.bo->cnt_built_ > site.bo->cnt_target_) { |
1482 | + if ((site.bo->cnt_built_ - site.bo->unconnected_) > site.bo->cnt_target_) { |
1483 | if (site.bo->stocklevel_time < game().get_gametime() - 5 * 1000) { |
1484 | site.bo->stocklevel_ = get_stocklevel(*site.bo); |
1485 | site.bo->stocklevel_time = game().get_gametime(); |
1486 | @@ -2753,7 +2942,11 @@ |
1487 | site.bo->stocklevel_ > 100) { // production stats == 0% |
1488 | site.bo->last_dismantle_time_ = game().get_gametime(); |
1489 | flags_to_be_removed.push_back(site.site->base_flag().get_position()); |
1490 | - game().send_player_dismantle(*site.site); |
1491 | + if (connected_to_wh) { |
1492 | + game().send_player_dismantle(*site.site); |
1493 | + } else { |
1494 | + game().send_player_bulldoze(*site.site); |
1495 | + } |
1496 | return true; |
1497 | } |
1498 | } |
1499 | @@ -2762,7 +2955,11 @@ |
1500 | if (site.site->get_statistics_percent() <= 10 && site.bo->cnt_built_ > 1) { |
1501 | |
1502 | flags_to_be_removed.push_back(site.site->base_flag().get_position()); |
1503 | - game().send_player_dismantle(*site.site); |
1504 | + if (connected_to_wh) { |
1505 | + game().send_player_dismantle(*site.site); |
1506 | + } else { |
1507 | + game().send_player_bulldoze(*site.site); |
1508 | + } |
1509 | return true; |
1510 | } |
1511 | |
1512 | @@ -2778,7 +2975,11 @@ |
1513 | |
1514 | site.bo->last_dismantle_time_ = game().get_gametime(); |
1515 | flags_to_be_removed.push_back(site.site->base_flag().get_position()); |
1516 | - game().send_player_dismantle(*site.site); |
1517 | + if (connected_to_wh) { |
1518 | + game().send_player_dismantle(*site.site); |
1519 | + } else { |
1520 | + game().send_player_bulldoze(*site.site); |
1521 | + } |
1522 | return true; |
1523 | } |
1524 | |
1525 | @@ -2792,7 +2993,11 @@ |
1526 | |
1527 | site.bo->last_dismantle_time_ = game().get_gametime(); |
1528 | flags_to_be_removed.push_back(site.site->base_flag().get_position()); |
1529 | - game().send_player_dismantle(*site.site); |
1530 | + if (connected_to_wh) { |
1531 | + game().send_player_dismantle(*site.site); |
1532 | + } else { |
1533 | + game().send_player_bulldoze(*site.site); |
1534 | + } |
1535 | return true; |
1536 | } |
1537 | |
1538 | @@ -2813,16 +3018,29 @@ |
1539 | |
1540 | site.bo->last_dismantle_time_ = game().get_gametime(); |
1541 | flags_to_be_removed.push_back(site.site->base_flag().get_position()); |
1542 | - game().send_player_dismantle(*site.site); |
1543 | + if (connected_to_wh) { |
1544 | + game().send_player_dismantle(*site.site); |
1545 | + } else { |
1546 | + game().send_player_bulldoze(*site.site); |
1547 | + } |
1548 | return true; |
1549 | } |
1550 | |
1551 | if (score > 120 && !site.site->is_stopped()) { |
1552 | |
1553 | game().send_player_start_stop_building(*site.site); |
1554 | + return true; |
1555 | } |
1556 | + const uint32_t trees_in_vicinity = |
1557 | + map.find_immovables(Area<FCoords>(map.get_fcoords(site.site->get_position()), 5), |
1558 | + nullptr, |
1559 | + FindImmovableAttribute(MapObjectDescr::get_attribute_id("tree"))); |
1560 | |
1561 | - if (score < 80 && site.site->is_stopped()) { |
1562 | + if (trees_in_vicinity > 25) { |
1563 | + if (!site.site->is_stopped()) { |
1564 | + game().send_player_start_stop_building(*site.site); |
1565 | + } |
1566 | + } else if (score < 80 && site.site->is_stopped()) { |
1567 | |
1568 | game().send_player_start_stop_building(*site.site); |
1569 | } |
1570 | @@ -3043,11 +3261,17 @@ |
1571 | // Get link to productionsite that should be checked |
1572 | ProductionSiteObserver& site = mines_.front(); |
1573 | |
1574 | + const bool connected_to_wh = !site.site->get_economy()->warehouses().empty(); |
1575 | + |
1576 | // first get rid of mines that are missing workers for some time (6 minutes), |
1577 | // released worker (if any) can be usefull elsewhere ! |
1578 | if (site.built_time_ + 6 * 60 * 1000 < gametime && !site.site->can_start_working()) { |
1579 | flags_to_be_removed.push_back(site.site->base_flag().get_position()); |
1580 | - game().send_player_dismantle(*site.site); |
1581 | + if (connected_to_wh) { |
1582 | + game().send_player_dismantle(*site.site); |
1583 | + } else { |
1584 | + game().send_player_bulldoze(*site.site); |
1585 | + } |
1586 | return true; |
1587 | } |
1588 | |
1589 | @@ -3059,7 +3283,11 @@ |
1590 | // dismantling when the failed count is too high |
1591 | if (site.no_resources_count > 12) { |
1592 | flags_to_be_removed.push_back(site.site->base_flag().get_position()); |
1593 | - game().send_player_dismantle(*site.site); |
1594 | + if (connected_to_wh) { |
1595 | + game().send_player_dismantle(*site.site); |
1596 | + } else { |
1597 | + game().send_player_bulldoze(*site.site); |
1598 | + } |
1599 | site.bo->construction_decision_time_ = gametime; |
1600 | return true; |
1601 | } |
1602 | @@ -3084,6 +3312,11 @@ |
1603 | return false; |
1604 | } |
1605 | |
1606 | + if (!connected_to_wh) { |
1607 | + // no enhancement possible |
1608 | + return false; |
1609 | + } |
1610 | + |
1611 | bool changed = false; |
1612 | if (player_->is_building_type_allowed(enhancement)) { |
1613 | // first exclude possibility there are enhancements in construction or unoccupied_ |
1614 | @@ -3212,6 +3445,18 @@ |
1615 | return count; |
1616 | } |
1617 | |
1618 | +// this just counts free positions in military and training sites |
1619 | +void DefaultAI::count_military_vacant_positions() { |
1620 | + // counting vacant positions |
1621 | + vacant_mil_positions_ = 0; |
1622 | + for (TrainingSiteObserver tso : trainingsites) { |
1623 | + vacant_mil_positions_ += tso.site->soldier_capacity() - tso.site->stationed_soldiers().size(); |
1624 | + } |
1625 | + for (MilitarySiteObserver mso : militarysites) { |
1626 | + vacant_mil_positions_ += mso.site->soldier_capacity() - mso.site->stationed_soldiers().size(); |
1627 | + } |
1628 | +} |
1629 | + |
1630 | // this function only manipulates with trainingsites' inputs priority |
1631 | // decreases it when too many unoccupied military buildings |
1632 | bool DefaultAI::check_trainingsites(uint32_t gametime) { |
1633 | @@ -3221,22 +3466,38 @@ |
1634 | if (!trainingsites.empty()) { |
1635 | taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + kTrainingSitesCheckInterval; |
1636 | } else { |
1637 | - taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + 5 * kTrainingSitesCheckInterval; |
1638 | - } |
1639 | - |
1640 | - uint8_t new_priority = DEFAULT_PRIORITY; |
1641 | - if (unstationed_milit_buildings_ > 2) { |
1642 | - new_priority = LOW_PRIORITY; |
1643 | - } else { |
1644 | - new_priority = DEFAULT_PRIORITY; |
1645 | - } |
1646 | + taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + 2 * kTrainingSitesCheckInterval; |
1647 | + return false; |
1648 | + } |
1649 | + |
1650 | + TrainingSite* ts = trainingsites.front().site; |
1651 | + TrainingSiteObserver& tso = trainingsites.front(); |
1652 | + |
1653 | + const BuildingIndex enhancement = ts->descr().enhancement(); |
1654 | + |
1655 | + if (enhancement != INVALID_INDEX && ts_without_trainers_ == 0 && mines_.size() > 3 && |
1656 | + (ts_basic_const_count_ + ts_advanced_const_count_) == 0 && ts_advanced_count_ > 0) { |
1657 | + |
1658 | + if (player_->is_building_type_allowed(enhancement)) { |
1659 | + game().send_player_enhance_building(*tso.site, enhancement); |
1660 | + } |
1661 | + } |
1662 | + |
1663 | + trainingsites.push_back(trainingsites.front()); |
1664 | + trainingsites.pop_front(); |
1665 | + |
1666 | + // changing capacity |
1667 | + if (tso.site->soldier_capacity() != 2) { |
1668 | + game().send_player_change_soldier_capacity(*ts, 2 - tso.site->soldier_capacity()); |
1669 | + } |
1670 | + |
1671 | + ts_without_trainers_ = 0; // zeroing |
1672 | for (std::list<TrainingSiteObserver>::iterator site = trainingsites.begin(); |
1673 | site != trainingsites.end(); |
1674 | ++site) { |
1675 | |
1676 | - for (uint32_t k = 0; k < site->bo->inputs_.size(); ++k) { |
1677 | - game().send_player_set_ware_priority( |
1678 | - *site->site, wwWARE, site->bo->inputs_.at(k), new_priority); |
1679 | + if (!site->site->can_start_working()) { |
1680 | + ts_without_trainers_ += 1; |
1681 | } |
1682 | } |
1683 | return true; |
1684 | @@ -3310,14 +3571,18 @@ |
1685 | BuildableField bf(f); |
1686 | update_buildable_field(bf, vision, true); |
1687 | const int32_t size_penalty = ms->get_size() - 1; |
1688 | + FindNodeAllyOwned find_ally(player_, game(), player_number()); |
1689 | + const int32_t allyOwnedFields = |
1690 | + map.find_fields(Area<FCoords>(f, vision), nullptr, find_ally); |
1691 | |
1692 | int16_t score = 0; |
1693 | - score += (bf.military_capacity_ > 5); |
1694 | - score += (bf.military_presence_ > 3); |
1695 | + score += (bf.area_military_capacity_ > 6); |
1696 | + score += (bf.area_military_capacity_ > 18); |
1697 | + score += (bf.area_military_presence_ > 4); |
1698 | score += (bf.military_loneliness_ < 180); |
1699 | - score += (bf.military_stationed_ > (2 + size_penalty)); |
1700 | - score -= (ms->soldier_capacity() * 2 > static_cast<uint32_t>(bf.military_capacity_)); |
1701 | - score += (bf.unowned_land_nearby_ < 10); |
1702 | + score += (bf.military_stationed_ > 2); |
1703 | + score -= size_penalty; |
1704 | + score += ((bf.unowned_land_nearby_ + allyOwnedFields) < 10); |
1705 | |
1706 | if (score >= 4) { |
1707 | if (ms->get_playercaps() & Widelands::Building::PCap_Dismantle) { |
1708 | @@ -3340,7 +3605,6 @@ |
1709 | |
1710 | // yes enemy is nearby, but still we must distinguish whether |
1711 | // he is accessible (over the land) |
1712 | - |
1713 | if (other_player_accessible( |
1714 | vision + 4, &unused1, &unused2, ms->get_position(), WalkSearch::kOtherPlayers)) { |
1715 | |
1716 | @@ -3502,7 +3766,10 @@ |
1717 | } |
1718 | } |
1719 | |
1720 | - throw wexception("Help: I do not know what to do with a %s", name); |
1721 | + throw wexception("Help: I (player %d / tribe %s) do not know what to do with a %s", |
1722 | + player_number(), |
1723 | + tribe_->name().c_str(), |
1724 | + name); |
1725 | } |
1726 | |
1727 | // this is called whenever we gain ownership of a PlayerImmovable |
1728 | @@ -3804,6 +4071,7 @@ |
1729 | |
1730 | // this is called whenever we gain a new building |
1731 | void DefaultAI::gain_building(Building& b) { |
1732 | + |
1733 | BuildingObserver& bo = get_building_observer(b.descr().name().c_str()); |
1734 | |
1735 | if (bo.type == BuildingObserver::CONSTRUCTIONSITE) { |
1736 | @@ -3817,6 +4085,17 @@ |
1737 | if (target_bo.type == BuildingObserver::MILITARYSITE) { |
1738 | ++num_milit_constructionsites; |
1739 | } |
1740 | + if (target_bo.type == BuildingObserver::MINE) { |
1741 | + mines_per_type[target_bo.mines_].in_construction += 1; |
1742 | + } |
1743 | + if (target_bo.type == BuildingObserver::TRAININGSITE) { |
1744 | + if (target_bo.trainingsite_type_ == TrainingSiteType::kBasic) { |
1745 | + ts_basic_const_count_ += 1; |
1746 | + } |
1747 | + if (target_bo.trainingsite_type_ == TrainingSiteType::kAdvanced) { |
1748 | + ts_advanced_const_count_ += 1; |
1749 | + } |
1750 | + } |
1751 | |
1752 | // Let defaultAI try to directly connect the constructionsite |
1753 | taskDue[ScheduleTasks::kRoadCheck] = game().get_gametime(); |
1754 | @@ -3852,6 +4131,9 @@ |
1755 | |
1756 | for (uint32_t i = 0; i < bo.inputs_.size(); ++i) |
1757 | ++wares.at(bo.inputs_.at(i)).consumers_; |
1758 | + |
1759 | + mines_per_type[bo.mines_].finished += 1; |
1760 | + |
1761 | } else if (bo.type == BuildingObserver::MILITARYSITE) { |
1762 | militarysites.push_back(MilitarySiteObserver()); |
1763 | militarysites.back().site = &dynamic_cast<MilitarySite&>(b); |
1764 | @@ -3860,9 +4142,16 @@ |
1765 | militarysites.back().enemies_nearby_ = true; |
1766 | |
1767 | } else if (bo.type == BuildingObserver::TRAININGSITE) { |
1768 | + ts_without_trainers_ += 1; |
1769 | trainingsites.push_back(TrainingSiteObserver()); |
1770 | trainingsites.back().site = &dynamic_cast<TrainingSite&>(b); |
1771 | trainingsites.back().bo = &bo; |
1772 | + if (bo.trainingsite_type_ == TrainingSiteType::kBasic) { |
1773 | + ts_basic_count_ += 1; |
1774 | + } |
1775 | + if (bo.trainingsite_type_ == TrainingSiteType::kAdvanced) { |
1776 | + ts_advanced_count_ += 1; |
1777 | + } |
1778 | |
1779 | } else if (bo.type == BuildingObserver::WAREHOUSE) { |
1780 | ++numof_warehouses_; |
1781 | @@ -3888,6 +4177,7 @@ |
1782 | |
1783 | // this is called whenever we lose a building |
1784 | void DefaultAI::lose_building(const Building& b) { |
1785 | + |
1786 | BuildingObserver& bo = get_building_observer(b.descr().name().c_str()); |
1787 | |
1788 | if (bo.type == BuildingObserver::CONSTRUCTIONSITE) { |
1789 | @@ -3901,6 +4191,17 @@ |
1790 | if (target_bo.type == BuildingObserver::MILITARYSITE) { |
1791 | --num_milit_constructionsites; |
1792 | } |
1793 | + if (target_bo.type == BuildingObserver::MINE) { |
1794 | + mines_per_type[target_bo.mines_].in_construction -= 1; |
1795 | + } |
1796 | + if (target_bo.type == BuildingObserver::TRAININGSITE) { |
1797 | + if (target_bo.trainingsite_type_ == TrainingSiteType::kBasic) { |
1798 | + ts_basic_const_count_ -= 1; |
1799 | + } |
1800 | + if (target_bo.trainingsite_type_ == TrainingSiteType::kAdvanced) { |
1801 | + ts_advanced_const_count_ -= 1; |
1802 | + } |
1803 | + } |
1804 | |
1805 | } else { |
1806 | --bo.cnt_built_; |
1807 | @@ -3938,6 +4239,9 @@ |
1808 | for (uint32_t i = 0; i < bo.inputs_.size(); ++i) { |
1809 | --wares.at(bo.inputs_.at(i)).consumers_; |
1810 | } |
1811 | + |
1812 | + mines_per_type[bo.mines_].finished -= 1; |
1813 | + |
1814 | } else if (bo.type == BuildingObserver::MILITARYSITE) { |
1815 | |
1816 | for (std::list<MilitarySiteObserver>::iterator i = militarysites.begin(); |
1817 | @@ -3955,6 +4259,12 @@ |
1818 | ++i) { |
1819 | if (i->site == &b) { |
1820 | trainingsites.erase(i); |
1821 | + if (bo.trainingsite_type_ == TrainingSiteType::kBasic) { |
1822 | + ts_basic_count_ -= 1; |
1823 | + } |
1824 | + if (bo.trainingsite_type_ == TrainingSiteType::kAdvanced) { |
1825 | + ts_advanced_count_ -= 1; |
1826 | + } |
1827 | break; |
1828 | } |
1829 | } |
1830 | @@ -3999,49 +4309,21 @@ |
1831 | return supplied == bo.inputs_.size(); |
1832 | } |
1833 | |
1834 | -/** |
1835 | - * The defaultAi "considers" via this function whether to attack an |
1836 | - * enemy, if opposing military buildings are in sight. In case of an attack it |
1837 | - * sends all available forces. |
1838 | - * |
1839 | - * \returns true, if attack was started. |
1840 | - */ |
1841 | - |
1842 | -bool DefaultAI::consider_attack(int32_t const gametime) { |
1843 | - |
1844 | - // we assume that we are not attacking so we extend waitperiod |
1845 | - // in case of attack the variable will be decreased below |
1846 | - // this is intended to save some CPU and add randomness in attacking |
1847 | - // and also differentiate according to type |
1848 | - next_attack_waittime_ += gametime % 30; |
1849 | - if (next_attack_waittime_ > 600 && type_ == DEFENSIVE) { |
1850 | - next_attack_waittime_ = 20; |
1851 | - } |
1852 | - if (next_attack_waittime_ > 450 && type_ == NORMAL) { |
1853 | - next_attack_waittime_ = 20; |
1854 | - } |
1855 | - if (next_attack_waittime_ > 300 && type_ == AGGRESSIVE) { |
1856 | - next_attack_waittime_ = 20; |
1857 | - } |
1858 | - |
1859 | - // Only useable, if it owns at least one militarysite |
1860 | - if (militarysites.empty()) { |
1861 | - taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime; |
1862 | - return false; |
1863 | - } |
1864 | - |
1865 | - // First we iterate over all players and define which ones (if any) |
1866 | - // are attackable (comparing overal strength) |
1867 | - // counting players in game |
1868 | - uint32_t plr_in_game = 0; |
1869 | +bool DefaultAI::check_enemy_sites(uint32_t const gametime) { |
1870 | + |
1871 | + Map& map = game().map(); |
1872 | + |
1873 | + // define which players are attackable |
1874 | std::vector<bool> player_attackable; |
1875 | - PlayerNumber const nr_players = game().map().get_nrplayers(); |
1876 | + PlayerNumber const nr_players = map.get_nrplayers(); |
1877 | player_attackable.resize(nr_players); |
1878 | - bool any_attackable = false; |
1879 | + uint32_t plr_in_game = 0; |
1880 | uint16_t const pn = player_number(); |
1881 | - std::unordered_set<uint32_t> irrelevant_immovables; |
1882 | - |
1883 | - std::vector<ImmovableFound> target_buildings; |
1884 | + |
1885 | + iterate_players_existing_novar(p, nr_players, game())++ plr_in_game; |
1886 | + |
1887 | + // receiving games statistics and parsing it (reading latest entry) |
1888 | + const Game::GeneralStatsVector& genstats = game().get_general_statistics(); |
1889 | |
1890 | // defining treshold ratio of own_strenght/enemy's_strength |
1891 | uint32_t treshold_ratio = 100; |
1892 | @@ -4052,37 +4334,10 @@ |
1893 | treshold_ratio = 120; |
1894 | } |
1895 | |
1896 | - iterate_players_existing_novar(p, nr_players, game())++ plr_in_game; |
1897 | - |
1898 | - // receiving games statistics and parsing it (reading latest entry) |
1899 | - const Game::GeneralStatsVector& genstats = game().get_general_statistics(); |
1900 | - |
1901 | - // first we try to prevent exhaustion of military forces (soldiers) |
1902 | - // via excessive attacking |
1903 | - // before building an economy with mines. |
1904 | - // 'Margin' is an difference between count of actual soldiers and |
1905 | - // military sites to be manned. |
1906 | - // If we have no mines yet, we need to preserve some soldiers for further |
1907 | - // expansion (if enemy allows this) |
1908 | - // TODO(sirver): this next line is completely unreadable and maybe even wrong given the |
1909 | - // precedence of ?: and +. Replace through some if/else. |
1910 | - int32_t needed_margin = (mines_.size() < 6) ? |
1911 | - ((6 - mines_.size()) * 3) : |
1912 | - 0 + 2 + ((type_ == NORMAL) ? 4 : 0 + ((type_ == DEFENSIVE) ? 8 : 0)); |
1913 | - const int32_t current_margin = |
1914 | - genstats[pn - 1].miltary_strength.back() - militarysites.size() - num_milit_constructionsites; |
1915 | - |
1916 | - if (current_margin < needed_margin) { // no attacking! |
1917 | - last_attack_target_.x = std::numeric_limits<uint16_t>::max(); |
1918 | - last_attack_target_.y = std::numeric_limits<uint16_t>::max(); |
1919 | - taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime; |
1920 | - return false; |
1921 | - } |
1922 | - |
1923 | // now we test all players which one are 'attackable' |
1924 | for (uint8_t j = 1; j <= plr_in_game; ++j) { |
1925 | - if (pn == j) { |
1926 | - player_attackable.at(j - 1) = false; |
1927 | + if (pn == j) { // its me |
1928 | + player_attackable[j - 1] = false; |
1929 | continue; |
1930 | } |
1931 | |
1932 | @@ -4095,12 +4350,10 @@ |
1933 | // Avoid division by zero |
1934 | } else if (genstats.at(j - 1).miltary_strength.back() == 0) { |
1935 | player_attackable.at(j - 1) = true; |
1936 | - any_attackable = true; |
1937 | // Check threshold |
1938 | } else if ((genstats.at(pn - 1).miltary_strength.back() * 100 / |
1939 | genstats.at(j - 1).miltary_strength.back()) > treshold_ratio) { |
1940 | player_attackable.at(j - 1) = true; |
1941 | - any_attackable = true; |
1942 | } else { |
1943 | player_attackable.at(j - 1) = false; |
1944 | } |
1945 | @@ -4112,147 +4365,229 @@ |
1946 | } |
1947 | } |
1948 | |
1949 | - // if we cannot attack anybody, terminating... |
1950 | - if (!any_attackable) { |
1951 | - taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime; |
1952 | - last_attack_target_.x = std::numeric_limits<uint16_t>::max(); |
1953 | - last_attack_target_.y = std::numeric_limits<uint16_t>::max(); |
1954 | - return false; |
1955 | - } |
1956 | - |
1957 | - // the logic of attacking is to pick n own military buildings - random ones |
1958 | - // and test the vicinity for attackable buildings |
1959 | - // candidates are put into target_buildings vector for later processing |
1960 | - const uint16_t test_every = 4; |
1961 | - Map& map = game().map(); |
1962 | - MilitarySite* best_ms_target = nullptr; |
1963 | - Warehouse* best_wh_target = nullptr; |
1964 | - int32_t best_ms_score = 0; |
1965 | - int32_t best_wh_score = 0; |
1966 | - const int8_t minimal_difference = 2; |
1967 | - |
1968 | - for (uint32_t position = gametime % test_every; position < militarysites.size(); |
1969 | - position += test_every) { |
1970 | - |
1971 | - std::list<MilitarySiteObserver>::iterator mso = militarysites.begin(); |
1972 | - std::advance(mso, position); |
1973 | - |
1974 | - MilitarySite* ms = mso->site; |
1975 | - |
1976 | - if (!mso->enemies_nearby_) { |
1977 | - continue; |
1978 | - } |
1979 | - |
1980 | + // first we scan vicitnity of couple of militarysites to get new enemy sites |
1981 | + // militarysites rotate |
1982 | + int32_t i = 0; |
1983 | + for (MilitarySiteObserver mso : militarysites) { |
1984 | + i += 1; |
1985 | + if (i % 4 == 0) |
1986 | + continue; |
1987 | + if (i > 20) |
1988 | + continue; |
1989 | + |
1990 | + MilitarySite* ms = mso.site; |
1991 | uint32_t const vision = ms->descr().vision_range(); |
1992 | FCoords f = map.get_fcoords(ms->get_position()); |
1993 | |
1994 | // get list of immovable around this our military site |
1995 | std::vector<ImmovableFound> immovables; |
1996 | - map.find_immovables(Area<FCoords>(f, vision + 3), &immovables, FindImmovableAttackable()); |
1997 | + map.find_immovables(Area<FCoords>(f, (vision + 3 < 13) ? 13 : vision + 3), |
1998 | + &immovables, |
1999 | + FindImmovableAttackable()); |
2000 | |
2001 | for (uint32_t j = 0; j < immovables.size(); ++j) { |
2002 | - |
2003 | - // skip if in irrelevant_immovables |
2004 | - const uint32_t hash = coords_hash(immovables.at(j).coords); |
2005 | - if (irrelevant_immovables.count(hash) == 0) { |
2006 | - irrelevant_immovables.insert(hash); |
2007 | - } |
2008 | - |
2009 | - // maybe these are not good candidates to attack |
2010 | - if (upcast(MilitarySite, bld, immovables.at(j).object)) { |
2011 | - |
2012 | - if (!player_attackable[bld->owner().player_number() - 1]) { |
2013 | - continue; |
2014 | - } |
2015 | - |
2016 | - // in case this is the same building as previously attacked |
2017 | - if (last_attack_target_ == bld->get_position()) { |
2018 | - continue; |
2019 | - } |
2020 | - |
2021 | + if (upcast(MilitarySite const, bld, immovables.at(j).object)) { |
2022 | + if (player_->is_hostile(bld->owner())) { |
2023 | + if (enemy_sites.count(coords_hash(bld->get_position())) == 0) { |
2024 | + enemy_sites[coords_hash(bld->get_position())] = EnemySiteObserver(); |
2025 | + } |
2026 | + } |
2027 | + } |
2028 | + if (upcast(Warehouse const, wh, immovables.at(j).object)) { |
2029 | + if (player_->is_hostile(wh->owner())) { |
2030 | + if (enemy_sites.count(coords_hash(wh->get_position())) == 0) { |
2031 | + enemy_sites[coords_hash(wh->get_position())] = EnemySiteObserver(); |
2032 | + } |
2033 | + } |
2034 | + } |
2035 | + } |
2036 | + } |
2037 | + |
2038 | + // now we update some of them |
2039 | + uint32_t best_target = std::numeric_limits<uint32_t>::max(); |
2040 | + uint8_t best_score = 0; |
2041 | + uint32_t count = 0; |
2042 | + |
2043 | + for (std::map<uint32_t, EnemySiteObserver>::iterator site = enemy_sites.begin(); |
2044 | + site != enemy_sites.end(); |
2045 | + ++site) { |
2046 | + |
2047 | + // we test max 12 sites and prefer ones tested more then 1 min ago |
2048 | + if (((site->second.last_tested + (enemysites_check_delay_ * 1000)) > gametime && count > 4) || |
2049 | + count > 12) { |
2050 | + continue; |
2051 | + } |
2052 | + count += 1; |
2053 | + |
2054 | + site->second.last_tested = gametime; |
2055 | + uint8_t defenders = 0; |
2056 | + bool is_warehouse = false; |
2057 | + bool is_attackable = false; |
2058 | + uint16_t onwer_number = 100; |
2059 | + |
2060 | + // testing if we can attack the building - result is a flag |
2061 | + // if we dont get a flag, we remove the building from observers list |
2062 | + FCoords f = map.get_fcoords(coords_unhash(site->first)); |
2063 | + uint32_t site_to_be_removed = std::numeric_limits<uint32_t>::max(); |
2064 | + Flag* flag = nullptr; |
2065 | + if (upcast(MilitarySite, bld, f.field->get_immovable())) { |
2066 | + if (player_->is_hostile(bld->owner())) { |
2067 | + defenders = bld->present_soldiers().size(); |
2068 | + flag = &bld->base_flag(); |
2069 | if (bld->can_attack()) { |
2070 | - |
2071 | - int32_t attack_soldiers = player_->find_attack_soldiers(bld->base_flag()); |
2072 | - if (attack_soldiers < 1) { |
2073 | - continue; |
2074 | - } |
2075 | - |
2076 | - const int32_t soldiers_difference = |
2077 | - player_->find_attack_soldiers(bld->base_flag()) - bld->present_soldiers().size(); |
2078 | - |
2079 | - if (soldiers_difference < minimal_difference) |
2080 | - continue; |
2081 | - if (soldiers_difference <= best_ms_score) |
2082 | - continue; |
2083 | - |
2084 | - best_ms_target = bld; |
2085 | - best_ms_score = soldiers_difference; |
2086 | - continue; |
2087 | - } |
2088 | - } else if (upcast(Warehouse, wh, immovables.at(j).object)) { |
2089 | - if (!player_->is_hostile(wh->owner())) { |
2090 | - continue; |
2091 | - } |
2092 | - |
2093 | - // in case this is the same building as previously attacked |
2094 | - if (last_attack_target_ == wh->get_position()) { |
2095 | - continue; |
2096 | - } |
2097 | - |
2098 | - if (wh->can_attack()) { |
2099 | - int32_t attack_soldiers = player_->find_attack_soldiers(wh->base_flag()); |
2100 | - if (attack_soldiers < 1) { |
2101 | - continue; |
2102 | - } |
2103 | - |
2104 | - const int32_t soldiers_difference = player_->find_attack_soldiers(wh->base_flag()) - |
2105 | - wh->present_soldiers().size() + |
2106 | - 3; //+3 is to boost attack here |
2107 | - |
2108 | - if (soldiers_difference < minimal_difference) |
2109 | - continue; |
2110 | - if (soldiers_difference <= best_wh_score) |
2111 | - continue; |
2112 | - |
2113 | - best_wh_target = wh; |
2114 | - best_wh_score = soldiers_difference; |
2115 | - } |
2116 | - } |
2117 | - } |
2118 | - } |
2119 | - |
2120 | - // we always try to attack warehouse first |
2121 | - if (best_wh_target != nullptr && gametime % 2 == 0) { |
2122 | - // attacking with all attack-ready soldiers |
2123 | - int32_t attackers = player_->find_attack_soldiers(best_wh_target->base_flag()); |
2124 | - |
2125 | - game().send_player_enemyflagaction(best_wh_target->base_flag(), pn, attackers); |
2126 | - last_attack_target_ = best_wh_target->get_position(); |
2127 | - taskDue[ScheduleTasks::kConsiderAttack] = (gametime % 10 + 10) * 1000 + gametime; |
2128 | - next_attack_waittime_ = 10; |
2129 | - return true; |
2130 | - |
2131 | - } else if (best_ms_target != nullptr) { |
2132 | - |
2133 | - // attacking with defenders + 6 soldiers |
2134 | - int32_t attackers = player_->find_attack_soldiers(best_ms_target->base_flag()); |
2135 | - const int32_t defenders = best_ms_target->present_soldiers().size(); |
2136 | - if (attackers > defenders + 10) { // we need to leave meaningful count of soldiers |
2137 | - // for next attack |
2138 | - attackers = defenders + 6; |
2139 | - } |
2140 | - |
2141 | - game().send_player_enemyflagaction(best_ms_target->base_flag(), pn, attackers); |
2142 | - last_attack_target_ = best_ms_target->get_position(); |
2143 | - taskDue[ScheduleTasks::kConsiderAttack] = (gametime % 10 + 10) * 1000 + gametime; |
2144 | - next_attack_waittime_ = 10; |
2145 | - return true; |
2146 | + is_attackable = true; |
2147 | + } |
2148 | + onwer_number = bld->owner().player_number(); |
2149 | + } |
2150 | + } |
2151 | + if (upcast(Warehouse, Wh, f.field->get_immovable())) { |
2152 | + if (player_->is_hostile(Wh->owner())) { |
2153 | + defenders = Wh->present_soldiers().size(); |
2154 | + flag = &Wh->base_flag(); |
2155 | + is_warehouse = true; |
2156 | + if (Wh->can_attack()) { |
2157 | + is_attackable = true; |
2158 | + } |
2159 | + onwer_number = Wh->owner().player_number(); |
2160 | + } |
2161 | + } |
2162 | + |
2163 | + // if flag is defined it is a good taget |
2164 | + if (flag) { |
2165 | + // updating some info |
2166 | + // updating info on mines nearby if needed |
2167 | + if (site->second.mines_nearby == ExtendedBool::kUnset) { |
2168 | + FindNodeMineable find_mines_spots_nearby(game(), f.field->get_resources()); |
2169 | + const int32_t minescount = |
2170 | + map.find_fields(Area<FCoords>(f, 6), nullptr, find_mines_spots_nearby); |
2171 | + if (minescount > 0) { |
2172 | + site->second.mines_nearby = ExtendedBool::kTrue; |
2173 | + } else { |
2174 | + site->second.mines_nearby = ExtendedBool::kFalse; |
2175 | + } |
2176 | + } |
2177 | + |
2178 | + site->second.warehouse = is_warehouse; |
2179 | + |
2180 | + // getting rid of default |
2181 | + if (site->second.last_time_attackable == std::numeric_limits<uint32_t>::max()) { |
2182 | + site->second.last_time_attackable = gametime; |
2183 | + } |
2184 | + |
2185 | + // can we attack: |
2186 | + if (is_attackable) { |
2187 | + site->second.attack_soldiers = player_->find_attack_soldiers(*flag); |
2188 | + } else { |
2189 | + site->second.attack_soldiers = 0; |
2190 | + } |
2191 | + |
2192 | + site->second.defenders = defenders; |
2193 | + |
2194 | + if (site->second.attack_soldiers > 0) { |
2195 | + site->second.score = site->second.attack_soldiers - site->second.defenders / 2; |
2196 | + |
2197 | + if (!is_warehouse) |
2198 | + site->second.score -= 1; |
2199 | + |
2200 | + // here is some differentiation based on "character" of a player |
2201 | + if (type_ == NORMAL) { |
2202 | + site->second.score -= 1; |
2203 | + site->second.score -= vacant_mil_positions_ / 10; |
2204 | + } else if (type_ == DEFENSIVE) { |
2205 | + site->second.score -= 2; |
2206 | + site->second.score -= vacant_mil_positions_ / 5; |
2207 | + } else { //=AGRESSIVE |
2208 | + site->second.score -= vacant_mil_positions_ / 15; |
2209 | + } |
2210 | + if (site->second.mines_nearby == ExtendedBool::kFalse) { |
2211 | + site->second.score -= 1; |
2212 | + } |
2213 | + // we dont want to attack multiple players at the same time too eagerly |
2214 | + if (onwer_number != last_attacked_player_) { |
2215 | + site->second.score -= 3; |
2216 | + } |
2217 | + // if we dont have mines yet |
2218 | + if (mines_.size() <= 2) { |
2219 | + site->second.score -= 2; |
2220 | + } |
2221 | + // also we should have at least some training sites |
2222 | + if ((ts_basic_count_ + ts_advanced_count_) == 0) { |
2223 | + site->second.score -= 2; |
2224 | + } |
2225 | + // treating no attack score |
2226 | + if (site->second.no_attack_counter < 0) { |
2227 | + site->second.score = 0; |
2228 | + site->second.no_attack_counter += 1; |
2229 | + } |
2230 | + } else { |
2231 | + site->second.score = 0; |
2232 | + } // or the score will remain 0 |
2233 | + |
2234 | + if (site->second.score > 0 && player_attackable[onwer_number - 1]) { |
2235 | + if (site->second.score > best_score) { |
2236 | + best_score = site->second.score; |
2237 | + best_target = site->first; |
2238 | + } |
2239 | + } |
2240 | + |
2241 | + if (site->second.attack_soldiers > 0) { |
2242 | + site->second.last_time_attackable = gametime; |
2243 | + } |
2244 | + if (site->second.last_time_attackable + 20 * 60 * 1000 < gametime) { |
2245 | + site_to_be_removed = site->first; |
2246 | + } |
2247 | + } else { // we dont have a flag, let remove the site from out observer list |
2248 | + site_to_be_removed = site->first; |
2249 | + } |
2250 | + |
2251 | + if (site_to_be_removed < std::numeric_limits<uint32_t>::max()) { |
2252 | + enemy_sites.erase(site_to_be_removed); |
2253 | + continue; |
2254 | + } |
2255 | + } |
2256 | + |
2257 | + // modifying enemysites_check_delay_,this depends on the count |
2258 | + // of enemysites in observer |
2259 | + if (count >= 13 && enemysites_check_delay_ < 180) { |
2260 | + enemysites_check_delay_ += 3; |
2261 | + } |
2262 | + if (count < 10 && enemysites_check_delay_ > 45) { |
2263 | + enemysites_check_delay_ -= 2; |
2264 | + } |
2265 | + |
2266 | + // if coordinates hash is not set |
2267 | + if (best_target == std::numeric_limits<uint32_t>::max()) { |
2268 | + return false; |
2269 | + } |
2270 | + |
2271 | + // attacking |
2272 | + FCoords f = map.get_fcoords(coords_unhash(best_target)); |
2273 | + // setting no attack counter here |
2274 | + // this gauranties that it will not be attacked in next 4 |
2275 | + // turns |
2276 | + enemy_sites[best_target].no_attack_counter = -4; |
2277 | + |
2278 | + Flag* flag = nullptr; // flag of a building to be attacked |
2279 | + if (upcast(MilitarySite, bld, f.field->get_immovable())) { |
2280 | + flag = &bld->base_flag(); |
2281 | + } else if (upcast(Warehouse, Wh, f.field->get_immovable())) { |
2282 | + flag = &Wh->base_flag(); |
2283 | } else { |
2284 | - taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime; |
2285 | - last_attack_target_.x = std::numeric_limits<uint16_t>::max(); |
2286 | - last_attack_target_.y = std::numeric_limits<uint16_t>::max(); |
2287 | + return false; // this should not happen |
2288 | + } |
2289 | + |
2290 | + // how many attack soldiers we can send? |
2291 | + uint32_t attackers = player_->find_attack_soldiers(*flag); |
2292 | + // Just add some randomness |
2293 | + attackers -= gametime % 3; |
2294 | + if (attackers <= 0) { |
2295 | return false; |
2296 | } |
2297 | + |
2298 | + game().send_player_enemyflagaction(*flag, player_number(), attackers); |
2299 | + last_attacked_player_ = flag->owner().player_number(); |
2300 | + |
2301 | + return true; |
2302 | } |
2303 | |
2304 | // This runs once in 15 minutes, and adjust wares targets based on number of |
2305 | @@ -4286,7 +4621,7 @@ |
2306 | // run over dueTasks map and returns task with lower duetime |
2307 | DefaultAI::ScheduleTasks DefaultAI::get_oldest_task(uint32_t const gametime) { |
2308 | |
2309 | - uint32_t oldestTaskTime = gametime; // we are looking for jobs due before now |
2310 | + uint32_t oldestTaskTime = gametime; // we are looking for jobs due before now |
2311 | ScheduleTasks DueTask = ScheduleTasks::kIdle; // default |
2312 | taskDue[ScheduleTasks::kIdle] = gametime; |
2313 | |
2314 | |
2315 | === modified file 'src/ai/defaultai.h' |
2316 | --- src/ai/defaultai.h 2015-03-05 20:57:07 +0000 |
2317 | +++ src/ai/defaultai.h 2015-03-26 20:42:28 +0000 |
2318 | @@ -9,6 +9,7 @@ |
2319 | * This program is distributed in the hope that it will be useful, |
2320 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2321 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2322 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2323 | * GNU General Public License for more details. |
2324 | * |
2325 | * You should have received a copy of the GNU General Public License |
2326 | @@ -82,9 +83,9 @@ |
2327 | enum class NewShip : uint8_t {kBuilt, kFoundOnLoad}; |
2328 | enum class ScheduleTasks : uint8_t { |
2329 | kBbuildableFieldsCheck, |
2330 | + kMineableFieldsCheck, |
2331 | kRoadCheck, |
2332 | kUnbuildableFCheck, |
2333 | - kConsiderAttack, |
2334 | kCheckEconomies, |
2335 | kProductionsitesStats, |
2336 | kConstructBuilding, |
2337 | @@ -96,7 +97,9 @@ |
2338 | kPrintStats, |
2339 | kIdle, |
2340 | kCheckMilitarysites, |
2341 | - kCheckTrainingsites |
2342 | + kCheckTrainingsites, |
2343 | + kCountMilitaryVacant, |
2344 | + kCheckEnemySites |
2345 | }; |
2346 | enum class MilitaryStrategy : uint8_t { |
2347 | kNoNewMilitary, |
2348 | @@ -161,7 +164,6 @@ |
2349 | int16_t* max_preciousness, |
2350 | int16_t* max_needed_preciousness); |
2351 | |
2352 | - |
2353 | ScheduleTasks get_oldest_task(uint32_t); |
2354 | |
2355 | bool construct_building(uint32_t); |
2356 | @@ -195,12 +197,14 @@ |
2357 | bool check_militarysites(uint32_t); |
2358 | bool marine_main_decisions(uint32_t); |
2359 | bool check_ships(uint32_t); |
2360 | + bool check_enemy_sites(uint32_t); |
2361 | void print_stats(uint32_t); |
2362 | uint32_t get_stocklevel_by_hint(size_t); |
2363 | uint32_t get_stocklevel(BuildingObserver&); |
2364 | uint32_t get_warehoused_stock(Widelands::WareIndex wt); |
2365 | uint32_t get_stocklevel(Widelands::WareIndex); // count all direct outputs_ |
2366 | void review_wares_targets(uint32_t); |
2367 | + void count_military_vacant_positions(); |
2368 | |
2369 | // sometimes scanning an area in radius gives inappropriate results, so this is to verify that |
2370 | // other player is accessible |
2371 | @@ -232,7 +236,7 @@ |
2372 | |
2373 | bool check_supply(const BuildingObserver&); |
2374 | |
2375 | - bool consider_attack(int32_t); |
2376 | + // bool consider_attack(int32_t); |
2377 | |
2378 | void print_land_stats(); |
2379 | |
2380 | @@ -252,6 +256,10 @@ |
2381 | uint32_t num_prod_constructionsites; |
2382 | uint32_t num_ports; |
2383 | |
2384 | + uint16_t last_attacked_player_; |
2385 | + // check ms in this interval - will auto-adjust |
2386 | + uint32_t enemysites_check_delay_; |
2387 | + |
2388 | std::list<Widelands::FCoords> unusable_fields; |
2389 | std::list<BuildableField*> buildable_fields; |
2390 | std::list<BlockedField> blocked_fields; |
2391 | @@ -268,6 +276,9 @@ |
2392 | std::list<TrainingSiteObserver> trainingsites; |
2393 | std::list<ShipObserver> allships; |
2394 | std::map<ScheduleTasks, uint32_t> taskDue; |
2395 | + std::map<uint32_t, EnemySiteObserver> enemy_sites; |
2396 | + // it will map mined material to observer |
2397 | + std::map<int32_t, MineTypesObserver> mines_per_type; |
2398 | |
2399 | std::vector<WareObserver> wares; |
2400 | |
2401 | @@ -284,10 +295,9 @@ |
2402 | // when territory is expanded for every candidate field benefits are calculated |
2403 | // but need for water, space, mines can vary |
2404 | // so if 255 = resource is needed, 0 = not needed |
2405 | - uint8_t resource_necessity_territory_; |
2406 | - uint8_t resource_necessity_mines_; |
2407 | - uint8_t resource_necessity_stones_; |
2408 | - uint8_t resource_necessity_water_; |
2409 | + int32_t resource_necessity_territory_; |
2410 | + int32_t resource_necessity_mines_; |
2411 | + int32_t resource_necessity_water_; |
2412 | bool resource_necessity_water_needed_; // unless atlanteans |
2413 | |
2414 | uint16_t unstationed_milit_buildings_; // counts empty military buildings (ones where no soldier |
2415 | @@ -295,14 +305,19 @@ |
2416 | uint16_t military_under_constr_; |
2417 | uint16_t military_last_dismantle_; |
2418 | uint32_t military_last_build_; // sometimes expansions just stops, this is time of last military |
2419 | - // building build |
2420 | - Widelands::Coords |
2421 | - last_attack_target_; // flag to abuilding (position) that was attacked last time |
2422 | - uint32_t next_attack_waittime_; // second till the next attack consideration |
2423 | - bool seafaring_economy; // false by default, until first port space is found |
2424 | + // building build |
2425 | + |
2426 | + bool seafaring_economy; // false by default, until first port space is found |
2427 | uint32_t colony_scan_area_; // distance from a possible port that is scanned for owned territory |
2428 | // it decreases with failed scans |
2429 | int32_t spots_; // sum of buildable fields |
2430 | + int32_t vacant_mil_positions_; // sum of vacant positions in militarysites and training sites |
2431 | + //statistics for training sites per type |
2432 | + uint8_t ts_basic_count_; |
2433 | + uint8_t ts_basic_const_count_; |
2434 | + uint8_t ts_advanced_count_; |
2435 | + uint8_t ts_advanced_const_count_; |
2436 | + uint8_t ts_without_trainers_; |
2437 | |
2438 | enum {kReprioritize, kStopShipyard, kStapShipyard}; |
2439 | |
2440 | |
2441 | === modified file 'tribes/atlanteans/dungeon/conf' |
2442 | --- tribes/atlanteans/dungeon/conf 2014-03-17 17:23:26 +0000 |
2443 | +++ tribes/atlanteans/dungeon/conf 2015-03-26 20:42:28 +0000 |
2444 | @@ -77,3 +77,6 @@ |
2445 | [idle] |
2446 | pics=dungeon_i_??.png # ??? |
2447 | hotspot=47 48 |
2448 | + |
2449 | +[aihints] |
2450 | +trainingsite_type=advanced |
2451 | |
2452 | === modified file 'tribes/atlanteans/labyrinth/conf' |
2453 | --- tribes/atlanteans/labyrinth/conf 2014-10-07 20:06:46 +0000 |
2454 | +++ tribes/atlanteans/labyrinth/conf 2015-03-26 20:42:28 +0000 |
2455 | @@ -99,4 +99,4 @@ |
2456 | hotspot=80 88 |
2457 | |
2458 | [aihints] |
2459 | -prohibited_till=2700 |
2460 | +trainingsite_type=basic |
2461 | |
2462 | === modified file 'tribes/barbarians/battlearena/conf' |
2463 | --- tribes/barbarians/battlearena/conf 2014-07-29 09:27:08 +0000 |
2464 | +++ tribes/barbarians/battlearena/conf 2015-03-26 20:42:28 +0000 |
2465 | @@ -74,3 +74,6 @@ |
2466 | pics=battlearena_w_??.png # ??? |
2467 | hotspot=110 72 |
2468 | fps=10 |
2469 | + |
2470 | +[aihints] |
2471 | +trainingsite_type=basic |
2472 | |
2473 | === modified file 'tribes/barbarians/trainingcamp/conf' |
2474 | --- tribes/barbarians/trainingcamp/conf 2014-09-24 21:06:00 +0000 |
2475 | +++ tribes/barbarians/trainingcamp/conf 2015-03-26 20:42:28 +0000 |
2476 | @@ -36,7 +36,7 @@ |
2477 | max_level=4 |
2478 | |
2479 | [aihints] |
2480 | -prohibited_till=2700 |
2481 | +trainingsite_type=advanced |
2482 | |
2483 | [soldier hp] |
2484 | min_level=0 |
2485 | |
2486 | === modified file 'tribes/empire/arena/conf' |
2487 | --- tribes/empire/arena/conf 2014-09-30 08:05:35 +0000 |
2488 | +++ tribes/empire/arena/conf 2015-03-26 20:42:28 +0000 |
2489 | @@ -54,3 +54,6 @@ |
2490 | [build] |
2491 | pics=arena_b_??.png # ??? |
2492 | hotspot=82 83 |
2493 | + |
2494 | +[aihints] |
2495 | +trainingsite_type=basic |
2496 | |
2497 | === modified file 'tribes/empire/colosseum/conf' |
2498 | --- tribes/empire/colosseum/conf 2014-09-30 08:05:35 +0000 |
2499 | +++ tribes/empire/colosseum/conf 2015-03-26 20:42:28 +0000 |
2500 | @@ -60,3 +60,6 @@ |
2501 | [idle] |
2502 | pics=colosseum_i_??.png # ??? |
2503 | hotspot=81 106 |
2504 | + |
2505 | +[aihints] |
2506 | +trainingsite_type=basic |
2507 | |
2508 | === modified file 'tribes/empire/piggery/conf' |
2509 | --- tribes/empire/piggery/conf 2014-08-02 19:55:23 +0000 |
2510 | +++ tribes/empire/piggery/conf 2015-03-26 20:42:28 +0000 |
2511 | @@ -2,6 +2,7 @@ |
2512 | output=meat |
2513 | |
2514 | [aihints] |
2515 | +forced_after=9000 |
2516 | |
2517 | [buildcost] |
2518 | log=2 |
2519 | |
2520 | === modified file 'tribes/empire/trainingcamp/conf' |
2521 | --- tribes/empire/trainingcamp/conf 2014-08-02 19:55:23 +0000 |
2522 | +++ tribes/empire/trainingcamp/conf 2015-03-26 20:42:28 +0000 |
2523 | @@ -127,4 +127,4 @@ |
2524 | hotspot=82 105 |
2525 | |
2526 | [aihints] |
2527 | -prohibited_till=2700 |
2528 | +trainingsite_type=advanced |
Please change the AI hint from ts_type=2 to something that is more understandable outside of the AI context, maybe trainingssite_ type="no_ bread_just_ meat" or something along these lines. Numbers are fully opaque to everybody that looks at the conf files.