source: tags/1.3.6/GameServer.pas

Last change on this file was 618, checked in by chronos, 2 months ago
  • Modified: Generate random resources in editor filled map.
File size: 152.2 KB
Line 
1{$INCLUDE Switches.inc}
2// {$DEFINE TEXTLOG}
3// {$DEFINE LOADPERF}
4unit GameServer;
5
6interface
7
8uses
9 Protocol, Database, DynLibs, Platform, DateUtils, LazFileUtils, Brain, Global,
10 Map,
11 {$IFDEF DPI}Graphics{$ELSE}Graphics{$ENDIF};
12
13const
14 FirstAICompatibleVersion = $000D00;
15 FirstBookCompatibleVersion = $010103;
16
17 maxBrain = 255;
18
19type
20 // notifications
21 TNotify = (
22 ntCreateWorld,
23 ntInitModule,
24 ntInitLocalHuman,
25 ntDLLError,
26 ntAIError,
27 ntClientError,
28 ntInitPlayers,
29 ntDeactivationMissing,
30 ntSetAIName,
31 ntException,
32 ntLoadBegin,
33 ntLoadState,
34 ntEndInfo,
35 ntBackOn,
36 ntBackOff,
37 ntLoadError,
38 ntStartDone,
39 ntStartGo,
40 ntStartGoRefresh,
41 ntStartGoRefreshMaps,
42 ntChangeClient,
43 ntNextPlayer,
44 ntDeinitModule
45 );
46
47 TNotifyFunction = procedure(ID: TNotify; Index: Integer = 0);
48
49var
50 // PARAMETERS
51 PlayersBrain: TBrains; { brain of the players view }
52 Difficulty: array [0 .. nPl - 1] of Integer absolute Database.Difficulty;
53 { difficulty }
54
55 // READ ONLY
56 DotNetClient: TClientCall;
57 Brains: TBrains; // { available brains }
58 NotifyMessage: string;
59
60 BrainNoTerm: TBrain;
61 BrainSuperVirtual: TBrain;
62 BrainTerm: TBrain;
63 BrainRandom: TBrain;
64 BrainNetworkClient: TBrain;
65 BrainNetworkServer: TBrain;
66
67 NetworkEnabled: Boolean;
68
69procedure Init(NotifyFunction: TNotifyFunction);
70procedure Done;
71
72procedure StartNewGame(const FileName, AMapFileName: string;
73 Newlx, Newly, NewLandMass, NewMaxTurn: Integer);
74function LoadGame(const FileName: string; Turn: Integer;
75 MovieMode: Boolean): Boolean;
76procedure EditMap(const AMapFileName: string; Newlx, Newly, NewLandMass: Integer);
77procedure FillMap(TerrainType: Cardinal; SpecialResources: Boolean);
78procedure DirectHelp(Command: Integer);
79function ToAutoSaveFileName(FileName: string): string;
80function IsAutoSaveFileName(FileName: string): Boolean;
81
82procedure ChangeClient;
83procedure NextPlayer;
84function PreviewMap(ALandMass: Integer): Pointer;
85
86
87implementation
88
89uses
90 Directories, CityProcessing, UnitProcessing, CmdList, LCLIntf, LCLType,
91 LMessages, Classes, SysUtils;
92
93resourcestring
94 SNoAiFound = 'No AI libraries found in directory %s';
95
96var
97 MaxTurn: Integer;
98 LoadTurn: Integer; { turn where to stop loading }
99 nLogOpened: Integer; { nLog of opened book }
100{$IFOPT O-}nHandoverStack, {$ENDIF}
101 LastEndClientCommand: Integer;
102 pContacted: Integer; // player contacted for negotiation
103 pDipActive: Integer; // player who's to speak in a negotiation
104 pTurn: Integer; { player who's turn it is }
105 GWinner: Integer;
106 GColdWarStart: Integer;
107 GStealFrom: Integer;
108 SpyMission: Integer;
109 ZOCTile: Integer;
110 CCCommand: Integer;
111 CCPlayer: Integer;
112 DebugMap: array [0 .. nPl - 1] of Pointer;
113 ExeInfo: TSearchRec;
114 Stat: array [0 .. nStat - 1, 0 .. nPl - 1] of ^TChart;
115 AutoSaveState: TCmdListState;
116 MapField: ^Cardinal; // predefined map
117 LastOffer: TOffer;
118 CCData: array [0 .. 14] of Integer;
119 bix: TBrains; { brain of the players }
120 DevModelTurn: array [0 .. nPl - 1] of Integer; { turn of last call to sResetModel }
121 OriginalDataVersion: array [0 .. nPl - 1] of Integer;
122 SavedTiles { , SavedResourceWeights } : array [0 .. ncmax - 1] of Cardinal;
123 SavedData: array [0 .. nPl - 1] of Pointer;
124 LogFileName: string;
125 SavePath: string; { name of file for saving the current game }
126 MapFileName: string; // name of map to use, empty for random
127 AICredits: string;
128 AIInfo: array [0 .. nPl - 1] of string;
129 Notify: TNotifyFunction;
130 LastClientTime: TDateTime;
131{$IFOPT O-}HandoverStack: array [0 .. 31] of Cardinal; {$ENDIF}
132 AutoSaveExists: Boolean;
133 LoadOK: Boolean;
134 WinOnAlone: Boolean;
135 PreviewElevation: Boolean;
136 MovieStopped: Boolean;
137
138const
139 PreviewRND = 41601260; { randseed for preview map }
140
141function Server(Command, Player, Subject: Integer; var Data): Integer;
142 stdcall; forward;
143
144procedure CallPlayer(Command, P: Integer; var Data);
145begin
146 if ((Mode <> moMovie) or (P = 0)) then
147 begin
148{$IFOPT O-}
149 HandoverStack[nHandoverStack] := P;
150 HandoverStack[nHandoverStack + 1] := Command;
151 Inc(nHandoverStack, 2);
152 bix[P].Client(Command, P, Data);
153 Dec(nHandoverStack, 2);
154{$ELSE}
155 try
156 bix[P].Client(Command, P, Data);
157 except
158 Notify(ntException + bix[P]);
159 end;
160{$ENDIF}
161 end;
162end;
163
164procedure CallAllPlayers(Command: Integer; var Data);
165var
166 I: Integer;
167begin
168 for I := 0 to nPl - 1 do
169 if Assigned(bix[I]) then
170 CallPlayer(Command, I, Data);
171end;
172
173procedure CallClient(bix, Command: Integer; var Data);
174begin
175 if ((Mode <> moMovie) or (bix = Brains.IndexOf(GameServer.bix[0]))) then
176 begin
177{$IFOPT O-}
178 HandoverStack[nHandoverStack] := bix;
179 HandoverStack[nHandoverStack + 1] := Command;
180 Inc(nHandoverStack, 2);
181 Brains[bix].Client(Command, -1, Data);
182 Dec(nHandoverStack, 2);
183{$ELSE}
184 try
185 Brain[bix].Client(Command, -1, Data);
186 except
187 Notify(ntException + bix);
188 end;
189{$ENDIF}
190 end
191end;
192
193procedure Init(NotifyFunction: TNotifyFunction);
194var
195 F: TSearchRec;
196 BasePath: string;
197 NewBrain: TBrain;
198 I: Integer;
199begin
200 Notify := NotifyFunction;
201 PreviewElevation := False;
202 PlayersBrain := TBrains.Create(False);
203 PlayersBrain.Count := nPl;
204 for I := 0 to nPl - 1 do
205 PlayersBrain[I] := nil;
206
207 bix := TBrains.Create(False);
208 bix.Count := nPl;
209 for I := 0 to nPl - 1 do
210 bix[I] := nil;
211
212 { get available brains }
213 Brains := TBrains.Create;
214 BrainNoTerm := Brains.AddNew;
215 BrainNoTerm.FileName := ':AIT';
216 BrainNoTerm.Flags := 0;
217 BrainNoTerm.Initialized := False;
218 BrainNoTerm.Kind := btNoTerm;
219 BrainSuperVirtual := Brains.AddNew;
220 BrainSuperVirtual.FileName := ':Supervisor';
221 BrainSuperVirtual.Flags := 0;
222 BrainSuperVirtual.Initialized := False;
223 BrainSuperVirtual.Kind := btSuperVirtual;
224 if NetworkEnabled then begin
225 BrainNetworkClient := Brains.AddNew;
226 BrainNetworkClient.FileName := ':NetworkClient';
227 BrainNetworkClient.Flags := fMultiple;
228 BrainNetworkClient.Initialized := False;
229 BrainNetworkClient.ServerVersion := CevoVersion;
230 BrainNetworkClient.Kind := btNetworkClient;
231 end;
232 BrainTerm := Brains.AddNew;
233 BrainTerm.FileName := ':StdIntf';
234 BrainTerm.Flags := fMultiple;
235 BrainTerm.Initialized := False;
236 BrainTerm.ServerVersion := CevoVersion;
237 BrainTerm.Kind := btTerm;
238 BrainRandom := Brains.AddNew;
239 BrainRandom.FileName := ':Random';
240 BrainRandom.Flags := fMultiple;
241 BrainRandom.Initialized := False;
242 BrainRandom.Kind := btRandom;
243 if NetworkEnabled then begin
244 BrainNetworkServer := Brains.AddNew;
245 BrainNetworkServer.FileName := ':NetworkServer';
246 BrainNetworkServer.Flags := fMultiple;
247 BrainNetworkServer.Initialized := False;
248 BrainNetworkServer.ServerVersion := CevoVersion;
249 BrainNetworkServer.Kind := btNetworkServer;
250 end;
251
252 if FindFirst(GetAiDir + DirectorySeparator + '*', faDirectory or faArchive or faReadOnly, F) = 0 then
253 repeat
254 BasePath := GetAiDir + DirectorySeparator + F.Name;
255 if (F.Name <> '.') and (F.Name <> '..') and DirectoryExists(BasePath) then begin
256 NewBrain := Brains.AddNew;
257 NewBrain.Kind := btAI;
258 NewBrain.LoadFromFile(BasePath + DirectorySeparator + F.Name + '.ai.txt');
259 if (NewBrain.ServerVersion >= FirstAICompatibleVersion) and
260 (NewBrain.ServerVersion <= CevoVersion) and
261 ((NewBrain.Flags and fDotNet = 0) or (@DotNetClient <> nil)) then begin
262 end else Brains.Delete(Brains.Count - 1);
263 end;
264 until FindNext(F) <> 0;
265 FindClose(F);
266
267 if Brains.GetKindCount(btAI) = 0 then
268 raise Exception.Create(Format(SNoAiFound, [GetAiDir]));
269end;
270
271procedure Done;
272var
273 I: Integer;
274begin
275 for I := 0 to Brains.Count - 1 do
276 with Brains[I] do
277 if Initialized then begin
278 CallClient(I, cReleaseModule, nil^);
279 if (Kind = btAI) and ((Flags and fDotNet) = 0) then
280 FreeLibrary(hm);
281 end;
282 FreeAndNil(PlayersBrain);
283 FreeAndNil(bix);
284 FreeAndNil(Brains);
285end;
286
287function PreviewMap(ALandMass: Integer): Pointer;
288begin
289 lx := lxmax;
290 ly := lymax;
291 MapSize := lx * ly;
292 LandMass := ALandMass;
293 DelphiRandSeed := PreviewRND;
294 if not PreviewElevation then begin
295 CreateElevation;
296 PreviewElevation := True;
297 end;
298 CreateMap(True);
299 Result := @RealMap;
300end;
301
302procedure ChangeClientWhenDone(Command, Player: Integer; var Data;
303 DataSize: Integer);
304begin
305 CCCommand := Command;
306 CCPlayer := Player;
307 if DataSize > 0 then
308 Move(Data, CCData, DataSize);
309 Notify(ntChangeClient);
310end;
311
312procedure PutMessage(Level: Integer; Text: string);
313begin
314 bix[0].Client(cDebugMessage, Level, PChar(Text)^);
315end;
316
317procedure ForceClientDeactivation;
318var
319 NullOffer: TOffer;
320begin
321 if pDipActive < 0 then
322 Server(sTurn, pTurn, 0, nil^) // no nego mode
323 else
324 case LastEndClientCommand of // nego mode
325 scContact:
326 Server(scReject, pDipActive, 0, nil^);
327 scDipCancelTreaty, scDipBreak:
328 Server(scDipNotice, pDipActive, 0, nil^);
329 else
330 begin // make null offer
331 NullOffer.nDeliver := 0;
332 NullOffer.nCost := 0;
333 Server(scDipOffer, pDipActive, 0, NullOffer);
334 end;
335 end;
336end;
337
338procedure ChangeClient;
339// hand over control to other client (as specified by CC...)
340var
341 P: Integer;
342 T: TDateTime;
343begin
344 T := NowPrecise;
345 PutMessage(1 shl 16 + 2, Format('CLIENT: took %.1f ms',
346 [(T - LastClientTime) / OneMillisecond]));
347 LastClientTime := T;
348 PutMessage(1 shl 16 + 2, Format('CLIENT: calling %d (%s)',
349 [CCPlayer, bix[CCPlayer].Name]));
350 if CCCommand = cTurn then
351 for P := 0 to nPl - 1 do
352 if (P <> CCPlayer) and (1 shl P and GWatching <> 0) then
353 CallPlayer(cShowTurnChange, P, CCPlayer);
354
355 P := CCPlayer;
356 CCPlayer := -1;
357 CallPlayer(CCCommand, P, CCData);
358 if (Mode = moPlaying) and (bix[P].Flags and aiThreaded = 0) and
359 (CCPlayer < 0) then
360 begin
361 Notify(ntDeactivationMissing, P);
362 ForceClientDeactivation;
363 end;
364end;
365
366procedure Inform(P: Integer);
367var
368 I, p1: Integer;
369begin
370 RW[P].Turn := GTurn;
371 if (GTurn = MaxTurn) and (P = pTurn) and (P = 0) then
372 RW[P].Happened := RW[P].Happened or phTimeUp;
373 if (GWinner > 0) and (P = pTurn) and (P = 0) then
374 RW[P].Happened := RW[P].Happened or phShipComplete;
375 RW[P].Alive := GAlive;
376 Move(GWonder, RW[P].Wonder, SizeOf(GWonder));
377 Move(GShip, RW[P].Ship, SizeOf(GShip));
378 for p1 := 0 to nPl - 1 do
379 if (p1 <> P) and Assigned(bix[p1]) and (Difficulty[p1] > 0) then
380 RW[P].EnemyReport[p1].Credibility := RW[p1].Credibility;
381 for p1 := 0 to nPl - 1 do
382 if (p1 <> P) and (1 shl p1 and GAlive <> 0) then
383 begin
384 if (GTestFlags and tfUncover <> 0) or (Difficulty[P] = 0) or
385 (RW[P].Treaty[p1] >= trFriendlyContact) then
386 GiveCivilReport(P, p1);
387 if (GTestFlags and tfUncover <> 0) or (Difficulty[P] = 0) or
388 (RW[P].Treaty[p1] = trAlliance) then
389 GiveMilReport(P, p1)
390 end;
391 for I := 0 to RW[P].nEnemyModel - 1 do
392 with RW[P].EnemyModel[I] do
393 Lost := Destroyed[P, Owner, mix];
394end;
395
396const
397 AutoSavePrefix = '~';
398
399function ToAutoSaveFileName(FileName: string): string;
400begin
401 if Copy(ExtractFileName(FileName), 1, 1) <> AutoSavePrefix then
402 Result := ExtractFileDir(FileName) + DirectorySeparator + AutoSavePrefix + ExtractFileName(FileName)
403 else Result := FileName;
404end;
405
406function FromAutoSaveFileName(FileName: string): string;
407begin
408 if Copy(ExtractFileName(FileName), 1, 1) = AutoSavePrefix then
409 Result := ExtractFileDir(FileName) + DirectorySeparator + Copy(ExtractFileName(FileName), 2, MaxInt)
410 else Result := FileName;
411end;
412
413function IsAutoSaveFileName(FileName: string): Boolean;
414begin
415 FileName := ExtractFileName(FileName);
416 Result := (Length(FileName) > 0) and (FileName[1] = AutoSavePrefix);
417end;
418
419procedure LogChanges;
420var
421 P, ix: Integer;
422begin
423 for P := 0 to nPl - 1 do
424 if (1 shl P and GWatching <> 0) and ProcessClientData[P] then
425 begin
426 // log unit status changes
427 for ix := 0 to RW[P].nUn - 1 do
428 with RW[P].Un[ix] do
429 if (Loc >= 0) and (SavedStatus <> Status) then
430 begin
431 CL.Put(sIntSetUnitStatus, P, ix, @Status);
432 SavedStatus := Status;
433 end;
434 // log city status changes
435 for ix := 0 to RW[P].nCity - 1 do
436 with RW[P].City[ix] do
437 if (Loc >= 0) and (SavedStatus <> Status) then
438 begin
439 CL.Put(sIntSetCityStatus, P, ix, @Status);
440 SavedStatus := Status;
441 end;
442 // log model status changes
443 for ix := 0 to RW[P].nModel - 1 do
444 with RW[P].Model[ix] do
445 if SavedStatus <> Status then
446 begin
447 CL.Put(sIntSetModelStatus, P, ix, @Status);
448 SavedStatus := Status;
449 end;
450 // log enemy city status changes
451 for ix := 0 to RW[P].nEnemyCity - 1 do
452 with RW[P].EnemyCity[ix] do
453 if (Loc >= 0) and (SavedStatus <> Status) then
454 begin
455 CL.Put(sIntSetECityStatus, P, ix, @Status);
456 SavedStatus := Status;
457 end;
458 // log data changes
459 if bix[P].DataSize > 0 then
460 begin
461 CL.PutDataChanges(sIntDataChange, P, SavedData[P], RW[P].Data,
462 bix[P].DataSize);
463 Move(RW[P].Data^, SavedData[P]^, bix[P].DataSize * 4);
464 end;
465 end;
466end;
467
468procedure NoLogChanges;
469var
470 P, ix: Integer;
471begin
472 for P := 0 to nPl - 1 do
473 if (1 shl P and GWatching <> 0) and ProcessClientData[P] then
474 begin
475 for ix := 0 to RW[P].nUn - 1 do
476 with RW[P].Un[ix] do
477 SavedStatus := Status;
478 for ix := 0 to RW[P].nCity - 1 do
479 with RW[P].City[ix] do
480 SavedStatus := Status;
481 for ix := 0 to RW[P].nModel - 1 do
482 with RW[P].Model[ix] do
483 SavedStatus := Status;
484 for ix := 0 to RW[P].nEnemyCity - 1 do
485 with RW[P].EnemyCity[ix] do
486 SavedStatus := Status;
487 if bix[P].DataSize > 0 then
488 Move(RW[P].Data^, SavedData[P]^, bix[P].DataSize * 4);
489 end;
490end;
491
492function HasChanges(P: Integer): Boolean;
493type
494 TDWordList = array [0 .. INFIN] of Cardinal;
495 PDWortList = ^TDWordList;
496var
497 ix: Integer;
498begin
499 Result := False;
500 for ix := 0 to RW[P].nUn - 1 do
501 with RW[P].Un[ix] do
502 if (Loc >= 0) and (SavedStatus <> Status) then begin
503 Result := True;
504 Exit;
505 end;
506 for ix := 0 to RW[P].nCity - 1 do
507 with RW[P].City[ix] do
508 if (Loc >= 0) and (SavedStatus <> Status) then begin
509 Result := True;
510 Exit;
511 end;
512 for ix := 0 to RW[P].nModel - 1 do
513 with RW[P].Model[ix] do
514 if SavedStatus <> Status then begin
515 Result := True;
516 Exit;
517 end;
518 for ix := 0 to RW[P].nEnemyCity - 1 do
519 with RW[P].EnemyCity[ix] do
520 if (Loc >= 0) and (SavedStatus <> Status) then begin
521 Result := True;
522 Exit;
523 end;
524 if RW[P].Data <> nil then
525 for ix := 0 to bix[P].DataSize - 1 do
526 if PDWortList(SavedData[P])[ix] <> PDWortList(RW[P].Data)[ix] then begin
527 Result := True;
528 Exit;
529 end;
530end;
531
532procedure InitBrain(bix: TBrain);
533var
534 InitModuleData: TInitModuleData;
535begin
536 Assert(bix.Kind <> btSuperVirtual);
537 with bix do begin
538 if Initialized then
539 Exit;
540 if Kind = btAI then
541 begin { get client function }
542 Notify(ntInitModule, Brains.IndexOf(bix));
543 if Flags and fDotNet > 0 then
544 Client := DotNetClient
545 else
546 begin
547 hm := LoadLibrary(PChar(DLLName));
548 if hm = 0 then
549 begin
550 Client := nil;
551 Notify(ntDLLError, Brains.IndexOf(bix));
552 end
553 else
554 begin
555 Client := GetProcAddress(hm, 'client');
556 if @Client = nil then
557 Notify(ntClientError, Brains.IndexOf(bix));
558 end;
559 end;
560 end;
561 if @Client <> nil then
562 begin
563 Initialized := True;
564 InitModuleData.Server := @Server;
565 InitModuleData.DataVersion := 0;
566 InitModuleData.DataSize := 0;
567 InitModuleData.Flags := 0;
568 CallClient(Brains.IndexOf(bix), cInitModule, InitModuleData);
569 DataVersion := InitModuleData.DataVersion;
570 DataSize := (InitModuleData.DataSize + 3) div 4;
571 if DataSize > MaxDataSize then
572 DataSize := 0;
573 Flags := Flags or InitModuleData.Flags;
574 end;
575 end;
576end;
577
578procedure SaveGame(FileName: string; Auto: Boolean);
579var
580 X, Y, I, Zero, Tile, nLocal: Integer;
581 LogFile: TFileStream;
582 S: string[255];
583 SaveMap: array [0 .. lxmax * lymax - 1] of Byte;
584begin
585 nLocal := 0;
586 for I := 0 to nPl - 1 do
587 if Assigned(bix[I]) and (bix[I].Kind = btTerm) then
588 Inc(nLocal);
589 if Difficulty[0] = 0 then
590 nLocal := 0;
591 if nLocal <= 1 then
592 for Y := 0 to ly - 1 do
593 for X := 0 to lx - 1 do
594 begin
595 Tile := RW[0].Map[(X + SaveMapCenterLoc + lx shr 1) mod lx + lx * Y];
596 SaveMap[X + lx * Y] := Tile and fTerrain + Tile and
597 (fCity or fUnit or fOwned) shr 16;
598 end;
599
600 if Auto and AutoSaveExists then // append to existing file
601 LogFile := TFileStream.Create(FileName, fmOpenReadWrite or fmShareExclusive)
602 else // create new file
603 LogFile := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
604
605 Zero := 0;
606 LogFile.Position := 0;
607 S := 'cEvoBook';
608 LogFile.Write(S[1], 8); { file id }
609 I := CevoVersion;
610 LogFile.Write(I, 4); { c-evo version }
611 LogFile.Write(ExeInfo.Time, 4);
612 LogFile.Write(lx, 4);
613 LogFile.Write(ly, 4);
614 LogFile.Write(LandMass, 4);
615 if LandMass = 0 then
616 LogFile.Write(MapField^, MapSize * 4);
617
618 LogFile.Write(MaxTurn, 4);
619 LogFile.Write(RND, 4);
620 LogFile.Write(GTurn, 4);
621 if nLocal > 1 then // multiplayer game -- no quick view
622 begin
623 I := $80;
624 LogFile.Write(I, 4);
625 end
626 else
627 LogFile.Write(SaveMap, ((MapSize - 1) div 4 + 1) * 4);
628 for I := 0 to nPl - 1 do
629 if not Assigned(bix[I]) then
630 LogFile.Write(Zero, 4)
631 else
632 begin
633 if PlayersBrain[I].Kind in [btRandom, btAI] then
634 S := bix[I].FileName
635 else
636 S := PlayersBrain[I].FileName;
637 Move(Zero, S[Length(S) + 1], 4);
638 LogFile.Write(S, (Length(S) div 4 + 1) * 4);
639 LogFile.Write(OriginalDataVersion[I], 4);
640 S := ''; { behavior }
641 Move(Zero, S[Length(S) + 1], 4);
642 LogFile.Write(S, (Length(S) div 4 + 1) * 4);
643 LogFile.Write(Difficulty[I], 4);
644 end;
645
646 if Auto and AutoSaveExists then
647 CL.AppendToFile(LogFile, AutoSaveState)
648 else
649 CL.SaveToFile(LogFile);
650 FreeAndNil(LogFile);
651 if Auto then
652 begin
653 AutoSaveState := CL.State;
654 AutoSaveExists := True;
655 end;
656end;
657
658procedure StartGame;
659var
660 I, P, p1, Human, nAlive, bixUni: Integer;
661 Game: TNewGameData;
662 // GameEx: TNewGameExData;
663 Path: shortstring;
664 BrainUsed: Set of 0 .. 254; { used brains }
665 AIBrains: TBrains;
666 Map: TMap;
667begin
668 for p1 := 0 to nPl - 1 do begin
669 if Assigned(PlayersBrain[p1]) and (PlayersBrain[p1].Kind = btSuperVirtual) then
670 bix[p1] := BrainTerm // supervisor and local human use same module
671 else if Assigned(PlayersBrain[p1]) and (PlayersBrain[p1].Kind = btRandom) then
672 if Brains.GetKindCount(btAI) = 0 then
673 bix[p1] := nil
674 else begin
675 AIBrains := TBrains.Create(False);
676 Brains.GetByKind(btAI, AIBrains);
677 bix[p1] := AIBrains[DelphiRandom(AIBrains.Count)];
678 FreeAndNil(AIBrains);
679 end
680 else
681 bix[p1] := PlayersBrain[p1];
682 if not Assigned(PlayersBrain[p1]) then
683 Difficulty[p1] := -1;
684 end;
685
686 if bix[0].Kind <> btNoTerm then
687 Notify(ntInitLocalHuman);
688
689 BrainUsed := [];
690 for P := 0 to nPl - 1 do
691 if Assigned(bix[P]) and ((Mode <> moMovie) or (P = 0)) then
692 begin { initiate selected control module }
693 AIInfo[P] := bix[P].Name + #0;
694 InitBrain(bix[P]);
695 if Mode = moPlaying then
696 begin // new game, this data version is original
697 OriginalDataVersion[P] := bix[P].DataVersion;
698 ProcessClientData[P] := True;
699 end
700 else // loading game, compare with data version read from file
701 ProcessClientData[P] := ProcessClientData[P] and
702 (OriginalDataVersion[P] = bix[P].DataVersion);
703 if @bix[P].Client = nil then // client function not found
704 if bix[0].Kind = btNoTerm then
705 bix[P] := nil
706 else
707 begin
708 bix[P] := BrainTerm;
709 OriginalDataVersion[P] := -1;
710 ProcessClientData[P] := False;
711 end;
712 if Assigned(bix[P]) then
713 Include(BrainUsed, Brains.IndexOf(bix[P]));
714 end;
715
716 Notify(ntCreateWorld);
717 nAlive := 0;
718 GAlive := 0;
719 if Mode = moMovie then
720 GWatching := 1
721 else
722 GWatching := 0;
723 GAI := 0;
724 for p1 := 0 to nPl - 1 do
725 if Assigned(bix[p1]) then
726 begin
727 if Mode <> moMovie then
728 Inc(GWatching, 1 shl p1);
729 if bix[p1].Kind = btAI then
730 Inc(GAI, 1 shl p1);
731 if Difficulty[p1] > 0 then
732 begin
733 Inc(GAlive, 1 shl p1);
734 Inc(nAlive);
735 end;
736 ServerVersion[p1] := bix[p1].ServerVersion;
737 end;
738 WinOnAlone := (bix[0].Kind = btNoTerm) and (nAlive > 1);
739 GWinner := 0;
740 GColdWarStart := -ColdWarTurns - 1;
741 uixSelectedTransport := -1;
742 SpyMission := smSabotageProd;
743 for p1 := 0 to nPl - 1 do
744 DebugMap[p1] := nil;
745
746 GTurn := 0;
747 for I := 0 to nWonder - 1 do
748 with GWonder[I] do
749 begin
750 CityID := -1;
751 EffectiveOwner := -1
752 end;
753 FillChar(GShip, SizeOf(GShip), 0);
754
755 for P := 0 to nPl - 1 do
756 if 1 shl P and (GAlive or GWatching) <> 0 then
757 with RW[P] do
758 begin
759 Government := gDespotism;
760 Money := StartMoney;
761 TaxRate := 30;
762 LuxRate := 0;
763 Research := 0;
764 ResearchTech := -2;
765 AnarchyStart := -AnarchyTurns - 1;
766 Happened := 0;
767 LastValidStat[P] := -1;
768 Worked[P] := 0;
769 Founded[P] := 0;
770 DevModelTurn[P] := -1;
771 OracleIncome := 0;
772
773 if bix[P].DataSize > 0 then
774 begin
775 GetMem(SavedData[P], bix[P].DataSize * 4);
776 GetMem(Data, bix[P].DataSize * 4);
777 FillChar(SavedData[P]^, bix[P].DataSize * 4, 0);
778 FillChar(Data^, bix[P].DataSize * 4, 0);
779 end
780 else
781 begin
782 Data := nil;
783 SavedData[P] := nil;
784 end;
785 nBattleHistory := 0;
786 BattleHistory := nil;
787 { if bix[p]=bixTerm then
788 begin
789 GetMem(BorderHelper,MapSize);
790 FillChar(BorderHelper^,MapSize,0);
791 end
792 else } BorderHelper := nil;
793 for I := 0 to nStat - 1 do
794 GetMem(Stat[I, P], 4 * (MaxTurn + 1));
795 if bix[P].Flags and fDotNet <> 0 then
796 begin
797 GetMem(RW[P].DefaultDebugMap, MapSize * 4);
798 FillChar(RW[P].DefaultDebugMap^, MapSize * 4, 0);
799 DebugMap[P] := RW[P].DefaultDebugMap;
800 end
801 else
802 RW[P].DefaultDebugMap := nil;
803
804 { !!!for i:=0 to nShipPart-1 do GShip[p].Parts[i]:=Delphirandom((3-i)*2); }
805 end;
806
807 if LandMass > 0 then
808 begin // random map
809 InitRandomGame;
810 PreviewElevation := False;
811 MapField := nil;
812 end
813 else
814 begin // predefined map
815 if Mode = moPlaying then begin
816 Map := TMap.Create;
817 Map.LoadFromFile(MapFileName); // new game -- load map from file
818 MapSize := Map.MapSize;
819 lx := Map.Size.X;
820 ly := Map.Size.Y;
821 Move(Map.Tiles[0], RealMap, MapSize * 4);
822 FreeAndNil(Map);
823 end;
824 GetMem(MapField, MapSize * 4);
825 Move(RealMap, MapField^, MapSize * 4);
826 Human := 0;
827 for p1 := 0 to nPl - 1 do
828 if Assigned(bix[p1]) and (bix[p1].Kind = btTerm) then
829 Inc(Human, 1 shl p1);
830 InitMapGame(Human);
831 end;
832 CityProcessing.InitGame;
833 UnitProcessing.InitGame;
834 for P := 0 to nPl - 1 do
835 if 1 shl P and (GAlive or GWatching) <> 0 then
836 Inform(P);
837
838 pTurn := -1;
839 if bix[0].Kind <> btNoTerm then
840 Notify(ntInitLocalHuman);
841 Game.lx := lx;
842 Game.ly := ly;
843 Game.LandMass := LandMass;
844 Game.MaxTurn := MaxTurn;
845 Move(Difficulty, Game.Difficulty, SizeOf(Difficulty));
846 // GameEx.lx:=lx; GameEx.ly:=ly; GameEx.LandMass:=LandMass;
847 // GameEx.MaxTurn:=MaxTurn; GameEx.RND:=RND;
848 // move(Difficulty,GameEx.Difficulty,SizeOf(Difficulty));
849 AICredits := '';
850 for I := 0 to Brains.Count - 1 do
851 with Brains[I] do begin
852 if Initialized then
853 if I in BrainUsed then
854 begin
855 if Kind = btAI then
856 Notify(ntInitPlayers);
857 for P := 0 to nPl - 1 do
858 begin
859 if Brains.IndexOf(bix[P]) = I then
860 Game.RO[P] := @RW[P]
861 else
862 Game.RO[P] := nil;
863 if (Kind = btTerm) and (Difficulty[0] = 0) and Assigned(bix[P]) then
864 Game.SuperVisorRO[P] := @RW[P]
865 else
866 Game.SuperVisorRO[P] := nil;
867 end;
868 if Flags and fDotNet > 0 then
869 begin
870 Path := DLLName;
871 Move(Path[1], Game.AssemblyPath, Length(Path));
872 Game.AssemblyPath[Length(Path)] := #0;
873 end
874 else
875 Game.AssemblyPath[0] := #0;
876 case Mode of
877 moLoading, moLoading_Fast:
878 CallClient(I, cLoadGame, Game);
879 moMovie:
880 CallClient(I, cMovie, Game);
881 moPlaying:
882 CallClient(I, cNewGame, Game);
883 end;
884 if (Kind = btAI) and (Credits <> '') then
885 if AICredits = '' then
886 AICredits := Credits
887 else
888 AICredits := AICredits + '\' + Credits;
889 end
890 else
891 begin { module no longer used -- unload }
892 CallClient(I, cReleaseModule, nil^);
893 if Kind = btAI then
894 begin
895 if Flags and fDotNet = 0 then
896 FreeLibrary(hm);
897 Client := nil;
898 end;
899 Initialized := False;
900 end;
901 end;
902 AICredits := AICredits + #0;
903
904 if bix[0].Kind <> btNoTerm then
905 begin
906 // uni ai?
907 bixUni := -1;
908 for p1 := 0 to nPl - 1 do
909 if Assigned(bix[p1]) and (bix[p1].Kind = btAI) then
910 if bixUni = -1 then
911 bixUni := Brains.IndexOf(bix[p1])
912 else if bixUni <> Brains.IndexOf(bix[p1]) then
913 bixUni := -2;
914 for p1 := 0 to nPl - 1 do
915 if Assigned(bix[p1]) and (bix[p1].Kind = btAI) then
916 begin
917 if bixUni = -2 then
918 NotifyMessage := bix[p1].FileName
919 else
920 NotifyMessage := '';
921 Notify(ntSetAIName, p1);
922 end;
923 end;
924
925 CheckBorders(-1);
926{$IFOPT O-}InvalidTreatyMap := 0; {$ENDIF}
927 AutoSaveExists := False;
928 pDipActive := -1;
929 pTurn := 0;
930
931 if Mode >= moMovie then
932 Notify(ntEndInfo);
933end;
934
935procedure EndGame;
936var
937 I, p1: Integer;
938begin
939 if LandMass = 0 then
940 FreeMem(MapField);
941 for p1 := 0 to nPl - 1 do
942 if Assigned(bix[p1]) then
943 begin
944 for I := 0 to nStat - 1 do
945 FreeMem(Stat[I, p1]);
946 if RW[p1].BattleHistory <> nil then
947 FreeMem(RW[p1].BattleHistory);
948 { if RW[p1].BorderHelper<>nil then FreeMem(RW[p1].BorderHelper); }
949 FreeMem(RW[p1].Data);
950 FreeMem(SavedData[p1]);
951 if RW[p1].DefaultDebugMap <> nil then
952 FreeMem(RW[p1].DefaultDebugMap);
953 end;
954 UnitProcessing.ReleaseGame;
955 CityProcessing.ReleaseGame;
956 Database.ReleaseGame;
957 FreeAndNil(CL);
958end;
959
960procedure GenerateStat(P: Integer);
961var
962 cix, uix: Integer;
963begin
964 if Difficulty[P] > 0 then
965 with RW[P] do
966 begin
967 Stat[stPop, P, GTurn] := 0;
968 for cix := 0 to nCity - 1 do
969 if City[cix].Loc >= 0 then
970 Inc(Stat[stPop, P, GTurn], City[cix].Size);
971 Stat[stScience, P, GTurn] := Researched[P] * 50;
972 if (RW[P].ResearchTech >= 0) and (RW[P].ResearchTech <> adMilitary) then
973 Inc(Stat[stScience, P, GTurn], Research * 100 div TechBaseCost(nTech[P],
974 Difficulty[P]));
975 Stat[stMil, P, GTurn] := 0;
976 for uix := 0 to nUn - 1 do
977 if Un[uix].Loc >= 0 then
978 with Model[Un[uix].mix] do
979 begin
980 if (Kind <= mkEnemyDeveloped) and (Un[uix].mix <> 1) then
981 Inc(Stat[stMil, P, GTurn], Weight * MStrength *
982 Un[uix].Health div 100)
983 else if Domain = dGround then
984 Inc(Stat[stMil, P, GTurn], (Attack + 2 * Defense) *
985 Un[uix].Health div 100)
986 else
987 Inc(Stat[stMil, P, GTurn], (Attack + Defense) *
988 Un[uix].Health div 100);
989 case Kind of
990 mkSlaves:
991 Inc(Stat[stPop, P, GTurn]);
992 mkSettler:
993 Inc(Stat[stPop, P, GTurn], 2);
994 end;
995 end;
996 Stat[stMil, P, GTurn] := Stat[stMil, P, GTurn] div 16;
997 Stat[stExplore, P, GTurn] := Discovered[P];
998 Stat[stTerritory, P, GTurn] := TerritoryCount[P];
999 Stat[stWork, P, GTurn] := Worked[P];
1000 LastValidStat[P] := GTurn;
1001 end;
1002end;
1003
1004procedure LogCityTileChanges;
1005var
1006 cix: Integer;
1007begin
1008 for cix := 0 to RW[pTurn].nCity - 1 do
1009 with RW[pTurn].City[cix] do
1010 if Loc >= 0 then
1011 begin
1012 { if SavedResourceWeights[cix]<>ResourceWeights then
1013 begin // log city resource weight changes
1014 CL.Put(sSetCityResourceWeights, pTurn, cix, @ResourceWeights);
1015 SavedResourceWeights[cix]:=ResourceWeights;
1016 end; }
1017 if SavedTiles[cix] <> Tiles then
1018 begin // log city tile changes
1019 CL.Put(sSetCityTiles, pTurn, cix, @Tiles);
1020 SavedTiles[cix] := Tiles;
1021 end;
1022 end;
1023end;
1024
1025procedure NoLogCityTileChanges;
1026var
1027 cix: Integer;
1028begin
1029 for cix := 0 to RW[pTurn].nCity - 1 do
1030 with RW[pTurn].City[cix] do
1031 if Loc >= 0 then
1032 begin
1033 // SavedResourceWeights[cix]:=ResourceWeights;
1034 SavedTiles[cix] := Tiles;
1035 end;
1036end;
1037
1038function HasCityTileChanges: Boolean;
1039var
1040 cix: Integer;
1041begin
1042 Result := False;
1043 for cix := 0 to RW[pTurn].nCity - 1 do
1044 with RW[pTurn].City[cix] do
1045 if Loc >= 0 then begin
1046 // if SavedResourceWeights[cix] <> ResourceWeights then Result := True;
1047 if SavedTiles[cix] <> Tiles then begin
1048 Result := True;
1049 Break;
1050 end;
1051 end;
1052end;
1053
1054procedure BeforeTurn0;
1055var
1056 p1, uix: Integer;
1057begin
1058 for uix := 0 to RW[pTurn].nUn - 1 do { init movement points for first turn }
1059 with RW[pTurn].Un[uix] do
1060 Movement := RW[pTurn].Model[mix].Speed;
1061
1062 if Difficulty[pTurn] > 0 then
1063 DiscoverViewAreas(pTurn)
1064 else { supervisor }
1065 begin
1066 DiscoverAll(pTurn, lObserveSuper);
1067 for p1 := 1 to nPl - 1 do
1068 if 1 shl p1 and GAlive <> 0 then
1069 begin
1070 GiveCivilReport(pTurn, p1);
1071 GiveMilReport(pTurn, p1);
1072 end;
1073 end;
1074 // CheckContact;
1075end;
1076
1077function LoadGame(const FileName: string; Turn: Integer; MovieMode: Boolean): Boolean;
1078var
1079 J: TBrain;
1080 I, ix, D, p1, Command, Subject: Integer;
1081 K: Integer;
1082{$IFDEF TEXTLOG}LoadPos0: Integer; {$ENDIF}
1083 Data: Pointer;
1084 LogFile: TFileStream;
1085 FormerCLState: TCmdListState;
1086 S: string[255];
1087 SaveMap: array [0 .. lxmax * lymax - 1] of Byte;
1088 Started, StatRequest: Boolean;
1089begin
1090 SavePath := ExtractFileDir(FileName);
1091 LogFileName := FileName;
1092 LoadTurn := Turn;
1093 LogFile := TFileStream.Create(LogFileName, fmOpenRead or
1094 fmShareExclusive);
1095 LogFile.Position := 0;
1096 LogFile.Read(S[1], 8); { file id }
1097 LogFile.Read(I, 4); { c-evo version }
1098 LogFile.Read(J, 4); { exe time }
1099
1100 if (I >= FirstBookCompatibleVersion) and (I <= CevoVersion) then
1101 begin
1102 Result := True;
1103 LogFile.Read(lx, 4);
1104 LogFile.Read(ly, 4);
1105 MapSize := lx * ly;
1106 LogFile.Read(LandMass, 4);
1107 if LandMass = 0 then
1108 LogFile.Read(RealMap, MapSize * 4); // use predefined map
1109 LogFile.Read(MaxTurn, 4);
1110 LogFile.Read(RND, 4);
1111 LogFile.Read(GTurn, 4);
1112 LogFile.Read(SaveMap, 4);
1113 if SaveMap[0] <> $80 then
1114 LogFile.Read(SaveMap[4], ((MapSize - 1) div 4 + 1) * 4 - 4);
1115 for p1 := 0 to nPl - 1 do
1116 begin
1117 LogFile.Read(S[0], 4);
1118 if S[0] = #0 then
1119 PlayersBrain[p1] := nil
1120 else
1121 begin
1122 LogFile.Read(S[4], Byte(S[0]) div 4 * 4);
1123 LogFile.Read(OriginalDataVersion[p1], 4);
1124 LogFile.Read(D, 4); { behavior }
1125 LogFile.Read(Difficulty[p1], 4);
1126 J := Brains.Last;
1127 while Assigned(J) and (AnsiCompareFileName(J.FileName, S) <> 0) do begin
1128 K := Brains.IndexOf(J) - 1;
1129 if K >= 0 then J := Brains[K]
1130 else J := nil;
1131 end;
1132 if not Assigned(J) then
1133 begin // ai not found -- replace by local player
1134 ProcessClientData[p1] := False;
1135 NotifyMessage := S;
1136 Notify(ntAIError);
1137 J := BrainTerm;
1138 end
1139 else
1140 ProcessClientData[p1] := True;
1141 if J.Kind = btNoTerm then
1142 J := BrainSuperVirtual;
1143 // crashed tournament -- load as supervisor
1144 PlayersBrain[p1] := J;
1145 end;
1146 end;
1147 end
1148 else
1149 Result := False;
1150
1151 if Result then begin
1152 CL := TCmdList.Create;
1153 CL.LoadFromFile(LogFile);
1154 end;
1155 FreeAndNil(LogFile);
1156 if not Result then
1157 Exit;
1158
1159 Notify(ntStartDone);
1160 if LoadTurn < 0 then
1161 LoadTurn := GTurn;
1162 if MovieMode then
1163 Mode := moMovie
1164 else if LoadTurn = 0 then
1165 Mode := moLoading
1166 else
1167 Mode := moLoading_Fast;
1168{$IFDEF TEXTLOG}AssignFile(TextLog, LogFileName + '.txt');
1169 Rewrite(TextLog); {$ENDIF}
1170 LoadOK := True;
1171 StartGame;
1172 if MovieMode then
1173 begin
1174 bix[0].Client(cShowGame, 0, nil^);
1175 Notify(ntBackOff);
1176 end
1177 else
1178 Notify(ntLoadBegin);
1179
1180 Started := False;
1181 StatRequest := False;
1182 MovieStopped := False;
1183{$IFDEF LOADPERF}QueryPerformanceCounter(time_total0);
1184 time_a := 0;
1185 time_b := 0;
1186 time_c := 0; {$ENDIF}
1187 while not MovieStopped and (CL.Progress < 1000) do
1188 begin
1189 FormerCLState := CL.State;
1190 CL.Get(Command, p1, Subject, Data);
1191 if p1 < 0 then
1192 p1 := pTurn;
1193 if StatRequest and (Command and (sctMask or sExecute) <> sctInternal or
1194 sExecute) then
1195 begin
1196 GenerateStat(pTurn);
1197 StatRequest := False;
1198 end;
1199 // complete all internal commands following an sTurn before generating statistics
1200 if (Command = sTurn) and not Started then
1201 begin
1202{$IFDEF TEXTLOG}WriteLn(TextLog, '---Turn 0 P0---'); {$ENDIF}
1203 for p1 := 0 to nPl - 1 do
1204 if Assigned(bix[p1]) and ((Mode <> moMovie) or (p1 = 0)) then
1205 CallPlayer(cReplay, p1, nil^);
1206 BeforeTurn0;
1207 if MovieMode then
1208 begin
1209 Inform(pTurn);
1210 CallPlayer(cMovieTurn, 0, nil^);
1211 end;
1212 StatRequest := True;
1213 Started := True;
1214 end
1215 else if (Command = sTurn) and (pTurn = 0) and (GTurn = LoadTurn) then
1216 begin
1217 Assert(CL.State.LoadPos = FormerCLState.LoadPos + 4); // size of sTurn
1218 CL.State := FormerCLState;
1219 CL.Cut;
1220 Break;
1221 end
1222 else if Command = sIntDataChange then
1223 begin
1224{$IFDEF TEXTLOG}LoadPos0 := CL.State.LoadPos; {$ENDIF}
1225 if ProcessClientData[p1] then
1226 CL.GetDataChanges(RW[p1].Data, bix[p1].DataSize)
1227 else
1228 CL.GetDataChanges(nil, 0);
1229{$IFDEF TEXTLOG}WriteLn(TextLog, Format('Data Changes P%d (%d Bytes)', [p1, CL.State.LoadPos - LoadPos0])); {$ENDIF}
1230 end
1231 else
1232 begin
1233{$IFDEF TEXTLOG}CmdInfo := Format('Command %x', [Command]); {$ENDIF}
1234 if Command and (sctMask or sExecute) = sctInternal or sExecute then
1235 IntServer(Command, p1, Subject, Data^) // internal command
1236 else
1237 begin
1238 StatRequest := Command = sTurn;
1239 Server(Command, p1, Subject, Data^);
1240 end;
1241{$IFDEF TEXTLOG}WriteLn(TextLog, CmdInfo); {$ENDIF}
1242 end;
1243 if not MovieMode then
1244 Notify(ntLoadState, CL.Progress * 128 div 1000);
1245 end;
1246
1247 if MovieMode then
1248 begin
1249 Notify(ntBackOn);
1250 bix[0].Client(cBreakGame, -1, nil^);
1251 EndGame;
1252 Notify(ntStartGo);
1253 Result := False;
1254 Exit;
1255 end;
1256
1257 if StatRequest then
1258 GenerateStat(pTurn);
1259 Assert(Started);
1260{$IFDEF TEXTLOG}CloseFile(TextLog); {$ENDIF}
1261{$IFDEF LOADPERF}QueryPerformanceCounter(time_total); { time in s is: (time_total-time_total0)/PerfFreq }{$ENDIF}
1262 NoLogChanges;
1263 NoLogCityTileChanges;
1264 if IsAutoSaveFileName(LogFileName) then begin
1265 LogFileName := FromAutoSaveFileName(LogFileName);
1266 nLogOpened := -1;
1267 end
1268 else
1269 nLogOpened := CL.State.nLog;
1270
1271 Mode := moPlaying;
1272 LastEndClientCommand := -1;
1273 if (GTestFlags and tfUncover <> 0) or (Difficulty[pTurn] = 0) then
1274 DiscoverAll(pTurn, lObserveSuper) { supervisor - all tiles visible }
1275 else
1276 DiscoverViewAreas(pTurn);
1277
1278 for p1 := 0 to nPl - 1 do
1279 if 1 shl p1 and (GAlive or GWatching) <> 0 then
1280 begin
1281 RecalcPeaceMap(p1);
1282 for ix := 0 to RW[p1].nEnemyUn - 1 do
1283 with RW[p1].EnemyUn[ix] do
1284 emix := RWemix[p1, Owner, mix];
1285 Inform(p1);
1286 end;
1287{$IFOPT O-}CheckBorders(-2); {$ENDIF} // for testing only
1288 Notify(ntEndInfo);
1289 if not LoadOK then
1290 begin
1291 NotifyMessage := LogFileName;
1292 Notify(ntLoadError);
1293 end;
1294 bix[0].Client(cShowGame, 0, nil^);
1295 Notify(ntBackOff);
1296 Inform(pTurn);
1297 ChangeClientWhenDone(cResume, 0, nil^, 0);
1298end;
1299
1300procedure InsertTerritoryUpdateCommands;
1301var
1302 p1, Command, Subject: Integer;
1303 Data: Pointer;
1304 FormerCLState: TCmdListState;
1305begin
1306 while CL.Progress < 1000 do
1307 begin
1308 FormerCLState := CL.State;
1309 CL.Get(Command, p1, Subject, Data);
1310 if (Command = sIntExpandTerritory) and (p1 = pTurn) then
1311 begin
1312 IntServer(Command, p1, Subject, Data^);
1313{$IFDEF TEXTLOG}WriteLn(TextLog, 'AfterTurn - ExpandTerritory'); {$ENDIF}
1314 end
1315 else
1316 begin
1317 CL.State := FormerCLState;
1318 Break;
1319 end;
1320 end;
1321{$IFOPT O-}InvalidTreatyMap := 0; {$ENDIF}
1322end;
1323
1324procedure StartNewGame(const FileName, AMapFileName: string;
1325 Newlx, Newly, NewLandMass, NewMaxTurn: Integer);
1326var
1327 I: Integer;
1328begin
1329 Notify(ntStartDone);
1330 SavePath := ExtractFileDir(FileName);
1331 LogFileName := FileName;
1332 MapFileName := AMapFileName;
1333 {$IFDEF FastContact}
1334 lx := 24;
1335 ly := 42;
1336 {$ELSE}
1337 lx := Newlx;
1338 ly := Newly;
1339 {$ENDIF}
1340 MapSize := lx * ly;
1341 if MapFileName <> '' then
1342 LandMass := 0
1343 else
1344 LandMass := NewLandMass;
1345 MaxTurn := NewMaxTurn;
1346 DelphiRandomize;
1347 RND := DelphiRandSeed;
1348 Mode := moPlaying;
1349 CL := TCmdList.Create;
1350 StartGame;
1351 NoLogChanges;
1352 CallAllPlayers(cGetReady, nil^);
1353 LogChanges;
1354 CL.Put(sTurn, 0, 0, nil);
1355 BeforeTurn0;
1356 NoLogCityTileChanges;
1357 GenerateStat(pTurn);
1358 nLogOpened := -1;
1359 LastEndClientCommand := -1;
1360 CallPlayer(cShowGame, 0, nil^);
1361 for I := 0 to nPl - 1 do
1362 if Assigned(bix[I]) and (bix[I].Kind = btNetworkServer) then
1363 CallPlayer(cShowGame, I, nil^);
1364 Notify(ntBackOff);
1365 Inform(pTurn);
1366 ChangeClientWhenDone(cTurn, 0, nil^, 0);
1367end;
1368
1369procedure FillMap(TerrainType: Cardinal; SpecialResources: Boolean);
1370var
1371 Loc1: Integer;
1372begin
1373 if SpecialResources then begin
1374 for Loc1 := 0 to MapSize - 1 do
1375 EditTile(Loc1, TerrainType or ($F shl 27));
1376 end else begin
1377 for Loc1 := 0 to MapSize - 1 do
1378 RealMap[Loc1] := TerrainType or ($F shl 27);
1379 end;
1380end;
1381
1382procedure DirectHelp(Command: Integer);
1383begin
1384 InitBrain(BrainTerm);
1385 BrainTerm.Client(Command, -1, nil^);
1386 AICredits := #0;
1387end;
1388
1389procedure EditMap(const AMapFileName: string; Newlx, Newly, NewLandMass: Integer);
1390var
1391 p1: Integer;
1392 Game: TNewGameData;
1393 Map: TMap;
1394begin
1395 Notify(ntStartDone);
1396 Notify(ntInitLocalHuman);
1397 MapFileName := AMapFileName;
1398 lx := Newlx;
1399 ly := Newly;
1400 MapSize := lx * ly;
1401 LandMass := NewLandMass;
1402 bix[0] := BrainTerm;
1403 Difficulty[0] := 0;
1404 InitBrain(BrainTerm);
1405
1406 DelphiRandomize;
1407 GAlive := 0;
1408 GWatching := 1;
1409 if FileExists(MapFileName) then begin
1410 Map := TMap.Create;
1411 Map.LoadFromFile(MapFileName);
1412 MapSize := Map.MapSize;
1413 lx := Map.Size.X;
1414 ly := Map.Size.Y;
1415 Move(Map.Tiles[0], RealMap, MapSize * 4);
1416 FreeAndNil(Map);
1417 end else begin
1418 FillMap(fOcean, False);
1419 end;
1420 CL := nil;
1421 InitMapEditor;
1422 RW[0].Data := nil;
1423 RW[0].BorderHelper := nil;
1424 RW[0].Alive := 0;
1425 Game.lx := lx;
1426 Game.ly := ly;
1427 Game.RO[0] := @RW[0];
1428 Game.Difficulty[0] := 0;
1429 for p1 := 1 to nPl - 1 do begin
1430 Game.RO[p1] := nil;
1431 Game.Difficulty[p1] := -1;
1432 end;
1433 BrainTerm.Client(cNewMap, -1, Game);
1434
1435 DiscoverAll(0, lObserveSuper);
1436 Notify(ntEndInfo);
1437 bix[0].Client(cShowGame, 0, nil^);
1438 Notify(ntBackOff);
1439 ChangeClientWhenDone(cEditMap, 0, nil^, 0);
1440end;
1441
1442procedure DestroySpacePort_TellPlayers(P, pCapturer: Integer);
1443var
1444 cix, I, p1: Integer;
1445 ShowShipChange: TShowShipChange;
1446begin
1447 // stop ship part production
1448 for cix := 0 to RW[P].nCity - 1 do
1449 with RW[P].City[cix] do
1450 if (Loc >= 0) and (Project and cpImp <> 0) and
1451 ((Project and cpIndex = woMIR) or
1452 (Imp[Project and cpIndex].Kind = ikShipPart)) then
1453 begin
1454 Inc(RW[P].Money, Prod0);
1455 Prod := 0;
1456 Prod0 := 0;
1457 Project := cpImp + imTrGoods;
1458 Project0 := cpImp + imTrGoods;
1459 end;
1460
1461 // destroy ship
1462 with GShip[P] do
1463 if Parts[0] + Parts[1] + Parts[2] > 0 then
1464 begin
1465 for I := 0 to nShipPart - 1 do
1466 begin
1467 ShowShipChange.Ship1Change[I] := -Parts[I];
1468 if pCapturer >= 0 then
1469 begin
1470 ShowShipChange.Ship2Change[I] := Parts[I];
1471 Inc(GShip[pCapturer].Parts[I], Parts[I]);
1472 end;
1473 Parts[I] := 0;
1474 end;
1475 if Mode >= moMovie then
1476 begin
1477 if pCapturer >= 0 then
1478 ShowShipChange.Reason := scrCapture
1479 else
1480 ShowShipChange.Reason := scrDestruction;
1481 ShowShipChange.Ship1Owner := P;
1482 ShowShipChange.Ship2Owner := pCapturer;
1483 for p1 := 0 to nPl - 1 do
1484 if 1 shl p1 and (GAlive or GWatching) <> 0 then
1485 begin
1486 Move(GShip, RW[p1].Ship, SizeOf(GShip));
1487 if 1 shl p1 and GWatching <> 0 then
1488 CallPlayer(cShowShipChange, p1, ShowShipChange);
1489 end;
1490 end;
1491 end;
1492end;
1493
1494procedure DestroyCity_TellPlayers(P, cix: Integer; SaveUnits: Boolean);
1495begin
1496 if RW[P].City[cix].Built[imSpacePort] > 0 then
1497 DestroySpacePort_TellPlayers(P, -1);
1498 DestroyCity(P, cix, SaveUnits);
1499end;
1500
1501procedure ChangeCityOwner_TellPlayers(pOld, cixOld, pNew: Integer);
1502begin
1503 if RW[pOld].City[cixOld].Built[imSpacePort] > 0 then
1504 if RW[pNew].NatBuilt[imSpacePort] > 0 then
1505 DestroySpacePort_TellPlayers(pOld, pNew)
1506 else
1507 DestroySpacePort_TellPlayers(pOld, -1);
1508 ChangeCityOwner(pOld, cixOld, pNew);
1509end;
1510
1511procedure CheckWin(P: Integer);
1512var
1513 I: Integer;
1514 ShipComplete: Boolean;
1515begin
1516 ShipComplete := True;
1517 for I := 0 to nShipPart - 1 do
1518 if GShip[P].Parts[I] < ShipNeed[I] then begin
1519 ShipComplete := False;
1520 Break;
1521 end;
1522 if ShipComplete then
1523 GWinner := GWinner or 1 shl P; // game won!
1524end;
1525
1526procedure BeforeTurn;
1527var
1528 I, p1, uix, cix, V21, Loc1, Cost, Job0, nAlive, nAppliers, ad, OldLoc,
1529 SiegedTiles, nUpdateLoc: Integer;
1530 UpdateLoc: array [0 .. numax - 1] of Integer;
1531 Radius: TVicinity21Loc;
1532 ShowShipChange: TShowShipChange;
1533 TribeExtinct, JobDone, MirBuilt: Boolean;
1534begin
1535{$IFOPT O-}Assert(1 shl pTurn and InvalidTreatyMap = 0); {$ENDIF}
1536 Assert(1 shl pTurn and (GAlive or GWatching) <> 0);
1537 if (1 shl pTurn and GAlive = 0) and (Difficulty[pTurn] > 0) then
1538 Exit;
1539
1540 if (GWonder[woGrLibrary].EffectiveOwner = pTurn) and (GWinner = 0) then
1541 begin // check great library effect
1542 nAlive := 0;
1543 for p1 := 0 to nPl - 1 do
1544 if 1 shl p1 and GAlive <> 0 then
1545 Inc(nAlive);
1546 ad := 0;
1547 while ad <= (nAdv - 5) do begin
1548 if RW[pTurn].Tech[ad] < tsSeen then
1549 begin
1550 nAppliers := 0;
1551 for p1 := 0 to nPl - 1 do
1552 if (p1 <> pTurn) and (1 shl p1 and GAlive <> 0) and
1553 (RW[p1].Tech[ad] >= tsApplicable) then
1554 Inc(nAppliers);
1555 if nAppliers * 2 > nAlive then
1556 begin
1557 SeeTech(pTurn, ad);
1558 Inc(nTech[pTurn]);
1559 if Mode >= moMovie then
1560 CallPlayer(cShowGreatLibTech, pTurn, ad);
1561 // do not call CallPlayer(pTurn) while map is invalid
1562 end;
1563 end;
1564 Inc(ad);
1565 end;
1566 end;
1567
1568 MaskD(ObserveLevel, MapSize, not Cardinal(3 shl (2 * pTurn)));
1569 if Mode > moLoading_Fast then
1570 MaskD(RW[pTurn].Map^, MapSize, Cardinal(not Cardinal(fUnit or fHiddenUnit or
1571 fStealthUnit or fObserved or fSpiedOut or fOwned or fOwnZoCUnit or
1572 fInEnemyZoC)));
1573 RW[pTurn].nEnemyUn := 0;
1574
1575 MirBuilt := False;
1576 if (Difficulty[pTurn] > 0) and (GWinner = 0) then
1577 with RW[pTurn] do
1578 begin
1579 if nCity > 0 then
1580 for p1 := 0 to nPl - 1 do
1581 if GTurn = EvaStart[p1] + PeaceEvaTurns then
1582 begin // peace contract -- remove all units from p1's territory
1583 Loc1 := City[0].Loc; // search destination for homeless units
1584 for cix := 1 to nCity - 1 do
1585 if (City[cix].Loc >= 0) and
1586 ((Loc1 < 0) or (City[cix].Built[imPalace] > 0)) then
1587 Loc1 := City[cix].Loc;
1588 for uix := 0 to nUn - 1 do
1589 with Un[uix] do
1590 if (Loc >= 0) and (Model[mix].Kind <> mkDiplomat) and
1591 ((Home >= 0) or (Loc1 >= 0)) and
1592 (RealMap[Loc] shr 27 = Cardinal(p1)) then
1593 begin
1594 OldLoc := Loc;
1595 if Master >= 0 then
1596 begin // transport unload
1597 if Model[mix].Domain = dAir then
1598 Dec(Un[Master].AirLoad)
1599 else
1600 Dec(Un[Master].TroopLoad);
1601 Master := -1;
1602 end
1603 else
1604 FreeUnit(pTurn, uix);
1605
1606 if Home >= 0 then
1607 Loc := City[Home].Loc
1608 else
1609 Loc := Loc1;
1610 PlaceUnit(pTurn, uix);
1611 UpdateUnitMap(OldLoc);
1612 UpdateUnitMap(Loc);
1613 Flags := Flags or unWithdrawn;
1614 Happened := Happened or phPeaceEvacuation;
1615 end;
1616 end;
1617
1618 if Mode >= moMovie then
1619 FillChar(ShowShipChange, SizeOf(ShowShipChange), 0);
1620 TribeExtinct := True;
1621 nUpdateLoc := 0;
1622 for cix := 0 to nCity - 1 do
1623 with City[cix] do
1624 if Loc >= 0 then
1625 begin { next turn for all cities - city loop 1 }
1626 // if ServerVersion[pTurn]>=$000EF0 then
1627 // Flags:=Flags and (chFounded or chCaptured or chProductionSabotaged or chDisorder)
1628 // else Flags:=Flags and (chCaptured or chProductionSabotaged or chDisorder);
1629 // check for siege
1630 SiegedTiles := 0;
1631 V21_to_Loc(Loc, Radius);
1632 for V21 := 1 to 26 do
1633 if Tiles and (1 shl V21) and not (1 shl CityOwnTile) <> 0 then
1634 begin
1635 Loc1 := Radius[V21];
1636 Assert((Loc1 >= 0) and (Loc1 < MapSize) and
1637 (UsedByCity[Loc1] = Loc));
1638 p1 := RealMap[Loc1] shr 27;
1639 if (RealMap[Loc1] and fCity <> 0) or (p1 < nPl) and
1640 (p1 <> pTurn) and (RW[pTurn].Treaty[p1] >= trPeace) or
1641 (ZoCMap[Loc1] > 0) and (Occupant[Loc1] <> pTurn) and
1642 (Treaty[Occupant[Loc1]] < trPeace) then
1643 begin
1644 Tiles := Tiles and not (1 shl V21);
1645 UsedByCity[Loc1] := -1;
1646 Flags := Flags or chSiege;
1647 Inc(SiegedTiles);
1648 end;
1649 end;
1650 while SiegedTiles > 0 do // replace sieged tiles
1651 begin
1652 if not AddBestCityTile(pTurn, cix) then
1653 Break;
1654 Dec(SiegedTiles);
1655 end;
1656
1657 if Flags and chFounded = 0 then
1658 begin
1659 // CollectCityResources(pTurn,cix); // old style
1660
1661 if CityTurn(pTurn, cix) then
1662 TribeExtinct := False
1663 else
1664 begin // city is erased
1665 RemoveDomainUnits(dSea, pTurn, Loc);
1666 RemoveDomainUnits(dAir, pTurn, Loc);
1667 Map[Loc] := Map[Loc] and not fCity; // !!! do this in inner core
1668 UpdateLoc[nUpdateLoc] := Loc;
1669 Inc(nUpdateLoc);
1670 DestroyCity_TellPlayers(pTurn, cix, True);
1671 end;
1672
1673 if (Flags and chProduction <> 0) and (Project0 and cpImp <> 0)
1674 then
1675 begin
1676 if Project0 and cpIndex = woMIR then // MIR completed
1677 MirBuilt := True
1678 else if Project0 and cpIndex = woManhattan then
1679 GColdWarStart := GTurn
1680 else if Imp[Project0 and cpIndex].Kind = ikShipPart
1681 then { ship parts produced }
1682 Inc(ShowShipChange.Ship1Change[Project0 and cpIndex -
1683 imShipComp]);
1684 end;
1685 end;
1686 end; { city loop 1 }
1687 if nUpdateLoc > 0 then
1688 begin
1689 CheckBorders(-1, pTurn);
1690 for I := 0 to nUpdateLoc - 1 do
1691 UpdateUnitMap(UpdateLoc[I], True);
1692 if Mode >= moMovie then
1693 for p1 := 0 to nPl - 1 do
1694 if (1 shl p1 and GWatching <> 0) and (p1 <> pTurn) then
1695 for I := 0 to nUpdateLoc - 1 do
1696 if ObserveLevel[UpdateLoc[I]] shr (2 * p1) and 3 >= lObserveUnhidden
1697 then
1698 CallPlayer(cShowCityChanged, p1, UpdateLoc[I]);
1699 end;
1700
1701 for uix := 0 to nUn - 1 do
1702 with Un[uix] do
1703 if Loc >= 0 then
1704 begin // unit loop 2
1705 if Health < 100 then
1706 Recover(pTurn, uix);
1707
1708 if Flags and unMountainDelay <> 0 then
1709 begin
1710 Movement := 0;
1711 Flags := Flags and not unMountainDelay;
1712 end
1713 else
1714 Movement := UnitSpeed(pTurn, mix, Health); { refresh movement }
1715
1716 Assert(Loc >= 0);
1717 if Model[mix].Kind <> mkDiplomat then
1718 begin // check treaty violation
1719 p1 := RealMap[Loc] shr 27;
1720 if (p1 < nPl) and (p1 <> pTurn) and (Treaty[p1] >= trPeace) then
1721 begin
1722 if (Job in [jCity, jPillage, jClear, jAfforest, jTrans]) or
1723 (Job in [jIrr, jMine, jFort, jBase]) and
1724 (RealMap[Loc] and fTerImp <> 0) then
1725 Job := jNone;
1726 if (GTurn > EvaStart[p1] + PeaceEvaTurns) and
1727 (Treaty[p1] <> trAlliance) then
1728 begin
1729 EvaStart[p1] := GTurn;
1730 Happened := Happened or phPeaceViolation;
1731 if Mode >= moMovie then
1732 CallPlayer(cShowPeaceViolation, p1, pTurn);
1733 end;
1734 end;
1735 end;
1736
1737 if ServerVersion[pTurn] >= $000EF0 then
1738 begin
1739 if (Health <= 0) or TribeExtinct then
1740 RemoveUnit_UpdateMap(pTurn, uix);
1741 end;
1742 end;
1743
1744 if ServerVersion[pTurn] < $000EF0 then
1745 for uix := 0 to nUn - 1 do
1746 with Un[uix] do
1747 if Loc >= 0 then
1748 begin // unit loop 3
1749 Loc1 := Loc;
1750 Job0 := Job;
1751 if Job <> jNone then
1752 JobDone := Work(pTurn, uix);
1753 { settlers do terrain improvement jobs }
1754 if (Health <= 0) or TribeExtinct then
1755 RemoveUnit_UpdateMap(pTurn, uix);
1756
1757 if (Job0 = jCity) and JobDone then // new city
1758 begin
1759 AddBestCityTile(pTurn, RW[pTurn].nCity - 1);
1760 UpdateUnitMap(Loc1, True);
1761 if Mode >= moMovie then // tell enemies
1762 for p1 := 0 to nPl - 1 do
1763 if (1 shl p1 and GWatching <> 0) and (p1 <> pTurn) and
1764 (ObserveLevel[Loc1] and (3 shl (2 * p1)) > 0) then
1765 CallPlayer(cShowCityChanged, p1, Loc1);
1766 end;
1767 end;
1768
1769 { pollution - city loop 3 }
1770 for cix := 0 to nCity - 1 do
1771 with City[cix] do
1772 if (Loc >= 0) and (Pollution >= MaxPollution) then
1773 Pollute(pTurn, cix);
1774
1775 CompactLists(pTurn);
1776 if (nUn = 0) and (nCity = 0) then
1777 begin // nation made extinct
1778 Happened := Happened or phExtinct;
1779 GAlive := GAlive and not (1 shl pTurn);
1780 Stat[stPop, pTurn, GTurn] := 0;
1781 Stat[stMil, pTurn, GTurn] := 0;
1782 Stat[stScience, pTurn, GTurn] := 0;
1783 Stat[stExplore, pTurn, GTurn] := 0;
1784 Stat[stTerritory, pTurn, GTurn] := 0;
1785 Stat[stWork, pTurn, GTurn] := 0;
1786 for p1 := 0 to nPl - 1 do
1787 if 1 shl p1 and (GAlive or GWatching) <> 0 then
1788 begin
1789 if p1 <> pTurn then
1790 begin
1791 GiveCivilReport(p1, pTurn);
1792 if (GTestFlags and tfUncover <> 0) or (Difficulty[p1] = 0) or
1793 (RW[p1].Treaty[pTurn] = trAlliance) then
1794 GiveMilReport(p1, pTurn);
1795 end;
1796 with RW[p1] do
1797 begin
1798 Alive := GAlive;
1799 for Loc1 := 0 to MapSize - 1 do
1800 if Territory[Loc1] = pTurn then
1801 // remove territory of extinct nation from player maps
1802 begin
1803 Territory[Loc1] := -1;
1804 Map[Loc1] := Map[Loc1] and not fPeace;
1805 end;
1806 end;
1807 end;
1808 Exit;
1809 end;
1810
1811 // check research
1812 Cost := TechCost(pTurn);
1813 if GTestFlags and tfImmAdvance <> 0 then
1814 Research := Cost;
1815 if (Happened and phTech = 0) and (Research >= Cost) then
1816 begin
1817 if ResearchTech = adMilitary then
1818 EnableDevModel(pTurn) { new Unit class initiated }
1819 else if ResearchTech >= 0 then
1820 DiscoverTech(pTurn, ResearchTech);
1821
1822 Dec(Research, Cost);
1823 Happened := Happened or phTech;
1824 ResearchTech := -1;
1825 end
1826 else if (ResearchTech = -2) and (nCity > 0) then
1827 begin
1828 Happened := Happened or phTech;
1829 ResearchTech := -1;
1830 end;
1831
1832 if Credibility < MaxCredibility then
1833 for p1 := 0 to nPl - 1 do
1834 if (p1 <> pTurn) and (1 shl p1 and GAlive <> 0) and
1835 (Treaty[p1] >= trPeace) then
1836 begin
1837 Inc(Credibility);
1838 Break;
1839 end;
1840
1841 if GWinner = 0 then
1842 CheckWin(pTurn);
1843 if (Mode >= moMovie) and (GWinner = 0) and
1844 ((ShowShipChange.Ship1Change[0] > 0) or
1845 (ShowShipChange.Ship1Change[1] > 0) or
1846 (ShowShipChange.Ship1Change[2] > 0)) then
1847 begin
1848 ShowShipChange.Reason := scrProduction;
1849 ShowShipChange.Ship1Owner := pTurn;
1850 ShowShipChange.Ship2Owner := -1;
1851 for p1 := 0 to nPl - 1 do
1852 if (p1 <> pTurn) and (1 shl p1 and (GAlive or GWatching) <> 0) then
1853 begin
1854 Move(GShip, RW[p1].Ship, SizeOf(GShip));
1855 if 1 shl p1 and GWatching <> 0 then
1856 CallPlayer(cShowShipChange, p1, ShowShipChange);
1857 end;
1858 end;
1859 if WinOnAlone and (GAlive and not (1 shl pTurn or 1) = 0) then
1860 GWinner := 1 shl pTurn; // break if only one nation left
1861
1862 if GTurn = AnarchyStart + AnarchyTurns then
1863 begin
1864 AnarchyStart := -AnarchyTurns - 1;
1865 Government := gDespotism;
1866 for p1 := 0 to nPl - 1 do
1867 if (p1 <> pTurn) and ((GAlive or GWatching) and (1 shl p1) <> 0) then
1868 RW[p1].EnemyReport[pTurn].Government := gDespotism;
1869 Inc(Happened, phChangeGov);
1870 end;
1871 end; // if Difficulty[pTurn]>0
1872
1873 if (pTurn = 0) and (GWinner > 0) then
1874 begin // game over, give world map and all reports to player 0
1875 DiscoverAll(pTurn, lObserveSuper);
1876 for p1 := 1 to nPl - 1 do
1877 if 1 shl p1 and GAlive <> 0 then
1878 begin
1879 if RW[pTurn].Treaty[p1] < trNone then
1880 begin
1881 RW[pTurn].Treaty[p1] := trNone;
1882 RW[p1].Treaty[pTurn] := trNone;
1883 end;
1884 GiveCivilReport(pTurn, p1);
1885 GiveMilReport(pTurn, p1);
1886 end;
1887 end
1888 else
1889 begin
1890 // show observed areas
1891 if (GTestFlags and tfUncover <> 0) or (Difficulty[pTurn] = 0)
1892 then { supervisor - all tiles visible }
1893 begin
1894 if (bix[pTurn].Kind <> btNoTerm) and
1895 ((Difficulty[pTurn] > 0) or (Mode > moLoading_Fast)) then
1896 DiscoverAll(pTurn, lObserveSuper);
1897 end
1898 else
1899 begin
1900 DiscoverViewAreas(pTurn);
1901 if MirBuilt then
1902 DiscoverAll(pTurn, lObserveUnhidden);
1903 end;
1904 end;
1905 // CheckContact;
1906end;
1907
1908procedure AfterTurn;
1909var
1910 cix, uix, p1, Loc1, Job0: Integer;
1911 JobDone: Boolean;
1912begin
1913 with RW[pTurn] do
1914 begin
1915 for cix := 0 to nCity - 1 do
1916 if City[cix].Loc >= 0 then
1917 begin
1918 // City[cix].Flags:=City[cix].Flags and not chProductionSabotaged;
1919 City[cix].Flags := City[cix].Flags and (chCaptured or chDisorder);
1920 CollectCityResources(pTurn, cix); // new style
1921 end;
1922
1923 Inc(Money, OracleIncome);
1924 OracleIncome := 0;
1925 if GWonder[woOracle].EffectiveOwner = pTurn then
1926 begin
1927 for p1 := 0 to nPl - 1 do
1928 if (1 shl p1 and GAlive <> 0) and
1929 ((p1 = pTurn) or (RW[pTurn].Treaty[p1] > trNoContact)) then
1930 for cix := 0 to RW[p1].nCity - 1 do
1931 if (RW[p1].City[cix].Loc >= 0) and
1932 (RW[p1].City[cix].Built[imTemple] > 0) then
1933 Inc(OracleIncome);
1934 end;
1935
1936 if (GTestFlags and tfImmImprove = 0) and (Government <> gAnarchy) then
1937 for cix := 0 to nCity - 1 do
1938 if (City[cix].Loc >= 0) and (City[cix].Flags and chCaptured = 0) then
1939 PayCityMaintenance(pTurn, cix);
1940
1941 if ServerVersion[pTurn] >= $000EF0 then
1942 begin // let settlers work
1943 for cix := 0 to nCity - 1 do
1944 City[cix].Flags := City[cix].Flags and not chFounded;
1945 for uix := 0 to nUn - 1 do
1946 with Un[uix] do
1947 if Loc >= 0 then
1948 begin
1949 Loc1 := Loc;
1950 Job0 := Job;
1951 if Job <> jNone then
1952 JobDone := Work(pTurn, uix);
1953 { settlers do terrain improvement jobs }
1954 if Health <= 0 then
1955 RemoveUnit_UpdateMap(pTurn, uix);
1956
1957 if (Job0 = jCity) and JobDone then // new city
1958 begin
1959 AddBestCityTile(pTurn, RW[pTurn].nCity - 1);
1960 UpdateUnitMap(Loc1, True);
1961 if Mode >= moMovie then // tell enemies
1962 for p1 := 0 to nPl - 1 do
1963 if (1 shl p1 and GWatching <> 0) and (p1 <> pTurn) and
1964 (ObserveLevel[Loc1] and (3 shl (2 * p1)) > 0) then
1965 CallPlayer(cShowCityChanged, p1, Loc1);
1966 end;
1967 end;
1968 end;
1969
1970 for uix := 0 to nUn - 1 do
1971 with Un[uix] do
1972 if Loc >= 0 then
1973 begin { next turn for all units }
1974 if Model[mix].Domain = dAir then
1975 if (Master >= 0) or (RealMap[Loc] and fCity <> 0) or
1976 (RealMap[Loc] and fTerImp = tiBase) then
1977 begin
1978 Fuel := Model[mix].Cap[mcFuel];
1979 Flags := Flags or unBombsLoaded;
1980 end
1981 else if Model[mix].Kind = mkSpecial_Glider then { glider }
1982 begin
1983 if RealMap[Loc] and fTerrain < fGrass then
1984 begin
1985 RemoveUnit_UpdateMap(pTurn, uix); // unit lost
1986 Happened := Happened or phGliderLost;
1987 end;
1988 end
1989 else
1990 begin
1991 Dec(Fuel);
1992 if Fuel < 0 then
1993 begin
1994 RemoveUnit_UpdateMap(pTurn, uix); // unit lost
1995 Happened := Happened or phPlaneLost;
1996 end
1997 end
1998 else if (Master < 0) and (Movement > 0) then // check HostileDamage
1999 begin
2000 Health := Health - HostileDamage(pTurn, mix, Loc, Movement);
2001 if Health < 0 then
2002 RemoveUnit_UpdateMap(pTurn, uix);
2003 end;
2004 end; { unit loop 1 }
2005
2006 for uix := 0 to nUn - 1 do
2007 with Un[uix] do
2008 begin
2009 Flags := Flags and not unWithdrawn;
2010 if (Loc >= 0) and (Model[mix].Domain = dGround) and (Master < 0) and
2011 ((Integer(Movement) = Model[mix].Speed) or
2012 (Model[mix].Cap[mcAcademy] > 0) and (Movement * 2 >= Model[mix].Speed))
2013 then
2014 Flags := Flags or unFortified; // fortify unmoved units
2015 end;
2016
2017 if (GTestFlags and tfUncover = 0) and (Difficulty[pTurn] > 0) then
2018 begin // restrict view area to current positions
2019 MaskD(ObserveLevel, MapSize, not Cardinal(3 shl (2 * pTurn)));
2020 if Mode > moLoading_Fast then
2021 MaskD(RW[pTurn].Map^, MapSize, Cardinal(not Cardinal(fUnit or fHiddenUnit or
2022 fStealthUnit or fObserved or fSpiedOut or fOwned or fOwnZoCUnit or
2023 fInEnemyZoC)));
2024 RW[pTurn].nEnemyUn := 0;
2025 DiscoverViewAreas(pTurn);
2026 end;
2027
2028 if GWinner = 0 then
2029 for p1 := 0 to nPl - 1 do
2030 if 1 shl p1 and GAlive <> 0 then
2031 CheckWin(p1);
2032 end;
2033end;
2034
2035procedure NextPlayer;
2036begin
2037 if GTurn = 0 then
2038 BeforeTurn0
2039 else
2040 BeforeTurn;
2041 NoLogCityTileChanges;
2042 GenerateStat(pTurn);
2043 Inform(pTurn);
2044 ChangeClient;
2045end;
2046
2047function ExecuteMove(P, uix, ToLoc: Integer; var MoveInfo: TMoveInfo;
2048 ShowMove: TShowMove): Integer;
2049var
2050 I, p1, FromLoc, uix1, nUpdateLoc: Integer;
2051 MinLevel, MissionResult: Cardinal;
2052 PModel: ^TModel;
2053 UpdateLoc: array [0 .. numax - 1] of Integer;
2054 SeeFrom, SeeTo, ExtDiscover: Boolean;
2055begin
2056 Result := 0;
2057 with RW[P], Un[uix] do
2058 begin
2059 PModel := @Model[mix];
2060 FromLoc := Loc;
2061
2062 if Master < 0 then
2063 FreeUnit(P, uix);
2064 if (MoveInfo.MoveType in [mtMove, mtCapture]) and MoveInfo.MountainDelay
2065 then
2066 begin
2067 Flags := Flags or unMountainDelay;
2068 end;
2069 Loc := -2;
2070 if TroopLoad + AirLoad > 0 then
2071 for I := 0 to nUn - 1 do
2072 if (Un[I].Loc >= 0) and (Un[I].Master = uix) then
2073 Un[I].Loc := -2;
2074 UpdateUnitMap(FromLoc);
2075
2076 if Mode >= moMovie then { show move in interface modules }
2077 begin
2078 ShowMove.EndHealth := MoveInfo.EndHealth;
2079 ShowMove.EndHealthDef := -1;
2080 if Master >= 0 then
2081 if Model[Un[Master].mix].Domain = dAir then
2082 ShowMove.Flags := ShowMove.Flags or umPlaneUnloading
2083 else
2084 ShowMove.Flags := ShowMove.Flags or umShipUnloading;
2085 if MoveInfo.ToMaster >= 0 then
2086 if Model[Un[MoveInfo.ToMaster].mix].Domain = dAir then
2087 ShowMove.Flags := ShowMove.Flags or umPlaneLoading
2088 else
2089 ShowMove.Flags := ShowMove.Flags or umShipLoading;
2090 for p1 := 0 to nPl - 1 do
2091 if (1 shl p1 and GWatching <> 0) and ((p1 <> P) or (bix[p1].Kind = btTerm))
2092 then
2093 begin
2094 if PModel.Cap[mcStealth] > 0 then
2095 MinLevel := lObserveSuper
2096 else if PModel.Cap[mcSub] > 0 then
2097 MinLevel := lObserveAll
2098 else
2099 MinLevel := lObserveUnhidden;
2100 SeeFrom := (p1 = P) or (ObserveLevel[FromLoc] shr (2 * p1) and
2101 3 >= MinLevel);
2102 SeeTo := (p1 = P) or (ObserveLevel[ToLoc] shr (2 * p1) and
2103 3 >= MinLevel);
2104 if SeeFrom and SeeTo then
2105 begin
2106 TellAboutModel(p1, P, mix);
2107 if p1 = P then
2108 ShowMove.emix := -1
2109 else
2110 ShowMove.emix := emixSafe(p1, P, mix);
2111 if MoveInfo.MoveType = mtCapture then
2112 CallPlayer(cShowCapturing, p1, ShowMove)
2113 else
2114 CallPlayer(cShowMoving, p1, ShowMove);
2115 end
2116 else if SeeFrom then
2117 CallPlayer(cShowUnitChanged, p1, FromLoc);
2118 end;
2119 end;
2120
2121 if MoveInfo.MoveType <> mtSpyMission then
2122 Loc := ToLoc;
2123 if TroopLoad + AirLoad > 0 then
2124 for I := 0 to nUn - 1 do
2125 if Un[I].Loc = -2 then
2126 Un[I].Loc := ToLoc;
2127
2128 ExtDiscover := False;
2129 nUpdateLoc := 0;
2130 if MoveInfo.MoveType = mtCapture then
2131 begin
2132 Assert(Occupant[ToLoc] < 0);
2133 for uix1 := 0 to RW[MoveInfo.Defender].nUn - 1 do
2134 with RW[MoveInfo.Defender].Un[uix1] do
2135 if (Loc >= 0) and (Home = MoveInfo.Dcix) then
2136 begin
2137 UpdateLoc[nUpdateLoc] := Loc;
2138 Inc(nUpdateLoc);
2139 end;
2140 // unit will be removed -- remember position and update for all players
2141
2142 if (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Size > 2) and (nCity < ncmax)
2143 then
2144 begin // city captured
2145 ChangeCityOwner_TellPlayers(MoveInfo.Defender, MoveInfo.Dcix, P);
2146 City[nCity - 1].Flags := CaptureTurns shl 16;
2147 CityShrink(P, nCity - 1);
2148 if Mode = moPlaying then
2149 with RW[P].City[nCity - 1] do
2150 begin
2151 // SavedResourceWeights[nCity-1]:=ResourceWeights;
2152 SavedTiles[nCity - 1] := Tiles;
2153 end;
2154 ExtDiscover := True;
2155
2156 // Temple of Zeus effect
2157 if GWonder[woZeus].EffectiveOwner = P then
2158 begin
2159 GiveCivilReport(P, MoveInfo.Defender);
2160 for I := 0 to nAdv - 1 do
2161 if not (I in FutureTech) and (RW[P].Tech[I] < tsSeen) and
2162 (RW[MoveInfo.Defender].Tech[I] >= tsApplicable) then
2163 begin
2164 Happened := Happened or phStealTech;
2165 GStealFrom := MoveInfo.Defender;
2166 Break;
2167 end;
2168 end;
2169 if Mode = moPlaying then
2170 LogCheckBorders(P, nCity - 1, MoveInfo.Defender);
2171{$IFOPT O-}
2172 if Mode < moPlaying then
2173 InvalidTreatyMap := not (1 shl P);
2174{$ENDIF}
2175 // territory should not be considered for the rest of the command
2176 // execution, because during loading a game it's incorrect before
2177 // subsequent sIntExpandTerritory is processed
2178 end
2179 else // city destroyed
2180 begin
2181 DestroyCity_TellPlayers(MoveInfo.Defender, MoveInfo.Dcix, False);
2182 CheckBorders(ToLoc, MoveInfo.Defender);
2183 end;
2184 RecalcPeaceMap(P);
2185 if Mode >= moMovie then
2186 Move(GWonder, Wonder, SizeOf(GWonder));
2187 end; { if MoveInfo.MoveType=mtCapture }
2188
2189 if MoveInfo.MoveType = mtSpyMission then
2190 begin
2191 MissionResult := DoSpyMission(P, MoveInfo.Defender, MoveInfo.Dcix,
2192 SpyMission);
2193 if (Mode = moPlaying) and (SpyMission = smStealForeignReports) then
2194 CallPlayer(cShowMissionResult, P, MissionResult);
2195 end;
2196
2197 Health := MoveInfo.EndHealth;
2198 Dec(Movement, MoveInfo.Cost);
2199 // transport unload
2200 if Master >= 0 then
2201 begin
2202 if PModel.Domain = dAir then
2203 Dec(Un[Master].AirLoad)
2204 else
2205 begin
2206 Dec(Un[Master].TroopLoad);
2207 Assert(Movement <= 0);
2208 end;
2209 Master := -1;
2210 end;
2211
2212 if (Health <= 0) or (MoveInfo.MoveType = mtSpyMission) then
2213 RemoveUnit(P, uix) // spy mission or victim of HostileDamage
2214 else
2215 begin // transport load
2216 Master := MoveInfo.ToMaster;
2217 if MoveInfo.ToMaster >= 0 then
2218 begin
2219 if PModel.Domain = dAir then
2220 Inc(Un[MoveInfo.ToMaster].AirLoad)
2221 else
2222 Inc(Un[MoveInfo.ToMaster].TroopLoad);
2223 end
2224 else
2225 PlaceUnit(P, uix);
2226 end;
2227
2228 if (MoveInfo.MoveType = mtCapture) and (nUpdateLoc > 0) then
2229 RecalcMapZoC(P);
2230 UpdateUnitMap(ToLoc, MoveInfo.MoveType = mtCapture);
2231 for I := 0 to nUpdateLoc - 1 do
2232 UpdateUnitMap(UpdateLoc[I]);
2233 // tell about lost units of defender
2234
2235 if (MoveInfo.MoveType <> mtSpyMission) and (Master < 0) then
2236 begin
2237 if (PModel.Kind = mkDiplomat) or (PModel.Domain = dAir) or
2238 (PModel.Cap[mcRadar] + PModel.Cap[mcCarrier] + PModel.Cap[mcAcademy] >
2239 0) or (RealMap[ToLoc] and fTerrain = fMountains) or
2240 (RealMap[ToLoc] and fTerImp = tiFort) or
2241 (RealMap[ToLoc] and fTerImp = tiBase) then
2242 ExtDiscover := True;
2243 if (PModel.Kind = mkDiplomat) or (PModel.Cap[mcSpy] > 0) then
2244 I := lObserveSuper
2245 else if (PModel.Domain = dAir) or
2246 (PModel.Cap[mcRadar] + PModel.Cap[mcCarrier] > 0) then
2247 I := lObserveAll
2248 else
2249 I := lObserveUnhidden;
2250 if ExtDiscover then
2251 begin
2252 if Discover21(ToLoc, P, I, True, PModel.Domain = dGround) then
2253 Result := Result or rEnemySpotted;
2254 end
2255 else
2256 begin
2257 if Discover9(ToLoc, P, I, True, PModel.Domain = dGround) then
2258 Result := Result or rEnemySpotted;
2259 end;
2260 end;
2261
2262 if Mode >= moMovie then { show after-move in interface modules }
2263 for p1 := 0 to nPl - 1 do
2264 if (1 shl p1 and GWatching <> 0) and ((p1 <> P) or (bix[p1].Kind = btTerm))
2265 then
2266 begin
2267 if PModel.Cap[mcStealth] > 0 then
2268 MinLevel := lObserveSuper
2269 else if PModel.Cap[mcSub] > 0 then
2270 MinLevel := lObserveAll
2271 else
2272 MinLevel := lObserveUnhidden;
2273 SeeFrom := (p1 = P) or (ObserveLevel[FromLoc] shr (2 * p1) and
2274 3 >= MinLevel);
2275 SeeTo := (p1 = P) or (ObserveLevel[ToLoc] shr (2 * p1) and
2276 3 >= MinLevel);
2277 if SeeTo and (MoveInfo.MoveType = mtCapture) then
2278 CallPlayer(cShowCityChanged, p1, ToLoc);
2279 if SeeFrom and SeeTo then
2280 CallPlayer(cShowAfterMove, p1, ToLoc)
2281 else if (MoveInfo.MoveType <> mtSpyMission) and SeeTo then
2282 CallPlayer(cShowUnitChanged, p1, ToLoc);
2283 for I := 0 to nUpdateLoc - 1 do
2284 if ObserveLevel[UpdateLoc[I]] shr (2 * p1) and 3 >= lObserveUnhidden
2285 then
2286 CallPlayer(cShowUnitChanged, p1, UpdateLoc[I]);
2287 end;
2288 end;
2289end;
2290
2291function ExecuteAttack(P, uix, ToLoc: Integer; var MoveInfo: TMoveInfo;
2292 ShowMove: TShowMove): Integer;
2293
2294 procedure WriteBattleHistory(ToLoc, FromLoc, Attacker, Defender, mixAttacker,
2295 mixDefender: Integer; AttackerLost, DefenderLost: Boolean);
2296 var
2297 AttackerBattle, DefenderBattle: ^TBattle;
2298 begin
2299 with RW[Attacker] do
2300 begin
2301 if nBattleHistory = 0 then
2302 ReallocMem(BattleHistory, 16 * SizeOf(TBattle))
2303 else if (nBattleHistory >= 16) and
2304 (nBattleHistory and (nBattleHistory - 1) = 0) then
2305 ReallocMem(BattleHistory, nBattleHistory * (2 * SizeOf(TBattle)));
2306 AttackerBattle := @BattleHistory[nBattleHistory];
2307 Inc(nBattleHistory);
2308 end;
2309 with RW[Defender] do
2310 begin
2311 if nBattleHistory = 0 then
2312 ReallocMem(BattleHistory, 16 * SizeOf(TBattle))
2313 else if (nBattleHistory >= 16) and
2314 (nBattleHistory and (nBattleHistory - 1) = 0) then
2315 ReallocMem(BattleHistory, nBattleHistory * (2 * SizeOf(TBattle)));
2316 DefenderBattle := @BattleHistory[nBattleHistory];
2317 Inc(nBattleHistory);
2318 end;
2319 AttackerBattle.Enemy := Defender;
2320 AttackerBattle.Flags := 0;
2321 AttackerBattle.Turn := GTurn;
2322 AttackerBattle.mix := mixAttacker;
2323 AttackerBattle.mixEnemy := mixDefender;
2324 AttackerBattle.ToLoc := ToLoc;
2325 AttackerBattle.FromLoc := FromLoc;
2326 DefenderBattle.Enemy := Attacker;
2327 DefenderBattle.Flags := bhEnemyAttack;
2328 DefenderBattle.Turn := GTurn;
2329 DefenderBattle.mix := mixDefender;
2330 DefenderBattle.mixEnemy := mixAttacker;
2331 DefenderBattle.ToLoc := ToLoc;
2332 DefenderBattle.FromLoc := FromLoc;
2333 if AttackerLost then
2334 begin
2335 AttackerBattle.Flags := AttackerBattle.Flags or bhMyUnitLost;
2336 DefenderBattle.Flags := DefenderBattle.Flags or bhEnemyUnitLost;
2337 end;
2338 if DefenderLost then
2339 begin
2340 AttackerBattle.Flags := AttackerBattle.Flags or bhEnemyUnitLost;
2341 DefenderBattle.Flags := DefenderBattle.Flags or bhMyUnitLost;
2342 end;
2343 end;
2344
2345var
2346 I, p1, FromLoc, uix1, nUpdateLoc, ExpGain, ExpelToLoc, cix1: Integer;
2347 PModel: ^TModel;
2348 UpdateLoc: array [0 .. numax - 1] of Integer;
2349 LoseCityPop, CityDestroyed, SeeFrom, SeeTo, ZoCDefenderDestroyed: Boolean;
2350begin
2351 Result := 0;
2352 with RW[P].Un[uix] do
2353 begin
2354 PModel := @RW[P].Model[mix];
2355 FromLoc := Loc;
2356
2357 ShowMove.EndHealth := MoveInfo.EndHealth;
2358 ShowMove.EndHealthDef := MoveInfo.EndHealthDef;
2359 if MoveInfo.MoveType = mtAttack then
2360 WriteBattleHistory(ToLoc, FromLoc, P, MoveInfo.Defender, mix,
2361 RW[MoveInfo.Defender].Un[MoveInfo.Duix].mix, MoveInfo.EndHealth <= 0,
2362 MoveInfo.EndHealthDef <= 0);
2363
2364 { if RW[p].Treaty[MoveInfo.Defender]=trCeaseFire then
2365 begin
2366 if Mode>=moMovie then
2367 CallPlayer(cShowCancelTreaty,MoveInfo.Defender,P);
2368 CancelTreaty(P,MoveInfo.Defender)
2369 end; }
2370 if Mode >= moMovie then { show attack in interface modules }
2371 for p1 := 0 to nPl - 1 do
2372 if (1 shl p1 and GWatching <> 0) and ((p1 <> P) or (bix[p1].Kind = btTerm))
2373 then
2374 begin
2375 SeeFrom := ObserveLevel[FromLoc] shr (2 * p1) and
2376 3 >= lObserveUnhidden;
2377 SeeTo := ObserveLevel[ToLoc] shr (2 * p1) and 3 >= lObserveUnhidden;
2378 if SeeFrom and SeeTo then
2379 begin
2380 TellAboutModel(p1, P, mix);
2381 if p1 = P then
2382 ShowMove.emix := -1
2383 else
2384 ShowMove.emix := emixSafe(p1, P, mix);
2385 CallPlayer(cShowAttacking, p1, ShowMove);
2386 end;
2387 end;
2388
2389 LoseCityPop := False;
2390 if (RealMap[ToLoc] and fCity <> 0) and
2391 ((MoveInfo.MoveType = mtAttack) and (MoveInfo.EndHealthDef <= 0) or
2392 (MoveInfo.MoveType = mtBombard) and (BombardmentDestroysCity or
2393 (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Size > 2))) then
2394 case PModel.Domain of
2395 dGround:
2396 LoseCityPop := (PModel.Cap[mcArtillery] > 0) or
2397 (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Built[imWalls] = 0) and
2398 (Continent[ToLoc] <> GrWallContinent[MoveInfo.Defender]);
2399 dSea:
2400 LoseCityPop := RW[MoveInfo.Defender].City[MoveInfo.Dcix].Built
2401 [imCoastalFort] = 0;
2402 dAir:
2403 LoseCityPop := RW[MoveInfo.Defender].City[MoveInfo.Dcix].Built
2404 [imMissileBat] = 0;
2405 end;
2406 CityDestroyed := LoseCityPop and
2407 (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Size <= 2);
2408
2409 if MoveInfo.MoveType = mtBombard then
2410 begin
2411 Assert(Movement >= 100);
2412 if PModel.Attack = 0 then
2413 Flags := Flags and not unBombsLoaded;
2414 Dec(Movement, 100);
2415 end
2416 else if MoveInfo.MoveType = mtExpel then
2417 begin
2418 Assert(Movement >= 100);
2419 Job := jNone;
2420 Flags := Flags and not unFortified;
2421 Dec(Movement, 100);
2422 end
2423 else
2424 begin
2425 Assert(MoveInfo.MoveType = mtAttack);
2426 if MoveInfo.EndHealth = 0 then
2427 RemoveUnit(P, uix, MoveInfo.Defender) // destroy attacker
2428 else
2429 begin // update attacker
2430 ExpGain := (Health - MoveInfo.EndHealth + 1) shr 1;
2431 if Exp + ExpGain > (nExp - 1) * ExpCost then
2432 Exp := (nExp - 1) * ExpCost
2433 else
2434 Inc(Exp, ExpGain);
2435 Health := MoveInfo.EndHealth;
2436 Job := jNone;
2437 if RW[MoveInfo.Defender].Model[RW[MoveInfo.Defender].Un[MoveInfo.Duix]
2438 .mix].Domain < dAir then
2439 Flags := Flags and not unBombsLoaded;
2440 Flags := Flags and not unFortified;
2441 if Movement > 100 then
2442 Dec(Movement, 100)
2443 else
2444 Movement := 0;
2445 end;
2446 end;
2447
2448 ZoCDefenderDestroyed := False;
2449 nUpdateLoc := 0;
2450 if MoveInfo.MoveType = mtExpel then
2451 with RW[MoveInfo.Defender], Un[MoveInfo.Duix] do
2452 begin // expel friendly unit
2453 if Home >= 0 then
2454 ExpelToLoc := City[Home].Loc
2455 else
2456 begin
2457 ExpelToLoc := City[0].Loc; // search destination for homeless units
2458 for cix1 := 1 to nCity - 1 do
2459 if (City[cix1].Loc >= 0) and
2460 ((ExpelToLoc < 0) or (City[cix1].Built[imPalace] > 0)) then
2461 ExpelToLoc := City[cix1].Loc;
2462 end;
2463 if ExpelToLoc >= 0 then
2464 begin
2465 FreeUnit(MoveInfo.Defender, MoveInfo.Duix);
2466 Loc := ExpelToLoc;
2467 PlaceUnit(MoveInfo.Defender, MoveInfo.Duix);
2468 UpdateLoc[nUpdateLoc] := Loc;
2469 Inc(nUpdateLoc);
2470 Flags := Flags or unWithdrawn;
2471 end;
2472 end
2473 else if (MoveInfo.MoveType = mtAttack) and (MoveInfo.EndHealthDef > 0) then
2474 with RW[MoveInfo.Defender].Un[MoveInfo.Duix] do
2475 begin // update defender
2476 ExpGain := (Health - MoveInfo.EndHealthDef + 1) shr 1;
2477 if Exp + ExpGain > (nExp - 1) * ExpCost then
2478 Exp := (nExp - 1) * ExpCost
2479 else
2480 Inc(Exp, ExpGain);
2481 Health := MoveInfo.EndHealthDef;
2482 end
2483 else
2484 begin // destroy defenders
2485 if MoveInfo.MoveType <> mtBombard then
2486 begin
2487 ZoCDefenderDestroyed := RW[MoveInfo.Defender].Model
2488 [RW[MoveInfo.Defender].Un[MoveInfo.Duix].mix].Flags and mdZOC <> 0;
2489 if ((RealMap[ToLoc] and fCity = 0) and
2490 (RealMap[ToLoc] and fTerImp <> tiBase) and
2491 (RealMap[ToLoc] and fTerImp <> tiFort)) or LoseCityPop and
2492 (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Size = 2) then
2493 RemoveAllUnits(MoveInfo.Defender, ToLoc, P)
2494 { no city, base or fortress }
2495 else
2496 RemoveUnit(MoveInfo.Defender, MoveInfo.Duix, P);
2497 end;
2498
2499 if LoseCityPop then // city defender defeated -- shrink city
2500 if not CityDestroyed then
2501 CityShrink(MoveInfo.Defender, MoveInfo.Dcix)
2502 else
2503 begin
2504 for uix1 := 0 to RW[MoveInfo.Defender].nUn - 1 do
2505 with RW[MoveInfo.Defender].Un[uix1] do
2506 if (Loc >= 0) and (Home = MoveInfo.Dcix) then
2507 begin
2508 UpdateLoc[nUpdateLoc] := Loc;
2509 Inc(nUpdateLoc);
2510 end;
2511 // unit will be removed -- remember position and update for all players
2512 DestroyCity_TellPlayers(MoveInfo.Defender, MoveInfo.Dcix, False);
2513 CheckBorders(ToLoc, MoveInfo.Defender);
2514 RecalcPeaceMap(P);
2515 end;
2516 end;
2517
2518 if CityDestroyed and (nUpdateLoc > 0) then
2519 RecalcMapZoC(P)
2520 else if ZoCDefenderDestroyed then
2521 RecalcV8ZoC(P, ToLoc);
2522 UpdateUnitMap(FromLoc);
2523 UpdateUnitMap(ToLoc, LoseCityPop);
2524 for I := 0 to nUpdateLoc - 1 do
2525 UpdateUnitMap(UpdateLoc[I]);
2526 // tell about lost units of defender
2527
2528 if Mode >= moMovie then
2529 begin
2530 for I := 0 to RW[P].nEnemyModel - 1 do
2531 with RW[P].EnemyModel[I] do
2532 Lost := Destroyed[P, Owner, mix];
2533 for p1 := 0 to nPl - 1 do { show after-attack in interface modules }
2534 if (1 shl p1 and GWatching <> 0) and ((p1 <> P) or (bix[p1].Kind = btTerm))
2535 then
2536 begin
2537 SeeFrom := ObserveLevel[FromLoc] shr (2 * p1) and
2538 3 >= lObserveUnhidden;
2539 SeeTo := ObserveLevel[ToLoc] shr (2 * p1) and 3 >= lObserveUnhidden;
2540 if SeeTo and CityDestroyed then
2541 CallPlayer(cShowCityChanged, p1, ToLoc); // city was destroyed
2542 if SeeFrom and SeeTo then
2543 begin
2544 CallPlayer(cShowAfterAttack, p1, ToLoc);
2545 CallPlayer(cShowAfterAttack, p1, FromLoc);
2546 end
2547 else
2548 begin
2549 if SeeTo then
2550 CallPlayer(cShowUnitChanged, p1, ToLoc);
2551 if SeeFrom then
2552 CallPlayer(cShowUnitChanged, p1, FromLoc);
2553 end;
2554 if SeeTo and (MoveInfo.MoveType = mtExpel) and (ExpelToLoc >= 0) then
2555 CallPlayer(cShowUnitChanged, p1, ExpelToLoc);
2556 end;
2557 end;
2558 end;
2559end;
2560
2561function MoveUnit(P, uix, dx, dy: Integer; TestOnly: Boolean): Integer;
2562var
2563 ToLoc: Integer;
2564 MoveInfo: TMoveInfo;
2565 ShowMove: TShowMove;
2566begin
2567{$IFOPT O-}Assert(1 shl P and InvalidTreatyMap = 0); {$ENDIF}
2568 with RW[P].Un[uix] do
2569 begin
2570 ToLoc := dLoc(Loc, dx, dy);
2571 if (ToLoc < 0) or (ToLoc >= MapSize) then
2572 begin
2573 Result := eInvalid;
2574 Exit;
2575 end;
2576 Result := CalculateMove(P, uix, ToLoc, 3 - dy and 1, TestOnly, MoveInfo);
2577 if Result = eZOC_EnemySpotted then
2578 ZOCTile := ToLoc;
2579 if (Result >= rExecuted) and not TestOnly then
2580 begin
2581 ShowMove.dx := dx;
2582 ShowMove.dy := dy;
2583 ShowMove.FromLoc := Loc;
2584 ShowMove.mix := mix;
2585 ShowMove.Health := Health;
2586 ShowMove.Fuel := Fuel;
2587 ShowMove.Exp := Exp;
2588 ShowMove.Load := TroopLoad + AirLoad;
2589 ShowMove.Owner := P;
2590 if (TroopLoad > 0) or (AirLoad > 0) then
2591 ShowMove.Flags := unMulti
2592 else
2593 ShowMove.Flags := 0;
2594 case MoveInfo.MoveType of
2595 mtCapture:
2596 ShowMove.Flags := ShowMove.Flags or umCapturing;
2597 mtSpyMission:
2598 ShowMove.Flags := ShowMove.Flags or umSpyMission;
2599 mtBombard:
2600 ShowMove.Flags := ShowMove.Flags or umBombarding;
2601 mtExpel:
2602 ShowMove.Flags := ShowMove.Flags or umExpelling;
2603 end;
2604 case MoveInfo.MoveType of
2605 mtMove, mtCapture, mtSpyMission:
2606 Result := ExecuteMove(P, uix, ToLoc, MoveInfo, ShowMove) or Result;
2607 mtAttack, mtBombard, mtExpel:
2608 Result := ExecuteAttack(P, uix, ToLoc, MoveInfo, ShowMove) or Result;
2609 end;
2610 end;
2611 end;
2612end;
2613
2614function Server(Command, Player, Subject: Integer; var Data): Integer; stdcall;
2615
2616 function CountPrice(const Offer: TOffer; PriceType: Integer): Integer;
2617 var
2618 I: Integer;
2619 begin
2620 Result := 0;
2621 for I := 0 to Offer.nDeliver + Offer.nCost - 1 do
2622 if Offer.Price[I] and $FFFF0000 = Cardinal(PriceType) then
2623 Inc(Result);
2624 end;
2625
2626{ procedure UpdateBorderHelper;
2627 var
2628 X, Y, Loc, Loc1, dx, dy, ObserveMask: Integer;
2629 begin
2630 ObserveMask:=3 shl (2*pTurn);
2631 for X:=0 to lx-1 do for Y:=0 to ly shr 1-1 do
2632 begin
2633 Loc:=lx*(Y*2)+X;
2634 if ObserveLevel[Loc] and ObserveMask<>0 then
2635 begin
2636 for dy:=0 to 1 do for dx:=0 to 1 do
2637 begin
2638 Loc1:=(Loc+dx-1+lx) mod lx +lx*((Y+dy)*2-1);
2639 if (Loc1>=0) and (Loc1<MapSize)
2640 and (ObserveLevel[Loc1] and ObserveMask<>0) then
2641 if RealMap[Loc1] and $78000000=RealMap[Loc] and $78000000 then
2642 begin
2643 RW[pTurn].BorderHelper[Loc]:=RW[pTurn].BorderHelper[Loc] and not (1 shl (dy*2+dx));
2644 RW[pTurn].BorderHelper[Loc1]:=RW[pTurn].BorderHelper[Loc1] and not (8 shr (dy*2+dx))
2645 end
2646 else
2647 begin
2648 RW[pTurn].BorderHelper[Loc]:=RW[pTurn].BorderHelper[Loc] or (1 shl (dy*2+dx));
2649 RW[pTurn].BorderHelper[Loc1]:=RW[pTurn].BorderHelper[Loc1] or (8 shr (dy*2+dx));
2650 end
2651 end
2652 end
2653 end
2654 end; }
2655
2656const
2657 ptSelect = 0;
2658 ptTrGoods = 1;
2659 ptUn = 2;
2660 ptCaravan = 3;
2661 ptImp = 4;
2662 ptWonder = 6;
2663 ptShip = 7;
2664 ptInvalid = 8;
2665
2666 function ProjectType(Project: Integer): Integer;
2667 begin
2668 if Project and cpCompleted <> 0 then
2669 Result := ptSelect
2670 else if Project and (cpImp + cpIndex) = cpImp + imTrGoods then
2671 Result := ptTrGoods
2672 else if Project and cpImp = 0 then
2673 if RW[Player].Model[Project and cpIndex].Kind = mkCaravan then
2674 Result := ptCaravan
2675 else
2676 Result := ptUn
2677 else if Project and cpIndex >= nImp then
2678 Result := ptInvalid
2679 else if Imp[Project and cpIndex].Kind = ikWonder then
2680 Result := ptWonder
2681 else if Imp[Project and cpIndex].Kind = ikShipPart then
2682 Result := ptShip
2683 else
2684 Result := ptImp;
2685 end;
2686
2687var
2688 D, I, J, p1, p2, pt0, pt1, uix1, cix1, Loc0, Loc1, dx, dy, NewCap, MinCap,
2689 MaxCap, CapWeight, Cost, NextProd, Preq, TotalFood, TotalProd, CheckSum,
2690 StopTurn, FutureMCost, NewProject, OldImp, mix, V8, V21, AStr, DStr,
2691 ABaseDamage, DBaseDamage: Integer;
2692 CityReport: TCityReport;
2693 FormerCLState: TCmdListState;
2694 Adjacent: TVicinity8Loc;
2695 Radius: TVicinity21Loc;
2696 ShowShipChange: TShowShipChange;
2697 ShowNegoData: TShowNegoData;
2698 Logged, Ok, HasShipChanged, AllHumansDead, OfferFullySupported: Boolean;
2699 Map: TMap;
2700begin
2701 if Command = sTurn then
2702 begin
2703 p2 := -1;
2704 for p1 := 0 to nPl - 1 do
2705 if (p1 <> Player) and (1 shl p1 and GWatching <> 0) then
2706 CallPlayer(cShowTurnChange, p1, p2);
2707 end;
2708
2709 Assert(MapSize = lx * ly);
2710 Assert(Command and (sctMask or sExecute) <> sctInternal or sExecute);
2711 // not for internal commands
2712 if (Command < 0) or (Command >= $10000) then
2713 begin
2714 Result := eUnknown;
2715 Exit;
2716 end;
2717
2718 if (Player < 0) or (Player >= nPl) or
2719 ((Command and (sctMask or sExecute) <> sctInfo) and
2720 ((Subject < 0) or (Subject >= $1000))) then
2721 begin
2722 Result := eInvalid;
2723 Exit;
2724 end;
2725
2726 if (1 shl Player and (GAlive or GWatching) = 0) and
2727 not ((Command = sTurn) or (Command = sBreak) or (Command = sResign) or
2728 (Command = sGetAIInfo) or (Command = sGetAICredits) or
2729 (Command = sGetVersion) or (Command and $FF0F = sGetChart)) then
2730 begin
2731 PutMessage(1 shl 16 + 1, Format('NOT Alive: %d', [Player]));
2732 Result := eNoTurn;
2733 Exit;
2734 end;
2735
2736 Result := eOK;
2737
2738 // check if command allowed now
2739 if (Mode = moPlaying) and not ((Command >= cClientEx) or (Command = sMessage)
2740 or (Command = sSetDebugMap) or (Command = sGetDebugMap) or
2741 (Command = sGetAIInfo) or (Command = sGetAICredits) or
2742 (Command = sGetVersion) or (Command = sGetTechCost) or
2743 (Command = sGetDefender) or (Command = sGetUnitReport) or
2744 (Command = sGetCityReport) or (Command = sGetCityTileInfo) or
2745 (Command = sGetCity) or (Command = sGetEnemyCityReport) or
2746 (Command = sGetEnemyCityAreaInfo) or (Command = sGetCityReportNew) or
2747 (Command and $FF0F = sGetChart) or (Command and $FF0F = sSetAttitude))
2748 // commands always allowed
2749 and not ((Player = pTurn) and (Command < $1000))
2750 // info request always allowed for pTurn
2751 and ((pDipActive < 0) and (Player <> pTurn) // not his turn
2752 or (pDipActive >= 0) and (Player <> pDipActive)
2753 // not active in negotiation mode
2754 or (pDipActive >= 0) and (Command and sctMask <> sctEndClient)) then
2755 // no nego command
2756 begin
2757 PutMessage(1 shl 16 + 1, Format('No Turn: %d calls %x',
2758 [Player, Command shr 4]));
2759 Result := eNoTurn;
2760 Exit;
2761 end;
2762
2763 // do not use EXIT hereafter!
2764
2765{$IFOPT O-}
2766 HandoverStack[nHandoverStack] := Player + $1000;
2767 HandoverStack[nHandoverStack + 1] := Command;
2768 Inc(nHandoverStack, 2);
2769
2770 InvalidTreatyMap := 0;
2771 // new command, sIntExpandTerritory of previous command was processed
2772{$ENDIF}
2773 if (Mode = moPlaying) and (Command >= sExecute) and
2774 ((Command and sctMask <> sctEndClient) or (Command = sTurn)) and
2775 (Command and sctMask <> sctModel) and (Command <> sCancelTreaty) and
2776 (Command <> sSetCityTiles) and (Command <> sBuyCityProject) and
2777 ((Command < cClientEx) or ProcessClientData[Player]) then
2778 begin { log command }
2779 FormerCLState := CL.State;
2780 CL.Put(Command, Player, Subject, @Data);
2781 Logged := True;
2782 end
2783 else
2784 Logged := False;
2785
2786 case Command of
2787
2788 {
2789 Info Request Commands
2790 ____________________________________________________________________
2791 }
2792 sMessage:
2793 bix[0].Client(cDebugMessage, Subject, Data);
2794
2795 sSetDebugMap:
2796 DebugMap[Player] := @Data;
2797
2798 sGetDebugMap:
2799 Pointer(Data) := DebugMap[Subject];
2800
2801 { sChangeSuperView:
2802 if Difficulty[Player]=0 then
2803 begin
2804 for I:=0 to nBrain-1 do if Brain[I].Initialized then
2805 CallClient(I, cShowSuperView, Subject)
2806 end
2807 else Result:=eInvalid; }
2808
2809 sRefreshDebugMap:
2810 bix[0].Client(cRefreshDebugMap, -1, Player);
2811
2812 sGetChart .. sGetChart + (nStat - 1) shl 4:
2813 if (Subject >= 0) and (Subject < nPl) and Assigned(bix[Subject]) then
2814 begin
2815 StopTurn := 0;
2816 if (Difficulty[Player] = 0) or (GTestFlags and tfUncover <> 0)
2817 // supervisor
2818 or (Subject = Player) // own chart
2819 or (GWinner > 0) // game end chart
2820 or (1 shl Subject and GAlive = 0) then // chart of extinct nation
2821 if Subject > Player then
2822 StopTurn := GTurn
2823 else
2824 StopTurn := GTurn + 1
2825 else if RW[Player].Treaty[Subject] > trNoContact then
2826 if Command shr 4 and $F = stMil then
2827 StopTurn := RW[Player].EnemyReport[Subject].TurnOfMilReport + 1
2828 else
2829 StopTurn := RW[Player].EnemyReport[Subject].TurnOfCivilReport + 1;
2830 Move(Stat[Command shr 4 and $F, Subject]^, Data,
2831 StopTurn * SizeOf(Integer));
2832 FillChar(TChart(Data)[StopTurn], (GTurn - StopTurn) *
2833 SizeOf(Integer), 0);
2834 end
2835 else
2836 Result := eInvalid;
2837
2838 sGetTechCost:
2839 Integer(Data) := TechCost(Player);
2840
2841 sGetAIInfo:
2842 if AIInfo[Subject] = '' then
2843 PChar(Data) := nil
2844 else
2845 PChar(Data) := @AIInfo[Subject][1];
2846
2847 sGetAICredits:
2848 if AICredits = '' then
2849 PChar(Data) := nil
2850 else
2851 PChar(Data) := @AICredits[1];
2852
2853 sGetVersion:
2854 Integer(Data) := CevoVersion;
2855
2856 sGetGameChanged:
2857 if Player <> 0 then
2858 Result := eInvalid
2859 else if (CL <> nil) and (CL.State.nLog = nLogOpened) and
2860 (CL.State.MoveCode = 0) and not HasCityTileChanges and
2861 not HasChanges(Player) then
2862 Result := eNotChanged;
2863
2864 sGetTileInfo:
2865 if (Subject >= 0) and (Subject < MapSize) then
2866 Result := GetTileInfo(Player, -2, Subject, TTileInfo(Data))
2867 else
2868 Result := eInvalid;
2869
2870 sGetCityTileInfo:
2871 if (Subject >= 0) and (Subject < MapSize) then
2872 Result := GetTileInfo(Player, -1, Subject, TTileInfo(Data))
2873 else
2874 Result := eInvalid;
2875
2876 sGetHypoCityTileInfo:
2877 if (Subject >= 0) and (Subject < MapSize) then
2878 begin
2879 if (TTileInfo(Data).ExplCity < 0) or
2880 (TTileInfo(Data).ExplCity >= RW[Player].nCity) then
2881 Result := eInvalid
2882 else if ObserveLevel[Subject] shr (Player * 2) and 3 = 0 then
2883 Result := eNoPreq
2884 else
2885 Result := GetTileInfo(Player, TTileInfo(Data).ExplCity, Subject,
2886 TTileInfo(Data));
2887 end
2888 else
2889 Result := eInvalid;
2890
2891 sGetJobProgress:
2892 if (Subject >= 0) and (Subject < MapSize) then
2893 begin
2894 if ObserveLevel[Subject] shr (Player * 2) and 3 = 0 then
2895 Result := eNoPreq
2896 else
2897 Result := GetJobProgress(Player, Subject, TJobProgressData(Data));
2898 end
2899 else
2900 Result := eInvalid;
2901
2902 sGetModels:
2903 if (GTestFlags and tfUncover <> 0) or (Difficulty[Player] = 0)
2904 then { supervisor only command }
2905 begin
2906 for p1 := 0 to nPl - 1 do
2907 if (p1 <> Player) and (1 shl p1 and GAlive <> 0) then
2908 for mix := 0 to RW[p1].nModel - 1 do
2909 TellAboutModel(Player, p1, mix);
2910 end
2911 else
2912 Result := eInvalid;
2913
2914 sGetUnits:
2915 if (Subject >= 0) and (Subject < MapSize) and
2916 (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) then
2917 Integer(Data) := GetUnitStack(Player, Subject)
2918 else
2919 Result := eNoPreq;
2920
2921 sGetDefender:
2922 if (Subject >= 0) and (Subject < MapSize) and (Occupant[Subject] = Player)
2923 then
2924 Strongest(Subject, Integer(Data), D, I, J)
2925 else
2926 Result := eInvalid;
2927
2928 sGetBattleForecast, sGetBattleForecastEx:
2929 if (Subject >= 0) and (Subject < MapSize) and
2930 (ObserveLevel[Subject] and (3 shl (Player * 2)) > 0) then
2931 with TBattleForecast(Data) do
2932 if (1 shl pAtt and GAlive <> 0) and (mixAtt >= 0) and
2933 (mixAtt < RW[pAtt].nModel) and
2934 ((pAtt = Player) or (RWemix[Player, pAtt, mixAtt] >= 0)) then
2935 begin
2936 Result := GetBattleForecast(Subject, TBattleForecast(Data), uix1,
2937 cix1, AStr, DStr, ABaseDamage, DBaseDamage);
2938 if Command = sGetBattleForecastEx then
2939 begin
2940 TBattleForecastEx(Data).AStr := (AStr + 200) div 400;
2941 TBattleForecastEx(Data).DStr := (DStr + 200) div 400;
2942 TBattleForecastEx(Data).ABaseDamage := ABaseDamage;
2943 TBattleForecastEx(Data).DBaseDamage := DBaseDamage;
2944 end;
2945 if Result = eOK then
2946 Result := eInvalid; // no enemy unit there!
2947 end
2948 else
2949 Result := eInvalid
2950 else
2951 Result := eInvalid;
2952
2953 sGetUnitReport:
2954 if (Subject < 0) or (Subject >= RW[Player].nUn) or
2955 (RW[Player].Un[Subject].Loc < 0) then
2956 Result := eInvalid
2957 else
2958 GetUnitReport(Player, Subject, TUnitReport(Data));
2959
2960 sGetMoveAdvice:
2961 if (Subject < 0) or (Subject >= RW[Player].nUn) or
2962 (RW[Player].Un[Subject].Loc < 0) then
2963 Result := eInvalid
2964 else
2965 Result := GetMoveAdvice(Player, Subject, TMoveAdviceData(Data));
2966
2967 sGetPlaneReturn:
2968 if (Subject < 0) or (Subject >= RW[Player].nUn) or
2969 (RW[Player].Un[Subject].Loc < 0) or
2970 (RW[Player].Model[RW[Player].Un[Subject].mix].Domain <> dAir) then
2971 Result := eInvalid
2972 else
2973 begin
2974 if CanPlaneReturn(Player, Subject, TPlaneReturnData(Data)) then
2975 Result := eOK
2976 else
2977 Result := eNoWay;
2978 end;
2979
2980 sGetCity:
2981 if (Subject >= 0) and (Subject < MapSize) and
2982 (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
2983 (RealMap[Subject] and fCity <> 0) then
2984 with TGetCityData(Data) do
2985 begin
2986 Owner := Player;
2987 SearchCity(Subject, Owner, cix1);
2988 C := RW[Owner].City[cix1];
2989 if (Owner <> Player) and (C.Project and cpImp = 0) then
2990 TellAboutModel(Player, Owner, C.Project and cpIndex);
2991 end
2992 else
2993 Result := eInvalid;
2994
2995 sGetCityReport:
2996 if (Subject < 0) or (Subject >= RW[Player].nCity) or
2997 (RW[Player].City[Subject].Loc < 0) then
2998 Result := eInvalid
2999 else
3000 Result := GetCityReport(Player, Subject, TCityReport(Data));
3001
3002 sGetCityReportNew:
3003 if (Subject < 0) or (Subject >= RW[Player].nCity) or
3004 (RW[Player].City[Subject].Loc < 0) then
3005 Result := eInvalid
3006 else
3007 GetCityReportNew(Player, Subject, TCityReportNew(Data));
3008
3009 sGetCityAreaInfo:
3010 if (Subject < 0) or (Subject >= RW[Player].nCity) or
3011 (RW[Player].City[Subject].Loc < 0) then
3012 Result := eInvalid
3013 else
3014 GetCityAreaInfo(Player, RW[Player].City[Subject].Loc,
3015 TCityAreaInfo(Data));
3016
3017 sGetEnemyCityReport:
3018 if (Subject >= 0) and (Subject < MapSize) and
3019 (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
3020 (RealMap[Subject] and fCity <> 0) then
3021 begin
3022 p1 := Occupant[Subject];
3023 if p1 < 0 then
3024 p1 := 1;
3025 SearchCity(Subject, p1, cix1);
3026 TCityReport(Data).HypoTiles := -1;
3027 TCityReport(Data).HypoTax := -1;
3028 TCityReport(Data).HypoLux := -1;
3029 GetCityReport(p1, cix1, TCityReport(Data));
3030 end
3031 else
3032 Result := eInvalid;
3033
3034 sGetEnemyCityReportNew:
3035 if (Subject >= 0) and (Subject < MapSize) and
3036 (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
3037 (RealMap[Subject] and fCity <> 0) then
3038 begin
3039 p1 := Occupant[Subject];
3040 if p1 < 0 then
3041 p1 := 1;
3042 SearchCity(Subject, p1, cix1);
3043 TCityReport(Data).HypoTiles := -1;
3044 TCityReport(Data).HypoTax := -1;
3045 TCityReport(Data).HypoLux := -1;
3046 GetCityReportNew(p1, cix1, TCityReportNew(Data));
3047 end
3048 else
3049 Result := eInvalid;
3050
3051 sGetEnemyCityAreaInfo:
3052 if (Subject >= 0) and (Subject < MapSize) and
3053 (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
3054 (RealMap[Subject] and fCity <> 0) then
3055 begin
3056 p1 := Occupant[Subject];
3057 if p1 < 0 then
3058 p1 := 1;
3059 SearchCity(Subject, p1, cix1);
3060 GetCityAreaInfo(p1, Subject, TCityAreaInfo(Data));
3061 end
3062 else
3063 Result := eInvalid;
3064
3065 sGetCityTileAdvice:
3066 if (Subject < 0) or (Subject >= RW[Player].nCity) or
3067 (RW[Player].City[Subject].Loc < 0) then
3068 Result := eInvalid
3069 else
3070 GetCityTileAdvice(Player, Subject, TCityTileAdviceData(Data));
3071
3072 {
3073 Map Editor Commands
3074 ____________________________________________________________________
3075 }
3076 sEditTile:
3077 if Player = 0 then
3078 with TEditTileData(Data) do
3079 EditTile(Loc, NewTile)
3080 else
3081 Result := eInvalid;
3082
3083 sRandomMap:
3084 if (Player = 0) and MapGeneratorAvailable then begin
3085 CreateElevation;
3086 PreviewElevation := False;
3087 CreateMap(False);
3088 FillChar(ObserveLevel, MapSize * 4, 0);
3089 DiscoverAll(Player, lObserveSuper);
3090 end else Result := eInvalid;
3091
3092 sFillMap:
3093 if Player = 0 then begin
3094 FillMap(TFillMapData(Data).Tile, True);
3095 FillChar(ObserveLevel, MapSize * 4, 0);
3096 DiscoverAll(Player, lObserveSuper);
3097 end else Result := eInvalid;
3098
3099 sMapGeneratorRequest:
3100 if not MapGeneratorAvailable then
3101 Result := eInvalid;
3102
3103 {
3104 Client Deactivation Commands
3105 ____________________________________________________________________
3106 }
3107 sTurn, sTurn - sExecute:
3108 begin
3109 AllHumansDead := True;
3110 for p1 := 0 to nPl - 1 do
3111 if (1 shl p1 and GAlive <> 0) and (bix[p1].Kind = btTerm) then begin
3112 AllHumansDead := False;
3113 Break;
3114 end;
3115 if (pDipActive >= 0) // still in negotiation mode
3116 or (pTurn = 0) and ((GWinner > 0) or (GTurn = MaxTurn) or
3117 (Difficulty[0] > 0) and AllHumansDead) then // game end reached
3118 Result := eViolation
3119 else if Command >= sExecute then
3120 begin
3121 if Mode = moPlaying then
3122 begin
3123 CL.State := FormerCLState;
3124 LogCityTileChanges;
3125{$IFNDEF SCR}
3126 if pTurn = 0 then
3127 begin
3128 LogChanges;
3129 SaveGame(ToAutoSaveFileName(LogFileName), True);
3130 end;
3131{$ENDIF}
3132 end
3133 else if (Mode = moMovie) and (pTurn = 0) then
3134 CallPlayer(cMovieEndTurn, 0, nil^);
3135 GWatching := GWatching and GAlive or 1;
3136 RW[pTurn].Happened := 0;
3137 uixSelectedTransport := -1;
3138 SpyMission := smSabotageProd;
3139 if 1 shl pTurn and GAlive <> 0 then
3140 begin
3141 // calculate checksum
3142 TotalFood := 0;
3143 TotalProd := 0;
3144 for I := 0 to RW[pTurn].nCity - 1 do
3145 if RW[pTurn].City[I].Loc >= 0 then
3146 begin
3147 Inc(TotalFood, RW[pTurn].City[I].Food);
3148 Inc(TotalProd, RW[pTurn].City[I].Prod);
3149 end;
3150 CheckSum := TotalFood and 7 + TotalProd and 7 shl 3 +
3151 RW[pTurn].Money and 7 shl 6 + Worked[pTurn] div 100 and 7 shl 9;
3152 end
3153 else
3154 CheckSum := 0;
3155
3156 if Mode < moPlaying then // check checksum
3157 begin
3158 if CheckSum <> Subject then
3159 LoadOK := False;
3160 end
3161 else // save checksum
3162 CL.Put(Command, Player, CheckSum, @Data);
3163{$IFDEF TEXTLOG}
3164 CmdInfo := '';
3165 if CheckSum and 7 <> Subject and 7 then
3166 CmdInfo := Format('***ERROR (Food %d) ',
3167 [(CheckSum and 7 - Subject and 7 + 12) mod 8 - 4]) + CmdInfo;
3168 if CheckSum shr 3 and 7 <> Subject shr 3 and 7 then
3169 CmdInfo := '***ERROR (Prod) ' + CmdInfo;
3170 if CheckSum shr 6 and 7 <> Subject shr 6 and 7 then
3171 CmdInfo := '***ERROR (Research) ' + CmdInfo;
3172 if CheckSum shr 9 and 7 <> Subject shr 9 and 7 then
3173 CmdInfo := '***ERROR (Work) ' + CmdInfo;
3174{$ENDIF}
3175 if 1 shl pTurn and GAlive <> 0 then
3176 begin
3177 AfterTurn;
3178 if Mode < moPlaying then
3179 InsertTerritoryUpdateCommands;
3180 // if bix[pTurn]=bixTerm then UpdateBorderHelper;
3181 end;
3182
3183 repeat
3184 pTurn := (pTurn + 1) mod nPl;
3185 if pTurn = 0 then
3186 Inc(GTurn);
3187 if Assigned(bix[pTurn]) and ((1 shl pTurn) and GAlive = 0) then
3188 begin // already made extinct -- continue statistics
3189 Stat[stExplore, pTurn, GTurn] := 0;
3190 Stat[stPop, pTurn, GTurn] := 0;
3191 Stat[stTerritory, pTurn, GTurn] := 0;
3192 Stat[stScience, pTurn, GTurn] := 0;
3193 Stat[stWork, pTurn, GTurn] := 0;
3194 Stat[stMil, pTurn, GTurn] := 0;
3195 end;
3196 until (pTurn = 0) or ((1 shl pTurn and (GAlive or GWatching) <> 0) and
3197 (GWinner = 0));
3198 if (Mode = moLoading_Fast) and
3199 ((GTurn = LoadTurn) or (GTurn = LoadTurn - 1) and (pTurn > 0)) then
3200 Mode := moLoading;
3201 if Mode = moPlaying then
3202 begin
3203 CCCommand := cTurn;
3204 CCPlayer := pTurn;
3205 Notify(ntNextPlayer);
3206 end
3207 else
3208 begin
3209 if GTurn = 0 then
3210 BeforeTurn0
3211 else
3212 BeforeTurn;
3213 if (Mode = moMovie) and (pTurn = 0) then
3214 begin
3215 Inform(pTurn);
3216 CallPlayer(cMovieTurn, 0, nil^);
3217 end;
3218 end;
3219{$IFDEF TEXTLOG}CmdInfo := CmdInfo + Format('---Turn %d P%d---', [GTurn, pTurn]); {$ENDIF}
3220 end;
3221 end; // sTurn
3222
3223 sBreak, sResign, sNextRound, sReload:
3224 if Mode = moMovie then
3225 MovieStopped := True
3226 else
3227 begin
3228 if Command = sReload then
3229 begin
3230 Ok := (Difficulty[0] = 0) and (bix[0].Kind <> btNoTerm) and
3231 (Integer(Data) >= 0) and (Integer(Data) < GTurn);
3232 for p1 := 1 to nPl - 1 do
3233 if bix[p1].Kind = btTerm then begin
3234 Ok := False;
3235 Break;
3236 end;
3237 // allow reload in AI-only games only
3238 end
3239 else
3240 Ok := Player = 0;
3241 if Ok then
3242 begin
3243 if (Command = sBreak) or (Command = sResign) then
3244 Notify(ntBackOn);
3245 for I := 0 to Brains.Count - 1 do
3246 if Brains[I].Initialized then
3247 begin
3248 if Brains[I].Kind = btAI then
3249 Notify(ntDeinitModule, I);
3250 CallClient(I, cBreakGame, nil^);
3251 end;
3252 Notify(ntEndInfo);
3253 if (Command = sBreak) or (Command = sReload) then
3254 begin
3255 LogCityTileChanges;
3256 LogChanges;
3257 SaveGame(LogFileName, False);
3258 end;
3259 DeleteFile(ToAutoSaveFileName(LogFileName));
3260 EndGame;
3261 case Command of
3262 sBreak:
3263 Notify(ntStartGoRefresh);
3264 sResign:
3265 Notify(ntStartGo);
3266 sNextRound:
3267 StartNewGame(LogFileName, MapFileName, lx, ly, LandMass, MaxTurn);
3268 sReload:
3269 LoadGame(LogFileName, Integer(Data), False);
3270 end;
3271 end
3272 else
3273 Result := eInvalid;
3274 end;
3275
3276 sAbandonMap, sSaveMap:
3277 if Player = 0 then
3278 begin
3279 if Command = sSaveMap then begin
3280 Map := TMap.Create;
3281 Map.Size := Point(lx, ly);
3282 Map.MaxTurn := MaxTurn;
3283 Move(RealMap, Map.Tiles[0], MapSize * 4);
3284 Map.SaveToFile(MapFileName);
3285 FreeAndNil(Map);
3286 end;
3287 Notify(ntBackOn);
3288 BrainTerm.Client(cBreakGame, -1, nil^);
3289 ReleaseMapEditor;
3290 if Command = sSaveMap then
3291 Notify(ntStartGoRefreshMaps)
3292 else
3293 Notify(ntStartGo);
3294 end
3295 else
3296 Result := eInvalid;
3297
3298 scContact .. scContact + (nPl - 1) shl 4, scContact - sExecute .. scContact
3299 - sExecute + (nPl - 1) shl 4:
3300 if (pDipActive >= 0) or (1 shl (Command shr 4 and $F) and GAlive = 0) then
3301 Result := eInvalid
3302 else if GWinner > 0 then
3303 Result := eViolation // game end reached
3304 else if RW[Player].Treaty[Command shr 4 and $F] = trNoContact then
3305 Result := eNoPreq
3306 else if GTurn < GColdWarStart + ColdWarTurns then
3307 Result := eColdWar
3308 else if RW[Player].Government = gAnarchy then
3309 Result := eAnarchy
3310 else if RW[Command shr 4 and $F].Government = gAnarchy then
3311 begin
3312 Result := eAnarchy;
3313 LastEndClientCommand := scReject; // enable cancel treaty
3314 pContacted := Command shr 4 and $F;
3315 end
3316 else if Command >= sExecute then
3317 begin // contact request
3318 pContacted := Command shr 4 and $F;
3319 pDipActive := pContacted;
3320 Assert(Mode = moPlaying);
3321 Inform(pDipActive);
3322 ChangeClientWhenDone(scContact, pDipActive, pTurn, 4);
3323 end;
3324
3325 scReject, scReject - sExecute:
3326 if LastEndClientCommand and $FF0F = scContact then
3327 begin
3328 if Command >= sExecute then
3329 begin // contact requested and not accepted yet
3330 pDipActive := -1;
3331 Assert(Mode = moPlaying);
3332 ChangeClientWhenDone(cContinue, pTurn, nil^, 0);
3333 end;
3334 end
3335 else
3336 Result := eInvalid;
3337
3338 scDipStart, scDipStart - sExecute:
3339 if LastEndClientCommand and $FF0F = scContact then
3340 begin
3341 if Command >= sExecute then
3342 begin // accept contact
3343 pContacted := pDipActive;
3344 RW[pContacted].EnemyReport[pTurn].Credibility :=
3345 RW[pTurn].Credibility;
3346 pDipActive := pTurn;
3347 Assert(Mode = moPlaying);
3348 IntServer(sIntHaveContact, pTurn, pContacted, nil^);
3349 ChangeClientWhenDone(scDipStart, pDipActive, nil^, 0);
3350 end;
3351 end
3352 else
3353 Result := eInvalid;
3354
3355 scDipNotice, scDipAccept, scDipCancelTreaty, scDipBreak,
3356 scDipNotice - sExecute, scDipAccept - sExecute,
3357 scDipCancelTreaty - sExecute, scDipBreak - sExecute:
3358 if pDipActive >= 0 then
3359 begin
3360 Assert(Mode = moPlaying);
3361 if pDipActive = pTurn then
3362 p1 := pContacted
3363 else
3364 p1 := pTurn;
3365 if (Command and not sExecute = scDipBreak and not sExecute) and
3366 (LastEndClientCommand <> scDipBreak) then // Ok
3367 else if (Command and not sExecute = scDipNotice and not sExecute) and
3368 ((LastEndClientCommand = scDipCancelTreaty) or
3369 (LastEndClientCommand = scDipBreak)) then // Ok
3370 else if (Command and not sExecute = scDipAccept and not sExecute) and
3371 (LastEndClientCommand = scDipOffer) then
3372 with LastOffer do
3373 begin
3374 // check if offer can be accepted
3375 if nDeliver + nCost = 0 then
3376 Result := eOfferNotAcceptable;
3377 for I := 0 to nDeliver + nCost - 1 do
3378 if Price[I] = opChoose then begin
3379 Result := eOfferNotAcceptable;
3380 Break;
3381 end;
3382 for I := 0 to nCost - 1 do
3383 if not PayPrice(pDipActive, p1, Price[nDeliver + I], False) then
3384 Result := eOfferNotAcceptable;
3385 if (Command >= sExecute) and (Result >= rExecuted) then
3386 begin
3387 IntServer(sIntPayPrices + nDeliver + nCost, p1, pDipActive,
3388 LastOffer);
3389 // CheckContact;
3390
3391 // tell other players about ship part trades
3392 HasShipChanged := False;
3393 FillChar(ShowShipChange, SizeOf(ShowShipChange), 0);
3394 for I := 0 to nDeliver + nCost - 1 do
3395 if Price[I] and opMask = opShipParts then
3396 begin
3397 HasShipChanged := True;
3398 if I >= nDeliver then
3399 begin // p1 has demanded from pDipActive
3400 ShowShipChange.Ship1Change[Price[I] shr 16 and 3] :=
3401 +Integer(Price[I] and $FFFF);
3402 ShowShipChange.Ship2Change[Price[I] shr 16 and 3] :=
3403 -Integer(Price[I] and $FFFF);
3404 end
3405 else
3406 begin // p1 has delivered to pDipActive
3407 ShowShipChange.Ship1Change[Price[I] shr 16 and 3] :=
3408 -Integer(Price[I] and $FFFF);
3409 ShowShipChange.Ship2Change[Price[I] shr 16 and 3] :=
3410 +Integer(Price[I] and $FFFF);
3411 end;
3412 end;
3413 if HasShipChanged then
3414 begin
3415 ShowShipChange.Reason := scrTrade;
3416 ShowShipChange.Ship1Owner := p1;
3417 ShowShipChange.Ship2Owner := pDipActive;
3418 for p2 := 0 to nPl - 1 do
3419 if (p2 <> p1) and (p2 <> pDipActive) and
3420 (1 shl p2 and (GAlive or GWatching) <> 0) then
3421 begin
3422 Move(GShip, RW[p2].Ship, SizeOf(GShip));
3423 if 1 shl p2 and GWatching <> 0 then
3424 CallPlayer(cShowShipChange, p2, ShowShipChange);
3425 end;
3426 end;
3427 end;
3428 end
3429 else if (Command and not sExecute = scDipCancelTreaty and not sExecute)
3430 and (RW[pDipActive].Treaty[p1] >= trPeace) then
3431 begin
3432 if (ServerVersion[pDipActive] >= $010100) and
3433 (GTurn < RW[pDipActive].LastCancelTreaty[p1] + CancelTreatyTurns)
3434 then
3435 Result := eCancelTreatyRush
3436 else if Command >= sExecute then
3437 begin
3438 IntServer(sIntCancelTreaty, pDipActive, p1, nil^);
3439 for p2 := 0 to nPl - 1 do
3440 if (p2 <> p1) and (1 shl p2 and PeaceEnded <> 0) then
3441 begin
3442 I := p1 shl 4 + pDipActive;
3443 CallPlayer(cShowSupportAllianceAgainst, p2, I);
3444 end;
3445 for p2 := 0 to nPl - 1 do
3446 if (p2 <> p1) and (1 shl p2 and PeaceEnded <> 0) then
3447 begin
3448 I := p2;
3449 CallPlayer(cShowCancelTreatyByAlliance, pDipActive, I);
3450 end;
3451 end;
3452 end
3453 else
3454 Result := eInvalid;
3455 if (Command >= sExecute) and (Result >= rExecuted) then
3456 if LastEndClientCommand = scDipBreak then
3457 begin // break negotiation
3458 pDipActive := -1;
3459 CallPlayer(cShowEndContact, pContacted, nil^);
3460 ChangeClientWhenDone(cContinue, pTurn, nil^, 0);
3461 end
3462 else
3463 begin
3464 if (GTestFlags and tfUncover <> 0) or (Difficulty[0] = 0) then
3465 with ShowNegoData do
3466 begin // display negotiation in log window
3467 pSender := pDipActive;
3468 pTarget := p1;
3469 Action := Command;
3470 bix[0].Client(cShowNego, 1 shl 16 + 3, ShowNegoData);
3471 end;
3472 pDipActive := p1;
3473 ChangeClientWhenDone(Command, pDipActive, nil^, 0);
3474 end;
3475 end
3476 else
3477 Result := eInvalid;
3478
3479 scDipOffer, scDipOffer - sExecute:
3480 if (pDipActive >= 0) and (LastEndClientCommand <> scDipCancelTreaty) and
3481 (LastEndClientCommand <> scDipBreak) then
3482 if (LastEndClientCommand = scDipOffer) and
3483 (LastOffer.nDeliver + LastOffer.nCost + TOffer(Data).nDeliver +
3484 TOffer(Data).nCost = 0) then
3485 begin
3486 if Command >= sExecute then
3487 begin // agreed discussion end
3488 pDipActive := -1;
3489 CallPlayer(cShowEndContact, pContacted, nil^);
3490 Assert(Mode = moPlaying);
3491 ChangeClientWhenDone(cContinue, pTurn, nil^, 0);
3492 end;
3493 end
3494 else
3495 begin
3496 // check if offer can be made
3497 if pDipActive = pTurn then
3498 p1 := pContacted
3499 else
3500 p1 := pTurn;
3501 if RW[pDipActive].Treaty[p1] < trPeace then
3502 begin // no tribute allowed!
3503 for I := 0 to TOffer(Data).nDeliver + TOffer(Data).nCost - 1 do
3504 if (TOffer(Data).Price[I] and opMask = opTribute) then begin
3505 Result := eInvalidOffer;
3506 Break;
3507 end;
3508 for I := 0 to TOffer(Data).nDeliver + TOffer(Data).nCost - 1 do
3509 if (TOffer(Data).Price[I] = opTreaty + trPeace) then begin
3510 Result := eOK;
3511 Break;
3512 end;
3513 end;
3514 for I := 0 to TOffer(Data).nDeliver - 1 do
3515 if (TOffer(Data).Price[I] <> opChoose) and
3516 not PayPrice(pDipActive, p1, TOffer(Data).Price[I], False) then
3517 Result := eInvalidOffer;
3518 if CountPrice(TOffer(Data), opTreaty) > 1 then
3519 Result := eInvalidOffer;
3520 for I := 0 to nShipPart - 1 do
3521 if CountPrice(TOffer(Data), opShipParts + I shl 16) > 1 then begin
3522 Result := eInvalidOffer;
3523 Break;
3524 end;
3525 if CountPrice(TOffer(Data), opMoney) > 1 then
3526 Result := eInvalidOffer;
3527 if CountPrice(TOffer(Data), opTribute) > 1 then
3528 Result := eInvalidOffer;
3529 case CountPrice(TOffer(Data), opChoose) of
3530 0:
3531 ;
3532 1:
3533 if (TOffer(Data).nCost = 0) or (TOffer(Data).nDeliver = 0) then
3534 Result := eInvalidOffer;
3535 else
3536 Result := eInvalidOffer;
3537 end;
3538
3539 // !!! check here if cost can be demanded
3540
3541 if (Command >= sExecute) and (Result >= rExecuted) then
3542 begin
3543 OfferFullySupported := (TOffer(Data).nDeliver <= 2) and
3544 (TOffer(Data).nCost <= 2); // >2 no more allowed
3545 for I := 0 to TOffer(Data).nDeliver + TOffer(Data).nCost - 1 do
3546 begin
3547 if TOffer(Data).Price[I] and opMask = opTribute then
3548 OfferFullySupported := False;
3549 // tribute no more part of the game
3550 if (TOffer(Data).Price[I] and opMask = opTreaty) and
3551 (TOffer(Data).Price[I] - opTreaty <= RW[pDipActive].Treaty[p1])
3552 then
3553 OfferFullySupported := False;
3554 // agreed treaty end no more part of the game
3555 if TOffer(Data).Price[I] = opTreaty + trCeaseFire then
3556 OfferFullySupported := False;
3557 // ceasefire no more part of the game
3558 end;
3559 if not OfferFullySupported then
3560 begin
3561 // some elements have been removed from the game -
3562 // automatically respond will null-offer
3563 LastOffer.nDeliver := 0;
3564 LastOffer.nCost := 0;
3565 ChangeClientWhenDone(scDipOffer, pDipActive, LastOffer,
3566 SizeOf(LastOffer));
3567 end
3568 else
3569 begin
3570 if (GTestFlags and tfUncover <> 0) or (Difficulty[0] = 0) then
3571 with ShowNegoData do
3572 begin // display negotiation in log window
3573 pSender := pDipActive;
3574 pTarget := p1;
3575 Action := Command;
3576 Offer := TOffer(Data);
3577 bix[0].Client(cShowNego, 1 shl 16 + 3, ShowNegoData);
3578 end;
3579 LastOffer := TOffer(Data);
3580 // show offered things to receiver
3581 for I := 0 to LastOffer.nDeliver - 1 do
3582 ShowPrice(pDipActive, p1, LastOffer.Price[I]);
3583 pDipActive := p1;
3584 Assert(Mode = moPlaying);
3585 ChangeClientWhenDone(scDipOffer, pDipActive, LastOffer,
3586 SizeOf(LastOffer));
3587 end;
3588 end;
3589 end
3590 else
3591 Result := eInvalid;
3592
3593 {
3594 General Commands
3595 ____________________________________________________________________
3596 }
3597 sClearTestFlag:
3598 if Player = 0 then
3599 begin
3600{$IFDEF TEXTLOG}CmdInfo := Format('ClearTestFlag %x', [Subject]); {$ENDIF}
3601 ClearTestFlags(Subject);
3602 end
3603 else
3604 Result := eInvalid;
3605
3606 sSetTestFlag:
3607 if Player = 0 then
3608 begin
3609{$IFDEF TEXTLOG}CmdInfo := Format('SetTestFlag %x', [Subject]); {$ENDIF}
3610 SetTestFlags(Player, Subject);
3611 // CheckContact;
3612 end
3613 else
3614 Result := eInvalid;
3615
3616 sSetGovernment, sSetGovernment - sExecute:
3617 begin
3618{$IFDEF TEXTLOG}CmdInfo := Format('SetGovernment P%d: %d', [Player, Subject]); {$ENDIF}
3619 if RW[Player].Happened and phChangeGov = 0 then
3620 Result := eViolation
3621 else if RW[Player].Government = Subject then
3622 Result := eNotChanged
3623 else if (Subject >= nGov) then
3624 Result := eInvalid
3625 else if (Subject >= gMonarchy) and
3626 (RW[Player].Tech[GovPreq[Subject]] < tsApplicable) then
3627 Result := eNoPreq
3628 else if Command >= sExecute then
3629 begin
3630 RW[Player].Government := Subject;
3631 for p1 := 0 to nPl - 1 do
3632 if (p1 <> Player) and ((GAlive or GWatching) and (1 shl p1) <> 0)
3633 then
3634 RW[p1].EnemyReport[Player].Government := Subject;
3635 end;
3636 end;
3637
3638 sSetRates, sSetRates - sExecute:
3639 begin
3640{$IFDEF TEXTLOG}CmdInfo := Format('SetRates P%d: %d/%d', [Player, Subject and $F * 10, Subject shr 4 * 10]); {$ENDIF}
3641 if Subject and $F + Subject shr 4 > 10 then
3642 Result := eInvalid
3643 else if (RW[Player].TaxRate = Subject and $F * 10) and
3644 (RW[Player].LuxRate = Subject shr 4 * 10) then
3645 Result := eNotChanged
3646 else if Command >= sExecute then
3647 begin
3648 RW[Player].TaxRate := Subject and $F * 10;
3649 RW[Player].LuxRate := Subject shr 4 * 10;
3650 end;
3651 end;
3652
3653 sRevolution:
3654 begin
3655{$IFDEF TEXTLOG}CmdInfo := Format('Revolution P%d', [Player]); {$ENDIF}
3656 if RW[Player].Government = gAnarchy then
3657 Result := eInvalid
3658 else
3659 begin
3660 RW[Player].Government := gAnarchy;
3661 for p1 := 0 to nPl - 1 do
3662 if (p1 <> Player) and ((GAlive or GWatching) and (1 shl p1) <> 0)
3663 then
3664 RW[p1].EnemyReport[Player].Government := gAnarchy;
3665 RW[Player].AnarchyStart := GTurn;
3666 end;
3667 end;
3668
3669 sSetResearch, sSetResearch - sExecute:
3670 with RW[Player] do
3671 begin
3672{$IFDEF TEXTLOG}CmdInfo := Format('SetResearch P%d: %d', [Player, Subject]);
3673 {$ENDIF}
3674 if (Happened and phTech <> 0) and
3675 ((Subject < nAdv) or (Subject = adMilitary)) then
3676 begin
3677 if (Mode = moPlaying) and (Subject = adMilitary) and
3678 (DevModelTurn[Player] <> GTurn) then
3679 Result := eNoModel
3680 else if Subject <> adMilitary then
3681 begin
3682 if Subject = futComputingTechnology then
3683 begin
3684 if Tech[Subject] >= MaxFutureTech_Computing then
3685 Result := eInvalid;
3686 end
3687 else if Subject in FutureTech then
3688 begin
3689 if Tech[Subject] >= MaxFutureTech then
3690 Result := eInvalid;
3691 end
3692 else if Tech[Subject] >= tsApplicable then
3693 Result := eInvalid; // already discovered
3694 if Tech[Subject] <> tsSeen then // look if preqs met
3695 if AdvPreq[Subject, 2] <> preNone then
3696 begin // 2 of 3 required
3697 I := 0;
3698 for J := 0 to 2 do
3699 if Tech[AdvPreq[Subject, J]] >= tsApplicable then
3700 Inc(I);
3701 if I < 2 then
3702 Result := eNoPreq;
3703 end
3704 else if (AdvPreq[Subject, 0] <> preNone) and
3705 (Tech[AdvPreq[Subject, 0]] < tsApplicable) or
3706 (AdvPreq[Subject, 1] <> preNone) and
3707 (Tech[AdvPreq[Subject, 1]] < tsApplicable) then
3708 Result := eNoPreq;
3709 end;
3710 if (Result = eOK) and (Command >= sExecute) then
3711 begin
3712 if (Mode = moPlaying) and (Subject = adMilitary) then
3713 IntServer(sIntSetDevModel, Player, 0, DevModel.Kind);
3714 // save DevModel, because sctModel commands are not Logged
3715 ResearchTech := Subject;
3716 end;
3717 end
3718 else
3719 Result := eViolation;
3720 end;
3721
3722 sStealTech, sStealTech - sExecute:
3723 begin
3724{$IFDEF TEXTLOG}CmdInfo := Format('StealTech P%d: %d', [Player, Subject]);
3725 {$ENDIF}
3726 if RW[Player].Happened and phStealTech = 0 then
3727 Result := eInvalid
3728 else if (Subject >= nAdv) or (Subject in FutureTech) or
3729 (RW[Player].Tech[Subject] >= tsSeen) or
3730 (RW[GStealFrom].Tech[Subject] < tsApplicable) then
3731 Result := eInvalid
3732 else if Command >= sExecute then
3733 begin
3734 SeeTech(Player, Subject);
3735 Dec(RW[Player].Happened, phStealTech);
3736 end;
3737 end;
3738
3739 sSetAttitude .. sSetAttitude + (nPl - 1) shl 4,
3740 sSetAttitude - sExecute .. sSetAttitude - sExecute + (nPl - 1) shl 4:
3741 begin
3742 p1 := Command shr 4 and $F;
3743{$IFDEF TEXTLOG}CmdInfo := Format('SetAttitude P%d to P%d: %d', [Player, p1, Subject]); {$ENDIF}
3744 if (Subject >= nAttitude) or (p1 >= nPl) or
3745 (RW[Player].EnemyReport[p1] = nil) then
3746 Result := eInvalid
3747 else if RW[Player].Treaty[p1] = trNoContact then
3748 Result := eNoPreq
3749 else if RW[Player].Attitude[p1] = Subject then
3750 Result := eNotChanged
3751 else if Command >= sExecute then
3752 begin
3753 RW[Player].Attitude[p1] := Subject;
3754 RW[p1].EnemyReport[Player].Attitude := Subject;
3755 end;
3756 end;
3757
3758 sCancelTreaty, sCancelTreaty - sExecute:
3759 if (LastEndClientCommand <> scReject) or
3760 (RW[Player].Treaty[pContacted] < trPeace) then
3761 Result := eInvalid
3762 else if (ServerVersion[Player] >= $010100) and
3763 (GTurn < RW[Player].LastCancelTreaty[pContacted] + CancelTreatyTurns)
3764 then
3765 Result := eCancelTreatyRush
3766 else if Command >= sExecute then
3767 begin
3768 CallPlayer(cShowCancelTreaty, pContacted, Player);
3769 IntServer(sIntCancelTreaty, Player, pContacted, nil^);
3770 for p2 := 0 to nPl - 1 do
3771 if (p2 <> pContacted) and (1 shl p2 and PeaceEnded <> 0) then
3772 begin
3773 I := pContacted shl 4 + Player;
3774 CallPlayer(cShowSupportAllianceAgainst, p2, I);
3775 end;
3776 for p2 := 0 to nPl - 1 do
3777 if (p2 <> pContacted) and (1 shl p2 and PeaceEnded <> 0) then
3778 begin
3779 I := p2;
3780 CallPlayer(cShowCancelTreatyByAlliance, Player, I);
3781 end;
3782 LastEndClientCommand := sTurn;
3783 end;
3784
3785 {
3786 Model Related Commands
3787 ____________________________________________________________________
3788 }
3789 sCreateDevModel, sCreateDevModel - sExecute:
3790 begin
3791{$IFDEF TEXTLOG}CmdInfo := Format('CreateDevModel P%d', [Player]); {$ENDIF}
3792 if Subject >= 4 then
3793 Result := eInvalid
3794 else if (upgrade[Subject, 0].Preq <> preNone) and
3795 (RW[Player].Tech[upgrade[Subject, 0].Preq] < tsApplicable) then
3796 Result := eNoPreq
3797 else if Command >= sExecute then
3798 begin
3799 with RW[Player].DevModel do
3800 begin
3801 Domain := Subject;
3802 MStrength := 0;
3803 MTrans := 0;
3804 MCost := 0;
3805 Upgrades := 0;
3806 FutureMCost := 0;
3807 for I := 0 to nUpgrade - 1 do
3808 with upgrade[Domain, I] do
3809 if (Preq = preNone) or (Preq >= 0) and
3810 ((RW[Player].Tech[Preq] >= tsApplicable) or
3811 (Preq in FutureTech) and (RW[Player].Tech[Preq] >= 0)) then
3812 begin
3813 if Preq in FutureTech then
3814 begin
3815 J := RW[Player].Tech[Preq];
3816 Inc(FutureMCost, J * Cost);
3817 end
3818 else
3819 begin
3820 J := 1;
3821 if Cost > MCost then
3822 MCost := Cost;
3823 end;
3824 Inc(Upgrades, 1 shl I);
3825 Inc(MStrength, J * Strength);
3826 Inc(MTrans, J * Trans);
3827 end;
3828 Inc(MCost, FutureMCost);
3829 FillChar(Cap, SizeOf(Cap), 0);
3830 Cap[mcOffense] := 2;
3831 Cap[mcDefense] := 1;
3832 for I := 0 to nFeature - 1 do
3833 with Feature[I] do
3834 if (1 shl Domain and Domains <> 0) and
3835 ((Preq = preNone) or (Preq = preSun) and
3836 (GWonder[woSun].EffectiveOwner = Player) or (Preq >= 0) and
3837 (RW[Player].Tech[Preq] >= tsApplicable)) and (I in AutoFeature)
3838 then
3839 Cap[I] := 1;
3840 MaxWeight := 5;
3841 if (WeightPreq7[Domain] <> preNA) and
3842 (RW[Player].Tech[WeightPreq7[Domain]] >= tsApplicable) then
3843 MaxWeight := 7;
3844 if (WeightPreq10[Domain] <> preNA) and
3845 (RW[Player].Tech[WeightPreq10[Domain]] >= tsApplicable) then
3846 if Domain = dSea then
3847 MaxWeight := 9
3848 else
3849 MaxWeight := 10;
3850 end;
3851 CalculateModel(RW[Player].DevModel);
3852 DevModelTurn[Player] := GTurn;
3853 end
3854 end;
3855
3856 sSetDevModelCap .. sSetDevModelCap + $3F0,
3857 sSetDevModelCap - sExecute .. sSetDevModelCap - sExecute + $3F0:
3858 begin
3859{$IFDEF TEXTLOG}CmdInfo := Format('SetDevModelCap P%d', [Player]); {$ENDIF}
3860 if Subject >= nFeature then
3861 Result := eInvalid
3862 else if DevModelTurn[Player] = GTurn then
3863 begin
3864 NewCap := Command shr 4 and $3F; { new value }
3865 with RW[Player].DevModel do
3866 if 1 shl Domain and Feature[Subject].Domains = 0 then
3867 Result := eDomainMismatch
3868 else if not ((Feature[Subject].Preq = preNone) or
3869 (Feature[Subject].Preq = preSun) and
3870 (GWonder[woSun].EffectiveOwner = Player) or
3871 (Feature[Subject].Preq >= 0) and
3872 (RW[Player].Tech[Feature[Subject].Preq] >= tsApplicable)) then
3873 Result := eNoPreq
3874 else
3875 begin
3876 if (Subject in AutoFeature) or (Subject = mcDefense) then
3877 MinCap := 1
3878 else
3879 MinCap := 0; { MinCap - minimum use of feature }
3880 if Subject >= mcFirstNonCap then
3881 MaxCap := 1
3882 else if Subject = mcDefense then
3883 begin
3884 if Domain = dGround then
3885 MaxCap := 2
3886 else
3887 MaxCap := 3;
3888 if RW[Player].Tech[adSteel] >= tsApplicable then
3889 Inc(MaxCap);
3890 end
3891 else
3892 MaxCap := 8; { MaxCap - maximum use of this feature }
3893 if (Domain = dGround) and (Subject = mcDefense) then
3894 CapWeight := 2
3895 else
3896 CapWeight := Feature[Subject].Weight;
3897 if (NewCap < MinCap) or (NewCap > MaxCap) or
3898 (Weight + (NewCap - Cap[Subject]) * CapWeight > MaxWeight) then
3899 Result := eViolation
3900 else if Command >= sExecute then
3901 begin
3902 Cap[Subject] := NewCap;
3903
3904 // mutual feature exclusion
3905 case Subject of
3906 mcSub:
3907 begin
3908 if ServerVersion[Player] >= $010103 then
3909 Cap[mcSeaTrans] := 0;
3910 Cap[mcArtillery] := 0;
3911 Cap[mcCarrier] := 0;
3912 if Cap[mcDefense] > 2 then
3913 Cap[mcDefense] := 2;
3914 end;
3915 mcSeaTrans:
3916 begin
3917 if ServerVersion[Player] >= $010103 then
3918 Cap[mcSub] := 0;
3919 end;
3920 mcCarrier:
3921 Cap[mcSub] := 0;
3922 mcArtillery:
3923 Cap[mcSub] := 0;
3924 mcAlpine:
3925 begin
3926 Cap[mcOver] := 0;
3927 Cap[mcMob] := 0;
3928 end;
3929 mcOver:
3930 Cap[mcAlpine] := 0;
3931 mcMob:
3932 begin
3933 Cap[mcAlpine] := 0;
3934 end;
3935 end;
3936
3937 CalculateModel(RW[Player].DevModel);
3938 end;
3939 end;
3940 end
3941 else
3942 Result := eNoModel;
3943 end;
3944
3945 {
3946 Unit Related Commands
3947 ____________________________________________________________________
3948 }
3949 sRemoveUnit, sRemoveUnit - sExecute:
3950 begin
3951{$IFDEF TEXTLOG}CmdInfo := Format('RemoveUnit P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
3952 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
3953 Result := eInvalid
3954 else
3955 begin
3956 Result := eRemoved;
3957 Loc0 := RW[Player].Un[Subject].Loc;
3958 if RealMap[Loc0] and fCity <> 0 then { check utilize }
3959 begin
3960 SearchCity(Loc0, Player, cix1);
3961 with RW[Player].City[cix1] do
3962 begin
3963 if (RW[Player].Model[RW[Player].Un[Subject].mix].Kind = mkCaravan)
3964 and ((Project and cpImp = 0) or
3965 (Imp[Project and cpIndex].Kind <> ikShipPart)) or
3966 (Project and cpImp = 0) and
3967 (RW[Player].Model[Project and cpIndex].Kind <> mkCaravan) then
3968 Result := eUtilized;
3969 if Command >= sExecute then
3970 begin
3971 if Result = eUtilized then
3972 begin
3973 with RW[Player].Un[Subject] do
3974 begin
3975 Cost := Integer(RW[Player].Model[mix].Cost) * Health *
3976 BuildCostMod[Difficulty[Player]] div 1200;
3977 if RW[Player].Model[mix].Cap[mcLine] > 0 then
3978 Cost := Cost div 2;
3979 end;
3980 if Project and (cpImp + cpIndex) = cpImp + imTrGoods then
3981 Inc(RW[Player].Money, Cost)
3982 else
3983 begin
3984 Inc(Prod, Cost * 2 div 3);
3985 Project0 := Project0 and not cpCompleted;
3986 if Project0 and not cpAuto <> Project and not cpAuto then
3987 Project0 := Project;
3988 Prod0 := Prod;
3989 end
3990 end;
3991 RemoveUnit_UpdateMap(Player, Subject);
3992 end;
3993 end;
3994 end
3995 else if Command >= sExecute then
3996 RemoveUnit_UpdateMap(Player, Subject);
3997 end
3998 end;
3999
4000 sSetUnitHome, sSetUnitHome - sExecute:
4001 begin
4002{$IFDEF TEXTLOG}CmdInfo := Format('SetUnitHome P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
4003 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4004 Result := eInvalid
4005 else
4006 begin
4007 Loc0 := RW[Player].Un[Subject].Loc;
4008 if RealMap[Loc0] and fCity = 0 then
4009 Result := eInvalid
4010 else
4011 begin
4012 SearchCity(Loc0, Player, cix1);
4013 if RW[Player].City[cix1].Flags and chCaptured <> 0 then
4014 Result := eViolation
4015 else if Command >= sExecute then
4016 RW[Player].Un[Subject].Home := cix1;
4017 end;
4018 end;
4019 end;
4020
4021 sSetSpyMission .. sSetSpyMission + (nSpyMission - 1) shl 4,
4022 sSetSpyMission - sExecute .. sSetSpyMission - sExecute +
4023 (nSpyMission - 1) shl 4:
4024 if Command >= sExecute then
4025 SpyMission := Command shr 4 and $F;
4026
4027 sLoadUnit, sLoadUnit - sExecute:
4028 begin
4029{$IFDEF TEXTLOG}CmdInfo := Format('LoadUnit P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
4030 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4031 Result := eInvalid
4032 else
4033 Result := LoadUnit(Player, Subject, Command < sExecute);
4034 end;
4035
4036 sUnloadUnit, sUnloadUnit - sExecute:
4037 begin
4038{$IFDEF TEXTLOG}CmdInfo := Format('UnloadUnit P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
4039 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4040 Result := eInvalid
4041 else
4042 Result := UnloadUnit(Player, Subject, Command < sExecute);
4043 end;
4044
4045 sSelectTransport, sSelectTransport - sExecute:
4046 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4047 Result := eInvalid
4048 else
4049 with RW[Player].Model[RW[Player].Un[Subject].mix] do
4050 begin
4051 if Cap[mcSeaTrans] + Cap[mcAirTrans] + Cap[mcCarrier] = 0 then
4052 Result := eInvalid
4053 else if Command >= sExecute then
4054 uixSelectedTransport := Subject;
4055 end;
4056
4057 sCreateUnit .. sCreateUnit + (nPl - 1) shl 4,
4058 sCreateUnit - sExecute .. sCreateUnit - sExecute + (nPl - 1) shl 4:
4059 if (GTestFlags and tfUncover <> 0) or (Difficulty[Player] = 0)
4060 then { supervisor only command }
4061 begin
4062 p1 := Command shr 4 and $F;
4063 Loc1 := Integer(Data);
4064 if (Occupant[Loc1] >= 0) and (p1 <> Occupant[Loc1]) or
4065 (RealMap[Loc1] and fCity <> 0) and
4066 (RealMap[Loc1] shr 27 <> Cardinal(p1)) or
4067 (RW[p1].Model[Subject].Domain < dAir) and
4068 ((RW[p1].Model[Subject].Domain = dSea) <> (RealMap[Integer(Data)] and
4069 fTerrain < fGrass)) then
4070 Result := eViolation
4071 else if Command >= sExecute then
4072 begin
4073 CreateUnit(p1, Subject);
4074 RW[p1].Un[RW[p1].nUn - 1].Loc := Integer(Data);
4075 PlaceUnit(p1, RW[p1].nUn - 1);
4076 UpdateUnitMap(Integer(Data));
4077 end;
4078 end
4079 else
4080 Result := eInvalid;
4081
4082 sMoveUnit + (0 + 6 * 8) * 16, sMoveUnit + (1 + 7 * 8) * 16,
4083 sMoveUnit + (2 + 0 * 8) * 16, sMoveUnit + (1 + 1 * 8) * 16,
4084 sMoveUnit + (0 + 2 * 8) * 16, sMoveUnit + (7 + 1 * 8) * 16,
4085 sMoveUnit + (6 + 0 * 8) * 16, sMoveUnit + (7 + 7 * 8) * 16,
4086 sMoveUnit - sExecute + (0 + 6 * 8) * 16, sMoveUnit - sExecute +
4087 (1 + 7 * 8) * 16, sMoveUnit - sExecute + (2 + 0 * 8) * 16,
4088 sMoveUnit - sExecute + (1 + 1 * 8) * 16, sMoveUnit - sExecute +
4089 (0 + 2 * 8) * 16, sMoveUnit - sExecute + (7 + 1 * 8) * 16,
4090 sMoveUnit - sExecute + (6 + 0 * 8) * 16, sMoveUnit - sExecute +
4091 (7 + 7 * 8) * 16:
4092 begin
4093 dx := (Command shr 4 + 4) and 7 - 4;
4094 dy := (Command shr 7 + 4) and 7 - 4;
4095{$IFDEF TEXTLOG}CmdInfo := Format('MoveUnit P%d I%d Mod%d Loc%d (%d,%d)', [Player, Subject, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc, dx, dy]); {$ENDIF}
4096 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4097 Result := eInvalid
4098 else
4099 Result := MoveUnit(Player, Subject, dx, dy, Command < sExecute);
4100 end;
4101
4102 {
4103 Settlers Related Commands
4104 ____________________________________________________________________
4105 }
4106 sAddToCity, sAddToCity - sExecute:
4107 begin
4108{$IFDEF TEXTLOG}CmdInfo := Format('AddToCity P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
4109 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4110 Result := eInvalid
4111 else if not (RW[Player].Model[RW[Player].Un[Subject].mix].Kind
4112 in [mkSettler, mkSlaves]) and
4113 (RW[Player].Un[Subject].Flags and unConscripts = 0) then
4114 Result := eViolation
4115 else
4116 begin
4117 Loc0 := RW[Player].Un[Subject].Loc;
4118 if RealMap[Loc0] and fCity = 0 then
4119 Result := eInvalid
4120 else
4121 begin
4122 SearchCity(Loc0, Player, cix1);
4123 with RW[Player].City[cix1] do
4124 if not CanCityGrow(Player, cix1) then
4125 Result := eMaxSize
4126 else if Command >= sExecute then
4127 begin { add to city }
4128 if Mode = moPlaying then
4129 SavedTiles[cix1] := 0; // save in every case
4130 if CanCityGrow(Player, cix1) then
4131 CityGrowth(Player, cix1);
4132 if (RW[Player].Model[RW[Player].Un[Subject].mix]
4133 .Kind = mkSettler) and CanCityGrow(Player, cix1) then
4134 CityGrowth(Player, cix1);
4135 RemoveUnit_UpdateMap(Player, Subject);
4136 end;
4137 end;
4138 end;
4139 end;
4140
4141 sStartJob .. sStartJob + $3F0, sStartJob - sExecute .. sStartJob + $3F0
4142 - sExecute:
4143 begin
4144 Loc0 := RW[Player].Un[Subject].Loc;
4145 I := Command shr 4 and $3F; // new job
4146{$IFDEF TEXTLOG}CmdInfo := Format('StartJob P%d Mod%d Loc%d: %d', [Player, RW[Player].Un[Subject].mix, Loc0, I]); {$ENDIF}
4147 if (Subject >= RW[Player].nUn) or (Loc0 < 0) then
4148 Result := eInvalid
4149 else if I >= nJob then
4150 Result := eInvalid
4151 else
4152 begin
4153 Result := StartJob(Player, Subject, I, Command < sExecute);
4154 if Result = eCity then
4155 begin // new city
4156 cix1 := RW[Player].nCity - 1;
4157 AddBestCityTile(Player, cix1);
4158 if Mode = moPlaying then
4159 with RW[Player].City[cix1] do
4160 begin
4161 // SavedResourceWeights[cix1]:=ResourceWeights;
4162 SavedTiles[cix1] := 0; // save in every case
4163 end;
4164 if Mode >= moMovie then { show new city in interface modules }
4165 for p1 := 0 to nPl - 1 do
4166 if (1 shl p1 and GWatching <> 0) and (p1 <> Player) and
4167 (ObserveLevel[Loc0] and (3 shl (2 * p1)) > 0) then
4168 CallPlayer(cShowCityChanged, p1, Loc0);
4169 end;
4170 end;
4171 end;
4172
4173 {
4174 City Related Commands
4175 ____________________________________________________________________
4176 }
4177 sSetCityProject, sSetCityProject - sExecute:
4178 begin
4179 NewProject := Integer(Data) and not cpAuto;
4180{$IFDEF TEXTLOG}CmdInfo := Format('SetCityProject P%d Loc%d: %d', [Player, RW[Player].City[Subject].Loc, NewProject]); {$ENDIF}
4181 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4182 then
4183 Result := eInvalid
4184 else
4185 with RW[Player].City[Subject] do
4186 begin
4187 if NewProject = Project then
4188 Result := eNotChanged
4189 else
4190 begin
4191 pt0 := ProjectType(Project0);
4192 pt1 := ProjectType(NewProject);
4193 if NewProject and cpImp = 0 then
4194 begin
4195 if NewProject and cpIndex >= RW[Player].nModel then
4196 Result := eInvalid
4197 else if (NewProject and cpConscripts <> 0) and
4198 not ((RW[Player].Tech[adConscription] >= tsApplicable) and
4199 (RW[Player].Model[NewProject and cpIndex].Domain = dGround)
4200 and (RW[Player].Model[NewProject and cpIndex].Kind < mkScout))
4201 then
4202 Result := eViolation
4203 // else if (RW[Player].Model[NewProject and cpIndex].Kind=mkSlaves)
4204 // and (GWonder[woPyramids].EffectiveOwner<>Player) then
4205 // result:=eNoPreq
4206 end
4207 else if NewProject and cpIndex >= nImp then
4208 Result := eInvalid
4209 else
4210 begin
4211 Preq := Imp[NewProject and cpIndex].Preq;
4212 for I := 0 to nImpReplacement - 1 do
4213 if (ImpReplacement[I].OldImp = NewProject and cpIndex) and
4214 (Built[ImpReplacement[I].NewImp] > 0) then begin
4215 Result := eObsolete;
4216 Break;
4217 end;
4218 if Result = eObsolete then
4219 else if Preq = preNA then
4220 Result := eInvalid
4221 else if (Preq >= 0) and (RW[Player].Tech[Preq] < tsApplicable)
4222 then
4223 Result := eNoPreq
4224 else if Built[NewProject and cpIndex] > 0 then
4225 Result := eInvalid
4226 else if (NewProject and cpIndex < nWonder) and
4227 (GWonder[NewProject and cpIndex].CityID <> WonderNotBuiltYet) then
4228 Result := eViolation // wonder already exists
4229 else if (NewProject and cpIndex = imSpacePort) and
4230 (RW[Player].NatBuilt[imSpacePort] > 0) then
4231 Result := eViolation // space port already exists
4232 else if (NewProject = cpImp + imBank) and (Built[imMarket] = 0)
4233 or (NewProject = cpImp + imUniversity) and
4234 (Built[imLibrary] = 0) or (NewProject = cpImp + imResLab) and
4235 (Built[imUniversity] = 0) or (NewProject = cpImp + imMfgPlant)
4236 and (Built[imFactory] = 0) then
4237 Result := eNoPreq;
4238 case NewProject - cpImp of
4239 woLighthouse, woMagellan, imCoastalFort, imHarbor, imPlatform:
4240 begin { city at ocean? }
4241 Preq := 0;
4242 V8_to_Loc(Loc, Adjacent);
4243 for V8 := 0 to 7 do
4244 begin
4245 Loc1 := Adjacent[V8];
4246 if (Loc1 >= 0) and (Loc1 < MapSize) and
4247 (RealMap[Loc1] and fTerrain = fShore) then
4248 Inc(Preq);
4249 end;
4250 if Preq = 0 then
4251 Result := eNoPreq;
4252 end;
4253 woHoover, imHydro:
4254 begin { city at river or mountains? }
4255 Preq := 0;
4256 V8_to_Loc(Loc, Adjacent);
4257 for V8 := 0 to 7 do
4258 begin
4259 Loc1 := Adjacent[V8];
4260 if (Loc1 >= 0) and (Loc1 < MapSize) and
4261 ((RealMap[Loc1] and fTerrain = fMountains) or
4262 (RealMap[Loc1] and fRiver <> 0)) then
4263 Inc(Preq);
4264 end;
4265 if Preq = 0 then
4266 Result := eNoPreq;
4267 end;
4268 woMIR, imShipComp, imShipPow, imShipHab:
4269 if RW[Player].NatBuilt[imSpacePort] = 0 then
4270 Result := eNoPreq;
4271 end;
4272 if (GTestFlags and tfNoRareNeed = 0) and
4273 (Imp[NewProject and cpIndex].Kind = ikShipPart) then
4274 if RW[Player].Tech[adMassProduction] < tsApplicable then
4275 Result := eNoPreq
4276 else
4277 begin // check for rare resources
4278 if NewProject and cpIndex = imShipComp then
4279 J := 1
4280 else if NewProject and cpIndex = imShipPow then
4281 J := 2
4282 else { if NewProject and cpIndex=imShipHab then }
4283 J := 3;
4284 // j = rare resource required
4285 Preq := 0;
4286 V21_to_Loc(Loc, Radius);
4287 for V21 := 1 to 26 do
4288 begin
4289 Loc1 := Radius[V21];
4290 if (Loc1 >= 0) and (Loc1 < MapSize) and
4291 (RealMap[Loc1] shr 25 and 3 = Cardinal(J)) then
4292 Inc(Preq);
4293 end;
4294 if Preq = 0 then
4295 Result := eNoPreq;
4296 end;
4297 end;
4298
4299 if (Command >= sExecute) and (Result >= rExecuted) then
4300 begin
4301 if pt0 <> ptSelect then
4302 if NewProject and (cpImp or cpIndex) = Project0 and
4303 (cpImp or cpIndex) then
4304 Prod := Prod0
4305 else if (pt1 = ptTrGoods) or (pt1 = ptShip) or (pt1 <> pt0)
4306 and (pt0 <> ptCaravan) then
4307 begin
4308 Inc(RW[Player].Money, Prod0);
4309 Prod := 0;
4310 Prod0 := 0;
4311 Project0 := cpImp + imTrGoods;
4312 end
4313 else
4314 Prod := Prod0 * 2 div 3;
4315 Project := NewProject;
4316 end;
4317 end;
4318 end;
4319 end;
4320
4321 sBuyCityProject, sBuyCityProject - sExecute:
4322 begin
4323{$IFDEF TEXTLOG}CmdInfo := Format('BuyCityProject P%d Loc%d', [Player, RW[Player].City[Subject].Loc]); {$ENDIF}
4324 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4325 then
4326 Result := eInvalid
4327 else
4328 with RW[Player].City[Subject] do
4329 if (RW[Player].Government = gAnarchy) or (Flags and chCaptured <> 0)
4330 then
4331 Result := eOutOfControl
4332 else if (Project and cpImp <> 0) and
4333 ((Project and cpIndex = imTrGoods) or
4334 (Imp[Project and cpIndex].Kind = ikShipPart)) then
4335 Result := eInvalid // don't buy colony ship
4336 else
4337 begin
4338 CityReport.HypoTiles := -1;
4339 CityReport.HypoTax := -1;
4340 CityReport.HypoLux := -1;
4341 GetCityReport(Player, Subject, CityReport);
4342 Cost := CityReport.ProdCost;
4343 NextProd := CityReport.ProdRep - CityReport.Support;
4344 if (CityReport.Working - CityReport.Happy > Size shr 1) or
4345 (NextProd < 0) then // !!! change to new style disorder
4346 NextProd := 0;
4347 Cost := Cost - Prod - NextProd;
4348 if (GWonder[woMich].EffectiveOwner = Player) and
4349 (Project and cpImp <> 0) then
4350 Cost := Cost * 2
4351 else
4352 Cost := Cost * 4;
4353 if Cost <= 0 then
4354 Result := eNotChanged
4355 else if Cost > RW[Player].Money then
4356 Result := eViolation
4357 else if Command >= sExecute then
4358 IntServer(sIntBuyMaterial, Player, Subject, Cost);
4359 // need to save material/cost because city tiles are not correct
4360 // when loading
4361 end;
4362 end;
4363
4364 sSellCityProject, sSellCityProject - sExecute:
4365 begin
4366{$IFDEF TEXTLOG}CmdInfo := Format('SellCityProject P%d Loc%d', [Player, RW[Player].City[Subject].Loc]); {$ENDIF}
4367 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4368 then
4369 Result := eInvalid
4370 else if Command >= sExecute then
4371 with RW[Player].City[Subject] do
4372 begin
4373 Inc(RW[Player].Money, Prod0);
4374 Prod := 0;
4375 Prod0 := 0;
4376 end;
4377 end;
4378
4379 sSellCityImprovement, sSellCityImprovement - sExecute:
4380 begin
4381{$IFDEF TEXTLOG}CmdInfo := Format('SellCityImprovement P%d Loc%d: %d', [Player, RW[Player].City[Subject].Loc, Integer(Data)]); {$ENDIF}
4382 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4383 then
4384 Result := eInvalid
4385 else
4386 with RW[Player].City[Subject] do
4387 if Built[Integer(Data)] = 0 then
4388 Result := eInvalid
4389 else if (RW[Player].Government = gAnarchy) or
4390 (Flags and chCaptured <> 0) then
4391 Result := eOutOfControl
4392 else if Flags and chImprovementSold <> 0 then
4393 Result := eOnlyOnce
4394 else if Command >= sExecute then
4395 begin
4396 Inc(RW[Player].Money, Imp[Integer(Data)].Cost * BuildCostMod
4397 [Difficulty[Player]] div 12);
4398 Built[Integer(Data)] := 0;
4399 if Imp[Integer(Data)].Kind in [ikNatLocal, ikNatGlobal] then
4400 begin
4401 RW[Player].NatBuilt[Integer(Data)] := 0;
4402 case Integer(Data) of
4403 imGrWall:
4404 GrWallContinent[Player] := -1;
4405 imSpacePort:
4406 DestroySpacePort_TellPlayers(Player, -1);
4407 end;
4408 end;
4409 Inc(Flags, chImprovementSold);
4410 end;
4411 end;
4412
4413 sRebuildCityImprovement, sRebuildCityImprovement - sExecute:
4414 begin
4415 OldImp := Integer(Data);
4416{$IFDEF TEXTLOG}CmdInfo := Format('RebuildCityImprovement P%d Loc%d: %d', [Player, RW[Player].City[Subject].Loc, OldImp]); {$ENDIF}
4417 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4418 then
4419 Result := eInvalid
4420 else
4421 begin
4422 if (OldImp < 0) or (OldImp >= nImp) or
4423 not (Imp[OldImp].Kind in [ikCommon, ikNatLocal, ikNatGlobal]) then
4424 Result := eInvalid
4425 else
4426 with RW[Player].City[Subject] do
4427 if (Built[OldImp] = 0) or (Project and cpImp = 0) or
4428 not (Imp[Project and cpIndex].Kind in [ikCommon, ikNatLocal,
4429 ikNatGlobal]) then
4430 Result := eInvalid
4431 else if (RW[Player].Government = gAnarchy) or
4432 (Flags and chCaptured <> 0) then
4433 Result := eOutOfControl
4434 else if Flags and chImprovementSold <> 0 then
4435 Result := eOnlyOnce
4436 else if Command >= sExecute then
4437 begin
4438 Inc(Prod, Imp[OldImp].Cost * BuildCostMod[Difficulty[Player]]
4439 div 12 * 2 div 3);
4440 Project0 := Project0 and not cpCompleted;
4441 if Project0 and not cpAuto <> Project and not cpAuto then
4442 Project0 := Project;
4443 Prod0 := Prod;
4444 Built[OldImp] := 0;
4445 if Imp[OldImp].Kind in [ikNatLocal, ikNatGlobal] then
4446 begin // nat. project lost
4447 RW[Player].NatBuilt[OldImp] := 0;
4448 case OldImp of
4449 imGrWall:
4450 GrWallContinent[Player] := -1;
4451 imSpacePort:
4452 DestroySpacePort_TellPlayers(Player, -1);
4453 end;
4454 end;
4455 Inc(Flags, chImprovementSold);
4456 end;
4457 end;
4458 end;
4459
4460 sSetCityTiles, sSetCityTiles - sExecute:
4461 begin
4462{$IFDEF TEXTLOG}CmdInfo := Format('SetCityTiles P%d Loc%d: %x', [Player, RW[Player].City[Subject].Loc, Integer(Data)]); {$ENDIF}
4463 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4464 then
4465 Result := eInvalid
4466 else
4467 Result := SetCityTiles(Player, Subject, Integer(Data),
4468 Command < sExecute);
4469 end;
4470
4471 {
4472 Client Exclusive Commands
4473 ____________________________________________________________________
4474 }
4475 else
4476 if Command >= cClientEx then
4477 begin
4478{$IFDEF TEXTLOG}CmdInfo := Format('ClientEx%x P%d', [Command, Player]);
4479 {$ENDIF}
4480 if ProcessClientData[Player] or (Mode = moPlaying) then
4481 CallPlayer(Command, Player, Data)
4482 end
4483 else
4484 Result := eUnknown;
4485 end; { case command }
4486
4487 // do not log invalid and non-relevant commands
4488 if Result = eZOC_EnemySpotted then
4489 begin
4490 Assert(Mode = moPlaying);
4491 CL.State := FormerCLState;
4492 IntServer(sIntDiscoverZOC, Player, 0, ZOCTile);
4493 end
4494 else if Result and rEffective = 0 then
4495 if Mode < moPlaying then
4496 begin
4497{$IFDEF TEXTLOG}CmdInfo := Format('***ERROR (%x) ', [Result]) + CmdInfo;
4498 {$ENDIF}
4499 LoadOK := False;
4500 end
4501 else
4502 begin
4503 if Logged then
4504 CL.State := FormerCLState;
4505 if (Result < rExecuted) and (Command >= sExecute) then
4506 PutMessage(1 shl 16 + 1, Format('INVALID: %d calls %x (%d)',
4507 [Player, Command, Subject]));
4508 end;
4509
4510 if (Command and (cClientEx or sExecute or sctMask) = sExecute or sctEndClient)
4511 and (Result >= rExecuted) then
4512 LastEndClientCommand := Command;
4513{$IFOPT O-}Dec(nHandoverStack, 2); {$ENDIF}
4514end;
4515
4516
4517initialization
4518
4519FindFirst(ParamStr(0), $21, ExeInfo);
4520FindClose(ExeInfo);
4521
4522{$IFOPT O-}nHandoverStack := 0; {$ENDIF}
4523
4524end.
Note: See TracBrowser for help on using the repository browser.