source: branches/delphi/Database.pas

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