| 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.
|
|---|