Changeset 160 for trunk/AI/StdAI/AI.pas


Ignore:
Timestamp:
Mar 6, 2019, 8:10:23 AM (6 years ago)
Author:
chronos
Message:
  • Added: StdAI from original game. Previously used only AI dev kit.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/AI/StdAI/AI.pas

    r124 r160  
    11{$INCLUDE Switches.inc}
     2{//$DEFINE PERF}
    23unit AI;
    34
     
    56
    67uses
    7 {$IFDEF DEBUG}SysUtils, {$ENDIF} // necessary for debug exceptions
    8   Protocol, CustomAI, ToolAI;
     8{$IFDEF DEBUG}SysUtils,Names,{$ENDIF} // necessary for debug exceptions
     9{$IFDEF PERF}SysUtils,Windows,{$ENDIF} // necessary for performance measurement
     10Protocol, CustomAI, ToolAI, Barbarina;
     11
     12
     13const
     14WaitAfterReject=20; // don't try to contact this number of turn after contact was rejected
     15MinCityFood=3;
     16LeaveDespotism=80; // stay in despotism until this turn
     17TechReportOutdated=30;
     18MilProdShare=50; // minimum share of total production to specialize in military production
     19
     20FutureTech=[futResearchTechnology,futProductionTechnology,futArmorTechnology,
     21  futMissileTechnology];
     22
     23nResearchOrder=46;
     24ResearchOrder: array[0..1,0..nResearchOrder-1] of integer=
     25((adWheel,adWarriorCode,adHorsebackRiding,adCeremonialBurial,adPolytheism,
     26adMonarchy,adMysticism,adPoetry,adAstronomy,adMonotheism,
     27adTheology,adChivalry,adPottery,adMedicine,adGunpowder,adChemistry,
     28adExplosives,adUniversity,adTactics,adSeafaring,adNavigation,adRefining,adCombustionEngine,
     29adAutomobile,adPhysics,adMagnetism,adElectricity,adRefrigeration,
     30adRadioCommunication,adTheoryOfGravity,adAtomicTheory,adElectronics,
     31adMassProduction,adPlastics,adFlight,adEnvironmentalism,
     32adSanitation,adMin,adComputers,adRecycling,adSyntheticFood,
     33adSelfContainedEnvironment,adNuclearFission,adNuclearPower,adTheLaser,
     34adIntelligenArms),
     35(adWheel,adWarriorCode,adHorsebackRiding,adAlphabet,adMapMaking,adBronzeWorking,adWriting,
     36adCodeOfLaws,adCurrency,adTrade,adLiterature,adTheRepublic,adMathematics,
     37adPhilosophy,adScience,adMasonry,adConstruction,adEngineering,adInvention,
     38adIronWorking,adBridgeBuilding,adSteamEngine,adRailroad,adSteel,
     39adBanking,adIndustrialization,adConscription,adDemocracy,adEconomics,
     40adTheCorporation,adMassProduction,adRobotics,adCommunism,adMetallurgy,
     41adBallistics,adMobileWarfare,adAmphibiousWarfare,adMin,adComputers,adRocketry,adAdvancedRocketry,
     42adAdvancedFlight,adSpaceFlight,adComposites,adIntelligence,adCombinedArms));
     43
     44LeaveOutTechs=[adPolytheism,adMysticism,adInvention,adEconomics,adPottery,
     45adMedicine,adEnvironmentalism,adRefining,adTrade,adLiterature,adMathematics,
     46adPhilosophy,adChemistry,adConscription,adCombustionEngine,adPhysics,
     47adTheoryOfGravity,adAtomicTheory,adSyntheticFood,adNuclearFission];
     48
     49TechValue_ForResearch_LeaveOut=$700;
     50TechValue_ForResearch_Urgent=$600;
     51TechValue_ForResearch_Next=$400;
     52TechValue_ForResearch=$FF;
     53ForceNeeded_NoLeaveOut=20; // advancedness behind to state-of-art
     54ForceNeeded_LeaveOut=30; // advancedness behind of state-of-art
     55Compromise=6;
     56
     57// basic strategies
     58bGender=$0001;
     59bMale=$0000;
     60bFemale=$0001;
     61bBarbarina=$0006;
     62bBarbarina_Hide=$0002;
     63
     64// model categories
     65nModelCat=4;
     66mctNone=-1; mctGroundDefender=0; mctGroundAttacker=1; mctTransport=2; mctCruiser=3;
     67
     68// mil research
     69BetterQuality: array[0..nModelCat-1] of integer=(50,50,80,80);
     70MaxBuildWorseThanBestModel=20; MaxExistWorseThanBestModel=50;
     71
     72maxCOD=256;
     73PresenceUnknown=$10000;
     74
     75nRequestedTechs=48;
     76
     77PlayerHash: array[0..nPl-1] of integer=(7,6,0,2,10,8,12,14,4,1,3,5,9,11,13);
    978
    1079type
    11   UnitRole = (Roam, Defend);
    12 
    13   TAI = class(TToolAI)
    14     constructor Create(Nation: integer); override;
    15 
    16   protected
    17     procedure DoTurn; override;
    18     procedure DoNegotiation; override;
    19     function ChooseResearchAdvance: integer; override;
    20     function ChooseGovernment: integer; override;
    21     function WantNegotiation(Nation: integer; NegoTime: TNegoTime)
    22       : boolean; override;
    23 
    24     procedure ProcessSettlers;
    25     procedure ProcessUnit(uix: integer; Role: UnitRole);
    26     procedure SetCityProduction;
    27   end;
     80Suggestion=(suContact, suPeace, suFriendly);
     81
     82TPersistentData=record
     83  LastResearchTech, BehaviorFlags, TheologyPartner: integer;
     84  RejectTurn: array[Suggestion,0..15] of smallint;
     85  RequestedTechs: array[0..nRequestedTechs-1] of integer;
     86    // ad + p shl 8 + Turn shl 16
     87  end;
     88
     89TAI = class(TBarbarina)
     90  constructor Create(Nation: integer); override;
     91
     92  procedure SetDataDefaults; override;
     93
     94protected
     95  Data: ^TPersistentData;
     96  WarNations, BombardingNations, mixSettlers, mixCaravan, mixTownGuard,
     97    mixSlaves, mixMilitia, mixCruiser, OceanWithShip: integer;
     98  NegoCause: (Routine,CheckBarbarina);
     99  SettlerSurplus: array[0..maxCOD-1] of integer;
     100  uixPatrol: array[0..maxCOD-1] of integer;
     101
     102  ContinentPresence: array[0..maxCOD-1] of integer;
     103  OceanPresence: array[0..maxCOD-1] of integer;
     104  UnitLack: array[0..maxCOD-1,mctGroundDefender..mctGroundAttacker] of integer;
     105
     106  TotalPopulation: array[0..nPl-1] of integer;
     107  ContinentPopulation: array[0..nPl-1,0..maxCOD-1] of integer;
     108    // 1 means enemy territory spotted but no city
     109  DistrictPopulation: array[0..maxCOD-1] of integer;
     110
     111  ModelCat: array[0..nMmax-1] of integer;
     112  ModelQuality: array[0..nMmax-1] of integer;
     113  ModelBestQuality: array[0..nModelCat-1] of integer;
     114
     115  AdvanceValue: array[0..nAdv-1] of integer;
     116  AdvanceValuesSet: boolean;
     117
     118  procedure DoTurn; override;
     119  procedure DoNegotiation; override;
     120  function ChooseResearchAdvance: integer; override;
     121  function ChooseStealAdvance: integer; override;
     122  function ChooseGovernment: integer; override;
     123  function WantNegotiation(Nation: integer; NegoTime: TNegoTime): boolean; override;
     124  function OnNegoRejected_CancelTreaty: boolean; override;
     125
     126  procedure FindBestTrade(Nation: integer; var adWanted, adGiveAway: integer);
     127  procedure CheckGender;
     128  procedure AnalyzeMap;
     129  procedure CollectModelCatStat;
     130  procedure AttackAndPatrol;
     131  procedure MoveUnitsHome;
     132  procedure CheckAttack(uix: integer);
     133  procedure Patrol(uix: integer);
     134  procedure SetCityProduction;
     135  procedure SetAdvanceValues;
     136  function HavePort: boolean;
     137  {$IFDEF DEBUG}procedure TraceAdvanceValues(Nation: integer);{$ENDIF}
     138
     139  // research
     140  procedure RateModel(const mi: TModelInfo; var Category, Quality: integer);
     141  procedure RateMyModel(mix: integer; var Category, Quality: integer);
     142  function IsBetterModel(const mi: TModelInfo): boolean;
     143
     144  //terraforming
     145  procedure TileWorkPlan(Loc, cix: integer;
     146    var Value, NextJob, TotalWork: integer);
     147  procedure ProcessSettlers;
     148
     149  // diplomacy
     150  function MostWanted(Nation, adGiveAway: integer): integer;
     151
     152  end;
     153
    28154
    29155implementation
    30156
    31157uses
    32   Pile;
     158Pile;
    33159
    34160const
    35   // fine adjustment
    36   Aggressive = 40; // 0 = never attacks, 100 = attacks even with heavy losses
    37   DestroyBonus = 30; // percent of building cost
     161// fine adjustment
     162Aggressive=40; // 0 = never attacks, 100 = attacks even with heavy losses
     163DestroyBonus=30; // percent of building cost
     164
     165var
     166LeaveOutValue: array[0..nAdv-1] of integer;
     167
    38168
    39169constructor TAI.Create(Nation: integer);
    40170begin
    41   inherited;
     171inherited;
     172Data:=pointer(RO.Data);
     173{$IFDEF DEBUG}if Nation=1 then SetDebugMap(DebugMap);{$ENDIF}
     174AdvanceValuesSet:=false;
    42175end;
    43176
    44 
    45 // -------------------------------
    46 // MY TURN
    47 // -------------------------------
    48 
    49 procedure TAI.DoTurn;
     177procedure TAI.SetDataDefaults;
     178begin
     179with Data^ do
     180  begin
     181  LastResearchTech:=-1;
     182  if PlayerHash[me]>7 then BehaviorFlags:=bFemale else BehaviorFlags:=bMale;
     183  DebugMessage(1, 'Gender:='+char(48+BehaviorFlags and bGender));
     184  TheologyPartner:=-1;
     185  fillchar(RejectTurn,sizeof(RejectTurn),$FF);
     186  Fillchar(RequestedTechs, sizeof(RequestedTechs), $FF);
     187  end
     188end;
     189
     190function TAI.OnNegoRejected_CancelTreaty: boolean;
     191begin
     192Data.RejectTurn[suContact,Opponent]:=RO.Turn;
     193result:= Data.BehaviorFlags and bBarbarina<>0;
     194end;
     195
     196
     197//-------------------------------
     198//            RESEARCH
     199//-------------------------------
     200
     201procedure TAI.RateModel(const mi: TModelInfo; var Category, Quality: integer);
    50202var
    51   uix: integer;
     203EffectiveTransport: integer;
    52204begin
    53   // correct tax rate if necessary
    54   if RO.Money > RO.nCity * 16 then
    55     ChangeRates(RO.TaxRate - 10, 0)
    56   else if RO.Money < RO.nCity * 8 then
    57     ChangeRates(RO.TaxRate + 10, 0);
    58 
    59   // better government form available?
    60   if RO.Government <> gAnarchy then
    61     if IsResearched(adTheRepublic) then
    62     begin
    63       if RO.Government <> gRepublic then
    64         Revolution
     205if mi.Kind>=mkScout then
     206  begin Category:=mctNone; exit end;
     207case mi.Domain of
     208  dGround:
     209    if mi.Speed>=250 then
     210      begin
     211      Category:=mctGroundAttacker;
     212      if mi.Attack=0 then Quality:=0
     213      else
     214        begin
     215        Quality:=trunc(100*(ln(mi.Attack)+ln(mi.Defense)+ln(mi.Speed/150)*1.7-ln(mi.Cost)));
     216        if mi.Cap and (1 shl (mcFanatic-mcFirstNonCap))<>0 then
     217          inc(Quality,trunc(100*ln(1.5)));
     218        if mi.Cap and (1 shl (mcLongRange-mcFirstNonCap))<>0 then
     219          inc(Quality,trunc(100*ln(1.5)));
     220        end
     221      end
     222    else
     223      begin
     224      Category:=mctGroundDefender;
     225      Quality:=trunc(100*(ln(mi.Defense)-ln(mi.Cost)*0.6));
     226      if mi.Cap and (1 shl (mcFanatic-mcFirstNonCap))<>0 then
     227        inc(Quality,trunc(100*ln(1.5)));
     228      end;
     229  dSea:
     230    if mi.Attack=0 then
     231      begin
     232      Category:=mctTransport;
     233      if mi.TTrans=0 then Quality:=0
     234      else
     235        begin
     236        EffectiveTransport:=mi.TTrans;
     237        if EffectiveTransport>4 then EffectiveTransport:=4; // rarely used more
     238        Quality:=100+trunc(100*(ln(EffectiveTransport)+ln(mi.Speed/150)+ln(mi.Defense)-ln(mi.Cost)));
     239        if mi.Cap and (1 shl (mcNav-mcFirstNonCap))<>0 then
     240          inc(Quality,trunc(100*ln(1.5)));
     241        if mi.Cap and (1 shl (mcAirDef-mcFirstNonCap))<>0 then
     242          inc(Quality,trunc(100*ln(1.3)));
     243        end
     244      end
     245    else
     246      begin
     247      Category:=mctCruiser;
     248      if mi.Attack=0 then Quality:=0
     249      else
     250        begin
     251        Quality:=trunc(100*(ln(mi.Attack)+ln(mi.Defense)*0.6-ln(mi.Cost)));
     252        if mi.Cap and (1 shl (mcNav-mcFirstNonCap))<>0 then
     253          inc(Quality,trunc(100*ln(1.4)));
     254        if mi.Cap and (1 shl (mcAirDef-mcFirstNonCap))<>0 then
     255          inc(Quality,trunc(100*ln(1.3)));
     256        if mi.Cap and (1 shl (mcLongRange-mcFirstNonCap))<>0 then
     257          inc(Quality,trunc(100*ln(2.0)));
     258        if mi.Cap and (1 shl (mcRadar-mcFirstNonCap))<>0 then
     259          inc(Quality,trunc(100*ln(1.5)));
     260        end
     261      end;
     262  dAir:
     263    begin
     264    Category:=mctNone;
     265    Quality:=0
     266    end;
     267  end;
     268//!!!assert(Quality>0);
     269end;
     270
     271procedure TAI.RateMyModel(mix: integer; var Category, Quality: integer);
     272var
     273mi: TModelInfo;
     274begin
     275MakeModelInfo(me,mix,MyModel[mix],mi);
     276RateModel(mi,Category,Quality);
     277end;
     278
     279function TAI.IsBetterModel(const mi: TModelInfo): boolean;
     280var
     281mix,Cat,Quality,Cat1,Quality1: integer;
     282begin
     283RateModel(mi,Cat,Quality);
     284for mix:=0 to RO.nModel-1 do if mi.Domain=MyModel[mix].Domain then
     285  begin
     286  RateMyModel(mix,Cat1,Quality1);
     287  if (Cat=Cat1) and (Quality<Quality1+BetterQuality[Cat])then
     288    begin result:=false; exit end
     289  end;
     290result:=true;
     291end;
     292
     293function TAI.ChooseResearchAdvance: integer;
     294var
     295adNext,iad,i,ad,Count,EarliestNeeded,EarliestNeeded_NoLeaveOut,
     296  NewResearch,StateOfArt,mix: integer;
     297mi: TModelInfo;
     298Entry: array[0..nAdv-1] of boolean;
     299ok: boolean;
     300
     301  function MarkEntry(ad: integer): boolean;
     302  begin
     303  if RO.Tech[ad]>=tsApplicable then
     304    result:=false // nothing more to research here
     305  else if RO.Tech[ad]=tsSeen then
     306    begin
     307    Entry[ad]:=true;
     308    result:=true
    65309    end
    66     else if IsResearched(adMonarchy) then
    67     begin
    68       if RO.Government <> gMonarchy then
    69         Revolution
    70     end;
    71 
    72   // do combat
    73   for uix := 0 to RO.nUn - 1 do
    74     if (MyUnit[uix].Loc >= 0) and not(MyModel[MyUnit[uix].mix].Kind
    75       in [mkSettler, mkSlaves]) then
    76       ProcessUnit(uix, Roam);
    77 
    78   ProcessSettlers;
    79 
    80   // do discover/patrol
    81 
    82   OptimizeCityTiles;
    83   SetCityProduction;
     310  else
     311    begin
     312    Entry[ad]:=true;
     313    if ad=adScience then
     314      begin
     315      if MarkEntry(adTheology) then Entry[ad]:=false;
     316      if MarkEntry(adPhilosophy) then Entry[ad]:=false;
     317      end
     318    else if ad=adMassProduction then
     319      begin
     320      if MarkEntry(adAutomobile) then Entry[ad]:=false;
     321      if Data.BehaviorFlags and bGender=bMale then
     322        begin if MarkEntry(adElectronics) then Entry[ad]:=false; end
     323      else begin if MarkEntry(adTheCorporation) then Entry[ad]:=false; end
     324      end
     325    else
     326      begin
     327      if AdvPreq[ad,0]>=0 then
     328        if MarkEntry(AdvPreq[ad,0]) then Entry[ad]:=false;
     329      if AdvPreq[ad,1]>=0 then
     330        if MarkEntry(AdvPreq[ad,1]) then Entry[ad]:=false;
     331      end;
     332    result:=true
     333    end
     334  end;
     335
     336  procedure OptimizeDevModel(OptimizeCaps: integer);
     337  var
     338  f,Cat,OriginalCat,Quality,BestQuality,Best: integer;
     339  mi: TModelInfo;
     340  begin
     341  MakeModelInfo(me,0,RO.DevModel,mi);
     342  RateModel(mi,OriginalCat,BestQuality);
     343  repeat
     344    Best:=-1;
     345    for f:=0 to nFeature-1 do
     346      if (1 shl f and OptimizeCaps<>0)
     347        and ((Feature[f].Preq<0) or IsResearched(Feature[f].Preq)) // check prerequisite
     348        and (RO.DevModel.Weight+Feature[f].Weight<=RO.DevModel.MaxWeight)
     349        and not((f>=mcFirstNonCap) and (RO.DevModel.Cap[f]>0)) then
     350        begin
     351        if SetNewModelFeature(f,RO.DevModel.Cap[f]+1)>=rExecuted then
     352          begin
     353          MakeModelInfo(me,0,RO.DevModel,mi);
     354          RateModel(mi,Cat,Quality);
     355          assert(Cat=OriginalCat);
     356          if Quality>BestQuality then
     357            begin
     358            Best:=f;
     359            BestQuality:=Quality;
     360            end;
     361          SetNewModelFeature(f,RO.DevModel.Cap[f]-1)
     362          end
     363        end;
     364    if Best>=0 then
     365      SetNewModelFeature(Best,RO.DevModel.Cap[Best]+1)
     366  until Best<0
     367  end;
     368
     369  function LeaveOutsMissing(ad: integer): boolean;
     370  var
     371  i: integer;
     372  begin
     373  result:=false;
     374  if RO.Tech[ad]<tsSeen then
     375    if ad in LeaveOutTechs then result:=true
     376    else if ad=adScience then
     377      begin
     378      result:=result or LeaveOutsMissing(adTheology);
     379      result:=result or LeaveOutsMissing(adPhilosophy);
     380      end
     381    else if ad=adMassProduction then
     382      result:=true
     383    else for i:=0 to 1 do
     384      if AdvPreq[ad,i]>=0 then
     385        result:=result or LeaveOutsMissing(AdvPreq[ad,i]);
     386  end;
     387
     388begin
     389if Data.BehaviorFlags and bBarbarina<>0 then
     390  begin
     391  result:=Barbarina_ChooseResearchAdvance;
     392  if result>=0 then exit
     393  end;
     394
     395SetAdvanceValues;
     396
     397// always complete traded techs first
     398result:=-1;
     399for ad:=0 to nAdv-1 do
     400  if (RO.Tech[ad]=tsSeen)
     401    and ((result<0) or (AdvanceValue[ad]>AdvanceValue[result])) then
     402    result:=ad;
     403if result>=0 then exit;
     404
     405if Data.BehaviorFlags and bBarbarina=0 then
     406  begin
     407  // develop new model?
     408  if IsResearched(adWarriorCode) and IsResearched(adHorsebackRiding)
     409    and not ((Data.BehaviorFlags and bGender=bMale) and (RO.Tech[adIronWorking]>=tsApplicable) // wait for gunpowder
     410      and (RO.Tech[adGunPowder]<tsApplicable)) then
     411    begin // check new ground models
     412    PrepareNewModel(dGround);
     413    SetNewModelFeature(mcDefense,1);
     414    SetNewModelFeature(mcOffense,2);
     415    SetNewModelFeature(mcMob,2);
     416    OptimizeDevModel(1 shl mcOffense+1 shl mcDefense+1 shl mcMob
     417      +1 shl mcLongRange+1 shl mcFanatic);
     418    MakeModelInfo(me,0,RO.DevModel,mi);
     419    if IsBetterModel(mi) then
     420      begin result:=adMilitary; exit end;
     421
     422    PrepareNewModel(dGround);
     423    SetNewModelFeature(mcDefense,2);
     424    SetNewModelFeature(mcOffense,1);
     425    OptimizeDevModel(1 shl mcOffense+1 shl mcDefense+1 shl mcFanatic);
     426    MakeModelInfo(me,0,RO.DevModel,mi);
     427    if IsBetterModel(mi) then
     428      begin result:=adMilitary; exit end;
     429    end;
     430
     431  if IsResearched(adMapMaking) and IsResearched(adSeafaring)
     432    and IsResearched(adNavigation) and IsResearched(adSteamEngine) then
     433    begin
     434    result:=adMilitary;
     435    for mix:=0 to RO.nModel-1 do if MyModel[mix].Cap[mcNav]>0 then result:=-1;
     436    if result=adMilitary then
     437      begin
     438      PrepareNewModel(dSea);
     439      SetNewModelFeature(mcWeapons,0);
     440      SetNewModelFeature(mcDefense,3);
     441      exit
     442      end
     443    end;
     444
     445  (*
     446  if IsResearched(adMapMaking) and IsResearched(adSeafaring) then
     447    begin // check new naval models
     448    PrepareNewModel(dSea);
     449    if RO.DevModel.MTrans>1 then
     450      begin // new transport?
     451      SetNewModelFeature(mcDefense,2);
     452      SetNewModelFeature(mcOffense,2);
     453      SetNewModelFeature(mcSeaTrans,1);
     454      OptimizeDevModel(1 shl mcDefense+1 shl mcSeaTrans+1 shl mcTurbines
     455        +1 shl mcAirDef);
     456      MakeModelInfo(me,0,RO.DevModel,mi);
     457      if IsBetterModel(mi) then
     458        begin result:=adMilitary; exit end;
     459      end;
     460
     461    // new cruiser?
     462    if IsResearched(adBallistics) or IsResearched(adGunPowder) then
     463      begin
     464      PrepareNewModel(dSea);
     465      SetNewModelFeature(mcDefense,1);
     466      SetNewModelFeature(mcOffense,2);
     467      OptimizeDevModel(1 shl mcOffense+1 shl mcDefense
     468        +1 shl mcLongRange+1 shl mcAirDef+1 shl mcRadar);
     469      MakeModelInfo(me,0,RO.DevModel,mi);
     470      if IsBetterModel(mi) then
     471        begin result:=adMilitary; exit end;
     472      end
     473    end;
     474  *)
     475  end;
     476
     477NewResearch:=-1;
     478
     479// check if cooperation with other gender doesn't work -- go for old needed techs then
     480StateOfArt:=-1;
     481for ad:=0 to nAdv-1 do
     482  if (RO.Tech[ad]>=tsApplicable) and (Advancedness[ad]>StateOfArt) then
     483    StateOfArt:=Advancedness[ad];
     484EarliestNeeded:=-1;
     485EarliestNeeded_NoLeaveOut:=-1;
     486for ad:=0 to nAdv-1 do
     487  if (RO.Tech[ad]<tsSeen) and (AdvanceValue[ad]>=$100)
     488    and ((EarliestNeeded<0)
     489      or (Advancedness[ad]<Advancedness[EarliestNeeded])) then
     490    begin
     491    ok:=false;
     492    for iad:=0 to nResearchOrder-1 do
     493      if ResearchOrder[Data.BehaviorFlags and bGender,iad]=ad then
     494        begin ok:=true; break; end;
     495    if not ok then
     496      begin
     497      EarliestNeeded:=ad;
     498      if not LeaveOutsMissing(ad) then
     499        EarliestNeeded_NoLeaveOut:=ad;
     500      end
     501    end;
     502if EarliestNeeded>=0 then
     503  begin
     504  if (EarliestNeeded_NoLeaveOut>=0)
     505    and (Advancedness[EarliestNeeded_NoLeaveOut]+ForceNeeded_NoLeaveOut<StateOfArt) then
     506    begin
     507    {$IFDEF DEBUG}DebugMessage(2,'No partner found, go for '
     508      +Name_Advance[EarliestNeeded_NoLeaveOut]);{$ENDIF}
     509    NewResearch:=EarliestNeeded_NoLeaveOut
     510    end
     511  else if Advancedness[EarliestNeeded]+ForceNeeded_LeaveOut<StateOfArt then
     512    begin
     513    {$IFDEF DEBUG}DebugMessage(2,'No partner found, go for '
     514      +Name_Advance[EarliestNeeded]);{$ENDIF}
     515    NewResearch:=EarliestNeeded
     516    end
     517  end;
     518
     519// choose first directly researchable advance from own branch
     520adNext:=-1;
     521if NewResearch<0 then
     522  for iad:=0 to nResearchOrder-1 do
     523    begin
     524    ad:=ResearchOrder[Data.BehaviorFlags and bGender,iad];
     525    if RO.Tech[ad]<tsApplicable then
     526      begin
     527      if adNext<0 then adNext:=ad;
     528      if AdvPreq[ad,2]<>preNone then
     529        begin // 2 of 3 required
     530        count:=0;
     531        for i:=0 to 2 do
     532          if RO.Tech[AdvPreq[ad,i]]>=tsApplicable then inc(count);
     533        if count>=2 then
     534          begin result:=ad; exit end
     535        end
     536      else if ((AdvPreq[ad,0]=preNone) or (RO.Tech[AdvPreq[ad,0]]>=tsApplicable))
     537        and ((AdvPreq[ad,1]=preNone) or (RO.Tech[AdvPreq[ad,1]]>=tsApplicable)) then
     538        begin result:=ad; exit end
     539      end
     540    end;
     541
     542if NewResearch<0 then
     543  if adNext>=0 then
     544    NewResearch:=adNext // need tech from other gender
     545  else if EarliestNeeded_NoLeaveOut>=0 then
     546    NewResearch:=EarliestNeeded_NoLeaveOut // own branch complete, pick tech from other gender
     547  else if EarliestNeeded>=0 then
     548    NewResearch:=EarliestNeeded // own branch complete, pick tech from other gender
     549  else
     550    begin // go for future techs
     551    result:=-1;
     552    i:=0;
     553    for ad:=nAdv-4 to nAdv-1 do
     554      if (RO.Tech[ad]<MaxFutureTech) and (RO.Tech[AdvPreq[ad,0]]>=tsApplicable) then
     555        begin
     556        inc(i);
     557        if random(i)=0 then result:=ad
     558        end;
     559    assert((result<0) or AdvanceResearchable(result));
     560    exit;
     561    end;
     562
     563assert(NewResearch>=0);
     564fillchar(Entry, sizeof(Entry), false);
     565MarkEntry(NewResearch);
     566result:=-1;
     567for ad:=0 to nAdv-1 do
     568  if Entry[ad]
     569    and ((result<0) or (Advancedness[ad]>Advancedness[result])) then
     570    result:=ad;
     571assert(result>=0);
     572end;
     573
     574function TAI.ChooseStealAdvance: integer;
     575var
     576ad: integer;
     577begin
     578result:=-1;
     579for ad:=0 to nAdv-1 do
     580  if AdvanceStealable(ad)
     581    and ((result<0) or (Advancedness[ad]>Advancedness[result])) then
     582    result:=ad
     583end;
     584
     585
     586//-------------------------------
     587//         TERRAFORMING
     588//-------------------------------
     589
     590const
     591twpAllowFarmland=$0001;
     592
     593procedure TAI.TileWorkPlan(Loc, cix: integer;
     594  var Value, NextJob, TotalWork: integer);
     595var
     596OldTile,TerrType: Cardinal;
     597TileInfo: TTileInfo;
     598begin
     599TotalWork:=0;
     600NextJob:=jNone;
     601if Map[Loc] and (fRare1 or fRare2)<>0 then
     602  begin Value:=3*8-1; exit end; // better than any tile with 2 food
     603
     604OldTile:=Map[Loc];
     605TerrType:=Map[Loc] and fTerrain;
     606if (TerrType>=fGrass) then
     607  begin
     608  if Map[Loc] and fPoll<>0 then
     609    begin // clean pollution
     610    if NextJob=jNone then NextJob:=jPoll;
     611    inc(TotalWork,PollWork);
     612    Map[Loc]:=Map[Loc] and not fPoll;
     613    end;
     614  if Map[Loc] and (fTerrain or fSpecial)=fSwamp then
     615    begin // drain swamp
     616    if NextJob=jNone then NextJob:=jClear;
     617    inc(TotalWork,Terrain[TerrType].IrrClearWork);
     618    Map[Loc]:=Map[Loc] and not fTerrain or fGrass;
     619    TerrType:=fGrass;
     620    Map[Loc]:=Map[Loc] or
     621      Cardinal(SpecialTile(Loc,TerrType,G.lx) shl 5);
     622    end
     623  else if IsResearched(adExplosives)
     624    and (Map[Loc] and (fTerrain or fSpecial) in [fTundra,fHills])
     625    and (Map[Loc] and fTerImp<>tiMine)
     626    and (SpecialTile(Loc,fHills,G.lx)=0) then
     627    begin // transform
     628    if NextJob=jNone then NextJob:=jTrans;
     629    inc(TotalWork,Terrain[TerrType].TransWork);
     630    Map[Loc]:=Map[Loc] and not fTerrain or fGrass;
     631    TerrType:=fGrass;
     632    Map[Loc]:=Map[Loc] or
     633      Cardinal(SpecialTile(Loc,TerrType,G.lx) shl 5);
     634    end;
     635  if (Terrain[TerrType].MineEff>0) and (RO.Government<>gDespotism) then
     636    begin
     637    if Map[Loc] and fTerImp<>tiMine then
     638      begin // add mine
     639      if NextJob=jNone then NextJob:=jMine;
     640      inc(TotalWork,Terrain[TerrType].MineAfforestWork);
     641      Map[Loc]:=Map[Loc] and not fTerImp or tiMine;
     642      end
     643    end
     644  else if Terrain[TerrType].IrrEff>0 then
     645    begin
     646    if Map[Loc] and fTerImp=tiIrrigation then
     647      begin // add farmland
     648      if (MyCity[cix].Built[imSupermarket]>0) and IsResearched(adRefrigeration)
     649        and (RO.Government<>gDespotism) then
     650        begin
     651        if NextJob=jNone then NextJob:=jFarm;
     652        inc(TotalWork,Terrain[TerrType].IrrClearWork*FarmWork);
     653        Map[Loc]:=Map[Loc] and not fTerImp or tiFarm;
     654        end
     655      end
     656    else if Map[Loc] and fTerImp<>tiFarm then
     657      begin // add irrigation
     658      if (RO.Government<>gDespotism)
     659        or (Map[Loc] and (fTerrain or fSpecial)<>fGrass) then
     660        begin
     661        if NextJob=jNone then NextJob:=jIrr;
     662        inc(TotalWork,Terrain[TerrType].IrrClearWork);
     663        Map[Loc]:=Map[Loc] and not fTerImp or tiIrrigation;
     664        end
     665      end
     666    end;
     667  if (Terrain[TerrType].MoveCost=1)
     668    and (Map[Loc] and (fRoad or fRR)=0)
     669    and ((Map[Loc] and fRiver=0) or IsResearched(adBridgeBuilding)) then
     670    begin // add road
     671    if NextJob=jNone then NextJob:=jRoad;
     672    inc(TotalWork,RoadWork);
     673    Map[Loc]:=Map[Loc] or fRoad;
     674    end;
     675  if ((Map[Loc] and fTerImp=tiMine)
     676      or (Terrain[TerrType].ProdRes[Map[Loc] shr 5 and 3]>=2))
     677    and IsResearched(adRailroad)
     678    and (Map[Loc] and fRR=0)
     679    and ((Map[Loc] and fRiver=0) or IsResearched(adBridgeBuilding))
     680    and (RO.Government<>gDespotism) then
     681    begin // add railroad
     682    if Map[Loc] and fRoad=0 then
     683      begin
     684      if NextJob=jNone then NextJob:=jRoad;
     685      inc(TotalWork,RoadWork*Terrain[TerrType].MoveCost);
     686      end;
     687    if NextJob=jNone then NextJob:=jRR;
     688    inc(TotalWork,RRWork*Terrain[TerrType].MoveCost);
     689    Map[Loc]:=Map[Loc] and not fRoad or fRR;
     690    end;
     691  end;
     692Server(sGetTileInfo,me,Loc,TileInfo);
     693Value:=TileInfo.Food*8+TileInfo.Prod*2+TileInfo.Trade;
     694Map[Loc]:=OldTile;
    84695end;
    85696
     
    87698procedure TAI.ProcessSettlers;
    88699var
    89   uix, cix, ecix, Loc, RadiusLoc, TestScore, BestNearCityScore, TerrType,
    90     Special, V21: integer;
    91   Radius: TVicinity21Loc;
    92   ResourceScore, CityScore: array [0 .. lxmax * lymax - 1] of integer;
     700i,uix,cix,ecix,dtr,Loc,RadiusLoc,Special,Food,Prod,Trade,CityFood,Happy,
     701  TestScore,BestNearCityScore,BestUnusedValue,BestUnusedLoc,
     702  Value,NextJob,TotalWork,V21,part,Loc1: integer;
     703Tile: Cardinal;
     704FoodOk,Started: boolean;
     705Radius: TVicinity21Loc;
     706CityAreaInfo: TCityAreaInfo;
     707TileFood, ResourceScore, CityScore: array[0..lxmax*lymax-1] of integer;
     708
     709  procedure AddJob(Loc,Job,Score: integer);
     710  // set Score=1 for low-priority jobs
     711  begin
     712  JobAssignment_AddJob(Loc,Job,Score);
     713  if (Score>1) and (District[Loc]>=0) and (District[Loc]<maxCOD) then
     714    dec(SettlerSurplus[District[Loc]]);
     715  end;
    93716
    94717  procedure ReserveCityRadius(Loc: integer);
    95718  var
    96     V21, RadiusLoc: integer;
    97     Radius: TVicinity21Loc;
    98   begin
    99     V21_to_Loc(Loc, Radius);
    100     for V21 := 1 to 26 do
    101     begin
    102       RadiusLoc := Radius[V21];
    103       if (RadiusLoc >= 0) and (RadiusLoc < MapSize) then
    104         ResourceScore[RadiusLoc] := 0
     719  V21,RadiusLoc: integer;
     720  Radius: TVicinity21Loc;
     721  begin
     722  V21_to_Loc(Loc,Radius);
     723  for V21:=1 to 26 do
     724    begin
     725    RadiusLoc:=Radius[V21];
     726    if (RadiusLoc>=0) then
     727      begin
     728      ResourceScore[RadiusLoc]:=0;
     729      TileFood[RadiusLoc]:=0;
     730      end
    105731    end
    106732  end;
    107733
    108 begin
    109   JobAssignment_Initialize;
    110 
    111   // rate resources of all tiles
    112   fillchar(ResourceScore, MapSize * sizeof(integer), 0);
    113   for Loc := 0 to MapSize - 1 do
    114     if (Map[Loc] and fRare) = 0 then
    115       if (Map[Loc] and fTerrain) = fGrass then
    116         if (Map[Loc] and fSpecial) <> 0 then
    117           ResourceScore[Loc] := 3 // plains, 3 points
    118         else
    119           ResourceScore[Loc] := 2 // grassland, 2 points
    120       else if (Map[Loc] and fSpecial) <> 0 then
    121         ResourceScore[Loc] := 4; // special resource, 4 points
    122   for cix := 0 to RO.nCity - 1 do
    123     if MyCity[cix].Loc >= 0 then
    124       ReserveCityRadius(MyCity[cix].Loc); // these resources already have a city
    125   for uix := 0 to RO.nUn - 1 do
    126     if (MyUnit[uix].Loc >= 0) and (MyUnit[uix].Job = jCity) then
    127       ReserveCityRadius(MyUnit[uix].Loc);
    128   // these resources almost already have a city
    129   for ecix := 0 to RO.nEnemyCity - 1 do
    130     if RO.EnemyCity[ecix].Loc >= 0 then
    131       ReserveCityRadius(RO.EnemyCity[ecix].Loc);
    132   // these resources already have an enemy city
    133 
    134   // rate possible new cities
    135   fillchar(CityScore, MapSize * sizeof(integer), 0);
    136   for Loc := 0 to MapSize - 1 do
    137     if ((Map[Loc] and fTerrain) = fGrass) and ((Map[Loc] and fRare) = 0) and
    138       ((RO.Territory[Loc] < 0) or (RO.Territory[Loc] = me)) then
    139     // don't consider founding cities in foreign nation territory
    140     begin
    141       TestScore := 0;
    142       BestNearCityScore := 0;
    143       V21_to_Loc(Loc, Radius);
    144       for V21 := 1 to 26 do
    145       begin // sum resource scores in potential city radius
    146         RadiusLoc := Radius[V21];
    147         if (RadiusLoc >= 0) and (RadiusLoc < MapSize) then
    148         begin
    149           TestScore := TestScore + ResourceScore[RadiusLoc];
    150           if CityScore[RadiusLoc] > BestNearCityScore then
    151             BestNearCityScore := CityScore[RadiusLoc]
    152         end
    153       end;
    154       if TestScore >= 10 then // city is worth founding
    155       begin
    156         TestScore := TestScore shl 8 + ((Loc xor me) * 4567) mod 251;
    157         // some unexactness, random but always the same for this tile
    158         if TestScore > BestNearCityScore then
    159         begin // better than all other sites in radius
    160           if BestNearCityScore > 0 then // found no other cities in radius
     734  procedure ScoreRoadConnections;
     735  var
     736  V8,nFragments,Loc,Loc1,History,RoadScore,a,b,FullyDeveloped,ConnectMask: integer;
     737  BridgeOk: boolean;
     738  Adjacent: TVicinity8Loc;
     739  begin
     740  BridgeOk:= IsResearched(adBridgeBuilding);
     741  if IsResearched(adRailroad) then FullyDeveloped:=fRR or fCity
     742  else FullyDeveloped:=fRoad or fRR or fCity;
     743  for Loc:=G.lx to G.lx*(G.ly-1)-1 do
     744    if ((1 shl (Map[Loc] and fTerrain)) and (1 shl fOcean or 1 shl fShore or 1 shl fDesert or 1 shl fArctic or 1 shl fUNKNOWN)=0)
     745      and (RO.Territory[Loc]=me)
     746      and (Map[Loc] and FullyDeveloped=0)
     747      and (BridgeOk or (Map[Loc] and fRiver=0)) then
     748      begin
     749      nFragments:=0;
     750      History:=0;
     751      if Map[Loc] and fRoad<>0 then ConnectMask:=fRR or fCity // check for railroad
     752      else ConnectMask:=fRoad or fRR or fCity; // check for road
     753      V8_to_Loc(Loc,Adjacent);
     754      for V8:=0 to 9 do
     755        begin
     756        Loc1:=Adjacent[V8 and 7];
     757        History:=History shl 1;
     758        if (Loc1>=0) and (RO.Territory[Loc1]=me)
     759          and (Map[Loc1] and ConnectMask<>0) then
    161760          begin
    162             for V21 := 1 to 26 do
     761          inc(History);
     762          if V8>=2 then
    163763            begin
    164               RadiusLoc := Radius[V21];
    165               if (RadiusLoc >= 0) and (RadiusLoc < MapSize) then
    166                 CityScore[RadiusLoc] := 0;
     764            inc(nFragments);
     765            case V8 and 1 of
     766              0:
     767                if History and 6<>0 then
     768                  dec(nFragments);
     769              1:
     770                if History and 2<>0 then
     771                  dec(nFragments)
     772                else if History and 4<>0 then
     773                  begin
     774                  V8_to_ab((V8-1) and 7,a,b);
     775                  ab_to_Loc(Loc,a shl 1,b shl 1,Loc1);
     776                  if (Loc1>=0)
     777                    and (Map[Loc1] and ConnectMask<>0) then
     778                    dec(nFragments)
     779                  end
     780              end
    167781            end;
    168782          end;
    169           CityScore[Loc] := TestScore
    170783        end;
    171       end
    172     end;
    173   for Loc := 0 to MapSize - 1 do
    174     if CityScore[Loc] > 0 then
    175       JobAssignment_AddJob(Loc, jCity, 10);
    176 
    177   // improve terrain
    178   for cix := 0 to RO.nCity - 1 do
    179     with MyCity[cix] do
    180       if Loc >= 0 then
    181       begin
    182         V21_to_Loc(Loc, Radius);
    183         for V21 := 1 to 26 do
    184           if (Tiles and (1 shl V21) and not(1 shl CityOwnTile)) <> 0 then
    185           begin // tile is exploited, but not the city own tile -- check if improvable
    186             RadiusLoc := Radius[V21];
    187             assert((RadiusLoc >= 0) and (RadiusLoc < MapSize));
    188             if (RadiusLoc >= 0) and (RadiusLoc < MapSize) then
     784      if nFragments>=2 then // road or railroad connection desirable
     785        begin
     786        if Map[Loc] and fRiver<>0 then RoadScore:=44+(nFragments-2)*4
     787        else RoadScore:=56-Terrain[Map[Loc] and fTerrain].MoveCost*4
     788          +(nFragments-2)*4;
     789        if Map[Loc] and fRoad<>0 then
     790          AddJob(Loc, jRR, RoadScore)
     791        else AddJob(Loc, jRoad, RoadScore)
     792        end;
     793      end;
     794  end;
     795
     796begin
     797fillchar(SettlerSurplus, sizeof(SettlerSurplus), 0);
     798JobAssignment_Initialize;
     799
     800if (Data.BehaviorFlags and bBarbarina=0) or (RO.nCity<3) then
     801  begin
     802  fillchar(TileFood,sizeof(TileFood),0);
     803  fillchar(ResourceScore,sizeof(ResourceScore),0);
     804  for Loc:=0 to MapSize-1 do
     805    if Map[Loc] and fTerrain<>fUNKNOWN then
     806      if Map[Loc] and fDeadLands<>0 then
     807        begin
     808        if not IsResearched(adMassProduction) or (Map[Loc] and fModern<>0) then
     809          ResourceScore[Loc]:=20;
     810        end
     811      else if Map[Loc] and fTerrain=fGrass then
     812        TileFood[Loc]:=Terrain[fGrass].FoodRes[Map[Loc] shr 5 and 3]-1
     813      else
     814        begin
     815        Special:=SpecialTile(Loc,Map[Loc] and fTerrain,G.lx);
     816        if Special<>0 then with Terrain[Map[Loc] and fTerrain] do
     817          begin
     818          Food:=FoodRes[Special];
     819          if MineEff=0 then inc(Food,IrrEff);
     820          Prod:=ProdRes[Special]+MineEff;
     821          Trade:=TradeRes[Special];
     822          if MoveCost=1 then inc(Trade);
     823          ResourceScore[Loc]:=Food+2*Prod+Trade-7;
     824          if Food>2 then TileFood[Loc]:=Food-2;
     825          end
     826        end;
     827
     828  for cix:=0 to RO.nCity-1 do
     829    if MyCity[cix].Loc>=0 then
     830      ReserveCityRadius(MyCity[cix].Loc); // these resources already have a city
     831  for uix:=0 to RO.nUn-1 do
     832    if (MyUnit[uix].Loc>=0) and (MyUnit[uix].Job=jCity) then
     833      ReserveCityRadius(MyUnit[uix].Loc); // these resources almost already have a city
     834  for ecix:=0 to RO.nEnemyCity-1 do
     835    if RO.EnemyCity[ecix].Loc>=0 then
     836      ReserveCityRadius(RO.EnemyCity[ecix].Loc); // these resources already have an enemy city
     837
     838  // rate possible new cities
     839  fillchar(CityScore, MapSize*sizeof(integer), 0);
     840  for Loc:=0 to MapSize-1 do
     841    begin
     842    FoodOk:= (TileFood[Loc]>0)
     843      and ((Map[Loc] and fTerrain=fGrass)
     844          and ((RO.Government<>gDespotism) or (Map[Loc] and fSpecial=fSpecial1))
     845        or (Map[Loc] and (fTerrain or fSpecial)=fPrairie or fSpecial1));
     846    if FoodOk and ((RO.Territory[Loc]<0) or (RO.Territory[Loc]=me)) then
     847      begin
     848      TestScore:=0;
     849      CityFood:=0;
     850      BestNearCityScore:=0;
     851      V21_to_Loc(Loc,Radius);
     852      for V21:=1 to 26 do
     853        begin // sum resource scores in potential city radius
     854        RadiusLoc:=Radius[V21];
     855        if (RadiusLoc>=0) then
     856          begin
     857          inc(CityFood,TileFood[RadiusLoc]);
     858          if ResourceScore[RadiusLoc]>0 then
     859            inc(TestScore,ResourceScore[RadiusLoc]);
     860          if CityScore[RadiusLoc]>BestNearCityScore then
     861            BestNearCityScore:=CityScore[RadiusLoc]
     862          end
     863        end;
     864      if CityFood>=MinCityFood then // city is worth founding
     865        begin
     866        TestScore:=(72+2*TestScore) shl 8 + ((loc xor me)*4567) mod 251;
     867          // some unexactness, random but always the same for this tile
     868        if TestScore>BestNearCityScore then
     869          begin // better than all other sites in radius
     870          if BestNearCityScore>0 then // found no other cities in radius
    189871            begin
    190               TerrType := Map[RadiusLoc] and fTerrain;
    191               Special := Map[RadiusLoc] shr 5 and 3;
    192               if TerrType >= fGrass then // can't improve water tiles
    193                 if (Terrain[TerrType].IrrEff > 0) // terrain is irrigatable
    194                   and not((RO.Government = gDespotism) and
    195                   (Terrain[TerrType].FoodRes[Special] >= 3))
    196                 // improvement makes no sense when limit is depotism
    197                   and ((Map[RadiusLoc] and fTerImp) = 0) then
    198                 // no terrain improvement yet
    199                   JobAssignment_AddJob(RadiusLoc, jIrr, 50) // irrigate!
    200                 else if (Terrain[TerrType].MoveCost = 1) // plain terrain
    201                   and ((Map[RadiusLoc] and (fRoad or fRR or fRiver)) = 0) then
    202                 // no road or railroad yet, no river
    203                   JobAssignment_AddJob(RadiusLoc, jRoad, 40);
    204               // build road (The Wheel trade benefit)
     872            for V21:=1 to 26 do
     873              begin
     874              RadiusLoc:=Radius[V21];
     875              if (RadiusLoc>=0) then
     876                CityScore[RadiusLoc]:=0;
     877              end;
     878            end;
     879          CityScore[Loc]:=TestScore
     880          end;
     881        end
     882      end;
     883    end;
     884  for Loc:=0 to MapSize-1 do
     885    if CityScore[Loc]>0 then
     886      AddJob(Loc, jCity, CityScore[Loc] shr 8);
     887  end;
     888
     889// improve terrain
     890for cix:=0 to RO.nCity-1 do with MyCity[cix] do if Loc>=0 then
     891  begin // order terrain improvements
     892  BestUnusedValue:=0;
     893  City_GetAreaInfo(cix,CityAreaInfo);
     894  V21_to_Loc(Loc,Radius);
     895  for V21:=1 to 26 do if V21<>CityOwnTile then
     896    if 1 shl V21 and Tiles<>0 then
     897      begin // tile is being exploited!
     898      RadiusLoc:=Radius[V21];
     899      if not (Map[RadiusLoc] and fTerrain in [fDesert,fArctic]) then
     900        begin
     901        assert(RadiusLoc>=0);
     902        TileWorkPlan(RadiusLoc,cix,Value,NextJob,TotalWork);
     903        if (NextJob=jRoad)
     904          and (Built[imPalace]+Built[imCourt]+Built[imTownHall]=0) then
     905          AddJob(RadiusLoc, NextJob, 44)
     906        else if NextJob<>jNone then
     907          AddJob(RadiusLoc, NextJob, 84)
     908        end
     909      end
     910    else if CityAreaInfo.Available[V21]=faAvailable then
     911      begin // tile could be exploited
     912      RadiusLoc:=Radius[V21];
     913      assert(RadiusLoc>=0);
     914      if not (Map[RadiusLoc] and fTerrain in [fDesert,fArctic]) then
     915        begin
     916        TileWorkPlan(RadiusLoc,cix,Value,NextJob,TotalWork);
     917        Value:=Value shl 16 +$FFFF-TotalWork;
     918        if Value>BestUnusedValue then
     919          begin
     920          BestUnusedValue:=Value;
     921          BestUnusedLoc:=RadiusLoc;
     922          end
     923        end
     924      end;
     925  if BestUnusedValue>0 then
     926    begin
     927    TileWorkPlan(BestUnusedLoc,cix,Value,NextJob,TotalWork);
     928    if NextJob<>jNone then
     929      AddJob(BestUnusedLoc, NextJob, 44)
     930    end
     931  end;
     932
     933ScoreRoadConnections;
     934
     935if Data.BehaviorFlags and bBarbarina=0 then // low priority jobs
     936  for Loc:=0 to MapSize-1 do if RO.Territory[Loc]=me then
     937    begin
     938    Tile:=Map[Loc];
     939    if Tile and fPoll<>0 then
     940      AddJob(Loc, jPoll, 1)
     941    else case Tile and (fTerrain or fSpecial or fCity) of
     942      fGrass, fGrass+fSpecial1:
     943        if IsResearched(adExplosives) and (SpecialTile(Loc,fHills,G.lx)>0) then
     944          AddJob(Loc, jTrans, 1);
     945      fSwamp:
     946        if SpecialTile(Loc,fSwamp,G.lx)=0 then
     947          AddJob(Loc, jClear, 1);
     948      fTundra,fHills:
     949        if IsResearched(adExplosives) and (Tile and fTerImp<>tiMine)
     950          and (SpecialTile(Loc,fHills,G.lx)=0) then
     951          AddJob(Loc, jTrans, 1);
     952      end
     953    end;
     954
     955// cities for colony ship production
     956if Data.BehaviorFlags and bBarbarina=bBarbarina then
     957  begin
     958  for part:=0 to nShipPart-1 do
     959    for i:=0 to ColonyShipPlan[part].nLocFoundCity-1 do
     960      begin
     961      Loc:=ColonyShipPlan[part].LocFoundCity[i];
     962      Started:=false;
     963      for uix:=0 to RO.nUn-1 do
     964        if (MyUnit[uix].Loc=Loc) and (MyUnit[uix].Job=jCity) then
     965          begin
     966          Started:=true;
     967          break
     968          end;
     969      if not Started then
     970        begin
     971        Tile:=RO.Map[Loc];
     972        if (Tile and fTerrain=fForest) or (Tile and fTerrain=fSwamp) then
     973          AddJob(Loc,jClear,235)
     974        else if Tile and fTerrain=fHills then
     975          begin
     976          if IsResearched(adExplosives) then
     977            AddJob(Loc,jTrans,235)
     978          end
     979        else AddJob(Loc,jCity,235);
     980        end;
     981      V21_to_Loc(Loc, Radius);
     982      for V21:=1 to 26 do
     983        begin
     984        Loc1:=Radius[V21];
     985        if (Loc1>=0) and (RO.Map[Loc1] and (fTerrain or fSpecial)=fSwamp) then
     986          AddJob(Loc1,jClear,255);
     987        end
     988      end
     989  end;
     990
     991// choose all settlers to work
     992for uix:=0 to RO.nUn-1 do with MyUnit[uix] do
     993  if (Loc>=0) and ((mix=mixSettlers) or (mix=mixSlaves)
     994    or (Data.BehaviorFlags and bBarbarina<>0) and (MyModel[mix].Kind=mkSettler)) then
     995    begin
     996    JobAssignment_AddUnit(uix);
     997    if (District[Loc]>=0) and (District[Loc]<maxCOD) then
     998      inc(SettlerSurplus[District[Loc]]);
     999    end;
     1000
     1001JobAssignment_Go;
     1002
     1003for uix:=0 to RO.nUn-1 do with MyUnit[uix] do
     1004  if (Loc>=0) and (Map[Loc] and fCity=0) and (Job=jNone)
     1005    and ((mix=mixSettlers) or (mix=mixSlaves))
     1006    and not JobAssignment_GotJob(uix) then
     1007    Unit_MoveEx(uix, maNextCity);
     1008
     1009//{$IFDEF DEBUG}DebugMessage(2, Format('Settler surplus in district 0: %d',[SettlerSurplus[0]]));{$ENDIF}
     1010
     1011// add settlers to city
     1012for uix:=0 to RO.nUn-1 do with MyUnit[uix] do
     1013  if (Loc>=0) and (Map[Loc] and fCity<>0)
     1014    and (MyModel[MyUnit[uix].mix].Kind=mkSettler) then
     1015    begin
     1016    dtr:=District[Loc];
     1017    if (mix<>mixSettlers)
     1018      or (dtr>=0) and (dtr<maxCOD)
     1019      and (SettlerSurplus[dtr]>DistrictPopulation[dtr] div 8) then
     1020      begin
     1021      City_FindMyCity(Loc, cix);
     1022      with MyCity[cix] do
     1023        if (Built[imSewer]>0)
     1024          or (Built[imAqueduct]>0) and (Size<=NeedSewerSize-2)
     1025          or (Size<=NeedAqueductSize-2) then
     1026          begin // settlers could be added to this city
     1027          Happy:=BasicHappy;
     1028          for i:=0 to 27 do if Built[i]>0 then inc(Happy);
     1029          if Built[imTemple]>0 then inc(Happy);
     1030          if Built[imCathedral]>0 then
     1031            begin
     1032            inc(Happy,2);
     1033            if RO.Wonder[woBach].EffectiveOwner=me then inc(Happy,1)
     1034            end;
     1035          if Built[imTheater]>0 then inc(Happy,2);
     1036          if (Built[imColosseum]>0) or (Happy shl 1>=Size+2) then
     1037            begin // bigger city would be happy
     1038//            {$IFDEF DEBUG}DebugMessage(2, Format('Adding settlers to city at %d',[Loc]));{$ENDIF}
     1039            Unit_AddToCity(uix);
     1040            if (dtr>=0) and (dtr<maxCOD) then dec(SettlerSurplus[dtr])
    2051041            end
    2061042          end;
    207       end;
    208 
    209   // choose all settlers to work
    210   for uix := 0 to RO.nUn - 1 do
    211     if (MyUnit[uix].Loc >= 0) and
    212       (MyModel[MyUnit[uix].mix].Kind in [mkSettler, mkSlaves]) then
    213       JobAssignment_AddUnit(uix);
    214 
    215   JobAssignment_Go;
     1043      end
     1044    end;
    2161045end; // ProcessSettlers
    2171046
    218 // ProcessUnit: execute attack, capture, discover or patrol task according to unit role
    219 procedure TAI.ProcessUnit(uix: integer; Role: UnitRole);
     1047
     1048//-------------------------------
     1049//            MY TURN
     1050//-------------------------------
     1051
     1052procedure TAI.DoTurn;
     1053var
     1054emix,i,p1,TaxSum,ScienceSum,NewTaxRate: integer;
     1055AllHateMe: boolean;
     1056{$IFDEF PERF}PF,t0,t1,t2,t3,t4,t5,t6,t7,t8,t9: int64;{$ENDIF}
     1057begin
     1058{$IFDEF DEBUG}fillchar(DebugMap, sizeof(DebugMap),0);{$ENDIF}
     1059
     1060{$IFDEF PERF}QueryPerformanceFrequency(PF);{$ENDIF}
     1061{$IFDEF PERF}QueryPerformanceCounter(t0);{$ENDIF}
     1062
     1063WarNations:=PresenceUnknown;
     1064for p1:=0 to nPl-1 do
     1065  if (p1<>me) and (1 shl p1 and RO.Alive<>0) and (RO.Treaty[p1]<trPeace) then
     1066    inc(WarNations,1 shl p1);
     1067BombardingNations:=0;
     1068for emix:=0 to RO.nEnemyModel-1 do with RO.EnemyModel[emix] do
     1069  if (Domain=dSea) and (1 shl (mcLongRange-mcFirstNonCap) and Cap<>0) then
     1070    BombardingNations:=BombardingNations or (1 shl Owner);
     1071BombardingNations:=BombardingNations and WarNations;
     1072
     1073AnalyzeMap;
     1074//for i:=0 to MapSize-1 do DebugMap[i]:=Formation[i];
     1075
     1076if (Data.BehaviorFlags and bBarbarina=0)
     1077  and (RO.Tech[ResearchOrder[Data.BehaviorFlags and bGender,8]]<tsApplicable) then
     1078  CheckGender;
     1079
     1080if G.Difficulty[me]<MaxDiff then // not on beginner level
     1081  begin
     1082  if  (Data.LastResearchTech=adHorsebackRiding)
     1083    and (RO.ResearchTech<0) and (random(6)=0)
     1084    and (HavePort or (ContinentPresence[0] and not (1 shl me or PresenceUnknown)<>0)) then
     1085    begin
     1086    Data.BehaviorFlags:=Data.BehaviorFlags or bBarbarina_Hide;
     1087    DebugMessage(1, 'Early Barbarina!');
     1088    end;
     1089  if Data.BehaviorFlags and bBarbarina=0 then
     1090    begin
     1091    AllHateMe:=false;
     1092    for p1:=0 to nPl-1 do
     1093      if (1 shl p1 and RO.Alive<>0) and (RO.Treaty[p1]>=trNone) then
     1094        if (RO.Treaty[p1]<trPeace) and
     1095          ((Data.RejectTurn[suContact,p1]>=0)
     1096          or (Data.RejectTurn[suPeace,p1]>=0)) then
     1097          AllHateMe:=true
     1098        else begin AllHateMe:=false; break end;
     1099    if AllHateMe then
     1100      begin
     1101      Data.BehaviorFlags:=Data.BehaviorFlags or bBarbarina_Hide;
     1102      DebugMessage(1, 'All hate me!');
     1103      end
     1104    end;
     1105
     1106  if Data.BehaviorFlags and bBarbarina=0 then
     1107    if Barbarina_GoHidden then
     1108      begin
     1109      Data.BehaviorFlags:=Data.BehaviorFlags or bBarbarina_Hide;
     1110      DebugMessage(1, 'Barbarina!');
     1111      end;
     1112  if Data.BehaviorFlags and bBarbarina=bBarbarina_Hide then
     1113    if Barbarina_Go then
     1114      begin
     1115      Data.BehaviorFlags:=Data.BehaviorFlags or bBarbarina;
     1116      DebugMessage(1, 'Barbarina - no mercy!');
     1117      end;
     1118  end;
     1119
     1120{$IFDEF PERF}QueryPerformanceCounter(t1);{$ENDIF}
     1121
     1122  // better government form available?
     1123if (Data.BehaviorFlags and bBarbarina=0) and (RO.Turn>=LeaveDespotism)
     1124  and (RO.Government<>gAnarchy) then
     1125  if IsResearched(adDemocracy) then
     1126    begin
     1127    if RO.Government<>gDemocracy then
     1128      Revolution //!!!
     1129    end
     1130  else if IsResearched(adTheRepublic) then
     1131    begin
     1132    if RO.Government<>gRepublic then
     1133      Revolution
     1134    end
     1135  else if IsResearched(adMonarchy) then
     1136    begin
     1137    if RO.Government<>gMonarchy then
     1138      Revolution
     1139    end;
     1140
     1141CollectModelCatStat;
     1142
     1143if Data.BehaviorFlags and bBarbarina=bBarbarina then
     1144  begin
     1145  MakeColonyShipPlan;
     1146  Barbarina_DoTurn
     1147  end
     1148else
     1149  begin
     1150  {$IFDEF PERF}QueryPerformanceCounter(t2);{$ENDIF}
     1151
     1152  {$IFDEF PERF}QueryPerformanceCounter(t3);{$ENDIF}
     1153
     1154  AttackAndPatrol;
     1155
     1156  {$IFDEF PERF}QueryPerformanceCounter(t4);{$ENDIF}
     1157
     1158  MoveUnitsHome;
     1159
     1160  {$IFDEF PERF}QueryPerformanceCounter(t5);{$ENDIF}
     1161  end;
     1162
     1163ProcessSettlers;
     1164
     1165{$IFDEF PERF}QueryPerformanceCounter(t6);{$ENDIF}
     1166
     1167if Data.BehaviorFlags and bBarbarina<>0 then
     1168  Barbarina_SetCityProduction
     1169else
     1170  SetCityProduction;
     1171
     1172{$IFDEF PERF}QueryPerformanceCounter(t7);{$ENDIF}
     1173
     1174// correct tax rate if necessary
     1175if not IsResearched(adWheel) then
     1176  ChangeRates(0,0)
     1177else
     1178  begin
     1179  if (RO.TaxRate=0) or (RO.Money<(TotalPopulation[me]-4)*2) then
     1180    NewTaxRate:=RO.TaxRate // don't check decreasing tax
     1181  else NewTaxRate:=RO.TaxRate-10;
     1182  while NewTaxRate<100 do
     1183    begin
     1184    SumCities(NewTaxRate,TaxSum,ScienceSum);
     1185    if RO.Money+TaxSum>=(TotalPopulation[me]-4) then break; // enough
     1186    inc(NewTaxRate,10);
     1187    end;
     1188  if NewTaxRate<>RO.TaxRate then
     1189    begin
     1190  //  {$IFDEF DEBUG}DebugMessage(3,Format('New tax rate: %d',[NewTaxRate]));{$ENDIF}
     1191    ChangeRates(NewTaxRate,0)
     1192    end;
     1193  end;
     1194
     1195// clean up RequestedTechs
     1196if (Data.LastResearchTech>=0)
     1197  and (Data.LastResearchTech<>RO.ResearchTech) then // research completed
     1198  for p1:=0 to nPl-1 do
     1199    if (p1<>me) and (1 shl p1 and RO.Alive<>0)
     1200      and (RO.EnemyReport[p1].TurnOfCivilReport+TechReportOutdated>RO.Turn)
     1201      and (RO.EnemyReport[p1].Tech[Data.LastResearchTech]<tsSeen) then
     1202      begin // latest researched advance might be of interest to this nation
     1203      for i:=0 to nRequestedTechs-1 do
     1204        if (Data.RequestedTechs[i]>=0)
     1205          and (Data.RequestedTechs[i] shr 8 and $F=p1) then
     1206          Data.RequestedTechs[i]:=-1;
     1207      end;
     1208if RO.ResearchTech=adMilitary then Data.LastResearchTech:=-1
     1209else Data.LastResearchTech:=RO.ResearchTech;
     1210for i:=0 to nRequestedTechs-1 do
     1211  if (Data.RequestedTechs[i]>=0)
     1212    and (RO.Tech[Data.RequestedTechs[i] and $FF]>=tsSeen) then
     1213    Data.RequestedTechs[i]:=-1;
     1214
     1215// prepare negotiation
     1216AdvanceValuesSet:=false;
     1217SetAdvanceValues;
     1218
     1219
     1220{$IFDEF DEBUG}
     1221(*for p1:=0 to nPl-1 do
     1222  if (p1<>me) and (1 shl p1 and RO.Alive<>0) and (RO.Treaty[p1]>=trPeace)
     1223    and (RO.EnemyReport[p1].TurnOfCivilReport>=0) then
     1224    TraceAdvanceValues(p1);*)
     1225{$ENDIF}
     1226
     1227{$IFDEF PERF}DebugMessage(2,Format('t1=%d t2=%d t3=%d t4=%d t5=%d t6=%d t7=%d t8=%d t9=%d (ns)',[(t1-t0)*1000000 div PF,(t2-t1)*1000000 div PF,(t3-t2)*1000000 div PF,(t4-t3)*1000000 div PF,(t5-t4)*1000000 div PF,(t6-t5)*1000000 div PF,(t7-t6)*1000000 div PF,(t8-t7)*1000000 div PF,(t9-t8)*1000000 div PF]));{$ENDIF}
     1228end;
     1229
     1230{$IFDEF DEBUG}
     1231procedure TAI.TraceAdvanceValues(Nation: integer);
     1232var
     1233ad: integer;
     1234begin
     1235for ad:=0 to nAdv-1 do
     1236  if (RO.Tech[ad]<tsSeen) and (RO.EnemyReport[Nation].Tech[ad]>=tsApplicable)
     1237    and (AdvanceValue[ad]>0) then
     1238    begin
     1239    DebugMessage(2,Format('%s (%d): +%x',
     1240      [Name_Advance[ad], Advancedness[ad], AdvanceValue[ad]]))
     1241    end
     1242end;
     1243{$ENDIF}
     1244
     1245
     1246procedure TAI.CheckGender;
     1247var
     1248p1,NewGender: integer;
     1249begin
     1250NewGender:=-1;
     1251for p1:=0 to nPl-1 do
     1252  if (p1<>me) and (1 shl p1 and RO.Alive<>0)
     1253    and (RO.Treaty[p1]>=trFriendlyContact) then
     1254    if PlayerHash[me]>PlayerHash[p1] then
     1255      begin
     1256      if NewGender=bMale then
     1257        begin NewGender:=-2; break end; // ambiguous, don't change gender
     1258      NewGender:=bFemale;
     1259      end
     1260    else
     1261      begin
     1262      if NewGender=bFemale then
     1263        begin NewGender:=-2; break end; // ambiguous, don't change gender
     1264      NewGender:=bMale;
     1265      end;
     1266if (NewGender>=0) and (NewGender<>Data.BehaviorFlags and bGender) then
     1267  begin
     1268  Data.BehaviorFlags:=Data.BehaviorFlags and not bGender or NewGender;
     1269  DebugMessage(1, 'Gender:='+char(48+NewGender));
     1270  end
     1271end;
     1272
     1273
     1274procedure TAI.SetAdvanceValues;
     1275
     1276  procedure RateResearchAdv(ad, Time: integer);
     1277  var
     1278  Value: integer;
     1279  begin
     1280  if Time=0 then Value:=TechValue_ForResearch_Next
     1281  else Value:=TechValue_ForResearch-Time;
     1282  if AdvanceValue[ad]<Value then
     1283    AdvanceValue[ad]:=Value;
     1284  end;
     1285
     1286  procedure SetPreqValues(ad, Value: integer);
     1287  begin
     1288  if (RO.Tech[ad]<tsSeen) and (ad<>RO.ResearchTech) then
     1289    begin
     1290    if AdvanceValue[ad]<Value then
     1291      AdvanceValue[ad]:=Value;
     1292    if ad=adScience then
     1293      begin
     1294      SetPreqValues(adTheology,Value-1);
     1295      SetPreqValues(adPhilosophy,Value-1);
     1296      end
     1297    else if ad=adMassProduction then
     1298      // preqs should be researched now
     1299    else
     1300      begin
     1301      if AdvPreq[ad,0]>=0 then
     1302        SetPreqValues(AdvPreq[ad,0],Value-1);
     1303      if AdvPreq[ad,1]>=0 then
     1304        SetPreqValues(AdvPreq[ad,1],Value-1);
     1305      end;
     1306    end
     1307  end;
     1308
     1309  procedure RateImpPreq(iix, Value: integer);
     1310  begin
     1311  if (Value>0) and (Imp[iix].Preq>=0) then
     1312    inc(AdvanceValue[Imp[iix].Preq],Value);
     1313  end;
     1314
     1315var
     1316emix,cix,adMissing,iad,ad,count,i,Time,d,CurrentCost,CurrentStrength,
     1317  MaxSize, MaxTrade: integer;
     1318PreView,Emergency,Bombarded: boolean;
     1319begin
     1320if AdvanceValuesSet then exit;
     1321AdvanceValuesSet:=true;
     1322
     1323fillchar(AdvanceValue,sizeof(AdvanceValue),0);
     1324
     1325// rate techs to ensure research progress
     1326Time:=0;
     1327for ad:=0 to nAdv-1 do if RO.Tech[ad]=tsSeen then inc(Time);
     1328adMissing:=-1;
     1329Emergency:=true;
     1330for iad:=0 to nResearchOrder-1 do
     1331  begin
     1332  ad:=ResearchOrder[Data.BehaviorFlags and bGender,iad];
     1333  if (ad<>RO.ResearchTech) and (RO.Tech[ad]<tsSeen) then
     1334    begin
     1335    if adMissing<0 then adMissing:=ad;
     1336    RateResearchAdv(ad,Time); // unseen tech of own gender
     1337    if AdvPreq[ad,2]<>preNone then
     1338      begin // 2 of 3 required
     1339      count:=0;
     1340      for i:=0 to 2 do
     1341        if (AdvPreq[ad,i]=RO.ResearchTech)
     1342          or (RO.Tech[AdvPreq[ad,i]]>=tsSeen) then
     1343          inc(count);
     1344      if count>=2 then Emergency:=false
     1345      else
     1346        begin
     1347        if ad<>adMassProduction then // don't score third preq for MP
     1348          begin
     1349          for i:=0 to 2 do
     1350            if (AdvPreq[ad,i]<>RO.ResearchTech)
     1351              and (RO.Tech[AdvPreq[ad,i]]<tsSeen) then
     1352              RateResearchAdv(AdvPreq[ad,i],Time);
     1353          end;
     1354        inc(Time,2-count)
     1355        end
     1356      end
     1357    else
     1358      begin
     1359      count:=0;
     1360      for i:=0 to 1 do
     1361        if (AdvPreq[ad,i]<>preNone) and (AdvPreq[ad,i]<>RO.ResearchTech)
     1362          and (RO.Tech[AdvPreq[ad,i]]<tsSeen) then
     1363          begin
     1364          RateResearchAdv(AdvPreq[ad,i],Time);
     1365          inc(count)
     1366          end;
     1367      if count=0 then Emergency:=false;
     1368      inc(Time,count);
     1369      end;
     1370    inc(Time,2);
     1371    end
     1372  end;
     1373if Emergency and (adMissing>=0) then
     1374  begin
     1375  {$IFDEF DEBUG}DebugMessage(2, 'Research emergency: Go for'
     1376    +Name_Advance[adMissing]+' now!');{$ENDIF}
     1377  SetPreqValues(adMissing, TechValue_ForResearch_Urgent);
     1378  end;
     1379for iad:=0 to nResearchOrder-1 do
     1380  begin
     1381  ad:=ResearchOrder[Data.BehaviorFlags and bGender xor 1,iad];
     1382  if ad=adScience then
     1383    inc(AdvanceValue[ad], 5*TechValue_ForResearch_LeaveOut)
     1384  else if LeaveOutValue[ad]>0 then
     1385    if AdvanceValue[ad]>0 then
     1386      inc(AdvanceValue[ad], LeaveOutValue[ad]*TechValue_ForResearch_LeaveOut)
     1387//    else AdvanceValue[ad]:=1;
     1388  end;
     1389
     1390// rate military techs
     1391for d:=0 to nDomains-1 do
     1392  begin
     1393  CurrentCost:=0;
     1394  CurrentStrength:=0;
     1395  for PreView:=true downto false do
     1396    for i:=0 to nUpgrade-1 do with Upgrade[d,i] do
     1397      if (Preq>=0) and not (Preq in FutureTech) then
     1398        if ((Ro.ResearchTech=Preq) or (RO.Tech[Preq]>=tsSeen)) = PreView then
     1399          if PreView then
     1400            begin
     1401            if Cost>CurrentCost then CurrentCost:=Cost;
     1402            inc(CurrentStrength, Strength);
     1403            end
     1404          else
     1405            begin // rate
     1406            if (i>0) and (Trans>0) then inc(AdvanceValue[Preq],$400);
     1407            if Cost<=CurrentCost then
     1408              inc(AdvanceValue[Preq], (4-d)*Strength*$400 div (CurrentStrength+Upgrade[d,0].Strength))
     1409            else inc(AdvanceValue[Preq], (4-d)*Strength*$200 div (CurrentStrength+Upgrade[d,0].Strength))
     1410            end;
     1411  end;
     1412// speed
     1413inc(AdvanceValue[adSteamEngine],$400);
     1414inc(AdvanceValue[adNuclearPower],$400);
     1415inc(AdvanceValue[adRocketry],$400);
     1416// features
     1417inc(AdvanceValue[adBallistics],$800);
     1418inc(AdvanceValue[adCommunism],$800);
     1419// weight
     1420inc(AdvanceValue[adAutomobile],$800);
     1421inc(AdvanceValue[adSteel],$800);
     1422inc(AdvanceValue[adAdvancedFlight],$400);
     1423
     1424// civil non-improvement
     1425if RO.Turn>=LeaveDespotism then
     1426  begin
     1427  inc(AdvanceValue[adDemocracy],$80*RO.nCity);
     1428  inc(AdvanceValue[adTheRepublic],$800);
     1429  end;
     1430inc(AdvanceValue[adRailroad],$800);
     1431// inc(AdvanceValue[adExplosives],$800); // no, has enough
     1432inc(AdvanceValue[adBridgeBuilding],$200);
     1433inc(AdvanceValue[adSpaceFlight],$200);
     1434inc(AdvanceValue[adSelfContainedEnvironment],$200);
     1435inc(AdvanceValue[adImpulseDrive],$200);
     1436inc(AdvanceValue[adTransstellarColonization],$200);
     1437
     1438// city improvements
     1439MaxSize:=0;
     1440for cix:=0 to RO.nCity-1 do
     1441  if MyCity[cix].Size>MaxSize then
     1442    MaxSize:=MyCity[cix].Size;
     1443if RO.Government in [gRepublic,gDemocracy,gLybertarianism] then
     1444  MaxTrade:=(MaxSize-1)*3
     1445else MaxTrade:=(MaxSize-1)*2;
     1446
     1447RateImpPreq(imCourt,(RO.nCity-1)*$100);
     1448RateImpPreq(imLibrary,(MaxTrade-10)*$180);
     1449RateImpPreq(imMarket,(MaxTrade-10)*$140);
     1450RateImpPreq(imUniversity,(MaxTrade-10)*$140);
     1451RateImpPreq(imBank,(MaxTrade-10)*$100);
     1452RateImpPreq(imObservatory,(MaxTrade-10)*$100);
     1453RateImpPreq(imResLab,(MaxTrade-14)*$140);
     1454RateImpPreq(imStockEx,(MaxTrade-10)*$10*(RO.nCity-1));
     1455RateImpPreq(imHighways,(MaxSize-5)*$200);
     1456RateImpPreq(imFactory,(MaxSize-8)*$200);
     1457RateImpPreq(imMfgPlant,(MaxSize-8)*$1C0);
     1458RateImpPreq(imRecycling,(MaxSize-8)*$180);
     1459RateImpPreq(imHarbor,(MaxSize-7)*$200);
     1460RateImpPreq(imSuperMarket,$300);
     1461if RO.Turn>=40 then RateImpPreq(imTemple,$400);
     1462if RO.Government<>gDespotism then
     1463  begin
     1464  RateImpPreq(imCathedral,$400);
     1465  RateImpPreq(imTheater,$400);
     1466  end;
     1467if MaxSize>=NeedAqueductSize-1 then
     1468  begin
     1469  RateImpPreq(imAqueduct,$600);
     1470  RateImpPreq(imGrWall,$300);
     1471  end;
     1472if cixStateImp[imPalace]>=0 then
     1473  with MyCity[cixStateImp[imPalace]] do
     1474    if (Built[imColosseum]+Built[imObservatory]>0) and (Size>=NeedSewerSize-1) then
     1475      RateImpPreq(imSewer,$400);
     1476Bombarded:=false;
     1477for emix:=0 to RO.nEnemyModel-1 do
     1478  if 1 shl (mcLongRange-mcFirstNonCap) and RO.EnemyModel[emix].Cap<>0 then
     1479    Bombarded:=true;
     1480if Bombarded then
     1481  RateImpPreq(imCoastalFort,$400);
     1482end;
     1483
     1484procedure TAI.AnalyzeMap;
     1485var
     1486cix,Loc,Loc1,V8,f1,p1: integer;
     1487Adjacent: TVicinity8Loc;
     1488begin
     1489inherited AnalyzeMap;
     1490
     1491// collect nation presence information for continents and oceans
     1492fillchar(ContinentPresence, sizeof(ContinentPresence), 0);
     1493fillchar(OceanPresence, sizeof(OceanPresence), 0);
     1494for Loc:=0 to MapSize-1 do
     1495  begin
     1496  f1:=Formation[Loc];
     1497  case f1 of
     1498    0..maxCOD-1:
     1499      begin
     1500      p1:=RO.Territory[Loc];
     1501      if p1>=0 then
     1502        if Map[Loc] and fTerrain>=fGrass then
     1503          ContinentPresence[f1]:=ContinentPresence[f1] or (1 shl p1)
     1504        else OceanPresence[f1]:=OceanPresence[f1] or (1 shl p1);
     1505      end;
     1506    nfUndiscovered:
     1507      begin // adjacent formations are not completely discovered
     1508      V8_to_Loc(Loc,Adjacent);
     1509      for V8:=0 to 7 do
     1510        begin
     1511        Loc1:=Adjacent[V8];
     1512        if Loc1>=0 then
     1513          begin
     1514          f1:=Formation[Loc1];
     1515          if (f1>=0) and (f1<maxCOD) then
     1516            if Map[Loc1] and fTerrain>=fGrass then
     1517              ContinentPresence[f1]:=ContinentPresence[f1] or PresenceUnknown
     1518            else OceanPresence[f1]:=OceanPresence[f1] or PresenceUnknown
     1519          end
     1520        end
     1521      end;
     1522    nfPeace:
     1523      begin // nation present in adjacent formations
     1524      V8_to_Loc(Loc,Adjacent);
     1525      for V8:=0 to 7 do
     1526        begin
     1527        Loc1:=Adjacent[V8];
     1528        if Loc1>=0 then
     1529          begin
     1530          f1:=Formation[Loc1];
     1531          if (f1>=0) and (f1<maxCOD) then
     1532            if Map[Loc1] and fTerrain>=fGrass then
     1533              ContinentPresence[f1]:=ContinentPresence[f1]
     1534                or (1 shl RO.Territory[Loc])
     1535            else OceanPresence[f1]:=OceanPresence[f1]
     1536              or (1 shl RO.Territory[Loc])
     1537          end
     1538        end
     1539      end;
     1540    end;
     1541  end;
     1542
     1543fillchar(TotalPopulation, sizeof(TotalPopulation), 0);
     1544fillchar(ContinentPopulation, sizeof(ContinentPopulation), 0);
     1545fillchar(DistrictPopulation, 4*nDistrict, 0);
     1546
     1547// count population
     1548for cix:=0 to RO.nEnemyCity-1 do with RO.EnemyCity[cix] do if Loc>=0 then
     1549  begin
     1550  inc(TotalPopulation[Owner],Size);
     1551  if (Formation[Loc]>=0) and (Formation[Loc]<maxCOD) then
     1552    inc(ContinentPopulation[Owner,Formation[Loc]],Size);
     1553  end;
     1554for cix:=0 to RO.nCity-1 do with RO.City[cix] do if Loc>=0 then
     1555  begin
     1556  inc(TotalPopulation[me],Size);
     1557  assert(District[Loc]>=0);
     1558  if District[Loc]<maxCOD then
     1559    inc(DistrictPopulation[District[Loc]],Size);
     1560  end;
     1561end;
     1562
     1563procedure TAI.CollectModelCatStat;
     1564var
     1565i,uix,Cat,mix,Quality: integer;
     1566begin
     1567// categorize models
     1568for Cat:=0 to nModelCat-1 do
     1569  ModelBestQuality[Cat]:=0;
     1570mixCaravan:=-1;
     1571mixSlaves:=-1;
     1572mixCruiser:=-1;
     1573for mix:=0 to RO.nModel-1 do
     1574  begin
     1575  ModelCat[mix]:=mctNone;
     1576  if mix=1 then mixMilitia:=mix
     1577  else
     1578    case MyModel[mix].Kind of
     1579      $00..$0F: // common units
     1580        if MyModel[mix].Cap[mcNav]>0 then mixCruiser:=mix // temporary!!!
     1581        else
     1582          begin
     1583          RateMyModel(mix,Cat,Quality);
     1584          ModelCat[mix]:=Cat;
     1585          ModelQuality[mix]:=Quality;
     1586          if (Cat>=0) and (Quality>ModelBestQuality[Cat]) then
     1587            ModelBestQuality[Cat]:=Quality;
     1588          end;
     1589      mkSpecial_TownGuard: mixTownGuard:=mix;
     1590      mkSettler: mixSettlers:=mix; // engineers always have higher mix
     1591      mkCaravan: mixCaravan:=mix;
     1592      mkSlaves: mixSlaves:=mix
     1593      end
     1594  end;
     1595
     1596// mark obsolete models with quality=0
     1597for mix:=0 to RO.nModel-1 do
     1598  if (MyModel[mix].Kind<$10) and (ModelCat[mix]>=0)
     1599    and (ModelQuality[mix]+MaxExistWorseThanBestModel
     1600      < ModelBestQuality[ModelCat[mix]]) then
     1601    ModelQuality[mix]:=ModelQuality[mix]-$40000000;
     1602
     1603OceanWithShip:=0;
     1604if mixCruiser>=0 then
     1605  for uix:=0 to RO.nUn-1 do with MyUnit[uix] do
     1606    if (Loc>=0) and (mix=mixCruiser) and (Map[Loc] and fTerrain<fGrass) then
     1607      begin
     1608      i:=Formation[Loc];
     1609      if (i>=0) and (i<maxCOD) then OceanWithShip:=OceanWithShip or (1 shl i)
     1610      end;
     1611end;
     1612
     1613
     1614procedure TAI.MoveUnitsHome;
    2201615const
    221   DistanceScore = 4;
     1616PatrolDestination=lxmax*lymax;
     1617FirstSurplusLoop: array[mctGroundDefender..mctGroundAttacker] of integer= (2,1);
    2221618var
    223   BestScore, BestCount, BestLoc, TerrType, TestLoc, NextLoc, TestDistance, Tile,
    224     V8, TestScore, euix, MyDamage, EnemyDamage, TerrOwner, StepSize, OldLoc,
    225     AttackForecast, MoveResult, AttackResult: integer;
    226   Exhausted: boolean;
    227   TestTask, BestTask: (utNone, utAttack, utCapture, utDiscover, utPatrol,
    228     utGoHome);
     1619Cat,i,mix,cix,uix,Loop,nModelOrder: integer;
     1620Adjacent: TVicinity8Loc;
     1621LocNeed: array[0..lxmax*lymax-1] of shortint;
     1622Destination: array[0..nUmax-1] of integer;
     1623DistrictNeed,DistrictNeed0: array[0..maxCOD-1] of integer;
     1624ModelOrder: array[0..nMmax-1] of integer;
     1625complete,Fortified: boolean;
     1626
     1627  function IsBombarded(cix: integer): boolean;
     1628  var
     1629  Loc1,V8: integer;
    2291630  Adjacent: TVicinity8Loc;
    230   AdjacentUnknown: array [0 .. lxmax * lymax - 1] of integer;
    231 
    232 begin
     1631  begin
     1632  result:=false;
     1633  if BombardingNations<>0 then with MyCity[cix] do
     1634    begin
     1635    V8_to_Loc(Loc,Adjacent);
     1636    for V8:=0 to 7 do
     1637      begin
     1638      Loc1:=Adjacent[V8];
     1639      if (Loc1>=0) and (Map[Loc1] and fTerrain<fGrass)
     1640        and (Formation[Loc1]>=0) and (Formation[Loc1]<maxCOD)
     1641        and (OceanPresence[Formation[Loc1]] and (BombardingNations or PresenceUnknown)<>0) then
     1642        begin result:=true; exit end
     1643      end;
     1644    end;
     1645  end;
     1646
     1647  procedure TryUtilize(uix: integer);
     1648  var
     1649  cix, ProdCost, UtilizeCost: integer;
     1650  begin
     1651  if (MyUnit[uix].Health=100)
     1652    and (Map[MyUnit[uix].Loc] and (fCity or fOwned)=fCity or fOwned) then
     1653    begin
     1654    City_FindMyCity(MyUnit[uix].Loc,cix);
     1655    with MyCity[cix] do if Project and cpImp=0 then
     1656      begin
     1657      ProdCost:=MyModel[Project and cpIndex].Cost;
     1658      UtilizeCost:=MyModel[MyUnit[uix].mix].Cost;
     1659      if Prod<(ProdCost-UtilizeCost*2 div 3)*BuildCostMod[G.Difficulty[me]] div 12 then
     1660        Unit_Disband(uix);
     1661      end
     1662    end
     1663  end;
     1664
     1665  procedure FindDestination(uix: integer);
     1666  var
     1667  MoveStyle,V8,Loc1,Time,NextLoc,NextTime,RecoverTurns: integer;
     1668  Reached: array[0..lxmax*lymax-1] of boolean;
     1669  begin
     1670  fillchar(Reached, MapSize, false);
    2331671  Pile.Create(MapSize);
    2341672  with MyUnit[uix] do
    235     repeat
    236       BestScore := -999999;
    237       BestTask := utNone;
    238       fillchar(AdjacentUnknown, sizeof(AdjacentUnknown), $FF);
    239       // -1, indicates tiles not checked yet
    240       Pile.Empty;
    241       Pile.Put(Loc, 0); // start search for something to do at current location
    242       while Pile.Get(TestLoc, TestDistance) do
    243       begin
    244         TestScore := 0;
    245         Tile := Map[TestLoc];
    246         AdjacentUnknown[TestLoc] := 0;
    247 
    248         if ((Tile and fUnit) <> 0) and ((Tile and fOwned) = 0) then
     1673    begin
     1674    Pile.Put(Loc, $800-Movement);
     1675    MoveStyle:=GetMyMoveStyle(mix, 100);
     1676    end;
     1677  while Pile.Get(Loc1, Time) do
     1678    begin
     1679    if LocNeed[Loc1]>0 then
     1680      begin
     1681      LocNeed[Loc1]:=0;
     1682      if (District[Loc1]>=0) and (District[Loc1]<maxCOD) then
     1683        begin
     1684        assert(DistrictNeed[District[Loc1]]>0);
     1685        dec(DistrictNeed[District[Loc1]]);
     1686        end;
     1687      Destination[uix]:=Loc1;
     1688      break;
     1689      end;
     1690    Reached[Loc1]:=true;
     1691    V8_to_Loc(Loc1, Adjacent);
     1692    for V8:=0 to 7 do
     1693      begin
     1694      NextLoc:=Adjacent[V8];
     1695      if (NextLoc>=0) and not Reached[NextLoc] and (RO.Territory[NextLoc]=me) then
     1696        case CheckStep(MoveStyle, Time, V8 and 1, NextTime, RecoverTurns, Map[Loc1], Map[NextLoc], false) of
     1697          csOk:
     1698            Pile.Put(NextLoc, NextTime);
     1699          csForbiddenTile:
     1700            Reached[NextLoc]:=true; // don't check moving there again
     1701          csCheckTerritory:
     1702            assert(false);
     1703          end
     1704      end;
     1705    end;
     1706  Pile.Free;
     1707  end;
     1708
     1709begin
     1710if not (RO.Government in [gAnarchy, gDespotism]) then // utilize townguards
     1711  for uix:=0 to RO.nUn-1 do with MyUnit[uix] do
     1712    if (Loc>=0) and (Master<0) and (mix=mixTownGuard) then
     1713    Unit_Disband(uix);
     1714
     1715fillchar(UnitLack,sizeof(UnitLack),0);
     1716fillchar(Destination, 4*RO.nUn, $FF);
     1717for i:=0 to maxCOD-1 do
     1718  if uixPatrol[i]>=0 then
     1719    Destination[uixPatrol[i]]:=PatrolDestination;
     1720for uix:=0 to RO.nUn-1 do
     1721  if (MyUnit[uix].mix=mixMilitia) or (MyUnit[uix].mix=mixCruiser) then
     1722    Destination[uix]:=PatrolDestination;
     1723
     1724// distribute attackers and defenders
     1725for Cat:=mctGroundDefender to mctGroundAttacker do
     1726  begin
     1727  nModelOrder:=0;
     1728  for mix:=0 to Ro.nModel-1 do
     1729    if ModelCat[mix]=Cat then
     1730      begin
     1731      i:=nModelOrder;
     1732      while (i>0) and (ModelQuality[mix]<ModelQuality[ModelOrder[i-1]]) do
     1733        begin ModelOrder[i]:=ModelOrder[i-1]; dec(i) end;
     1734      ModelOrder[i]:=mix;
     1735      inc(nModelOrder);
     1736      end;
     1737
     1738  Loop:=0;
     1739  repeat
     1740    if Loop=FirstSurplusLoop[Cat] then
     1741      for uix:=0 to RO.nUn-1 do with MyUnit[uix] do
     1742        if (Loc>=0) and (Destination[uix]<0) and (Master<0)
     1743          and (ModelCat[mix]=Cat)
     1744          and (ModelQuality[mix]<0) then
     1745          TryUtilize(uix);
     1746
     1747    fillchar(LocNeed, MapSize, 0);
     1748    fillchar(DistrictNeed, sizeof(DistrictNeed), 0);
     1749
     1750    for cix:=0 to RO.nCity-1 do with MyCity[cix] do if Loc>=0 then
     1751      if ((Cat<>mctGroundDefender) or (Loop<>0) or IsBombarded(cix))
     1752        and ((Loop<>FirstSurplusLoop[Cat]) or (Built[imBarracks]+Built[imMilAcademy]>0))
     1753        and ((Loop<>FirstSurplusLoop[Cat]+1) or (Built[imBarracks]+Built[imMilAcademy]=0)) then
     1754        begin
     1755        LocNeed[Loc]:=1;
     1756        if (District[Loc]>=0) and (District[Loc]<maxCOD) then
     1757          begin
     1758          inc(DistrictNeed[District[Loc]]);
     1759          if Loop<FirstSurplusLoop[Cat] then
     1760            inc(UnitLack[District[Loc],Cat])
     1761          end
     1762        end;
     1763
     1764    if Loop=0 then // protect city building sites
     1765      for uix:=0 to RO.nUn-1 do with MyUnit[uix] do
     1766        if (Loc>=0) and (Job=jCity) and (RO.Territory[Loc]=me) then
     1767          begin
     1768          LocNeed[Loc]:=1;
     1769          if (District[Loc]>=0) and (District[Loc]<maxCOD) then
     1770            inc(DistrictNeed[District[Loc]]);
     1771          end;
     1772
     1773    complete:= Loop>=FirstSurplusLoop[Cat];
     1774    for i:=nModelOrder-1 downto 0 do
     1775      begin
     1776      for Fortified:=true downto false do
     1777        for uix:=0 to RO.nUn-1 do with MyUnit[uix] do
     1778          if (mix=ModelOrder[i])
     1779            and (Loc>=0) and (Destination[uix]<0) and (Master<0)
     1780            and ((Flags and unFortified<>0) = Fortified)
     1781            and (LocNeed[Loc]>0) then
     1782            begin
     1783            LocNeed[Loc]:=0;
     1784            if (District[Loc]>=0) and (District[Loc]<maxCOD) then
     1785              dec(DistrictNeed[District[Loc]]);
     1786            Destination[uix]:=Loc;
     1787            complete:=false;
     1788            end;
     1789
     1790      for uix:=0 to RO.nUn-1 do with MyUnit[uix] do
     1791        if (mix=ModelOrder[i])
     1792          and (Loc>=0) and (Destination[uix]<0) and (Master<0) then
     1793          if (District[Loc]>=0) and (District[Loc]<maxCOD)
     1794            and (DistrictNeed[District[Loc]]=0) then
     1795          else
     1796            begin // unassigned unit
     1797            FindDestination(uix);
     1798            if Destination[uix]>=0 then complete:=false;
     1799            end;
     1800      end;
     1801    inc(Loop)
     1802  until complete;
     1803  end;
     1804
     1805// distribute obsolete settlers
     1806repeat
     1807  fillchar(LocNeed, MapSize, 0);
     1808  fillchar(DistrictNeed, sizeof(DistrictNeed), 0);
     1809
     1810  for cix:=0 to RO.nCity-1 do with MyCity[cix] do if Loc>=0 then
     1811    if (Built[imSewer]>0)
     1812      or (Built[imAqueduct]>0) and (Size<=NeedSewerSize-2)
     1813      or (Size<=NeedAqueductSize-2)
     1814      or (Project=mixSettlers) then
     1815      begin
     1816      LocNeed[Loc]:=1;
     1817      if (District[Loc]>=0) and (District[Loc]<maxCOD) then
     1818        inc(DistrictNeed[District[Loc]]);
     1819      end;
     1820  DistrictNeed0:=DistrictNeed;
     1821
     1822  complete:=true;
     1823  for uix:=0 to RO.nUn-1 do with MyUnit[uix] do
     1824    if (Loc>=0) and (Destination[uix]<0) and (Master<0) then
     1825      if (MyModel[mix].Kind=mkSettler) and (mix<>mixSettlers)
     1826        and (Job=jNone) then
     1827        if (District[Loc]>=0) and (District[Loc]<maxCOD)
     1828          and (DistrictNeed[District[Loc]]=0) then
     1829          begin
     1830          if DistrictNeed0[District[Loc]]>0 then
     1831            complete:=false
     1832          end
     1833        else
     1834          begin // unassigned unit
     1835          FindDestination(uix);
     1836//          if (Destination[uix]<0) and (RO.Territory[Loc]=me) then
     1837//            complete:=false; // causes hangup when unit can't move due to zoc
     1838          end;
     1839until complete;
     1840
     1841for uix:=0 to RO.nUn-1 do with MyUnit[uix] do if Loc>=0 then
     1842  if Destination[uix]<0 then
     1843    begin
     1844    if (MyModel[mix].Kind<>mkSettler) and (MyModel[mix].Kind<>mkSlaves)
     1845      and (Master<0) and (Map[Loc] and fCity=0) then
     1846      Unit_MoveEx(uix, maNextCity);
     1847    end
     1848  else if (Destination[uix]<>PatrolDestination) and (Loc<>Destination[uix]) then
     1849    Unit_MoveEx(uix, Destination[uix]);
     1850
     1851for uix:=0 to RO.nUn-1 do with MyUnit[uix] do
     1852  if (Loc>=0) and (RO.Territory[Loc]=me)
     1853    and (District[Loc]>=0) and (District[Loc]<maxCOD)
     1854    and (ModelQuality[mix]>0) then
     1855    case ModelCat[mix] of
     1856      mctGroundDefender,mctGroundAttacker:
     1857        dec(UnitLack[District[Loc],ModelCat[mix]])
     1858      end;
     1859end; // MoveUnitsHome
     1860
     1861
     1862procedure TAI.CheckAttack(uix: integer);
     1863var
     1864AttackScore,BestCount,AttackLoc,TestLoc,NextLoc,TestTime,V8,
     1865  TestScore,euix,MyDamage,EnemyDamage,OldLoc,
     1866  AttackForecast,MoveResult,AttackResult,MoveStyle,NextTime,RecoverTurns: integer;
     1867Tile: Cardinal;
     1868Exhausted: boolean;
     1869Adjacent: TVicinity8Loc;
     1870Reached: array[0..lxmax*lymax-1] of boolean;
     1871
     1872begin
     1873with MyUnit[uix] do
     1874  begin
     1875  MoveStyle:=GetMyMoveStyle(mix,Health);
     1876  repeat
     1877    AttackScore:=-999999;
     1878    AttackLoc:=-1;
     1879    fillchar(Reached, MapSize, false);
     1880    Pile.Create(MapSize);
     1881    Pile.Put(Loc, $800-Movement); // start search for something to do at current location
     1882    while Pile.Get(TestLoc,TestTime) do
     1883      begin
     1884      TestScore:=0;
     1885      Tile:=Map[TestLoc];
     1886      Reached[TestLoc]:=true;
     1887
     1888      if ((Tile and fUnit)<>0) and ((Tile and fOwned)=0) then
    2491889        begin // enemy unit
    250           Unit_FindEnemyDefender(TestLoc, euix);
    251           if RO.Treaty[RO.EnemyUn[euix].Owner] < trPeace then
    252           begin // unfriendly unit -- check attack
    253             if Unit_AttackForecast(uix, TestLoc, 100, AttackForecast) then
     1890        assert(TestTime<$1000);
     1891        Unit_FindEnemyDefender(TestLoc,euix);
     1892        if RO.Treaty[RO.EnemyUn[euix].Owner]<trPeace then
     1893          if Unit_AttackForecast(uix,TestLoc,$800-TestTime,AttackForecast) then
    2541894            begin // attack possible, but advantageous?
    255               if AttackForecast > 0 then
     1895            if AttackForecast=0 then
    2561896              begin // enemy unit would be destroyed
    257                 MyDamage := Health - AttackForecast;
    258                 EnemyDamage := RO.EnemyUn[euix].Health + DestroyBonus;
     1897              MyDamage:=Health+DestroyBonus;
     1898              EnemyDamage:=RO.EnemyUn[euix].Health+DestroyBonus;
    2591899              end
    260               else // own unit would be destroyed
     1900            else if AttackForecast>0 then
     1901              begin // enemy unit would be destroyed
     1902              MyDamage:=Health-AttackForecast;
     1903              EnemyDamage:=RO.EnemyUn[euix].Health+DestroyBonus;
     1904              end
     1905            else // own unit would be destroyed
    2611906              begin
    262                 MyDamage := Health + DestroyBonus;
    263                 EnemyDamage := RO.EnemyUn[euix].Health + AttackForecast;
     1907              MyDamage:=Health+DestroyBonus;
     1908              EnemyDamage:=RO.EnemyUn[euix].Health+AttackForecast;
    2641909              end;
    265               TestScore := Aggressive * 2 *
    266                 (EnemyDamage * RO.EnemyModel[RO.EnemyUn[euix].emix].Cost)
    267                 div (MyDamage * MyModel[mix].Cost);
    268               if TestScore <= 100 then
    269                 TestScore := 0 // own losses exceed enemy losses, no good
    270               else
     1910            TestScore:=Aggressive*2
     1911              *(EnemyDamage*RO.EnemyModel[RO.EnemyUn[euix].emix].Cost)
     1912              div (MyDamage*MyModel[mix].Cost);
     1913            if TestScore<=100 then TestScore:=0 // own losses exceed enemy losses, no good
     1914            else
    2711915              begin
    272                 TestScore := (TestScore - 100) div 10 + 30;
    273                 TestTask := utAttack
     1916              if TestScore>AttackScore then
     1917                BestCount:=0;
     1918              if TestScore>=AttackScore then
     1919                begin
     1920                inc(BestCount);
     1921                if random(BestCount)=0 then
     1922                  begin
     1923                  AttackScore:=TestScore;
     1924                  AttackLoc:=TestLoc;
     1925                  end
     1926                end;
    2741927              end
    275             end
    276           end
     1928            end;
    2771929        end // enemy unit
    2781930
    279         else if ((Tile and fCity) <> 0) and ((Tile and fOwned) = 0) then
    280         begin // enemy city, empty or unobserved
    281           if (MyModel[mix].Domain = dGround)
    282           // ships of this AI have no long-range guns, so don't try to attack cities with them
    283             and ((RO.Territory[TestLoc] < 0)
    284             // happens only for unobserved cities of extinct tribes, new owner unknown
    285             or (RO.Treaty[RO.Territory[TestLoc]] < trPeace)) then
    286           begin // unfriendly city -- check attack/capture
    287             if (Tile and fObserved) <> 0 then
    288             begin // observed and no unit present -- city is undefended, capture!
    289               TestScore := 40;
    290               TestTask := utCapture
    291             end
    292             else if Role = Roam then
    293             begin // unobserved city, possibly defended -- go for attack
    294               TestScore := 30;
    295               TestTask := utPatrol
    296             end
    297           end
    298         end // enemy city, empty or unobserved
    299 
     1931      else if ((Tile and fCity)<>0) and ((Tile and fOwned)=0) then
     1932        // enemy city
     1933
     1934      else
     1935        begin // no enemy city or unit here
     1936        V8_to_Loc(TestLoc,Adjacent);
     1937        for V8:=0 to 7 do
     1938          begin
     1939          NextLoc:=Adjacent[V8];
     1940          if (NextLoc>=0) and not Reached[NextLoc]
     1941            and (Map[NextLoc] and fTerrain<>fUNKNOWN) then
     1942            if Map[NextLoc] and (fUnit or fOwned)=fUnit then
     1943              Pile.Put(NextLoc, TestTime) // foreign unit!
     1944            else case CheckStep(MoveStyle, TestTime, V8 and 1, NextTime,
     1945              RecoverTurns, Map[Loc], Map[NextLoc], true) of
     1946              csOk,csCheckTerritory:
     1947                if NextTime<$1000 then Pile.Put(NextLoc, NextTime);
     1948              csForbiddenTile:
     1949                Reached[NextLoc]:=true; // don't check moving there again
     1950              end
     1951          end;
     1952        end; // no enemy city or unit here
     1953      end; // while Pile.Get
     1954    Pile.Free;
     1955
     1956    if AttackLoc>=0 then
     1957      begin
     1958      OldLoc:=Loc;
     1959      MoveResult:=Unit_Move(uix,AttackLoc);
     1960      Exhausted:= (Loc=OldLoc)
     1961        or ((MoveResult and (rMoreTurns or rUnitRemoved))<>0);
     1962      if MoveResult and rLocationReached<>0 then
     1963        if Movement<100 then
     1964          Exhausted:=true
    3001965        else
     1966          begin
     1967          AttackResult:=Unit_Attack(uix,AttackLoc);
     1968          Exhausted:= ((AttackResult and rExecuted)=0)
     1969            or ((AttackResult and rUnitRemoved)<>0);
     1970          end;
     1971      end
     1972    else Exhausted:=true;
     1973  until Exhausted;
     1974  end;
     1975end; // CheckAttack
     1976
     1977
     1978procedure TAI.Patrol(uix: integer);
     1979const
     1980DistanceScore=4;
     1981var
     1982PatrolScore,BestCount,PatrolLoc,TestLoc,NextLoc,TestTime,V8,
     1983  TestScore,OldLoc,MoveResult,MoveStyle,NextTime,RecoverTurns: integer;
     1984Tile: Cardinal;
     1985Exhausted,CaptureOnly: boolean;
     1986Adjacent: TVicinity8Loc;
     1987AdjacentUnknown: array[0..lxmax*lymax-1] of shortint;
     1988
     1989begin
     1990with MyUnit[uix] do
     1991  begin
     1992  CaptureOnly:= ((100-Health)*Terrain[Map[Loc] and fTerrain].Defense>60)
     1993    and not (Map[Loc] and fTerrain in [fOcean, fShore, fArctic, fDesert]);
     1994  MoveStyle:=GetMyMoveStyle(mix, Health);
     1995  repeat
     1996    PatrolScore:=-999999;
     1997    PatrolLoc:=-1;
     1998    FillChar(AdjacentUnknown,MapSize,$FF); // -1, indicates tiles not checked yet
     1999    Pile.Create(MapSize);
     2000    Pile.Put(Loc, $800-Movement);
     2001    while Pile.Get(TestLoc,TestTime) do
     2002      begin
     2003      if (50*$1000-DistanceScore*TestTime<=PatrolScore) // assume a score of 50 is the best achievable
     2004        or CaptureOnly and (TestTime>=$1000) then
     2005        break;
     2006
     2007      TestScore:=0;
     2008      Tile:=Map[TestLoc];
     2009      AdjacentUnknown[TestLoc]:=0;
     2010
     2011      if ((Tile and fUnit)<>0) and ((Tile and fOwned)=0) then
     2012        // enemy unit
     2013
     2014      else if ((Tile and fCity)<>0) and ((Tile and fOwned)=0) then
     2015        begin
     2016        if ((Tile and fObserved)<>0)
     2017          and (MyModel[mix].Domain=dGround) and (MyModel[mix].Attack>0)
     2018          and ((RO.Territory[TestLoc]<0) // happens only for unobserved cities of extinct tribes, new owner unknown
     2019            or (RO.Treaty[RO.Territory[TestLoc]]<trPeace)) then
     2020          TestScore:=40 // unfriendly undefended city -- capture!
     2021        end
     2022
     2023      else
    3012024        begin // no enemy city or unit here
    302           // add surrounding tiles to queue, but only if there's a chance to beat BestScore
    303           if 50 - DistanceScore * (TestDistance + 1) >= BestScore then
    304           // assume a score of 50 is the best achievable
     2025        V8_to_Loc(TestLoc,Adjacent);
     2026        for V8:=0 to 7 do
    3052027          begin
    306             V8_to_Loc(TestLoc, Adjacent);
    307             for V8 := 0 to 7 do
     2028          NextLoc:=Adjacent[V8];
     2029          if (NextLoc>=0) and (AdjacentUnknown[NextLoc]<0) then
     2030            if Map[NextLoc] and fTerrain=fUNKNOWN then
     2031              inc(AdjacentUnknown[TestLoc])
     2032            else if Formation[NextLoc]=Formation[TestLoc] then
     2033              case CheckStep(MoveStyle, TestTime, V8 and 1, NextTime, RecoverTurns, Map[TestLoc], Map[NextLoc], true) of
     2034                csOk:
     2035                  Pile.Put(NextLoc, NextTime);
     2036                csForbiddenTile:
     2037                  AdjacentUnknown[NextLoc]:=0; // don't check moving there again
     2038                csCheckTerritory:
     2039                  if RO.Territory[NextLoc]=RO.Territory[TestLoc] then
     2040                    Pile.Put(NextLoc, NextTime);
     2041                end
     2042          end;
     2043        if not CaptureOnly then
     2044          if AdjacentUnknown[TestLoc]>0 then
     2045            TestScore:=20+AdjacentUnknown[TestLoc]
     2046          else TestScore:=(RO.Turn-RO.MapObservedLast[TestLoc]) div 16;
     2047        end; // no enemy city or unit here
     2048
     2049      if TestScore>0 then
     2050        begin
     2051        TestScore:=TestScore*$1000-DistanceScore*TestTime;
     2052        if TestScore>PatrolScore then
     2053          BestCount:=0;
     2054        if TestScore>=PatrolScore then
     2055          begin
     2056          inc(BestCount);
     2057          if random(BestCount)=0 then
    3082058            begin
    309               NextLoc := Adjacent[V8];
    310               if (NextLoc >= 0) and (NextLoc < MapSize) and
    311                 (AdjacentUnknown[NextLoc] < 0) then // tile not checked yet
    312               begin
    313                 TerrType := Map[NextLoc] and fTerrain;
    314                 if TerrType = fUNKNOWN then
    315                   inc(AdjacentUnknown[TestLoc])
    316                 else
    317                 begin
    318                   case MyModel[mix].Domain of
    319                     dGround:
    320                       begin
    321                         TerrOwner := RO.Territory[NextLoc];
    322                         if (TerrType >= fGrass) and (TerrType <> fArctic)
    323                         // terrain can be walked
    324                           and ((TerrOwner < 0) or (TerrOwner = me) or
    325                           (RO.Treaty[TerrOwner] < trPeace))
    326                         // no peace treaty violated
    327                           and (((Map[NextLoc] and (fUnit or fCity)) <> 0) or
    328                           (Map[TestLoc] and Map[NextLoc] and fInEnemyZoC = 0))
    329                         then // no ZoC violated
    330                         begin // yes, consider walking this tile
    331                           if TerrType = fMountains then
    332                             StepSize := 2 // mountains cause delay
    333                           else
    334                             StepSize := 1
    335                         end
    336                         else
    337                           StepSize := 0 // no, don't walk here
    338                       end;
    339                     dSea:
    340                       if TerrType = fShore then
    341                       // ships of this AI can only move along shore
    342                         StepSize := 1
    343                       else
    344                         StepSize := 0;
    345                     dAir:
    346                       StepSize := 1;
    347                   end;
    348                   if StepSize > 0 then
    349                     Pile.Put(NextLoc, TestDistance + StepSize)
    350                 end
    351               end;
    352             end;
    353           end;
    354           if Role = Defend then
    355             TestScore := 0 // don't discover/patrol
    356           else if AdjacentUnknown[TestLoc] > 0 then
    357           begin
    358             TestScore := 20 + AdjacentUnknown[TestLoc];
    359             TestTask := utDiscover
    360           end
    361           else
    362           begin
    363             TestScore := (RO.Turn - RO.MapObservedLast[TestLoc]) div 10;
    364             TestTask := utPatrol
    365           end
    366         end; // no enemy city or unit here
    367 
    368         if TestScore > 0 then
    369         begin
    370           TestScore := TestScore - DistanceScore * TestDistance;
    371           if TestScore > BestScore then
    372             BestCount := 0;
    373           if TestScore >= BestScore then
    374           begin
    375             inc(BestCount);
    376             if random(BestCount) = 0 then
    377             begin
    378               BestScore := TestScore;
    379               BestLoc := TestLoc;
    380               BestTask := TestTask;
     2059            PatrolScore:=TestScore;
     2060            PatrolLoc:=TestLoc;
    3812061            end
    3822062          end;
    3832063        end
    384       end;
    385 
    386       if (BestTask = utNone) and ((Map[Loc] and fCity) = 0) then
    387       begin // nothing to do, move home
    388         if Home >= 0 then
    389           BestLoc := MyCity[Home].Loc
    390         else
    391           BestLoc := maNextCity;
    392         BestTask := utGoHome;
    393       end;
    394       if BestTask <> utNone then
     2064      end; // while Pile.Get
     2065    Pile.Free;
     2066
     2067    if PatrolLoc>=0 then
    3952068      begin // attack/capture/discover/patrol task found, execute it
    396         OldLoc := Loc;
    397         MoveResult := Unit_Move(uix, BestLoc);
    398         Exhausted := (Loc = OldLoc) or
    399           ((MoveResult and (rMoreTurns or rUnitRemoved)) <> 0);
    400         if (BestTask = utAttack) and ((MoveResult and rLocationReached) <> 0)
    401         then
    402           if Movement < 100 then
    403             Exhausted := true
    404           else
    405           begin
    406             AttackResult := Unit_Attack(uix, BestLoc);
    407             Exhausted := ((AttackResult and rExecuted) = 0) or
    408               ((AttackResult and rUnitRemoved) <> 0);
    409           end;
    410         if not Exhausted then
    411           Exhausted := (Movement < 100) and
    412             ((Map[Loc] and (fRoad or fRR or fRiver or fCity)) = 0);
    413         // no road, too few movement points for further movement
    414       end
    415       else
    416         Exhausted := true;
    417     until Exhausted;
    418   Pile.Free;
    419 end; // ProcessUnit
    420 
    421 // SetCityProduction: choose production of each city
     2069      OldLoc:=Loc;
     2070      MoveResult:=Unit_Move(uix,PatrolLoc);
     2071      Exhausted:= (Loc=OldLoc)
     2072        or ((MoveResult and (rMoreTurns or rUnitRemoved))<>0);
     2073      end
     2074    else Exhausted:=true;
     2075  until Exhausted;
     2076  end;
     2077end; // Patrol
     2078
     2079procedure TAI.AttackAndPatrol;
     2080const
     2081nAttackCatOrder=3;
     2082AttackCatOrder: array[0..nAttackCatOrder-1] of integer=
     2083(mctGroundAttacker, mctCruiser, mctGroundDefender);
     2084var
     2085iCat,uix,uix1: integer;
     2086IsPatrolUnit,Fortified: boolean;
     2087begin
     2088for uix:=0 to RO.nUn-1 do with MyUnit[uix] do // utilize militia
     2089  if (Loc>=0) and (mix=mixMilitia)
     2090    and ((Formation[Loc]<0) or (Formation[Loc]>=maxCOD)
     2091      or (ContinentPresence[Formation[Loc]] and PresenceUnknown=0)) then
     2092    Unit_Disband(uix);
     2093
     2094if RO.nEnemyUn>0 then
     2095  for iCat:=0 to nAttackCatOrder-1 do
     2096    for Fortified:=false to true do
     2097      for uix:=RO.nUn-1 downto 0 do with MyUnit[uix] do
     2098        if (Loc>=0) and (ModelCat[mix]=AttackCatOrder[iCat])
     2099          and (MyModel[mix].Attack>0)
     2100          and ((Flags and unFortified<>0) = Fortified) then
     2101          CheckAttack(uix);
     2102
     2103fillchar(uixPatrol, sizeof(uixPatrol), $FF);
     2104for uix:=0 to RO.nUn-1 do with MyUnit[uix],MyModel[mix] do
     2105  if (Loc>=0) and (Domain=dGround) and (Attack>0) and (Speed>=250)
     2106    and (Map[Loc] and fTerrain>=fGrass)
     2107    and (Formation[Loc]>=0) and (Formation[Loc]<maxCOD)
     2108    and ((uixPatrol[Formation[Loc]]<0)
     2109      or (MyUnit[uix].ID<MyUnit[uixPatrol[Formation[Loc]]].ID)) then
     2110      uixPatrol[Formation[Loc]]:=uix;
     2111
     2112for uix:=0 to RO.nUn-1 do with MyUnit[uix] do if Loc>=0 then
     2113  begin
     2114  if mix=mixMilitia then
     2115    if (RO.nUn<3) and (RO.nCity=1) or (Map[Loc] and fCity=0) then
     2116      IsPatrolUnit:=true
     2117    else
     2118      begin // militia
     2119      IsPatrolUnit:=false;
     2120      for uix1:=0 to RO.nUn-1 do
     2121        if (uix1<>uix) and (MyUnit[uix1].Loc=Loc)
     2122          and (MyUnit[uix1].mix<>mixSettlers) then
     2123          IsPatrolUnit:=true
     2124      end
     2125  else IsPatrolUnit:=(mix=mixCruiser)
     2126    or (Map[Loc] and fTerrain>=fGrass)
     2127    and (Formation[Loc]>=0) and (Formation[Loc]<maxCOD)
     2128    and (uix=uixPatrol[Formation[Loc]]);
     2129  if IsPatrolUnit then Patrol(uix);
     2130  end
     2131end; // AttackAndPatrol
     2132
     2133
     2134function TAI.HavePort: boolean;
     2135var
     2136V8, cix,AdjacentLoc,f: integer;
     2137Adjacent: TVicinity8Loc;
     2138begin
     2139result:=false;
     2140for cix:=0 to RO.nCity-1 do with MyCity[cix] do if Loc>=0 then
     2141  begin
     2142  V8_to_Loc(Loc,Adjacent);
     2143  for V8:=0 to 7 do
     2144    begin
     2145    AdjacentLoc:=Adjacent[V8];
     2146    if (AdjacentLoc>=0) and ((Map[AdjacentLoc] and fTerrain)<fGrass) then
     2147      begin
     2148      f:=Formation[AdjacentLoc];
     2149      if (f>=0) and (f<maxCOD) and (OceanPresence[f] and not (1 shl me)<>0) then
     2150        result:=true;
     2151      end
     2152    end;
     2153  end
     2154end;
     2155
     2156
    4222157procedure TAI.SetCityProduction;
    4232158var
    424   cix, mix, mixSettler, mixShip, mixArmy, V8, NewImprovement, count, wix,
    425     AdjacentLoc: integer;
    426   IsPort: boolean;
    427   Adjacent: TVicinity8Loc;
    428   Report: TCityReport;
     2159uix,cix,iix,dtr,V8,V21,NewImprovement,AdjacentLoc,MaxSettlers,
     2160  maxcount,cixMilAcademy: integer;
     2161TerrType: cardinal;
     2162IsPort,IsNavalBase,NeedCruiser,CheckProd,Destructed,ProduceSettlers,ProduceMil: boolean;
     2163Adjacent: TVicinity8Loc;
     2164Radius: TVicinity21Loc;
     2165Report: TCityReport;
     2166HomeCount, CityProdRep: array[0..nCmax-1] of integer;
     2167MilProdCity: array[0..nCmax-1] of boolean;
    4292168
    4302169  procedure TryBuild(Improvement: integer);
    4312170  begin
    432     if (NewImprovement < 0) // already improvement of higher priority found
    433       and (MyCity[cix].Built[Improvement] = 0) // not built yet
    434       and City_Improvable(cix, Improvement) then
    435       NewImprovement := Improvement;
     2171  if (NewImprovement=imTrGoods) // not already improvement of higher priority found
     2172    and (MyCity[cix].Built[Improvement]=0) // not built yet
     2173    and ((Imp[Improvement].Preq=preNone)
     2174      or (RO.Tech[Imp[Improvement].Preq]>=tsApplicable))
     2175    and City_Improvable(cix, Improvement) then
     2176    NewImprovement:=Improvement;
     2177  end;
     2178
     2179  procedure TryDestruct(Improvement: integer);
     2180  begin
     2181  if Destructed or (MyCity[cix].Built[Improvement]=0) then exit;
     2182  if City_CurrentImprovementProject(cix)>=0 then
     2183    City_RebuildImprovement(cix,Improvement)
     2184  else City_SellImprovement(cix, Improvement);
     2185{    if (CurrentImprovementProject>=0)
     2186      and (Imp[CurrentImprovementProject].Kind in [ikCommon,ikNatGlobal,ikNatLocal])
     2187      and ((Imp[CurrentImprovementProject].Cost*3-Imp[Improvement].Cost*2)
     2188      *BuildCostMod[G.Difficulty[me]]>MyCity[cix].Prod*(12*3)) then}
     2189  Destructed:=true
     2190  end;
     2191
     2192  function ChooseBuildModel(Cat: integer): integer;
     2193  var
     2194  count, mix: integer;
     2195  begin
     2196  count:=0;
     2197  for mix:=0 to RO.nModel-1 do
     2198    if (ModelCat[mix]=Cat)
     2199      and (ModelQuality[mix]>=ModelBestQuality[Cat]-MaxBuildWorseThanBestModel) then
     2200      begin inc(count); if random(count)=0 then result:=mix end;
     2201  assert(count>0);
     2202  end;
     2203
     2204  procedure NominateMilProdCities;
     2205  // find military production cities
     2206  var
     2207  cix, Total, d, Threshold, NewThreshold, Share, SharePlus, cixWorst: integer;
     2208  begin
     2209  fillchar(MilProdCity, RO.nCity, 0);
     2210  GetCityProdPotential;
     2211  for d:=0 to maxCOD-1 do
     2212    begin
     2213    Total:=0;
     2214    for cix:=0 to RO.nCity-1 do with MyCity[cix] do
     2215      if (Loc>=0) and (District[Loc]=d) then
     2216        Total:=Total+CityResult[cix];
     2217    if Total=0 then continue; // district does not exist
     2218
     2219    Share:=0;
     2220    cixWorst:=-1;
     2221    for cix:=0 to RO.nCity-1 do with MyCity[cix] do
     2222      if (Loc>=0) and (District[Loc]=d)
     2223        and (Built[imBarracks]+Built[imMilAcademy]>0) then
     2224        begin
     2225        MilProdCity[cix]:=true;
     2226        inc(Share,CityResult[cix]);
     2227        if (cixWorst<0) or (CityResult[cix]<CityResult[cixWorst]) then
     2228          cixWorst:=cix
     2229        end;
     2230
     2231    Threshold:=$FFFF;
     2232    while (Threshold>0) and (Share<Total*MilProdShare div 100) do
     2233      begin
     2234      NewThreshold:=-1;
     2235      for cix:=0 to RO.nCity-1 do with MyCity[cix] do
     2236        if (Loc>=0) and (District[Loc]=d)
     2237          and (Built[imBarracks]+Built[imMilAcademy]=0) and (Built[imObservatory]=0)
     2238          and (CityResult[cix]<Threshold)
     2239          and (CityResult[cix]>=NewThreshold) then
     2240          if CityResult[cix]>NewThreshold then
     2241            begin
     2242            NewThreshold:=CityResult[cix];
     2243            SharePlus:=CityResult[cix]
     2244            end
     2245          else inc(SharePlus,CityResult[cix]);
     2246      Threshold:=NewThreshold;
     2247      inc(Share,SharePlus);
     2248      end;
     2249
     2250    for cix:=0 to RO.nCity-1 do with MyCity[cix] do
     2251      if (Loc>=0) and (District[Loc]=d)
     2252        and (Built[imBarracks]+Built[imMilAcademy]=0)
     2253        and (CityResult[cix]>=Threshold) then
     2254        MilProdCity[cix]:=true;
     2255{    if (cixWorst>=0)
     2256      and (Share-CityResult[cixWorst]*2>=Total*MilProdShare div 100) then
     2257      MilProdCity[cixWorst]:=false;}
     2258    end;
     2259
     2260  // check best city for military academy
     2261  cixMilAcademy:=cixStateImp[imMilAcademy];
     2262  if cixStateImp[imPalace]>=0 then
     2263    begin
     2264    d:=District[MyCity[cixStateImp[imPalace]].Loc];
     2265    if (d>=0) and (d<maxCOD) then
     2266      begin
     2267      cixMilAcademy:=-1;
     2268      for cix:=0 to RO.nCity-1 do with MyCity[cix] do
     2269        if (Loc>=0) and (District[Loc]=d)
     2270          and (Built[imObservatory]+Built[imPalace]=0)
     2271          and ((cixMilAcademy<0) or (CityResult[cix]>CityResult[cixMilAcademy])) then
     2272          cixMilAcademy:=cix;
     2273      end;
     2274    if (cixMilAcademy>=0) and (cixStateImp[imMilAcademy]>=0)
     2275      and (cixMilAcademy<>cixStateImp[imMilAcademy])
     2276      and (MyCity[cixStateImp[imMilAcademy]].Built[imObservatory]=0)
     2277      and (CityResult[cixMilAcademy]<=CityResult[cixStateImp[imMilAcademy]]*3 div 2) then
     2278      cixMilAcademy:=cixStateImp[imMilAcademy] // because not so much better
     2279    end
     2280  end;
     2281
     2282  procedure ChangeHomeCities;
     2283  var
     2284  uix,NewHome,HomeSupport,NewHomeSupport,SingleSupport: integer;
     2285  begin
     2286  if RO.Government in [gAnarchy, gFundamentalism] then exit;
     2287  for uix:=0 to RO.nUn-1 do with MyUnit[uix] do
     2288    if (Loc>=0) and (Home>=0) and (Map[Loc] and fCity<>0)
     2289      and (MyCity[Home].Loc<>Loc) and (MyModel[mix].Kind<>mkSettler) then
     2290      begin
     2291      City_FindMyCity(Loc, NewHome);
     2292      case RO.Government of
     2293        gDespotism:
     2294          begin
     2295          HomeSupport:=HomeCount[Home]-MyCity[Home].Size;
     2296          NewHomeSupport:=HomeCount[NewHome]-MyCity[NewHome].Size;
     2297          end;
     2298        gMonarchy, gCommunism:
     2299          begin
     2300          HomeSupport:=HomeCount[Home]-MyCity[Home].Size div 2;
     2301          NewHomeSupport:=HomeCount[NewHome]-MyCity[NewHome].Size div 2;
     2302          end;
     2303        else
     2304          begin
     2305          HomeSupport:=HomeCount[Home];
     2306          NewHomeSupport:=HomeCount[NewHome];
     2307          end;
     2308        end;
     2309      if HomeSupport>0 then
     2310        begin
     2311        if MyModel[mix].Flags and mdDoubleSupport=0 then SingleSupport:=1
     2312        else SingleSupport:=2;
     2313        HomeSupport:=HomeSupport-SingleSupport;
     2314        NewHomeSupport:=NewHomeSupport+SingleSupport;
     2315        if HomeSupport<0 then HomeSupport:=0;
     2316        if NewHomeSupport<0 then NewHomeSupport:=0;
     2317        if (NewHomeSupport<=0)
     2318          or (CityProdRep[Home]-HomeSupport<=CityProdRep[NewHome]-NewHomeSupport) then
     2319          begin
     2320          dec(HomeCount[Home],SingleSupport);
     2321          inc(HomeCount[NewHome],SingleSupport);
     2322          Unit_SetHomeHere(uix)
     2323          end
     2324        end
     2325      end
    4362326  end;
    4372327
    4382328begin
    439   // only produce newest models
    440   mixSettler := -1;
    441   mixArmy := -1;
    442   mixShip := -1;
    443   for mix := 0 to RO.nModel - 1 do
    444     with MyModel[mix] do
    445       if Kind = mkSettler then
    446         mixSettler := mix
    447       else if (Domain = dGround) and (Kind < mkSpecial_TownGuard) then
    448         mixArmy := mix
    449       else if Domain = dSea then
    450         mixShip := mix;
    451 
    452   for cix := 0 to RO.nCity - 1 do
    453     with MyCity[cix] do
    454       if (RO.Turn = 0) or ((Flags and chProduction) <> 0)
    455       // city production complete
    456         or not City_HasProject(cix) then
     2329fillchar(HomeCount, 4*RO.nCity, 0);
     2330for uix:=0 to RO.nUn-1 do with MyUnit[uix] do
     2331  if (Loc>=0) and (Home>=0) then
     2332    if MyModel[mix].Flags and mdDoubleSupport=0 then
     2333      inc(HomeCount[Home])
     2334    else inc(HomeCount[Home],2);
     2335
     2336NominateMilProdCities;
     2337
     2338for cix:=0 to RO.nCity-1 do with MyCity[cix] do
     2339  if (Loc>=0) and (Flags and chCaptured=0) and (District[Loc]>=0) then
     2340    begin
     2341    if size<4 then
     2342      City_OptimizeTiles(cix,rwMaxGrowth)
     2343    else City_OptimizeTiles(cix,rwForceProd);
     2344
     2345    City_GetReport(cix, Report);
     2346    CityProdRep[cix]:=Report.ProdRep;
     2347
     2348    Destructed:=false;
     2349    CheckProd:= (RO.Turn=0) or ((Flags and chProduction)<>0) // city production complete
     2350      or not City_HasProject(cix);
     2351    if not CheckProd then
     2352      begin // check whether producing double state improvement or wonder
     2353      iix:=City_CurrentImprovementProject(cix);
     2354      if (iix>=0)
     2355        and (((Imp[iix].Kind in [ikNatLocal,ikNatGlobal]) and (RO.NatBuilt[iix]>0))
     2356          or ((Imp[iix].Kind=ikWonder) and (RO.Wonder[iix].CityID<>-1))) then
     2357        CheckProd:=true;
     2358      end;
     2359    if CheckProd then
    4572360      begin // check production
    458         IsPort := false;
    459         V8_to_Loc(Loc, Adjacent);
    460         for V8 := 0 to 7 do
    461         begin
    462           AdjacentLoc := Adjacent[V8];
    463           if (AdjacentLoc >= 0) and (AdjacentLoc < MapSize) and
    464             ((Map[AdjacentLoc] and fTerrain) = fShore) then
    465             IsPort := true; // shore tile at adjacent location -- city is port!
     2361      IsPort:=false;
     2362      IsNavalBase:=false;
     2363      NeedCruiser:=false;
     2364      V8_to_Loc(Loc,Adjacent);
     2365      for V8:=0 to 7 do
     2366        begin
     2367        AdjacentLoc:=Adjacent[V8];
     2368        if (AdjacentLoc>=0) and ((Map[AdjacentLoc] and fTerrain)<fGrass) then
     2369          begin
     2370          IsPort:=true; // shore tile at adjacent location -- city is port!
     2371          if (Formation[AdjacentLoc]>=0) and (Formation[AdjacentLoc]<maxCOD)
     2372            and (OceanPresence[Formation[AdjacentLoc]] and WarNations<>0) then
     2373            begin
     2374            IsNavalBase:=true;
     2375            if (1 shl Formation[AdjacentLoc]) and OceanWithShip=0 then
     2376              NeedCruiser:=true
     2377            end
     2378          end
    4662379        end;
    467         City_GetReport(cix, Report);
    468 
    469         if (Report.Support = 0) or (SupportFree[RO.Government] < 2) and
    470           (Report.Support < Report.ProdRep div 2) then
    471         begin // enough material to support more units
    472           if (RO.Turn > 4) and
    473             ((Report.Eaten - Size * 2) div SettlerFood[RO.Government] <
    474             Size div 4) then
    475             // less than 1 settler per 4 citizens -- produce more!
    476             City_StartUnitProduction(cix, mixSettler)
    477           else if IsPort and (mixShip >= 0) and (random(2) = 0) then
    478             City_StartUnitProduction(cix, mixShip)
    479           else
    480             City_StartUnitProduction(cix, mixArmy)
     2380
     2381      if RO.Turn=0 then
     2382        begin
     2383        NewImprovement:=-1;
     2384        City_StartUnitProduction(cix,mixMilitia); // militia
    4812385        end
    482         else
    483         begin // check for building a city improvement
    484           NewImprovement := -1;
    485           if Built[imPalace] + Built[imCourt] + Built[imTownHall] = 0 then
     2386      else NewImprovement:=imTrGoods;
     2387
     2388      dtr:=District[Loc]; // formation of city
     2389
     2390      if NewImprovement=imTrGoods then
     2391        begin
     2392        if (Built[imPalace]+Built[imCourt]+Built[imTownHall]=0) then
     2393          TryBuild(imTownHall);
     2394        end;
     2395
     2396      if (NewImprovement=imTrGoods)
     2397        and (RO.Government=gDespotism) and (Report.Support=0) then
     2398        begin // produce town guard
     2399        NewImprovement:=-1;
     2400        City_StartUnitProduction(cix,mixTownGuard);
     2401        end;
     2402
     2403      if NewImprovement=imTrGoods then
     2404        begin
     2405        if RO.Government=gDespotism then maxcount:=Size
     2406        else maxcount:=Size div 2;
     2407
     2408        if IsResearched(adRailroad) and (mixSettlers=0) // better wait for engineers
     2409          or (Built[imColosseum]+Built[imObservatory]>0) then
     2410          MaxSettlers:=1
     2411        else MaxSettlers:=(Size+2) div 6;
     2412        ProduceSettlers:=(HomeCount[cix]<maxcount+Size div 2)
     2413          and ((Report.Eaten-Size*2) div SettlerFood[RO.Government]<MaxSettlers)
     2414          and ((dtr<0) or (dtr>=maxCOD) or (SettlerSurplus[dtr]<=0));
     2415
     2416        ProduceMil:=(HomeCount[cix]<maxcount+Size div 2)
     2417          and (Built[imBarracks]+Built[imMilAcademy]>0)
     2418          and ((ModelBestQuality[mctGroundDefender]>0)
     2419            or (ModelBestQuality[mctGroundAttacker]>0))
     2420          and ((dtr<maxCOD)
     2421            and ((UnitLack[dtr,mctGroundAttacker]>0)
     2422              or (UnitLack[dtr,mctGroundDefender]>0))
     2423            or (HomeCount[cix]<maxcount));
     2424
     2425        if ProduceMil or not ProduceSettlers and (HomeCount[cix]<maxcount) then
    4862426          begin
    487             TryBuild(imCourt);
    488             TryBuild(imTownHall);
     2427          NewImprovement:=-1;
     2428          if (dtr>=maxCOD)
     2429            or (ModelBestQuality[mctGroundDefender]=0)
     2430            or (UnitLack[dtr,mctGroundAttacker]
     2431              >=UnitLack[dtr,mctGroundDefender]) then
     2432            City_StartUnitProduction(cix,ChooseBuildModel(mctGroundAttacker))
     2433          else City_StartUnitProduction(cix,ChooseBuildModel(mctGroundDefender))
     2434          end
     2435        else if ProduceSettlers then
     2436          begin
     2437          NewImprovement:=-1;
     2438          City_StartUnitProduction(cix,mixSettlers);
     2439          end
     2440        end;
     2441
     2442      if NewImprovement>=0 then
     2443        begin // produce improvement
     2444        if (RO.Turn>=40) and (Report.Happy*2<=Size)
     2445          and (Built[imColosseum]=0) then
     2446          TryBuild(imTemple);
     2447        if cix=cixMilAcademy then
     2448          TryBuild(imMilAcademy)
     2449        else if ((Built[imPalace]>0) or MilProdCity[cix] and (Built[imTemple]>0))
     2450          and (Built[imObservatory]=0) then
     2451          TryBuild(imBarracks);
     2452        if Report.Trade-Report.Corruption>=11 then
     2453          TryBuild(imLibrary);
     2454        if Report.Trade-Report.Corruption>=11 then
     2455          TryBuild(imMarket);
     2456        if (Report.Trade-Report.Corruption>=11) and (Report.Happy>=4) then
     2457          TryBuild(imUniversity);
     2458        if (Built[imPalace]>0) and (Report.Trade-Report.Corruption>=11)
     2459          and (Report.Happy>=4) and (RO.NatBuilt[imObservatory]=0) then
     2460          TryBuild(imObservatory); // always build observatory in capital
     2461        if (Report.Trade-Report.Corruption>=15) and (Report.Happy>=4) then
     2462          TryBuild(imResLab);
     2463        if (Size>=9) and (Built[imPalace]+Built[imCourt]>0) then
     2464          TryBuild(imHighways);
     2465        if (RO.Government<>gDespotism) and (Report.Happy*2<=Size)
     2466          and (Built[imCathedral]+Built[imTheater]+Built[imColosseum]=0) then
     2467          begin
     2468          TryBuild(imCathedral);
     2469          TryBuild(imTheater);
    4892470          end;
    490           if Report.Trade - Report.Corruption >= 11 then
    491             TryBuild(imLibrary);
    492           if Report.Trade - Report.Corruption >= 11 then
    493             TryBuild(imMarket);
    494           if Size >= 9 then
    495             TryBuild(imHighways);
    496           if (RO.Government <> gDespotism) and (Size >= 4) then
    497             TryBuild(imTemple);
    498           if (RO.Government <> gDespotism) and (Size >= 6) then
    499             TryBuild(imTheater);
    500           if (RO.Government <> gDespotism) and (Size >= 8) then
    501             TryBuild(imAqueduct);
    502           if (Report.ProdRep >= 4) or (RO.nCity = 1) then
    503             TryBuild(imBarracks);
    504           TryBuild(imWalls);
    505           if IsPort then
    506             TryBuild(imCoastalFort);
    507           if NewImprovement < 0 then
    508           begin // nothing to produce -- check for building a wonder
    509             count := 0;
    510             for wix := 0 to nImp - 1 do
    511               if (Imp[wix].Kind = ikWonder) and (RO.Wonder[wix].CityID = -1)
    512               // not built yet
    513                 and ((Report.ProdRep - Report.Support) * 40 >= Imp[wix].Cost)
    514               // takes less than 40 turns to produce
    515                 and City_Improvable(cix, wix) then
    516               begin
    517                 inc(count);
    518                 if random(count) = 0 then
    519                   NewImprovement := wix // yes, build this wonder!
    520               end;
     2471        if (RO.Government<>gDespotism) and (Size>=NeedAqueductSize) then
     2472          TryBuild(imAqueduct);
     2473        if (Built[imColosseum]+Built[imObservatory]>0) and (Size>=NeedSewerSize) then
     2474          TryBuild(imSewer);
     2475        if (RO.NatBuilt[imGrWall]=0) and (Built[imObservatory]+Built[imMilAcademy]=0)
     2476          and (RO.nCity>=6) and (cixStateImp[imPalace]>=0)
     2477          and (Formation[Loc]=Formation[MyCity[cixStateImp[imPalace]].Loc])
     2478          and (Report.ProdRep-Report.Support>=6) then
     2479          TryBuild(imGrWall);
     2480  //        if Map[Loc] and fGrWall=0 then
     2481  //          TryBuild(imWalls);
     2482  //        if IsNavalBase then
     2483  //          TryBuild(imCoastalFort);
     2484        if (RO.NatBuilt[imSpacePort]=0) and (Built[imObservatory]+Built[imMilAcademy]=0)
     2485          and (Report.ProdRep-Report.Support>=10) then
     2486          TryBuild(imSpacePort);
     2487        if Report.ProdRep>=8 then
     2488          TryBuild(imFactory);
     2489        if Report.ProdRep>=12 then
     2490          TryBuild(imMfgPlant);
     2491        if IsPort then
     2492          if Size>8 then
     2493            TryBuild(imHarbor)
     2494          else if (Built[imHarbor]=0) and (Size>4)
     2495            and ((Size and 1<>0) and (Report.Happy*2>Size)
     2496              or (Built[imColosseum]>0)) then
     2497            begin // check building harbor
     2498            V21_to_Loc(Loc,Radius);
     2499            for V21:=1 to 26 do // city is in growth mode - using any 1-food tile?
     2500              if Tiles and (1 shl V21)<>0 then
     2501                begin
     2502                TerrType:=Map[Radius[V21]] and (fTerrain or fSpecial);
     2503                if TerrType in [fDesert,fTundra,fSwamp,fForest,fHills,fMountains] then
     2504                  begin TryBuild(imHarbor); break end
     2505                end
     2506            end;
     2507        if (Size<=10) and (Report.FoodRep-Report.Eaten<2) and
     2508          (Report.Happy*2>=Size+2) then
     2509          TryBuild(imSuperMarket);
     2510
     2511        // less important
     2512        if (Built[imPalace]>0) and (RO.NatBuilt[imColosseum]=0)
     2513          and (Size>=10) then
     2514          TryBuild(imColosseum); // always build colosseum in capital
     2515        if (Built[imPalace]+Built[imCourt]=0)
     2516          and ((Report.Corruption>2) or IsResearched(Imp[imHighways].Preq)) then
     2517          TryBuild(imCourt); // replace courthouse
     2518        if Report.PollRep>=15 then
     2519          TryBuild(imRecycling);
     2520        if (Report.Trade-Report.Corruption>=11)
     2521          and (RO.Money<TotalPopulation[me]*2) then
     2522          TryBuild(imBank);
     2523        if (RO.NatBuilt[imStockEx]=0) and (Built[imObservatory]+Built[imMilAcademy]=0)
     2524          and (Report.ProdRep-Report.Support>=8) then
     2525          TryBuild(imStockEx);
     2526
     2527        // every improvement checked -- start production now
     2528        if NewImprovement<>imTrGoods then
     2529          begin
     2530          if City_StartImprovement(cix, NewImprovement)<rExecuted then
     2531            NewImprovement:=imTrGoods
    5212532          end;
    522           if NewImprovement >= 0 then
    523             City_StartImprovement(cix, NewImprovement)
    524           else if City_HasProject(cix) then
    525             City_StopProduction(cix); // nothing to produce
    526         end
    527       end // check production
     2533        if (NewImprovement=imTrGoods) and (RO.Turn and $F=0) then
     2534          begin // try colony ship parts
     2535          NewImprovement:=imShipComp;
     2536          while (NewImprovement<=imShipHab)
     2537            and ((RO.Tech[Imp[NewImprovement].Preq]<0)
     2538            or (City_StartImprovement(cix, NewImprovement)<rExecuted)) do
     2539            inc(NewImprovement);
     2540          if NewImprovement>imShipHab then NewImprovement:=imTrGoods
     2541          end
     2542        end;
     2543
     2544      if (NewImprovement=imTrGoods) and NeedCruiser and (mixCruiser>=0)
     2545        and (Project and (cpImp or cpIndex)<>mixCruiser)
     2546        and (Report.ProdRep-Report.Support>=6) then
     2547        begin
     2548        NewImprovement:=-1;
     2549        City_StartUnitProduction(cix,mixCruiser);
     2550        end;
     2551
     2552      if (NewImprovement=imTrGoods) and City_HasProject(cix) then
     2553        City_StopProduction(cix);
     2554
     2555      // rebuild imps no longer needed
     2556      if (RO.TaxRate=0) and (RO.Money>=TotalPopulation[me]*4) then
     2557        TryDestruct(imBank)
     2558      else if Report.Happy*2>=Size+6 then
     2559        TryDestruct(imTheater)
     2560      else if Report.Happy*2>=Size+4 then
     2561        TryDestruct(imTemple)
     2562      end;
     2563
     2564    // rebuild imps no longer needed, no report needed
     2565    if (Built[imObservatory]>0)
     2566      or (Project and (cpImp or cpIndex)=cpImp or imObservatory)
     2567      {or not MilProdCity[cix]} then
     2568      TryDestruct(imBarracks);
     2569    if Map[Loc] and fGrWall<>0 then
     2570      TryDestruct(imWalls);
     2571    if Built[imColosseum]>0 then
     2572      begin
     2573      TryDestruct(imTheater);
     2574      TryDestruct(imCathedral);
     2575      TryDestruct(imTemple);
     2576      end;
     2577    end;
     2578
     2579ChangeHomeCities;
    5282580end; // SetCityProduction
    5292581
    530 function TAI.ChooseResearchAdvance: integer;
    531 var
    532   mix: integer;
    533 begin
    534   if not IsResearched(adWheel) then
    535   begin
    536     result := adWheel;
    537     exit
    538   end // research the wheel first
    539   else if not IsResearched(adWarriorCode) then
    540   begin
    541     result := adWarriorCode;
    542     exit
    543   end // research warrior code first
    544   else if not IsResearched(adHorsebackRiding) then
    545   begin
    546     result := adHorsebackRiding;
    547     exit
    548   end; // research horseback riding first
    549 
    550   result := -1; // random advance
    551   if random(10) = 0 then
    552   begin // check military research
    553     result := adMilitary;
    554     if IsResearched(adMapMaking) and (random(2) = 0) then
    555     begin // try to develop new ship
    556       PrepareNewModel(dSea);
    557       SetNewModelFeature(mcDefense, 3);
    558       SetNewModelFeature(mcOffense, RO.DevModel.MaxWeight - 3);
    559     end
    560     else
    561     begin // try to develop new ground unit
    562       PrepareNewModel(dGround);
    563       SetNewModelFeature(mcDefense, 1);
    564       SetNewModelFeature(mcOffense, RO.DevModel.MaxWeight - 4);
    565       SetNewModelFeature(mcMob, 2);
    566     end;
    567 
    568     // don't develop model twice
    569     for mix := 0 to RO.nModel - 1 do
    570       if (RO.DevModel.Domain = MyModel[mix].Domain) and
    571         (RO.DevModel.Attack = MyModel[mix].Attack) and
    572         (RO.DevModel.Defense = MyModel[mix].Defense) then
    573         result := -1; // already have this model
    574   end;
    575 end; // ChooseResearchAdvance
    5762582
    5772583function TAI.ChooseGovernment: integer;
    5782584begin
    579   if IsResearched(adTheRepublic) then
    580     result := gRepublic
    581   else if IsResearched(adMonarchy) then
    582     result := gMonarchy
     2585if Data.BehaviorFlags and bBarbarina<>0 then
     2586  if IsResearched(adTheology) then result:=gFundamentalism
     2587  else result:=gDespotism
     2588else if IsResearched(adDemocracy) then
     2589  result:=gDemocracy //!!!
     2590else if IsResearched(adTheRepublic) then
     2591  result:=gRepublic
     2592else if IsResearched(adMonarchy) then
     2593  result:=gMonarchy
     2594else result:=gDespotism
     2595end;
     2596
     2597
     2598//-------------------------------
     2599//           DIPLOMACY
     2600//-------------------------------
     2601
     2602function TAI.MostWanted(Nation, adGiveAway: integer): integer;
     2603var
     2604ad: integer;
     2605begin
     2606result:=-1;
     2607if RO.Tech[adGiveAway]>=tsApplicable then
     2608  if (adGiveAway=adTheRepublic) and (Data.BehaviorFlags and bGender=bFemale)
     2609    and (RO.Tech[adTheology]<tsSeen) then
     2610    begin
     2611    if RO.EnemyReport[Nation].Tech[adTheology]>=tsApplicable then
     2612      result:=adTheology
     2613    end
     2614  else for ad:=0 to nAdv-5 do // no future techs
     2615      if (AdvanceValue[ad]>0)
     2616        and (RO.Tech[ad]<tsSeen) and (ad<>RO.ResearchTech)
     2617        and (RO.EnemyReport[Nation].Tech[ad]>=tsApplicable)
     2618        and ((Advancedness[adGiveAway]<=Advancedness[ad]+AdvanceValue[ad] shr 8+Compromise)
     2619          or (adGiveAway=adScience) and (Nation=Data.TheologyPartner))
     2620        and ((result<0)
     2621          or ((Advancedness[adGiveAway]+Compromise>=Advancedness[ad]) // acceptable for opponent
     2622            or (ad=adScience))
     2623          and (AdvanceValue[ad]>AdvanceValue[result])
     2624          or (result<>adScience)
     2625          and (Advancedness[adGiveAway]+Compromise<Advancedness[result])
     2626          and (Advancedness[ad]<Advancedness[result]))
     2627        and ((ad<>adTheRepublic) or (Data.BehaviorFlags and bGender=bFemale)
     2628          or (RO.EnemyReport[Nation].Tech[adTheology]>=tsSeen)) then
     2629        result:=ad
     2630end;
     2631
     2632procedure TAI.FindBestTrade(Nation: integer; var adWanted, adGiveAway: integer);
     2633var
     2634i,ad,ead,adTestGiveAway: integer;
     2635begin
     2636adWanted:=-1;
     2637adGiveAway:=-1;
     2638for ead:=0 to nAdv-5 do // no future techs
     2639  if (AdvanceValue[ead]>=$100)
     2640    and (RO.Tech[ead]<tsSeen) and (ead<>RO.ResearchTech)
     2641    and (RO.EnemyReport[Nation].Tech[ead]>=tsApplicable)
     2642    and ((adWanted<0) or (AdvanceValue[ead]>AdvanceValue[adWanted])) then
     2643    begin
     2644    adTestGiveAway:=-1;
     2645    for i:=0 to nRequestedTechs-1 do
     2646      if (Data.RequestedTechs[i]>=0)
     2647        and (Data.RequestedTechs[i] and $FFFF=Nation shl 8+ead) then
     2648        adTestGiveAway:=-2; // already requested before
     2649    if adTestGiveAway=-1 then
     2650      begin
     2651      for ad:=0 to nAdv-5 do // no future techs
     2652        if (RO.Tech[ad]>=tsApplicable)
     2653          and (ad<>RO.EnemyReport[Nation].ResearchTech)
     2654          and (RO.EnemyReport[Nation].Tech[ad]<tsSeen)
     2655          and ((Advancedness[ad]+Compromise>=Advancedness[ead]) or (ead=adScience))
     2656          and (Advancedness[ad]<=Advancedness[ead]+AdvanceValue[ead] shr 8+Compromise)
     2657          and ((adTestGiveAway<0) or (Advancedness[ad]<Advancedness[adTestGiveAway])) then
     2658          adTestGiveAway:=ad;
     2659      if adTestGiveAway>=0 then
     2660        begin
     2661        adWanted:=ead;
     2662        adGiveAway:=adTestGiveAway
     2663        end
     2664      end
     2665    end;
     2666end;
     2667
     2668
     2669function TAI.WantNegotiation(Nation: integer; NegoTime: TNegoTime): boolean;
     2670var
     2671p1,count,adWanted,adGiveAway: integer;
     2672begin
     2673if Data.BehaviorFlags and bBarbarina=bBarbarina then
     2674  begin result:=Barbarina_WantNegotiation(Nation,NegoTime); exit end;
     2675
     2676if RO.Treaty[Nation]<trPeace then
     2677  begin
     2678  if Data.BehaviorFlags and bBarbarina<>0 then
     2679    begin result:=false; exit end;
     2680  count:=0;
     2681  for p1:=0 to nPl-1 do
     2682    if (p1<>me) and (1 shl p1 and RO.Alive<>0) and (RO.Treaty[p1]>=trPeace) then
     2683      inc(count);
     2684  if count>=3 then // enough peace made
     2685    begin result:=false; exit; end
     2686  end;
     2687
     2688NegoCause:=Routine;
     2689case NegoTime of
     2690  EnemyCalled:
     2691    result:=true;
     2692  EndOfTurn:
     2693    if (Data.RejectTurn[suContact,Nation]>=0)
     2694      and (Data.RejectTurn[suContact,Nation]+WaitAfterReject>=RO.Turn) then
     2695      result:=false
     2696    else if RO.Treaty[Nation]<trPeace then
     2697      result:=(Data.RejectTurn[suPeace,Nation]<0)
     2698        or (Data.RejectTurn[suPeace,Nation]+WaitAfterReject<RO.Turn)
     2699    else if RO.Treaty[Nation]=trPeace then
     2700      result:= (Data.BehaviorFlags and bBarbarina=0)
     2701        and ((Data.RejectTurn[suFriendly,Nation]<0)
     2702          or (Data.RejectTurn[suFriendly,Nation]+WaitAfterReject<RO.Turn))
     2703    else
     2704      begin
     2705      FindBestTrade(Nation,adWanted,adGiveAway);
     2706      result:= adWanted>=0;
     2707      end;
     2708  BeginOfTurn:
     2709    if (Data.RejectTurn[suContact,Nation]>=0)
     2710      and (Data.RejectTurn[suContact,Nation]+WaitAfterReject>=RO.Turn) then
     2711      result:=false
     2712    else if (Data.BehaviorFlags and bGender=bMale) and Barbarina_WantCheckNegotiation(Nation) then
     2713      begin NegoCause:=CheckBarbarina; result:=true; end
     2714    else result:=false;
     2715  end;
     2716end;
     2717
     2718procedure TAI.DoNegotiation;
     2719var
     2720i, adWanted, adGiveAway, adToGet, Slot: integer;
     2721BuildFreeOffer: boolean;
     2722begin
     2723if MyLastAction=scDipOffer then
     2724  if OppoAction=scDipAccept then
     2725    begin // evaluate accepted offers
     2726    AdvanceValuesSet:=false;
     2727    if (MyLastOffer.nDeliver=1) and (MyLastOffer.nCost>0)
     2728      and (MyLastOffer.Price[1]=opTech+adTheology) then
     2729      Data.TheologyPartner:=Opponent;
     2730    end
    5832731  else
    584     result := gDespotism
     2732    begin // evaluate rejected offers
     2733    if MyLastOffer.nDeliver+MyLastOffer.nCost=1 then
     2734      if MyLastOffer.Price[0]=opTreaty+trPeace then
     2735        Data.RejectTurn[suPeace,Opponent]:=RO.Turn
     2736      else if MyLastOffer.Price[0]=opTreaty+trFriendlyContact then
     2737        Data.RejectTurn[suFriendly,Opponent]:=RO.Turn;
     2738    end;
     2739if OppoAction=scDipBreak then
     2740  Data.RejectTurn[suContact,Opponent]:=RO.Turn
     2741else if OppoAction=scDipCancelTreaty then
     2742  begin
     2743  case RO.Treaty[Opponent] of
     2744    trNone: Data.RejectTurn[suPeace,Opponent]:=RO.Turn;
     2745    trPeace: Data.RejectTurn[suFriendly,Opponent]:=RO.Turn;
     2746    end;
     2747  end;
     2748
     2749if Data.BehaviorFlags and bBarbarina=bBarbarina then
     2750  begin Barbarina_DoNegotiation; exit end;
     2751
     2752if NegoCause=CheckBarbarina then
     2753  begin Barbarina_DoCheckNegotiation; exit end;
     2754
     2755SetAdvanceValues; // in case no turn played after loading this game
     2756
     2757BuildFreeOffer:=false;
     2758if (OppoAction=scDipStart) or (OppoAction=scDipAccept) then
     2759  BuildFreeOffer:=true
     2760else if (OppoAction=scDipOffer) and (OppoOffer.nDeliver+OppoOffer.nCost=0) then
     2761  BuildFreeOffer:=true
     2762else if OppoAction=scDipOffer then
     2763  begin
     2764  if (Data.BehaviorFlags and bBarbarina=0)
     2765    and (OppoOffer.nDeliver+OppoOffer.nCost=1)
     2766    and (OppoOffer.Price[0] and opMask=opTreaty)
     2767    and (integer(OppoOffer.Price[0]-opTreaty)>RO.Treaty[Opponent])
     2768    and ((OppoOffer.Price[0]-opTreaty<trAlliance) or (RO.Tech[adScience]>=tsSeen)) then
     2769    MyAction:=scDipAccept // accept all treaties
     2770  else if (RO.Treaty[Opponent]>=trPeace)
     2771    and (OppoOffer.nDeliver=1)
     2772    and (OppoOffer.Price[0] and $FFFF0000=opCivilReport+cardinal(Opponent) shl 16)
     2773    and (OppoOffer.nCost=1)
     2774    and (OppoOffer.Price[1] and $FFFF0000=opCivilReport+cardinal(me) shl 16) then
     2775    MyAction:=scDipAccept // accept exchange of civil reports
     2776  else if (OppoOffer.nDeliver=1) and (OppoOffer.nCost=1)
     2777    and (OppoOffer.Price[1] and opMask=opTech) then
     2778    begin // opponent wants tech
     2779    BuildFreeOffer:=true;
     2780    adGiveAway:=OppoOffer.Price[1]-opTech;
     2781    if (OppoOffer.Price[0] and opMask=opTech)
     2782      and (MyLastAction=scDipOffer)
     2783      and (MyLastOffer.nDeliver=1) and (MyLastOffer.nCost=1)
     2784      and (OppoOffer.Price[0]=MyLastOffer.Price[1]) then
     2785      begin // opponent makes counter offer, check whether to accept
     2786      adToGet:=OppoOffer.Price[0]-opTech;
     2787      if (adGiveAway=adTheRepublic) and (Data.BehaviorFlags and bGender=bFemale)
     2788        and (RO.Tech[adTheology]<tsSeen) then
     2789        begin
     2790        if adToGet=adTheology then MyAction:=scDipAccept;
     2791        end
     2792      else if (RO.Tech[adGiveAway]>=tsApplicable) and (RO.Tech[adToGet]<tsSeen)
     2793        and (AdvanceValue[adToGet]>0)
     2794        and ((Advancedness[adGiveAway]<=Advancedness[adToGet]
     2795          +AdvanceValue[adToGet] shr 8+Compromise)
     2796          or (adGiveAway=adScience) and (Opponent=Data.TheologyPartner)) then
     2797        MyAction:=scDipAccept
     2798      end
     2799    else if (OppoOffer.Price[0] and opMask=opChoose)
     2800      or (OppoOffer.Price[0] and opMask=opTech) then
     2801      begin // choose price
     2802      adWanted:=MostWanted(Opponent,OppoOffer.Price[1]-opTech);
     2803      if (OppoOffer.Price[0] and opMask=opTech)
     2804        and (Cardinal(adWanted)=OppoOffer.Price[0]-opTech) then
     2805        MyAction:=scDipAccept // opponent's offer is already perfect
     2806      else if adWanted>=0 then
     2807        begin // make improved counter offer
     2808        MyOffer.nDeliver:=1;
     2809        MyOffer.nCost:=1;
     2810        MyOffer.Price[0]:=OppoOffer.Price[1];
     2811        MyOffer.Price[1]:=opTech+adWanted;
     2812        MyAction:=scDipOffer;
     2813        BuildFreeOffer:=false
     2814        end
     2815      end;
     2816    if MyAction=scDipAccept then BuildFreeOffer:=false
     2817    end
     2818  else BuildFreeOffer:=true
     2819  end;
     2820if (MyAction=scDipAccept) and (OppoAction=scDipOffer) then
     2821  begin
     2822  AdvanceValuesSet:=false;
     2823  if (OppoOffer.nDeliver>0) and (OppoOffer.Price[0]=opTech+adTheology) then
     2824    Data.TheologyPartner:=Opponent
     2825  end;
     2826
     2827if BuildFreeOffer then
     2828  begin
     2829  if (Data.BehaviorFlags and bBarbarina=0)
     2830    and (RO.Treaty[Opponent]<trPeace)
     2831    and ((Data.RejectTurn[suPeace,Opponent]<0)
     2832      or (Data.RejectTurn[suPeace,Opponent]+WaitAfterReject<RO.Turn)) then
     2833    begin
     2834    MyOffer.nDeliver:=1;
     2835    MyOffer.nCost:=0;
     2836    MyOffer.Price[0]:=opTreaty+trPeace;
     2837    MyAction:=scDipOffer
     2838    end
     2839  else if (Data.BehaviorFlags and bBarbarina=0)
     2840    and (RO.Treaty[Opponent]=trPeace)
     2841    and ((Data.RejectTurn[suFriendly,Opponent]<0)
     2842      or (Data.RejectTurn[suFriendly,Opponent]+WaitAfterReject<RO.Turn)) then
     2843    begin
     2844    MyOffer.nDeliver:=1;
     2845    MyOffer.nCost:=0;
     2846    MyOffer.Price[0]:=opTreaty+trFriendlyContact;
     2847    MyAction:=scDipOffer
     2848    end
     2849  else
     2850    begin
     2851    FindBestTrade(Opponent, adWanted, adGiveAway);
     2852    if adWanted>=0 then
     2853      begin
     2854      MyOffer.nDeliver:=1;
     2855      MyOffer.nCost:=1;
     2856      MyOffer.Price[0]:=opTech+adGiveAway;
     2857      MyOffer.Price[1]:=opTech+adWanted;
     2858      MyAction:=scDipOffer;
     2859      for i:=0 to nRequestedTechs-1 do
     2860        if Data.RequestedTechs[i]<0 then
     2861          begin Slot:=i; break end
     2862        else if (i=0) or (Data.RequestedTechs[i] shr 16
     2863          <Data.RequestedTechs[Slot] shr 16) then // find most outdated entry
     2864          Slot:=i;
     2865      Data.RequestedTechs[Slot]:=RO.Turn shl 16+Opponent shl 8+adWanted
     2866      end
     2867    end
     2868  end;
     2869end; // Negotiation
     2870
     2871
     2872procedure SetLeaveOutValue;
     2873  procedure Process(ad: integer);
     2874  var
     2875  i: integer;
     2876  begin
     2877  if LeaveOutValue[ad]<0 then
     2878    begin
     2879    LeaveOutValue[ad]:=0;
     2880    for i:=0 to 1 do if AdvPreq[ad,i]>=0 then
     2881      begin
     2882      Process(AdvPreq[ad,i]);
     2883      if AdvPreq[ad,i] in LeaveOutTechs then
     2884        inc(LeaveOutValue[ad], LeaveOutValue[AdvPreq[ad,i]]+1)
     2885      end
     2886    end
     2887  end;
     2888var
     2889ad: integer;
     2890begin
     2891FillChar(LeaveOutValue,SizeOf(LeaveOutValue),$FF);
     2892for ad:=0 to nAdv-5 do Process(ad);
    5852893end;
    5862894
    5872895
    588 // -------------------------------
    589 // DIPLOMACY
    590 // -------------------------------
    591 
    592 function TAI.WantNegotiation(Nation: integer; NegoTime: TNegoTime): boolean;
    593 begin
    594   result := (NegoTime = EnemyCalled) // always accept contact
    595     or (NegoTime = EndOfTurn) and (RO.Turn mod 20 = Nation + me)
    596   // ask for contact only once in 20 turns
    597 end;
    598 
    599 procedure TAI.DoNegotiation;
    600 begin
    601   if (RO.Treaty[Opponent] < trPeace) and Odd(me + Opponent) then
    602   // make peace with some random nations
    603     if (OppoAction = scDipOffer) and (OppoOffer.nCost = 0) and
    604       (OppoOffer.nDeliver = 1) and (OppoOffer.Price[0] = opTreaty + trPeace)
    605     then
    606       MyAction := scDipAccept // accept peace
    607     else if OppoAction = scDipStart then
    608     begin
    609       MyOffer.nCost := 0;
    610       MyOffer.nDeliver := 1;
    611       MyOffer.Price[0] := opTreaty + trPeace;
    612       // offer peace in exchange of nothing
    613       MyAction := scDipOffer;
    614     end
    615 end;
     2896initialization
     2897RWDataSize:=sizeof(TPersistentData);
     2898SetLeaveOutValue;
    6162899
    6172900end.
     2901
Note: See TracChangeset for help on using the changeset viewer.