source: tags/1.3.5/GameServer.pas

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