| 1 | {$INCLUDE Switches.inc}
|
|---|
| 2 | unit ToolAI;
|
|---|
| 3 |
|
|---|
| 4 | interface
|
|---|
| 5 |
|
|---|
| 6 | uses
|
|---|
| 7 | {$IFDEF DEBUG}SysUtils,{$ENDIF} // necessary for debug exceptions
|
|---|
| 8 | {$IFDEF DEBUG}Names,{$ENDIF}
|
|---|
| 9 | Protocol, CustomAI;
|
|---|
| 10 |
|
|---|
| 11 | type
|
|---|
| 12 | TGroupTransportPlan = record
|
|---|
| 13 | LoadLoc, uixTransport, nLoad, TurnsEmpty, TurnsLoaded: integer;
|
|---|
| 14 | uixLoad: array[0..15] of integer;
|
|---|
| 15 | end;
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 | TToolAI = class(TCustomAI)
|
|---|
| 19 | protected
|
|---|
| 20 | {$IFDEF DEBUG}DebugMap: array[0..lxmax * lymax - 1] of integer;{$ENDIF}
|
|---|
| 21 |
|
|---|
| 22 | function CityTaxBalance(cix: integer; const CityReport: TCityReport): integer;
|
|---|
| 23 | // calculates exact difference of income and maintenance cost for a single city
|
|---|
| 24 | // positive result = income higher than maintenance
|
|---|
| 25 | // negative result = income lower than maintenance
|
|---|
| 26 | // respects production and food converted to gold
|
|---|
| 27 | // CityReport must have been prepared before
|
|---|
| 28 | procedure SumCities(TaxRate: integer; var TaxSum, ScienceSum: integer);
|
|---|
| 29 | // calculates exact total tax and science income
|
|---|
| 30 | // tax is reduced by maintenance (so might be negative)
|
|---|
| 31 | // luxury not supported
|
|---|
| 32 |
|
|---|
| 33 | procedure OptimizeCityTiles;
|
|---|
| 34 | // obsolete; use City_OptimizeTiles instead
|
|---|
| 35 |
|
|---|
| 36 | procedure GetCityProdPotential;
|
|---|
| 37 | // calculates potential collected production resources of a city
|
|---|
| 38 | // result: list for all cities in CityResult
|
|---|
| 39 | procedure GetCityTradePotential;
|
|---|
| 40 | // calculates potential collected trade resources of a city
|
|---|
| 41 | // result: list for all cities in CityResult
|
|---|
| 42 |
|
|---|
| 43 | procedure JobAssignment_Initialize;
|
|---|
| 44 | // initialization, must be called first of the JobAssignment functions
|
|---|
| 45 | procedure JobAssignment_AddJob(Loc, Job, Score: integer);
|
|---|
| 46 | // add job for settlers with certain score
|
|---|
| 47 | // jobs include founding cities!
|
|---|
| 48 | procedure JobAssignment_AddUnit(uix: integer);
|
|---|
| 49 | // add a settler unit to do jobs
|
|---|
| 50 | procedure JobAssignment_Go;
|
|---|
| 51 | // to be called after all jobs and the settlers for them have been added
|
|---|
| 52 | // assigns each job to one settler, moves the settlers and makes them work
|
|---|
| 53 | // settlers prefer jobs which are closer to their current location and jobs with a higher score
|
|---|
| 54 | // starting a job one turn earlier counts the same as 4 points of score
|
|---|
| 55 | // function does not cancel jobs that are already started
|
|---|
| 56 | function JobAssignment_GotJob(uix: integer): boolean;
|
|---|
| 57 | // can be called after JobAssignment_Go to find out whether
|
|---|
| 58 | // a certain settler has been assigned a job to
|
|---|
| 59 |
|
|---|
| 60 | procedure AnalyzeMap;
|
|---|
| 61 | // calculates formations and districts
|
|---|
| 62 |
|
|---|
| 63 | function CheckStep(MoveStyle, TimeBeforeStep, CrossCorner: integer;
|
|---|
| 64 | var TimeAfterStep, RecoverTurns: integer; FromTile, ToTile: integer): integer;
|
|---|
| 65 | // forecast single unit move between adjacent tiles
|
|---|
| 66 | // format of TimeBeforeStep and TimeAfterStep: $1000*number of turns + $800-MP left
|
|---|
| 67 | // RecoverTurns: number of turns needed to rest outside city in order to
|
|---|
| 68 | // recover from damage taken in this move (rounded up)
|
|---|
| 69 | // FromTile and ToTile must be Map[FromLoc] and Map[ToLoc], no location codes
|
|---|
| 70 | // CrossCorner=1 for long moves that cross the tile corner, =0 for short ones that don't
|
|---|
| 71 |
|
|---|
| 72 | function GetMyMoveStyle(mix, Health: integer): integer;
|
|---|
| 73 |
|
|---|
| 74 | function Unit_MoveEx(uix, ToLoc: integer; Options: integer = 0): integer;
|
|---|
| 75 |
|
|---|
| 76 | procedure SeaTransport_BeginInitialize;
|
|---|
| 77 | procedure SeaTransport_EndInitialize;
|
|---|
| 78 | // sea transport, obligatory call order:
|
|---|
| 79 | // 1. BeginInitialize
|
|---|
| 80 | // [2. AddLoad/AddTransport/AddDestination]
|
|---|
| 81 | // 3. EndInitialize
|
|---|
| 82 | // [4. MakeGroupPlan, MakeGroupPlan, MakeGroupPlan...]
|
|---|
| 83 | // don't use Pile between BeginInitialize and EndInitialize
|
|---|
| 84 | // sea transport only works well if
|
|---|
| 85 | // - all transports have same speed
|
|---|
| 86 | // - all transports have same capacity
|
|---|
| 87 | // - no transport is damaged
|
|---|
| 88 | procedure SeaTransport_AddLoad(uix: integer);
|
|---|
| 89 | procedure SeaTransport_AddTransport(uix: integer);
|
|---|
| 90 | procedure SeaTransport_AddDestination(Loc: integer);
|
|---|
| 91 | function SeaTransport_MakeGroupPlan(var TransportPlan: TGroupTransportPlan): boolean;
|
|---|
| 92 | // make plan for group of units to transport from a single loading location by a single transport
|
|---|
| 93 | // the plan optimizes:
|
|---|
| 94 | // - time for the units to move to the loading location
|
|---|
| 95 | // - time for the transport to move to the loading location
|
|---|
| 96 | // - time for the transport to move to one of the destination locations
|
|---|
| 97 | // after the plan is made, units and transport are removed from the pool, so that
|
|---|
| 98 | // subsequent calls to MakeGroupPlan result in plans that may be executed parallel
|
|---|
| 99 | // function returns false if no more transports are possible
|
|---|
| 100 |
|
|---|
| 101 | end;
|
|---|
| 102 |
|
|---|
| 103 |
|
|---|
| 104 | const
|
|---|
| 105 | // no-formations
|
|---|
| 106 | nfUndiscovered = -1;
|
|---|
| 107 | nfPole = -2;
|
|---|
| 108 | nfPeace = -3;
|
|---|
| 109 |
|
|---|
| 110 | // return codes of CheckStep
|
|---|
| 111 | csOk = 0;
|
|---|
| 112 | // step is valid
|
|---|
| 113 | // TimeAfterMove has been calculated
|
|---|
| 114 | csForbiddenTile = 1;
|
|---|
| 115 | // unit can not move onto this tile
|
|---|
| 116 | // TimeAfterMove not calculated
|
|---|
| 117 | csForbiddenStep = 2;
|
|---|
| 118 | // (ZoC unit only) unit can not do this step because of ZoC violation
|
|---|
| 119 | // maybe tile can be reached using another way
|
|---|
| 120 | // TimeAfterMove not calculated
|
|---|
| 121 | csCheckTerritory = 3;
|
|---|
| 122 | // move within other nations's territory shortly after making peace
|
|---|
| 123 | // step is only possible if RO.Territory is the same for both tiles
|
|---|
| 124 | // TimeAfterMove has been calculated
|
|---|
| 125 |
|
|---|
| 126 | // Unit_MoveEx
|
|---|
| 127 | mxAdjacent = $00000001;
|
|---|
| 128 |
|
|---|
| 129 |
|
|---|
| 130 | var
|
|---|
| 131 | nContinent, nOcean, nDistrict: integer;
|
|---|
| 132 | Formation: array[0..lxmax * lymax - 1] of integer;
|
|---|
| 133 | // water: ocean index, land: continent index, sorted by size
|
|---|
| 134 | // territory unpassable due to peace treaty divides a continent
|
|---|
| 135 | District: array[0..lxmax * lymax - 1] of integer;
|
|---|
| 136 | // index of coherent own territory, sorted by size
|
|---|
| 137 | CityResult: array[0..nCmax - 1] of integer;
|
|---|
| 138 |
|
|---|
| 139 | Advancedness: array[0..nAdv - 1] of integer;
|
|---|
| 140 | // total number of prerequisites for each advance
|
|---|
| 141 |
|
|---|
| 142 |
|
|---|
| 143 | implementation
|
|---|
| 144 |
|
|---|
| 145 | uses
|
|---|
| 146 | Pile;
|
|---|
| 147 |
|
|---|
| 148 | type
|
|---|
| 149 | pinteger = ^integer;
|
|---|
| 150 |
|
|---|
| 151 | var
|
|---|
| 152 | // for JobAssignment
|
|---|
| 153 | MaxScore: integer;
|
|---|
| 154 | TileJob, TileJobScore: array[0..lxmax * lymax - 1] of byte;
|
|---|
| 155 | JobLocOfSettler: array[0..nUmax - 1] of integer; // ToAssign = find job
|
|---|
| 156 |
|
|---|
| 157 | // for Transport
|
|---|
| 158 | TransportMoveStyle, TransportCapacity, nTransportLoad: integer;
|
|---|
| 159 | InitComplete, HaveDestinations: boolean;
|
|---|
| 160 | uixTransportLoad, TransportAvailable: array[0..nUmax - 1] of integer;
|
|---|
| 161 | TurnsAfterLoad: array[0..lxmax * lymax - 1] of shortint;
|
|---|
| 162 |
|
|---|
| 163 |
|
|---|
| 164 | procedure ReplaceD(Start, Stop: pinteger; Raider, Twix: integer);
|
|---|
| 165 | begin
|
|---|
| 166 | while Start <> Stop do
|
|---|
| 167 | begin
|
|---|
| 168 | if Start^ = Raider then Start^ := Twix;
|
|---|
| 169 | Inc(Start);
|
|---|
| 170 | end;
|
|---|
| 171 | end;
|
|---|
| 172 |
|
|---|
| 173 | function NextZero(Start, Stop: pinteger; Mask: cardinal): pinteger;
|
|---|
| 174 | begin
|
|---|
| 175 | while (Start <> Stop) and (Start^ and Mask <> 0) do Inc(Start);
|
|---|
| 176 | Result := Start;
|
|---|
| 177 | end;
|
|---|
| 178 |
|
|---|
| 179 |
|
|---|
| 180 | function TToolAI.CityTaxBalance(cix: integer; const CityReport: TCityReport): integer;
|
|---|
| 181 | var
|
|---|
| 182 | i: integer;
|
|---|
| 183 | begin
|
|---|
| 184 | Result := 0;
|
|---|
| 185 | if (CityReport.Working - CityReport.Happy <= MyCity[cix].Size shr 1) {no disorder} and
|
|---|
| 186 | (MyCity[cix].Flags and chCaptured = 0) then // not captured
|
|---|
| 187 | begin
|
|---|
| 188 | Inc(Result, CityReport.Tax);
|
|---|
| 189 | if (MyCity[cix].Project and (cpImp + cpIndex) = cpImp + imTrGoods) and
|
|---|
| 190 | (CityReport.ProdRep > CityReport.Support) then
|
|---|
| 191 | Inc(Result, CityReport.ProdRep - CityReport.Support);
|
|---|
| 192 | if ((RO.Government = gLybertarianism) or
|
|---|
| 193 | (MyCity[cix].Size >= NeedAqueductSize) and
|
|---|
| 194 | (CityReport.FoodRep < CityReport.Eaten + 2)) and
|
|---|
| 195 | (CityReport.FoodRep > CityReport.Eaten) then
|
|---|
| 196 | Inc(Result, CityReport.FoodRep - CityReport.Eaten);
|
|---|
| 197 | end;
|
|---|
| 198 | for i := 28 to nImp - 1 do if MyCity[cix].Built[i] > 0 then
|
|---|
| 199 | Dec(Result, Imp[i].Maint);
|
|---|
| 200 | end;
|
|---|
| 201 |
|
|---|
| 202 | procedure TToolAI.SumCities(TaxRate: integer; var TaxSum, ScienceSum: integer);
|
|---|
| 203 | var
|
|---|
| 204 | cix, p1: integer;
|
|---|
| 205 | CityReport: TCityReport;
|
|---|
| 206 | begin
|
|---|
| 207 | TaxSum := 0;
|
|---|
| 208 | ScienceSum := 0;
|
|---|
| 209 | if RO.Government = gAnarchy then exit;
|
|---|
| 210 | for p1 := 0 to nPl - 1 do
|
|---|
| 211 | if RO.Tribute[p1] <= RO.TributePaid[p1] then
|
|---|
| 212 | // don't rely on tribute from bankrupt nations
|
|---|
| 213 | TaxSum := TaxSum + RO.Tribute[p1];
|
|---|
| 214 | for cix := 0 to RO.nCity - 1 do if MyCity[cix].Loc >= 0 then
|
|---|
| 215 | begin
|
|---|
| 216 | City_GetHypoReport(cix, -1, TaxRate, 0, CityReport);
|
|---|
| 217 | if (CityReport.Working - CityReport.Happy <= MyCity[cix].Size shr 1) {no disorder} and
|
|---|
| 218 | (MyCity[cix].Flags and chCaptured = 0) then // not captured
|
|---|
| 219 | ScienceSum := ScienceSum + CityReport.Science;
|
|---|
| 220 | TaxSum := TaxSum + CityTaxBalance(cix, CityReport);
|
|---|
| 221 | end;
|
|---|
| 222 | end;
|
|---|
| 223 |
|
|---|
| 224 |
|
|---|
| 225 | //------------------------------------------------------------------------------
|
|---|
| 226 | // City Tiles Processing
|
|---|
| 227 |
|
|---|
| 228 | const
|
|---|
| 229 | pctOptimize = 0;
|
|---|
| 230 | pctGetProdPotential = 1;
|
|---|
| 231 | pctGetTradePotential = 2;
|
|---|
| 232 |
|
|---|
| 233 | procedure TToolAI.OptimizeCityTiles;
|
|---|
| 234 | var
|
|---|
| 235 | cix: integer;
|
|---|
| 236 | begin
|
|---|
| 237 | for cix := 0 to RO.nCity - 1 do with MyCity[cix] do if Loc >= 0 then
|
|---|
| 238 | City_OptimizeTiles(cix);
|
|---|
| 239 | end;
|
|---|
| 240 |
|
|---|
| 241 | procedure TToolAI.GetCityProdPotential;
|
|---|
| 242 | var
|
|---|
| 243 | cix: integer;
|
|---|
| 244 | Advice: TCityTileAdviceData;
|
|---|
| 245 | begin
|
|---|
| 246 | for cix := 0 to RO.nCity - 1 do with MyCity[cix] do if Loc >= 0 then
|
|---|
| 247 | begin
|
|---|
| 248 | Advice.ResourceWeights := rwMaxProd;
|
|---|
| 249 | Server(sGetCityTileAdvice, me, cix, Advice);
|
|---|
| 250 | CityResult[cix] := Advice.CityReport.ProdRep; // considers factory, but shouldn't
|
|---|
| 251 | end;
|
|---|
| 252 | end;
|
|---|
| 253 |
|
|---|
| 254 | procedure TToolAI.GetCityTradePotential;
|
|---|
| 255 | var
|
|---|
| 256 | cix: integer;
|
|---|
| 257 | Advice: TCityTileAdviceData;
|
|---|
| 258 | begin
|
|---|
| 259 | for cix := 0 to RO.nCity - 1 do with MyCity[cix] do if Loc >= 0 then
|
|---|
| 260 | begin
|
|---|
| 261 | Advice.ResourceWeights := rwMaxScience;
|
|---|
| 262 | Server(sGetCityTileAdvice, me, cix, Advice);
|
|---|
| 263 | CityResult[cix] := Advice.CityReport.Trade;
|
|---|
| 264 | end;
|
|---|
| 265 | end;
|
|---|
| 266 |
|
|---|
| 267 |
|
|---|
| 268 | //------------------------------------------------------------------------------
|
|---|
| 269 | // JobAssignment
|
|---|
| 270 |
|
|---|
| 271 | const
|
|---|
| 272 | ToAssign = lxmax * lymax;
|
|---|
| 273 |
|
|---|
| 274 | procedure TToolAI.JobAssignment_Initialize;
|
|---|
| 275 | begin
|
|---|
| 276 | fillchar(JobLocOfSettler, RO.nUn * sizeof(integer), $FF); // -1
|
|---|
| 277 | fillchar(TileJob, MapSize, jNone);
|
|---|
| 278 | fillchar(TileJobScore, MapSize, 0);
|
|---|
| 279 | MaxScore := 0;
|
|---|
| 280 | end;
|
|---|
| 281 |
|
|---|
| 282 | procedure TToolAI.JobAssignment_AddJob(Loc, Job, Score: integer);
|
|---|
| 283 | begin
|
|---|
| 284 | if Score > 255 then Score := 255;
|
|---|
| 285 | if Score > TileJobScore[Loc] then
|
|---|
| 286 | begin
|
|---|
| 287 | TileJob[Loc] := Job;
|
|---|
| 288 | TileJobScore[Loc] := Score;
|
|---|
| 289 | if Score > MaxScore then MaxScore := Score;
|
|---|
| 290 | end;
|
|---|
| 291 | end;
|
|---|
| 292 |
|
|---|
| 293 | procedure TToolAI.JobAssignment_AddUnit(uix: integer);
|
|---|
| 294 | begin
|
|---|
| 295 | Assert(MyModel[MyUnit[uix].mix].Kind in [mkSettler, mkSlaves]);
|
|---|
| 296 | JobLocOfSettler[uix] := ToAssign;
|
|---|
| 297 | end;
|
|---|
| 298 |
|
|---|
| 299 | function TToolAI.JobAssignment_GotJob(uix: integer): boolean;
|
|---|
| 300 | begin
|
|---|
| 301 | Result := JobLocOfSettler[uix] >= 0;
|
|---|
| 302 | end;
|
|---|
| 303 |
|
|---|
| 304 | procedure TToolAI.JobAssignment_Go;
|
|---|
| 305 | const
|
|---|
| 306 | DistanceScore = 4;
|
|---|
| 307 | StepSizeByTerrain: array[0..11] of integer =
|
|---|
| 308 | (0, 0, 1, 2, 1, 1, 0, 1, 0, 1, 1, 2);
|
|---|
| 309 | //Oc-Sh-Gr-De-Pr-Tu-Ar-Sw-XX-Fo-Hi-Mo
|
|---|
| 310 | var
|
|---|
| 311 | uix, BestScore, BestCount, BestLoc, BestJob, BestDistance, TestLoc, NextLoc,
|
|---|
| 312 | TestDistance, V8, TestScore, StepSize, MoveResult: integer;
|
|---|
| 313 | UnitsToAssign: boolean;
|
|---|
| 314 | Adjacent: TVicinity8Loc;
|
|---|
| 315 | SettlerOfJobLoc, DistToLoc: array[0..lxmax * lymax - 1] of smallint;
|
|---|
| 316 | // DistToLoc is only defined where SettlerOfJobLoc>=0
|
|---|
| 317 | TileChecked: array[0..lxmax * lymax - 1] of boolean;
|
|---|
| 318 | begin
|
|---|
| 319 | fillchar(SettlerOfJobLoc, MapSize * 2, $FF); // -1
|
|---|
| 320 |
|
|---|
| 321 | // keep up jobs that are already started
|
|---|
| 322 | for uix := 0 to RO.nUn - 1 do
|
|---|
| 323 | if (MyUnit[uix].Loc >= 0) and (MyUnit[uix].Job > jNone) then
|
|---|
| 324 | begin
|
|---|
| 325 | JobLocOfSettler[uix] := MyUnit[uix].Loc;
|
|---|
| 326 | SettlerOfJobLoc[MyUnit[uix].Loc] := uix;
|
|---|
| 327 | DistToLoc[MyUnit[uix].Loc] := 0;
|
|---|
| 328 | end;
|
|---|
| 329 |
|
|---|
| 330 | // assign remaining jobs to remaining settlers
|
|---|
| 331 | UnitsToAssign := True;
|
|---|
| 332 | while UnitsToAssign do
|
|---|
| 333 | begin
|
|---|
| 334 | UnitsToAssign := False;
|
|---|
| 335 | for uix := 0 to RO.nUn - 1 do if JobLocOfSettler[uix] = ToAssign then
|
|---|
| 336 | begin
|
|---|
| 337 | BestJob := jNone;
|
|---|
| 338 | BestScore := -999999;
|
|---|
| 339 | FillChar(TileChecked, MapSize * sizeof(boolean), False);
|
|---|
| 340 | Pile.Create(MapSize);
|
|---|
| 341 | Pile.Put(MyUnit[uix].Loc, 0); // start search for new job at current location
|
|---|
| 342 | while Pile.Get(TestLoc, TestDistance) do
|
|---|
| 343 | begin
|
|---|
| 344 | // add surrounding tiles to queue, but only if there's a chance to beat BestScore
|
|---|
| 345 | if MaxScore - DistanceScore * (TestDistance + 1) >= BestScore then
|
|---|
| 346 | begin
|
|---|
| 347 | V8_to_Loc(TestLoc, Adjacent);
|
|---|
| 348 | for V8 := 0 to 7 do
|
|---|
| 349 | begin
|
|---|
| 350 | NextLoc := Adjacent[V8];
|
|---|
| 351 | if (NextLoc >= 0) and not TileChecked[NextLoc] and
|
|---|
| 352 | (Map[NextLoc] and fTerrain <> fUNKNOWN) then
|
|---|
| 353 | begin
|
|---|
| 354 | StepSize := StepSizeByTerrain[Map[NextLoc] and fTerrain];
|
|---|
| 355 | if (StepSize > 0) // no water or arctic tile
|
|---|
| 356 | and (Map[NextLoc] and (fUnit or fOwned) <> fUnit) // no foreign unit
|
|---|
| 357 | and ((RO.Territory[NextLoc] < 0) or
|
|---|
| 358 | (RO.Territory[NextLoc] = me)) // no foreign territory
|
|---|
| 359 | and (Map[TestLoc] and Map[NextLoc] and fInEnemyZoC = 0) then
|
|---|
| 360 | // move not prevented by ZoC
|
|---|
| 361 | Pile.Put(NextLoc, TestDistance + StepSize);
|
|---|
| 362 | // simplification, only optimal for 150 mp units in land with no roads
|
|---|
| 363 | end;
|
|---|
| 364 | end;
|
|---|
| 365 | end;
|
|---|
| 366 |
|
|---|
| 367 | // check tile for job
|
|---|
| 368 | if (TileJob[TestLoc] > jNone) and
|
|---|
| 369 | ((MyModel[MyUnit[uix].mix].Kind <> mkSlaves) or
|
|---|
| 370 | (TileJob[TestLoc] <> jCity)) and ((SettlerOfJobLoc[TestLoc] < 0) or
|
|---|
| 371 | (DistToLoc[TestLoc] > TestDistance)) then
|
|---|
| 372 | begin
|
|---|
| 373 | TestScore := integer(TileJobScore[TestLoc]) - DistanceScore * TestDistance;
|
|---|
| 374 | if TestScore > BestScore then
|
|---|
| 375 | BestCount := 0;
|
|---|
| 376 | if TestScore >= BestScore then
|
|---|
| 377 | begin
|
|---|
| 378 | Inc(BestCount);
|
|---|
| 379 | if random(BestCount) = 0 then
|
|---|
| 380 | begin
|
|---|
| 381 | BestScore := TestScore;
|
|---|
| 382 | BestLoc := TestLoc;
|
|---|
| 383 | BestJob := TileJob[TestLoc];
|
|---|
| 384 | BestDistance := TestDistance;
|
|---|
| 385 | end;
|
|---|
| 386 | end;
|
|---|
| 387 | end;
|
|---|
| 388 | TileChecked[TestLoc] := True;
|
|---|
| 389 | end;
|
|---|
| 390 | Pile.Free;
|
|---|
| 391 |
|
|---|
| 392 | if BestJob > jNone then
|
|---|
| 393 | begin // new job found for this unit
|
|---|
| 394 | if SettlerOfJobLoc[BestLoc] >= 0 then
|
|---|
| 395 | begin // another unit was already assigned to this job, but is not as close -- reassign that unit!
|
|---|
| 396 | JobLocOfSettler[SettlerOfJobLoc[BestLoc]] := ToAssign;
|
|---|
| 397 | UnitsToAssign := True;
|
|---|
| 398 | end;
|
|---|
| 399 | JobLocOfSettler[uix] := BestLoc;
|
|---|
| 400 | SettlerOfJobLoc[BestLoc] := uix;
|
|---|
| 401 | DistToLoc[BestLoc] := BestDistance;
|
|---|
| 402 | end
|
|---|
| 403 | else
|
|---|
| 404 | JobLocOfSettler[uix] := -1; // no jobs for this settler
|
|---|
| 405 | end; // for uix
|
|---|
| 406 | end;
|
|---|
| 407 |
|
|---|
| 408 | // move settlers and start new jobs
|
|---|
| 409 | for uix := 0 to RO.nUn - 1 do with MyUnit[uix] do
|
|---|
| 410 | if (Loc >= 0) and (Job = jNone) and (JobLocOfSettler[uix] >= 0) then
|
|---|
| 411 | begin
|
|---|
| 412 | if Loc <> JobLocOfSettler[uix] then
|
|---|
| 413 | repeat
|
|---|
| 414 | MoveResult := Unit_Move(uix, JobLocOfSettler[uix])
|
|---|
| 415 | until (MoveResult < rExecuted) or (MoveResult and
|
|---|
| 416 | (rLocationReached or rMoreTurns or rUnitRemoved) <> 0);
|
|---|
| 417 | if (Loc = JobLocOfSettler[uix]) and (Movement >= 100) then
|
|---|
| 418 | Unit_StartJob(uix, TileJob[JobLocOfSettler[uix]]);
|
|---|
| 419 | end;
|
|---|
| 420 | end; // JobAssignment_Go
|
|---|
| 421 |
|
|---|
| 422 |
|
|---|
| 423 | //------------------------------------------------------------------------------
|
|---|
| 424 | // Map Analysis
|
|---|
| 425 |
|
|---|
| 426 | procedure TToolAI.AnalyzeMap;
|
|---|
| 427 | var
|
|---|
| 428 | i, j, Loc, Loc1, V8, Count, Kind, MostIndex: integer;
|
|---|
| 429 | Adjacent: TVicinity8Loc;
|
|---|
| 430 | IndexOfID: array[0..lxmax * lymax - 1] of smallint;
|
|---|
| 431 | IDOfIndex: array[0..lxmax * lymax div 2 - 1] of smallint;
|
|---|
| 432 | begin
|
|---|
| 433 | fillchar(District, MapSize * 4, $FF);
|
|---|
| 434 | for Loc := 0 to MapSize - 1 do
|
|---|
| 435 | if Map[Loc] and fTerrain = fUNKNOWN then Formation[Loc] := nfUndiscovered
|
|---|
| 436 | else if Map[Loc] and fTerrain = fArctic then Formation[Loc] := nfPole
|
|---|
| 437 | else if Map[Loc] and fPeace <> 0 then Formation[Loc] := nfPeace
|
|---|
| 438 | else
|
|---|
| 439 | begin
|
|---|
| 440 | Formation[Loc] := Loc;
|
|---|
| 441 | V8_to_Loc(Loc, Adjacent);
|
|---|
| 442 | for V8 := 0 to 7 do
|
|---|
| 443 | begin
|
|---|
| 444 | Loc1 := Adjacent[V8];
|
|---|
| 445 | if (Loc1 < Loc) and (Loc1 >= 0) and (Formation[Loc1] >= 0) and
|
|---|
| 446 | ((Map[Loc1] and fTerrain >= fGrass) = (Map[Loc] and fTerrain >= fGrass)) then
|
|---|
| 447 | if Formation[Loc] = Loc then Formation[Loc] := Formation[Loc1]
|
|---|
| 448 | else if Formation[Loc] < Formation[Loc1] then
|
|---|
| 449 | ReplaceD(@Formation[Formation[Loc1]], @Formation[Loc + 1],
|
|---|
| 450 | Formation[Loc1], Formation[Loc])
|
|---|
| 451 | else if Formation[Loc] > Formation[Loc1] then
|
|---|
| 452 | ReplaceD(@Formation[Formation[Loc]], @Formation[Loc + 1],
|
|---|
| 453 | Formation[Loc], Formation[Loc1]);
|
|---|
| 454 | end;
|
|---|
| 455 | if (RO.Territory[Loc] = me) and (Map[Loc] and fTerrain >= fGrass) then
|
|---|
| 456 | begin
|
|---|
| 457 | District[Loc] := Loc;
|
|---|
| 458 | for V8 := 0 to 7 do
|
|---|
| 459 | begin
|
|---|
| 460 | Loc1 := Adjacent[V8];
|
|---|
| 461 | if (Loc1 < Loc) and (Loc1 >= 0) and (District[Loc1] >= 0) then
|
|---|
| 462 | if District[Loc] = Loc then District[Loc] := District[Loc1]
|
|---|
| 463 | else if District[Loc] < District[Loc1] then
|
|---|
| 464 | ReplaceD(@District[District[Loc1]], @District[Loc + 1],
|
|---|
| 465 | District[Loc1], District[Loc])
|
|---|
| 466 | else if District[Loc] > District[Loc1] then
|
|---|
| 467 | ReplaceD(@District[District[Loc]], @District[Loc + 1],
|
|---|
| 468 | District[Loc], District[Loc1]);
|
|---|
| 469 | end;
|
|---|
| 470 | end;
|
|---|
| 471 | end;
|
|---|
| 472 |
|
|---|
| 473 | // sort continents, oceans and districts by size
|
|---|
| 474 | for Kind := 0 to 2 do
|
|---|
| 475 | begin
|
|---|
| 476 | FillChar(IndexOfID, MapSize * 2, 0);
|
|---|
| 477 | case Kind of
|
|---|
| 478 | 0: // continents
|
|---|
| 479 | for Loc := 0 to MapSize - 1 do
|
|---|
| 480 | if (Formation[Loc] >= 0) and (Map[Loc] and fTerrain >= fGrass) then
|
|---|
| 481 | Inc(IndexOfID[Formation[Loc]]);
|
|---|
| 482 | 1: // oceans
|
|---|
| 483 | for Loc := 0 to MapSize - 1 do
|
|---|
| 484 | if (Formation[Loc] >= 0) and (Map[Loc] and fTerrain < fGrass) then
|
|---|
| 485 | Inc(IndexOfID[Formation[Loc]]);
|
|---|
| 486 | 2: // districts
|
|---|
| 487 | for Loc := 0 to MapSize - 1 do
|
|---|
| 488 | if District[Loc] >= 0 then
|
|---|
| 489 | Inc(IndexOfID[District[Loc]]);
|
|---|
| 490 | end;
|
|---|
| 491 |
|
|---|
| 492 | Count := 0;
|
|---|
| 493 | for Loc := 0 to MapSize - 1 do if IndexOfID[Loc] > 0 then
|
|---|
| 494 | begin
|
|---|
| 495 | IDOfIndex[Count] := Loc;
|
|---|
| 496 | Inc(Count);
|
|---|
| 497 | end;
|
|---|
| 498 | for i := 0 to Count - 2 do
|
|---|
| 499 | begin
|
|---|
| 500 | MostIndex := i;
|
|---|
| 501 | for j := i + 1 to Count - 1 do
|
|---|
| 502 | if IndexOfID[IDOfIndex[j]] > IndexOfID[IDOfIndex[MostIndex]] then MostIndex := j;
|
|---|
| 503 | if MostIndex <> i then
|
|---|
| 504 | begin
|
|---|
| 505 | j := IDOfIndex[i];
|
|---|
| 506 | IDOfIndex[i] := IDOfIndex[MostIndex];
|
|---|
| 507 | IDOfIndex[MostIndex] := j;
|
|---|
| 508 | end;
|
|---|
| 509 | end;
|
|---|
| 510 | for i := 0 to Count - 1 do
|
|---|
| 511 | IndexOfID[IDOfIndex[i]] := i;
|
|---|
| 512 |
|
|---|
| 513 | case Kind of
|
|---|
| 514 | 0: // continents
|
|---|
| 515 | begin
|
|---|
| 516 | nContinent := Count;
|
|---|
| 517 | for Loc := 0 to MapSize - 1 do
|
|---|
| 518 | if (Formation[Loc] >= 0) and (Map[Loc] and fTerrain >= fGrass) then
|
|---|
| 519 | Formation[Loc] := IndexOfID[Formation[Loc]];
|
|---|
| 520 | end;
|
|---|
| 521 | 1: // oceans
|
|---|
| 522 | begin
|
|---|
| 523 | nOcean := Count;
|
|---|
| 524 | for Loc := 0 to MapSize - 1 do
|
|---|
| 525 | if (Formation[Loc] >= 0) and (Map[Loc] and fTerrain < fGrass) then
|
|---|
| 526 | Formation[Loc] := IndexOfID[Formation[Loc]];
|
|---|
| 527 | end;
|
|---|
| 528 | 2: // districts
|
|---|
| 529 | begin
|
|---|
| 530 | nDistrict := Count;
|
|---|
| 531 | for Loc := 0 to MapSize - 1 do
|
|---|
| 532 | if District[Loc] >= 0 then
|
|---|
| 533 | District[Loc] := IndexOfID[District[Loc]];
|
|---|
| 534 | end;
|
|---|
| 535 | end;
|
|---|
| 536 | end;
|
|---|
| 537 | end;
|
|---|
| 538 |
|
|---|
| 539 |
|
|---|
| 540 | //------------------------------------------------------------------------------
|
|---|
| 541 | // Path Finding
|
|---|
| 542 |
|
|---|
| 543 | const
|
|---|
| 544 | // basic move styles
|
|---|
| 545 | msGround = $00000000;
|
|---|
| 546 | msNoGround = $10000000;
|
|---|
| 547 | msAlpine = $20000000;
|
|---|
| 548 | msOver = $40000000;
|
|---|
| 549 | msSpy = $50000000;
|
|---|
| 550 |
|
|---|
| 551 | // other
|
|---|
| 552 | msHostile = $08000000;
|
|---|
| 553 |
|
|---|
| 554 | // bits: |31|30|29|28|27|26 .. 16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
|
|---|
| 555 | // ground: | Basic |Ho| Speed | HeavyCost | RailCost |
|
|---|
| 556 | // other: | Basic | 0| Speed | X X X | MaxTerrType |
|
|---|
| 557 |
|
|---|
| 558 | function TToolAI.GetMyMoveStyle(mix, Health: integer): integer;
|
|---|
| 559 | begin
|
|---|
| 560 | with MyModel[mix] do
|
|---|
| 561 | begin
|
|---|
| 562 | Result := Speed shl 16;
|
|---|
| 563 | case Domain of
|
|---|
| 564 | dGround:
|
|---|
| 565 | begin
|
|---|
| 566 | Inc(Result, (50 + (Speed - 150) * 13 shr 7) shl 8); //HeavyCost
|
|---|
| 567 | if RO.Wonder[woShinkansen].EffectiveOwner <> me then
|
|---|
| 568 | Inc(Result, Speed * (4 * 1311) shr 17); // RailCost
|
|---|
| 569 | if RO.Wonder[woGardens].EffectiveOwner <> me then
|
|---|
| 570 | Inc(Result, msHostile);
|
|---|
| 571 | if Kind = mkDiplomat then
|
|---|
| 572 | Inc(Result, msSpy)
|
|---|
| 573 | else if Cap[mcOver] > 0 then
|
|---|
| 574 | Inc(Result, msOver)
|
|---|
| 575 | else if Cap[mcAlpine] > 0 then
|
|---|
| 576 | Inc(Result, msAlpine)
|
|---|
| 577 | else
|
|---|
| 578 | Inc(Result, msGround);
|
|---|
| 579 | end;
|
|---|
| 580 | dSea:
|
|---|
| 581 | begin
|
|---|
| 582 | Result := Speed;
|
|---|
| 583 | if RO.Wonder[woMagellan].EffectiveOwner = me then Inc(Result, 200);
|
|---|
| 584 | if Health < 100 then Result := ((Result - 250) * Health div 5000) * 50 + 250;
|
|---|
| 585 | Result := Result shl 16;
|
|---|
| 586 | Inc(Result, msNoGround);
|
|---|
| 587 | if Cap[mcNav] > 0 then Inc(Result);
|
|---|
| 588 | end;
|
|---|
| 589 | dAir:
|
|---|
| 590 | Inc(Result, msNoGround + fUNKNOWN xor 1 - 1);
|
|---|
| 591 | end;
|
|---|
| 592 | end;
|
|---|
| 593 | end;
|
|---|
| 594 |
|
|---|
| 595 | function TToolAI.CheckStep(MoveStyle, TimeBeforeStep, CrossCorner: integer;
|
|---|
| 596 | var TimeAfterStep, RecoverTurns: integer; FromTile, ToTile: integer): integer;
|
|---|
| 597 | var
|
|---|
| 598 | MoveCost, RecoverCost: integer;
|
|---|
| 599 | begin
|
|---|
| 600 | Assert(((FromTile and fTerrain <= fMountains) or (FromTile and fTerrain = fUNKNOWN)) and
|
|---|
| 601 | ((ToTile and fTerrain <= fMountains) or (ToTile and fTerrain = fUNKNOWN)));
|
|---|
| 602 | // do not pass location codes for FromTile and ToTile!
|
|---|
| 603 | RecoverTurns := 0;
|
|---|
| 604 | if MoveStyle < msGround + $10000000 then
|
|---|
| 605 | begin // common ground units
|
|---|
| 606 | if (ToTile + 1) and fTerrain < fGrass + 1 then
|
|---|
| 607 | Result := csForbiddenTile
|
|---|
| 608 | else if (ToTile and not FromTile and fPeace = 0) and
|
|---|
| 609 | (ToTile and (fUnit or fOwned) <> fUnit) then
|
|---|
| 610 | if (FromTile and fCity <> 0) or (ToTile and (fCity or fOwned) = fCity or fOwned) or
|
|---|
| 611 | (ToTile and FromTile and (fInEnemyZoc or fOwnZoCUnit) <> fInEnemyZoc) then
|
|---|
| 612 | begin // ZoC is ok
|
|---|
| 613 | if (ToTile and (fRR or fCity) = 0) or (FromTile and (fRR or fCity) = 0) then
|
|---|
| 614 | begin // no railroad
|
|---|
| 615 | if (ToTile and (fRoad or fRR or fCity) <> 0) and
|
|---|
| 616 | (FromTile and (fRoad or fRR or fCity) <> 0) or
|
|---|
| 617 | (FromTile and ToTile and (fRiver or fCanal) <> 0) then
|
|---|
| 618 | MoveCost := 20 //move along road, river or canal
|
|---|
| 619 | else
|
|---|
| 620 | begin
|
|---|
| 621 | case Terrain[ToTile and fTerrain].MoveCost of
|
|---|
| 622 | 1: MoveCost := 50; // plain terrain
|
|---|
| 623 | 2: MoveCost := MoveStyle shr 8 and $FF; // heavy terrain
|
|---|
| 624 | else // mountains
|
|---|
| 625 | begin
|
|---|
| 626 | if TimeBeforeStep and $FFF + MoveStyle shr 16 and $7FF <= $800 then
|
|---|
| 627 | TimeAfterStep := TimeBeforeStep and $7FFFF000 + $1800
|
|---|
| 628 | else
|
|---|
| 629 | TimeAfterStep := TimeBeforeStep and $7FFFF000 + $2800; // must wait for next turn
|
|---|
| 630 | if (MoveStyle and msHostile <> 0) and
|
|---|
| 631 | ((FromTile and (fTerrain or fSpecial1) = fDesert) or
|
|---|
| 632 | (FromTile and fTerrain = fArctic)) and
|
|---|
| 633 | (FromTile and (fCity or fRiver or fCanal) = 0) then
|
|---|
| 634 | begin
|
|---|
| 635 | RecoverCost := ($800 - TimeBeforeStep and $FFF) * 5 shr 1;
|
|---|
| 636 | while RecoverCost > 0 do
|
|---|
| 637 | begin
|
|---|
| 638 | Inc(RecoverTurns);
|
|---|
| 639 | Dec(RecoverCost, MoveStyle shr 16 and $7FF);
|
|---|
| 640 | end;
|
|---|
| 641 | end;
|
|---|
| 642 | Result := csOk;
|
|---|
| 643 | if ToTile and fPeace <> 0 then
|
|---|
| 644 | Result := csCheckTerritory;
|
|---|
| 645 | exit;
|
|---|
| 646 | end;
|
|---|
| 647 | end;
|
|---|
| 648 | end;
|
|---|
| 649 | end
|
|---|
| 650 | else
|
|---|
| 651 | MoveCost := MoveStyle and $FF; //move along railroad
|
|---|
| 652 |
|
|---|
| 653 | Inc(MoveCost, MoveCost shl CrossCorner);
|
|---|
| 654 | if (MoveStyle and msHostile = 0) or
|
|---|
| 655 | (ToTile and (fTerrain or fSpecial1) <> fDesert) and
|
|---|
| 656 | (ToTile and fTerrain <> fArctic) or
|
|---|
| 657 | (ToTile and (fCity or fRiver or fCanal) <> 0) then
|
|---|
| 658 | RecoverCost := 0
|
|---|
| 659 | else
|
|---|
| 660 | RecoverCost := (MoveCost * 5) shr 1;
|
|---|
| 661 | // damage from movement: MoveCost*DesertThurst/NoCityRecovery
|
|---|
| 662 | if (TimeBeforeStep and $FFF + MoveCost <= $800) and (TimeBeforeStep and $FFF < $800) then
|
|---|
| 663 | TimeAfterStep := TimeBeforeStep + MoveCost
|
|---|
| 664 | else
|
|---|
| 665 | begin
|
|---|
| 666 | TimeAfterStep := TimeBeforeStep and $7FFFF000 + $1800 - MoveStyle shr
|
|---|
| 667 | 16 and $7FF + MoveCost; // must wait for next turn
|
|---|
| 668 | if (MoveStyle and msHostile <> 0) and
|
|---|
| 669 | ((FromTile and (fTerrain or fSpecial1) = fDesert) or
|
|---|
| 670 | (FromTile and fTerrain = fArctic)) and
|
|---|
| 671 | (FromTile and (fCity or fRiver or fCanal) = 0) then
|
|---|
| 672 | Inc(RecoverCost, ($800 - TimeBeforeStep and $FFF) * 5 shr 1);
|
|---|
| 673 | end;
|
|---|
| 674 | while RecoverCost > 0 do
|
|---|
| 675 | begin
|
|---|
| 676 | Inc(RecoverTurns);
|
|---|
| 677 | Dec(RecoverCost, MoveStyle shr 16 and $7FF);
|
|---|
| 678 | end;
|
|---|
| 679 | Result := csOk;
|
|---|
| 680 | if ToTile and fPeace <> 0 then
|
|---|
| 681 | Result := csCheckTerritory;
|
|---|
| 682 | end
|
|---|
| 683 | else
|
|---|
| 684 | Result := csForbiddenStep // ZoC violation
|
|---|
| 685 | else
|
|---|
| 686 | Result := csForbiddenTile;
|
|---|
| 687 | end
|
|---|
| 688 |
|
|---|
| 689 | else if MoveStyle < msNoGround + $10000000 then
|
|---|
| 690 | begin // ships and aircraft
|
|---|
| 691 | if ((ToTile and fTerrain xor 1 > MoveStyle and fTerrain) and
|
|---|
| 692 | (ToTile and (fCity or fCanal) = 0)) or (ToTile and not FromTile and fPeace <> 0) or
|
|---|
| 693 | (ToTile and (fUnit or fOwned) = fUnit) then
|
|---|
| 694 | Result := csForbiddenTile
|
|---|
| 695 | else
|
|---|
| 696 | begin
|
|---|
| 697 | MoveCost := 50 shl CrossCorner + 50;
|
|---|
| 698 | if TimeBeforeStep and $FFF + MoveCost <= $800 then
|
|---|
| 699 | TimeAfterStep := TimeBeforeStep + MoveCost
|
|---|
| 700 | else
|
|---|
| 701 | TimeAfterStep := TimeBeforeStep and $7FFFF000 + $1800 - MoveStyle shr 16 and $7FF + MoveCost;
|
|---|
| 702 | // must wait for next turn
|
|---|
| 703 | Result := csOk;
|
|---|
| 704 | if ToTile and fPeace <> 0 then
|
|---|
| 705 | Result := csCheckTerritory;
|
|---|
| 706 | end;
|
|---|
| 707 | end
|
|---|
| 708 |
|
|---|
| 709 | else if MoveStyle < msAlpine + $10000000 then
|
|---|
| 710 | begin // alpine
|
|---|
| 711 | if (ToTile + 1) and fTerrain < fGrass + 1 then
|
|---|
| 712 | Result := csForbiddenTile
|
|---|
| 713 | else if (ToTile and not FromTile and fPeace = 0) and
|
|---|
| 714 | (ToTile and (fUnit or fOwned) <> fUnit) then
|
|---|
| 715 | if (FromTile and fCity <> 0) or (ToTile and (fCity or fOwned) = fCity or fOwned) or
|
|---|
| 716 | (ToTile and FromTile and (fInEnemyZoc or fOwnZoCUnit) <> fInEnemyZoc) then
|
|---|
| 717 | begin
|
|---|
| 718 | if (ToTile and (fRR or fCity) = 0) or (FromTile and (fRR or fCity) = 0) then
|
|---|
| 719 | MoveCost := 20 // no railroad
|
|---|
| 720 | else
|
|---|
| 721 | MoveCost := MoveStyle and $FF; //move along railroad
|
|---|
| 722 | Inc(MoveCost, MoveCost shl CrossCorner);
|
|---|
| 723 | if (TimeBeforeStep and $FFF + MoveCost <= $800) and (TimeBeforeStep and $FFF < $800) then
|
|---|
| 724 | TimeAfterStep := TimeBeforeStep + MoveCost
|
|---|
| 725 | else
|
|---|
| 726 | TimeAfterStep := TimeBeforeStep and $7FFFF000 + $1800 - MoveStyle shr 16 and $7FF + MoveCost;
|
|---|
| 727 | // must wait for next turn
|
|---|
| 728 | Result := csOk;
|
|---|
| 729 | if ToTile and fPeace <> 0 then
|
|---|
| 730 | Result := csCheckTerritory;
|
|---|
| 731 | end
|
|---|
| 732 | else
|
|---|
| 733 | Result := csForbiddenStep // ZoC violation
|
|---|
| 734 | else
|
|---|
| 735 | Result := csForbiddenTile;
|
|---|
| 736 | end
|
|---|
| 737 |
|
|---|
| 738 | else if MoveStyle < msOver + $10000000 then
|
|---|
| 739 | begin // overweight
|
|---|
| 740 | if (ToTile + 1) and fTerrain < fGrass + 1 then
|
|---|
| 741 | Result := csForbiddenTile
|
|---|
| 742 | else if (ToTile and not FromTile and fPeace = 0) and
|
|---|
| 743 | (ToTile and (fUnit or fOwned) <> fUnit) then
|
|---|
| 744 | if (FromTile and fCity <> 0) or (ToTile and (fCity or fOwned) = fCity or fOwned) or
|
|---|
| 745 | (ToTile and FromTile and (fInEnemyZoc or fOwnZoCUnit) <> fInEnemyZoc) then
|
|---|
| 746 | begin
|
|---|
| 747 | if (ToTile and (fRR or fCity) = 0) or (FromTile and (fRR or fCity) = 0) then
|
|---|
| 748 | begin // no railroad
|
|---|
| 749 | if (ToTile and (fRoad or fRR or fCity) <> 0) and
|
|---|
| 750 | (FromTile and (fRoad or fRR or fCity) <> 0) or
|
|---|
| 751 | (FromTile and ToTile and (fRiver or fCanal) <> 0) then
|
|---|
| 752 | MoveCost := 40 //move along road, river or canal
|
|---|
| 753 | else
|
|---|
| 754 | begin
|
|---|
| 755 | Result := csForbiddenTile;
|
|---|
| 756 | exit;
|
|---|
| 757 | end;
|
|---|
| 758 | end
|
|---|
| 759 | else
|
|---|
| 760 | MoveCost := MoveStyle and $FF; //move along railroad
|
|---|
| 761 | Inc(MoveCost, MoveCost shl CrossCorner);
|
|---|
| 762 | if (TimeBeforeStep and $FFF + MoveCost <= $800) and (TimeBeforeStep and $FFF < $800) then
|
|---|
| 763 | TimeAfterStep := TimeBeforeStep + MoveCost
|
|---|
| 764 | else
|
|---|
| 765 | TimeAfterStep := TimeBeforeStep and $7FFFF000 + $1800 - MoveStyle shr 16 and $7FF + MoveCost;
|
|---|
| 766 | // must wait for next turn
|
|---|
| 767 | Result := csOk;
|
|---|
| 768 | if ToTile and fPeace <> 0 then
|
|---|
| 769 | Result := csCheckTerritory;
|
|---|
| 770 | end
|
|---|
| 771 | else
|
|---|
| 772 | Result := csForbiddenStep // ZoC violation
|
|---|
| 773 | else
|
|---|
| 774 | Result := csForbiddenTile;
|
|---|
| 775 | end
|
|---|
| 776 |
|
|---|
| 777 | else {if MoveStyle < msSpy + $10000000 then}
|
|---|
| 778 | begin // spies
|
|---|
| 779 | if (ToTile + 1) and fTerrain < fGrass + 1 then
|
|---|
| 780 | Result := csForbiddenTile
|
|---|
| 781 | else if ToTile and (fUnit or fOwned) <> fUnit then
|
|---|
| 782 | begin
|
|---|
| 783 | if (ToTile and (fRR or fCity) = 0) or (FromTile and (fRR or fCity) = 0) then
|
|---|
| 784 | begin // no railroad
|
|---|
| 785 | if (ToTile and (fRoad or fRR or fCity) <> 0) and
|
|---|
| 786 | (FromTile and (fRoad or fRR or fCity) <> 0) or
|
|---|
| 787 | (FromTile and ToTile and (fRiver or fCanal) <> 0) then
|
|---|
| 788 | MoveCost := 20 //move along road, river or canal
|
|---|
| 789 | else
|
|---|
| 790 | begin
|
|---|
| 791 | case Terrain[ToTile and fTerrain].MoveCost of
|
|---|
| 792 | 1: MoveCost := 50; // plain terrain
|
|---|
| 793 | 2: MoveCost := MoveStyle shr 8 and $FF; // heavy terrain
|
|---|
| 794 | else // mountains
|
|---|
| 795 | begin
|
|---|
| 796 | if TimeBeforeStep and $FFF + MoveStyle shr 16 and $7FF <= $800 then
|
|---|
| 797 | TimeAfterStep := TimeBeforeStep and $7FFFF000 + $1800
|
|---|
| 798 | else
|
|---|
| 799 | TimeAfterStep := TimeBeforeStep and $7FFFF000 + $2800; // must wait for next turn
|
|---|
| 800 | Result := csOk;
|
|---|
| 801 | exit;
|
|---|
| 802 | end;
|
|---|
| 803 | end;
|
|---|
| 804 | end;
|
|---|
| 805 | end
|
|---|
| 806 | else
|
|---|
| 807 | MoveCost := MoveStyle and $FF; //move along railroad
|
|---|
| 808 | Inc(MoveCost, MoveCost shl CrossCorner);
|
|---|
| 809 | if (TimeBeforeStep and $FFF + MoveCost <= $800) and (TimeBeforeStep and $FFF < $800) then
|
|---|
| 810 | TimeAfterStep := TimeBeforeStep + MoveCost
|
|---|
| 811 | else
|
|---|
| 812 | TimeAfterStep := TimeBeforeStep and $7FFFF000 + $1800 - MoveStyle shr 16 and $7FF + MoveCost;
|
|---|
| 813 | // must wait for next turn
|
|---|
| 814 | Result := csOk;
|
|---|
| 815 | end
|
|---|
| 816 | else
|
|---|
| 817 | Result := csForbiddenTile;
|
|---|
| 818 | end;
|
|---|
| 819 | end; // CheckStep
|
|---|
| 820 |
|
|---|
| 821 | (*
|
|---|
| 822 | -------- Pathfinding Reference Implementation --------
|
|---|
| 823 | var
|
|---|
| 824 | MoveStyle,V8,Loc,Time,NextLoc,NextTime,RecoverTurns: integer;
|
|---|
| 825 | Adjacent: TVicinity8Loc;
|
|---|
| 826 | Reached: array[0..lxmax*lymax-1] of boolean;
|
|---|
| 827 | begin
|
|---|
| 828 | fillchar(Reached, MapSize, false);
|
|---|
| 829 | MoveStyle:=GetMyMoveStyle(MyUnit[uix].mix, MyUnit[uix].Health);
|
|---|
| 830 | Pile.Create(MapSize);
|
|---|
| 831 | Pile.Put(MyUnit[uix].Loc, $800-MyUnit[uix].Movement);
|
|---|
| 832 | while Pile.Get(Loc, Time) do
|
|---|
| 833 | begin
|
|---|
| 834 | // todo: check exit condition, e.g. whether destination reached
|
|---|
| 835 |
|
|---|
| 836 | Reached[Loc]:=True;
|
|---|
| 837 | V8_to_Loc(Loc, Adjacent);
|
|---|
| 838 | for V8:=0 to 7 do
|
|---|
| 839 | begin
|
|---|
| 840 | NextLoc:=Adjacent[V8];
|
|---|
| 841 | if (NextLoc>=0) and not Reached[NextLoc] then
|
|---|
| 842 | case CheckStep(MoveStyle, Time, V8 and 1, NextTime, RecoverTurns, Map[Loc], Map[NextLoc]) of
|
|---|
| 843 | csOk:
|
|---|
| 844 | Pile.Put(NextLoc, NextTime+RecoverTurns*$1000);
|
|---|
| 845 | csForbiddenTile:
|
|---|
| 846 | Reached[NextLoc]:=True; // don't check moving there again
|
|---|
| 847 | csCheckTerritory:
|
|---|
| 848 | if RO.Territory[NextLoc]=RO.Territory[Loc] then
|
|---|
| 849 | Pile.Put(NextLoc, NextTime+RecoverTurns*$1000);
|
|---|
| 850 | end
|
|---|
| 851 | end;
|
|---|
| 852 | end;
|
|---|
| 853 | Pile.Free;
|
|---|
| 854 | end;
|
|---|
| 855 | *)
|
|---|
| 856 |
|
|---|
| 857 | function TToolAI.Unit_MoveEx(uix, ToLoc: integer; Options: integer): integer;
|
|---|
| 858 | var
|
|---|
| 859 | Loc, NextLoc, Temp, FromLoc, EndLoc, Time, V8, MoveResult, RecoverTurns, NextTime,
|
|---|
| 860 | MoveStyle: integer;
|
|---|
| 861 | Adjacent: TVicinity8Loc;
|
|---|
| 862 | PreLoc: array[0..lxmax * lymax - 1] of integer;
|
|---|
| 863 | Reached: array[0..lxmax * lymax - 1] of boolean;
|
|---|
| 864 | begin
|
|---|
| 865 | Result := eOk;
|
|---|
| 866 | FromLoc := MyUnit[uix].Loc;
|
|---|
| 867 | if FromLoc = ToLoc then exit;
|
|---|
| 868 |
|
|---|
| 869 | FillChar(Reached, MapSize, False);
|
|---|
| 870 | MoveStyle := GetMyMoveStyle(MyUnit[uix].mix, MyUnit[uix].Health);
|
|---|
| 871 | EndLoc := -1;
|
|---|
| 872 | Pile.Create(MapSize);
|
|---|
| 873 | Pile.Put(FromLoc, $800 - MyUnit[uix].Movement);
|
|---|
| 874 | while Pile.Get(Loc, Time) do
|
|---|
| 875 | begin
|
|---|
| 876 | if (Loc = ToLoc) or (ToLoc = maNextCity) and (Map[Loc] and fCity <> 0) and
|
|---|
| 877 | (Map[Loc] and fOwned <> 0) then
|
|---|
| 878 | begin
|
|---|
| 879 | EndLoc := Loc;
|
|---|
| 880 | Break;
|
|---|
| 881 | end;
|
|---|
| 882 | Reached[Loc] := True;
|
|---|
| 883 | V8_to_Loc(Loc, Adjacent);
|
|---|
| 884 | for V8 := 0 to 7 do
|
|---|
| 885 | begin
|
|---|
| 886 | NextLoc := Adjacent[V8];
|
|---|
| 887 | if NextLoc >= 0 then
|
|---|
| 888 | if (NextLoc = ToLoc) and (Options and mxAdjacent <> 0) then
|
|---|
| 889 | begin
|
|---|
| 890 | EndLoc := Loc;
|
|---|
| 891 | Break;
|
|---|
| 892 | end
|
|---|
| 893 | else if not Reached[NextLoc] then
|
|---|
| 894 | case CheckStep(MoveStyle, Time, V8 and 1, NextTime, RecoverTurns,
|
|---|
| 895 | Map[Loc], Map[NextLoc]) of
|
|---|
| 896 | csOk:
|
|---|
| 897 | if Pile.Put(NextLoc, NextTime + RecoverTurns * $1000) then
|
|---|
| 898 | PreLoc[NextLoc] := Loc;
|
|---|
| 899 | csForbiddenTile:
|
|---|
| 900 | Reached[NextLoc] := True; // don't check moving there again
|
|---|
| 901 | csCheckTerritory:
|
|---|
| 902 | if RO.Territory[NextLoc] = RO.Territory[Loc] then
|
|---|
| 903 | if Pile.Put(NextLoc, NextTime + RecoverTurns * $1000) then
|
|---|
| 904 | PreLoc[NextLoc] := Loc;
|
|---|
| 905 | end;
|
|---|
| 906 | end;
|
|---|
| 907 | if EndLoc >= 0 then Break;
|
|---|
| 908 | end;
|
|---|
| 909 | Pile.Free;
|
|---|
| 910 |
|
|---|
| 911 | if EndLoc >= 0 then
|
|---|
| 912 | begin
|
|---|
| 913 | Loc := EndLoc;
|
|---|
| 914 | NextLoc := PreLoc[Loc];
|
|---|
| 915 | while Loc <> FromLoc do
|
|---|
| 916 | begin // invert meaning of PreLoc
|
|---|
| 917 | Temp := Loc;
|
|---|
| 918 | Loc := NextLoc;
|
|---|
| 919 | NextLoc := PreLoc[Loc];
|
|---|
| 920 | PreLoc[Loc] := Temp;
|
|---|
| 921 | end;
|
|---|
| 922 | while Loc <> EndLoc do
|
|---|
| 923 | begin
|
|---|
| 924 | Loc := PreLoc[Loc];
|
|---|
| 925 | MoveResult := Unit_Step(uix, Loc);
|
|---|
| 926 | if (MoveResult <> eOK) and (MoveResult <> eLoaded) then
|
|---|
| 927 | begin
|
|---|
| 928 | Result := MoveResult;
|
|---|
| 929 | break;
|
|---|
| 930 | end;
|
|---|
| 931 | end;
|
|---|
| 932 | end
|
|---|
| 933 | else
|
|---|
| 934 | Result := eNoWay;
|
|---|
| 935 | end;
|
|---|
| 936 |
|
|---|
| 937 |
|
|---|
| 938 | //------------------------------------------------------------------------------
|
|---|
| 939 | // Oversea Transport
|
|---|
| 940 |
|
|---|
| 941 | procedure TToolAI.SeaTransport_BeginInitialize;
|
|---|
| 942 | begin
|
|---|
| 943 | fillchar(TransportAvailable, RO.nUn * sizeof(integer), $FF); // -1
|
|---|
| 944 | InitComplete := False;
|
|---|
| 945 | HaveDestinations := False;
|
|---|
| 946 | nTransportLoad := 0;
|
|---|
| 947 | TransportMoveStyle := 0;
|
|---|
| 948 | TransportCapacity := $100;
|
|---|
| 949 | Pile.Create(MapSize);
|
|---|
| 950 | end;
|
|---|
| 951 |
|
|---|
| 952 | procedure TToolAI.SeaTransport_AddLoad(uix: integer);
|
|---|
| 953 | var
|
|---|
| 954 | i: integer;
|
|---|
| 955 | begin
|
|---|
| 956 | Assert(not InitComplete); // call order violation!
|
|---|
| 957 | if Map[MyUnit[uix].Loc] and fTerrain < fGrass then exit;
|
|---|
| 958 | for i := 0 to nTransportLoad - 1 do
|
|---|
| 959 | if uix = uixTransportLoad[i] then Exit;
|
|---|
| 960 | uixTransportLoad[nTransportLoad] := uix;
|
|---|
| 961 | Inc(nTransportLoad);
|
|---|
| 962 | end;
|
|---|
| 963 |
|
|---|
| 964 | procedure TToolAI.SeaTransport_AddTransport(uix: integer);
|
|---|
| 965 | var
|
|---|
| 966 | MoveStyle: integer;
|
|---|
| 967 | begin
|
|---|
| 968 | Assert(not InitComplete); // call order violation!
|
|---|
| 969 | Assert(MyModel[MyUnit[uix].mix].Cap[mcSeaTrans] > 0);
|
|---|
| 970 | TransportAvailable[uix] := 1;
|
|---|
| 971 | with MyModel[MyUnit[uix].mix] do
|
|---|
| 972 | begin
|
|---|
| 973 | if MTrans * Cap[mcSeaTrans] < TransportCapacity then
|
|---|
| 974 | TransportCapacity := MTrans * Cap[mcSeaTrans];
|
|---|
| 975 | MoveStyle := GetMyMoveStyle(MyUnit[uix].mix, 100);
|
|---|
| 976 | if (TransportMoveStyle = 0) or (MoveStyle < TransportMoveStyle) and
|
|---|
| 977 | (MoveStyle and not TransportMoveStyle and 1 = 0) or
|
|---|
| 978 | (not MoveStyle and TransportMoveStyle and 1 <> 0) then
|
|---|
| 979 | TransportMoveStyle := MoveStyle;
|
|---|
| 980 | end;
|
|---|
| 981 | end;
|
|---|
| 982 |
|
|---|
| 983 | procedure TToolAI.SeaTransport_AddDestination(Loc: integer);
|
|---|
| 984 | begin
|
|---|
| 985 | Assert(not InitComplete); // call order violation!
|
|---|
| 986 | Pile.Put(Loc, $800);
|
|---|
| 987 | HaveDestinations := True;
|
|---|
| 988 | end;
|
|---|
| 989 |
|
|---|
| 990 | procedure TToolAI.SeaTransport_EndInitialize;
|
|---|
| 991 | var
|
|---|
| 992 | Loc0, Time0, V8, Loc1, ArriveTime, RecoverTurns: integer;
|
|---|
| 993 | Adjacent: TVicinity8Loc;
|
|---|
| 994 | begin
|
|---|
| 995 | Assert(not InitComplete); // call order violation!
|
|---|
| 996 | InitComplete := True;
|
|---|
| 997 | if HaveDestinations then
|
|---|
| 998 | begin // calculate TurnsAfterLoad from destination locs
|
|---|
| 999 | fillchar(TurnsAfterLoad, MapSize, $FF); // -1
|
|---|
| 1000 | while Pile.Get(Loc0, Time0) do
|
|---|
| 1001 | begin // search backward
|
|---|
| 1002 | if Time0 = $800 then TurnsAfterLoad[Loc0] := 1
|
|---|
| 1003 | else
|
|---|
| 1004 | TurnsAfterLoad[Loc0] := Time0 shr 12;
|
|---|
| 1005 | V8_to_Loc(Loc0, Adjacent);
|
|---|
| 1006 | for V8 := 0 to 7 do
|
|---|
| 1007 | begin
|
|---|
| 1008 | Loc1 := Adjacent[V8];
|
|---|
| 1009 | if (Loc1 >= 0) and (TurnsAfterLoad[Loc1] = -1) then
|
|---|
| 1010 | begin
|
|---|
| 1011 | case CheckStep(TransportMoveStyle, Time0, V8 and 1, ArriveTime,
|
|---|
| 1012 | RecoverTurns, Map[Loc0], Map[Loc1]) of
|
|---|
| 1013 | csOk: Pile.Put(Loc1, ArriveTime);
|
|---|
| 1014 | csForbiddenStep: TurnsAfterLoad[Loc1] := -2;
|
|---|
| 1015 | end;
|
|---|
| 1016 | end;
|
|---|
| 1017 | end;
|
|---|
| 1018 | end;
|
|---|
| 1019 | end;
|
|---|
| 1020 | Pile.Free;
|
|---|
| 1021 | end;
|
|---|
| 1022 |
|
|---|
| 1023 |
|
|---|
| 1024 | function TToolAI.SeaTransport_MakeGroupPlan(
|
|---|
| 1025 | var TransportPlan: TGroupTransportPlan): boolean;
|
|---|
| 1026 | var
|
|---|
| 1027 | V8, i, j, iPicked, uix, Loc0, Time0, Loc1, RecoverTurns, MoveStyle, TurnsLoaded,
|
|---|
| 1028 | TurnCount, tuix, tuix1, ArriveTime, TotalDelay, BestTotalDelay,
|
|---|
| 1029 | GroupCount, BestGroupCount, BestLoadLoc, FullMovementLoc, nSelectedLoad, f,
|
|---|
| 1030 | OriginContinent, a, b: integer;
|
|---|
| 1031 | CompleteFlag, NotReachedFlag, ContinueUnit: cardinal;
|
|---|
| 1032 | IsComplete, ok, IsFirstLoc: boolean;
|
|---|
| 1033 | StartLocPtr, ArrivedEnd: pinteger;
|
|---|
| 1034 | Adjacent: TVicinity8Loc;
|
|---|
| 1035 | uixSelectedLoad: array[0..15] of integer;
|
|---|
| 1036 | tuixSelectedLoad: array[0..15] of integer;
|
|---|
| 1037 | Arrived: array[0..lxmax * lymax] of cardinal;
|
|---|
| 1038 | ResponsibleTransport: array[0..lxmax * lymax - 1] of smallint;
|
|---|
| 1039 | TurnsBeforeLoad: array[0..lxmax * lymax - 1] of shortint;
|
|---|
| 1040 | GroupComplete: array[0..lxmax * lymax - 1] of boolean;
|
|---|
| 1041 | begin
|
|---|
| 1042 | Assert(InitComplete); // call order violation!
|
|---|
| 1043 |
|
|---|
| 1044 | if HaveDestinations and (nTransportLoad > 0) then
|
|---|
| 1045 | begin // transport and units already adjacent?
|
|---|
| 1046 | for uix := 0 to RO.nUn - 1 do
|
|---|
| 1047 | if (TransportAvailable[uix] > 0) and (Map[MyUnit[uix].Loc] and
|
|---|
| 1048 | fTerrain = fShore) then
|
|---|
| 1049 | begin
|
|---|
| 1050 | GroupCount := 0;
|
|---|
| 1051 | for tuix := 0 to nTransportLoad - 1 do
|
|---|
| 1052 | begin
|
|---|
| 1053 | Loc_to_ab(MyUnit[uix].Loc, MyUnit[uixTransportLoad[tuix]].Loc, a, b);
|
|---|
| 1054 | if (abs(a) <= 1) and (abs(b) <= 1) then
|
|---|
| 1055 | begin
|
|---|
| 1056 | Assert((a <> 0) or (b <> 0));
|
|---|
| 1057 | Inc(GroupCount);
|
|---|
| 1058 | end;
|
|---|
| 1059 | end;
|
|---|
| 1060 | if (GroupCount = nTransportLoad) or (GroupCount >= TransportCapacity) then
|
|---|
| 1061 | begin
|
|---|
| 1062 | TransportPlan.LoadLoc := MyUnit[uix].Loc;
|
|---|
| 1063 | TransportPlan.uixTransport := uix;
|
|---|
| 1064 | TransportAvailable[uix] := 0;
|
|---|
| 1065 | TransportPlan.TurnsEmpty := 0;
|
|---|
| 1066 | TransportPlan.TurnsLoaded := TurnsAfterLoad[TransportPlan.LoadLoc];
|
|---|
| 1067 | TransportPlan.nLoad := 0;
|
|---|
| 1068 | for tuix := nTransportLoad - 1 downto 0 do
|
|---|
| 1069 | begin
|
|---|
| 1070 | Loc_to_ab(TransportPlan.LoadLoc, MyUnit[uixTransportLoad[tuix]].Loc, a, b);
|
|---|
| 1071 | if (abs(a) <= 1) and (abs(b) <= 1) then
|
|---|
| 1072 | begin
|
|---|
| 1073 | TransportPlan.uixLoad[TransportPlan.nLoad] := uixTransportLoad[tuix];
|
|---|
| 1074 | uixTransportLoad[tuix] := uixTransportLoad[nTransportLoad - 1];
|
|---|
| 1075 | Dec(nTransportLoad);
|
|---|
| 1076 | Inc(TransportPlan.nLoad);
|
|---|
| 1077 | if TransportPlan.nLoad = TransportCapacity then break;
|
|---|
| 1078 | end;
|
|---|
| 1079 | end;
|
|---|
| 1080 | Result := True;
|
|---|
| 1081 | exit;
|
|---|
| 1082 | end;
|
|---|
| 1083 | end;
|
|---|
| 1084 | end;
|
|---|
| 1085 |
|
|---|
| 1086 | while HaveDestinations and (nTransportLoad > 0) do
|
|---|
| 1087 | begin
|
|---|
| 1088 | // select units from same continent
|
|---|
| 1089 | fillchar(Arrived, 4 * nContinent, 0); // misuse Arrived as counter
|
|---|
| 1090 | for tuix := 0 to nTransportLoad - 1 do
|
|---|
| 1091 | begin
|
|---|
| 1092 | Assert(Map[MyUnit[uixTransportLoad[tuix]].Loc] and fTerrain >= fGrass);
|
|---|
| 1093 | f := Formation[MyUnit[uixTransportLoad[tuix]].Loc];
|
|---|
| 1094 | if f >= 0 then Inc(Arrived[f]);
|
|---|
| 1095 | end;
|
|---|
| 1096 | OriginContinent := 0;
|
|---|
| 1097 | for f := 1 to nContinent - 1 do
|
|---|
| 1098 | if Arrived[f] > Arrived[OriginContinent] then OriginContinent := f;
|
|---|
| 1099 | nSelectedLoad := 0;
|
|---|
| 1100 | for tuix := 0 to nTransportLoad - 1 do
|
|---|
| 1101 | if Formation[MyUnit[uixTransportLoad[tuix]].Loc] = OriginContinent then
|
|---|
| 1102 | begin
|
|---|
| 1103 | tuixSelectedLoad[nSelectedLoad] := tuix;
|
|---|
| 1104 | uixSelectedLoad[nSelectedLoad] := uixTransportLoad[tuix];
|
|---|
| 1105 | Inc(nSelectedLoad);
|
|---|
| 1106 | if nSelectedLoad = 16 then break;
|
|---|
| 1107 | end;
|
|---|
| 1108 |
|
|---|
| 1109 | Pile.Create(MapSize);
|
|---|
| 1110 | fillchar(ResponsibleTransport, MapSize * 2, $FF); // -1
|
|---|
| 1111 | fillchar(TurnsBeforeLoad, MapSize, $FF); // -1
|
|---|
| 1112 | ok := False;
|
|---|
| 1113 | for uix := 0 to RO.nUn - 1 do if TransportAvailable[uix] > 0 then
|
|---|
| 1114 | begin
|
|---|
| 1115 | ok := True;
|
|---|
| 1116 | Pile.Put(MyUnit[uix].Loc, ($800 - MyUnit[uix].Movement) shl 12 + uix);
|
|---|
| 1117 | end;
|
|---|
| 1118 | if not ok then // no transports
|
|---|
| 1119 | begin
|
|---|
| 1120 | TransportPlan.LoadLoc := -1;
|
|---|
| 1121 | Result := False;
|
|---|
| 1122 | Pile.Free;
|
|---|
| 1123 | exit;
|
|---|
| 1124 | end;
|
|---|
| 1125 | while Pile.Get(Loc0, Time0) do
|
|---|
| 1126 | begin
|
|---|
| 1127 | uix := Time0 and $FFF;
|
|---|
| 1128 | Time0 := Time0 shr 12;
|
|---|
| 1129 | ResponsibleTransport[Loc0] := uix;
|
|---|
| 1130 | TurnsBeforeLoad[Loc0] := Time0 shr 12;
|
|---|
| 1131 | V8_to_Loc(Loc0, Adjacent);
|
|---|
| 1132 | for V8 := 0 to 7 do
|
|---|
| 1133 | begin
|
|---|
| 1134 | Loc1 := Adjacent[V8];
|
|---|
| 1135 | if (Loc1 >= 0) and (ResponsibleTransport[Loc1] < 0) then
|
|---|
| 1136 | case CheckStep(GetMyMoveStyle(MyUnit[uix].mix, MyUnit[uix].Health),
|
|---|
| 1137 | Time0, V8 and 1, ArriveTime, RecoverTurns, Map[Loc0], Map[Loc1]) of
|
|---|
| 1138 | csOk: Pile.Put(Loc1, ArriveTime shl 12 + uix);
|
|---|
| 1139 | csForbiddenTile: ResponsibleTransport[Loc1] := RO.nUn; // don't check again
|
|---|
| 1140 | end;
|
|---|
| 1141 | end;
|
|---|
| 1142 | end;
|
|---|
| 1143 |
|
|---|
| 1144 | fillchar(Arrived, MapSize * 4, $55); // set NotReachedFlag for all tiles
|
|---|
| 1145 | fillchar(GroupComplete, MapSize, False);
|
|---|
| 1146 | BestLoadLoc := -1;
|
|---|
| 1147 |
|
|---|
| 1148 | // check direct loading
|
|---|
| 1149 | for tuix := 0 to nSelectedLoad - 1 do
|
|---|
| 1150 | begin
|
|---|
| 1151 | uix := uixSelectedLoad[tuix];
|
|---|
| 1152 | if MyUnit[uix].Movement = integer(MyModel[MyUnit[uix].mix].Speed) then
|
|---|
| 1153 | begin
|
|---|
| 1154 | NotReachedFlag := 1 shl (2 * tuix);
|
|---|
| 1155 | CompleteFlag := NotReachedFlag shl 1;
|
|---|
| 1156 | V8_to_Loc(MyUnit[uix].Loc, Adjacent);
|
|---|
| 1157 | for V8 := 0 to 7 do
|
|---|
| 1158 | begin
|
|---|
| 1159 | Loc1 := Adjacent[V8];
|
|---|
| 1160 | if (Loc1 >= 0) and (Map[Loc1] and fTerrain < fGrass) and
|
|---|
| 1161 | not GroupComplete[Loc1] then
|
|---|
| 1162 | begin // possible transport start location
|
|---|
| 1163 | Arrived[Loc1] := (Arrived[Loc1] or CompleteFlag) and not NotReachedFlag;
|
|---|
| 1164 | if (TurnsBeforeLoad[Loc1] >= 0) and (TurnsAfterLoad[Loc1] >= 0) then
|
|---|
| 1165 | begin
|
|---|
| 1166 | i := 1;
|
|---|
| 1167 | GroupCount := 0;
|
|---|
| 1168 | for tuix1 := 0 to nSelectedLoad - 1 do
|
|---|
| 1169 | begin
|
|---|
| 1170 | if Arrived[loc1] and i = 0 then Inc(GroupCount);
|
|---|
| 1171 | i := i shl 2;
|
|---|
| 1172 | end;
|
|---|
| 1173 | assert(GroupCount <= TransportCapacity);
|
|---|
| 1174 | if (GroupCount = TransportCapacity) or (GroupCount = nSelectedLoad) then
|
|---|
| 1175 | GroupComplete[loc1] := True;
|
|---|
| 1176 | TotalDelay := TurnsBeforeLoad[Loc1] + TurnsAfterLoad[Loc1];
|
|---|
| 1177 | if (BestLoadLoc < 0) or
|
|---|
| 1178 | (GroupCount shl 16 - TotalDelay > BestGroupCount shl 16 - BestTotalDelay) then
|
|---|
| 1179 | begin
|
|---|
| 1180 | BestLoadLoc := Loc1;
|
|---|
| 1181 | BestGroupCount := GroupCount;
|
|---|
| 1182 | BestTotalDelay := TotalDelay;
|
|---|
| 1183 | end;
|
|---|
| 1184 | end;
|
|---|
| 1185 | end;
|
|---|
| 1186 | end;
|
|---|
| 1187 | end;
|
|---|
| 1188 | end;
|
|---|
| 1189 |
|
|---|
| 1190 | TurnCount := 0;
|
|---|
| 1191 | ArrivedEnd := @Arrived[MapSize];
|
|---|
| 1192 |
|
|---|
| 1193 | // check moving+loading
|
|---|
| 1194 | ContinueUnit := 1 shl nSelectedLoad - 1;
|
|---|
| 1195 | while (ContinueUnit > 0) and ((BestLoadLoc < 0) or (TurnCount < BestTotalDelay - 2)) do
|
|---|
| 1196 | begin
|
|---|
| 1197 | for tuix := 0 to nSelectedLoad - 1 do if 1 shl tuix and ContinueUnit <> 0 then
|
|---|
| 1198 | begin
|
|---|
| 1199 | uix := uixSelectedLoad[tuix];
|
|---|
| 1200 | MoveStyle := GetMyMoveStyle(MyUnit[uix].mix, MyUnit[uix].Health);
|
|---|
| 1201 | NotReachedFlag := 1 shl (2 * tuix);
|
|---|
| 1202 | CompleteFlag := NotReachedFlag shl 1;
|
|---|
| 1203 | FullMovementLoc := -1;
|
|---|
| 1204 |
|
|---|
| 1205 | Pile.Empty;
|
|---|
| 1206 | if TurnCount = 0 then
|
|---|
| 1207 | begin
|
|---|
| 1208 | Pile.Put(MyUnit[uix].Loc, $1800 - MyUnit[uix].Movement);
|
|---|
| 1209 | if MyUnit[uix].Movement = integer(MyModel[MyUnit[uix].mix].Speed) then
|
|---|
| 1210 | FullMovementLoc := MyUnit[uix].Loc;
|
|---|
| 1211 | // surrounding tiles can be loaded immediately
|
|---|
| 1212 | StartLocPtr := ArrivedEnd;
|
|---|
| 1213 | end
|
|---|
| 1214 | else
|
|---|
| 1215 | StartLocPtr := @Arrived;
|
|---|
| 1216 | IsFirstLoc := True;
|
|---|
| 1217 |
|
|---|
| 1218 | repeat
|
|---|
| 1219 | if StartLocPtr <> ArrivedEnd then
|
|---|
| 1220 | // search next movement start location for this turn
|
|---|
| 1221 | StartLocPtr := NextZero(StartLocPtr, ArrivedEnd, CompleteFlag or NotReachedFlag);
|
|---|
| 1222 | if StartLocPtr <> ArrivedEnd then
|
|---|
| 1223 | begin
|
|---|
| 1224 | Loc0 := (integer(StartLocPtr) - integer(@Arrived)) shr 2;
|
|---|
| 1225 | Inc(StartLocPtr);
|
|---|
| 1226 | Time0 := $800;
|
|---|
| 1227 | end
|
|---|
| 1228 | else if not Pile.Get(Loc0, Time0) then
|
|---|
| 1229 | begin
|
|---|
| 1230 | if IsFirstLoc then ContinueUnit := ContinueUnit and not (1 shl tuix);
|
|---|
| 1231 | break;
|
|---|
| 1232 | end;
|
|---|
| 1233 | IsFirstLoc := False;
|
|---|
| 1234 |
|
|---|
| 1235 | Arrived[Loc0] := Arrived[Loc0] and not NotReachedFlag;
|
|---|
| 1236 | if not GroupComplete[Loc0] and (Map[Loc0] and fTerrain <> fMountains) then
|
|---|
| 1237 | begin // check whether group complete -- no mountains because complete flag might be faked there
|
|---|
| 1238 | i := 1;
|
|---|
| 1239 | GroupCount := 0;
|
|---|
| 1240 | for tuix1 := 0 to nSelectedLoad - 1 do
|
|---|
| 1241 | begin
|
|---|
| 1242 | if Arrived[Loc0] and i = 0 then Inc(GroupCount);
|
|---|
| 1243 | i := i shl 2;
|
|---|
| 1244 | end;
|
|---|
| 1245 | assert(GroupCount <= TransportCapacity);
|
|---|
| 1246 | if (GroupCount = TransportCapacity) or (GroupCount = nSelectedLoad) then
|
|---|
| 1247 | GroupComplete[Loc0] := True;
|
|---|
| 1248 | end;
|
|---|
| 1249 |
|
|---|
| 1250 | V8_to_Loc(Loc0, Adjacent);
|
|---|
| 1251 | IsComplete := True;
|
|---|
| 1252 | for V8 := 0 to 7 do
|
|---|
| 1253 | begin
|
|---|
| 1254 | Loc1 := Adjacent[V8];
|
|---|
| 1255 | if (Loc1 < G.ly) or (Loc1 >= MapSize - G.ly) then
|
|---|
| 1256 | Adjacent[V8] := -1 // pole, don't consider moving here
|
|---|
| 1257 | else if Arrived[Loc1] and NotReachedFlag = 0 then
|
|---|
| 1258 | Adjacent[V8] := -1 // unit has already arrived this tile
|
|---|
| 1259 | else if GroupComplete[Loc1] then
|
|---|
| 1260 | Adjacent[V8] := -1 // already other group complete
|
|---|
| 1261 | else if Map[Loc1] and fTerrain < fGrass then
|
|---|
| 1262 | begin // possible transport start location
|
|---|
| 1263 | Arrived[Loc1] := (Arrived[Loc1] or CompleteFlag) and not NotReachedFlag;
|
|---|
| 1264 | Adjacent[V8] := -1;
|
|---|
| 1265 | if (TurnsBeforeLoad[Loc1] >= 0) and (TurnsAfterLoad[Loc1] >= 0) then
|
|---|
| 1266 | begin
|
|---|
| 1267 | i := 1;
|
|---|
| 1268 | GroupCount := 0;
|
|---|
| 1269 | for tuix1 := 0 to nSelectedLoad - 1 do
|
|---|
| 1270 | begin
|
|---|
| 1271 | if Arrived[loc1] and i = 0 then Inc(GroupCount);
|
|---|
| 1272 | i := i shl 2;
|
|---|
| 1273 | end;
|
|---|
| 1274 | assert(GroupCount <= TransportCapacity);
|
|---|
| 1275 | if (GroupCount = TransportCapacity) or (GroupCount = nSelectedLoad) then
|
|---|
| 1276 | GroupComplete[loc1] := True;
|
|---|
| 1277 | if TurnsBeforeLoad[Loc1] > TurnCount + 1 then
|
|---|
| 1278 | TotalDelay := TurnsBeforeLoad[Loc1] + TurnsAfterLoad[Loc1]
|
|---|
| 1279 | else
|
|---|
| 1280 | TotalDelay := TurnCount + 1 + TurnsAfterLoad[Loc1];
|
|---|
| 1281 | if (BestLoadLoc < 0) or (GroupCount shl
|
|---|
| 1282 | 16 - TotalDelay > BestGroupCount shl 16 - BestTotalDelay) then
|
|---|
| 1283 | begin
|
|---|
| 1284 | BestLoadLoc := Loc1;
|
|---|
| 1285 | BestGroupCount := GroupCount;
|
|---|
| 1286 | BestTotalDelay := TotalDelay;
|
|---|
| 1287 | end;
|
|---|
| 1288 | end;
|
|---|
| 1289 | end
|
|---|
| 1290 | else if (Map[Loc1] and fTerrain = fMountains) and
|
|---|
| 1291 | ((Map[Loc0] and (fRoad or fRR or fCity) = 0) or
|
|---|
| 1292 | (Map[Loc1] and (fRoad or fRR or fCity) = 0)) and
|
|---|
| 1293 | (Map[Loc0] and Map[Loc1] and (fRiver or fCanal) = 0) then
|
|---|
| 1294 | begin // mountain delay too complicated for this algorithm
|
|---|
| 1295 | Arrived[Loc1] := (Arrived[Loc1] or CompleteFlag) and not NotReachedFlag;
|
|---|
| 1296 | Adjacent[V8] := -1;
|
|---|
| 1297 | end
|
|---|
| 1298 | else
|
|---|
| 1299 | IsComplete := False;
|
|---|
| 1300 | end;
|
|---|
| 1301 | if IsComplete then
|
|---|
| 1302 | begin
|
|---|
| 1303 | Arrived[Loc0] := (Arrived[Loc0] or CompleteFlag) and not NotReachedFlag;
|
|---|
| 1304 | continue;
|
|---|
| 1305 | end;
|
|---|
| 1306 | IsComplete := True;
|
|---|
| 1307 | for V8 := 0 to 7 do
|
|---|
| 1308 | begin
|
|---|
| 1309 | Loc1 := Adjacent[V8];
|
|---|
| 1310 | if Loc1 >= 0 then
|
|---|
| 1311 | begin
|
|---|
| 1312 | ok := False;
|
|---|
| 1313 | case CheckStep(MoveStyle, Time0, V8 and 1, ArriveTime,
|
|---|
| 1314 | RecoverTurns, Map[Loc0], Map[Loc1]) of
|
|---|
| 1315 | csOk: ok := True;
|
|---|
| 1316 | csForbiddenTile:
|
|---|
| 1317 | ;// !!! don't check moving there again
|
|---|
| 1318 | csCheckTerritory:
|
|---|
| 1319 | ok := RO.Territory[Loc1] = RO.Territory[Loc0];
|
|---|
| 1320 | end;
|
|---|
| 1321 | if ok and Pile.TestPut(Loc1, ArriveTime) then
|
|---|
| 1322 | if ArriveTime < $2000 then Pile.Put(Loc1, ArriveTime)
|
|---|
| 1323 | else
|
|---|
| 1324 | IsComplete := False;
|
|---|
| 1325 | end;
|
|---|
| 1326 | end;
|
|---|
| 1327 | if IsComplete then
|
|---|
| 1328 | Arrived[Loc0] := (Arrived[Loc0] or CompleteFlag) and not NotReachedFlag;
|
|---|
| 1329 | until False;
|
|---|
| 1330 | end;
|
|---|
| 1331 |
|
|---|
| 1332 | Inc(TurnCount);
|
|---|
| 1333 | end;
|
|---|
| 1334 | Pile.Free;
|
|---|
| 1335 |
|
|---|
| 1336 | if BestLoadLoc >= 0 then
|
|---|
| 1337 | begin
|
|---|
| 1338 | TransportPlan.LoadLoc := BestLoadLoc;
|
|---|
| 1339 | TransportPlan.uixTransport := ResponsibleTransport[BestLoadLoc];
|
|---|
| 1340 | TransportAvailable[TransportPlan.uixTransport] := 0;
|
|---|
| 1341 | TransportPlan.TurnsEmpty := BestTotalDelay - TurnsAfterLoad[BestLoadLoc];
|
|---|
| 1342 | TransportPlan.TurnsLoaded := TurnsAfterLoad[BestLoadLoc];
|
|---|
| 1343 | TransportPlan.nLoad := 0;
|
|---|
| 1344 | for tuix := nSelectedLoad - 1 downto 0 do
|
|---|
| 1345 | if 1 shl (2 * tuix) and Arrived[BestLoadLoc] = 0 then
|
|---|
| 1346 | begin
|
|---|
| 1347 | assert(uixTransportLoad[tuixSelectedLoad[tuix]] = uixSelectedLoad[tuix]);
|
|---|
| 1348 | TransportPlan.uixLoad[TransportPlan.nLoad] := uixSelectedLoad[tuix];
|
|---|
| 1349 | uixTransportLoad[tuixSelectedLoad[tuix]] :=
|
|---|
| 1350 | uixTransportLoad[nTransportLoad - 1];
|
|---|
| 1351 | Dec(nTransportLoad);
|
|---|
| 1352 | Inc(TransportPlan.nLoad);
|
|---|
| 1353 | end;
|
|---|
| 1354 | Result := True;
|
|---|
| 1355 | exit;
|
|---|
| 1356 | end;
|
|---|
| 1357 |
|
|---|
| 1358 | // no loading location for a single of these units -- remove all
|
|---|
| 1359 | // should be pretty rare case
|
|---|
| 1360 | for tuix := nSelectedLoad - 1 downto 0 do
|
|---|
| 1361 | begin
|
|---|
| 1362 | assert(uixTransportLoad[tuixSelectedLoad[tuix]] = uixSelectedLoad[tuix]);
|
|---|
| 1363 | uixTransportLoad[tuixSelectedLoad[tuix]] :=
|
|---|
| 1364 | uixTransportLoad[nTransportLoad - 1];
|
|---|
| 1365 | Dec(nTransportLoad);
|
|---|
| 1366 | end;
|
|---|
| 1367 | end;
|
|---|
| 1368 | TransportPlan.LoadLoc := -1;
|
|---|
| 1369 | Result := False;
|
|---|
| 1370 | end;
|
|---|
| 1371 |
|
|---|
| 1372 |
|
|---|
| 1373 | //------------------------------------------------------------------------------
|
|---|
| 1374 |
|
|---|
| 1375 |
|
|---|
| 1376 | procedure SetAdvancedness;
|
|---|
| 1377 | var
|
|---|
| 1378 | ad, j, Reduction, AgeThreshold: integer;
|
|---|
| 1379 | known: array[0..nAdv - 1] of integer;
|
|---|
| 1380 |
|
|---|
| 1381 | procedure MarkPreqs(ad: integer);
|
|---|
| 1382 | var
|
|---|
| 1383 | i: integer;
|
|---|
| 1384 | begin
|
|---|
| 1385 | if known[ad] = 0 then
|
|---|
| 1386 | begin
|
|---|
| 1387 | known[ad] := 1;
|
|---|
| 1388 | for i := 0 to 2 do
|
|---|
| 1389 | if AdvPreq[ad, i] >= 0 then MarkPreqs(AdvPreq[ad, i]);
|
|---|
| 1390 | end;
|
|---|
| 1391 | end;
|
|---|
| 1392 |
|
|---|
| 1393 | begin
|
|---|
| 1394 | FillChar(Advancedness, SizeOf(Advancedness), 0);
|
|---|
| 1395 | for ad := 0 to nAdv - 1 do
|
|---|
| 1396 | begin
|
|---|
| 1397 | FillChar(known, SizeOf(known), 0);
|
|---|
| 1398 | MarkPreqs(ad);
|
|---|
| 1399 | for j := 0 to nAdv - 1 do if known[j] > 0 then Inc(Advancedness[ad]);
|
|---|
| 1400 | end;
|
|---|
| 1401 | AgeThreshold := Advancedness[adScience];
|
|---|
| 1402 | Reduction := Advancedness[adScience] div 3;
|
|---|
| 1403 | for ad := 0 to nAdv - 5 do
|
|---|
| 1404 | if Advancedness[ad] >= AgeThreshold then
|
|---|
| 1405 | Dec(Advancedness[ad], Reduction);
|
|---|
| 1406 | AgeThreshold := Advancedness[adMassProduction];
|
|---|
| 1407 | Reduction := (Advancedness[adMassProduction] - Advancedness[adScience]) div 3;
|
|---|
| 1408 | for ad := 0 to nAdv - 5 do
|
|---|
| 1409 | if Advancedness[ad] >= AgeThreshold then
|
|---|
| 1410 | Dec(Advancedness[ad], Reduction);
|
|---|
| 1411 | end;
|
|---|
| 1412 |
|
|---|
| 1413 |
|
|---|
| 1414 | initialization
|
|---|
| 1415 | SetAdvancedness;
|
|---|
| 1416 |
|
|---|
| 1417 | end.
|
|---|