1 | {$INCLUDE Switches.inc}
|
---|
2 | unit AI;
|
---|
3 |
|
---|
4 | interface
|
---|
5 |
|
---|
6 | uses
|
---|
7 | {$IFDEF DEBUG}SysUtils,{$ENDIF} // necessary for debug exceptions
|
---|
8 | Protocol, CustomAI, ToolAI;
|
---|
9 |
|
---|
10 | type
|
---|
11 | UnitRole = (Roam, Defend);
|
---|
12 |
|
---|
13 | TAI = class(TToolAI)
|
---|
14 | constructor Create(Nation: integer); override;
|
---|
15 |
|
---|
16 | protected
|
---|
17 | procedure DoTurn; override;
|
---|
18 | procedure DoNegotiation; override;
|
---|
19 | function ChooseResearchAdvance: integer; override;
|
---|
20 | function ChooseGovernment: integer; override;
|
---|
21 | function WantNegotiation(Nation: integer; NegoTime: TNegoTime): boolean; override;
|
---|
22 |
|
---|
23 | procedure ProcessSettlers;
|
---|
24 | procedure ProcessUnit(uix: integer; Role: UnitRole);
|
---|
25 | procedure SetCityProduction;
|
---|
26 | end;
|
---|
27 |
|
---|
28 |
|
---|
29 | implementation
|
---|
30 |
|
---|
31 | uses
|
---|
32 | Pile;
|
---|
33 |
|
---|
34 | const
|
---|
35 | // fine adjustment
|
---|
36 | Aggressive = 40; // 0 = never attacks, 100 = attacks even with heavy losses
|
---|
37 | DestroyBonus = 30; // percent of building cost
|
---|
38 |
|
---|
39 |
|
---|
40 | constructor TAI.Create(Nation: integer);
|
---|
41 | begin
|
---|
42 | inherited;
|
---|
43 | end;
|
---|
44 |
|
---|
45 |
|
---|
46 | //-------------------------------
|
---|
47 | // MY TURN
|
---|
48 | //-------------------------------
|
---|
49 |
|
---|
50 | procedure TAI.DoTurn;
|
---|
51 | var
|
---|
52 | uix: integer;
|
---|
53 | begin
|
---|
54 | // correct tax rate if necessary
|
---|
55 | if RO.Money > RO.nCity * 16 then
|
---|
56 | ChangeRates(RO.TaxRate - 10, 0)
|
---|
57 | else if RO.Money < RO.nCity * 8 then
|
---|
58 | ChangeRates(RO.TaxRate + 10, 0);
|
---|
59 |
|
---|
60 | // better government form available?
|
---|
61 | if RO.Government <> gAnarchy then
|
---|
62 | if IsResearched(adTheRepublic) then
|
---|
63 | begin
|
---|
64 | if RO.Government <> gRepublic then
|
---|
65 | Revolution;
|
---|
66 | end
|
---|
67 | else if IsResearched(adMonarchy) then
|
---|
68 | begin
|
---|
69 | if RO.Government <> gMonarchy then
|
---|
70 | Revolution;
|
---|
71 | end;
|
---|
72 |
|
---|
73 | // do combat
|
---|
74 | for uix := 0 to RO.nUn - 1 do
|
---|
75 | if (MyUnit[uix].Loc >= 0) and not (MyModel[MyUnit[uix].mix].Kind in
|
---|
76 | [mkSettler, mkSlaves]) then
|
---|
77 | ProcessUnit(uix, Roam);
|
---|
78 |
|
---|
79 | ProcessSettlers;
|
---|
80 |
|
---|
81 | // do discover/patrol
|
---|
82 |
|
---|
83 | OptimizeCityTiles;
|
---|
84 | SetCityProduction;
|
---|
85 | end;
|
---|
86 |
|
---|
87 |
|
---|
88 | // ProcessSettlers: move settlers, do terrain improvement, found cities
|
---|
89 | procedure TAI.ProcessSettlers;
|
---|
90 | var
|
---|
91 | uix, cix, ecix, Loc, RadiusLoc, TestScore, BestNearCityScore, TerrType, Special, V21: integer;
|
---|
92 | Radius: TVicinity21Loc;
|
---|
93 | ResourceScore, CityScore: array[0..lxmax * lymax - 1] of integer;
|
---|
94 |
|
---|
95 | procedure ReserveCityRadius(Loc: integer);
|
---|
96 | var
|
---|
97 | V21, RadiusLoc: integer;
|
---|
98 | Radius: TVicinity21Loc;
|
---|
99 | begin
|
---|
100 | V21_to_Loc(Loc, Radius);
|
---|
101 | for V21 := 1 to 26 do
|
---|
102 | begin
|
---|
103 | RadiusLoc := Radius[V21];
|
---|
104 | if (RadiusLoc >= 0) and (RadiusLoc < MapSize) then
|
---|
105 | ResourceScore[RadiusLoc] := 0;
|
---|
106 | end;
|
---|
107 | end;
|
---|
108 |
|
---|
109 | begin
|
---|
110 | JobAssignment_Initialize;
|
---|
111 |
|
---|
112 | // rate resources of all tiles
|
---|
113 | fillchar(ResourceScore, MapSize * sizeof(integer), 0);
|
---|
114 | for Loc := 0 to MapSize - 1 do
|
---|
115 | if (Map[Loc] and fRare) = 0 then
|
---|
116 | if (Map[Loc] and fTerrain) = fGrass then
|
---|
117 | if (Map[Loc] and fSpecial) <> 0 then
|
---|
118 | ResourceScore[Loc] := 3 // plains, 3 points
|
---|
119 | else
|
---|
120 | ResourceScore[Loc] := 2 // grassland, 2 points
|
---|
121 | else if (Map[Loc] and fSpecial) <> 0 then
|
---|
122 | ResourceScore[Loc] := 4; // special resource, 4 points
|
---|
123 | for cix := 0 to RO.nCity - 1 do
|
---|
124 | if MyCity[cix].Loc >= 0 then
|
---|
125 | ReserveCityRadius(MyCity[cix].Loc); // these resources already have a city
|
---|
126 | for uix := 0 to RO.nUn - 1 do
|
---|
127 | if (MyUnit[uix].Loc >= 0) and (MyUnit[uix].Job = jCity) then
|
---|
128 | ReserveCityRadius(MyUnit[uix].Loc); // these resources almost already have a city
|
---|
129 | for ecix := 0 to RO.nEnemyCity - 1 do
|
---|
130 | if RO.EnemyCity[ecix].Loc >= 0 then
|
---|
131 | ReserveCityRadius(RO.EnemyCity[ecix].Loc);
|
---|
132 | // these resources already have an enemy city
|
---|
133 |
|
---|
134 | // rate possible new cities
|
---|
135 | fillchar(CityScore, MapSize * sizeof(integer), 0);
|
---|
136 | for Loc := 0 to MapSize - 1 do
|
---|
137 | if ((Map[Loc] and fTerrain) = fGrass) and ((Map[Loc] and fRare) = 0) and
|
---|
138 | ((RO.Territory[Loc] < 0) or (RO.Territory[Loc] = me)) then
|
---|
139 | // don't consider founding cities in foreign nation territory
|
---|
140 | begin
|
---|
141 | TestScore := 0;
|
---|
142 | BestNearCityScore := 0;
|
---|
143 | V21_to_Loc(Loc, Radius);
|
---|
144 | for V21 := 1 to 26 do
|
---|
145 | begin // sum resource scores in potential city radius
|
---|
146 | RadiusLoc := Radius[V21];
|
---|
147 | if (RadiusLoc >= 0) and (RadiusLoc < MapSize) then
|
---|
148 | begin
|
---|
149 | TestScore := TestScore + ResourceScore[RadiusLoc];
|
---|
150 | if CityScore[RadiusLoc] > BestNearCityScore then
|
---|
151 | BestNearCityScore := CityScore[RadiusLoc];
|
---|
152 | end;
|
---|
153 | end;
|
---|
154 | if TestScore >= 10 then // city is worth founding
|
---|
155 | begin
|
---|
156 | TestScore := TestScore shl 8 + ((loc xor me) * 4567) mod 251;
|
---|
157 | // some unexactness, random but always the same for this tile
|
---|
158 | if TestScore > BestNearCityScore then
|
---|
159 | begin // better than all other sites in radius
|
---|
160 | if BestNearCityScore > 0 then // found no other cities in radius
|
---|
161 | begin
|
---|
162 | for V21 := 1 to 26 do
|
---|
163 | begin
|
---|
164 | RadiusLoc := Radius[V21];
|
---|
165 | if (RadiusLoc >= 0) and (RadiusLoc < MapSize) then
|
---|
166 | CityScore[RadiusLoc] := 0;
|
---|
167 | end;
|
---|
168 | end;
|
---|
169 | CityScore[Loc] := TestScore;
|
---|
170 | end;
|
---|
171 | end;
|
---|
172 | end;
|
---|
173 | for Loc := 0 to MapSize - 1 do
|
---|
174 | if CityScore[Loc] > 0 then
|
---|
175 | JobAssignment_AddJob(Loc, jCity, 10);
|
---|
176 |
|
---|
177 | // improve terrain
|
---|
178 | for cix := 0 to RO.nCity - 1 do
|
---|
179 | with MyCity[cix] do
|
---|
180 | if Loc >= 0 then
|
---|
181 | begin
|
---|
182 | V21_to_Loc(Loc, Radius);
|
---|
183 | for V21 := 1 to 26 do
|
---|
184 | if (Tiles and (1 shl V21) and not (1 shl CityOwnTile)) <> 0 then
|
---|
185 | begin // tile is exploited, but not the city own tile -- check if improvable
|
---|
186 | RadiusLoc := Radius[V21];
|
---|
187 | Assert((RadiusLoc >= 0) and (RadiusLoc < MapSize));
|
---|
188 | if (RadiusLoc >= 0) and (RadiusLoc < MapSize) then
|
---|
189 | begin
|
---|
190 | TerrType := Map[RadiusLoc] and fTerrain;
|
---|
191 | Special := Map[RadiusLoc] shr 5 and 3;
|
---|
192 | if TerrType >= fGrass then // can't improve water tiles
|
---|
193 | if (Terrain[TerrType].IrrEff > 0) // terrain is irrigatable
|
---|
194 | and not ((RO.Government = gDespotism) and
|
---|
195 | (Terrain[TerrType].FoodRes[Special] >= 3))
|
---|
196 | // improvement makes no sense when limit is depotism
|
---|
197 | and ((Map[RadiusLoc] and fTerImp) = 0) then // no terrain improvement yet
|
---|
198 | JobAssignment_AddJob(RadiusLoc, jIrr, 50) // irrigate!
|
---|
199 | else if (Terrain[TerrType].MoveCost = 1) // plain terrain
|
---|
200 | and ((Map[RadiusLoc] and (fRoad or fRR or fRiver)) = 0) then
|
---|
201 | // no road or railroad yet, no river
|
---|
202 | JobAssignment_AddJob(RadiusLoc, jRoad, 40);
|
---|
203 | // build road (The Wheel trade benefit)
|
---|
204 | end;
|
---|
205 | end;
|
---|
206 | end;
|
---|
207 |
|
---|
208 | // choose all settlers to work
|
---|
209 | for uix := 0 to RO.nUn - 1 do
|
---|
210 | if (MyUnit[uix].Loc >= 0) and (MyModel[MyUnit[uix].mix].Kind in
|
---|
211 | [mkSettler, mkSlaves]) then
|
---|
212 | JobAssignment_AddUnit(uix);
|
---|
213 |
|
---|
214 | JobAssignment_Go;
|
---|
215 | end; // ProcessSettlers
|
---|
216 |
|
---|
217 |
|
---|
218 | // ProcessUnit: execute attack, capture, discover or patrol task according to unit role
|
---|
219 | procedure TAI.ProcessUnit(uix: integer; Role: UnitRole);
|
---|
220 | const
|
---|
221 | DistanceScore = 4;
|
---|
222 | var
|
---|
223 | BestScore, BestCount, BestLoc, TerrType, TestLoc, NextLoc, TestDistance,
|
---|
224 | Tile, V8, TestScore, euix, MyDamage, EnemyDamage, TerrOwner, StepSize, OldLoc,
|
---|
225 | AttackForecast, MoveResult, AttackResult: integer;
|
---|
226 | Exhausted: boolean;
|
---|
227 | TestTask, BestTask: (utNone, utAttack, utCapture, utDiscover, utPatrol, utGoHome);
|
---|
228 | Adjacent: TVicinity8Loc;
|
---|
229 | AdjacentUnknown: array[0..lxmax * lymax - 1] of integer;
|
---|
230 | begin
|
---|
231 | Pile.Create(MapSize);
|
---|
232 | with MyUnit[uix] do
|
---|
233 | repeat
|
---|
234 | BestScore := -999999;
|
---|
235 | BestTask := utNone;
|
---|
236 | FillChar(AdjacentUnknown, SizeOf(AdjacentUnknown), $FF);
|
---|
237 | // -1, indicates tiles not checked yet
|
---|
238 | Pile.Empty;
|
---|
239 | Pile.Put(Loc, 0); // start search for something to do at current location
|
---|
240 | while Pile.Get(TestLoc, TestDistance) do
|
---|
241 | begin
|
---|
242 | TestScore := 0;
|
---|
243 | Tile := Map[TestLoc];
|
---|
244 | AdjacentUnknown[TestLoc] := 0;
|
---|
245 |
|
---|
246 | if ((Tile and fUnit) <> 0) and ((Tile and fOwned) = 0) then
|
---|
247 | begin // enemy unit
|
---|
248 | Unit_FindEnemyDefender(TestLoc, euix);
|
---|
249 | if RO.Treaty[RO.EnemyUn[euix].Owner] < trPeace then
|
---|
250 | begin // unfriendly unit -- check attack
|
---|
251 | if Unit_AttackForecast(uix, TestLoc, 100, AttackForecast) then
|
---|
252 | begin // attack possible, but advantageous?
|
---|
253 | if AttackForecast > 0 then
|
---|
254 | begin // enemy unit would be destroyed
|
---|
255 | MyDamage := Health - AttackForecast;
|
---|
256 | EnemyDamage := RO.EnemyUn[euix].Health + DestroyBonus;
|
---|
257 | end
|
---|
258 | else // own unit would be destroyed
|
---|
259 | begin
|
---|
260 | MyDamage := Health + DestroyBonus;
|
---|
261 | EnemyDamage := RO.EnemyUn[euix].Health + AttackForecast;
|
---|
262 | end;
|
---|
263 | TestScore := Aggressive * 2 *
|
---|
264 | (EnemyDamage * RO.EnemyModel[RO.EnemyUn[euix].emix].Cost) div
|
---|
265 | (MyDamage * MyModel[mix].Cost);
|
---|
266 | if TestScore <= 100 then
|
---|
267 | TestScore := 0 // own losses exceed enemy losses, no good
|
---|
268 | else
|
---|
269 | begin
|
---|
270 | TestScore := (TestScore - 100) div 10 + 30;
|
---|
271 | TestTask := utAttack;
|
---|
272 | end;
|
---|
273 | end;
|
---|
274 | end;
|
---|
275 | end // enemy unit
|
---|
276 |
|
---|
277 | else if ((Tile and fCity) <> 0) and ((Tile and fOwned) = 0) then
|
---|
278 | begin // enemy city, empty or unobserved
|
---|
279 | if (MyModel[mix].Domain = dGround)
|
---|
280 | // ships of this AI have no long-range guns, so don't try to attack cities with them
|
---|
281 | and ((RO.Territory[TestLoc] <
|
---|
282 | 0) // happens only for unobserved cities of extinct tribes, new owner unknown
|
---|
283 | or (RO.Treaty[RO.Territory[TestLoc]] < trPeace)) then
|
---|
284 | begin // unfriendly city -- check attack/capture
|
---|
285 | if (Tile and fObserved) <> 0 then
|
---|
286 | begin // observed and no unit present -- city is undefended, capture!
|
---|
287 | TestScore := 40;
|
---|
288 | TestTask := utCapture;
|
---|
289 | end
|
---|
290 | else if Role = Roam then
|
---|
291 | begin // unobserved city, possibly defended -- go for attack
|
---|
292 | TestScore := 30;
|
---|
293 | TestTask := utPatrol;
|
---|
294 | end;
|
---|
295 | end;
|
---|
296 | end // enemy city, empty or unobserved
|
---|
297 |
|
---|
298 | else
|
---|
299 | begin // no enemy city or unit here
|
---|
300 | // add surrounding tiles to queue, but only if there's a chance to beat BestScore
|
---|
301 | if 50 - DistanceScore * (TestDistance + 1) >= BestScore then
|
---|
302 | // assume a score of 50 is the best achievable
|
---|
303 | begin
|
---|
304 | V8_to_Loc(TestLoc, Adjacent);
|
---|
305 | for V8 := 0 to 7 do
|
---|
306 | begin
|
---|
307 | NextLoc := Adjacent[V8];
|
---|
308 | if (NextLoc >= 0) and (NextLoc < MapSize) and
|
---|
309 | (AdjacentUnknown[NextLoc] < 0) then // tile not checked yet
|
---|
310 | begin
|
---|
311 | TerrType := Map[NextLoc] and fTerrain;
|
---|
312 | if TerrType = fUNKNOWN then
|
---|
313 | Inc(AdjacentUnknown[TestLoc])
|
---|
314 | else
|
---|
315 | begin
|
---|
316 | case MyModel[mix].Domain of
|
---|
317 | dGround:
|
---|
318 | begin
|
---|
319 | TerrOwner := RO.Territory[NextLoc];
|
---|
320 | if (TerrType >= fGrass) and
|
---|
321 | (TerrType <> fArctic) // terrain can be walked
|
---|
322 | and ((TerrOwner < 0) or (TerrOwner = me) or
|
---|
323 | (RO.Treaty[TerrOwner] < trPeace)) // no peace treaty violated
|
---|
324 | and (((Map[NextLoc] and (fUnit or fCity)) <>
|
---|
325 | 0) or (Map[TestLoc] and Map[NextLoc] and fInEnemyZoC = 0)) then
|
---|
326 | // no ZoC violated
|
---|
327 | begin // yes, consider walking this tile
|
---|
328 | if TerrType = fMountains then
|
---|
329 | StepSize := 2 // mountains cause delay
|
---|
330 | else
|
---|
331 | StepSize := 1;
|
---|
332 | end
|
---|
333 | else
|
---|
334 | StepSize := 0; // no, don't walk here
|
---|
335 | end;
|
---|
336 | dSea:
|
---|
337 | if TerrType = fShore then // ships of this AI can only move along shore
|
---|
338 | StepSize := 1
|
---|
339 | else
|
---|
340 | StepSize := 0;
|
---|
341 | dAir:
|
---|
342 | StepSize := 1;
|
---|
343 | end;
|
---|
344 | if StepSize > 0 then
|
---|
345 | Pile.Put(NextLoc, TestDistance + StepSize);
|
---|
346 | end;
|
---|
347 | end;
|
---|
348 | end;
|
---|
349 | end;
|
---|
350 | if Role = Defend then TestScore := 0 // don't discover/patrol
|
---|
351 | else if AdjacentUnknown[TestLoc] > 0 then
|
---|
352 | begin
|
---|
353 | TestScore := 20 + AdjacentUnknown[TestLoc];
|
---|
354 | TestTask := utDiscover;
|
---|
355 | end
|
---|
356 | else
|
---|
357 | begin
|
---|
358 | TestScore := (RO.Turn - RO.MapObservedLast[TestLoc]) div 10;
|
---|
359 | TestTask := utPatrol;
|
---|
360 | end;
|
---|
361 | end; // no enemy city or unit here
|
---|
362 |
|
---|
363 | if TestScore > 0 then
|
---|
364 | begin
|
---|
365 | TestScore := TestScore - DistanceScore * TestDistance;
|
---|
366 | if TestScore > BestScore then
|
---|
367 | BestCount := 0;
|
---|
368 | if TestScore >= BestScore then
|
---|
369 | begin
|
---|
370 | Inc(BestCount);
|
---|
371 | if random(BestCount) = 0 then
|
---|
372 | begin
|
---|
373 | BestScore := TestScore;
|
---|
374 | BestLoc := TestLoc;
|
---|
375 | BestTask := TestTask;
|
---|
376 | end;
|
---|
377 | end;
|
---|
378 | end;
|
---|
379 | end;
|
---|
380 |
|
---|
381 | if (BestTask = utNone) and ((Map[Loc] and fCity) = 0) then
|
---|
382 | begin // nothing to do, move home
|
---|
383 | if Home >= 0 then
|
---|
384 | BestLoc := MyCity[Home].Loc
|
---|
385 | else
|
---|
386 | BestLoc := maNextCity;
|
---|
387 | BestTask := utGoHome;
|
---|
388 | end;
|
---|
389 | if BestTask <> utNone then
|
---|
390 | begin // attack/capture/discover/patrol task found, execute it
|
---|
391 | OldLoc := Loc;
|
---|
392 | MoveResult := Unit_Move(uix, BestLoc);
|
---|
393 | Exhausted := (Loc = OldLoc) or
|
---|
394 | ((MoveResult and (rMoreTurns or rUnitRemoved)) <> 0);
|
---|
395 | if (BestTask = utAttack) and ((MoveResult and rLocationReached) <> 0) then
|
---|
396 | if Movement < 100 then
|
---|
397 | Exhausted := True
|
---|
398 | else
|
---|
399 | begin
|
---|
400 | AttackResult := Unit_Attack(uix, BestLoc);
|
---|
401 | Exhausted := ((AttackResult and rExecuted) = 0) or
|
---|
402 | ((AttackResult and rUnitRemoved) <> 0);
|
---|
403 | end;
|
---|
404 | if not Exhausted then
|
---|
405 | Exhausted := (Movement < 100) and
|
---|
406 | ((Map[Loc] and (fRoad or fRR or fRiver or fCity)) = 0);
|
---|
407 | // no road, too few movement points for further movement
|
---|
408 | end
|
---|
409 | else
|
---|
410 | Exhausted := True;
|
---|
411 | until Exhausted;
|
---|
412 | Pile.Free;
|
---|
413 | end; // ProcessUnit
|
---|
414 |
|
---|
415 |
|
---|
416 | // SetCityProduction: choose production of each city
|
---|
417 | procedure TAI.SetCityProduction;
|
---|
418 | var
|
---|
419 | cix, mix, mixSettler, mixShip, mixArmy, V8, NewImprovement, Count, wix, AdjacentLoc: integer;
|
---|
420 | IsPort: boolean;
|
---|
421 | Adjacent: TVicinity8Loc;
|
---|
422 | Report: TCityReport;
|
---|
423 |
|
---|
424 | procedure TryBuild(Improvement: integer);
|
---|
425 | begin
|
---|
426 | if (NewImprovement < 0) // already improvement of higher priority found
|
---|
427 | and (MyCity[cix].Built[Improvement] = 0) // not built yet
|
---|
428 | and City_Improvable(cix, Improvement) then
|
---|
429 | NewImprovement := Improvement;
|
---|
430 | end;
|
---|
431 |
|
---|
432 | begin
|
---|
433 | // only produce newest models
|
---|
434 | mixSettler := -1;
|
---|
435 | mixArmy := -1;
|
---|
436 | mixShip := -1;
|
---|
437 | for mix := 0 to RO.nModel - 1 do
|
---|
438 | with MyModel[mix] do
|
---|
439 | if Kind = mkSettler then
|
---|
440 | mixSettler := mix
|
---|
441 | else if (Domain = dGround) and (Kind < mkSpecial_TownGuard) then
|
---|
442 | mixArmy := mix
|
---|
443 | else if Domain = dSea then
|
---|
444 | mixShip := mix;
|
---|
445 |
|
---|
446 | for cix := 0 to RO.nCity - 1 do
|
---|
447 | with MyCity[cix] do
|
---|
448 | if (RO.Turn = 0) or ((Flags and chProduction) <> 0) // city production complete
|
---|
449 | or not City_HasProject(cix) then
|
---|
450 | begin // check production
|
---|
451 | IsPort := False;
|
---|
452 | V8_to_Loc(Loc, Adjacent);
|
---|
453 | for v8 := 0 to 7 do
|
---|
454 | begin
|
---|
455 | AdjacentLoc := Adjacent[V8];
|
---|
456 | if (AdjacentLoc >= 0) and (AdjacentLoc < MapSize) and
|
---|
457 | ((Map[AdjacentLoc] and fTerrain) = fShore) then
|
---|
458 | IsPort := True; // shore tile at adjacent location -- city is port!
|
---|
459 | end;
|
---|
460 | City_GetReport(cix, Report);
|
---|
461 |
|
---|
462 | if (Report.Support = 0) or (SupportFree[RO.Government] < 2) and
|
---|
463 | (Report.Support < Report.ProdRep div 2) then
|
---|
464 | begin // enough material to support more units
|
---|
465 | if (RO.Turn > 4) and ((Report.Eaten - Size * 2) div
|
---|
466 | SettlerFood[RO.Government] < Size div 4) then
|
---|
467 | // less than 1 settler per 4 citizens -- produce more!
|
---|
468 | City_StartUnitProduction(cix, mixSettler)
|
---|
469 | else if IsPort and (mixShip >= 0) and (random(2) = 0) then
|
---|
470 | City_StartUnitProduction(cix, mixShip)
|
---|
471 | else
|
---|
472 | City_StartUnitProduction(cix, mixArmy);
|
---|
473 | end
|
---|
474 | else
|
---|
475 | begin // check for building a city improvement
|
---|
476 | NewImprovement := -1;
|
---|
477 | if Built[imPalace] + Built[imCourt] + Built[imTownHall] = 0 then
|
---|
478 | begin
|
---|
479 | TryBuild(imCourt);
|
---|
480 | TryBuild(imTownHall);
|
---|
481 | end;
|
---|
482 | if Report.Trade - Report.Corruption >= 11 then
|
---|
483 | TryBuild(imLibrary);
|
---|
484 | if Report.Trade - Report.Corruption >= 11 then
|
---|
485 | TryBuild(imMarket);
|
---|
486 | if Size >= 9 then
|
---|
487 | TryBuild(imHighways);
|
---|
488 | if (RO.Government <> gDespotism) and (Size >= 4) then
|
---|
489 | TryBuild(imTemple);
|
---|
490 | if (RO.Government <> gDespotism) and (Size >= 6) then
|
---|
491 | TryBuild(imTheater);
|
---|
492 | if (RO.Government <> gDespotism) and (Size >= 8) then
|
---|
493 | TryBuild(imAqueduct);
|
---|
494 | if (Report.ProdRep >= 4) or (RO.nCity = 1) then
|
---|
495 | TryBuild(imBarracks);
|
---|
496 | TryBuild(imWalls);
|
---|
497 | if IsPort then
|
---|
498 | TryBuild(imCoastalFort);
|
---|
499 | if NewImprovement < 0 then
|
---|
500 | begin // nothing to produce -- check for building a wonder
|
---|
501 | Count := 0;
|
---|
502 | for wix := 0 to nImp - 1 do
|
---|
503 | if (Imp[wix].Kind = ikWonder) and (RO.Wonder[wix].CityID = -1) // not built yet
|
---|
504 | and ((Report.ProdRep - Report.Support) * 40 >= Imp[wix].Cost)
|
---|
505 | // takes less than 40 turns to produce
|
---|
506 | and City_Improvable(cix, wix) then
|
---|
507 | begin
|
---|
508 | Inc(Count);
|
---|
509 | if random(Count) = 0 then
|
---|
510 | NewImprovement := wix; // yes, build this wonder!
|
---|
511 | end;
|
---|
512 | end;
|
---|
513 | if NewImprovement >= 0 then
|
---|
514 | City_StartImprovement(cix, NewImprovement)
|
---|
515 | else if City_HasProject(cix) then
|
---|
516 | City_StopProduction(cix); // nothing to produce
|
---|
517 | end;
|
---|
518 | end; // check production
|
---|
519 | end; // SetCityProduction
|
---|
520 |
|
---|
521 |
|
---|
522 | function TAI.ChooseResearchAdvance: integer;
|
---|
523 | var
|
---|
524 | mix: integer;
|
---|
525 | begin
|
---|
526 | if not IsResearched(adWheel) then
|
---|
527 | begin
|
---|
528 | Result := adWheel;
|
---|
529 | exit;
|
---|
530 | end // research the wheel first
|
---|
531 | else if not IsResearched(adWarriorCode) then
|
---|
532 | begin
|
---|
533 | Result := adWarriorCode;
|
---|
534 | exit;
|
---|
535 | end // research warrior code first
|
---|
536 | else if not IsResearched(adHorsebackRiding) then
|
---|
537 | begin
|
---|
538 | Result := adHorsebackRiding;
|
---|
539 | exit;
|
---|
540 | end; // research horseback riding first
|
---|
541 |
|
---|
542 | Result := -1; // random advance
|
---|
543 | if random(10) = 0 then
|
---|
544 | begin // check military research
|
---|
545 | Result := adMilitary;
|
---|
546 | if IsResearched(adMapMaking) and (random(2) = 0) then
|
---|
547 | begin // try to develop new ship
|
---|
548 | PrepareNewModel(dSea);
|
---|
549 | SetNewModelFeature(mcDefense, 3);
|
---|
550 | SetNewModelFeature(mcOffense, RO.DevModel.MaxWeight - 3);
|
---|
551 | end
|
---|
552 | else
|
---|
553 | begin // try to develop new ground unit
|
---|
554 | PrepareNewModel(dGround);
|
---|
555 | SetNewModelFeature(mcDefense, 1);
|
---|
556 | SetNewModelFeature(mcOffense, RO.DevModel.MaxWeight - 4);
|
---|
557 | SetNewModelFeature(mcMob, 2);
|
---|
558 | end;
|
---|
559 |
|
---|
560 | // don't develop model twice
|
---|
561 | for mix := 0 to RO.nModel - 1 do
|
---|
562 | if (RO.DevModel.Domain = MyModel[mix].Domain) and
|
---|
563 | (RO.DevModel.Attack = MyModel[mix].Attack) and
|
---|
564 | (RO.DevModel.Defense = MyModel[mix].Defense) then
|
---|
565 | Result := -1; // already have this model
|
---|
566 | end;
|
---|
567 | end; // ChooseResearchAdvance
|
---|
568 |
|
---|
569 |
|
---|
570 | function TAI.ChooseGovernment: integer;
|
---|
571 | begin
|
---|
572 | if IsResearched(adTheRepublic) then
|
---|
573 | Result := gRepublic
|
---|
574 | else if IsResearched(adMonarchy) then
|
---|
575 | Result := gMonarchy
|
---|
576 | else
|
---|
577 | Result := gDespotism;
|
---|
578 | end;
|
---|
579 |
|
---|
580 |
|
---|
581 | //-------------------------------
|
---|
582 | // DIPLOMACY
|
---|
583 | //-------------------------------
|
---|
584 |
|
---|
585 | function TAI.WantNegotiation(Nation: integer; NegoTime: TNegoTime): boolean;
|
---|
586 | begin
|
---|
587 | Result := (NegoTime = EnemyCalled) // always accept contact
|
---|
588 | or (NegoTime = EndOfTurn) and (RO.Turn mod 20 = Nation + me);
|
---|
589 | // ask for contact only once in 20 turns
|
---|
590 | end;
|
---|
591 |
|
---|
592 | procedure TAI.DoNegotiation;
|
---|
593 | begin
|
---|
594 | if (RO.Treaty[Opponent] < trPeace) and Odd(me + Opponent) then
|
---|
595 | // make peace with some random nations
|
---|
596 | if (OppoAction = scDipOffer) and (OppoOffer.nCost = 0) and
|
---|
597 | (OppoOffer.nDeliver = 1) and (OppoOffer.Price[0] = opTreaty + trPeace) then
|
---|
598 | MyAction := scDipAccept // accept peace
|
---|
599 | else if OppoAction = scDipStart then
|
---|
600 | begin
|
---|
601 | MyOffer.nCost := 0;
|
---|
602 | MyOffer.nDeliver := 1;
|
---|
603 | MyOffer.Price[0] := opTreaty + trPeace; // offer peace in exchange of nothing
|
---|
604 | MyAction := scDipOffer;
|
---|
605 | end;
|
---|
606 | end;
|
---|
607 |
|
---|
608 | end.
|
---|