source: tags/1.3.4/GameServer.pas

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