Changeset 6 for trunk/LocalPlayer/ClientTools.pas
- Timestamp:
- Jan 7, 2017, 11:32:14 AM (7 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LocalPlayer/ClientTools.pas
r2 r6 1 1 {$INCLUDE switches} 2 3 2 unit ClientTools; 4 3 … … 9 8 10 9 const 11 nOfferedResourceWeights=6;12 OfferedResourceWeights: array[0..nOfferedResourceWeights-1] of cardinal=13 (rwOff, rwMaxScience, rwForceScience, rwMaxGrowth, rwForceProd, rwMaxProd);10 nOfferedResourceWeights = 6; 11 OfferedResourceWeights: array [0 .. nOfferedResourceWeights - 1] of cardinal = 12 (rwOff, rwMaxScience, rwForceScience, rwMaxGrowth, rwForceProd, rwMaxProd); 14 13 15 14 type 16 TImpOrder=array[0..(nImp+4) div 4 *4 -1] of ShortInt;17 TEnhancementJobs=array[0..11,0..7] of Byte;18 JobResultSet=set of 0..39;15 TImpOrder = array [0 .. (nImp + 4) div 4 * 4 - 1] of ShortInt; 16 TEnhancementJobs = array [0 .. 11, 0 .. 7] of Byte; 17 JobResultSet = set of 0 .. 39; 19 18 20 19 var 21 Server: TServerCall; 22 G: TNewGameData; 23 me: integer; 24 MyRO: ^TPlayerContext; 25 MyMap: ^TTileList; 26 MyUn: ^TUnList; 27 MyCity: ^TCityList; 28 MyModel: ^TModelList; 29 30 AdvValue: array[0..nAdv-1] of integer; 31 32 33 function dLoc(Loc,dx,dy: integer): integer; 34 function Distance(Loc0,Loc1: integer): integer; 35 function UnrestAtLoc(uix,Loc: integer): boolean; 20 Server: TServerCall; 21 G: TNewGameData; 22 me: integer; 23 MyRO: ^TPlayerContext; 24 MyMap: ^TTileList; 25 MyUn: ^TUnList; 26 MyCity: ^TCityList; 27 MyModel: ^TModelList; 28 29 AdvValue: array [0 .. nAdv - 1] of integer; 30 31 function dLoc(Loc, dx, dy: integer): integer; 32 function Distance(Loc0, Loc1: integer): integer; 33 function UnrestAtLoc(uix, Loc: integer): boolean; 36 34 function GetMoveAdvice(uix, ToLoc: integer; 37 35 var MoveAdviceData: TMoveAdviceData): integer; … … 43 41 function IsMilReportNew(Enemy: integer): boolean; 44 42 function CutCityFoodSurplus(FoodSurplus: integer; IsCityAlive: boolean; 45 gov,size: integer): integer; 46 function CityTaxBalance(cix: integer; const CityReport: TCityReportNew): integer; 43 gov, size: integer): integer; 44 function CityTaxBalance(cix: integer; const CityReport: TCityReportNew) 45 : integer; 47 46 procedure SumCities(var TaxSum, ScienceSum: integer); 48 function JobTest(uix, Job: integer; IgnoreResults: JobResultSet = []): boolean;47 function JobTest(uix, Job: integer; IgnoreResults: JobResultSet = []): boolean; 49 48 procedure GetUnitInfo(Loc: integer; var uix: integer; var UnitInfo: TUnitInfo); 50 49 procedure GetCityInfo(Loc: integer; var cix: integer; var CityInfo: TCityInfo); 51 50 function UnitExhausted(uix: integer): boolean; 52 51 function ModelHash(const ModelInfo: TModelInfo): integer; 53 function ProcessEnhancement(uix: integer; const Jobs: TEnhancementJobs): integer; 52 function ProcessEnhancement(uix: integer; const Jobs: TEnhancementJobs) 53 : integer; 54 54 function AutoBuild(cix: integer; const ImpOrder: TImpOrder): boolean; 55 55 procedure DebugMessage(Level: integer; Text: string); … … 62 62 procedure CityOptimizer_EndOfTurn; 63 63 64 65 64 implementation 66 65 67 66 var 68 CityNeedsOptimize: array[0..ncmax-1] of boolean; 69 70 71 function dLoc(Loc,dx,dy: integer): integer; 67 CityNeedsOptimize: array [0 .. ncmax - 1] of boolean; 68 69 function dLoc(Loc, dx, dy: integer): integer; 72 70 var 73 y0: integer;71 y0: integer; 74 72 begin 75 y0:=(Loc+G.lx*1024) div G.lx -1024; 76 result:=(Loc+(dx+y0 and 1+G.lx*1024) shr 1) mod G.lx +G.lx*(y0+dy) 73 y0 := (Loc + G.lx * 1024) div G.lx - 1024; 74 result := (Loc + (dx + y0 and 1 + G.lx * 1024) shr 1) mod G.lx + G.lx 75 * (y0 + dy) 77 76 end; 78 77 79 function Distance(Loc0, Loc1: integer): integer;78 function Distance(Loc0, Loc1: integer): integer; 80 79 var 81 dx,dy: integer;80 dx, dy: integer; 82 81 begin 83 inc(Loc0,G.lx*1024);84 inc(Loc1,G.lx*1024);85 dx:=abs(((Loc1 mod G.lx *2 +Loc1 div G.lx and 1) 86 -(Loc0 mod G.lx *2 +Loc0 div G.lx and 1)+3*G.lx) mod (2*G.lx) -G.lx);87 dy:=abs(Loc1 div G.lx-Loc0 div G.lx);88 result:=dx+dy+abs(dx-dy) shr 1;82 inc(Loc0, G.lx * 1024); 83 inc(Loc1, G.lx * 1024); 84 dx := abs(((Loc1 mod G.lx * 2 + Loc1 div G.lx and 1) - (Loc0 mod G.lx * 2 + 85 Loc0 div G.lx and 1) + 3 * G.lx) mod (2 * G.lx) - G.lx); 86 dy := abs(Loc1 div G.lx - Loc0 div G.lx); 87 result := dx + dy + abs(dx - dy) shr 1; 89 88 end; 90 89 91 function UnrestAtLoc(uix, Loc: integer): boolean;90 function UnrestAtLoc(uix, Loc: integer): boolean; 92 91 var 93 uix1: integer;92 uix1: integer; 94 93 begin 95 result:=false;96 if MyModel[MyUn[uix].mix].Flags and mdCivil=0 then97 case MyRO.Government of98 gRepublic, gFuture:99 result:=(MyRO.Territory[Loc]>=0) and (MyRO.Territory[Loc]<>me)100 and (MyRO.Treaty[MyRO.Territory[Loc]]<trAlliance);101 gDemocracy:102 result:=(MyRO.Territory[Loc]<0) or (MyRO.Territory[Loc]<>me)103 and (MyRO.Treaty[MyRO.Territory[Loc]]<trAlliance);94 result := false; 95 if MyModel[MyUn[uix].mix].Flags and mdCivil = 0 then 96 case MyRO.Government of 97 gRepublic, gFuture: 98 result := (MyRO.Territory[Loc] >= 0) and (MyRO.Territory[Loc] <> me) and 99 (MyRO.Treaty[MyRO.Territory[Loc]] < trAlliance); 100 gDemocracy: 101 result := (MyRO.Territory[Loc] < 0) or (MyRO.Territory[Loc] <> me) and 102 (MyRO.Treaty[MyRO.Territory[Loc]] < trAlliance); 104 103 end; 105 with MyModel[MyUn[uix].mix] do106 if Cap[mcSeaTrans]+Cap[mcAirTrans]+Cap[mcCarrier]>0 then107 for uix1:=0 to MyRO.nUn-1 do // check transported units too108 if (MyUn[uix1].Loc>=0) and (MyUn[uix1].Master=uix) then109 result:=result or UnrestAtLoc(uix1,Loc);104 with MyModel[MyUn[uix].mix] do 105 if Cap[mcSeaTrans] + Cap[mcAirTrans] + Cap[mcCarrier] > 0 then 106 for uix1 := 0 to MyRO.nUn - 1 do // check transported units too 107 if (MyUn[uix1].Loc >= 0) and (MyUn[uix1].Master = uix) then 108 result := result or UnrestAtLoc(uix1, Loc); 110 109 end; 111 110 112 function GetMoveAdvice(uix, ToLoc: integer; var MoveAdviceData: TMoveAdviceData): integer; 111 function GetMoveAdvice(uix, ToLoc: integer; 112 var MoveAdviceData: TMoveAdviceData): integer; 113 113 var 114 MinEndHealth: integer;114 MinEndHealth: integer; 115 115 begin 116 if MyModel[MyUn[uix].mix].Domain=dGround then MinEndHealth:=100 117 else MinEndHealth:=1; // resistent to hostile terrain -- don't consider 118 repeat 119 if MyUn[uix].Health>=MinEndHealth then 120 begin 121 MoveAdviceData.ToLoc:=ToLoc; 122 MoveAdviceData.MoreTurns:=999; 123 MoveAdviceData.MaxHostile_MovementLeft:=MyUn[uix].Health-MinEndHealth; 124 result:=Server(sGetMoveAdvice,me,uix,MoveAdviceData); 125 if (MinEndHealth<=1) or (result<>eNoWay) then exit; 116 if MyModel[MyUn[uix].mix].Domain = dGround then 117 MinEndHealth := 100 118 else 119 MinEndHealth := 1; // resistent to hostile terrain -- don't consider 120 repeat 121 if MyUn[uix].Health >= MinEndHealth then 122 begin 123 MoveAdviceData.ToLoc := ToLoc; 124 MoveAdviceData.MoreTurns := 999; 125 MoveAdviceData.MaxHostile_MovementLeft := MyUn[uix].Health - MinEndHealth; 126 result := Server(sGetMoveAdvice, me, uix, MoveAdviceData); 127 if (MinEndHealth <= 1) or (result <> eNoWay) then 128 exit; 126 129 end; 127 case MinEndHealth of 128 100: MinEndHealth:=50; 129 50: MinEndHealth:=25; 130 25: MinEndHealth:=12; 131 else MinEndHealth:=1 130 case MinEndHealth of 131 100: 132 MinEndHealth := 50; 133 50: 134 MinEndHealth := 25; 135 25: 136 MinEndHealth := 12; 137 else 138 MinEndHealth := 1 132 139 end; 133 until false 134 end; 135 136 function ColorOfHealth(Health: integer): integer; 137 var 138 red,green: integer; 139 begin 140 green:=400*Health div 100; if green>200 then green:=200; 141 red:=510*(100-Health) div 100; if red>255 then red:=255; 142 result:=green shl 8 + red 143 end; 144 145 function IsMultiPlayerGame: boolean; 146 var 147 p1: integer; 148 begin 149 result:=false; 150 for p1:=1 to nPl-1 do 151 if G.RO[p1]<>nil then result:=true; 152 end; 153 154 procedure ItsMeAgain(p: integer); 155 begin 156 if G.RO[p]<>nil then 157 MyRO:=pointer(G.RO[p]) 158 else if G.SuperVisorRO[p]<>nil then 159 MyRO:=pointer(G.SuperVisorRO[p]) 160 else exit; 161 me:=p; 162 MyMap:=pointer(MyRO.Map); 163 MyUn:=pointer(MyRO.Un); 164 MyCity:=pointer(MyRO.City); 165 MyModel:=pointer(MyRO.Model); 166 end; 167 168 function GetAge(p: integer): integer; 169 var 170 i: integer; 171 begin 172 if p=me then 173 begin 174 result:=0; 175 for i:=1 to 3 do 176 if MyRO.Tech[AgePreq[i]]>=tsApplicable then result:=i; 177 end 178 else 179 begin 180 result:=0; 181 for i:=1 to 3 do 182 if MyRO.EnemyReport[p].Tech[AgePreq[i]]>=tsApplicable then result:=i; 183 end 184 end; 185 186 function IsCivilReportNew(Enemy: integer): boolean; 187 var 188 i: integer; 189 begin 190 assert(Enemy<>me); 191 i:=MyRO.EnemyReport[Enemy].TurnOfCivilReport; 192 result:= (i=MyRO.Turn) or (i=MyRO.Turn-1) and (Enemy>me); 193 end; 194 195 function IsMilReportNew(Enemy: integer): boolean; 196 var 197 i: integer; 198 begin 199 assert(Enemy<>me); 200 i:=MyRO.EnemyReport[Enemy].TurnOfMilReport; 201 result:= (i=MyRO.Turn) or (i=MyRO.Turn-1) and (Enemy>me); 202 end; 203 204 function CutCityFoodSurplus(FoodSurplus: integer; IsCityAlive: boolean; 205 gov,size: integer): integer; 206 begin 207 result:=FoodSurplus; 208 if not IsCityAlive 209 or (result>0) 210 and ((gov=gFuture) 211 or (size>=NeedAqueductSize) and (result<2)) then 212 result:=0; {no growth} 213 end; 214 215 function CityTaxBalance(cix: integer; const CityReport: TCityReportNew): integer; 216 var 217 i: integer; 218 begin 219 result:=0; 220 if (CityReport.HappinessBalance>=0) {no disorder} 221 and (MyCity[cix].Flags and chCaptured=0) then // not captured 222 begin 223 inc(result, CityReport.Tax); 224 if (MyCity[cix].Project and (cpImp+cpIndex)=cpImp+imTrGoods) 225 and (CityReport.Production>0) then 226 inc(result, CityReport.Production); 227 if ((MyRO.Government=gFuture) 228 or (MyCity[cix].Size>=NeedAqueductSize) 229 and (CityReport.FoodSurplus<2)) 230 and (CityReport.FoodSurplus>0) then 231 inc(result, CityReport.FoodSurplus); 232 end; 233 for i:=28 to nImp-1 do if MyCity[cix].Built[i]>0 then 234 dec(result, Imp[i].Maint); 235 end; 236 237 procedure SumCities(var TaxSum, ScienceSum: integer); 238 var 239 cix: integer; 240 CityReport: TCityReportNew; 241 begin 242 TaxSum:=MyRO.OracleIncome; 243 ScienceSum:=0; 244 if MyRO.Government=gAnarchy then exit; 245 for cix:=0 to MyRO.nCity-1 do if MyCity[cix].Loc>=0 then 246 begin 247 CityReport.HypoTiles:=-1; 248 CityReport.HypoTaxRate:=-1; 249 CityReport.HypoLuxuryRate:=-1; 250 Server(sGetCityReportNew,me,cix,CityReport); 251 if (CityReport.HappinessBalance>=0) {no disorder} 252 and (MyCity[cix].Flags and chCaptured=0) then // not captured 253 ScienceSum:=ScienceSum+CityReport.Science; 254 TaxSum:=TaxSum+CityTaxBalance(cix, CityReport); 255 end; 256 end; 257 258 function JobTest(uix,Job: integer; IgnoreResults: JobResultSet): boolean; 259 var 260 Test: integer; 261 begin 262 Test:=Server(sStartJob+Job shl 4-sExecute,me,uix,nil^); 263 result:= (Test>=rExecuted) or (Test in IgnoreResults); 264 end; 265 266 procedure GetUnitInfo(Loc: integer; var uix: integer; var UnitInfo: TUnitInfo); 267 var 268 i,Cnt: integer; 269 begin 270 if MyMap[Loc] and fOwned<>0 then 271 begin 272 Server(sGetDefender,me,Loc,uix); 273 Cnt:=0; 274 for i:=0 to MyRO.nUn-1 do 275 if MyUn[i].Loc=Loc then inc(Cnt); 276 MakeUnitInfo(me,MyUn[uix],UnitInfo); 277 if Cnt>1 then UnitInfo.Flags:=UnitInfo.Flags or unMulti; 278 end 279 else 280 begin 281 uix:=MyRO.nEnemyUn-1; 282 while (uix>=0) and (MyRO.EnemyUn[uix].Loc<>Loc) do dec(uix); 283 UnitInfo:=MyRO.EnemyUn[uix]; 284 end 285 end;{GetUnitInfo} 286 287 procedure GetCityInfo(Loc: integer; var cix: integer; var CityInfo: TCityInfo); 288 begin 289 if MyMap[Loc] and fOwned<>0 then 290 begin 291 CityInfo.Loc:=Loc; 292 cix:=MyRO.nCity-1; 293 while (cix>=0) and (MyCity[cix].Loc<>Loc) do dec(cix); 294 with CityInfo do 295 begin 296 Owner:=me; 297 ID:=MyCity[cix].ID; 298 Size:=MyCity[cix].Size; 299 Flags:=0; 300 if MyCity[cix].Built[imPalace]>0 then inc(Flags,ciCapital); 301 if (MyCity[cix].Built[imWalls]>0) 302 or (MyMap[MyCity[cix].Loc] and fGrWall<>0) then inc(Flags,ciWalled); 303 if MyCity[cix].Built[imCoastalFort]>0 then inc(Flags,ciCoastalFort); 304 if MyCity[cix].Built[imMissileBat]>0 then inc(Flags,ciMissileBat); 305 if MyCity[cix].Built[imBunker]>0 then inc(Flags,ciBunker); 306 if MyCity[cix].Built[imSpacePort]>0 then inc(Flags,ciSpacePort); 140 until false end; 141 142 function ColorOfHealth(Health: integer): integer; 143 var 144 red, green: integer; 145 begin 146 green := 400 * Health div 100; 147 if green > 200 then 148 green := 200; 149 red := 510 * (100 - Health) div 100; 150 if red > 255 then 151 red := 255; 152 result := green shl 8 + red 153 end; 154 155 function IsMultiPlayerGame: boolean; 156 var 157 p1: integer; 158 begin 159 result := false; 160 for p1 := 1 to nPl - 1 do 161 if G.RO[p1] <> nil then 162 result := true; 163 end; 164 165 procedure ItsMeAgain(p: integer); 166 begin 167 if G.RO[p] <> nil then 168 MyRO := pointer(G.RO[p]) 169 else if G.SuperVisorRO[p] <> nil then 170 MyRO := pointer(G.SuperVisorRO[p]) 171 else 172 exit; 173 me := p; 174 MyMap := pointer(MyRO.Map); 175 MyUn := pointer(MyRO.Un); 176 MyCity := pointer(MyRO.City); 177 MyModel := pointer(MyRO.Model); 178 end; 179 180 function GetAge(p: integer): integer; 181 var 182 i: integer; 183 begin 184 if p = me then 185 begin 186 result := 0; 187 for i := 1 to 3 do 188 if MyRO.Tech[AgePreq[i]] >= tsApplicable then 189 result := i; 307 190 end 308 end 309 else 310 begin 311 cix:=MyRO.nEnemyCity-1; 312 while (cix>=0) and (MyRO.EnemyCity[cix].Loc<>Loc) do dec(cix); 313 CityInfo:=MyRO.EnemyCity[cix]; 314 end 315 end; 316 317 function UnitExhausted(uix: integer): boolean; 318 // check if another move of this unit is still possible 319 var 320 dx, dy: integer; 321 begin 322 result:=true; 323 if (MyUn[uix].Movement>0) or (MyRO.Wonder[woShinkansen].EffectiveOwner=me) then 324 if (MyUn[uix].Movement>=100) or ((MyModel[MyUn[uix].mix].Kind=mkCaravan) 325 and (MyMap[MyUn[uix].Loc] and fCity<>0)) then 326 result:=false 327 else for dx:=-2 to 2 do for dy:=-2 to 2 do if abs(dx)+abs(dy)=2 then 328 if Server(sMoveUnit-sExecute+dx and 7 shl 4+dy and 7 shl 7,me,uix,nil^)>=rExecuted then 329 result:=false; 330 end; 331 332 function ModelHash(const ModelInfo: TModelInfo): integer; 333 var 334 i,FeatureCode,Hash1,Hash2,Hash2r,d: cardinal; 335 begin 336 with ModelInfo do 337 if Kind>mkEnemyDeveloped then 338 result:=integer($C0000000+Speed div 50+Kind shl 8) 339 else 340 begin 341 FeatureCode:=0; 342 for i:=mcFirstNonCap to nFeature-1 do 343 if 1 shl Domain and Feature[i].Domains<>0 then 344 begin 345 FeatureCode:=FeatureCode*2; 346 if 1 shl (i-mcFirstNonCap)<>0 then 347 inc(FeatureCode); 348 end; 349 case Domain of 350 dGround: 351 begin 352 assert(FeatureCode<1 shl 8); 353 assert(Attack<5113); 354 assert(Defense<2273); 355 assert(Cost<1611); 356 Hash1:=(Attack*2273+Defense)*9+(Speed-150) div 50; 357 Hash2:=FeatureCode*1611+Cost; 358 end; 359 dSea: 360 begin 361 assert(FeatureCode<1 shl 9); 362 assert(Attack<12193); 363 assert(Defense<6097); 364 assert(Cost<4381); 365 Hash1:=((Attack*6097+Defense)*5+(Speed-350) div 100)*2; 366 if Weight>=6 then inc(Hash1); 367 Hash2:=((TTrans*17+ATrans_Fuel) shl 9+FeatureCode)*4381+Cost; 368 end; 369 dAir: 370 begin 371 assert(FeatureCode<1 shl 5); 372 assert(Attack<2407); 373 assert(Defense<1605); 374 assert(Bombs<4813); 375 assert(Cost<2089); 376 Hash1:=(Attack*1605+Defense) shl 5+FeatureCode; 377 Hash2:=((Bombs*7+ATrans_Fuel)*4+TTrans)*2089+Cost; 378 end; 379 end; 380 Hash2r:=0; 381 for i:=0 to 7 do 382 begin 383 Hash2r:=Hash2r*13; 384 d:=Hash2 div 13; 385 inc(Hash2r,Hash2-d*13); 386 Hash2:=d 387 end; 388 result:=integer(Domain shl 30+Hash1 xor Hash2r) 191 else 192 begin 193 result := 0; 194 for i := 1 to 3 do 195 if MyRO.EnemyReport[p].Tech[AgePreq[i]] >= tsApplicable then 196 result := i; 389 197 end 390 end; 391 392 function ProcessEnhancement(uix: integer; const Jobs: TEnhancementJobs): integer; 393 { return values: 394 eJobDone - all applicable jobs done 395 eOK - enhancement not complete 396 eDied - job done and died (thurst) } 397 var 398 stage, NextJob, Tile: integer; 399 Done: Set of jNone..jTrans; 400 begin 401 Done:=[]; 402 Tile:=MyMap[MyUn[uix].Loc]; 403 if Tile and fRoad<>0 then include(Done,jRoad); 404 if Tile and fRR<>0 then include(Done,jRR); 405 if (Tile and fTerImp=tiIrrigation) or (Tile and fTerImp=tiFarm) then 406 include(Done,jIrr); 407 if Tile and fTerImp=tiFarm then include(Done,jFarm); 408 if Tile and fTerImp=tiMine then include(Done,jMine); 409 if Tile and fPoll=0 then include(Done,jPoll); 410 411 if MyUn[uix].Job=jNone then result:=eJobDone 412 else result:=eOK; 413 while (result<>eOK) and (result<>eDied) do 414 begin 415 stage:=-1; 416 repeat 417 if stage=-1 then NextJob:=jPoll 418 else NextJob:=Jobs[Tile and fTerrain,stage]; 419 if (NextJob=jNone) or not (NextJob in Done) then Break; 420 inc(stage); 421 until stage=5; 422 if (stage=5) or (NextJob=jNone) then 423 begin result:=eJobDone; Break; end; // tile enhancement complete 424 result:=Server(sStartJob+NextJob shl 4,me,uix,nil^); 425 include(Done,NextJob) 426 end; 427 end; 428 429 function AutoBuild(cix: integer; const ImpOrder: TImpOrder): boolean; 430 var 431 i,NewProject: integer; 432 begin 433 result:=false; 434 if (MyCity[cix].Project and (cpImp+cpIndex)=cpImp+imTrGoods) 435 or (MyCity[cix].Flags and chProduction<>0) then 436 begin 437 i:=0; 438 repeat 439 while (ImpOrder[i]>=0) and (MyCity[cix].Built[ImpOrder[i]]>0) do inc(i); 440 if ImpOrder[i]<0 then Break; 441 assert(i<nImp); 442 NewProject:=cpImp+ImpOrder[i]; 443 if Server(sSetCityProject,me,cix,NewProject)>=rExecuted then 444 begin 445 result:=true; 446 CityOptimizer_CityChange(cix); 447 Break; 448 end; 449 inc(i); 450 until false 451 end 452 end; 453 454 procedure CalculateAdvValues; 455 var 456 i,j: integer; 457 known: array[0..nAdv-1] of integer; 458 459 procedure MarkPreqs(i: integer); 460 begin 461 if known[i]=0 then 462 begin 463 known[i]:=1; 464 if (i<>adScience) and (i<>adMassProduction) then 465 begin 466 if (AdvPreq[i,0]>=0) then MarkPreqs(AdvPreq[i,0]); 467 if (AdvPreq[i,1]>=0) then MarkPreqs(AdvPreq[i,1]); 198 end; 199 200 function IsCivilReportNew(Enemy: integer): boolean; 201 var 202 i: integer; 203 begin 204 assert(Enemy <> me); 205 i := MyRO.EnemyReport[Enemy].TurnOfCivilReport; 206 result := (i = MyRO.Turn) or (i = MyRO.Turn - 1) and (Enemy > me); 207 end; 208 209 function IsMilReportNew(Enemy: integer): boolean; 210 var 211 i: integer; 212 begin 213 assert(Enemy <> me); 214 i := MyRO.EnemyReport[Enemy].TurnOfMilReport; 215 result := (i = MyRO.Turn) or (i = MyRO.Turn - 1) and (Enemy > me); 216 end; 217 218 function CutCityFoodSurplus(FoodSurplus: integer; IsCityAlive: boolean; 219 gov, size: integer): integer; 220 begin 221 result := FoodSurplus; 222 if not IsCityAlive or (result > 0) and 223 ((gov = gFuture) or (size >= NeedAqueductSize) and (result < 2)) then 224 result := 0; { no growth } 225 end; 226 227 function CityTaxBalance(cix: integer; 228 const CityReport: TCityReportNew): integer; 229 var 230 i: integer; 231 begin 232 result := 0; 233 if (CityReport.HappinessBalance >= 0) { no disorder } 234 and (MyCity[cix].Flags and chCaptured = 0) then // not captured 235 begin 236 inc(result, CityReport.Tax); 237 if (MyCity[cix].Project and (cpImp + cpIndex) = cpImp + imTrGoods) and 238 (CityReport.Production > 0) then 239 inc(result, CityReport.Production); 240 if ((MyRO.Government = gFuture) or (MyCity[cix].size >= NeedAqueductSize) 241 and (CityReport.FoodSurplus < 2)) and (CityReport.FoodSurplus > 0) then 242 inc(result, CityReport.FoodSurplus); 243 end; 244 for i := 28 to nImp - 1 do 245 if MyCity[cix].Built[i] > 0 then 246 dec(result, Imp[i].Maint); 247 end; 248 249 procedure SumCities(var TaxSum, ScienceSum: integer); 250 var 251 cix: integer; 252 CityReport: TCityReportNew; 253 begin 254 TaxSum := MyRO.OracleIncome; 255 ScienceSum := 0; 256 if MyRO.Government = gAnarchy then 257 exit; 258 for cix := 0 to MyRO.nCity - 1 do 259 if MyCity[cix].Loc >= 0 then 260 begin 261 CityReport.HypoTiles := -1; 262 CityReport.HypoTaxRate := -1; 263 CityReport.HypoLuxuryRate := -1; 264 Server(sGetCityReportNew, me, cix, CityReport); 265 if (CityReport.HappinessBalance >= 0) { no disorder } 266 and (MyCity[cix].Flags and chCaptured = 0) then // not captured 267 ScienceSum := ScienceSum + CityReport.Science; 268 TaxSum := TaxSum + CityTaxBalance(cix, CityReport); 269 end; 270 end; 271 272 function JobTest(uix, Job: integer; IgnoreResults: JobResultSet): boolean; 273 var 274 Test: integer; 275 begin 276 Test := Server(sStartJob + Job shl 4 - sExecute, me, uix, nil^); 277 result := (Test >= rExecuted) or (Test in IgnoreResults); 278 end; 279 280 procedure GetUnitInfo(Loc: integer; var uix: integer; 281 var UnitInfo: TUnitInfo); 282 var 283 i, Cnt: integer; 284 begin 285 if MyMap[Loc] and fOwned <> 0 then 286 begin 287 Server(sGetDefender, me, Loc, uix); 288 Cnt := 0; 289 for i := 0 to MyRO.nUn - 1 do 290 if MyUn[i].Loc = Loc then 291 inc(Cnt); 292 MakeUnitInfo(me, MyUn[uix], UnitInfo); 293 if Cnt > 1 then 294 UnitInfo.Flags := UnitInfo.Flags or unMulti; 295 end 296 else 297 begin 298 uix := MyRO.nEnemyUn - 1; 299 while (uix >= 0) and (MyRO.EnemyUn[uix].Loc <> Loc) do 300 dec(uix); 301 UnitInfo := MyRO.EnemyUn[uix]; 302 end 303 end; { GetUnitInfo } 304 305 procedure GetCityInfo(Loc: integer; var cix: integer; 306 var CityInfo: TCityInfo); 307 begin 308 if MyMap[Loc] and fOwned <> 0 then 309 begin 310 CityInfo.Loc := Loc; 311 cix := MyRO.nCity - 1; 312 while (cix >= 0) and (MyCity[cix].Loc <> Loc) do 313 dec(cix); 314 with CityInfo do 315 begin 316 Owner := me; 317 ID := MyCity[cix].ID; 318 size := MyCity[cix].size; 319 Flags := 0; 320 if MyCity[cix].Built[imPalace] > 0 then 321 inc(Flags, ciCapital); 322 if (MyCity[cix].Built[imWalls] > 0) or 323 (MyMap[MyCity[cix].Loc] and fGrWall <> 0) then 324 inc(Flags, ciWalled); 325 if MyCity[cix].Built[imCoastalFort] > 0 then 326 inc(Flags, ciCoastalFort); 327 if MyCity[cix].Built[imMissileBat] > 0 then 328 inc(Flags, ciMissileBat); 329 if MyCity[cix].Built[imBunker] > 0 then 330 inc(Flags, ciBunker); 331 if MyCity[cix].Built[imSpacePort] > 0 then 332 inc(Flags, ciSpacePort); 468 333 end 469 334 end 470 end; 471 472 begin 473 FillChar(AdvValue,SizeOf(AdvValue),0); 474 for i:=0 to nAdv-1 do 475 begin 476 FillChar(known,SizeOf(known),0); 477 MarkPreqs(i); 478 for j:=0 to nAdv-1 do if known[j]>0 then inc(AdvValue[i]); 479 if i in FutureTech then inc(AdvValue[i],3000) 480 else if known[adMassProduction]>0 then inc(AdvValue[i],2000) 481 else if known[adScience]>0 then inc(AdvValue[i],1000) 482 end; 483 end; 484 485 procedure DebugMessage(Level: integer; Text: string); 486 begin 487 Server(sMessage,me,Level,pchar(Text)^) 488 end; 489 490 function MarkCitiesAround(Loc,cixExcept: integer): boolean; 491 // return whether a city was marked 492 var 493 cix: integer; 494 begin 495 result:=false; 496 for cix:=0 to MyRO.nCity-1 do 497 if (cix<>cixExcept) and (MyCity[cix].Loc>=0) 498 and (MyCity[cix].Flags and chCaptured=0) 499 and (Distance(MyCity[cix].Loc,Loc)<=5) then 500 begin 501 CityNeedsOptimize[cix]:=true; 502 result:=true; 335 else 336 begin 337 cix := MyRO.nEnemyCity - 1; 338 while (cix >= 0) and (MyRO.EnemyCity[cix].Loc <> Loc) do 339 dec(cix); 340 CityInfo := MyRO.EnemyCity[cix]; 503 341 end 504 end; 505 506 procedure OptimizeCities(CheckOnly: boolean); 507 var 508 cix,fix,dx,dy,Loc1,OptiType: integer; 509 done: boolean; 510 Advice: TCityTileAdviceData; 511 begin 512 repeat 513 done:=true; 514 for cix:=0 to MyRO.nCity-1 do if CityNeedsOptimize[cix] then 515 begin 516 OptiType:=MyCity[cix].Status shr 4 and $0F; 517 if OptiType<>0 then 518 begin 519 Advice.ResourceWeights:=OfferedResourceWeights[OptiType]; 520 Server(sGetCityTileAdvice,me,cix,Advice); 521 if Advice.Tiles<>MyCity[cix].Tiles then 522 if CheckOnly then 523 assert(false) 342 end; 343 344 function UnitExhausted(uix: integer): boolean; 345 // check if another move of this unit is still possible 346 var 347 dx, dy: integer; 348 begin 349 result := true; 350 if (MyUn[uix].Movement > 0) or 351 (MyRO.Wonder[woShinkansen].EffectiveOwner = me) then 352 if (MyUn[uix].Movement >= 100) or 353 ((MyModel[MyUn[uix].mix].Kind = mkCaravan) and 354 (MyMap[MyUn[uix].Loc] and fCity <> 0)) then 355 result := false 356 else 357 for dx := -2 to 2 do 358 for dy := -2 to 2 do 359 if abs(dx) + abs(dy) = 2 then 360 if Server(sMoveUnit - sExecute + dx and 7 shl 4 + dy and 7 shl 7, 361 me, uix, nil^) >= rExecuted then 362 result := false; 363 end; 364 365 function ModelHash(const ModelInfo: TModelInfo): integer; 366 var 367 i, FeatureCode, Hash1, Hash2, Hash2r, d: cardinal; 368 begin 369 with ModelInfo do 370 if Kind > mkEnemyDeveloped then 371 result := integer($C0000000 + Speed div 50 + Kind shl 8) 372 else 373 begin 374 FeatureCode := 0; 375 for i := mcFirstNonCap to nFeature - 1 do 376 if 1 shl Domain and Feature[i].Domains <> 0 then 377 begin 378 FeatureCode := FeatureCode * 2; 379 if 1 shl (i - mcFirstNonCap) <> 0 then 380 inc(FeatureCode); 381 end; 382 case Domain of 383 dGround: 384 begin 385 assert(FeatureCode < 1 shl 8); 386 assert(Attack < 5113); 387 assert(Defense < 2273); 388 assert(Cost < 1611); 389 Hash1 := (Attack * 2273 + Defense) * 9 + (Speed - 150) div 50; 390 Hash2 := FeatureCode * 1611 + Cost; 391 end; 392 dSea: 393 begin 394 assert(FeatureCode < 1 shl 9); 395 assert(Attack < 12193); 396 assert(Defense < 6097); 397 assert(Cost < 4381); 398 Hash1 := ((Attack * 6097 + Defense) * 5 + (Speed - 350) 399 div 100) * 2; 400 if Weight >= 6 then 401 inc(Hash1); 402 Hash2 := ((TTrans * 17 + ATrans_Fuel) shl 9 + FeatureCode) * 403 4381 + Cost; 404 end; 405 dAir: 406 begin 407 assert(FeatureCode < 1 shl 5); 408 assert(Attack < 2407); 409 assert(Defense < 1605); 410 assert(Bombs < 4813); 411 assert(Cost < 2089); 412 Hash1 := (Attack * 1605 + Defense) shl 5 + FeatureCode; 413 Hash2 := ((Bombs * 7 + ATrans_Fuel) * 4 + TTrans) * 2089 + Cost; 414 end; 415 end; 416 Hash2r := 0; 417 for i := 0 to 7 do 418 begin 419 Hash2r := Hash2r * 13; 420 d := Hash2 div 13; 421 inc(Hash2r, Hash2 - d * 13); 422 Hash2 := d 423 end; 424 result := integer(Domain shl 30 + Hash1 xor Hash2r) 425 end 426 end; 427 428 function ProcessEnhancement(uix: integer; 429 const Jobs: TEnhancementJobs): integer; 430 { return values: 431 eJobDone - all applicable jobs done 432 eOK - enhancement not complete 433 eDied - job done and died (thurst) } 434 var 435 stage, NextJob, Tile: integer; 436 Done: Set of jNone .. jTrans; 437 begin 438 Done := []; 439 Tile := MyMap[MyUn[uix].Loc]; 440 if Tile and fRoad <> 0 then 441 include(Done, jRoad); 442 if Tile and fRR <> 0 then 443 include(Done, jRR); 444 if (Tile and fTerImp = tiIrrigation) or (Tile and fTerImp = tiFarm) then 445 include(Done, jIrr); 446 if Tile and fTerImp = tiFarm then 447 include(Done, jFarm); 448 if Tile and fTerImp = tiMine then 449 include(Done, jMine); 450 if Tile and fPoll = 0 then 451 include(Done, jPoll); 452 453 if MyUn[uix].Job = jNone then 454 result := eJobDone 455 else 456 result := eOK; 457 while (result <> eOK) and (result <> eDied) do 458 begin 459 stage := -1; 460 repeat 461 if stage = -1 then 462 NextJob := jPoll 524 463 else 464 NextJob := Jobs[Tile and fTerrain, stage]; 465 if (NextJob = jNone) or not(NextJob in Done) then 466 Break; 467 inc(stage); 468 until stage = 5; 469 if (stage = 5) or (NextJob = jNone) then 470 begin 471 result := eJobDone; 472 Break; 473 end; // tile enhancement complete 474 result := Server(sStartJob + NextJob shl 4, me, uix, nil^); 475 include(Done, NextJob) 476 end; 477 end; 478 479 function AutoBuild(cix: integer; const ImpOrder: TImpOrder): boolean; 480 var 481 i, NewProject: integer; 482 begin 483 result := false; 484 if (MyCity[cix].Project and (cpImp + cpIndex) = cpImp + imTrGoods) or 485 (MyCity[cix].Flags and chProduction <> 0) then 486 begin 487 i := 0; 488 repeat 489 while (ImpOrder[i] >= 0) and (MyCity[cix].Built[ImpOrder[i]] > 0) do 490 inc(i); 491 if ImpOrder[i] < 0 then 492 Break; 493 assert(i < nImp); 494 NewProject := cpImp + ImpOrder[i]; 495 if Server(sSetCityProject, me, cix, NewProject) >= rExecuted then 496 begin 497 result := true; 498 CityOptimizer_CityChange(cix); 499 Break; 500 end; 501 inc(i); 502 until false end end; 503 504 procedure CalculateAdvValues; 505 var 506 i, j: integer; 507 known: array [0 .. nAdv - 1] of integer; 508 509 procedure MarkPreqs(i: integer); 510 begin 511 if known[i] = 0 then 525 512 begin 526 for fix:=1 to 26 do 527 if MyCity[cix].Tiles and not Advice.Tiles and (1 shl fix)<>0 then 528 begin // tile no longer used by this city -- check using it by another 529 dy:=fix shr 2-3; dx:=fix and 3 shl 1 -3 + (dy+3) and 1; 530 Loc1:=dLoc(MyCity[cix].Loc,dx,dy); 531 if MarkCitiesAround(Loc1,cix) then 532 done:=false; 513 known[i] := 1; 514 if (i <> adScience) and (i <> adMassProduction) then 515 begin 516 if (AdvPreq[i, 0] >= 0) then 517 MarkPreqs(AdvPreq[i, 0]); 518 if (AdvPreq[i, 1] >= 0) then 519 MarkPreqs(AdvPreq[i, 1]); 520 end 521 end 522 end; 523 524 begin 525 FillChar(AdvValue, SizeOf(AdvValue), 0); 526 for i := 0 to nAdv - 1 do 527 begin 528 FillChar(known, SizeOf(known), 0); 529 MarkPreqs(i); 530 for j := 0 to nAdv - 1 do 531 if known[j] > 0 then 532 inc(AdvValue[i]); 533 if i in FutureTech then 534 inc(AdvValue[i], 3000) 535 else if known[adMassProduction] > 0 then 536 inc(AdvValue[i], 2000) 537 else if known[adScience] > 0 then 538 inc(AdvValue[i], 1000) 539 end; 540 end; 541 542 procedure DebugMessage(Level: integer; Text: string); 543 begin 544 Server(sMessage, me, Level, pchar(Text)^) 545 end; 546 547 function MarkCitiesAround(Loc, cixExcept: integer): boolean; 548 // return whether a city was marked 549 var 550 cix: integer; 551 begin 552 result := false; 553 for cix := 0 to MyRO.nCity - 1 do 554 if (cix <> cixExcept) and (MyCity[cix].Loc >= 0) and 555 (MyCity[cix].Flags and chCaptured = 0) and 556 (Distance(MyCity[cix].Loc, Loc) <= 5) then 557 begin 558 CityNeedsOptimize[cix] := true; 559 result := true; 560 end 561 end; 562 563 procedure OptimizeCities(CheckOnly: boolean); 564 var 565 cix, fix, dx, dy, Loc1, OptiType: integer; 566 Done: boolean; 567 Advice: TCityTileAdviceData; 568 begin 569 repeat 570 Done := true; 571 for cix := 0 to MyRO.nCity - 1 do 572 if CityNeedsOptimize[cix] then 573 begin 574 OptiType := MyCity[cix].Status shr 4 and $0F; 575 if OptiType <> 0 then 576 begin 577 Advice.ResourceWeights := OfferedResourceWeights[OptiType]; 578 Server(sGetCityTileAdvice, me, cix, Advice); 579 if Advice.Tiles <> MyCity[cix].Tiles then 580 if CheckOnly then 581 assert(false) 582 else 583 begin 584 for fix := 1 to 26 do 585 if MyCity[cix].Tiles and not Advice.Tiles and 586 (1 shl fix) <> 0 then 587 begin // tile no longer used by this city -- check using it by another 588 dy := fix shr 2 - 3; 589 dx := fix and 3 shl 1 - 3 + (dy + 3) and 1; 590 Loc1 := dLoc(MyCity[cix].Loc, dx, dy); 591 if MarkCitiesAround(Loc1, cix) then 592 Done := false; 593 end; 594 Server(sSetCityTiles, me, cix, Advice.Tiles); 595 end; 533 596 end; 534 Server(sSetCityTiles,me,cix,Advice.Tiles); 535 end; 536 end; 537 CityNeedsOptimize[cix]:=false; 538 end; 539 until done; 540 end; 541 542 procedure CityOptimizer_BeginOfTurn; 543 var 544 cix: integer; 545 begin 546 fillchar(CityNeedsOptimize,MyRO.nCity-1,0); //false 547 if MyRO.Government<>gAnarchy then 548 begin 549 for cix:=0 to MyRO.nCity-1 do 550 if (MyCity[cix].Loc>=0) and (MyCity[cix].Flags and chCaptured=0) then 551 CityNeedsOptimize[cix]:=true; 552 OptimizeCities(false); // optimize all cities 553 end 554 end; 555 556 procedure CityOptimizer_CityChange(cix: integer); 557 begin 558 if (MyRO.Government<>gAnarchy) and (MyCity[cix].Flags and chCaptured=0) then 559 begin 560 CityNeedsOptimize[cix]:=true; 561 OptimizeCities(false); 562 end 563 end; 564 565 procedure CityOptimizer_TileBecomesAvailable(Loc: integer); 566 begin 567 if (MyRO.Government<>gAnarchy) and MarkCitiesAround(Loc,-1) then 568 OptimizeCities(false); 569 end; 570 571 procedure CityOptimizer_ReleaseCityTiles(cix, ReleasedTiles: integer); 572 var 573 fix,dx,dy,Loc1: integer; 574 done: boolean; 575 begin 576 if (MyRO.Government<>gAnarchy) and (ReleasedTiles<>0) then 577 begin 578 done:=true; 579 for fix:=1 to 26 do if ReleasedTiles and (1 shl fix)<>0 then 580 begin 581 dy:=fix shr 2-3; dx:=fix and 3 shl 1 -3 + (dy+3) and 1; 582 Loc1:=dLoc(MyCity[cix].Loc,dx,dy); 583 if MarkCitiesAround(Loc1,cix) then 584 done:=false; 585 end; 586 if not done then 587 OptimizeCities(false); 588 end 589 end; 590 591 procedure CityOptimizer_BeforeRemoveUnit(uix: integer); 592 var 593 uix1: integer; 594 begin 595 if MyRO.Government<>gAnarchy then 596 begin 597 if MyUn[uix].Home>=0 then 598 CityNeedsOptimize[MyUn[uix].Home]:=true; 599 600 // transported units are also removed 601 for uix1:=0 to MyRO.nUn-1 do 602 if (MyUn[uix1].Loc>=0) and (MyUn[uix1].Master=uix) 603 and (MyUn[uix1].Home>=0) then 604 CityNeedsOptimize[MyUn[uix1].Home]:=true; 605 end 606 end; 607 608 procedure CityOptimizer_AfterRemoveUnit; 609 begin 610 if MyRO.Government<>gAnarchy then 611 OptimizeCities(false); 612 end; 613 614 procedure CityOptimizer_EndOfTurn; 615 // all cities should already be optimized here -- only check this 616 var 617 cix: integer; 618 begin 597 CityNeedsOptimize[cix] := false; 598 end; 599 until Done; 600 end; 601 602 procedure CityOptimizer_BeginOfTurn; 603 var 604 cix: integer; 605 begin 606 FillChar(CityNeedsOptimize, MyRO.nCity - 1, 0); // false 607 if MyRO.Government <> gAnarchy then 608 begin 609 for cix := 0 to MyRO.nCity - 1 do 610 if (MyCity[cix].Loc >= 0) and (MyCity[cix].Flags and chCaptured = 0) 611 then 612 CityNeedsOptimize[cix] := true; 613 OptimizeCities(false); // optimize all cities 614 end 615 end; 616 617 procedure CityOptimizer_CityChange(cix: integer); 618 begin 619 if (MyRO.Government <> gAnarchy) and 620 (MyCity[cix].Flags and chCaptured = 0) then 621 begin 622 CityNeedsOptimize[cix] := true; 623 OptimizeCities(false); 624 end 625 end; 626 627 procedure CityOptimizer_TileBecomesAvailable(Loc: integer); 628 begin 629 if (MyRO.Government <> gAnarchy) and MarkCitiesAround(Loc, -1) then 630 OptimizeCities(false); 631 end; 632 633 procedure CityOptimizer_ReleaseCityTiles(cix, ReleasedTiles: integer); 634 var 635 fix, dx, dy, Loc1: integer; 636 Done: boolean; 637 begin 638 if (MyRO.Government <> gAnarchy) and (ReleasedTiles <> 0) then 639 begin 640 Done := true; 641 for fix := 1 to 26 do 642 if ReleasedTiles and (1 shl fix) <> 0 then 643 begin 644 dy := fix shr 2 - 3; 645 dx := fix and 3 shl 1 - 3 + (dy + 3) and 1; 646 Loc1 := dLoc(MyCity[cix].Loc, dx, dy); 647 if MarkCitiesAround(Loc1, cix) then 648 Done := false; 649 end; 650 if not Done then 651 OptimizeCities(false); 652 end 653 end; 654 655 procedure CityOptimizer_BeforeRemoveUnit(uix: integer); 656 var 657 uix1: integer; 658 begin 659 if MyRO.Government <> gAnarchy then 660 begin 661 if MyUn[uix].Home >= 0 then 662 CityNeedsOptimize[MyUn[uix].Home] := true; 663 664 // transported units are also removed 665 for uix1 := 0 to MyRO.nUn - 1 do 666 if (MyUn[uix1].Loc >= 0) and (MyUn[uix1].Master = uix) and 667 (MyUn[uix1].Home >= 0) then 668 CityNeedsOptimize[MyUn[uix1].Home] := true; 669 end 670 end; 671 672 procedure CityOptimizer_AfterRemoveUnit; 673 begin 674 if MyRO.Government <> gAnarchy then 675 OptimizeCities(false); 676 end; 677 678 procedure CityOptimizer_EndOfTurn; 679 // all cities should already be optimized here -- only check this 680 var 681 cix: integer; 682 begin 619 683 {$IFOPT O-} 620 if MyRO.Government<>gAnarchy then 621 begin 622 fillchar(CityNeedsOptimize,MyRO.nCity-1,0); //false 623 for cix:=0 to MyRO.nCity-1 do 624 if (MyCity[cix].Loc>=0) and (MyCity[cix].Flags and chCaptured=0) then 625 CityNeedsOptimize[cix]:=true; 626 OptimizeCities(true); // check all cities 627 end; 684 if MyRO.Government <> gAnarchy then 685 begin 686 FillChar(CityNeedsOptimize, MyRO.nCity - 1, 0); // false 687 for cix := 0 to MyRO.nCity - 1 do 688 if (MyCity[cix].Loc >= 0) and (MyCity[cix].Flags and chCaptured = 0) 689 then 690 CityNeedsOptimize[cix] := true; 691 OptimizeCities(true); // check all cities 692 end; 628 693 {$ENDIF} 629 end; 630 694 end; 631 695 632 696 initialization 633 assert(nImp<128); 697 698 assert(nImp < 128); 634 699 CalculateAdvValues; 635 700 636 701 end. 637
Note:
See TracChangeset
for help on using the changeset viewer.