source: trunk/Database.pas@ 468

Last change on this file since 468 was 451, checked in by chronos, 3 years ago
  • Added: Support for ARM 32-bit and 64-bit architectures for loading StdAI and other AIs.
File size: 125.3 KB
RevLine 
[38]1{$INCLUDE Switches.inc}
[6]2// {$DEFINE TEXTLOG}
3// {$DEFINE LOADPERF}
[2]4unit Database;
5
6interface
7
8uses
[38]9 SysUtils, Protocol, CmdList;
[2]10
11const
[6]12 // additional test flags
[207]13 //{$DEFINE FastContact} { extra small world with railroad everywhere }
[2]14
[6]15 neumax = 4096;
16 necmax = 1024;
17 nemmax = 1024;
[2]18
[6]19 lNoObserve = 0;
20 lObserveUnhidden = 1;
21 lObserveAll = 2;
22 lObserveSuper = 3; // observe levels
[2]23
[6]24 TerrType_Canalable = [fGrass, fDesert, fPrairie, fTundra, fSwamp,
25 fForest, fHills];
[2]26
[6]27 nStartUn = 1;
[447]28 StartUn: array [0 .. nStartUn - 1] of Integer = (0); // mix of start units
[2]29
[6]30 CityOwnTile = 13;
[2]31
[227]32type
33 TGameMode = (moLoading_Fast, moLoading, moMovie, moPlaying);
34
[2]35var
[144]36 GAlive: Integer; { players alive; bitset of 1 shl p }
37 GWatching: Integer;
38 GInitialized: Integer;
39 GAI: Integer;
40 RND: Integer; { world map randseed }
41 lx: Integer;
42 ly: Integer;
43 MapSize: Integer; // = lx*ly
44 LandMass: Integer;
[6]45{$IFOPT O-}InvalidTreatyMap, {$ENDIF}
[144]46 SaveMapCenterLoc: Integer;
47 PeaceEnded: Integer;
48 GTurn: Integer; { current turn }
49 GTestFlags: Integer;
[227]50 Mode: TGameMode;
[328]51 GWonder: array [0 .. nWonder - 1] of TWonderInfo;
[447]52 ServerVersion: array [0 .. nPl - 1] of Integer;
53 ProcessClientData: array [0 .. nPl - 1] of Boolean;
[6]54 CL: TCmdList;
55{$IFDEF TEXTLOG}CmdInfo: string;
56 TextLog: TextFile; {$ENDIF}
57{$IFDEF LOADPERF}time_total, time_total0, time_x0, time_x1, time_a, time_b, time_c: int64; {$ENDIF}
58 // map data
59 RealMap: array [0 .. lxmax * lymax - 1] of Cardinal;
[447]60 Continent: array [0 .. lxmax * lymax - 1] of Integer;
[6]61 { continent id for each tile }
62 Occupant: array [0 .. lxmax * lymax - 1] of ShortInt;
63 { occupying player for each tile }
64 ZoCMap: array [0 .. lxmax * lymax - 1] of ShortInt;
65 ObserveLevel: array [0 .. lxmax * lymax - 1] of Cardinal;
66 { Observe Level of player p in bits 2*p and 2*p+1 }
[447]67 UsedByCity: array [0 .. lxmax * lymax - 1] of Integer;
[6]68 { location of exploiting city for
69 each tile, =-1 if not exploited }
[2]70
[6]71 // player data
72 RW: array [0 .. nPl - 1] of TPlayerContext; { player data }
[447]73 Difficulty: array [0 .. nPl - 1] of Integer;
[6]74 GShip: array [0 .. nPl - 1] of TShipInfo;
75 ResourceMask: array [0 .. nPl - 1] of Cardinal;
[447]76 Founded: array [0 .. nPl - 1] of Integer; { number of cities founded }
77 TerritoryCount: array [0 .. nPl] of Integer;
[6]78 LastValidStat, Researched, Discovered, // number of tiles discovered
[447]79 GrWallContinent: array [0 .. nPl - 1] of Integer;
[6]80 RWemix: array [0 .. nPl - 1, 0 .. nPl - 1, 0 .. nmmax - 1] of SmallInt;
[2]81 // [p1,p2,mix] -> index of p2's model mix in p1's enemy model list
[6]82 Destroyed: array [0 .. nPl - 1, 0 .. nPl - 1, 0 .. nmmax - 1] of SmallInt;
[2]83 // [p1,p2,mix] -> number of p2's units with model mix that p1 has destroyed
[447]84 nTech: array [0 .. nPl - 1] of Integer; { number of known techs }
[6]85 // NewContact: array[0..nPl-1,0..nPl-1] of boolean;
[2]86
87type
[447]88 TVicinity8Loc = array [0 .. 7] of Integer;
89 TVicinity21Loc = array [0 .. 27] of Integer;
[2]90
[447]91procedure MaskD(var X: array of Cardinal; Count, Mask: Cardinal);
92procedure IntServer(Command, Player, Subject: Integer; var Data);
93procedure CompactLists(P: Integer);
94procedure ClearTestFlags(ClearFlags: Integer);
95procedure SetTestFlags(P, SetFlags: Integer);
[2]96
97// Tech Related Functions
[447]98function TechBaseCost(nTech, diff: Integer): Integer;
99function TechCost(P: Integer): Integer;
100procedure CalculateModel(var M: TModel);
101procedure CheckSpecialModels(P, pre: Integer);
102procedure EnableDevModel(P: Integer);
103procedure SeeTech(P, ad: Integer);
104procedure DiscoverTech(P, ad: Integer);
105procedure CheckExpiration(Wonder: Integer);
[2]106
107// Location Navigation
[447]108function dLoc(Loc, dx, dy: Integer): Integer;
109procedure dxdy(Loc0, Loc1: Integer; var dx, dy: Integer);
110function Distance(Loc0, Loc1: Integer): Integer;
111procedure V8_to_Loc(Loc0: Integer; var VicinityLoc: TVicinity8Loc);
112procedure V21_to_Loc(Loc0: Integer; var VicinityLoc: TVicinity21Loc);
[2]113
114// Game Initialization
115procedure InitRandomGame;
[447]116procedure InitMapGame(Human: Integer);
[2]117procedure ReleaseGame;
118
119// Map Editor
[447]120function MapGeneratorAvailable: Boolean;
[2]121procedure CreateElevation;
[447]122procedure CreateMap(preview: Boolean);
[2]123procedure InitMapEditor;
124procedure ReleaseMapEditor;
[447]125procedure EditTile(Loc, NewTile: Integer);
[2]126
127// Map Revealing
[447]128function GetTileInfo(P, cix, Loc: Integer; var Info: TTileInfo): Integer;
129procedure Strongest(Loc: Integer; var uix, Strength, Bonus, Cnt: Integer);
130function UnitSpeed(P, mix, Health: Integer): Integer;
131procedure GetUnitReport(P, uix: Integer; var UnitReport: TUnitReport);
132procedure SearchCity(Loc: Integer; var P, cix: Integer);
133procedure TellAboutModel(P, taOwner, tamix: Integer);
134function emixSafe(P, taOwner, tamix: Integer): Integer;
135function Discover9(Loc, P, Level: Integer;
136 TellAllied, EnableContact: Boolean): Boolean;
137function Discover21(Loc, P, AdjacentLevel: Integer;
138 TellAllied, EnableContact: Boolean): Boolean;
139procedure DiscoverAll(P, Level: Integer);
140procedure DiscoverViewAreas(P: Integer);
141function GetUnitStack(P, Loc: Integer): Integer;
142procedure UpdateUnitMap(Loc: Integer; CityChange: Boolean = False);
143procedure RecalcV8ZoC(P, Loc: Integer);
144procedure RecalcMapZoC(P: Integer);
145procedure RecalcPeaceMap(P: Integer);
[2]146
147// Territory Calculation
[447]148procedure CheckBorders(OriginLoc: Integer; PlayerLosingCity: Integer = -1);
149procedure LogCheckBorders(P, cix: Integer; PlayerLosingCity: Integer = -1);
[2]150
151// Map Processing
[447]152procedure CreateUnit(P, mix: Integer);
153procedure FreeUnit(P, uix: Integer);
154procedure PlaceUnit(P, uix: Integer);
155procedure RemoveUnit(P, uix: Integer; Enemy: Integer = -1);
156procedure RemoveUnit_UpdateMap(P, uix: Integer);
157procedure RemoveAllUnits(P, Loc: Integer; Enemy: Integer = -1);
158procedure RemoveDomainUnits(D, P, Loc: Integer);
159procedure FoundCity(P, FoundLoc: Integer);
160procedure DestroyCity(P, cix: Integer; SaveUnits: Boolean);
161procedure ChangeCityOwner(pOld, cixOld, pNew: Integer);
162procedure CompleteJob(P, Loc, Job: Integer);
[2]163
164// Diplomacy
[447]165procedure IntroduceEnemy(p1, p2: Integer);
166procedure GiveCivilReport(P, pAbout: Integer);
167procedure GiveMilReport(P, pAbout: Integer);
168procedure ShowPrice(pSender, pTarget, Price: Integer);
169function PayPrice(pSender, pTarget, Price: Integer; execute: Boolean): Boolean;
170procedure CancelTreaty(P, pWith: Integer; DecreaseCredibility: Boolean = True);
171function DoSpyMission(P, pCity, cix, Mission: Integer): Cardinal;
[2]172
[442]173
[2]174implementation
175
176uses
[6]177{$IFDEF LOADPERF}SysUtils, Windows, {$ENDIF}
178{$IFDEF TEXTLOG}SysUtils, {$ENDIF}
179 IPQ;
[2]180
181var
[447]182 UnBuilt: array [0 .. nPl - 1] of Integer; { number of units built }
[2]183
[447]184procedure MaskD(var X: array of Cardinal; Count, Mask: Cardinal);
[10]185var
186 I: Integer;
[9]187begin
[10]188 for I := 0 to Count - 1 do
[447]189 X[I] := X[I] and Mask;
[2]190end;
191
[447]192procedure CompactLists(P: Integer);
[2]193var
[447]194 uix, uix1, cix: Integer;
195{$IFOPT O-}V21: Integer;
[6]196 Radius: TVicinity21Loc; {$ENDIF}
[2]197begin
[447]198 with RW[P] do
[2]199 begin
[6]200 // compact unit list
201 uix := 0;
202 while uix < nUn do
203 if Un[uix].Loc < 0 then
[2]204 begin
[447]205 Dec(nUn);
[6]206 Un[uix] := Un[nUn]; { replace removed unit by last }
207 if (Un[uix].TroopLoad > 0) or (Un[uix].AirLoad > 0) then
208 for uix1 := 0 to nUn - 1 do
209 if Un[uix1].Master = nUn then
210 Un[uix1].Master := uix;
211 // index of last unit changes
[2]212 end
[6]213 else
[447]214 Inc(uix);
[2]215
[6]216 // compact city list
217 cix := 0;
218 while cix < nCity do
219 if City[cix].Loc < 0 then
[2]220 begin
[447]221 Dec(nCity);
[6]222 City[cix] := City[nCity]; { replace city by last }
223 for uix1 := 0 to nUn - 1 do
224 if Un[uix1].Home = nCity then
225 Un[uix1].Home := cix;
226 { index of last city changes }
[2]227 end
[6]228 else
[447]229 Inc(cix);
[2]230
[6]231 // compact enemy city list
232 cix := 0;
233 while cix < nEnemyCity do
234 if EnemyCity[cix].Loc < 0 then
[2]235 begin
[447]236 Dec(nEnemyCity);
[6]237 EnemyCity[cix] := EnemyCity[nEnemyCity]; { replace city by last }
[2]238 end
[6]239 else
[447]240 Inc(cix);
[2]241
242{$IFOPT O-}
[6]243 for cix := 0 to nCity - 1 do
244 with City[cix] do
245 begin
246 V21_to_Loc(Loc, Radius);
247 for V21 := 1 to 26 do
248 if Tiles and (1 shl V21) <> 0 then
[447]249 Assert(UsedByCity[Radius[V21]] = Loc);
[442]250 end;
[2]251{$ENDIF}
252 end;
[442]253end;
[2]254
255{
[6]256 Tech Related Functions
257 ____________________________________________________________________
[2]258}
[447]259function TechBaseCost(nTech, diff: Integer): Integer;
[2]260var
[447]261 c0: Single;
[2]262begin
[6]263 c0 := TechFormula_M[diff] * (nTech + 4) *
264 exp((nTech + 4) / TechFormula_D[diff]);
265 if c0 >= $10000000 then
[447]266 Result := $10000000
[6]267 else
[447]268 Result := trunc(c0);
[2]269end;
270
[447]271function TechCost(P: Integer): Integer;
[2]272begin
[447]273 with RW[P] do
[2]274 begin
[447]275 Result := TechBaseCost(nTech[P], Difficulty[P]);
[6]276 if ResearchTech >= 0 then
277 if (ResearchTech = adMilitary) or (Tech[ResearchTech] = tsSeen) then
[447]278 Result := Result shr 1
[6]279 else if ResearchTech in FutureTech then
280 if Government = gFuture then
[447]281 Result := Result * 2
[6]282 else
[447]283 Result := Result * 4;
[442]284 end;
[2]285end;
286
[447]287procedure SetModelFlags(var M: TModel);
[2]288begin
[447]289 M.Flags := 0;
290 if (M.Domain = dGround) and (M.Kind <> mkDiplomat) then
291 M.Flags := M.Flags or mdZOC;
292 if (M.Kind = mkDiplomat) or (M.Attack + M.Cap[mcBombs] = 0) then
293 M.Flags := M.Flags or mdCivil;
294 if (M.Cap[mcOver] > 0) or (M.Domain = dSea) and (M.Weight >= 6) then
295 M.Flags := M.Flags or mdDoubleSupport;
[2]296end;
297
[447]298procedure CalculateModel(var M: TModel);
[6]299{ calculate attack, defense, cost... of a model by features }
[2]300var
[447]301 I: Integer;
[2]302begin
[447]303 with M do
[2]304 begin
[6]305 Attack := (Cap[mcOffense] + Cap[mcOver]) * MStrength;
306 Defense := (Cap[mcDefense] + Cap[mcOver]) * MStrength;
307 case Domain of
308 dGround:
309 Speed := 150 + Cap[mcMob] * 50;
310 dSea:
311 begin
312 Speed := 350 + 200 * Cap[mcNP] + 200 * Cap[mcTurbines];
313 if Cap[mcNP] = 0 then
[447]314 Inc(Speed, 100 * Cap[mcSE]);
[6]315 end;
316 dAir:
317 Speed := 850 + 400 * Cap[mcJet];
[2]318 end;
[6]319 Cost := 0;
[447]320 for I := 0 to nFeature - 1 do
321 if 1 shl Domain and Feature[I].Domains <> 0 then
322 Inc(Cost, Cap[I] * Feature[I].Cost);
[6]323 Cost := Cost * MCost;
324 Weight := 0;
[447]325 for I := 0 to nFeature - 1 do
326 if 1 shl Domain and Feature[I].Domains <> 0 then
327 if (Domain = dGround) and (I = mcDefense) then
328 Inc(Weight, Cap[I] * 2)
[6]329 else
[447]330 Inc(Weight, Cap[I] * Feature[I].Weight);
[2]331 end;
[447]332 SetModelFlags(M);
[2]333end;
334
[447]335procedure CheckSpecialModels(P, pre: Integer);
[2]336var
[447]337 I, mix1: Integer;
338 HasAlready: Boolean;
[2]339begin
[447]340 for I := 0 to nSpecialModel -
[6]341 1 do { check whether new special model available }
[447]342 if (SpecialModelPreq[I] = pre) and (RW[P].nModel < nmmax) then
[2]343 begin
[447]344 HasAlready := False;
345 for mix1 := 0 to RW[P].nModel - 1 do
346 if (RW[P].Model[mix1].Kind = SpecialModel[I].Kind) and
347 (RW[P].Model[mix1].Attack = SpecialModel[I].Attack) and
348 (RW[P].Model[mix1].Speed = SpecialModel[I].Speed) then
349 HasAlready := True;
[6]350 if not HasAlready then
[2]351 begin
[447]352 RW[P].Model[RW[P].nModel] := SpecialModel[I];
353 SetModelFlags(RW[P].Model[RW[P].nModel]);
354 with RW[P].Model[RW[P].nModel] do
[2]355 begin
[6]356 Status := 0;
357 SavedStatus := 0;
358 IntroTurn := GTurn;
359 Built := 0;
360 Lost := 0;
[447]361 ID := P shl 12 + RW[P].nModel;
362 if (Kind = mkSpecial_Boat) and (ServerVersion[P] < $000EF0) then
[6]363 Speed := 350; // old longboat
[2]364 end;
[447]365 Inc(RW[P].nModel);
[442]366 end;
[2]367 end;
368end;
369
[447]370procedure EnableDevModel(P: Integer);
[2]371begin
[447]372 with RW[P] do
[6]373 if nModel < nmmax then
[2]374 begin
[6]375 Model[nModel] := DevModel;
376 with Model[nModel] do
377 begin
378 Status := 0;
379 SavedStatus := 0;
380 IntroTurn := GTurn;
381 Built := 0;
382 Lost := 0;
[447]383 ID := P shl 12 + nModel;
[6]384 end;
[447]385 Inc(nModel);
386 Inc(Researched[P]);
[442]387 end;
[2]388end;
389
[447]390procedure SeeTech(P, ad: Integer);
[2]391begin
[447]392{$IFDEF TEXTLOG}CmdInfo := CmdInfo + Format(' P%d:A%d', [P, ad]); {$ENDIF}
393 RW[P].Tech[ad] := tsSeen;
[6]394 // inc(nTech[p]);
[447]395 Inc(Researched[P]);
[2]396end;
397
398procedure FreeSlaves;
399var
[447]400 p1, uix: Integer;
[2]401begin
[6]402 for p1 := 0 to nPl - 1 do
403 if (GAlive and (1 shl p1) <> 0) then
404 for uix := 0 to RW[p1].nUn - 1 do
405 if RW[p1].Model[RW[p1].Un[uix].mix].Kind = mkSlaves then
[442]406 RW[p1].Un[uix].Job := jNone;
[2]407end;
408
[447]409procedure DiscoverTech(P, ad: Integer);
[2]410
[447]411 procedure TellAboutKeyTech(P, Source: Integer);
[2]412 var
[447]413 I, p1: Integer;
[2]414 begin
[447]415 for I := 1 to 3 do
416 if ad = AgePreq[I] then
[6]417 for p1 := 0 to nPl - 1 do
[447]418 if (p1 <> P) and ((GAlive or GWatching) and (1 shl p1) <> 0) then
419 RW[p1].EnemyReport[P].Tech[ad] := Source;
[2]420 end;
421
422var
[447]423 I: Integer;
[2]424begin
[6]425 if ad in FutureTech then
[2]426 begin
[447]427 if RW[P].Tech[ad] < tsApplicable then
428 RW[P].Tech[ad] := 1
[6]429 else
[447]430 Inc(RW[P].Tech[ad]);
[6]431 if ad <> futResearchTechnology then
[447]432 Inc(nTech[P], 2);
433 Inc(Researched[P], 8);
434 Exit;
[2]435 end;
436
[447]437 if RW[P].Tech[ad] = tsSeen then
[6]438 begin
[447]439 Inc(nTech[P]);
440 Inc(Researched[P]);
[6]441 end
442 else
443 begin
[447]444 Inc(nTech[P], 2);
445 Inc(Researched[P], 2);
[6]446 end;
[447]447 RW[P].Tech[ad] := tsResearched;
448 TellAboutKeyTech(P, tsResearched);
449 CheckSpecialModels(P, ad);
[6]450 if ad = adScience then
[447]451 ResourceMask[P] := ResourceMask[P] or fSpecial2;
[6]452 if ad = adMassProduction then
[447]453 ResourceMask[P] := ResourceMask[P] or fModern;
[2]454
[447]455 for I := 0 to nWonder - 1 do { check whether wonders expired }
456 if (GWonder[I].EffectiveOwner <> GWonder[woEiffel].EffectiveOwner) and
457 (Imp[I].Expiration = ad) then
[2]458 begin
[447]459 GWonder[I].EffectiveOwner := -1;
460 if I = woPyramids then
[6]461 FreeSlaves;
[2]462 end;
463end;
464
[447]465procedure CheckExpiration(Wonder: Integer);
[2]466// GWonder[Wonder].EffectiveOwner must be set before!
467var
[447]468 P: Integer;
[2]469begin
[6]470 if (Imp[Wonder].Expiration >= 0) and
471 (GWonder[woEiffel].EffectiveOwner <> GWonder[Wonder].EffectiveOwner) then
[447]472 for P := 0 to nPl - 1 do // check if already expired
473 if (1 shl P and GAlive <> 0) and
474 (RW[P].Tech[Imp[Wonder].Expiration] >= tsApplicable) then
[2]475 begin
[6]476 GWonder[Wonder].EffectiveOwner := -1;
477 if Wonder = woPyramids then
[442]478 FreeSlaves;
479 end;
[2]480end;
481
482{
[6]483 Location Navigation
484 ____________________________________________________________________
[2]485}
[447]486function dLoc(Loc, dx, dy: Integer): Integer;
[6]487{ relative location, dx in hor and dy in ver direction from Loc }
[2]488var
[447]489 y0: Integer;
[2]490begin
[38]491 if not (Loc >= 0) and (Loc < MapSize) and (dx + lx >= 0) then
492 raise Exception.Create('Relative location error');
[447]493 Assert((Loc >= 0) and (Loc < MapSize) and (dx + lx >= 0));
[6]494 y0 := Loc div lx;
[447]495 Result := (Loc + (dx + y0 and 1 + lx + lx) shr 1) mod lx + lx * (y0 + dy);
496 if (Result < 0) or (Result >= MapSize) then
497 Result := -1;
[2]498end;
499
[447]500procedure dxdy(Loc0, Loc1: Integer; var dx, dy: Integer);
[2]501begin
[6]502 dx := ((Loc1 mod lx * 2 + Loc1 div lx and 1) -
503 (Loc0 mod lx * 2 + Loc0 div lx and 1) + 3 * lx) mod (2 * lx) - lx;
504 dy := Loc1 div lx - Loc0 div lx;
[2]505end;
506
[447]507function Distance(Loc0, Loc1: Integer): Integer;
[2]508var
[447]509 dx, dy: Integer;
[2]510begin
[6]511 dxdy(Loc0, Loc1, dx, dy);
512 dx := abs(dx);
513 dy := abs(dy);
[447]514 Result := dx + dy + abs(dx - dy) shr 1;
[2]515end;
516
[447]517procedure V8_to_Loc(Loc0: Integer; var VicinityLoc: TVicinity8Loc);
[2]518var
[447]519 x0, y0, lx0: Integer;
[2]520begin
[6]521 lx0 := lx; // put in register!
522 y0 := Loc0 div lx0;
523 x0 := Loc0 - y0 * lx0; // Loc0 mod lx;
524 y0 := y0 and 1;
525 VicinityLoc[1] := Loc0 + lx0 * 2;
526 VicinityLoc[3] := Loc0 - 1;
527 VicinityLoc[5] := Loc0 - lx0 * 2;
528 VicinityLoc[7] := Loc0 + 1;
[447]529 Inc(Loc0, y0);
[6]530 VicinityLoc[0] := Loc0 + lx0;
531 VicinityLoc[2] := Loc0 + lx0 - 1;
532 VicinityLoc[4] := Loc0 - lx0 - 1;
533 VicinityLoc[6] := Loc0 - lx0;
[2]534
[6]535 // world is round!
536 if x0 < lx0 - 1 then
[2]537 begin
[6]538 if x0 = 0 then
[2]539 begin
[447]540 Inc(VicinityLoc[3], lx0);
[6]541 if y0 = 0 then
[2]542 begin
[447]543 Inc(VicinityLoc[2], lx0);
544 Inc(VicinityLoc[4], lx0);
[442]545 end;
546 end;
[2]547 end
[6]548 else
[2]549 begin
[447]550 Dec(VicinityLoc[7], lx0);
[6]551 if y0 = 1 then
[2]552 begin
[447]553 Dec(VicinityLoc[0], lx0);
554 Dec(VicinityLoc[6], lx0);
[442]555 end;
[2]556 end;
557end;
558
[447]559procedure V21_to_Loc(Loc0: Integer; var VicinityLoc: TVicinity21Loc);
[2]560var
[447]561 dx, dy, bit, y0, xComp, yComp, xComp0, xCompSwitch: Integer;
562 dst: ^Integer;
[2]563begin
[6]564 y0 := Loc0 div lx;
565 xComp0 := Loc0 - y0 * lx - 1; // Loc0 mod lx -1
566 xCompSwitch := xComp0 - 1 + y0 and 1;
567 if xComp0 < 0 then
[447]568 Inc(xComp0, lx);
[6]569 if xCompSwitch < 0 then
[447]570 Inc(xCompSwitch, lx);
[6]571 xCompSwitch := xCompSwitch xor xComp0;
572 yComp := lx * (y0 - 3);
573 dst := @VicinityLoc;
574 bit := 1;
575 for dy := 0 to 6 do
[2]576 begin
[6]577 xComp0 := xComp0 xor xCompSwitch;
578 xComp := xComp0;
579 for dx := 0 to 3 do
[2]580 begin
[6]581 if bit and $67F7F76 <> 0 then
582 dst^ := xComp + yComp
583 else
584 dst^ := -1;
[447]585 Inc(xComp);
[6]586 if xComp >= lx then
[447]587 Dec(xComp, lx);
588 Inc(dst);
[6]589 bit := bit shl 1;
[2]590 end;
[447]591 Inc(yComp, lx);
[2]592 end;
593end;
594
595{
[6]596 Map Creation
597 ____________________________________________________________________
[2]598}
599var
[447]600 primitive: Integer;
601 StartLoc, StartLoc2: array [0 .. nPl - 1] of Integer; { starting coordinates }
[6]602 Elevation: array [0 .. lxmax * lymax - 1] of Byte; { map elevation }
[447]603 ElCount: array [Byte] of Integer; { count of elevation occurance }
[2]604
605procedure CalculatePrimitive;
606var
[447]607 I, J: Integer;
[2]608begin
[6]609 primitive := 1;
[447]610 I := 2;
611 while I * I <= MapSize + 1 do // test whether prime
[6]612 begin
[447]613 if (MapSize + 1) mod I = 0 then
[6]614 primitive := 0;
[447]615 Inc(I);
[6]616 end;
[2]617
[6]618 if primitive > 0 then
619 repeat
[447]620 Inc(primitive);
621 I := 1;
622 J := 0;
[6]623 repeat
[447]624 Inc(J);
625 I := I * primitive mod (MapSize + 1);
626 until (I = 1) or (J = MapSize + 1);
627 until J = MapSize;
[2]628end;
629
[447]630function MapGeneratorAvailable: Boolean;
[2]631begin
[447]632 Result := (primitive > 0) and (lx >= 20) and (ly >= 40);
[2]633end;
634
635procedure CreateElevation;
636const
[447]637 D = 64;
[6]638 Smooth = 0.049; { causes low amplitude of short waves }
639 Detail = 0.095; { causes short period of short waves }
640 Merge = 5; { elevation merging range at the connection line of the
641 round world,in relation to lx }
[2]642
643var
[447]644 sa, ca, f1, f2: array [1 .. D] of Single;
645 imerge, X, Y: Integer;
646 V, maxv: Single;
[2]647
[447]648 function Value(X, Y: Integer): Single; { elevation formula }
[2]649 var
[447]650 I: Integer;
[2]651 begin
[447]652 Result := 0;
653 for I := 1 to D do
654 Result := Result + sin(f1[I] * ((X * 2 + Y and 1) * sa[I] + Y * 1.5 *
655 ca[I])) * f2[I];
[6]656 { x values effectively multiplied with 2 to get 2 horizantal periods
657 of the prime waves }
[2]658 end;
659
660begin
[447]661 for X := 1 to D do { prepare formula parameters }
[2]662 begin
[447]663{$IFNDEF SCR} if X = 1 then
664 V := pi / 2 { first wave goes horizontal }
665 else {$ENDIF} V := DelphiRandom * 2 * pi;
666 sa[X] := sin(V) / lx;
667 ca[X] := cos(V) / ly;
668 f1[X] := 2 * pi * exp(Detail * (X - 1));
669 f2[X] := exp(-X * Smooth);
[2]670 end;
671
[6]672 imerge := 2 * lx div Merge;
673 FillChar(ElCount, SizeOf(ElCount), 0);
674 maxv := 0;
[447]675 for X := 0 to lx - 1 do
676 for Y := 0 to ly - 1 do
[6]677 begin
[447]678 V := Value(X, Y);
679 if X * 2 < imerge then
680 V := (X * 2 * V + (imerge - X * 2) * Value(X + lx, Y)) / imerge;
681 V := V - sqr(sqr(2 * Y / ly - 1)); { soft cut at poles }
682 if V > maxv then
683 maxv := V;
[2]684
[447]685 if V < -4 then
686 Elevation[X + lx * Y] := 0
687 else if V > 8.75 then
688 Elevation[X + lx * Y] := 255
[6]689 else
[447]690 Elevation[X + lx * Y] := Round((V + 4) * 20);
691 Inc(ElCount[Elevation[X + lx * Y]]);
[6]692 end;
[2]693end;
694
695procedure FindContinents;
696
[447]697 procedure ReplaceCont(A, B, Stop: Integer);
[6]698 { replace continent name a by b }
[2]699 // make sure always continent[loc]<=loc
700 var
[447]701 I: Integer;
[2]702 begin
[447]703 if A < B then
[6]704 begin
[447]705 I := A;
706 A := B;
707 B := I
[6]708 end;
[447]709 if A > B then
710 for I := A to Stop do
711 if Continent[I] = A then
712 Continent[I] := B;
[2]713 end;
714
715var
[447]716 X, Y, Loc, Wrong: Integer;
[2]717begin
[447]718 for Y := 1 to ly - 2 do
719 for X := 0 to lx - 1 do
[2]720 begin
[447]721 Loc := X + lx * Y;
[6]722 Continent[Loc] := -1;
723 if RealMap[Loc] and fTerrain >= fGrass then
724 begin
[447]725 if (Y - 2 >= 1) and (RealMap[Loc - 2 * lx] and fTerrain >= fGrass) then
[6]726 Continent[Loc] := Continent[Loc - 2 * lx];
[447]727 if (X - 1 + Y and 1 >= 0) and (Y - 1 >= 1) and
728 (RealMap[Loc - 1 + Y and 1 - lx] and fTerrain >= fGrass) then
729 Continent[Loc] := Continent[Loc - 1 + Y and 1 - lx];
730 if (X + Y and 1 < lx) and (Y - 1 >= 1) and
731 (RealMap[Loc + Y and 1 - lx] and fTerrain >= fGrass) then
732 Continent[Loc] := Continent[Loc + Y and 1 - lx];
733 if (X - 1 >= 0) and (RealMap[Loc - 1] and fTerrain >= fGrass) then
[6]734 if Continent[Loc] = -1 then
735 Continent[Loc] := Continent[Loc - 1]
736 else
737 ReplaceCont(Continent[Loc - 1], Continent[Loc], Loc);
738 if Continent[Loc] = -1 then
[442]739 Continent[Loc] := Loc;
740 end;
[6]741 end;
[2]742
[6]743 { connect continents due to round earth }
[447]744 for Y := 1 to ly - 2 do
745 if RealMap[lx * Y] and fTerrain >= fGrass then
[6]746 begin
747 Wrong := -1;
[447]748 if RealMap[lx - 1 + lx * Y] and fTerrain >= fGrass then
749 Wrong := Continent[lx - 1 + lx * Y];
750 if (Y and 1 = 0) and (Y - 1 >= 1) and
751 (RealMap[lx - 1 + lx * (Y - 1)] and fTerrain >= fGrass) then
752 Wrong := Continent[lx - 1 + lx * (Y - 1)];
753 if (Y and 1 = 0) and (Y + 1 < ly - 1) and
754 (RealMap[lx - 1 + lx * (Y + 1)] and fTerrain >= fGrass) then
755 Wrong := Continent[lx - 1 + lx * (Y + 1)];
[6]756 if Wrong >= 0 then
[447]757 ReplaceCont(Wrong, Continent[lx * Y], MapSize - 1);
[6]758 end;
[2]759end;
760
761procedure RarePositions;
762// distribute rare resources
763// must be done after FindContinents
764var
[447]765 I, J, Cnt, X, Y, dx, dy, Loc0, Loc1, xworst, yworst, totalrare, RareMaxWater,
766 RareType, iBest, jbest, MinDist, xBlock, yBlock, V8: Integer;
767 AreaCount, RareByArea, RareAdjacent: array [0 .. 7, 0 .. 4] of Integer;
768 RareLoc: array [0 .. 11] of Integer;
769 Dist: array [0 .. 11, 0 .. 11] of Integer;
[6]770 Adjacent: TVicinity8Loc;
[2]771begin
[6]772 RareMaxWater := 0;
773 repeat
774 FillChar(AreaCount, SizeOf(AreaCount), 0);
[447]775 for Y := 1 to ly - 2 do
[2]776 begin
[447]777 yBlock := Y * 5 div ly;
778 if yBlock = (Y + 1) * 5 div ly then
779 for X := 0 to lx - 1 do
[2]780 begin
[447]781 xBlock := X * 8 div lx;
782 if xBlock = (X + 1) * 8 div lx then
[2]783 begin
[447]784 Loc0 := X + lx * Y;
[6]785 if RealMap[Loc0] and fTerrain >= fGrass then
[2]786 begin
[6]787 Cnt := 0;
788 V8_to_Loc(Loc0, Adjacent);
789 for V8 := 0 to 7 do
790 begin
791 Loc1 := Adjacent[V8];
792 if (Loc1 >= 0) and (Loc1 < MapSize) and
793 (RealMap[Loc1] and fTerrain < fGrass) then
[447]794 Inc(Cnt); // count adjacent water
[6]795 end;
796 if Cnt <= RareMaxWater then // inner land
797 begin
[447]798 Inc(AreaCount[xBlock, yBlock]);
[120]799 if DelphiRandom(AreaCount[xBlock, yBlock]) = 0 then
[442]800 RareByArea[xBlock, yBlock] := Loc0;
801 end;
[2]802 end;
803 end;
[442]804 end;
[2]805 end;
[6]806 totalrare := 0;
[447]807 for X := 0 to 7 do
808 for Y := 0 to 4 do
809 if AreaCount[X, Y] > 0 then
810 Inc(totalrare);
811 Inc(RareMaxWater);
[6]812 until totalrare >= 12;
[2]813
[6]814 while totalrare > 12 do
[2]815 begin // remove rarebyarea resources too close to each other
[6]816 FillChar(RareAdjacent, SizeOf(RareAdjacent), 0);
[447]817 for X := 0 to 7 do
818 for Y := 0 to 4 do
819 if AreaCount[X, Y] > 0 then
[2]820 begin
[447]821 if (AreaCount[(X + 1) mod 8, Y] > 0) and
822 (Continent[RareByArea[X, Y]] = Continent
823 [RareByArea[(X + 1) mod 8, Y]]) then
[6]824 begin
[447]825 Inc(RareAdjacent[X, Y]);
826 Inc(RareAdjacent[(X + 1) mod 8, Y]);
[6]827 end;
[447]828 if Y < 4 then
[6]829 begin
[447]830 if (AreaCount[X, Y + 1] > 0) and
831 (Continent[RareByArea[X, Y]] = Continent[RareByArea[X, Y + 1]])
[6]832 then
833 begin
[447]834 Inc(RareAdjacent[X, Y]);
835 Inc(RareAdjacent[X, Y + 1]);
[6]836 end;
[447]837 if (AreaCount[(X + 1) mod 8, Y + 1] > 0) and
838 (Continent[RareByArea[X, Y]] = Continent[RareByArea[(X + 1) mod 8,
839 Y + 1]]) then
[6]840 begin
[447]841 Inc(RareAdjacent[X, Y]);
842 Inc(RareAdjacent[(X + 1) mod 8, Y + 1]);
[6]843 end;
[447]844 if (AreaCount[(X + 7) mod 8, Y + 1] > 0) and
845 (Continent[RareByArea[X, Y]] = Continent[RareByArea[(X + 7) mod 8,
846 Y + 1]]) then
[6]847 begin
[447]848 Inc(RareAdjacent[X, Y]);
849 Inc(RareAdjacent[(X + 7) mod 8, Y + 1]);
[6]850 end;
[442]851 end;
[2]852 end;
[6]853 xworst := 0;
854 yworst := 0;
855 Cnt := 0;
[447]856 for X := 0 to 7 do
857 for Y := 0 to 4 do
858 if AreaCount[X, Y] > 0 then
[2]859 begin
[447]860 if (Cnt = 0) or (RareAdjacent[X, Y] > RareAdjacent[xworst, yworst])
[6]861 then
862 begin
[447]863 xworst := X;
864 yworst := Y;
[442]865 Cnt := 1;
[6]866 end
[447]867 else if (RareAdjacent[X, Y] = RareAdjacent[xworst, yworst]) then
[6]868 begin
[447]869 Inc(Cnt);
[120]870 if DelphiRandom(Cnt) = 0 then
[6]871 begin
[447]872 xworst := X;
873 yworst := Y;
[442]874 end;
[6]875 end;
[2]876 end;
[6]877 AreaCount[xworst, yworst] := 0;
[447]878 Dec(totalrare);
[6]879 end;
880
881 Cnt := 0;
[447]882 for X := 0 to 7 do
883 for Y := 0 to 4 do
884 if AreaCount[X, Y] > 0 then
[2]885 begin
[447]886 RareLoc[Cnt] := RareByArea[X, Y];
887 Inc(Cnt);
[2]888 end;
[447]889 for I := 0 to 11 do
[2]890 begin
[447]891 RealMap[RareLoc[I]] := RealMap[RareLoc[I]] and not(fTerrain or fSpecial) or
[6]892 (fDesert or fDeadLands);
893 for dy := -1 to 1 do
894 for dx := -1 to 1 do
895 if (dx + dy) and 1 = 0 then
896 begin
[447]897 Loc1 := dLoc(RareLoc[I], dx, dy);
[6]898 if (Loc1 >= 0) and (RealMap[Loc1] and fTerrain = fMountains) then
899 RealMap[Loc1] := RealMap[Loc1] and not fTerrain or fHills;
[442]900 end;
[2]901 end;
[447]902 for I := 0 to 11 do
903 for J := 0 to 11 do
904 Dist[I, J] := Distance(RareLoc[I], RareLoc[J]);
[2]905
[21]906 ibest := 0;
907 jbest := 0;
[6]908 MinDist := Distance(0, MapSize - lx shr 1) shr 1;
909 for RareType := 1 to 3 do
[2]910 begin
[6]911 Cnt := 0;
[447]912 for I := 0 to 11 do
913 if RareLoc[I] >= 0 then
914 for J := 0 to 11 do
915 if RareLoc[J] >= 0 then
[6]916 if (Cnt > 0) and (Dist[iBest, jbest] >= MinDist) then
917 begin
[447]918 if Dist[I, J] >= MinDist then
[6]919 begin
[447]920 Inc(Cnt);
[120]921 if DelphiRandom(Cnt) = 0 then
[6]922 begin
[447]923 iBest := I;
924 jbest := J;
[442]925 end;
926 end;
[6]927 end
[447]928 else if (Cnt = 0) or (Dist[I, J] > Dist[iBest, jbest]) then
[6]929 begin
[447]930 iBest := I;
931 jbest := J;
[6]932 Cnt := 1;
933 end;
934 RealMap[RareLoc[iBest]] := RealMap[RareLoc[iBest]] or
935 Cardinal(RareType) shl 25;
936 RealMap[RareLoc[jbest]] := RealMap[RareLoc[jbest]] or
937 Cardinal(RareType) shl 25;
938 RareLoc[iBest] := -1;
939 RareLoc[jbest] := -1;
[2]940 end;
[442]941end;
[2]942
[447]943function CheckShore(Loc: Integer): Boolean;
[2]944var
[447]945 Loc1, OldTile, V21: Integer;
[6]946 Radius: TVicinity21Loc;
[2]947begin
[447]948 Result := False;
[6]949 OldTile := RealMap[Loc];
950 if OldTile and fTerrain < fGrass then
[2]951 begin
[6]952 RealMap[Loc] := RealMap[Loc] and not fTerrain or fOcean;
953 V21_to_Loc(Loc, Radius);
954 for V21 := 1 to 26 do
[2]955 begin
[6]956 Loc1 := Radius[V21];
957 if (Loc1 >= 0) and (Loc1 < MapSize) and
958 (RealMap[Loc1] and fTerrain >= fGrass) and
959 (RealMap[Loc1] and fTerrain <> fArctic) then
960 RealMap[Loc] := RealMap[Loc] and not fTerrain or fShore;
[2]961 end;
[6]962 if (RealMap[Loc] xor Cardinal(OldTile)) and fTerrain <> 0 then
[447]963 Result := True;
[2]964 end;
965end;
966
[447]967function ActualSpecialTile(Loc: Integer): Cardinal;
[2]968begin
[447]969 Result := SpecialTile(Loc, RealMap[Loc] and fTerrain, lx);
[2]970end;
971
[447]972procedure CreateMap(preview: Boolean);
[2]973const
[6]974 ShHiHills = 6; { of land }
975 ShMountains = 6; { of land }
976 ShRandHills = 12; { of land }
977 ShTestRiver = 40;
978 ShSwamp = 25; { of grassland }
979 MinRivLen = 3;
980 unification = 70;
981 hotunification = 50; // min. 25
[2]982
[447]983 Zone: array [0 .. 3, 2 .. 9] of Single = { terrain distribution }
[6]984 ((0.25, 0, 0, 0.4, 0, 0, 0, 0.35), (0.55, 0, 0.1, 0, 0, 0, 0, 0.35),
985 (0.4, 0, 0.35, 0, 0, 0, 0, 0.25), (0, 0.7, 0, 0, 0, 0, 0, 0.3));
986 { Grs Dst Pra Tun - - - For }
[2]987
[447]988 function RndLow(Y: Integer): Cardinal;
[6]989 { random lowland appropriate to climate }
[2]990 var
[447]991 z0, I: Integer;
992 P, p0, ZPlus: Single;
[2]993 begin
[447]994 if ly - 1 - Y > Y then
[2]995 begin
[447]996 z0 := 6 * Y div ly;
997 ZPlus := 6 * Y / ly - z0;
[6]998 end
999 else
1000 begin
[447]1001 z0 := 6 * (ly - 1 - Y) div ly;
1002 ZPlus := 6 * (ly - 1 - Y) / ly - z0;
[2]1003 end;
[6]1004 p0 := 1;
[447]1005 for I := 2 to 9 do
[6]1006 begin
[447]1007 P := Zone[z0, I] * (1 - ZPlus) + Zone[z0 + 1, I] * ZPlus;
[6]1008 { weight between zones z0 and z0+1 }
[447]1009 if DelphiRandom * p0 < P then
[6]1010 begin
[447]1011 RndLow := I;
[186]1012 Break;
[6]1013 end;
[447]1014 p0 := p0 - P;
[6]1015 end;
[2]1016 end;
1017
[447]1018 function RunRiver(Loc0: Integer): Integer;
[6]1019 { runs river from start point Loc0; return value: length }
[2]1020 var
[447]1021 Dir, T, Loc, Loc1, Cost: Integer;
[6]1022 Q: TIPQ;
[447]1023 From: array [0 .. lxmax * lymax - 1] of Integer;
1024 Time: array [0 .. lxmax * lymax - 1] of Integer;
1025 OneTileLake: Boolean;
[2]1026 begin
[6]1027 FillChar(Time, SizeOf(Time), 255); { -1 }
1028 Q := TIPQ.Create(MapSize);
1029 Q.Put(Loc0, 0);
1030 while Q.Get(Loc, T) and (RealMap[Loc] and fRiver = 0) do
[2]1031 begin
[6]1032 if (RealMap[Loc] and fTerrain < fGrass) then
[2]1033 begin
[447]1034 OneTileLake := True;
[6]1035 for Dir := 0 to 3 do
[2]1036 begin
[6]1037 Loc1 := dLoc(Loc, Dir and 1 * 2 - 1, Dir shr 1 * 2 - 1);
1038 if (Loc1 >= 0) and (RealMap[Loc1] and fTerrain < fGrass) then
[447]1039 OneTileLake := False;
[2]1040 end;
[6]1041 if not OneTileLake then
1042 Break;
[2]1043 end;
[6]1044 Time[Loc] := T;
1045 for Dir := 0 to 3 do
[2]1046 begin
[6]1047 Loc1 := dLoc(Loc, Dir and 1 * 2 - 1, Dir shr 1 * 2 - 1);
1048 if (Loc1 >= lx) and (Loc1 < lx * (ly - 1)) and (Time[Loc1] < 0) then
[2]1049 begin
[6]1050 if RealMap[Loc1] and fRiver = 0 then
[2]1051 begin
[6]1052 Cost := Elevation[Loc1] - Elevation[Loc];
1053 if Cost < 0 then
1054 Cost := 0;
[2]1055 end
[6]1056 else
1057 Cost := 0;
1058 if Q.Put(Loc1, T + Cost shl 8 + 1) then
[186]1059 From[Loc1] := Loc;
1060 end;
1061 end;
[2]1062 end;
[6]1063 Loc1 := Loc;
[447]1064 Result := 0;
[6]1065 while Loc <> Loc0 do
[2]1066 begin
[6]1067 Loc := From[Loc];
[447]1068 Inc(Result);
[6]1069 end;
[447]1070 if (Result > 1) and ((Result >= MinRivLen) or
[6]1071 (RealMap[Loc1] and fTerrain >= fGrass)) then
1072 begin
1073 Loc := Loc1;
1074 while Loc <> Loc0 do
[2]1075 begin
[6]1076 Loc := From[Loc];
1077 if RealMap[Loc] and fTerrain in [fHills, fMountains] then
1078 RealMap[Loc] := fGrass or fRiver
1079 else if RealMap[Loc] and fTerrain >= fGrass then
1080 RealMap[Loc] := RealMap[Loc] or fRiver;
[186]1081 end;
[2]1082 end
[6]1083 else
[447]1084 Result := 0;
[290]1085 FreeAndNil(Q);
[2]1086 end;
1087
1088var
[447]1089 X, Y, N, Dir, plus, Count, Loc0, Loc1, bLand, bHills, bMountains, V8: Integer;
1090 CopyFrom: array [0 .. lxmax * lymax - 1] of Integer;
[6]1091 Adjacent: TVicinity8Loc;
[2]1092
1093begin
[45]1094 FillChar(RealMap, MapSize * SizeOf(Cardinal), 0);
[6]1095 plus := 0;
1096 bMountains := 256;
1097 while plus < MapSize * LandMass * ShMountains div 10000 do
1098 begin
[447]1099 Dec(bMountains);
1100 Inc(plus, ElCount[bMountains]);
[6]1101 end;
1102 Count := plus;
1103 plus := 0;
1104 bHills := bMountains;
1105 while plus < MapSize * LandMass * ShHiHills div 10000 do
1106 begin
[447]1107 Dec(bHills);
1108 Inc(plus, ElCount[bHills]);
[6]1109 end;
[447]1110 Inc(Count, plus);
[6]1111 bLand := bHills;
1112 while Count < MapSize * LandMass div 100 do
1113 begin
[447]1114 Dec(bLand);
1115 Inc(Count, ElCount[bLand]);
[6]1116 end;
[2]1117
[6]1118 for Loc0 := lx to lx * (ly - 1) - 1 do
1119 if Elevation[Loc0] >= bMountains then
1120 RealMap[Loc0] := fMountains
1121 else if Elevation[Loc0] >= bHills then
1122 RealMap[Loc0] := fHills
1123 else if Elevation[Loc0] >= bLand then
1124 RealMap[Loc0] := fGrass;
[2]1125
[6]1126 // remove one-tile islands
1127 for Loc0 := 0 to MapSize - 1 do
1128 if RealMap[Loc0] >= fGrass then
[2]1129 begin
[6]1130 Count := 0;
1131 V8_to_Loc(Loc0, Adjacent);
1132 for V8 := 0 to 7 do
[2]1133 begin
[6]1134 Loc1 := Adjacent[V8];
1135 if (Loc1 < 0) or (Loc1 >= MapSize) or
1136 (RealMap[Loc1] and fTerrain < fGrass) or
1137 (RealMap[Loc1] and fTerrain = fArctic) then
[447]1138 Inc(Count); // count adjacent water
[2]1139 end;
[6]1140 if Count = 8 then
[442]1141 RealMap[Loc0] := fOcean;
[2]1142 end;
1143
[6]1144 if not preview then
[2]1145 begin
[6]1146 plus := 36 * 56 * 20 * ShTestRiver div (LandMass * 100);
1147 if plus > MapSize then
1148 plus := MapSize;
[120]1149 Loc0 := DelphiRandom(MapSize);
[447]1150 for N := 0 to plus - 1 do
[2]1151 begin
[6]1152 if (RealMap[Loc0] and fTerrain >= fGrass) and (Loc0 >= lx) and
1153 (Loc0 < MapSize - lx) then
1154 RunRiver(Loc0);
1155 Loc0 := (Loc0 + 1) * primitive mod (MapSize + 1) - 1;
[2]1156 end;
1157 end;
1158
[6]1159 for Loc0 := 0 to MapSize - 1 do
[120]1160 if (RealMap[Loc0] = fGrass) and (DelphiRandom(100) < ShRandHills) then
[6]1161 RealMap[Loc0] := RealMap[Loc0] or fHills;
[2]1162
[6]1163 // make terrain types coherent
1164 for Loc0 := 0 to MapSize - 1 do
1165 CopyFrom[Loc0] := Loc0;
[2]1166
[447]1167 for N := 0 to unification * MapSize div 100 do
[2]1168 begin
[447]1169 Y := DelphiRandom(ly);
1170 if abs(Y - (ly shr 1)) > ly div 4 + DelphiRandom(ly * hotunification div 100) then
1171 if Y < ly shr 1 then
1172 Y := ly shr 1 - Y
[6]1173 else
[447]1174 Y := 3 * ly shr 1 - Y;
1175 Loc0 := lx * Y + DelphiRandom(lx);
[6]1176 if RealMap[Loc0] and fTerrain = fGrass then
[2]1177 begin
[120]1178 Dir := DelphiRandom(4);
[6]1179 Loc1 := dLoc(Loc0, Dir and 1 * 2 - 1, Dir shr 1 * 2 - 1);
1180 if (Loc1 >= 0) and (RealMap[Loc1] and fTerrain = fGrass) then
[2]1181 begin
[6]1182 while CopyFrom[Loc0] <> Loc0 do
1183 Loc0 := CopyFrom[Loc0];
1184 while CopyFrom[Loc1] <> Loc1 do
1185 Loc1 := CopyFrom[Loc1];
1186 if Loc1 < Loc0 then
1187 CopyFrom[Loc0] := Loc1
1188 else
1189 CopyFrom[Loc1] := Loc0;
[2]1190 end;
1191 end;
1192 end;
1193
[6]1194 for Loc0 := 0 to MapSize - 1 do
1195 if (RealMap[Loc0] and fTerrain = fGrass) and (CopyFrom[Loc0] = Loc0) then
1196 RealMap[Loc0] := RealMap[Loc0] and not fTerrain or RndLow(Loc0 div lx);
[2]1197
[6]1198 for Loc0 := 0 to MapSize - 1 do
1199 if RealMap[Loc0] and fTerrain = fGrass then
[2]1200 begin
[6]1201 Loc1 := Loc0;
1202 while CopyFrom[Loc1] <> Loc1 do
1203 Loc1 := CopyFrom[Loc1];
1204 RealMap[Loc0] := RealMap[Loc0] and not fTerrain or
[442]1205 RealMap[Loc1] and fTerrain;
[2]1206 end;
1207
[6]1208 for Loc0 := 0 to MapSize - 1 do
1209 if RealMap[Loc0] and fTerrain = fGrass then
[2]1210 begin // change grassland to swamp
[120]1211 if DelphiRandom(100) < ShSwamp then
[6]1212 RealMap[Loc0] := RealMap[Loc0] and not fTerrain or fSwamp;
[2]1213 end;
1214
[6]1215 for Loc0 := 0 to MapSize - 1 do // change desert to prairie 1
1216 if RealMap[Loc0] and fTerrain = fDesert then
[2]1217 begin
[6]1218 if RealMap[Loc0] and fRiver <> 0 then
1219 Count := 5
1220 else
[2]1221 begin
[6]1222 Count := 0;
1223 for Dir := 0 to 3 do
[2]1224 begin
[6]1225 Loc1 := dLoc(Loc0, Dir and 1 * 2 - 1, Dir shr 1 * 2 - 1);
1226 if Loc1 >= 0 then
1227 if RealMap[Loc1] and fTerrain < fGrass then
[447]1228 Inc(Count, 2);
[2]1229 end;
1230 end;
[6]1231 if Count >= 4 then
[442]1232 RealMap[Loc0] := RealMap[Loc0] and not fTerrain or fPrairie;
[2]1233 end;
1234
[6]1235 for Loc0 := 0 to MapSize - 1 do // change desert to prairie 2
1236 if RealMap[Loc0] and fTerrain = fDesert then
[2]1237 begin
[6]1238 Count := 0;
1239 for Dir := 0 to 3 do
[2]1240 begin
[6]1241 Loc1 := dLoc(Loc0, Dir and 1 * 2 - 1, Dir shr 1 * 2 - 1);
1242 if Loc1 >= 0 then
1243 if RealMap[Loc1] and fTerrain <> fDesert then
[447]1244 Inc(Count);
[2]1245 end;
[6]1246 if Count >= 4 then
[442]1247 RealMap[Loc0] := RealMap[Loc0] and not fTerrain or fPrairie;
[2]1248 end;
1249
[6]1250 for Loc0 := 0 to MapSize - 1 do
1251 CheckShore(Loc0); // change ocean to shore
[447]1252 for X := 0 to lx - 1 do
[2]1253 begin
[447]1254 RealMap[X + lx * 0] := fArctic;
1255 if RealMap[X + lx * 1] >= fGrass then
1256 RealMap[X + lx * 1] := RealMap[X + lx * 1] and not fTerrain or fTundra;
1257 if RealMap[X + lx * (ly - 2)] >= fGrass then
1258 RealMap[X + lx * (ly - 2)] := RealMap[X + lx * (ly - 2)] and
[6]1259 not fTerrain or fTundra;
[447]1260 RealMap[X + lx * (ly - 1)] := fArctic;
[2]1261 end;
1262
[6]1263 for Loc0 := 0 to MapSize - 1 do // define special terrain tiles
1264 RealMap[Loc0] := RealMap[Loc0] or ActualSpecialTile(Loc0) shl 5 or
1265 ($F shl 27);
[2]1266
[6]1267 if not preview then
1268 begin
1269 FindContinents;
1270 RarePositions;
1271 end;
[2]1272end;
1273
1274procedure StartPositions;
1275// define nation start positions
1276// must be done after FindContinents
1277
1278var
[6]1279 CountGood: (cgBest, cgFlat, cgLand);
[2]1280
[447]1281 function IsGoodTile(Loc: Integer): Boolean;
[2]1282 var
[447]1283 xLoc, yLoc: Integer;
[2]1284 begin
[6]1285 xLoc := Loc mod lx;
1286 yLoc := Loc div lx;
1287 if RealMap[Loc] and fDeadLands <> 0 then
[447]1288 Result := False
[6]1289 else
1290 case CountGood of
1291 cgBest:
[447]1292 Result := (RealMap[Loc] and fTerrain in [fGrass, fPrairie, fTundra,
[6]1293 fSwamp, fForest]) and Odd((lymax + xLoc - yLoc shr 1) shr 1 + xLoc +
1294 (yLoc + 1) shr 1);
1295 cgFlat:
[447]1296 Result := (RealMap[Loc] and fTerrain in [fGrass, fPrairie, fTundra,
[6]1297 fSwamp, fForest]);
1298 cgLand:
[447]1299 Result := RealMap[Loc] and fTerrain >= fGrass;
[2]1300 end;
1301 end;
1302
1303const
[6]1304 MaxCityLoc = 64;
[2]1305
1306var
[447]1307 p1, p2, nAlive, C, Loc, Loc1, CntGood, CntGoodGrass, MinDist, I, J, N,
[21]1308 nsc, V21, V8, BestDist, TestDist, MinGood, nIrrLoc,
[447]1309 FineDistSQR, nRest: Integer;
1310 ccount: array [0 .. lxmax * lymax - 1] of Word;
1311 sc, StartLoc0, sccount: array [1 .. nPl] of Integer;
1312 TestStartLoc: array [0 .. nPl - 1] of Integer;
1313 CityLoc: array [1 .. nPl, 0 .. MaxCityLoc - 1] of Integer;
1314 nCityLoc: array [1 .. nPl] of Integer;
1315 RestLoc: array [0 .. MaxCityLoc - 1] of Integer;
1316 IrrLoc: array [0 .. 20] of Integer;
[6]1317 Radius: TVicinity21Loc;
1318 Adjacent: TVicinity8Loc;
[447]1319 ok: Boolean;
[2]1320
1321begin
[6]1322 nAlive := 0;
1323 for p1 := 0 to nPl - 1 do
1324 if 1 shl p1 and GAlive <> 0 then
[447]1325 Inc(nAlive);
[6]1326 if nAlive = 0 then
[447]1327 Exit;
[2]1328
[6]1329 { count good tiles }
1330 FillChar(ccount, MapSize * 2, 0);
1331 for Loc := 0 to MapSize - 1 do
1332 if RealMap[Loc] and fTerrain = fGrass then
1333 if ActualSpecialTile(Loc) = 1 then
[447]1334 Inc(ccount[Continent[Loc]], 3)
[6]1335 else
[447]1336 Inc(ccount[Continent[Loc]], 2)
[6]1337 else if RealMap[Loc] and fTerrain in [fPrairie, fSwamp, fForest, fHills]
1338 then
[447]1339 Inc(ccount[Continent[Loc]]);
[2]1340
[6]1341 Loc := 0;
1342 while ccount[Loc] > 0 do
[447]1343 Inc(Loc);
1344 for I := 1 to nAlive do
[6]1345 begin
[447]1346 sc[I] := Loc;
1347 sccount[I] := 1
[2]1348 end;
[6]1349 { init with zero size start continents, then search bigger ones }
1350 for Loc := 0 to MapSize - 1 do
1351 if ccount[Loc] > 0 then
1352 begin // search biggest continents
1353 p1 := nAlive + 1;
1354 while (p1 > 1) and (ccount[Loc] > ccount[sc[p1 - 1]]) do
1355 begin
1356 if p1 < nAlive + 1 then
1357 sc[p1] := sc[p1 - 1];
[447]1358 Dec(p1);
[6]1359 end;
1360 if p1 < nAlive + 1 then
1361 sc[p1] := Loc;
1362 end;
1363 nsc := nAlive;
1364 repeat
[447]1365 C := 1; // search least crowded continent after smallest
1366 for I := 2 to nsc - 1 do
1367 if ccount[sc[I]] * (2 * sccount[C] + 1) > ccount[sc[C]] *
1368 (2 * sccount[I] + 1) then
1369 C := I;
1370 if ccount[sc[nsc]] * (2 * sccount[C] + 1) > ccount[sc[C]] then
[6]1371 Break; // even least crowded continent is more crowded than smallest
[447]1372 Inc(sccount[C]);
1373 Dec(nsc);
[6]1374 until sccount[nsc] > 1;
[2]1375
[6]1376 MinGood := 7;
1377 CountGood := cgBest;
1378 repeat
[447]1379 Dec(MinGood);
[6]1380 if (MinGood = 3) and (CountGood < cgLand) then // too demanding!
[2]1381 begin
[447]1382 Inc(CountGood);
[442]1383 MinGood := 6;
[6]1384 end;
1385 FillChar(nCityLoc, SizeOf(nCityLoc), 0);
[120]1386 Loc := DelphiRandom(MapSize);
[447]1387 for I := 0 to MapSize - 1 do
[6]1388 begin
1389 if ((Loc >= 4 * lx) and (Loc < MapSize - 4 * lx) or (CountGood >= cgLand))
1390 and IsGoodTile(Loc) then
[2]1391 begin
[447]1392 C := nsc;
1393 while (C > 0) and (Continent[Loc] <> sc[C]) do
1394 Dec(C);
1395 if (C > 0) and (nCityLoc[C] < MaxCityLoc) then
[2]1396 begin
[6]1397 CntGood := 1;
1398 V21_to_Loc(Loc, Radius);
1399 for V21 := 1 to 26 do
1400 if V21 <> CityOwnTile then
1401 begin
1402 Loc1 := Radius[V21];
1403 if (Loc1 >= 0) and (Loc1 < MapSize) and IsGoodTile(Loc1) then
[447]1404 Inc(CntGood);
[6]1405 end;
1406 if CntGood >= MinGood then
[2]1407 begin
[447]1408 CityLoc[C, nCityLoc[C]] := Loc;
1409 Inc(nCityLoc[C]);
[186]1410 end;
1411 end;
[2]1412 end;
[6]1413 Loc := (Loc + 1) * primitive mod (MapSize + 1) - 1;
[2]1414 end;
1415
[447]1416 ok := True;
1417 for C := 1 to nsc do
1418 if nCityLoc[C] < sccount[C] * (8 - MinGood) div (7 - MinGood) then
1419 ok := False;
[6]1420 until ok;
[2]1421
[6]1422 FineDistSQR := MapSize * LandMass * 9 div (nAlive * 100);
1423 p1 := 1;
[447]1424 for C := 1 to nsc do
[2]1425 begin // for all start continents
[447]1426 if sccount[C] = 1 then
1427 StartLoc0[p1] := CityLoc[C, DelphiRandom(nCityLoc[C])]
[6]1428 else
[2]1429 begin
[6]1430 BestDist := 0;
[447]1431 N := 1 shl sccount[C] * 32; // number of tries to find good distribution
1432 if N > 1 shl 12 then
1433 N := 1 shl 12;
1434 while (N > 0) and (BestDist * BestDist < FineDistSQR) do
[2]1435 begin
[6]1436 MinDist := MaxInt;
[447]1437 nRest := nCityLoc[C];
1438 for I := 0 to nRest - 1 do
1439 RestLoc[I] := CityLoc[C, I];
1440 for I := 0 to sccount[C] - 1 do
[2]1441 begin
[6]1442 if nRest = 0 then
1443 Break;
[447]1444 J := DelphiRandom(nRest);
1445 TestStartLoc[I] := RestLoc[J];
1446 RestLoc[J] := RestLoc[nRest - 1];
1447 Dec(nRest);
1448 for J := 0 to I - 1 do
[2]1449 begin
[447]1450 TestDist := Distance(TestStartLoc[I], TestStartLoc[J]);
[6]1451 if TestDist < MinDist then
[442]1452 MinDist := TestDist;
[2]1453 end;
[447]1454 if I = sccount[C] - 1 then
[2]1455 begin
[447]1456 Assert(MinDist > BestDist);
[6]1457 BestDist := MinDist;
[447]1458 for J := 0 to sccount[C] - 1 do
1459 StartLoc0[p1 + J] := TestStartLoc[J];
[2]1460 end
[6]1461 else if BestDist > 0 then
[2]1462 begin
[447]1463 J := 0;
1464 while J < nRest do
[2]1465 begin // remove all locs from rest which have too little distance to this one
[447]1466 TestDist := Distance(TestStartLoc[I], RestLoc[J]);
[6]1467 if TestDist <= BestDist then
1468 begin
[447]1469 RestLoc[J] := RestLoc[nRest - 1];
1470 Dec(nRest);
[6]1471 end
1472 else
[447]1473 Inc(J);
[2]1474 end;
1475 end;
1476 end;
[447]1477 Dec(N)
[2]1478 end;
1479 end;
[447]1480 p1 := p1 + sccount[C]
[2]1481 end;
1482
[6]1483 // make start locs fertile
1484 for p1 := 1 to nAlive do
[2]1485 begin
[6]1486 RealMap[StartLoc0[p1]] := RealMap[StartLoc0[p1]] and
1487 not(fTerrain or fSpecial) or fGrass or fSpecial1;
1488 CntGood := 1;
1489 CntGoodGrass := 1;
1490 V21_to_Loc(StartLoc0[p1], Radius);
1491 for V21 := 1 to 26 do
1492 if V21 <> CityOwnTile then
1493 begin
1494 Loc1 := Radius[V21];
1495 if (Loc1 >= 0) and (Loc1 < MapSize) and IsGoodTile(Loc1) then
1496 if RealMap[Loc1] and fTerrain = fGrass then
[447]1497 Inc(CntGoodGrass)
[6]1498 else
[447]1499 Inc(CntGood);
[6]1500 end;
1501 for V21 := 1 to 26 do
1502 if V21 <> CityOwnTile then
1503 begin
1504 Loc1 := Radius[V21];
1505 if (Loc1 >= 0) and (Loc1 < MapSize) and
1506 (RealMap[Loc1] and fDeadLands = 0) then
[120]1507 if IsGoodTile(Loc1) and (DelphiRandom(CntGood) < MinGood - CntGoodGrass + 1)
[6]1508 then
1509 begin
1510 RealMap[Loc1] := RealMap[Loc1] and not(fTerrain or fSpecial)
1511 or fGrass;
1512 RealMap[Loc1] := RealMap[Loc1] or ActualSpecialTile(Loc1) shl 5;
1513 end
1514 else if RealMap[Loc1] and fTerrain = fDesert then
1515 RealMap[Loc1] := RealMap[Loc1] and not fTerrain or fPrairie
1516 else if (RealMap[Loc1] and fTerrain in [fPrairie, fTundra, fSwamp])
[120]1517 and (DelphiRandom(2) = 0) then
[6]1518 RealMap[Loc1] := RealMap[Loc1] and not fTerrain or fForest;
1519 end;
[2]1520
[6]1521 // first irrigation
1522 nIrrLoc := 0;
1523 for V21 := 1 to 26 do
1524 if V21 <> CityOwnTile then
[2]1525 begin
[6]1526 Loc1 := Radius[V21];
1527 if (Loc1 >= 0) and (Loc1 < MapSize) and
1528 (RealMap[Loc1] and (fTerrain or fSpecial) = fGrass or fSpecial1) then
1529 begin
1530 IrrLoc[nIrrLoc] := Loc1;
[447]1531 Inc(nIrrLoc);
[6]1532 end;
[2]1533 end;
[447]1534 I := 2;
1535 if I > nIrrLoc then
1536 I := nIrrLoc;
1537 while I > 0 do
[2]1538 begin
[447]1539 J := DelphiRandom(nIrrLoc);
1540 RealMap[IrrLoc[J]] := RealMap[IrrLoc[J]] or tiIrrigation;
1541 IrrLoc[J] := IrrLoc[nIrrLoc - 1];
1542 Dec(nIrrLoc);
1543 Dec(I);
[2]1544 end;
1545 end;
1546
[6]1547 StartLoc[0] := 0;
1548 for p1 := 0 to nPl - 1 do
1549 if 1 shl p1 and GAlive <> 0 then
1550 begin
1551 repeat
[447]1552 I := DelphiRandom(nAlive) + 1
1553 until StartLoc0[I] >= 0;
1554 StartLoc[p1] := StartLoc0[I];
1555 StartLoc0[I] := -1
[6]1556 end;
1557 SaveMapCenterLoc := StartLoc[0];
[2]1558
[6]1559 // second unit starting position
1560 for p1 := 0 to nPl - 1 do
1561 if 1 shl p1 and GAlive <> 0 then
[2]1562 begin
[6]1563 StartLoc2[p1] := StartLoc[p1];
1564 V8_to_Loc(StartLoc[p1], Adjacent);
1565 for V8 := 0 to 7 do
1566 begin
1567 Loc1 := Adjacent[V8];
1568 for p2 := 0 to nPl - 1 do
1569 if (1 shl p2 and GAlive <> 0) and (StartLoc[p2] = Loc1) then
1570 Loc1 := -1;
1571 for p2 := 0 to p1 - 1 do
1572 if (1 shl p2 and GAlive <> 0) and (StartLoc2[p2] = Loc1) then
1573 Loc1 := -1;
1574 if (Loc1 < 0) or (Loc1 >= MapSize) or
1575 (RealMap[Loc1] and fTerrain in [fOcean, fShore, fDesert, fArctic,
1576 fMountains]) or (RealMap[Loc1] and fDeadLands <> 0) then
1577 TestDist := -1
1578 else if RealMap[Loc1] and fTerrain = fGrass then
1579 TestDist := 2
1580 else if Terrain[RealMap[Loc1] and fTerrain].IrrEff > 0 then
1581 TestDist := 1
1582 else
1583 TestDist := 0;
1584 if (StartLoc2[p1] = StartLoc[p1]) or (TestDist > BestDist) then
1585 begin
1586 StartLoc2[p1] := Loc1;
1587 BestDist := TestDist;
[447]1588 N := 1;
[6]1589 end
1590 else if TestDist = BestDist then
1591 begin
[447]1592 Inc(N);
1593 if DelphiRandom(N) = 0 then
[6]1594 StartLoc2[p1] := Loc1;
1595 end;
[186]1596 end;
[6]1597 end;
[442]1598end;
[2]1599
[447]1600procedure PredefinedStartPositions(Human: Integer);
[2]1601// use predefined nation start positions
1602var
[447]1603 I, p1, Loc1, nAlive, nStartLoc0, nPrefStartLoc0, imax: Integer;
1604 StartLoc0: array [0 .. lxmax * lymax - 1] of Integer;
1605 ishuman: Boolean;
[2]1606begin
[6]1607 nAlive := 0;
1608 for p1 := 0 to nPl - 1 do
1609 if 1 shl p1 and GAlive <> 0 then
[447]1610 Inc(nAlive);
[6]1611 if nAlive = 0 then
[447]1612 Exit;
[2]1613
[44]1614 for I := 0 to Length(StartLoc0) - 1 do
1615 StartLoc0[I] := 0;
1616
[6]1617 // calculate starting positions
1618 nStartLoc0 := 0;
1619 nPrefStartLoc0 := 0;
1620 for Loc1 := 0 to MapSize - 1 do
1621 if RealMap[Loc1] and fPrefStartPos <> 0 then
[2]1622 begin
[6]1623 StartLoc0[nStartLoc0] := StartLoc0[nPrefStartLoc0];
1624 StartLoc0[nPrefStartLoc0] := Loc1;
[447]1625 Inc(nPrefStartLoc0);
1626 Inc(nStartLoc0);
[6]1627 RealMap[Loc1] := RealMap[Loc1] and not fPrefStartPos;
[2]1628 end
[6]1629 else if RealMap[Loc1] and fStartPos <> 0 then
[2]1630 begin
[6]1631 StartLoc0[nStartLoc0] := Loc1;
[447]1632 Inc(nStartLoc0);
[6]1633 RealMap[Loc1] := RealMap[Loc1] and not fStartPos;
[2]1634 end;
[447]1635 Assert(nStartLoc0 >= nAlive);
[2]1636
[6]1637 StartLoc[0] := 0;
[447]1638 for ishuman := True downto False do
[6]1639 for p1 := 0 to nPl - 1 do
1640 if (1 shl p1 and GAlive <> 0) and ((1 shl p1 and Human <> 0) = ishuman)
1641 then
[2]1642 begin
[447]1643 Dec(nStartLoc0);
[6]1644 imax := nStartLoc0;
1645 if nPrefStartLoc0 > 0 then
1646 begin
[447]1647 Dec(nPrefStartLoc0);
[6]1648 imax := nPrefStartLoc0;
1649 end;
[447]1650 I := DelphiRandom(imax + 1);
1651 StartLoc[p1] := StartLoc0[I];
1652 StartLoc2[p1] := StartLoc0[I];
1653 StartLoc0[I] := StartLoc0[imax];
[6]1654 StartLoc0[imax] := StartLoc0[nStartLoc0];
[2]1655 end;
[6]1656 SaveMapCenterLoc := StartLoc[0];
[442]1657end;
[2]1658
1659procedure InitGame;
1660var
[447]1661 I, P, p1, uix, Loc1: Integer;
[2]1662begin
[207]1663 {$IFDEF FastContact}
1664 { Railroad everywhere }
[6]1665 for Loc1 := 0 to MapSize - 1 do
1666 if RealMap[Loc1] and fTerrain >= fGrass then
1667 RealMap[Loc1] := RealMap[Loc1] or fRR;
[207]1668 {$ENDIF}
[2]1669
[6]1670 { !!!for Loc1:=0 to MapSize-1 do
1671 if RealMap[Loc1] and fterrain>=fGrass then
[120]1672 if Delphirandom(3)=0 then RealMap[Loc1]:=RealMap[Loc1] or fRoad
[451]1673 else if Delphirandom(3)=0 then RealMap[Loc1]:=RealMap[Loc1] or fRR;}
[6]1674 {random Road and Railroad }
1675 { !!!for Loc1:=0 to MapSize-1 do
[120]1676 if (RealMap[Loc1] and fterrain>=fGrass) and (Delphirandom(20)=0) then
[6]1677 RealMap[Loc1]:=RealMap[Loc1] or fPoll; }
[2]1678
[10]1679 FillChar(Occupant, MapSize, Byte(-1));
[6]1680 FillChar(ZoCMap, MapSize, 0);
1681 FillChar(ObserveLevel, MapSize * 4, 0);
[10]1682 FillChar(UsedByCity, MapSize * 4, Byte(-1));
[6]1683 GTestFlags := 0;
1684 GInitialized := GAlive or GWatching;
[447]1685 for P := 0 to nPl - 1 do
1686 if 1 shl P and GInitialized <> 0 then
1687 with RW[P] do
[6]1688 begin
[447]1689 Researched[P] := 0;
1690 Discovered[P] := 0;
1691 TerritoryCount[P] := 0;
1692 nTech[P] := 0;
1693 if Difficulty[P] = 0 then
1694 ResourceMask[P] := $FFFFFFFF
[6]1695 else
[447]1696 ResourceMask[P] := $FFFFFFFF and not(fSpecial2 or fModern);
1697 GrWallContinent[P] := -1;
[2]1698
[6]1699 GetMem(Map, 4 * MapSize);
1700 GetMem(MapObservedLast, 2 * MapSize);
[10]1701 FillChar(MapObservedLast^, 2 * MapSize, Byte(-1));
[6]1702 GetMem(Territory, MapSize);
1703 FillChar(Territory^, MapSize, $FF);
1704 GetMem(Un, numax * SizeOf(TUn));
1705 GetMem(Model, (nmmax + 1) * SizeOf(TModel));
1706 // draft needs one model behind last
1707 GetMem(City, ncmax * SizeOf(TCity));
1708 GetMem(EnemyUn, neumax * SizeOf(TUnitInfo));
1709 GetMem(EnemyCity, necmax * SizeOf(TCityInfo));
1710 GetMem(EnemyModel, nemmax * SizeOf(TModelInfo));
1711 for p1 := 0 to nPl - 1 do
1712 begin
1713 if 1 shl p1 and GInitialized <> 0 then
1714 begin
[447]1715 FillChar(RWemix[P, p1], SizeOf(RWemix[P, p1]), 255); { -1 }
1716 FillChar(Destroyed[P, p1], SizeOf(Destroyed[P, p1]), 0);
[6]1717 end;
1718 Attitude[p1] := atNeutral;
1719 Treaty[p1] := trNoContact;
1720 LastCancelTreaty[p1] := -CancelTreatyTurns - 1;
1721 EvaStart[p1] := -PeaceEvaTurns - 1;
1722 Tribute[p1] := 0;
1723 TributePaid[p1] := 0;
[447]1724 if (p1 <> P) and (1 shl p1 and GAlive <> 0) then
[6]1725 begin // initialize enemy report
1726 GetMem(EnemyReport[p1], SizeOf(TEnemyReport) - 2 *
1727 (INFIN + 1 - nmmax));
[10]1728 FillChar(EnemyReport[p1].Tech, nAdv, Byte(tsNA));
[6]1729 EnemyReport[p1].TurnOfContact := -1;
1730 EnemyReport[p1].TurnOfCivilReport := -1;
1731 EnemyReport[p1].TurnOfMilReport := -1;
1732 EnemyReport[p1].Attitude := atNeutral;
1733 EnemyReport[p1].Government := gDespotism;
[447]1734 if 1 shl P and GAlive = 0 then
[6]1735 Treaty[p1] := trNone // supervisor
1736 end
1737 else
1738 EnemyReport[p1] := nil;
1739 end;
1740 TestFlags := GTestFlags;
1741 Credibility := InitialCredibility;
1742 MaxCredibility := 100;
1743 nUn := 0;
1744 nModel := 0;
1745 nCity := 0;
1746 nEnemyUn := 0;
1747 nEnemyCity := 0;
1748 nEnemyModel := 0;
1749 for Loc1 := 0 to MapSize - 1 do
1750 Map[Loc1] := fUNKNOWN;
[10]1751 FillChar(Tech, nAdv, Byte(tsNA));
[6]1752 FillChar(NatBuilt, SizeOf(NatBuilt), 0);
1753 end;
1754
1755 // create initial models and units
[447]1756 for P := 0 to nPl - 1 do
1757 if (1 shl P and GAlive <> 0) then
1758 with RW[P] do
[2]1759 begin
[6]1760 nModel := 0;
[447]1761 for I := 0 to nSpecialModel - 1 do
1762 if SpecialModelPreq[I] = preNone then
[6]1763 begin
[447]1764 Model[nModel] := SpecialModel[I];
[6]1765 Model[nModel].Status := 0;
1766 Model[nModel].IntroTurn := 0;
1767 Model[nModel].Built := 0;
1768 Model[nModel].Lost := 0;
[447]1769 Model[nModel].ID := P shl 12 + nModel;
[6]1770 SetModelFlags(Model[nModel]);
[447]1771 Inc(nModel);
[6]1772 end;
1773 nUn := 0;
[447]1774 UnBuilt[P] := 0;
[6]1775 for uix := 0 to nStartUn - 1 do
1776 begin
[447]1777 CreateUnit(P, StartUn[uix]);
1778 Dec(Model[StartUn[uix]].Built);
1779 Un[uix].Loc := StartLoc2[P];
1780 PlaceUnit(P, uix);
[6]1781 end;
[447]1782 FoundCity(P, StartLoc[P]); // capital
1783 Founded[P] := 1;
[6]1784 with City[0] do
1785 begin
[447]1786 ID := P shl 12;
[6]1787 Flags := chFounded;
1788 end;
[2]1789 end;
1790
[6]1791 TerritoryCount[nPl] := MapSize;
1792 // fillchar(NewContact, sizeof(NewContact), false);
[442]1793end;
[2]1794
1795procedure InitRandomGame;
1796begin
[120]1797 DelphiRandSeed := RND;
[6]1798 CalculatePrimitive;
1799 CreateElevation;
[447]1800 CreateMap(False);
[6]1801 StartPositions;
1802 InitGame;
[186]1803end;
[2]1804
[447]1805procedure InitMapGame(Human: Integer);
[2]1806begin
[120]1807 DelphiRandSeed := RND;
[6]1808 FindContinents;
1809 PredefinedStartPositions(Human);
1810 InitGame;
[186]1811end;
[2]1812
1813procedure ReleaseGame;
1814var
[447]1815 p1, p2: Integer;
[2]1816begin
[6]1817 for p1 := 0 to nPl - 1 do
1818 if 1 shl p1 and GInitialized <> 0 then
1819 begin
1820 for p2 := 0 to nPl - 1 do
1821 if RW[p1].EnemyReport[p2] <> nil then
1822 FreeMem(RW[p1].EnemyReport[p2]);
1823 FreeMem(RW[p1].EnemyUn);
1824 FreeMem(RW[p1].EnemyCity);
1825 FreeMem(RW[p1].EnemyModel);
1826 FreeMem(RW[p1].Un);
1827 FreeMem(RW[p1].City);
1828 FreeMem(RW[p1].Model);
1829 FreeMem(RW[p1].Territory);
1830 FreeMem(RW[p1].MapObservedLast);
1831 FreeMem(RW[p1].Map);
[186]1832 end;
[2]1833end;
1834
1835procedure InitMapEditor;
1836var
[447]1837 p1: Integer;
[2]1838begin
[6]1839 CalculatePrimitive;
[10]1840 FillChar(Occupant, MapSize, Byte(-1));
[6]1841 FillChar(ObserveLevel, MapSize * 4, 0);
1842 with RW[0] do
[2]1843 begin
[6]1844 ResourceMask[0] := $FFFFFFFF;
1845 GetMem(Map, 4 * MapSize);
1846 GetMem(MapObservedLast, 2 * MapSize);
[10]1847 FillChar(MapObservedLast^, 2 * MapSize, Byte(-1));
[6]1848 GetMem(Territory, MapSize);
1849 FillChar(Territory^, MapSize, $FF);
1850 Un := nil;
1851 Model := nil;
1852 City := nil;
1853 EnemyUn := nil;
1854 EnemyCity := nil;
1855 EnemyModel := nil;
1856 for p1 := 0 to nPl - 1 do
1857 EnemyReport[p1] := nil;
1858 nUn := 0;
1859 nModel := 0;
1860 nCity := 0;
1861 nEnemyUn := 0;
1862 nEnemyCity := 0;
1863 nEnemyModel := 0;
[2]1864 end;
1865end;
1866
1867procedure ReleaseMapEditor;
1868begin
[6]1869 FreeMem(RW[0].Territory);
1870 FreeMem(RW[0].MapObservedLast);
1871 FreeMem(RW[0].Map);
[2]1872end;
1873
[447]1874procedure EditTile(Loc, NewTile: Integer);
[2]1875var
[447]1876 Loc1, V21: Integer;
[6]1877 Radius: TVicinity21Loc;
[2]1878begin
[6]1879 if NewTile and fDeadLands <> 0 then
1880 NewTile := NewTile and (fDeadLands or fModern or fRiver) or fDesert;
1881 case NewTile and fTerrain of
1882 fOcean, fShore:
1883 NewTile := NewTile and (fTerrain or fSpecial);
1884 fMountains, fArctic:
1885 NewTile := NewTile and not fRiver;
[2]1886 end;
[6]1887 with Terrain[NewTile and fTerrain] do
1888 if (ClearTerrain >= 0) or (AfforestTerrain >= 0) or (TransTerrain >= 0) then
1889 NewTile := NewTile or fSpecial;
1890 // only automatic special resources for transformable tiles
1891 if NewTile and fRR <> 0 then
1892 NewTile := NewTile and not fRoad;
1893 if not((NewTile and fTerrain) in TerrType_Canalable) then
1894 NewTile := NewTile and not fCanal;
1895 if Terrain[NewTile and fTerrain].IrrEff = 0 then
[2]1896 begin
[6]1897 NewTile := NewTile and not(fPrefStartPos or fStartPos);
1898 if (NewTile and fTerImp = tiIrrigation) or (NewTile and fTerImp = tiFarm)
1899 then
[442]1900 NewTile := NewTile and not fTerImp;
[2]1901 end;
[6]1902 if (Terrain[NewTile and fTerrain].MineEff = 0) and
1903 (NewTile and fTerImp = tiMine) then
1904 NewTile := NewTile and not fTerImp;
[2]1905
[6]1906 RealMap[Loc] := NewTile;
1907 if NewTile and fSpecial = fSpecial then
1908 // standard special resource distribution
1909 RealMap[Loc] := RealMap[Loc] and not fSpecial or
1910 ActualSpecialTile(Loc) shl 5;
[2]1911
[6]1912 // automatic shore tiles
1913 V21_to_Loc(Loc, Radius);
1914 for V21 := 1 to 26 do
[2]1915 begin
[6]1916 Loc1 := Radius[V21];
1917 if (Loc1 >= 0) and (Loc1 < MapSize) then
[2]1918 begin
[6]1919 if CheckShore(Loc1) then
1920 RealMap[Loc1] := RealMap[Loc1] and not fSpecial or
1921 ActualSpecialTile(Loc1) shl 5;
1922 RealMap[Loc1] := RealMap[Loc1] or ($F shl 27);
1923 RW[0].Map[Loc1] := RealMap[Loc1] and $07FFFFFF or fObserved;
[442]1924 end;
[2]1925 end;
[6]1926 // RealMap[Loc]:=RealMap[Loc] and not fSpecial;
1927 // RW[0].Map[Loc]:=RealMap[Loc] or fObserved;
[2]1928end;
1929
1930{
[6]1931 Map Revealing
1932 ____________________________________________________________________
[2]1933}
[447]1934function GetTileInfo(P, cix, Loc: Integer; var Info: TTileInfo): Integer;
[2]1935// cix>=0 - known city index of player p -- only core internal!
1936// cix=-1 - search city, player unknown, only if permission for p
1937// cix=-2 - don't search city, don't calculate city benefits, just government of player p
1938var
[447]1939 p0, Tile, special: Integer;
[2]1940begin
[6]1941 with Info do
[2]1942 begin
[447]1943 p0 := P;
[6]1944 if cix >= 0 then
1945 Tile := RealMap[Loc]
1946 else
[2]1947 begin
[447]1948 Tile := RW[P].Map[Loc];
[6]1949 if Tile and fTerrain = fUNKNOWN then
1950 begin
[447]1951 Result := eNoPreq;
1952 Exit;
[6]1953 end;
[2]1954 end;
1955
[6]1956 if (cix = -1) and (UsedByCity[Loc] >= 0) then
[2]1957 begin // search exploiting player and city
[447]1958 SearchCity(UsedByCity[Loc], P, cix);
1959 if not((P = p0) or (ObserveLevel[UsedByCity[Loc]] shr (2 * p0) and
[6]1960 3 = lObserveSuper)) then
1961 cix := -1
[2]1962 end;
[6]1963 if cix = -1 then
1964 begin
[447]1965 Result := eInvalid;
1966 Exit;
[6]1967 end; // no city found here
[2]1968
[447]1969 special := Tile and fSpecial and ResourceMask[P] shr 5;
[6]1970 with Terrain[Tile and fTerrain] do
[2]1971 begin
[6]1972 Food := FoodRes[special];
1973 Prod := ProdRes[special];
1974 Trade := TradeRes[special];
1975 if (special > 0) and (Tile and fTerrain <> fGrass) and
[447]1976 (RW[P].NatBuilt[imSpacePort] > 0) then
[2]1977 begin // GeoSat effect
[6]1978 Food := 2 * Food - FoodRes[0];
1979 Prod := 2 * Prod - ProdRes[0];
1980 Trade := 2 * Trade - TradeRes[0];
[2]1981 end;
1982
[6]1983 if (Tile and fTerImp = tiIrrigation) or (Tile and fTerImp = tiFarm) or
1984 (Tile and fCity <> 0) then
[447]1985 Inc(Food, IrrEff); { irrigation effect }
[6]1986 if Tile and fTerImp = tiMine then
[447]1987 Inc(Prod, MineEff); { mining effect }
1988 if (Tile and fRiver <> 0) and (RW[P].Tech[adMapMaking] >= tsApplicable)
[6]1989 then
[447]1990 Inc(Trade); { river effect }
[6]1991 if (Tile and (fRoad or fRR) <> 0) and (MoveCost = 1) and
[447]1992 (RW[P].Tech[adWheel] >= tsApplicable) then
1993 Inc(Trade); { road effect }
[6]1994 if (Tile and (fRR or fCity) <> 0) and
[447]1995 (RW[P].Tech[adRailroad] >= tsApplicable) then
1996 Inc(Prod, Prod shr 1); { railroad effect }
[2]1997
[6]1998 ExplCity := -1;
[447]1999 if (cix >= 0) and (P = p0) then
[6]2000 ExplCity := cix;
2001 if cix >= 0 then
2002 if Tile and fTerrain >= fGrass then
[2]2003 begin
[6]2004 if ((Tile and fTerImp = tiFarm) or (Tile and fCity <> 0)) and
[447]2005 (RW[P].City[cix].Built[imSupermarket] > 0) then
2006 Inc(Food, Food shr 1); { farmland effect }
[6]2007 if (Tile and (fRoad or fRR) <> 0) and (MoveCost = 1) and
[447]2008 (RW[P].City[cix].Built[imHighways] > 0) then
2009 Inc(Trade, 1); { superhighway effect }
[2]2010 end
[6]2011 else
[2]2012 begin
[447]2013 if RW[P].City[cix].Built[imHarbor] > 0 then
2014 Inc(Food); { harbour effect }
2015 if RW[P].City[cix].Built[imPlatform] > 0 then
2016 Inc(Prod); { oil platform effect }
2017 if GWonder[woLighthouse].EffectiveOwner = P then
2018 Inc(Prod);
[2]2019 end;
2020 end;
2021
[6]2022 { good government influence }
[447]2023 if (RW[P].Government in [gRepublic, gDemocracy, gFuture]) and (Trade > 0)
[6]2024 then
[447]2025 Inc(Trade);
2026 if (RW[P].Government = gCommunism) and (Prod > 1) then
2027 Inc(Prod);
[2]2028
[447]2029 if RW[P].Government in [gAnarchy, gDespotism] then
[6]2030 begin { bad government influence }
2031 if Food > 3 then
2032 Food := 3;
2033 if Prod > 2 then
2034 Prod := 2;
2035 if Trade > 2 then
2036 Trade := 2;
[2]2037 end;
2038
[6]2039 if Tile and (fTerrain or fPoll) > fPoll then
2040 begin { pollution - decrease ressources }
[447]2041 Dec(Food, Food shr 1);
2042 Dec(Prod, Prod shr 1);
2043 Dec(Trade, Trade shr 1);
[2]2044 end;
2045
[6]2046 if Tile and fCity <> 0 then
2047 Trade := 0
[447]2048 else if (cix >= 0) and (RW[P].City[cix].Built[imCourt] + RW[P].City[cix]
[6]2049 .Built[imPalace] = 0) then
[447]2050 if RW[P].City[cix].Built[imTownHall] = 0 then
[6]2051 Trade := 0
2052 else if Trade > 3 then
2053 Trade := 3;
[2]2054 end;
[447]2055 Result := eOK;
[442]2056end;
[2]2057
[447]2058procedure Strongest(Loc: Integer; var uix, Strength, Bonus, Cnt: Integer);
[6]2059{ find strongest defender at Loc }
[2]2060var
[6]2061 Defender, uix1, Det, Cost, TestStrength, TestBonus, TestDet, TestCost,
[447]2062 Domain: Integer;
[6]2063 PUn: ^TUn;
2064 PModel: ^TModel;
[2]2065begin
[6]2066 Defender := Occupant[Loc];
[44]2067 Cost := 0;
[6]2068 Cnt := 0;
2069 Det := -1;
2070 for uix1 := 0 to RW[Defender].nUn - 1 do
[2]2071 begin
[6]2072 PUn := @RW[Defender].Un[uix1];
2073 PModel := @RW[Defender].Model[PUn.mix];
2074 if PModel.Kind = mkSpecial_Glider then
2075 Domain := dGround
2076 else
2077 Domain := PModel.Domain;
2078 if PUn.Loc = Loc then
[2]2079 begin
[447]2080 Inc(Cnt);
[6]2081 if PUn.Master < 0 then
[2]2082 begin
[6]2083 if Domain < dSea then
[2]2084 begin
[6]2085 TestBonus := Terrain[RealMap[Loc] and fTerrain].Defense;
2086 if RealMap[Loc] and fTerImp = tiFort then
[447]2087 Inc(TestBonus, 4);
[6]2088 if PUn.Flags and unFortified <> 0 then
[447]2089 Inc(TestBonus, 2);
[6]2090 if (PModel.Kind = mkSpecial_TownGuard) and
2091 (RealMap[Loc] and fCity <> 0) then
[447]2092 Inc(TestBonus, 4);
[2]2093 end
[6]2094 else
2095 TestBonus := 4;
[447]2096 Inc(TestBonus, PUn.exp div ExpCost);
[6]2097 TestStrength := PModel.Defense * TestBonus * PUn.Health;
2098 if (Domain = dAir) and ((RealMap[Loc] and fCity <> 0) or
2099 (RealMap[Loc] and fTerImp = tiBase)) then
2100 TestStrength := 0;
2101 if (Domain = dSea) and (RealMap[Loc] and fTerrain >= fGrass) then
2102 TestStrength := TestStrength shr 1;
2103 TestDet := TestStrength;
2104 if PModel.Cap[mcStealth] > 0 then
2105 else if PModel.Cap[mcSub] > 0 then
[447]2106 Inc(TestDet, 1 shl 28)
[6]2107 else if (Domain = dGround) and (PModel.Cap[mcFanatic] > 0) and
2108 not(RW[Defender].Government in [gRepublic, gDemocracy, gFuture]) then
[447]2109 Inc(TestDet, 4 shl 28) // fanatic ground units always defend
[6]2110 else if PModel.Flags and mdZOC <> 0 then
[447]2111 Inc(TestDet, 3 shl 28)
[6]2112 else
[447]2113 Inc(TestDet, 2 shl 28);
[6]2114 TestCost := RW[Defender].Model[PUn.mix].Cost;
2115 if (TestDet > Det) or (TestDet = Det) and (TestCost < Cost) then
[2]2116 begin
[6]2117 uix := uix1;
2118 Strength := TestStrength;
2119 Bonus := TestBonus;
2120 Det := TestDet;
2121 Cost := TestCost;
[186]2122 end;
2123 end;
2124 end;
[2]2125 end;
2126end;
2127
[447]2128function UnitSpeed(P, mix, Health: Integer): Integer;
[2]2129begin
[447]2130 with RW[P].Model[mix] do
[2]2131 begin
[447]2132 Result := Speed;
[6]2133 if Domain = dSea then
[2]2134 begin
[447]2135 if GWonder[woMagellan].EffectiveOwner = P then
2136 Inc(Result, 200);
[6]2137 if Health < 100 then
[447]2138 Result := ((Result - 250) * Health div 5000) * 50 + 250;
[442]2139 end;
2140 end;
[2]2141end;
2142
[447]2143procedure GetUnitReport(P, uix: Integer; var UnitReport: TUnitReport);
[2]2144var
[447]2145 TerrOwner: Integer;
[6]2146 PModel: ^TModel;
[2]2147begin
[6]2148 UnitReport.FoodSupport := 0;
2149 UnitReport.ProdSupport := 0;
2150 UnitReport.ReportFlags := 0;
[447]2151 if RW[P].Government <> gAnarchy then
2152 with RW[P].Un[uix] do
[6]2153 begin
[447]2154 PModel := @RW[P].Model[mix];
[6]2155 if (PModel.Kind = mkSettler)
2156 { and (GWonder[woFreeSettlers].EffectiveOwner<>p) } then
[447]2157 UnitReport.FoodSupport := SettlerFood[RW[P].Government]
[6]2158 else if Flags and unConscripts <> 0 then
2159 UnitReport.FoodSupport := 1;
[2]2160
[447]2161 if RW[P].Government <> gFundamentalism then
[2]2162 begin
[6]2163 if GTestFlags and tfImmImprove = 0 then
2164 begin
2165 if PModel.Flags and mdDoubleSupport <> 0 then
2166 UnitReport.ProdSupport := 2
2167 else
2168 UnitReport.ProdSupport := 1;
2169 if PModel.Kind = mkSpecial_TownGuard then
2170 UnitReport.ReportFlags := UnitReport.ReportFlags or
2171 urfAlwaysSupport;
2172 end;
2173 if PModel.Flags and mdCivil = 0 then
2174 begin
2175 TerrOwner := RealMap[Loc] shr 27;
[447]2176 case RW[P].Government of
[6]2177 gRepublic, gFuture:
[447]2178 if (TerrOwner <> P) and (TerrOwner < nPl) and
2179 (RW[P].Treaty[TerrOwner] < trAlliance) then
[6]2180 UnitReport.ReportFlags := UnitReport.ReportFlags or urfDeployed;
2181 gDemocracy:
[447]2182 if (TerrOwner >= nPl) or (TerrOwner <> P) and
2183 (RW[P].Treaty[TerrOwner] < trAlliance) then
[6]2184 UnitReport.ReportFlags := UnitReport.ReportFlags or urfDeployed;
2185 end;
[186]2186 end;
[2]2187 end;
2188 end;
2189end;
2190
[447]2191procedure SearchCity(Loc: Integer; var P, cix: Integer);
[2]2192// set p to supposed owner before call
2193var
[447]2194 I: Integer;
[2]2195begin
[6]2196 if RealMap[Loc] < nPl shl 27 then
[447]2197 P := RealMap[Loc] shr 27;
2198 for I := 0 to nPl - 1 do
[2]2199 begin
[447]2200 if 1 shl P and GAlive <> 0 then
2201 with RW[P] do
[6]2202 begin
2203 cix := nCity - 1;
2204 while (cix >= 0) and (City[cix].Loc <> Loc) do
[447]2205 Dec(cix);
[6]2206 if cix >= 0 then
[447]2207 Exit;
[6]2208 end;
[447]2209 Assert(I < nPl - 1);
2210 P := (P + 1) mod nPl;
[2]2211 end;
2212end;
2213
[447]2214procedure MakeCityInfo(P, cix: Integer; var ci: TCityInfo);
[2]2215begin
[447]2216 Assert((P >= 0) and (P < nPl));
2217 Assert((cix >= 0) and (cix < RW[P].nCity));
2218 with RW[P].City[cix] do
[2]2219 begin
[6]2220 ci.Loc := Loc;
2221 ci.ID := ID;
[447]2222 ci.Owner := P;
[6]2223 ci.Size := Size;
2224 ci.Flags := 0;
2225 if Built[imPalace] > 0 then
[447]2226 Inc(ci.Flags, ciCapital);
2227 if (Built[imWalls] > 0) or (Continent[Loc] = GrWallContinent[P]) then
2228 Inc(ci.Flags, ciWalled);
[6]2229 if Built[imCoastalFort] > 0 then
[447]2230 Inc(ci.Flags, ciCoastalFort);
[6]2231 if Built[imMissileBat] > 0 then
[447]2232 Inc(ci.Flags, ciMissileBat);
[6]2233 if Built[imBunker] > 0 then
[447]2234 Inc(ci.Flags, ciBunker);
[6]2235 if Built[imSpacePort] > 0 then
[447]2236 Inc(ci.Flags, ciSpacePort);
[2]2237 end;
2238end;
2239
[447]2240procedure TellAboutModel(P, taOwner, tamix: Integer);
[2]2241var
[447]2242 I: Integer;
[2]2243begin
[447]2244 if (P = taOwner) or (Mode < moPlaying) then
2245 Exit;
2246 I := 0;
2247 while (I < RW[P].nEnemyModel) and ((RW[P].EnemyModel[I].Owner <> taOwner) or
2248 (RW[P].EnemyModel[I].mix <> tamix)) do
2249 Inc(I);
2250 if I = RW[P].nEnemyModel then
2251 IntServer(sIntTellAboutModel + P shl 4, taOwner, tamix, nil^);
[2]2252end;
2253
[447]2254function emixSafe(P, taOwner, tamix: Integer): Integer;
[2]2255begin
[447]2256 Result := RWemix[P, taOwner, tamix];
2257 if Result < 0 then
[2]2258 begin // sIntTellAboutModel comes too late
[447]2259 Assert(Mode = moMovie);
2260 Result := $FFFF;
[2]2261 end;
2262end;
2263
[447]2264procedure IntroduceEnemy(p1, p2: Integer);
[2]2265begin
[6]2266 RW[p1].Treaty[p2] := trNone;
2267 RW[p2].Treaty[p1] := trNone;
[2]2268end;
2269
[447]2270function DiscoverTile(Loc, P, pTell, Level: Integer; EnableContact: Boolean;
2271 euix: Integer = -2): Boolean;
[2]2272// euix = -2: full discover
2273// euix = -1: unit and city only, append units in EnemyUn
2274// euix >= 0: unit and city only, replace EnemyUn[euix]
2275
[447]2276 procedure SetContact(p1, p2: Integer);
[2]2277 begin
[6]2278 if (Mode < moPlaying) or (p1 = p2) or (RW[p1].Treaty[p2] > trNoContact) then
[447]2279 Exit;
[6]2280 IntServer(sIntTellAboutNation, p1, p2, nil^);
2281 // NewContact[p1,p2]:=true
[2]2282 end;
2283
2284var
[447]2285 I, uix, cix, TerrOwner, TerrOwnerTreaty, Strength, Bonus, Cnt, pFoundCity,
2286 cixFoundCity, MinLevel, Loc1, V8: Integer;
[6]2287 Tile, AddFlags: Cardinal;
2288 Adjacent: TVicinity8Loc;
2289 unx: ^TUn;
2290 mox: ^TModel;
[2]2291begin
[447]2292 Result := False;
[6]2293 with RW[pTell] do
[2]2294 begin
[6]2295 Tile := RealMap[Loc] and ResourceMask[pTell];
2296 if Mode = moLoading_Fast then
2297 AddFlags := 0 // don't discover units
2298 else
[2]2299 begin
[6]2300 AddFlags := Map[Loc] and fInEnemyZoC // always preserve this flag!
2301 or fObserved;
2302 if Level = lObserveSuper then
2303 AddFlags := AddFlags or fSpiedOut;
2304 if (GrWallContinent[pTell] >= 0) and
2305 (Continent[Loc] = GrWallContinent[pTell]) then
2306 AddFlags := AddFlags or fGrWall;
2307 if (Mode = moPlaying) and ((Tile and (nPl shl 27) <> nPl shl 27) and
[447]2308 (pTell = P)) then
[2]2309 begin // set fPeace flag?
[6]2310 TerrOwner := Tile shr 27;
2311 if TerrOwner <> pTell then
[2]2312 begin
[6]2313 TerrOwnerTreaty := RW[pTell].Treaty[TerrOwner];
2314 if 1 shl TerrOwnerTreaty and
2315 (1 shl trPeace or 1 shl TrFriendlyContact) <> 0 then
2316 AddFlags := AddFlags or fPeace;
[442]2317 end;
[2]2318 end;
2319
[6]2320 if Occupant[Loc] >= 0 then
2321 if Occupant[Loc] = pTell then
[2]2322 begin
[6]2323 AddFlags := AddFlags or (fOwned or fUnit);
2324 if ZoCMap[Loc] > 0 then
2325 AddFlags := AddFlags or fOwnZoCUnit;
2326 // Level:=lObserveSuper // always see own units
[2]2327 end
[6]2328 else if Map[Loc] and fUnit <> 0 then
2329 AddFlags := AddFlags or fUnit
2330 else
[2]2331 begin
[6]2332 Strongest(Loc, uix, Strength, Bonus, Cnt);
2333 unx := @RW[Occupant[Loc]].Un[uix];
2334 mox := @RW[Occupant[Loc]].Model[unx.mix];
[447]2335 Assert((ZoCMap[Loc] <> 0) = (mox.Flags and mdZOC <> 0));
[6]2336 if (mox.Cap[mcStealth] > 0) and (Tile and fCity = 0) and
2337 (Tile and fTerImp <> tiBase) then
2338 MinLevel := lObserveSuper
2339 else if (mox.Cap[mcSub] > 0) and (Tile and fTerrain < fGrass) then
2340 MinLevel := lObserveAll
2341 else
2342 MinLevel := lObserveUnhidden;
2343 if Level >= MinLevel then
[2]2344 begin
[6]2345 AddFlags := AddFlags or fUnit;
2346 if euix >= 0 then
2347 uix := euix
2348 else
[2]2349 begin
[6]2350 uix := nEnemyUn;
[447]2351 Inc(nEnemyUn);
2352 Assert(nEnemyUn < neumax);
[2]2353 end;
[6]2354 MakeUnitInfo(Occupant[Loc], unx^, EnemyUn[uix]);
2355 if Cnt > 1 then
2356 EnemyUn[uix].Flags := EnemyUn[uix].Flags or unMulti;
[447]2357 if (mox.Flags and mdZOC <> 0) and (pTell = P) and
[6]2358 (Treaty[Occupant[Loc]] < trAlliance) then
[2]2359 begin // set fInEnemyZoC flags of surrounding tiles
[6]2360 V8_to_Loc(Loc, Adjacent);
2361 for V8 := 0 to 7 do
[2]2362 begin
[6]2363 Loc1 := Adjacent[V8];
2364 if (Loc1 >= 0) and (Loc1 < MapSize) then
2365 Map[Loc1] := Map[Loc1] or fInEnemyZoC
[442]2366 end;
[2]2367 end;
[6]2368 if EnableContact and (mox.Domain = dGround) then
2369 SetContact(pTell, Occupant[Loc]);
2370 if Mode >= moMovie then
[2]2371 begin
[6]2372 TellAboutModel(pTell, Occupant[Loc], unx.mix);
2373 EnemyUn[uix].emix := emixSafe(pTell, Occupant[Loc], unx.mix);
[2]2374 end;
[6]2375 // Level:=lObserveSuper; // don't discover unit twice
[447]2376 if (pTell = P) and
[6]2377 ((Tile and fCity = 0) or (1 shl pTell and GAI <> 0)) then
[447]2378 Result := True;
[2]2379 end
[6]2380 else
[186]2381 AddFlags := AddFlags or Map[Loc] and (fStealthUnit or fHiddenUnit);
2382 end;
[2]2383 end; // if Mode>moLoading_Fast
2384
[6]2385 if Tile and fCity <> 0 then
2386 if ObserveLevel[Loc] shr (2 * pTell) and 3 > 0 then
2387 AddFlags := AddFlags or Map[Loc] and fOwned
2388 else
[2]2389 begin
[6]2390 pFoundCity := Tile shr 27;
2391 if pFoundCity = pTell then
2392 AddFlags := AddFlags or fOwned
2393 else
[2]2394 begin
[6]2395 if EnableContact then
2396 SetContact(pTell, pFoundCity);
2397 cixFoundCity := RW[pFoundCity].nCity - 1;
2398 while (cixFoundCity >= 0) and
2399 (RW[pFoundCity].City[cixFoundCity].Loc <> Loc) do
[447]2400 Dec(cixFoundCity);
2401 Assert(cixFoundCity >= 0);
2402 I := 0;
2403 while (I < nEnemyCity) and (EnemyCity[I].Loc <> Loc) do
2404 Inc(I);
2405 if I = nEnemyCity then
[2]2406 begin
[447]2407 Inc(nEnemyCity);
2408 Assert(nEnemyCity < necmax);
2409 EnemyCity[I].Status := 0;
2410 EnemyCity[I].SavedStatus := 0;
2411 if pTell = P then
2412 Result := True;
[2]2413 end;
[447]2414 MakeCityInfo(pFoundCity, cixFoundCity, EnemyCity[I]);
[2]2415 end;
2416 end
[6]2417 else if Map[Loc] and fCity <> 0 then // remove enemycity
2418 for cix := 0 to nEnemyCity - 1 do
2419 if EnemyCity[cix].Loc = Loc then
2420 EnemyCity[cix].Loc := -1;
[2]2421
[6]2422 if Map[Loc] and fTerrain = fUNKNOWN then
[447]2423 Inc(Discovered[pTell]);
[6]2424 if euix >= -1 then
2425 Map[Loc] := Map[Loc] and not(fUnit or fCity or fOwned or fOwnZoCUnit) or
2426 (Tile and $07FFFFFF or AddFlags) and
2427 (fUnit or fCity or fOwned or fOwnZoCUnit)
2428 else
[2]2429 begin
[6]2430 Map[Loc] := Tile and $07FFFFFF or AddFlags;
2431 if Tile and $78000000 = $78000000 then
2432 Territory[Loc] := -1
2433 else
2434 Territory[Loc] := Tile shr 27;
2435 MapObservedLast[Loc] := GTurn
[2]2436 end;
[6]2437 ObserveLevel[Loc] := ObserveLevel[Loc] and not(3 shl (2 * pTell)) or
2438 Cardinal(Level) shl (2 * pTell);
[186]2439 end;
[442]2440end;
[2]2441
[447]2442function Discover9(Loc, P, Level: Integer;
2443 TellAllied, EnableContact: Boolean): Boolean;
[2]2444var
[447]2445 V9, Loc1, pTell, OldLevel: Integer;
[6]2446 Radius: TVicinity8Loc;
[2]2447begin
[447]2448 Assert((Mode > moLoading_Fast) or (RW[P].nEnemyUn = 0));
2449 Result := False;
[6]2450 V8_to_Loc(Loc, Radius);
2451 for V9 := 0 to 8 do
[2]2452 begin
[6]2453 if V9 = 8 then
2454 Loc1 := Loc
2455 else
2456 Loc1 := Radius[V9];
2457 if (Loc1 >= 0) and (Loc1 < MapSize) then
2458 if TellAllied then
[2]2459 begin
[6]2460 for pTell := 0 to nPl - 1 do
[447]2461 if (pTell = P) or (1 shl pTell and GAlive <> 0) and
2462 (RW[P].Treaty[pTell] = trAlliance) then
[2]2463 begin
[6]2464 OldLevel := ObserveLevel[Loc1] shr (2 * pTell) and 3;
2465 if Level > OldLevel then
[447]2466 Result := DiscoverTile(Loc1, P, pTell, Level, EnableContact)
2467 or Result;
[186]2468 end;
[2]2469 end
[6]2470 else
[2]2471 begin
[447]2472 OldLevel := ObserveLevel[Loc1] shr (2 * P) and 3;
[6]2473 if Level > OldLevel then
[447]2474 Result := DiscoverTile(Loc1, P, P, Level, EnableContact) or Result;
[186]2475 end;
[2]2476 end;
2477end;
2478
[447]2479function Discover21(Loc, P, AdjacentLevel: Integer;
2480 TellAllied, EnableContact: Boolean): Boolean;
[2]2481var
[447]2482 V21, Loc1, pTell, Level, OldLevel, AdjacentFlags: Integer;
[6]2483 Radius: TVicinity21Loc;
[2]2484begin
[447]2485 Assert((Mode > moLoading_Fast) or (RW[P].nEnemyUn = 0));
2486 Result := False;
[6]2487 AdjacentFlags := $00267620 shr 1;
2488 V21_to_Loc(Loc, Radius);
2489 for V21 := 1 to 26 do
[2]2490 begin
[6]2491 Loc1 := Radius[V21];
2492 if (Loc1 >= 0) and (Loc1 < MapSize) then
[2]2493 begin
[6]2494 if AdjacentFlags and 1 <> 0 then
2495 Level := AdjacentLevel
2496 else
2497 Level := lObserveUnhidden;
2498 if TellAllied then
[2]2499 begin
[6]2500 for pTell := 0 to nPl - 1 do
[447]2501 if (pTell = P) or (1 shl pTell and GAlive <> 0) and
2502 (RW[P].Treaty[pTell] = trAlliance) then
[2]2503 begin
[6]2504 OldLevel := ObserveLevel[Loc1] shr (2 * pTell) and 3;
2505 if Level > OldLevel then
[447]2506 Result := DiscoverTile(Loc1, P, pTell, Level, EnableContact)
2507 or Result;
[186]2508 end;
[2]2509 end
[6]2510 else
[2]2511 begin
[447]2512 OldLevel := ObserveLevel[Loc1] shr (2 * P) and 3;
[6]2513 if Level > OldLevel then
[447]2514 Result := DiscoverTile(Loc1, P, P, Level, EnableContact) or Result;
[186]2515 end;
[2]2516 end;
[6]2517 AdjacentFlags := AdjacentFlags shr 1;
[2]2518 end;
2519end;
2520
[447]2521procedure DiscoverAll(P, Level: Integer);
[6]2522{ player p discovers complete playground (for supervisor) }
[2]2523var
[447]2524 Loc, OldLevel: Integer;
[2]2525begin
[447]2526 Assert((Mode > moLoading_Fast) or (RW[P].nEnemyUn = 0));
[6]2527 for Loc := 0 to MapSize - 1 do
[2]2528 begin
[447]2529 OldLevel := ObserveLevel[Loc] shr (2 * P) and 3;
[6]2530 if Level > OldLevel then
[447]2531 DiscoverTile(Loc, P, P, Level, False);
[2]2532 end;
2533end;
2534
[447]2535procedure DiscoverViewAreas(P: Integer);
[2]2536var
[447]2537 pTell, uix, cix, ecix, Loc, RealOwner: Integer;
[6]2538 PModel: ^TModel;
[2]2539begin // discover unit and city view areas
[6]2540 for pTell := 0 to nPl - 1 do
[447]2541 if (pTell = P) or (RW[P].Treaty[pTell] = trAlliance) then
[2]2542 begin
[6]2543 for uix := 0 to RW[pTell].nUn - 1 do
2544 with RW[pTell].Un[uix] do
2545 if (Loc >= 0) and (Master < 0) and (RealMap[Loc] and fCity = 0) then
2546 begin
2547 PModel := @RW[pTell].Model[mix];
2548 if (PModel.Kind = mkDiplomat) or (PModel.Cap[mcSpy] > 0) then
[447]2549 Discover21(Loc, P, lObserveSuper, False, True)
[6]2550 else if (PModel.Cap[mcRadar] + PModel.Cap[mcCarrier] > 0) or
2551 (PModel.Domain = dAir) then
[447]2552 Discover21(Loc, P, lObserveAll, False, False)
[6]2553 else if (RealMap[Loc] and fTerrain = fMountains) or
2554 (RealMap[Loc] and fTerImp = tiFort) or
2555 (RealMap[Loc] and fTerImp = tiBase) or (PModel.Cap[mcAcademy] > 0)
2556 then
[447]2557 Discover21(Loc, P, lObserveUnhidden, False,
[6]2558 PModel.Domain = dGround)
2559 else
[447]2560 Discover9(Loc, P, lObserveUnhidden, False,
[6]2561 PModel.Domain = dGround);
2562 end;
2563 for cix := 0 to RW[pTell].nCity - 1 do
2564 if RW[pTell].City[cix].Loc >= 0 then
[447]2565 Discover21(RW[pTell].City[cix].Loc, P, lObserveUnhidden, False, True);
[6]2566 for ecix := 0 to RW[pTell].nEnemyCity - 1 do
[2]2567 begin // players know territory, so no use in hiding city owner
[6]2568 Loc := RW[pTell].EnemyCity[ecix].Loc;
2569 if Loc >= 0 then
[2]2570 begin
[6]2571 RealOwner := (RealMap[Loc] shr 27) and $F;
2572 if RealOwner < nPl then
2573 RW[pTell].EnemyCity[ecix].Owner := RealOwner
2574 else
[2]2575 begin
[6]2576 RW[pTell].EnemyCity[ecix].Loc := -1;
[442]2577 RW[pTell].Map[Loc] := RW[pTell].Map[Loc] and not fCity;
[186]2578 end;
2579 end;
2580 end;
[2]2581 end;
2582end;
2583
[447]2584function GetUnitStack(P, Loc: Integer): Integer;
[2]2585var
[447]2586 uix: Integer;
[6]2587 unx: ^TUn;
[2]2588begin
[447]2589 Result := 0;
[6]2590 if Occupant[Loc] < 0 then
[447]2591 Exit;
[6]2592 for uix := 0 to RW[Occupant[Loc]].nUn - 1 do
[2]2593 begin
[6]2594 unx := @RW[Occupant[Loc]].Un[uix];
2595 if unx.Loc = Loc then
[2]2596 begin
[447]2597 MakeUnitInfo(Occupant[Loc], unx^, RW[P].EnemyUn[RW[P].nEnemyUn + Result]);
2598 TellAboutModel(P, Occupant[Loc], unx.mix);
2599 RW[P].EnemyUn[RW[P].nEnemyUn + Result].emix :=
2600 RWemix[P, Occupant[Loc], unx.mix];
2601 Inc(Result);
[186]2602 end;
2603 end;
[2]2604end;
2605
[447]2606procedure UpdateUnitMap(Loc: Integer; CityChange: Boolean = False);
[2]2607// update maps and enemy units of all players after unit change
2608var
[447]2609 P, euix, OldLevel: Integer;
[6]2610 AddFlags, ClearFlags: Cardinal;
[2]2611begin
[6]2612 if (Mode = moLoading_Fast) and not CityChange then
[447]2613 Exit;
2614 for P := 0 to nPl - 1 do
2615 if 1 shl P and (GAlive or GWatching) <> 0 then
[2]2616 begin
[447]2617 OldLevel := ObserveLevel[Loc] shr (2 * P) and 3;
[6]2618 if OldLevel > lNoObserve then
[2]2619 begin
[447]2620 if RW[P].Map[Loc] and (fUnit or fOwned) = fUnit then
[2]2621 begin
[6]2622 // replace unit located here in EnemyUn
2623 // do not just set loc:=-1 because total number would be unlimited
[447]2624 euix := RW[P].nEnemyUn - 1;
[6]2625 while euix >= 0 do
2626 begin
[447]2627 if RW[P].EnemyUn[euix].Loc = Loc then
[6]2628 begin
[447]2629 RW[P].EnemyUn[euix].Loc := -1;
[6]2630 Break;
2631 end;
[447]2632 Dec(euix);
[6]2633 end;
[447]2634 RW[P].Map[Loc] := RW[P].Map[Loc] and not fUnit
[6]2635 end
2636 else
2637 begin // look for empty slot in EnemyUn
[447]2638 euix := RW[P].nEnemyUn - 1;
2639 while (euix >= 0) and (RW[P].EnemyUn[euix].Loc >= 0) do
2640 Dec(euix);
[2]2641 end;
[6]2642 if (Occupant[Loc] < 0) and not CityChange then
2643 begin // calling DiscoverTile not necessary, only clear map flags
2644 ClearFlags := fUnit or fHiddenUnit or fStealthUnit or fOwnZoCUnit;
2645 if RealMap[Loc] and fCity = 0 then
2646 ClearFlags := ClearFlags or fOwned;
[447]2647 RW[P].Map[Loc] := RW[P].Map[Loc] and not ClearFlags;
[6]2648 end
[447]2649 else if (Occupant[Loc] <> P) or CityChange then
[6]2650 begin // city or enemy unit update necessary, call DiscoverTile
[447]2651 ObserveLevel[Loc] := ObserveLevel[Loc] and not(3 shl (2 * P));
2652 DiscoverTile(Loc, P, P, OldLevel, False, euix);
[6]2653 end
2654 else { if (Occupant[Loc]=p) and not CityChange then }
2655 begin // calling DiscoverTile not necessary, only set map flags
2656 ClearFlags := 0;
2657 AddFlags := fUnit or fOwned;
2658 if ZoCMap[Loc] > 0 then
2659 AddFlags := AddFlags or fOwnZoCUnit
2660 else
2661 ClearFlags := ClearFlags or fOwnZoCUnit;
[447]2662 RW[P].Map[Loc] := RW[P].Map[Loc] and not ClearFlags or AddFlags;
[186]2663 end;
2664 end;
2665 end;
[2]2666end;
2667
[447]2668procedure RecalcV8ZoC(P, Loc: Integer);
[2]2669// recalculate fInEnemyZoC flags around single tile
2670var
[447]2671 V8, V8V8, Loc1, Loc2, p1, ObserveMask: Integer;
[6]2672 Tile1: ^Cardinal;
2673 Adjacent, AdjacentAdjacent: TVicinity8Loc;
[2]2674begin
[6]2675 if Mode = moLoading_Fast then
[447]2676 Exit;
2677 ObserveMask := 3 shl (2 * P);
[6]2678 V8_to_Loc(Loc, Adjacent);
2679 for V8 := 0 to 7 do
[2]2680 begin
[6]2681 Loc1 := Adjacent[V8];
2682 if (Loc1 >= 0) and (Loc1 < MapSize) then
[2]2683 begin
[447]2684 Tile1 := @RW[P].Map[Loc1];
[6]2685 Tile1^ := Tile1^ and not fInEnemyZoC;
2686 V8_to_Loc(Loc1, AdjacentAdjacent);
2687 for V8V8 := 0 to 7 do
[2]2688 begin
[6]2689 Loc2 := AdjacentAdjacent[V8V8];
2690 if (Loc2 >= 0) and (Loc2 < MapSize) and (ZoCMap[Loc2] > 0) and
2691 (ObserveLevel[Loc2] and ObserveMask <> 0) then
[2]2692 begin
[6]2693 p1 := Occupant[Loc2];
[447]2694 Assert(p1 <> nPl);
2695 if (p1 <> P) and (RW[P].Treaty[p1] < trAlliance) then
[6]2696 begin
2697 Tile1^ := Tile1^ or fInEnemyZoC;
[442]2698 Break;
[186]2699 end;
2700 end;
[2]2701 end;
[186]2702 end;
2703 end;
[2]2704end;
2705
[447]2706procedure RecalcMapZoC(P: Integer);
[2]2707// recalculate fInEnemyZoC flags for the whole map
2708var
[447]2709 Loc, Loc1, V8, p1, ObserveMask: Integer;
[6]2710 Adjacent: TVicinity8Loc;
[2]2711begin
[6]2712 if Mode = moLoading_Fast then
[447]2713 Exit;
2714 MaskD(RW[P].Map^, MapSize, Cardinal(not Cardinal(fInEnemyZoC)));
2715 ObserveMask := 3 shl (2 * P);
[6]2716 for Loc := 0 to MapSize - 1 do
2717 if (ZoCMap[Loc] > 0) and (ObserveLevel[Loc] and ObserveMask <> 0) then
[2]2718 begin
[6]2719 p1 := Occupant[Loc];
[447]2720 Assert(p1 <> nPl);
2721 if (p1 <> P) and (RW[P].Treaty[p1] < trAlliance) then
[2]2722 begin // this non-allied enemy ZoC unit is known to this player -- set flags!
[6]2723 V8_to_Loc(Loc, Adjacent);
2724 for V8 := 0 to 7 do
[2]2725 begin
[6]2726 Loc1 := Adjacent[V8];
2727 if (Loc1 >= 0) and (Loc1 < MapSize) then
[447]2728 RW[P].Map[Loc1] := RW[P].Map[Loc1] or fInEnemyZoC;
[186]2729 end;
2730 end;
2731 end;
[2]2732end;
2733
[447]2734procedure RecalcPeaceMap(P: Integer);
[2]2735// recalculate fPeace flags for the whole map
2736var
[447]2737 Loc, p1: Integer;
2738 PeacePlayer: array [-1 .. nPl - 1] of Boolean;
[2]2739begin
[6]2740 if Mode <> moPlaying then
[447]2741 Exit;
2742 MaskD(RW[P].Map^, MapSize, Cardinal(not Cardinal(fPeace)));
[6]2743 for p1 := -1 to nPl - 1 do
[447]2744 PeacePlayer[p1] := (p1 >= 0) and (p1 <> P) and (1 shl p1 and GAlive <> 0)
2745 and (RW[P].Treaty[p1] in [trPeace, TrFriendlyContact]);
[6]2746 for Loc := 0 to MapSize - 1 do
[447]2747 if PeacePlayer[RW[P].Territory[Loc]] then
2748 RW[P].Map[Loc] := RW[P].Map[Loc] or fPeace;
[2]2749end;
2750
2751{
[6]2752 Territory Calculation
2753 ____________________________________________________________________
[2]2754}
2755var
[6]2756 BorderChanges: array [0 .. sIntExpandTerritory and $F - 1] of Cardinal;
[2]2757
[447]2758procedure ChangeTerritory(Loc, P: Integer);
[2]2759var
[447]2760 p1: Integer;
[2]2761begin
[447]2762 Assert(P >= 0); // no player's territory indicated by p=nPl
[186]2763 Dec(TerritoryCount[RealMap[Loc] shr 27]);
[447]2764 Inc(TerritoryCount[P]);
2765 RealMap[Loc] := RealMap[Loc] and not($F shl 27) or Cardinal(P) shl 27;
2766 if P = $F then
2767 P := -1;
[6]2768 for p1 := 0 to nPl - 1 do
2769 if 1 shl p1 and (GAlive or GWatching) <> 0 then
2770 if RW[p1].Map[Loc] and fTerrain <> fUNKNOWN then
2771 begin
[447]2772 RW[p1].Territory[Loc] := P;
2773 if (P < nPl) and (P <> p1) and (1 shl P and GAlive <> 0) and
2774 (RW[p1].Treaty[P] in [trPeace, TrFriendlyContact]) then
[6]2775 RW[p1].Map[Loc] := RW[p1].Map[Loc] or fPeace
2776 else
2777 RW[p1].Map[Loc] := RW[p1].Map[Loc] and not fPeace;
[186]2778 end;
[2]2779end;
2780
[447]2781procedure ExpandTerritory(OriginLoc: Integer);
[2]2782var
[447]2783 I, dx, dy, dxMax, dyMax, Loc, NewOwner: Integer;
[2]2784begin
[120]2785 if OriginLoc = -1 then
2786 raise Exception.Create('Location error');
[447]2787 I := 0;
[6]2788 dyMax := 0;
2789 while (dyMax + 1) + (dyMax + 1) shr 1 <= CountryRadius do
[447]2790 Inc(dyMax);
[6]2791 for dy := -dyMax to dyMax do
[2]2792 begin
[6]2793 dxMax := dy and 1;
2794 while abs(dy) + (dxMax + 2) + abs(abs(dy) - (dxMax + 2)) shr 1 <=
2795 CountryRadius do
[447]2796 Inc(dxMax, 2);
[6]2797 for dx := -dxMax to dxMax do
2798 if (dy + dx) and 1 = 0 then
2799 begin
[447]2800 NewOwner := BorderChanges[I div 8] shr (I mod 8 * 4) and $F;
[6]2801 Loc := dLoc(OriginLoc, dx, dy);
2802 if (Loc >= 0) and (Cardinal(NewOwner) <> RealMap[Loc] shr 27) then
2803 ChangeTerritory(Loc, NewOwner);
[447]2804 Inc(I);
[186]2805 end;
2806 end;
[2]2807end;
2808
[447]2809procedure CheckBorders(OriginLoc, PlayerLosingCity: Integer);
[2]2810// OriginLoc: only changes in CountryRadius around this location possible,
[6]2811// -1 for complete map, -2 for double-check (no more changes allowed)
[2]2812// PlayerLosingCity: do nothing but remove tiles no longer in reach from this
[6]2813// player's territory, -1 for full border recalculation
[2]2814var
[447]2815 I, R, Loc, Loc1, dx, dy, p1, p2, cix, NewDist, dxMax, dyMax, OldOwner, V8: Integer;
[45]2816 NewOwner: Cardinal;
[6]2817 Adjacent: TVicinity8Loc;
[447]2818 AtPeace: array [0 .. nPl, 0 .. nPl] of Boolean;
[6]2819 Country, FormerCountry, { to who's country a tile belongs }
2820 Dist, FormerDist, StolenDist: array [0 .. lxmax * lymax - 1] of ShortInt;
[2]2821begin
[6]2822 if PlayerLosingCity >= 0 then
[2]2823 begin
[6]2824 for Loc := 0 to MapSize - 1 do
2825 StolenDist[Loc] := CountryRadius + 1;
2826 for cix := 0 to RW[PlayerLosingCity].nCity - 1 do
2827 if RW[PlayerLosingCity].City[cix].Loc >= 0 then
2828 StolenDist[RW[PlayerLosingCity].City[cix].Loc] := 0;
[2]2829
[447]2830 for R := 1 to CountryRadius shr 1 do
[2]2831 begin
[447]2832 Move(StolenDist, FormerDist, MapSize);
[6]2833 for Loc := 0 to MapSize - 1 do
2834 if (FormerDist[Loc] <= CountryRadius - 2)
2835 // use same conditions as below!
2836 and ((1 shl (RealMap[Loc] and fTerrain)) and
2837 (1 shl fShore + 1 shl fMountains + 1 shl fArctic) = 0) then
[2]2838 begin
[6]2839 V8_to_Loc(Loc, Adjacent);
2840 for V8 := 0 to 7 do
[2]2841 begin
[6]2842 Loc1 := Adjacent[V8];
2843 NewDist := FormerDist[Loc] + 2 + V8 and 1;
2844 if (Loc1 >= 0) and (Loc1 < MapSize) and (NewDist < StolenDist[Loc1])
2845 then
2846 StolenDist[Loc1] := NewDist;
[186]2847 end;
2848 end;
[2]2849 end;
2850 end;
2851
[10]2852 FillChar(Country, MapSize, Byte(-1));
[6]2853 for Loc := 0 to MapSize - 1 do
2854 Dist[Loc] := CountryRadius + 1;
2855 for p1 := 0 to nPl - 1 do
2856 if 1 shl p1 and GAlive <> 0 then
2857 for cix := 0 to RW[p1].nCity - 1 do
2858 if RW[p1].City[cix].Loc >= 0 then
2859 begin
2860 Country[RW[p1].City[cix].Loc] := p1;
2861 Dist[RW[p1].City[cix].Loc] := 0;
2862 end;
[2]2863
[447]2864 for R := 1 to CountryRadius shr 1 do
[2]2865 begin
[447]2866 Move(Country, FormerCountry, MapSize);
2867 Move(Dist, FormerDist, MapSize);
[6]2868 for Loc := 0 to MapSize - 1 do
2869 if (FormerDist[Loc] <= CountryRadius - 2) // use same conditions as above!
2870 and ((1 shl (RealMap[Loc] and fTerrain)) and
2871 (1 shl fShore + 1 shl fMountains + 1 shl fArctic) = 0) then
[2]2872 begin
[447]2873 Assert(FormerCountry[Loc] >= 0);
[6]2874 V8_to_Loc(Loc, Adjacent);
2875 for V8 := 0 to 7 do
[2]2876 begin
[6]2877 Loc1 := Adjacent[V8];
2878 NewDist := FormerDist[Loc] + 2 + V8 and 1;
2879 if (Loc1 >= 0) and (Loc1 < MapSize) and (NewDist < Dist[Loc1]) then
[2]2880 begin
[6]2881 Country[Loc1] := FormerCountry[Loc];
2882 Dist[Loc1] := NewDist;
[186]2883 end;
2884 end;
2885 end;
[2]2886 end;
2887
[447]2888 FillChar(AtPeace, SizeOf(AtPeace), False);
[6]2889 for p1 := 0 to nPl - 1 do
2890 if 1 shl p1 and GAlive <> 0 then
2891 for p2 := 0 to nPl - 1 do
2892 if (p2 <> p1) and (1 shl p2 and GAlive <> 0) and
2893 (RW[p1].Treaty[p2] >= trPeace) then
[447]2894 AtPeace[p1, p2] := True;
[2]2895
[6]2896 if OriginLoc >= 0 then
[2]2897 begin // update area only
[447]2898 I := 0;
[6]2899 FillChar(BorderChanges, SizeOf(BorderChanges), 0);
2900 dyMax := 0;
2901 while (dyMax + 1) + (dyMax + 1) shr 1 <= CountryRadius do
[447]2902 Inc(dyMax);
[6]2903 for dy := -dyMax to dyMax do
[2]2904 begin
[6]2905 dxMax := dy and 1;
2906 while abs(dy) + (dxMax + 2) + abs(abs(dy) - (dxMax + 2)) shr 1 <=
2907 CountryRadius do
[447]2908 Inc(dxMax, 2);
[6]2909 for dx := -dxMax to dxMax do
2910 if (dy + dx) and 1 = 0 then
[2]2911 begin
[6]2912 Loc := dLoc(OriginLoc, dx, dy);
2913 if Loc >= 0 then
2914 begin
2915 OldOwner := RealMap[Loc] shr 27;
2916 NewOwner := Country[Loc] and $F;
2917 if NewOwner <> OldOwner then
2918 if AtPeace[NewOwner, OldOwner] and
2919 not((OldOwner = PlayerLosingCity) and
2920 (StolenDist[Loc] > CountryRadius)) then
2921 NewOwner := OldOwner // peace fixes borders
2922 else
2923 ChangeTerritory(Loc, NewOwner);
[447]2924 BorderChanges[I shr 3] := BorderChanges[I shr 3] or
2925 ((NewOwner shl ((I and 7) * 4)) and $ffffffff);
[6]2926 end;
[447]2927 Inc(I);
[45]2928 end;
2929 end;
[2]2930 end
[6]2931 else
2932 for Loc := 0 to MapSize - 1 do // update complete map
[2]2933 begin
[6]2934 OldOwner := RealMap[Loc] shr 27;
2935 NewOwner := Country[Loc] and $F;
2936 if (NewOwner <> OldOwner) and (not AtPeace[NewOwner, OldOwner] or
2937 ((OldOwner = PlayerLosingCity) and (StolenDist[Loc] > CountryRadius)))
2938 then
2939 begin
[447]2940 Assert(OriginLoc <> -2); // test if border saving works
[6]2941 ChangeTerritory(Loc, NewOwner);
2942 end;
[2]2943 end;
2944
[6]2945{$IFOPT O-} if OriginLoc <> -2 then
2946 CheckBorders(-2); {$ENDIF} // check: single pass should do!
[442]2947end;
[2]2948
[447]2949procedure LogCheckBorders(P, cix, PlayerLosingCity: Integer);
[2]2950begin
[447]2951 CheckBorders(RW[P].City[cix].Loc, PlayerLosingCity);
2952 IntServer(sIntExpandTerritory, P, cix, BorderChanges);
[2]2953end;
2954
2955{
[6]2956 Map Processing
2957 ____________________________________________________________________
[2]2958}
2959
[447]2960procedure CreateUnit(P, mix: Integer);
[2]2961begin
[447]2962 with RW[P] do
[2]2963 begin
[6]2964 Un[nUn].mix := mix;
2965 with Un[nUn] do
[2]2966 begin
[447]2967 ID := UnBuilt[P];
2968 Inc(UnBuilt[P]);
[6]2969 Status := 0;
2970 SavedStatus := 0;
[447]2971 Inc(Model[mix].Built);
[6]2972 Home := -1;
2973 Health := 100;
2974 Flags := 0;
2975 Movement := 0;
2976 if Model[mix].Domain = dAir then
[2]2977 begin
[6]2978 Fuel := Model[mix].Cap[mcFuel];
[442]2979 Flags := Flags or unBombsLoaded;
[2]2980 end;
[6]2981 Job := jNone;
2982 exp := ExpCost shr 1;
2983 TroopLoad := 0;
2984 AirLoad := 0;
2985 Master := -1;
[2]2986 end;
[447]2987 Inc(nUn);
[2]2988 end
2989end;
2990
[447]2991procedure FreeUnit(P, uix: Integer);
[2]2992// loc or master should be set after call
2993// implementation is critical for loading performance, change carefully
2994var
[447]2995 Loc0, uix1: Integer;
2996 Occ, ZoC: Boolean;
[2]2997begin
[447]2998 with RW[P].Un[uix] do
[2]2999 begin
[6]3000 Job := jNone;
3001 Flags := Flags and not(unFortified or unMountainDelay);
[442]3002 Loc0 := Loc;
[2]3003 end;
[6]3004 if Occupant[Loc0] >= 0 then
[2]3005 begin
[447]3006 Assert(Occupant[Loc0] = P);
3007 Occ := False;
3008 ZoC := False;
3009 for uix1 := 0 to RW[P].nUn - 1 do
3010 with RW[P].Un[uix1] do
[6]3011 if (Loc = Loc0) and (Master < 0) and (uix1 <> uix) then
3012 begin
[447]3013 Occ := True;
3014 if RW[P].Model[mix].Flags and mdZOC <> 0 then
[6]3015 begin
[447]3016 ZoC := True;
[442]3017 Break;
3018 end;
[6]3019 end;
3020 if not Occ then
3021 Occupant[Loc0] := -1;
3022 if not ZoC then
3023 ZoCMap[Loc0] := 0;
[2]3024 end;
3025end;
3026
[447]3027procedure PlaceUnit(P, uix: Integer);
[2]3028begin
[447]3029 with RW[P].Un[uix] do
[2]3030 begin
[447]3031 Occupant[Loc] := P;
3032 if RW[P].Model[mix].Flags and mdZOC <> 0 then
[6]3033 ZoCMap[Loc] := 1;
[186]3034 end;
[2]3035end;
3036
[447]3037procedure CountLost(P, mix, Enemy: Integer);
[2]3038begin
[447]3039 Inc(RW[P].Model[mix].Lost);
3040 TellAboutModel(Enemy, P, mix);
3041 Inc(Destroyed[Enemy, P, mix]);
[2]3042end;
3043
[447]3044procedure RemoveUnit(P, uix: Integer; Enemy: Integer = -1);
[2]3045// use enemy only from inside sMoveUnit if attack
3046var
[447]3047 uix1: Integer;
[2]3048begin
[447]3049 with RW[P].Un[uix] do
[2]3050 begin
[447]3051 Assert((Loc >= 0) or (RW[P].Model[mix].Kind = mkDiplomat));
[6]3052 // already freed when spy mission
3053 if Loc >= 0 then
[447]3054 FreeUnit(P, uix);
[6]3055 if Master >= 0 then
[447]3056 if RW[P].Model[mix].Domain = dAir then
3057 Dec(RW[P].Un[Master].AirLoad)
[6]3058 else
[447]3059 Dec(RW[P].Un[Master].TroopLoad);
[6]3060 if (TroopLoad > 0) or (AirLoad > 0) then
[447]3061 for uix1 := 0 to RW[P].nUn - 1 do
3062 if (RW[P].Un[uix1].Loc >= 0) and (RW[P].Un[uix1].Master = uix) then
[6]3063 { unit mastered by removed unit -- remove too }
[2]3064 begin
[447]3065 RW[P].Un[uix1].Loc := -1;
[6]3066 if Enemy >= 0 then
[447]3067 CountLost(P, RW[P].Un[uix1].mix, Enemy);
[2]3068 end;
[6]3069 Loc := -1;
3070 if Enemy >= 0 then
[447]3071 CountLost(P, mix, Enemy);
[186]3072 end;
3073end;
[2]3074
[447]3075procedure RemoveUnit_UpdateMap(P, uix: Integer);
[2]3076var
[186]3077 Loc0: Integer;
[2]3078begin
[447]3079 Loc0 := RW[P].Un[uix].Loc;
3080 RemoveUnit(P, uix);
[6]3081 if Mode > moLoading_Fast then
3082 UpdateUnitMap(Loc0);
[2]3083end;
3084
[447]3085procedure RemoveAllUnits(P, Loc: Integer; Enemy: Integer = -1);
[2]3086var
[447]3087 uix: Integer;
[2]3088begin
[447]3089 for uix := 0 to RW[P].nUn - 1 do
3090 if RW[P].Un[uix].Loc = Loc then
[2]3091 begin
[6]3092 if Enemy >= 0 then
[447]3093 CountLost(P, RW[P].Un[uix].mix, Enemy);
3094 RW[P].Un[uix].Loc := -1;
[2]3095 end;
[6]3096 Occupant[Loc] := -1;
3097 ZoCMap[Loc] := 0;
[2]3098end;
3099
[447]3100procedure RemoveDomainUnits(D, P, Loc: Integer);
[2]3101var
[447]3102 uix: Integer;
[2]3103begin
[447]3104 for uix := 0 to RW[P].nUn - 1 do
3105 if (RW[P].Model[RW[P].Un[uix].mix].Domain = D) and (RW[P].Un[uix].Loc = Loc)
[6]3106 then
[447]3107 RemoveUnit(P, uix);
[2]3108end;
3109
[447]3110procedure FoundCity(P, FoundLoc: Integer);
[2]3111var
[447]3112 p1, cix1, V21, dx, dy: Integer;
[2]3113begin
[447]3114 if RW[P].nCity = ncmax then
3115 Exit;
3116 Inc(RW[P].nCity);
3117 with RW[P].City[RW[P].nCity - 1] do
[2]3118 begin
[6]3119 Size := 2;
3120 Status := 0;
3121 SavedStatus := 0;
3122 FillChar(Built, SizeOf(Built), 0);
3123 Food := 0;
3124 Project := cpImp + imTrGoods;
3125 Prod := 0;
3126 Project0 := Project;
3127 Prod0 := 0;
3128 Pollution := 0;
3129 N1 := 0;
3130 Loc := FoundLoc;
3131 if UsedByCity[FoundLoc] >= 0 then
3132 begin { central tile is exploited - toggle in exploiting city }
[447]3133 p1 := P;
[6]3134 SearchCity(UsedByCity[FoundLoc], p1, cix1);
3135 dxdy(UsedByCity[FoundLoc], FoundLoc, dx, dy);
3136 V21 := (dy + 3) shl 2 + (dx + 3) shr 1;
3137 RW[p1].City[cix1].Tiles := RW[p1].City[cix1].Tiles and not(1 shl V21);
[2]3138 end;
[6]3139 Tiles := 1 shl 13; { exploit central tile }
3140 UsedByCity[FoundLoc] := FoundLoc;
3141 RealMap[FoundLoc] := RealMap[FoundLoc] and
3142 (fTerrain or fSpecial or fRiver or nPl shl 27) or fCity;
3143
[447]3144 ChangeTerritory(Loc, P);
[2]3145 end;
[186]3146end;
[2]3147
[447]3148procedure StealCity(P, cix: Integer; SaveUnits: Boolean);
[2]3149var
[447]3150 I, J, uix1, cix1, nearest: Integer;
[2]3151begin
[447]3152 for I := 0 to nWonder - 1 do
3153 if RW[P].City[cix].Built[I] = 1 then
[2]3154 begin
[447]3155 GWonder[I].EffectiveOwner := -1;
3156 if I = woPyramids then
[6]3157 FreeSlaves;
[447]3158 if I = woEiffel then // deactivate expired wonders
3159 for J := 0 to nWonder - 1 do
3160 if GWonder[J].EffectiveOwner = P then
3161 CheckExpiration(J);
[2]3162 end;
[447]3163 for I := nWonder to nImp - 1 do
3164 if (Imp[I].Kind <> ikCommon) and (RW[P].City[cix].Built[I] > 0) then
[6]3165 begin { destroy national projects }
[447]3166 RW[P].NatBuilt[I] := 0;
3167 if I = imGrWall then
3168 GrWallContinent[P] := -1;
[2]3169 end;
3170
[447]3171 for uix1 := 0 to RW[P].nUn - 1 do
3172 with RW[P].Un[uix1] do
[6]3173 if (Loc >= 0) and (Home = cix) then
3174 if SaveUnits then
3175 begin // support units by nearest other city
3176 nearest := -1;
[447]3177 for cix1 := 0 to RW[P].nCity - 1 do
3178 if (cix1 <> cix) and (RW[P].City[cix1].Loc >= 0) and
3179 ((nearest < 0) or (Distance(RW[P].City[cix1].Loc, Loc) <
3180 Distance(RW[P].City[nearest].Loc, Loc))) then
[6]3181 nearest := cix1;
[442]3182 Home := nearest;
[6]3183 end
3184 else
[447]3185 RemoveUnit(P, uix1); // destroy supported units
[186]3186end;
[2]3187
[447]3188procedure DestroyCity(P, cix: Integer; SaveUnits: Boolean);
[2]3189var
[447]3190 I, V21: Integer;
[6]3191 Radius: TVicinity21Loc;
[2]3192begin
[447]3193 StealCity(P, cix, SaveUnits);
3194 with RW[P].City[cix] do begin
3195 for I := 0 to nWonder - 1 do
3196 if Built[I] > 0 then
3197 GWonder[I].CityID := WonderDestroyed;
[6]3198 V21_to_Loc(Loc, Radius);
3199 for V21 := 1 to 26 do
3200 if 1 shl V21 and Tiles <> 0 then
3201 UsedByCity[Radius[V21]] := -1;
3202 RealMap[Loc] := RealMap[Loc] and not fCity;
[447]3203 Loc := -1;
[186]3204 end;
3205end;
[2]3206
[447]3207procedure ChangeCityOwner(pOld, cixOld, pNew: Integer);
[2]3208var
[447]3209 I, J, cix1, Loc1, V21: Integer;
[6]3210 Radius: TVicinity21Loc;
[2]3211begin
[447]3212 Inc(RW[pNew].nCity);
[6]3213 RW[pNew].City[RW[pNew].nCity - 1] := RW[pOld].City[cixOld];
[447]3214 StealCity(pOld, cixOld, False);
[6]3215 RW[pOld].City[cixOld].Loc := -1;
3216 with RW[pNew].City[(RW[pNew].nCity - 1)] do
[2]3217 begin
[6]3218 Food := 0;
3219 Project := cpImp + imTrGoods;
3220 Prod := 0;
3221 Project0 := Project;
3222 Prod0 := 0;
3223 Status := 0;
3224 SavedStatus := 0;
3225 N1 := 0;
[2]3226
[6]3227 // check for siege
3228 V21_to_Loc(Loc, Radius);
3229 for V21 := 1 to 26 do
3230 if Tiles and (1 shl V21) and not(1 shl CityOwnTile) <> 0 then
3231 begin
3232 Loc1 := Radius[V21];
[447]3233 Assert((Loc1 >= 0) and (Loc1 < MapSize) and (UsedByCity[Loc1] = Loc));
[6]3234 if (ZoCMap[Loc1] > 0) and (Occupant[Loc1] <> pNew) and
3235 (RW[pNew].Treaty[Occupant[Loc1]] < trAlliance) then
3236 begin // tile can't remain exploited
3237 Tiles := Tiles and not(1 shl V21);
3238 UsedByCity[Loc1] := -1;
3239 end;
3240 // don't check for siege by peace territory here, because territory
3241 // might not be up to date -- done in turn beginning anyway
[2]3242 end;
[6]3243 Built[imTownHall] := 0;
3244 Built[imCourt] := 0;
[447]3245 for I := nWonder to nImp - 1 do
3246 if Imp[I].Kind <> ikCommon then
3247 Built[I] := 0; { destroy national projects }
3248 for I := 0 to nWonder - 1 do
3249 if Built[I] = 1 then
[2]3250 begin // new wonder owner!
[447]3251 GWonder[I].EffectiveOwner := pNew;
3252 if I = woEiffel then // reactivate expired wonders
[2]3253 begin
[447]3254 for J := 0 to nWonder - 1 do
3255 if Imp[J].Expiration >= 0 then
[6]3256 for cix1 := 0 to (RW[pNew].nCity - 1) do
[447]3257 if RW[pNew].City[cix1].Built[J] = 1 then
3258 GWonder[J].EffectiveOwner := pNew;
[2]3259 end
[6]3260 else
[447]3261 CheckExpiration(I);
3262 case I of
[6]3263 woLighthouse:
3264 CheckSpecialModels(pNew, preLighthouse);
3265 woLeo:
3266 CheckSpecialModels(pNew, preLeo);
3267 woPyramids:
3268 CheckSpecialModels(pNew, preBuilder);
[2]3269 end;
3270 end;
3271
[6]3272 // remove city from enemy cities
3273 // not done by Discover, because fCity still set!
3274 cix1 := RW[pNew].nEnemyCity - 1;
3275 while (cix1 >= 0) and (RW[pNew].EnemyCity[cix1].Loc <> Loc) do
[447]3276 Dec(cix1);
3277 Assert(cix1 >= 0);
[6]3278 RW[pNew].EnemyCity[cix1].Loc := -1;
[2]3279
[6]3280 ChangeTerritory(Loc, pNew);
[2]3281 end;
[186]3282end;
[2]3283
[447]3284procedure CompleteJob(P, Loc, Job: Integer);
[2]3285var
[447]3286 ChangedTerrain, p1: Integer;
[2]3287begin
[447]3288 Assert(Job <> jCity);
[6]3289 ChangedTerrain := -1;
3290 case Job of
3291 jRoad:
3292 RealMap[Loc] := RealMap[Loc] or fRoad;
3293 jRR:
3294 RealMap[Loc] := RealMap[Loc] and not fRoad or fRR;
3295 jClear:
[2]3296 begin
[6]3297 ChangedTerrain := Terrain[RealMap[Loc] and fTerrain].ClearTerrain;
3298 RealMap[Loc] := RealMap[Loc] and not fTerrain or
3299 Cardinal(ChangedTerrain);
3300 RealMap[Loc] := RealMap[Loc] and not(3 shl 5) or
3301 ActualSpecialTile(Loc) shl 5;
[2]3302 end;
[6]3303 jIrr:
3304 RealMap[Loc] := RealMap[Loc] and not fTerImp or tiIrrigation;
3305 jFarm:
3306 RealMap[Loc] := RealMap[Loc] and not fTerImp or tiFarm;
3307 jAfforest:
[2]3308 begin
[6]3309 ChangedTerrain := Terrain[RealMap[Loc] and fTerrain].AfforestTerrain;
3310 RealMap[Loc] := RealMap[Loc] and not fTerrain or
3311 Cardinal(ChangedTerrain);
3312 RealMap[Loc] := RealMap[Loc] and not(3 shl 5) or
3313 ActualSpecialTile(Loc) shl 5;
3314 end;
3315 jMine:
3316 RealMap[Loc] := RealMap[Loc] and not fTerImp or tiMine;
3317 jFort:
3318 RealMap[Loc] := RealMap[Loc] and not fTerImp or tiFort;
3319 jCanal:
3320 RealMap[Loc] := RealMap[Loc] or fCanal;
3321 jTrans:
3322 begin
3323 ChangedTerrain := Terrain[RealMap[Loc] and fTerrain].TransTerrain;
3324 RealMap[Loc] := RealMap[Loc] and not fTerrain or
3325 Cardinal(ChangedTerrain);
3326 RealMap[Loc] := RealMap[Loc] and not(3 shl 5) or
3327 ActualSpecialTile(Loc) shl 5;
3328 if not(RealMap[Loc] and fTerrain in TerrType_Canalable) then
3329 begin
[447]3330 RemoveDomainUnits(dSea, P, Loc);
[6]3331 RealMap[Loc] := RealMap[Loc] and not fCanal;
3332 end;
3333 end;
3334 jPoll:
3335 RealMap[Loc] := RealMap[Loc] and not fPoll;
3336 jBase:
3337 RealMap[Loc] := RealMap[Loc] and not fTerImp or tiBase;
3338 jPillage:
3339 if RealMap[Loc] and fTerImp <> 0 then
3340 begin
3341 if RealMap[Loc] and fTerImp = tiBase then
[447]3342 RemoveDomainUnits(dAir, P, Loc);
[6]3343 RealMap[Loc] := RealMap[Loc] and not fTerImp
[2]3344 end
[6]3345 else if RealMap[Loc] and fCanal <> 0 then
[2]3346 begin
[447]3347 RemoveDomainUnits(dSea, P, Loc);
[6]3348 RealMap[Loc] := RealMap[Loc] and not fCanal
[2]3349 end
[6]3350 else if RealMap[Loc] and fRR <> 0 then
3351 RealMap[Loc] := RealMap[Loc] and not fRR or fRoad
3352 else if RealMap[Loc] and fRoad <> 0 then
3353 RealMap[Loc] := RealMap[Loc] and not fRoad;
[2]3354 end;
[6]3355 if ChangedTerrain >= 0 then
[2]3356 begin // remove terrain improvements if not possible on new terrain
[6]3357 if ((RealMap[Loc] and fTerImp = tiIrrigation) or
3358 (RealMap[Loc] and fTerImp = tiFarm)) and
3359 ((Terrain[ChangedTerrain].IrrClearWork = 0) or
3360 (Terrain[ChangedTerrain].ClearTerrain >= 0)) then
3361 RealMap[Loc] := RealMap[Loc] and not fTerImp;
3362 if (RealMap[Loc] and fTerImp = tiMine) and
3363 ((Terrain[ChangedTerrain].MineAfforestWork = 0) or
3364 (Terrain[ChangedTerrain].AfforestTerrain >= 0)) then
3365 RealMap[Loc] := RealMap[Loc] and not fTerImp;
[2]3366 end;
3367
[6]3368 // update map of all observing players
3369 if Mode > moLoading_Fast then
3370 for p1 := 0 to nPl - 1 do
3371 if (1 shl p1 and (GAlive or GWatching) <> 0) and
3372 (ObserveLevel[Loc] shr (2 * p1) and 3 > lNoObserve) then
3373 RW[p1].Map[Loc] := RW[p1].Map[Loc] and
3374 not(fTerrain or fSpecial or fTerImp or fRoad or fRR or fCanal or
3375 fPoll) or RealMap[Loc] and (fTerrain or fSpecial or fTerImp or
3376 fRoad or fRR or fCanal or fPoll);
[442]3377end;
[2]3378
3379{
[6]3380 Diplomacy
3381 ____________________________________________________________________
[2]3382}
[447]3383procedure GiveCivilReport(P, pAbout: Integer);
[2]3384begin
[447]3385 with RW[P].EnemyReport[pAbout]^ do
[2]3386 begin
[6]3387 // general info
3388 TurnOfCivilReport := LastValidStat[pAbout];
[447]3389 Move(RW[pAbout].Treaty, Treaty, SizeOf(Treaty));
[6]3390 Government := RW[pAbout].Government;
3391 Money := RW[pAbout].Money;
[2]3392
[6]3393 // tech info
3394 ResearchTech := RW[pAbout].ResearchTech;
3395 ResearchDone := RW[pAbout].Research * 100 div TechCost(pAbout);
3396 if ResearchDone > 100 then
3397 ResearchDone := 100;
[447]3398 Move(RW[pAbout].Tech, Tech, nAdv);
[2]3399 end;
3400end;
3401
[447]3402procedure GiveMilReport(P, pAbout: Integer);
[2]3403var
[447]3404 uix, mix: Integer;
[2]3405begin
[447]3406 with RW[P].EnemyReport[pAbout]^ do
[2]3407 begin
[6]3408 TurnOfMilReport := LastValidStat[pAbout];
3409 nModelCounted := RW[pAbout].nModel;
3410 for mix := 0 to RW[pAbout].nModel - 1 do
3411 begin
[447]3412 TellAboutModel(P, pAbout, mix);
[6]3413 UnCount[mix] := 0
3414 end;
3415 for uix := 0 to RW[pAbout].nUn - 1 do
3416 if RW[pAbout].Un[uix].Loc >= 0 then
[447]3417 Inc(UnCount[RW[pAbout].Un[uix].mix]);
[186]3418 end;
[2]3419end;
3420
[447]3421procedure ShowPrice(pSender, pTarget, Price: Integer);
[2]3422begin
[6]3423 case Price and opMask of
3424 opTech: // + advance
3425 with RW[pTarget].EnemyReport[pSender]^ do
3426 if Tech[Price - opTech] < tsApplicable then
3427 Tech[Price - opTech] := tsApplicable;
3428 opModel: // + model index
3429 TellAboutModel(pTarget, pSender, Price - opModel);
3430 { opCity: // + city ID
3431 begin
3432 end; }
[186]3433 end;
[2]3434end;
3435
[447]3436function CopyCivilReport(pSender, pTarget, pAbout: Integer): Boolean;
[2]3437var
[447]3438 I: Integer;
[6]3439 rSender, rTarget: ^TEnemyReport;
[2]3440begin // copy third nation civil report
[447]3441 Result := False;
[6]3442 if RW[pTarget].Treaty[pAbout] = trNoContact then
3443 IntroduceEnemy(pTarget, pAbout);
[447]3444 rSender := Pointer(RW[pSender].EnemyReport[pAbout]);
3445 rTarget := Pointer(RW[pTarget].EnemyReport[pAbout]);
[6]3446 if rSender.TurnOfCivilReport > rTarget.TurnOfCivilReport then
[2]3447 begin // only if newer than current information
[6]3448 rTarget.TurnOfCivilReport := rSender.TurnOfCivilReport;
3449 rTarget.Treaty := rSender.Treaty;
3450 rTarget.Government := rSender.Government;
3451 rTarget.Money := rSender.Money;
3452 rTarget.ResearchTech := rSender.ResearchTech;
3453 rTarget.ResearchDone := rSender.ResearchDone;
[447]3454 Result := True;
[2]3455 end;
[447]3456 for I := 0 to nAdv - 1 do
3457 if rTarget.Tech[I] < rSender.Tech[I] then
[2]3458 begin
[447]3459 rTarget.Tech[I] := rSender.Tech[I];
3460 Result := True;
[186]3461 end;
[2]3462end;
3463
[447]3464function CopyMilReport(pSender, pTarget, pAbout: Integer): Boolean;
[2]3465var
[447]3466 mix: Integer;
[6]3467 rSender, rTarget: ^TEnemyReport;
[2]3468begin // copy third nation military report
[447]3469 Result := False;
[6]3470 if RW[pTarget].Treaty[pAbout] = trNoContact then
3471 IntroduceEnemy(pTarget, pAbout);
[447]3472 rSender := Pointer(RW[pSender].EnemyReport[pAbout]);
3473 rTarget := Pointer(RW[pTarget].EnemyReport[pAbout]);
[6]3474 if rSender.TurnOfMilReport > rTarget.TurnOfMilReport then
[2]3475 begin // only if newer than current information
[6]3476 rTarget.TurnOfMilReport := rSender.TurnOfMilReport;
3477 rTarget.nModelCounted := rSender.nModelCounted;
[447]3478 Move(rSender.UnCount, rTarget.UnCount, 2 * rSender.nModelCounted);
[6]3479 for mix := 0 to rTarget.nModelCounted - 1 do
3480 TellAboutModel(pTarget, pAbout, mix);
[447]3481 Result := True;
[186]3482 end;
[2]3483end;
3484
[447]3485procedure CopyModel(pSender, pTarget, mix: Integer);
[2]3486var
[447]3487 I: Integer;
[6]3488 miSender, miTarget: TModelInfo;
[447]3489 ok: Boolean;
[2]3490begin
[6]3491 // only if target doesn't already have a model like this
3492 ok := RW[pTarget].nModel < nmmax;
3493 MakeModelInfo(pSender, mix, RW[pSender].Model[mix], miSender);
[447]3494 for I := 0 to RW[pTarget].nModel - 1 do
[2]3495 begin
[447]3496 MakeModelInfo(pTarget, I, RW[pTarget].Model[I], miTarget);
[6]3497 if IsSameModel(miSender, miTarget) then
[447]3498 ok := False;
[2]3499 end;
[6]3500 if ok then
[2]3501 begin
[6]3502 RW[pTarget].Model[RW[pTarget].nModel] := RW[pSender].Model[mix];
3503 with RW[pTarget].Model[RW[pTarget].nModel] do
[2]3504 begin
[6]3505 IntroTurn := GTurn;
3506 if Kind = mkSelfDeveloped then
3507 Kind := mkEnemyDeveloped;
3508 Status := 0;
3509 SavedStatus := 0;
3510 Built := 0;
3511 Lost := 0;
[2]3512 end;
[447]3513 Inc(RW[pTarget].nModel);
3514 Inc(Researched[pTarget]);
[6]3515 TellAboutModel(pSender, pTarget, RW[pTarget].nModel - 1);
[186]3516 end;
[2]3517end;
3518
[447]3519procedure CopyMap(pSender, pTarget: Integer);
[2]3520var
[447]3521 Loc, I, cix: Integer;
[6]3522 Tile: Cardinal;
[2]3523begin
[6]3524 for Loc := 0 to MapSize - 1 do
3525 if (RW[pSender].MapObservedLast[Loc] > RW[pTarget].MapObservedLast[Loc])
3526 then
[2]3527 begin
[6]3528 Tile := RW[pSender].Map[Loc];
3529 if Tile and fCity <> 0 then
[2]3530 begin
[447]3531 I := 0;
3532 while (I < RW[pTarget].nEnemyCity) and
3533 (RW[pTarget].EnemyCity[I].Loc <> Loc) do
3534 Inc(I);
3535 if I = RW[pTarget].nEnemyCity then
[2]3536 begin
[447]3537 Inc(RW[pTarget].nEnemyCity);
3538 Assert(RW[pTarget].nEnemyCity < necmax);
3539 RW[pTarget].EnemyCity[I].Status := 0;
3540 RW[pTarget].EnemyCity[I].SavedStatus := 0;
[2]3541 end;
[6]3542 if Tile and fOwned <> 0 then
[2]3543 begin // city owned by sender -- create new info
[6]3544 cix := RW[pSender].nCity - 1;
3545 while (cix >= 0) and (RW[pSender].City[cix].Loc <> Loc) do
[447]3546 Dec(cix);
3547 MakeCityInfo(pSender, cix, RW[pTarget].EnemyCity[I]);
[2]3548 end
[6]3549 else // city not owned by sender -- copy old info
[2]3550 begin
[6]3551 cix := RW[pSender].nEnemyCity - 1;
3552 while (cix >= 0) and (RW[pSender].EnemyCity[cix].Loc <> Loc) do
[447]3553 Dec(cix);
3554 RW[pTarget].EnemyCity[I] := RW[pSender].EnemyCity[cix];
[2]3555 end;
3556 end
[6]3557 else if RW[pTarget].Map[Loc] and fCity <> 0 then // remove enemycity
3558 for cix := 0 to RW[pTarget].nEnemyCity - 1 do
3559 if RW[pTarget].EnemyCity[cix].Loc = Loc then
3560 RW[pTarget].EnemyCity[cix].Loc := -1;
[2]3561
[6]3562 Tile := Tile and (not(fSpecial or fModern) or ResourceMask[pTarget]);
3563 Tile := Tile or (RW[pTarget].Map[Loc] and fModern);
3564 if (Tile and fTerrain = RW[pTarget].Map[Loc] and fTerrain) then
3565 Tile := Tile or (RW[pTarget].Map[Loc] and fSpecial);
[2]3566
[6]3567 if RW[pTarget].Map[Loc] and fTerrain = fUNKNOWN then
[447]3568 Inc(Discovered[pTarget]);
[6]3569 RW[pTarget].Map[Loc] := RW[pTarget].Map[Loc] and fInEnemyZoC
3570 // always preserve this flag!
3571 or Tile and not(fUnit or fHiddenUnit or fStealthUnit or fObserved or
3572 fSpiedOut or fOwned or fInEnemyZoC or fOwnZoCUnit or fPeace or fGrWall);
3573 if RW[pSender].Territory[Loc] <> RW[pTarget].Territory[Loc] then
[2]3574 begin
[6]3575 RW[pTarget].Territory[Loc] := RW[pSender].Territory[Loc];
3576 { if RW[pTarget].BorderHelper<>nil then
3577 RW[pTarget].BorderHelper[Loc]:=0; }
[2]3578 end;
[6]3579 RW[pTarget].Territory[Loc] := RW[pSender].Territory[Loc];
3580 RW[pTarget].MapObservedLast[Loc] := RW[pSender].MapObservedLast[Loc];
[2]3581 end;
3582end;
3583
[447]3584function PayPrice(pSender, pTarget, Price: Integer; execute: Boolean): Boolean;
[2]3585var
[447]3586 pSubject, I, N, NewTreaty: Integer;
[2]3587begin
[447]3588 Result := True;
[6]3589 case Price and opMask of
3590 opCivilReport: // + turn + concerned player shl 16
[2]3591 begin
[6]3592 pSubject := Price shr 16 and $F;
3593 if pTarget = pSubject then
[447]3594 Result := False
[6]3595 else if pSender = pSubject then
3596 begin
3597 if execute then
[442]3598 GiveCivilReport(pTarget, pSender);
[6]3599 end
3600 else if RW[pSender].EnemyReport[pSubject].TurnOfCivilReport < 0 then
[447]3601 Result := False
[6]3602 else if execute then
3603 CopyCivilReport(pSender, pTarget, pSubject);
3604 end;
3605 opMilReport: // + turn + concerned player shl 16
[2]3606 begin
[6]3607 pSubject := Price shr 16 and $F;
3608 if pTarget = pSubject then
[447]3609 Result := False
[6]3610 else if pSender = pSubject then
3611 begin
3612 if execute then
[442]3613 GiveMilReport(pTarget, pSender);
[6]3614 end
3615 else if RW[pSender].EnemyReport[pSubject].TurnOfMilReport < 0 then
[447]3616 Result := False
[6]3617 else if execute then
[442]3618 CopyMilReport(pSender, pTarget, pSubject);
[6]3619 end;
3620 opMap:
3621 if execute then
[2]3622 begin
[6]3623 CopyMap(pSender, pTarget);
3624 RecalcPeaceMap(pTarget);
[2]3625 end;
[6]3626 opTreaty .. opTreaty + trAlliance: // + nation treaty
[2]3627 begin
[6]3628 if Price - opTreaty = RW[pSender].Treaty[pTarget] - 1 then
3629 begin // agreed treaty end
3630 if execute then
[447]3631 CancelTreaty(pSender, pTarget, False);
[6]3632 end
3633 else
[2]3634 begin
[6]3635 NewTreaty := -1;
3636 if Price - opTreaty = RW[pSender].Treaty[pTarget] + 1 then
3637 NewTreaty := Price - opTreaty
3638 else if (RW[pSender].Treaty[pTarget] = trNone) and
3639 (Price - opTreaty = trPeace) then
3640 NewTreaty := trPeace;
3641 if NewTreaty < 0 then
[447]3642 Result := False
[6]3643 else if execute then
[2]3644 begin
[447]3645 Assert(NewTreaty > RW[pSender].Treaty[pTarget]);
[6]3646 RW[pSender].Treaty[pTarget] := NewTreaty;
3647 RW[pTarget].Treaty[pSender] := NewTreaty;
3648 if NewTreaty >= TrFriendlyContact then
3649 begin
3650 GiveCivilReport(pTarget, pSender);
3651 GiveCivilReport(pSender, pTarget);
3652 end;
3653 if NewTreaty = trAlliance then
3654 begin
3655 GiveMilReport(pTarget, pSender);
3656 GiveMilReport(pSender, pTarget);
3657 CopyMap(pSender, pTarget);
3658 CopyMap(pTarget, pSender);
3659 RecalcMapZoC(pSender);
3660 RecalcMapZoC(pTarget);
3661 end;
3662 if not(NewTreaty in [trPeace, TrFriendlyContact]) then
3663 begin
3664 RW[pSender].EvaStart[pTarget] := -PeaceEvaTurns - 1;
3665 RW[pTarget].EvaStart[pSender] := -PeaceEvaTurns - 1;
3666 end;
3667 RecalcPeaceMap(pSender);
3668 RecalcPeaceMap(pTarget);
[186]3669 end;
3670 end;
[6]3671 end;
3672 opShipParts: // + number + part type shl 16
[2]3673 begin
[447]3674 N := Price and $FFFF; // number
3675 I := Price shr 16 and $F; // type
3676 if (I < nShipPart) and (GShip[pSender].Parts[I] >= N) then
[2]3677 begin
[6]3678 if execute then
3679 begin
[447]3680 Dec(GShip[pSender].Parts[I], N);
3681 RW[pSender].Ship[pSender].Parts[I] := GShip[pSender].Parts[I];
3682 RW[pTarget].Ship[pSender].Parts[I] := GShip[pSender].Parts[I];
[6]3683 if RW[pTarget].NatBuilt[imSpacePort] > 0 then
3684 begin // space ship control requires space port
[447]3685 Inc(GShip[pTarget].Parts[I], N);
3686 RW[pSender].Ship[pTarget].Parts[I] := GShip[pTarget].Parts[I];
3687 RW[pTarget].Ship[pTarget].Parts[I] := GShip[pTarget].Parts[I];
[186]3688 end;
3689 end;
[2]3690 end
[6]3691 else
[447]3692 Result := False;
[6]3693 end;
3694 opMoney: // + value
3695 if (Price - opMoney <= MaxMoneyPrice) and
3696 (RW[pSender].Money >= Price - opMoney) then
[2]3697 begin
[6]3698 if execute then
[2]3699 begin
[447]3700 Dec(RW[pSender].Money, Price - opMoney);
3701 Inc(RW[pTarget].Money, Price - opMoney);
[186]3702 end;
[2]3703 end
[6]3704 else
[447]3705 Result := False;
[6]3706 opTribute: // + value
3707 if execute then
[2]3708 begin
3709 end;
[6]3710 opTech: // + advance
3711 if RW[pSender].Tech[Price - opTech] >= tsApplicable then
[2]3712 begin
[6]3713 if execute and (RW[pTarget].Tech[Price - opTech] = tsNA) then
[2]3714 begin
[6]3715 SeeTech(pTarget, Price - opTech);
3716 RW[pSender].EnemyReport[pTarget].Tech[Price - opTech] := tsSeen;
[186]3717 end;
[2]3718 end
[6]3719 else
[447]3720 Result := False;
[6]3721 opAllTech:
3722 if execute then
[447]3723 for I := 0 to nAdv - 1 do
3724 if (RW[pSender].Tech[I] >= tsApplicable) and
3725 (RW[pTarget].Tech[I] = tsNA) then
[6]3726 begin
[447]3727 SeeTech(pTarget, I);
3728 RW[pSender].EnemyReport[pTarget].Tech[I] := tsSeen;
3729 RW[pTarget].EnemyReport[pSender].Tech[I] := tsApplicable;
[6]3730 end;
3731 opModel: // + model index
3732 if Price - opModel < RW[pSender].nModel then
3733 begin
3734 if execute then
[442]3735 CopyModel(pSender, pTarget, Price - opModel);
[6]3736 end
3737 else
[447]3738 Result := False;
[6]3739 opAllModel:
3740 if execute then
[447]3741 for I := 0 to RW[pSender].nModel - 1 do
[2]3742 begin
[447]3743 TellAboutModel(pTarget, pSender, I);
3744 CopyModel(pSender, pTarget, I);
[2]3745 end;
[6]3746 { opCity: // + city ID
[2]3747 begin
[447]3748 Result:=False
[6]3749 end; }
[442]3750 end;
[2]3751end;
3752
[447]3753procedure CancelTreaty(P, pWith: Integer; DecreaseCredibility: Boolean);
[2]3754// side effect: PeaceEnded := bitarray of players with which peace treaty was canceled
3755var
[447]3756 p1, OldTreaty: Integer;
[2]3757begin
[447]3758 OldTreaty := RW[P].Treaty[pWith];
[6]3759 PeaceEnded := 0;
3760 if OldTreaty >= trPeace then
[447]3761 RW[P].LastCancelTreaty[pWith] := GTurn;
[6]3762 if DecreaseCredibility then
[2]3763 begin
[6]3764 case OldTreaty of
3765 trPeace:
3766 begin
[447]3767 RW[P].Credibility := RW[P].Credibility shr 1;
3768 if RW[P].MaxCredibility > 0 then
3769 Dec(RW[P].MaxCredibility, 10);
3770 if RW[P].Credibility > RW[P].MaxCredibility then
3771 RW[P].Credibility := RW[P].MaxCredibility;
[6]3772 end;
3773 trAlliance:
[447]3774 RW[P].Credibility := RW[P].Credibility * 3 div 4;
[2]3775 end;
[447]3776 RW[pWith].EnemyReport[P].Credibility := RW[P].Credibility;
[2]3777 end;
3778
[6]3779 if OldTreaty = trPeace then
[2]3780 begin
[6]3781 for p1 := 0 to nPl - 1 do
[447]3782 if (p1 = pWith) or DecreaseCredibility and (p1 <> P) and
3783 (RW[pWith].Treaty[p1] = trAlliance) and (RW[P].Treaty[p1] >= trPeace)
[6]3784 then
[2]3785 begin
[447]3786 RW[P].Treaty[p1] := trNone;
3787 RW[p1].Treaty[P] := trNone;
3788 RW[P].EvaStart[p1] := -PeaceEvaTurns - 1;
3789 RW[p1].EvaStart[P] := -PeaceEvaTurns - 1;
3790 Inc(PeaceEnded, 1 shl p1);
[2]3791 end;
[6]3792 CheckBorders(-1);
3793 if (Mode > moLoading_Fast) and (PeaceEnded > 0) then
[447]3794 RecalcMapZoC(P);
[2]3795 end
[6]3796 else
[2]3797 begin
[447]3798 RW[P].Treaty[pWith] := OldTreaty - 1;
3799 RW[pWith].Treaty[P] := OldTreaty - 1;
[6]3800 if OldTreaty = TrFriendlyContact then
[2]3801 begin // necessary for loading
[447]3802 GiveCivilReport(P, pWith);
3803 GiveCivilReport(pWith, P);
[2]3804 end
[6]3805 else if OldTreaty = trAlliance then
[2]3806 begin // necessary for loading
[447]3807 GiveMilReport(P, pWith);
3808 GiveMilReport(pWith, P);
[2]3809 end;
[6]3810 if (Mode > moLoading_Fast) and (OldTreaty = trAlliance) then
[2]3811 begin
[447]3812 RecalcMapZoC(P);
[6]3813 RecalcMapZoC(pWith);
[186]3814 end;
[2]3815 end;
[6]3816 if OldTreaty in [trPeace, trAlliance] then
[2]3817 begin
[447]3818 RecalcPeaceMap(P);
[6]3819 RecalcPeaceMap(pWith);
[186]3820 end;
[2]3821end;
3822
[447]3823function DoSpyMission(P, pCity, cix, Mission: Integer): Cardinal;
[2]3824var
[447]3825 p1: Integer;
[2]3826begin
[447]3827 Result := 0;
[6]3828 case Mission of
3829 smSabotageProd:
3830 RW[pCity].City[cix].Flags := RW[pCity].City[cix].Flags or
3831 chProductionSabotaged;
3832 smStealMap:
[2]3833 begin
[447]3834 CopyMap(pCity, P);
3835 RecalcPeaceMap(P);
[6]3836 end;
3837 smStealCivilReport:
3838 begin
[447]3839 if RW[P].Treaty[pCity] = trNoContact then
3840 IntroduceEnemy(P, pCity);
3841 GiveCivilReport(P, pCity);
[6]3842 end;
3843 smStealMilReport:
3844 begin
[447]3845 if RW[P].Treaty[pCity] = trNoContact then
3846 IntroduceEnemy(P, pCity);
3847 GiveMilReport(P, pCity);
[6]3848 end;
3849 smStealForeignReports:
3850 begin
3851 for p1 := 0 to nPl - 1 do
[447]3852 if (p1 <> P) and (p1 <> pCity) and (RW[pCity].EnemyReport[p1] <> nil)
[6]3853 then
3854 begin
3855 if RW[pCity].EnemyReport[p1].TurnOfCivilReport >= 0 then
[447]3856 if CopyCivilReport(pCity, P, p1) then
3857 Result := Result or (1 shl (2 * p1));
[6]3858 if RW[pCity].EnemyReport[p1].TurnOfMilReport >= 0 then
[447]3859 if CopyMilReport(pCity, P, p1) then
3860 Result := Result or (2 shl (2 * p1));
[186]3861 end;
[6]3862 end;
[2]3863 end;
3864end;
3865
3866{
[6]3867 Test Flags
3868 ____________________________________________________________________
[2]3869}
[447]3870procedure ClearTestFlags(ClearFlags: Integer);
[2]3871var
[447]3872 p1: Integer;
[2]3873begin
[6]3874 GTestFlags := GTestFlags and (not ClearFlags or tfTested or tfAllTechs or
3875 tfAllContact);
3876 for p1 := 0 to nPl - 1 do
3877 if 1 shl p1 and (GAlive or GWatching) <> 0 then
3878 RW[p1].TestFlags := GTestFlags;
[2]3879end;
3880
[447]3881procedure SetTestFlags(P, SetFlags: Integer);
[2]3882var
[447]3883 I, p1, p2, MoreFlags: Integer;
[2]3884begin
[6]3885 MoreFlags := SetFlags and not GTestFlags;
3886 GTestFlags := GTestFlags or (SetFlags and $7FF);
3887 for p1 := 0 to nPl - 1 do
3888 if 1 shl p1 and (GAlive or GWatching) <> 0 then
3889 RW[p1].TestFlags := GTestFlags;
[2]3890
[6]3891 if MoreFlags and (tfUncover or tfAllContact) <> 0 then
3892 for p1 := 0 to nPl - 2 do
3893 if 1 shl p1 and GAlive <> 0 then
3894 for p2 := p1 + 1 to nPl - 1 do
3895 if 1 shl p2 and GAlive <> 0 then
3896 begin // make p1 and p2 know each other
3897 if RW[p1].Treaty[p2] = trNoContact then
[442]3898 IntroduceEnemy(p1, p2);
[6]3899 end;
[2]3900
[6]3901 if MoreFlags and tfAllTechs <> 0 then
3902 for p1 := 0 to nPl - 1 do
[2]3903 begin
[6]3904 ResourceMask[p1] := $FFFFFFFF;
3905 if 1 shl p1 and GAlive <> 0 then
[2]3906 begin
[447]3907 for I := 0 to nAdv - 1 do // give all techs to player p1
3908 if not(I in FutureTech) and (RW[p1].Tech[I] < tsApplicable) then
[2]3909 begin
[447]3910 RW[p1].Tech[I] := tsCheat;
3911 CheckSpecialModels(p1, I);
[2]3912 end;
[6]3913 for p2 := 0 to nPl - 1 do
3914 if (p2 <> p1) and (1 shl p2 and (GAlive or GWatching) <> 0) then
[447]3915 for I := 1 to 3 do
3916 if RW[p2].EnemyReport[p1].Tech[AgePreq[I]] < tsApplicable then
3917 RW[p2].EnemyReport[p1].Tech[AgePreq[I]] := tsCheat;
[186]3918 end;
[2]3919 end;
3920
[6]3921 if MoreFlags and tfUncover <> 0 then
[2]3922 begin
[447]3923 DiscoverAll(P, lObserveSuper);
[6]3924 for p1 := 0 to nPl - 1 do
3925 if 1 shl p1 and GAlive <> 0 then
[2]3926 begin
[6]3927 ResourceMask[p1] := $FFFFFFFF;
[447]3928 if p1 <> P then
[6]3929 begin
[447]3930 GiveCivilReport(P, p1);
3931 GiveMilReport(P, p1);
[186]3932 end;
3933 end;
[2]3934 end;
3935end;
3936
3937{
[6]3938 Internal Command Processing
3939 ____________________________________________________________________
[2]3940}
[447]3941procedure IntServer(Command, Player, Subject: Integer; var Data);
[2]3942var
[447]3943 I, p1: Integer;
[2]3944begin
[6]3945 if Mode = moPlaying then
3946 CL.Put(Command, Player, Subject, @Data);
[2]3947
[6]3948 case Command of
[2]3949
[6]3950 sIntTellAboutNation:
3951 begin
3952{$IFDEF TEXTLOG}CmdInfo := Format('IntTellAboutNation P%d+P%d', [Player, Subject]); {$ENDIF}
[447]3953 Assert((Player >= 0) and (Player < nPl) and (Subject >= 0) and
[6]3954 (Subject < nPl));
3955 IntroduceEnemy(Player, Subject);
3956 end;
[2]3957
[6]3958 sIntHaveContact:
3959 begin
3960{$IFDEF TEXTLOG}CmdInfo := Format('IntHaveContact P%d+P%d', [Player, Subject]); {$ENDIF}
[447]3961 Assert(RW[Player].Treaty[Subject] > trNoContact);
[6]3962 RW[Player].EnemyReport[Subject].TurnOfContact := GTurn;
3963 RW[Subject].EnemyReport[Player].TurnOfContact := GTurn;
3964 end;
[2]3965
[6]3966 sIntCancelTreaty:
3967 begin
3968{$IFDEF TEXTLOG}CmdInfo := Format('IntCancelTreaty P%d with P%d', [Player, Subject]); {$ENDIF}
3969 CancelTreaty(Player, Subject);
3970 end;
[2]3971
[6]3972 (* sIntChoosePeace:
3973 begin
3974 {$IFDEF TEXTLOG}CmdInfo:=Format('IntChoosePeace P%d+P%d', [Player,Subject]);{$ENDIF}
3975 RW[Player].Treaty[Subject]:=trPeace;
3976 RW[Subject].Treaty[Player]:=trPeace;
3977 end; *)
[2]3978
[6]3979 sIntTellAboutModel .. sIntTellAboutModel + (nPl - 1) shl 4:
3980 begin
3981 p1 := (Command - sIntTellAboutModel) shr 4; // told player
3982{$IFDEF TEXTLOG}CmdInfo := Format('IntTellAboutModel P%d about P%d Mod%d', [p1, Player, Subject]); {$ENDIF}
[447]3983 Assert((Player >= 0) and (Player < nPl));
3984 Assert((Subject >= 0) and (Subject < RW[Player].nModel));
[6]3985 MakeModelInfo(Player, Subject, RW[Player].Model[Subject],
3986 RW[p1].EnemyModel[RW[p1].nEnemyModel]);
3987 RWemix[p1, Player, Subject] := RW[p1].nEnemyModel;
[447]3988 Inc(RW[p1].nEnemyModel);
3989 Assert(RW[p1].nEnemyModel < nemmax);
[6]3990 end;
[2]3991
[6]3992 sIntDiscoverZOC:
3993 begin
[447]3994{$IFDEF TEXTLOG}CmdInfo := Format('IntDiscoverZOC P%d Loc%d', [Player, Integer(Data)]); {$ENDIF}
3995 Discover9(Integer(Data), Player, lObserveUnhidden, True, False);
[6]3996 end;
[2]3997
[6]3998 sIntExpandTerritory:
3999 if Mode < moPlaying then
[2]4000 begin
[6]4001{$IFDEF TEXTLOG}CmdInfo := Format('IntExpandTerritory P%d Loc%d', [Player, RW[Player].City[Subject].Loc]); {$ENDIF}
[447]4002 Move(Data, BorderChanges, SizeOf(BorderChanges));
[6]4003 ExpandTerritory(RW[Player].City[Subject].Loc);
[2]4004 end;
4005
[6]4006 sIntBuyMaterial:
4007 with RW[Player].City[Subject] do
[2]4008 begin
[447]4009{$IFDEF TEXTLOG}CmdInfo := Format('IntBuyMaterial P%d Loc%d Cost%d', [Player, Loc, Integer(Data)]); {$ENDIF}
4010 Dec(RW[Player].Money, Integer(Data));
[6]4011 if (GWonder[woMich].EffectiveOwner = Player) and (Project and cpImp <> 0)
4012 then
[447]4013 Inc(Prod, Integer(Data) div 2)
[6]4014 else
[447]4015 Inc(Prod, Integer(Data) div 4);
[6]4016 if Project0 and not cpAuto <> Project and not cpAuto then
4017 Project0 := Project;
4018 Prod0 := Prod;
[2]4019 end;
4020
[6]4021 sIntPayPrices .. sIntPayPrices + 12:
4022 begin
4023{$IFDEF TEXTLOG}CmdInfo := Format('IntPayPrices P%d+P%d', [Player, Subject]); {$ENDIF}
[447]4024 for I := 0 to TOffer(Data).nDeliver - 1 do
4025 PayPrice(Player, Subject, TOffer(Data).Price[I], True);
4026 for I := 0 to TOffer(Data).nCost - 1 do
[6]4027 PayPrice(Subject, Player, TOffer(Data).Price[TOffer(Data).nDeliver
[447]4028 + I], True);
4029 for I := 0 to TOffer(Data).nDeliver + TOffer(Data).nCost - 1 do
4030 if TOffer(Data).Price[I] = opTreaty + trAlliance then
[6]4031 begin // add view area of allied player
4032 DiscoverViewAreas(Player);
4033 DiscoverViewAreas(Subject);
[442]4034 Break;
4035 end;
[6]4036 end;
[2]4037
[6]4038 sIntSetDevModel:
4039 if Mode < moPlaying then
[442]4040 Move(Data, RW[Player].DevModel.Kind, sIntSetDevModel and $F * 4);
[2]4041
[6]4042 sIntSetModelStatus:
4043 if ProcessClientData[Player] then
4044 begin
4045{$IFDEF TEXTLOG}CmdInfo := Format('IntSetModelStatus P%d', [Player]);
4046 {$ENDIF}
[447]4047 RW[Player].Model[Subject].Status := Integer(Data);
[6]4048 end;
[2]4049
[6]4050 sIntSetUnitStatus:
4051 if ProcessClientData[Player] then
4052 begin
4053{$IFDEF TEXTLOG}CmdInfo := Format('IntSetUnitStatus P%d', [Player]);
4054 {$ENDIF}
[447]4055 RW[Player].Un[Subject].Status := Integer(Data);
[6]4056 end;
[2]4057
[6]4058 sIntSetCityStatus:
4059 if ProcessClientData[Player] then
4060 begin
4061{$IFDEF TEXTLOG}CmdInfo := Format('IntSetCityStatus P%d', [Player]);
4062 {$ENDIF}
[447]4063 RW[Player].City[Subject].Status := Integer(Data);
[6]4064 end;
[2]4065
[6]4066 sIntSetECityStatus:
4067 if ProcessClientData[Player] then
4068 begin
4069{$IFDEF TEXTLOG}CmdInfo := Format('IntSetECityStatus P%d', [Player]);
4070 {$ENDIF}
[447]4071 RW[Player].EnemyCity[Subject].Status := Integer(Data);
[6]4072 end;
[442]4073 end;
4074end;
[2]4075
4076end.
Note: See TracBrowser for help on using the repository browser.