source: trunk/GameServer.pas

Last change on this file was 550, checked in by chronos, 2 days 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-} if Mode < moPlaying then
2188 InvalidTreatyMap := not(1 shl P); {$ENDIF}
2189 // territory should not be considered for the rest of the command
2190 // execution, because during loading a game it's incorrect before
2191 // subsequent sIntExpandTerritory is processed
2192 end
2193 else // city destroyed
2194 begin
2195 DestroyCity_TellPlayers(MoveInfo.Defender, MoveInfo.Dcix, False);
2196 CheckBorders(ToLoc, MoveInfo.Defender);
2197 end;
2198 RecalcPeaceMap(P);
2199 if Mode >= moMovie then
2200 Move(GWonder, Wonder, SizeOf(GWonder));
2201 end; { if MoveInfo.MoveType=mtCapture }
2202
2203 if MoveInfo.MoveType = mtSpyMission then
2204 begin
2205 MissionResult := DoSpyMission(P, MoveInfo.Defender, MoveInfo.Dcix,
2206 SpyMission);
2207 if (Mode = moPlaying) and (SpyMission = smStealForeignReports) then
2208 CallPlayer(cShowMissionResult, P, MissionResult);
2209 end;
2210
2211 Health := MoveInfo.EndHealth;
2212 Dec(Movement, MoveInfo.Cost);
2213 // transport unload
2214 if Master >= 0 then
2215 begin
2216 if PModel.Domain = dAir then
2217 Dec(Un[Master].AirLoad)
2218 else
2219 begin
2220 Dec(Un[Master].TroopLoad);
2221 Assert(Movement <= 0);
2222 end;
2223 Master := -1;
2224 end;
2225
2226 if (Health <= 0) or (MoveInfo.MoveType = mtSpyMission) then
2227 RemoveUnit(P, uix) // spy mission or victim of HostileDamage
2228 else
2229 begin // transport load
2230 Master := MoveInfo.ToMaster;
2231 if MoveInfo.ToMaster >= 0 then
2232 begin
2233 if PModel.Domain = dAir then
2234 Inc(Un[MoveInfo.ToMaster].AirLoad)
2235 else
2236 Inc(Un[MoveInfo.ToMaster].TroopLoad);
2237 end
2238 else
2239 PlaceUnit(P, uix);
2240 end;
2241
2242 if (MoveInfo.MoveType = mtCapture) and (nUpdateLoc > 0) then
2243 RecalcMapZoC(P);
2244 UpdateUnitMap(ToLoc, MoveInfo.MoveType = mtCapture);
2245 for I := 0 to nUpdateLoc - 1 do
2246 UpdateUnitMap(UpdateLoc[I]);
2247 // tell about lost units of defender
2248
2249 if (MoveInfo.MoveType <> mtSpyMission) and (Master < 0) then
2250 begin
2251 if (PModel.Kind = mkDiplomat) or (PModel.Domain = dAir) or
2252 (PModel.Cap[mcRadar] + PModel.Cap[mcCarrier] + PModel.Cap[mcAcademy] >
2253 0) or (RealMap[ToLoc] and fTerrain = fMountains) or
2254 (RealMap[ToLoc] and fTerImp = tiFort) or
2255 (RealMap[ToLoc] and fTerImp = tiBase) then
2256 ExtDiscover := True;
2257 if (PModel.Kind = mkDiplomat) or (PModel.Cap[mcSpy] > 0) then
2258 I := lObserveSuper
2259 else if (PModel.Domain = dAir) or
2260 (PModel.Cap[mcRadar] + PModel.Cap[mcCarrier] > 0) then
2261 I := lObserveAll
2262 else
2263 I := lObserveUnhidden;
2264 if ExtDiscover then
2265 begin
2266 if Discover21(ToLoc, P, I, True, PModel.Domain = dGround) then
2267 Result := Result or rEnemySpotted;
2268 end
2269 else
2270 begin
2271 if Discover9(ToLoc, P, I, True, PModel.Domain = dGround) then
2272 Result := Result or rEnemySpotted;
2273 end;
2274 end;
2275
2276 if Mode >= moMovie then { show after-move in interface modules }
2277 for p1 := 0 to nPl - 1 do
2278 if (1 shl p1 and GWatching <> 0) and ((p1 <> P) or (bix[p1].Kind = btTerm))
2279 then
2280 begin
2281 if PModel.Cap[mcStealth] > 0 then
2282 MinLevel := lObserveSuper
2283 else if PModel.Cap[mcSub] > 0 then
2284 MinLevel := lObserveAll
2285 else
2286 MinLevel := lObserveUnhidden;
2287 SeeFrom := (p1 = P) or (ObserveLevel[FromLoc] shr (2 * p1) and
2288 3 >= MinLevel);
2289 SeeTo := (p1 = P) or (ObserveLevel[ToLoc] shr (2 * p1) and
2290 3 >= MinLevel);
2291 if SeeTo and (MoveInfo.MoveType = mtCapture) then
2292 CallPlayer(cShowCityChanged, p1, ToLoc);
2293 if SeeFrom and SeeTo then
2294 CallPlayer(cShowAfterMove, p1, ToLoc)
2295 else if (MoveInfo.MoveType <> mtSpyMission) and SeeTo then
2296 CallPlayer(cShowUnitChanged, p1, ToLoc);
2297 for I := 0 to nUpdateLoc - 1 do
2298 if ObserveLevel[UpdateLoc[I]] shr (2 * p1) and 3 >= lObserveUnhidden
2299 then
2300 CallPlayer(cShowUnitChanged, p1, UpdateLoc[I]);
2301 end;
2302 end;
2303end;
2304
2305function ExecuteAttack(P, uix, ToLoc: Integer; var MoveInfo: TMoveInfo;
2306 ShowMove: TShowMove): Integer;
2307
2308 procedure WriteBattleHistory(ToLoc, FromLoc, Attacker, Defender, mixAttacker,
2309 mixDefender: Integer; AttackerLost, DefenderLost: Boolean);
2310 var
2311 AttackerBattle, DefenderBattle: ^TBattle;
2312 begin
2313 with RW[Attacker] do
2314 begin
2315 if nBattleHistory = 0 then
2316 ReallocMem(BattleHistory, 16 * SizeOf(TBattle))
2317 else if (nBattleHistory >= 16) and
2318 (nBattleHistory and (nBattleHistory - 1) = 0) then
2319 ReallocMem(BattleHistory, nBattleHistory * (2 * SizeOf(TBattle)));
2320 AttackerBattle := @BattleHistory[nBattleHistory];
2321 Inc(nBattleHistory);
2322 end;
2323 with RW[Defender] do
2324 begin
2325 if nBattleHistory = 0 then
2326 ReallocMem(BattleHistory, 16 * SizeOf(TBattle))
2327 else if (nBattleHistory >= 16) and
2328 (nBattleHistory and (nBattleHistory - 1) = 0) then
2329 ReallocMem(BattleHistory, nBattleHistory * (2 * SizeOf(TBattle)));
2330 DefenderBattle := @BattleHistory[nBattleHistory];
2331 Inc(nBattleHistory);
2332 end;
2333 AttackerBattle.Enemy := Defender;
2334 AttackerBattle.Flags := 0;
2335 AttackerBattle.Turn := GTurn;
2336 AttackerBattle.mix := mixAttacker;
2337 AttackerBattle.mixEnemy := mixDefender;
2338 AttackerBattle.ToLoc := ToLoc;
2339 AttackerBattle.FromLoc := FromLoc;
2340 DefenderBattle.Enemy := Attacker;
2341 DefenderBattle.Flags := bhEnemyAttack;
2342 DefenderBattle.Turn := GTurn;
2343 DefenderBattle.mix := mixDefender;
2344 DefenderBattle.mixEnemy := mixAttacker;
2345 DefenderBattle.ToLoc := ToLoc;
2346 DefenderBattle.FromLoc := FromLoc;
2347 if AttackerLost then
2348 begin
2349 AttackerBattle.Flags := AttackerBattle.Flags or bhMyUnitLost;
2350 DefenderBattle.Flags := DefenderBattle.Flags or bhEnemyUnitLost;
2351 end;
2352 if DefenderLost then
2353 begin
2354 AttackerBattle.Flags := AttackerBattle.Flags or bhEnemyUnitLost;
2355 DefenderBattle.Flags := DefenderBattle.Flags or bhMyUnitLost;
2356 end;
2357 end;
2358
2359var
2360 I, p1, FromLoc, uix1, nUpdateLoc, ExpGain, ExpelToLoc, cix1: Integer;
2361 PModel: ^TModel;
2362 UpdateLoc: array [0 .. numax - 1] of Integer;
2363 LoseCityPop, CityDestroyed, SeeFrom, SeeTo, ZoCDefenderDestroyed: Boolean;
2364begin
2365 Result := 0;
2366 with RW[P].Un[uix] do
2367 begin
2368 PModel := @RW[P].Model[mix];
2369 FromLoc := Loc;
2370
2371 ShowMove.EndHealth := MoveInfo.EndHealth;
2372 ShowMove.EndHealthDef := MoveInfo.EndHealthDef;
2373 if MoveInfo.MoveType = mtAttack then
2374 WriteBattleHistory(ToLoc, FromLoc, P, MoveInfo.Defender, mix,
2375 RW[MoveInfo.Defender].Un[MoveInfo.Duix].mix, MoveInfo.EndHealth <= 0,
2376 MoveInfo.EndHealthDef <= 0);
2377
2378 { if RW[p].Treaty[MoveInfo.Defender]=trCeaseFire then
2379 begin
2380 if Mode>=moMovie then
2381 CallPlayer(cShowCancelTreaty,MoveInfo.Defender,P);
2382 CancelTreaty(P,MoveInfo.Defender)
2383 end; }
2384 if Mode >= moMovie then { show attack in interface modules }
2385 for p1 := 0 to nPl - 1 do
2386 if (1 shl p1 and GWatching <> 0) and ((p1 <> P) or (bix[p1].Kind = btTerm))
2387 then
2388 begin
2389 SeeFrom := ObserveLevel[FromLoc] shr (2 * p1) and
2390 3 >= lObserveUnhidden;
2391 SeeTo := ObserveLevel[ToLoc] shr (2 * p1) and 3 >= lObserveUnhidden;
2392 if SeeFrom and SeeTo then
2393 begin
2394 TellAboutModel(p1, P, mix);
2395 if p1 = P then
2396 ShowMove.emix := -1
2397 else
2398 ShowMove.emix := emixSafe(p1, P, mix);
2399 CallPlayer(cShowAttacking, p1, ShowMove);
2400 end;
2401 end;
2402
2403 LoseCityPop := False;
2404 if (RealMap[ToLoc] and fCity <> 0) and
2405 ((MoveInfo.MoveType = mtAttack) and (MoveInfo.EndHealthDef <= 0) or
2406 (MoveInfo.MoveType = mtBombard) and (BombardmentDestroysCity or
2407 (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Size > 2))) then
2408 case PModel.Domain of
2409 dGround:
2410 LoseCityPop := (PModel.Cap[mcArtillery] > 0) or
2411 (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Built[imWalls] = 0) and
2412 (Continent[ToLoc] <> GrWallContinent[MoveInfo.Defender]);
2413 dSea:
2414 LoseCityPop := RW[MoveInfo.Defender].City[MoveInfo.Dcix].Built
2415 [imCoastalFort] = 0;
2416 dAir:
2417 LoseCityPop := RW[MoveInfo.Defender].City[MoveInfo.Dcix].Built
2418 [imMissileBat] = 0;
2419 end;
2420 CityDestroyed := LoseCityPop and
2421 (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Size <= 2);
2422
2423 if MoveInfo.MoveType = mtBombard then
2424 begin
2425 Assert(Movement >= 100);
2426 if PModel.Attack = 0 then
2427 Flags := Flags and not unBombsLoaded;
2428 Dec(Movement, 100);
2429 end
2430 else if MoveInfo.MoveType = mtExpel then
2431 begin
2432 Assert(Movement >= 100);
2433 Job := jNone;
2434 Flags := Flags and not unFortified;
2435 Dec(Movement, 100);
2436 end
2437 else
2438 begin
2439 Assert(MoveInfo.MoveType = mtAttack);
2440 if MoveInfo.EndHealth = 0 then
2441 RemoveUnit(P, uix, MoveInfo.Defender) // destroy attacker
2442 else
2443 begin // update attacker
2444 ExpGain := (Health - MoveInfo.EndHealth + 1) shr 1;
2445 if Exp + ExpGain > (nExp - 1) * ExpCost then
2446 Exp := (nExp - 1) * ExpCost
2447 else
2448 Inc(Exp, ExpGain);
2449 Health := MoveInfo.EndHealth;
2450 Job := jNone;
2451 if RW[MoveInfo.Defender].Model[RW[MoveInfo.Defender].Un[MoveInfo.Duix]
2452 .mix].Domain < dAir then
2453 Flags := Flags and not unBombsLoaded;
2454 Flags := Flags and not unFortified;
2455 if Movement > 100 then
2456 Dec(Movement, 100)
2457 else
2458 Movement := 0;
2459 end;
2460 end;
2461
2462 ZoCDefenderDestroyed := False;
2463 nUpdateLoc := 0;
2464 if MoveInfo.MoveType = mtExpel then
2465 with RW[MoveInfo.Defender], Un[MoveInfo.Duix] do
2466 begin // expel friendly unit
2467 if Home >= 0 then
2468 ExpelToLoc := City[Home].Loc
2469 else
2470 begin
2471 ExpelToLoc := City[0].Loc; // search destination for homeless units
2472 for cix1 := 1 to nCity - 1 do
2473 if (City[cix1].Loc >= 0) and
2474 ((ExpelToLoc < 0) or (City[cix1].Built[imPalace] > 0)) then
2475 ExpelToLoc := City[cix1].Loc;
2476 end;
2477 if ExpelToLoc >= 0 then
2478 begin
2479 FreeUnit(MoveInfo.Defender, MoveInfo.Duix);
2480 Loc := ExpelToLoc;
2481 PlaceUnit(MoveInfo.Defender, MoveInfo.Duix);
2482 UpdateLoc[nUpdateLoc] := Loc;
2483 Inc(nUpdateLoc);
2484 Flags := Flags or unWithdrawn;
2485 end;
2486 end
2487 else if (MoveInfo.MoveType = mtAttack) and (MoveInfo.EndHealthDef > 0) then
2488 with RW[MoveInfo.Defender].Un[MoveInfo.Duix] do
2489 begin // update defender
2490 ExpGain := (Health - MoveInfo.EndHealthDef + 1) shr 1;
2491 if Exp + ExpGain > (nExp - 1) * ExpCost then
2492 Exp := (nExp - 1) * ExpCost
2493 else
2494 Inc(Exp, ExpGain);
2495 Health := MoveInfo.EndHealthDef;
2496 end
2497 else
2498 begin // destroy defenders
2499 if MoveInfo.MoveType <> mtBombard then
2500 begin
2501 ZoCDefenderDestroyed := RW[MoveInfo.Defender].Model
2502 [RW[MoveInfo.Defender].Un[MoveInfo.Duix].mix].Flags and mdZOC <> 0;
2503 if ((RealMap[ToLoc] and fCity = 0) and
2504 (RealMap[ToLoc] and fTerImp <> tiBase) and
2505 (RealMap[ToLoc] and fTerImp <> tiFort)) or LoseCityPop and
2506 (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Size = 2) then
2507 RemoveAllUnits(MoveInfo.Defender, ToLoc, P)
2508 { no city, base or fortress }
2509 else
2510 RemoveUnit(MoveInfo.Defender, MoveInfo.Duix, P);
2511 end;
2512
2513 if LoseCityPop then // city defender defeated -- shrink city
2514 if not CityDestroyed then
2515 CityShrink(MoveInfo.Defender, MoveInfo.Dcix)
2516 else
2517 begin
2518 for uix1 := 0 to RW[MoveInfo.Defender].nUn - 1 do
2519 with RW[MoveInfo.Defender].Un[uix1] do
2520 if (Loc >= 0) and (Home = MoveInfo.Dcix) then
2521 begin
2522 UpdateLoc[nUpdateLoc] := Loc;
2523 Inc(nUpdateLoc);
2524 end;
2525 // unit will be removed -- remember position and update for all players
2526 DestroyCity_TellPlayers(MoveInfo.Defender, MoveInfo.Dcix, False);
2527 CheckBorders(ToLoc, MoveInfo.Defender);
2528 RecalcPeaceMap(P);
2529 end;
2530 end;
2531
2532 if CityDestroyed and (nUpdateLoc > 0) then
2533 RecalcMapZoC(P)
2534 else if ZoCDefenderDestroyed then
2535 RecalcV8ZoC(P, ToLoc);
2536 UpdateUnitMap(FromLoc);
2537 UpdateUnitMap(ToLoc, LoseCityPop);
2538 for I := 0 to nUpdateLoc - 1 do
2539 UpdateUnitMap(UpdateLoc[I]);
2540 // tell about lost units of defender
2541
2542 if Mode >= moMovie then
2543 begin
2544 for I := 0 to RW[P].nEnemyModel - 1 do
2545 with RW[P].EnemyModel[I] do
2546 Lost := Destroyed[P, Owner, mix];
2547 for p1 := 0 to nPl - 1 do { show after-attack in interface modules }
2548 if (1 shl p1 and GWatching <> 0) and ((p1 <> P) or (bix[p1].Kind = btTerm))
2549 then
2550 begin
2551 SeeFrom := ObserveLevel[FromLoc] shr (2 * p1) and
2552 3 >= lObserveUnhidden;
2553 SeeTo := ObserveLevel[ToLoc] shr (2 * p1) and 3 >= lObserveUnhidden;
2554 if SeeTo and CityDestroyed then
2555 CallPlayer(cShowCityChanged, p1, ToLoc); // city was destroyed
2556 if SeeFrom and SeeTo then
2557 begin
2558 CallPlayer(cShowAfterAttack, p1, ToLoc);
2559 CallPlayer(cShowAfterAttack, p1, FromLoc);
2560 end
2561 else
2562 begin
2563 if SeeTo then
2564 CallPlayer(cShowUnitChanged, p1, ToLoc);
2565 if SeeFrom then
2566 CallPlayer(cShowUnitChanged, p1, FromLoc);
2567 end;
2568 if SeeTo and (MoveInfo.MoveType = mtExpel) and (ExpelToLoc >= 0) then
2569 CallPlayer(cShowUnitChanged, p1, ExpelToLoc);
2570 end;
2571 end;
2572 end;
2573end;
2574
2575function MoveUnit(P, uix, dx, dy: Integer; TestOnly: Boolean): Integer;
2576var
2577 ToLoc: Integer;
2578 MoveInfo: TMoveInfo;
2579 ShowMove: TShowMove;
2580begin
2581{$IFOPT O-}Assert(1 shl P and InvalidTreatyMap = 0); {$ENDIF}
2582 with RW[P].Un[uix] do
2583 begin
2584 ToLoc := dLoc(Loc, dx, dy);
2585 if (ToLoc < 0) or (ToLoc >= MapSize) then
2586 begin
2587 Result := eInvalid;
2588 Exit;
2589 end;
2590 Result := CalculateMove(P, uix, ToLoc, 3 - dy and 1, TestOnly, MoveInfo);
2591 if Result = eZOC_EnemySpotted then
2592 ZOCTile := ToLoc;
2593 if (Result >= rExecuted) and not TestOnly then
2594 begin
2595 ShowMove.dx := dx;
2596 ShowMove.dy := dy;
2597 ShowMove.FromLoc := Loc;
2598 ShowMove.mix := mix;
2599 ShowMove.Health := Health;
2600 ShowMove.Fuel := Fuel;
2601 ShowMove.Exp := Exp;
2602 ShowMove.Load := TroopLoad + AirLoad;
2603 ShowMove.Owner := P;
2604 if (TroopLoad > 0) or (AirLoad > 0) then
2605 ShowMove.Flags := unMulti
2606 else
2607 ShowMove.Flags := 0;
2608 case MoveInfo.MoveType of
2609 mtCapture:
2610 ShowMove.Flags := ShowMove.Flags or umCapturing;
2611 mtSpyMission:
2612 ShowMove.Flags := ShowMove.Flags or umSpyMission;
2613 mtBombard:
2614 ShowMove.Flags := ShowMove.Flags or umBombarding;
2615 mtExpel:
2616 ShowMove.Flags := ShowMove.Flags or umExpelling;
2617 end;
2618 case MoveInfo.MoveType of
2619 mtMove, mtCapture, mtSpyMission:
2620 Result := ExecuteMove(P, uix, ToLoc, MoveInfo, ShowMove) or Result;
2621 mtAttack, mtBombard, mtExpel:
2622 Result := ExecuteAttack(P, uix, ToLoc, MoveInfo, ShowMove) or Result;
2623 end;
2624 end;
2625 end;
2626end;
2627
2628function Server(Command, Player, Subject: Integer; var Data): Integer; stdcall;
2629
2630 function CountPrice(const Offer: TOffer; PriceType: Integer): Integer;
2631 var
2632 I: Integer;
2633 begin
2634 Result := 0;
2635 for I := 0 to Offer.nDeliver + Offer.nCost - 1 do
2636 if Offer.Price[I] and $FFFF0000 = Cardinal(PriceType) then
2637 Inc(Result);
2638 end;
2639
2640{ procedure UpdateBorderHelper;
2641 var
2642 X, Y, Loc, Loc1, dx, dy, ObserveMask: Integer;
2643 begin
2644 ObserveMask:=3 shl (2*pTurn);
2645 for X:=0 to lx-1 do for Y:=0 to ly shr 1-1 do
2646 begin
2647 Loc:=lx*(Y*2)+X;
2648 if ObserveLevel[Loc] and ObserveMask<>0 then
2649 begin
2650 for dy:=0 to 1 do for dx:=0 to 1 do
2651 begin
2652 Loc1:=(Loc+dx-1+lx) mod lx +lx*((Y+dy)*2-1);
2653 if (Loc1>=0) and (Loc1<MapSize)
2654 and (ObserveLevel[Loc1] and ObserveMask<>0) then
2655 if RealMap[Loc1] and $78000000=RealMap[Loc] and $78000000 then
2656 begin
2657 RW[pTurn].BorderHelper[Loc]:=RW[pTurn].BorderHelper[Loc] and not (1 shl (dy*2+dx));
2658 RW[pTurn].BorderHelper[Loc1]:=RW[pTurn].BorderHelper[Loc1] and not (8 shr (dy*2+dx))
2659 end
2660 else
2661 begin
2662 RW[pTurn].BorderHelper[Loc]:=RW[pTurn].BorderHelper[Loc] or (1 shl (dy*2+dx));
2663 RW[pTurn].BorderHelper[Loc1]:=RW[pTurn].BorderHelper[Loc1] or (8 shr (dy*2+dx));
2664 end
2665 end
2666 end
2667 end
2668 end; }
2669
2670const
2671 ptSelect = 0;
2672 ptTrGoods = 1;
2673 ptUn = 2;
2674 ptCaravan = 3;
2675 ptImp = 4;
2676 ptWonder = 6;
2677 ptShip = 7;
2678 ptInvalid = 8;
2679
2680 function ProjectType(Project: Integer): Integer;
2681 begin
2682 if Project and cpCompleted <> 0 then
2683 Result := ptSelect
2684 else if Project and (cpImp + cpIndex) = cpImp + imTrGoods then
2685 Result := ptTrGoods
2686 else if Project and cpImp = 0 then
2687 if RW[Player].Model[Project and cpIndex].Kind = mkCaravan then
2688 Result := ptCaravan
2689 else
2690 Result := ptUn
2691 else if Project and cpIndex >= nImp then
2692 Result := ptInvalid
2693 else if Imp[Project and cpIndex].Kind = ikWonder then
2694 Result := ptWonder
2695 else if Imp[Project and cpIndex].Kind = ikShipPart then
2696 Result := ptShip
2697 else
2698 Result := ptImp;
2699 end;
2700
2701var
2702 D, I, J, p1, p2, pt0, pt1, uix1, cix1, Loc0, Loc1, dx, dy, NewCap, MinCap,
2703 MaxCap, CapWeight, Cost, NextProd, Preq, TotalFood, TotalProd, CheckSum,
2704 StopTurn, FutureMCost, NewProject, OldImp, mix, V8, V21, AStr, DStr,
2705 ABaseDamage, DBaseDamage: Integer;
2706 CityReport: TCityReport;
2707 FormerCLState: TCmdListState;
2708 Adjacent: TVicinity8Loc;
2709 Radius: TVicinity21Loc;
2710 ShowShipChange: TShowShipChange;
2711 ShowNegoData: TShowNegoData;
2712 Logged, Ok, HasShipChanged, AllHumansDead, OfferFullySupported: Boolean;
2713begin
2714 if Command = sTurn then
2715 begin
2716 p2 := -1;
2717 for p1 := 0 to nPl - 1 do
2718 if (p1 <> Player) and (1 shl p1 and GWatching <> 0) then
2719 CallPlayer(cShowTurnChange, p1, p2);
2720 end;
2721
2722 Assert(MapSize = lx * ly);
2723 Assert(Command and (sctMask or sExecute) <> sctInternal or sExecute);
2724 // not for internal commands
2725 if (Command < 0) or (Command >= $10000) then
2726 begin
2727 Result := eUnknown;
2728 Exit;
2729 end;
2730
2731 if (Player < 0) or (Player >= nPl) or
2732 ((Command and (sctMask or sExecute) <> sctInfo) and
2733 ((Subject < 0) or (Subject >= $1000))) then
2734 begin
2735 Result := eInvalid;
2736 Exit;
2737 end;
2738
2739 if (1 shl Player and (GAlive or GWatching) = 0) and
2740 not ((Command = sTurn) or (Command = sBreak) or (Command = sResign) or
2741 (Command = sGetAIInfo) or (Command = sGetAICredits) or
2742 (Command = sGetVersion) or (Command and $FF0F = sGetChart)) then
2743 begin
2744 PutMessage(1 shl 16 + 1, Format('NOT Alive: %d', [Player]));
2745 Result := eNoTurn;
2746 Exit;
2747 end;
2748
2749 Result := eOK;
2750
2751 // check if command allowed now
2752 if (Mode = moPlaying) and not ((Command >= cClientEx) or (Command = sMessage)
2753 or (Command = sSetDebugMap) or (Command = sGetDebugMap) or
2754 (Command = sGetAIInfo) or (Command = sGetAICredits) or
2755 (Command = sGetVersion) or (Command = sGetTechCost) or
2756 (Command = sGetDefender) or (Command = sGetUnitReport) or
2757 (Command = sGetCityReport) or (Command = sGetCityTileInfo) or
2758 (Command = sGetCity) or (Command = sGetEnemyCityReport) or
2759 (Command = sGetEnemyCityAreaInfo) or (Command = sGetCityReportNew) or
2760 (Command and $FF0F = sGetChart) or (Command and $FF0F = sSetAttitude))
2761 // commands always allowed
2762 and not ((Player = pTurn) and (Command < $1000))
2763 // info request always allowed for pTurn
2764 and ((pDipActive < 0) and (Player <> pTurn) // not his turn
2765 or (pDipActive >= 0) and (Player <> pDipActive)
2766 // not active in negotiation mode
2767 or (pDipActive >= 0) and (Command and sctMask <> sctEndClient)) then
2768 // no nego command
2769 begin
2770 PutMessage(1 shl 16 + 1, Format('No Turn: %d calls %x',
2771 [Player, Command shr 4]));
2772 Result := eNoTurn;
2773 Exit;
2774 end;
2775
2776 // do not use EXIT hereafter!
2777
2778{$IFOPT O-}
2779 HandoverStack[nHandoverStack] := Player + $1000;
2780 HandoverStack[nHandoverStack + 1] := Command;
2781 Inc(nHandoverStack, 2);
2782
2783 InvalidTreatyMap := 0;
2784 // new command, sIntExpandTerritory of previous command was processed
2785{$ENDIF}
2786 if (Mode = moPlaying) and (Command >= sExecute) and
2787 ((Command and sctMask <> sctEndClient) or (Command = sTurn)) and
2788 (Command and sctMask <> sctModel) and (Command <> sCancelTreaty) and
2789 (Command <> sSetCityTiles) and (Command <> sBuyCityProject) and
2790 ((Command < cClientEx) or ProcessClientData[Player]) then
2791 begin { log command }
2792 FormerCLState := CL.State;
2793 CL.Put(Command, Player, Subject, @Data);
2794 Logged := True;
2795 end
2796 else
2797 Logged := False;
2798
2799 case Command of
2800
2801 {
2802 Info Request Commands
2803 ____________________________________________________________________
2804 }
2805 sMessage:
2806 bix[0].Client(cDebugMessage, Subject, Data);
2807
2808 sSetDebugMap:
2809 DebugMap[Player] := @Data;
2810
2811 sGetDebugMap:
2812 Pointer(Data) := DebugMap[Subject];
2813
2814 { sChangeSuperView:
2815 if Difficulty[Player]=0 then
2816 begin
2817 for I:=0 to nBrain-1 do if Brain[I].Initialized then
2818 CallClient(I, cShowSuperView, Subject)
2819 end
2820 else Result:=eInvalid; }
2821
2822 sRefreshDebugMap:
2823 bix[0].Client(cRefreshDebugMap, -1, Player);
2824
2825 sGetChart .. sGetChart + (nStat - 1) shl 4:
2826 if (Subject >= 0) and (Subject < nPl) and Assigned(bix[Subject]) then
2827 begin
2828 StopTurn := 0;
2829 if (Difficulty[Player] = 0) or (GTestFlags and tfUncover <> 0)
2830 // supervisor
2831 or (Subject = Player) // own chart
2832 or (GWinner > 0) // game end chart
2833 or (1 shl Subject and GAlive = 0) then // chart of extinct nation
2834 if Subject > Player then
2835 StopTurn := GTurn
2836 else
2837 StopTurn := GTurn + 1
2838 else if RW[Player].Treaty[Subject] > trNoContact then
2839 if Command shr 4 and $F = stMil then
2840 StopTurn := RW[Player].EnemyReport[Subject].TurnOfMilReport + 1
2841 else
2842 StopTurn := RW[Player].EnemyReport[Subject].TurnOfCivilReport + 1;
2843 Move(Stat[Command shr 4 and $F, Subject]^, Data,
2844 StopTurn * SizeOf(Integer));
2845 FillChar(TChart(Data)[StopTurn], (GTurn - StopTurn) *
2846 SizeOf(Integer), 0);
2847 end
2848 else
2849 Result := eInvalid;
2850
2851 sGetTechCost:
2852 Integer(Data) := TechCost(Player);
2853
2854 sGetAIInfo:
2855 if AIInfo[Subject] = '' then
2856 PChar(Data) := nil
2857 else
2858 PChar(Data) := @AIInfo[Subject][1];
2859
2860 sGetAICredits:
2861 if AICredits = '' then
2862 PChar(Data) := nil
2863 else
2864 PChar(Data) := @AICredits[1];
2865
2866 sGetVersion:
2867 Integer(Data) := CevoVersion;
2868
2869 sGetGameChanged:
2870 if Player <> 0 then
2871 Result := eInvalid
2872 else if (CL <> nil) and (CL.State.nLog = nLogOpened) and
2873 (CL.State.MoveCode = 0) and not HasCityTileChanges and
2874 not HasChanges(Player) then
2875 Result := eNotChanged;
2876
2877 sGetTileInfo:
2878 if (Subject >= 0) and (Subject < MapSize) then
2879 Result := GetTileInfo(Player, -2, Subject, TTileInfo(Data))
2880 else
2881 Result := eInvalid;
2882
2883 sGetCityTileInfo:
2884 if (Subject >= 0) and (Subject < MapSize) then
2885 Result := GetTileInfo(Player, -1, Subject, TTileInfo(Data))
2886 else
2887 Result := eInvalid;
2888
2889 sGetHypoCityTileInfo:
2890 if (Subject >= 0) and (Subject < MapSize) then
2891 begin
2892 if (TTileInfo(Data).ExplCity < 0) or
2893 (TTileInfo(Data).ExplCity >= RW[Player].nCity) then
2894 Result := eInvalid
2895 else if ObserveLevel[Subject] shr (Player * 2) and 3 = 0 then
2896 Result := eNoPreq
2897 else
2898 Result := GetTileInfo(Player, TTileInfo(Data).ExplCity, Subject,
2899 TTileInfo(Data));
2900 end
2901 else
2902 Result := eInvalid;
2903
2904 sGetJobProgress:
2905 if (Subject >= 0) and (Subject < MapSize) then
2906 begin
2907 if ObserveLevel[Subject] shr (Player * 2) and 3 = 0 then
2908 Result := eNoPreq
2909 else
2910 Result := GetJobProgress(Player, Subject, TJobProgressData(Data));
2911 end
2912 else
2913 Result := eInvalid;
2914
2915 sGetModels:
2916 if (GTestFlags and tfUncover <> 0) or (Difficulty[Player] = 0)
2917 then { supervisor only command }
2918 begin
2919 for p1 := 0 to nPl - 1 do
2920 if (p1 <> Player) and (1 shl p1 and GAlive <> 0) then
2921 for mix := 0 to RW[p1].nModel - 1 do
2922 TellAboutModel(Player, p1, mix);
2923 end
2924 else
2925 Result := eInvalid;
2926
2927 sGetUnits:
2928 if (Subject >= 0) and (Subject < MapSize) and
2929 (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) then
2930 Integer(Data) := GetUnitStack(Player, Subject)
2931 else
2932 Result := eNoPreq;
2933
2934 sGetDefender:
2935 if (Subject >= 0) and (Subject < MapSize) and (Occupant[Subject] = Player)
2936 then
2937 Strongest(Subject, Integer(Data), D, I, J)
2938 else
2939 Result := eInvalid;
2940
2941 sGetBattleForecast, sGetBattleForecastEx:
2942 if (Subject >= 0) and (Subject < MapSize) and
2943 (ObserveLevel[Subject] and (3 shl (Player * 2)) > 0) then
2944 with TBattleForecast(Data) do
2945 if (1 shl pAtt and GAlive <> 0) and (mixAtt >= 0) and
2946 (mixAtt < RW[pAtt].nModel) and
2947 ((pAtt = Player) or (RWemix[Player, pAtt, mixAtt] >= 0)) then
2948 begin
2949 Result := GetBattleForecast(Subject, TBattleForecast(Data), uix1,
2950 cix1, AStr, DStr, ABaseDamage, DBaseDamage);
2951 if Command = sGetBattleForecastEx then
2952 begin
2953 TBattleForecastEx(Data).AStr := (AStr + 200) div 400;
2954 TBattleForecastEx(Data).DStr := (DStr + 200) div 400;
2955 TBattleForecastEx(Data).ABaseDamage := ABaseDamage;
2956 TBattleForecastEx(Data).DBaseDamage := DBaseDamage;
2957 end;
2958 if Result = eOK then
2959 Result := eInvalid; // no enemy unit there!
2960 end
2961 else
2962 Result := eInvalid
2963 else
2964 Result := eInvalid;
2965
2966 sGetUnitReport:
2967 if (Subject < 0) or (Subject >= RW[Player].nUn) or
2968 (RW[Player].Un[Subject].Loc < 0) then
2969 Result := eInvalid
2970 else
2971 GetUnitReport(Player, Subject, TUnitReport(Data));
2972
2973 sGetMoveAdvice:
2974 if (Subject < 0) or (Subject >= RW[Player].nUn) or
2975 (RW[Player].Un[Subject].Loc < 0) then
2976 Result := eInvalid
2977 else
2978 Result := GetMoveAdvice(Player, Subject, TMoveAdviceData(Data));
2979
2980 sGetPlaneReturn:
2981 if (Subject < 0) or (Subject >= RW[Player].nUn) or
2982 (RW[Player].Un[Subject].Loc < 0) or
2983 (RW[Player].Model[RW[Player].Un[Subject].mix].Domain <> dAir) then
2984 Result := eInvalid
2985 else
2986 begin
2987 if CanPlaneReturn(Player, Subject, TPlaneReturnData(Data)) then
2988 Result := eOK
2989 else
2990 Result := eNoWay;
2991 end;
2992
2993 sGetCity:
2994 if (Subject >= 0) and (Subject < MapSize) and
2995 (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
2996 (RealMap[Subject] and fCity <> 0) then
2997 with TGetCityData(Data) do
2998 begin
2999 Owner := Player;
3000 SearchCity(Subject, Owner, cix1);
3001 C := RW[Owner].City[cix1];
3002 if (Owner <> Player) and (C.Project and cpImp = 0) then
3003 TellAboutModel(Player, Owner, C.Project and cpIndex);
3004 end
3005 else
3006 Result := eInvalid;
3007
3008 sGetCityReport:
3009 if (Subject < 0) or (Subject >= RW[Player].nCity) or
3010 (RW[Player].City[Subject].Loc < 0) then
3011 Result := eInvalid
3012 else
3013 Result := GetCityReport(Player, Subject, TCityReport(Data));
3014
3015 sGetCityReportNew:
3016 if (Subject < 0) or (Subject >= RW[Player].nCity) or
3017 (RW[Player].City[Subject].Loc < 0) then
3018 Result := eInvalid
3019 else
3020 GetCityReportNew(Player, Subject, TCityReportNew(Data));
3021
3022 sGetCityAreaInfo:
3023 if (Subject < 0) or (Subject >= RW[Player].nCity) or
3024 (RW[Player].City[Subject].Loc < 0) then
3025 Result := eInvalid
3026 else
3027 GetCityAreaInfo(Player, RW[Player].City[Subject].Loc,
3028 TCityAreaInfo(Data));
3029
3030 sGetEnemyCityReport:
3031 if (Subject >= 0) and (Subject < MapSize) and
3032 (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
3033 (RealMap[Subject] and fCity <> 0) then
3034 begin
3035 p1 := Occupant[Subject];
3036 if p1 < 0 then
3037 p1 := 1;
3038 SearchCity(Subject, p1, cix1);
3039 TCityReport(Data).HypoTiles := -1;
3040 TCityReport(Data).HypoTax := -1;
3041 TCityReport(Data).HypoLux := -1;
3042 GetCityReport(p1, cix1, TCityReport(Data));
3043 end
3044 else
3045 Result := eInvalid;
3046
3047 sGetEnemyCityReportNew:
3048 if (Subject >= 0) and (Subject < MapSize) and
3049 (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
3050 (RealMap[Subject] and fCity <> 0) then
3051 begin
3052 p1 := Occupant[Subject];
3053 if p1 < 0 then
3054 p1 := 1;
3055 SearchCity(Subject, p1, cix1);
3056 TCityReport(Data).HypoTiles := -1;
3057 TCityReport(Data).HypoTax := -1;
3058 TCityReport(Data).HypoLux := -1;
3059 GetCityReportNew(p1, cix1, TCityReportNew(Data));
3060 end
3061 else
3062 Result := eInvalid;
3063
3064 sGetEnemyCityAreaInfo:
3065 if (Subject >= 0) and (Subject < MapSize) and
3066 (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
3067 (RealMap[Subject] and fCity <> 0) then
3068 begin
3069 p1 := Occupant[Subject];
3070 if p1 < 0 then
3071 p1 := 1;
3072 SearchCity(Subject, p1, cix1);
3073 GetCityAreaInfo(p1, Subject, TCityAreaInfo(Data));
3074 end
3075 else
3076 Result := eInvalid;
3077
3078 sGetCityTileAdvice:
3079 if (Subject < 0) or (Subject >= RW[Player].nCity) or
3080 (RW[Player].City[Subject].Loc < 0) then
3081 Result := eInvalid
3082 else
3083 GetCityTileAdvice(Player, Subject, TCityTileAdviceData(Data));
3084
3085 {
3086 Map Editor Commands
3087 ____________________________________________________________________
3088 }
3089 sEditTile:
3090 if Player = 0 then
3091 with TEditTileData(Data) do
3092 EditTile(Loc, NewTile)
3093 else
3094 Result := eInvalid;
3095
3096 sRandomMap:
3097 if (Player = 0) and MapGeneratorAvailable then
3098 begin
3099 CreateElevation;
3100 PreviewElevation := False;
3101 CreateMap(False);
3102 FillChar(ObserveLevel, MapSize * 4, 0);
3103 DiscoverAll(Player, lObserveSuper);
3104 end
3105 else
3106 Result := eInvalid;
3107
3108 sMapGeneratorRequest:
3109 if not MapGeneratorAvailable then
3110 Result := eInvalid;
3111
3112 {
3113 Client Deactivation Commands
3114 ____________________________________________________________________
3115 }
3116 sTurn, sTurn - sExecute:
3117 begin
3118 AllHumansDead := True;
3119 for p1 := 0 to nPl - 1 do
3120 if (1 shl p1 and GAlive <> 0) and (bix[p1].Kind = btTerm) then begin
3121 AllHumansDead := False;
3122 Break;
3123 end;
3124 if (pDipActive >= 0) // still in negotiation mode
3125 or (pTurn = 0) and ((GWinner > 0) or (GTurn = MaxTurn) or
3126 (Difficulty[0] > 0) and AllHumansDead) then // game end reached
3127 Result := eViolation
3128 else if Command >= sExecute then
3129 begin
3130 if Mode = moPlaying then
3131 begin
3132 CL.State := FormerCLState;
3133 LogCityTileChanges;
3134{$IFNDEF SCR}
3135 if pTurn = 0 then
3136 begin
3137 LogChanges;
3138 SaveGame('~' + LogFileName, True);
3139 end;
3140{$ENDIF}
3141 end
3142 else if (Mode = moMovie) and (pTurn = 0) then
3143 CallPlayer(cMovieEndTurn, 0, nil^);
3144 GWatching := GWatching and GAlive or 1;
3145 RW[pTurn].Happened := 0;
3146 uixSelectedTransport := -1;
3147 SpyMission := smSabotageProd;
3148 if 1 shl pTurn and GAlive <> 0 then
3149 begin
3150 // calculate checksum
3151 TotalFood := 0;
3152 TotalProd := 0;
3153 for I := 0 to RW[pTurn].nCity - 1 do
3154 if RW[pTurn].City[I].Loc >= 0 then
3155 begin
3156 Inc(TotalFood, RW[pTurn].City[I].Food);
3157 Inc(TotalProd, RW[pTurn].City[I].Prod);
3158 end;
3159 CheckSum := TotalFood and 7 + TotalProd and 7 shl 3 +
3160 RW[pTurn].Money and 7 shl 6 + Worked[pTurn] div 100 and 7 shl 9;
3161 end
3162 else
3163 CheckSum := 0;
3164
3165 if Mode < moPlaying then // check checksum
3166 begin
3167 if CheckSum <> Subject then
3168 LoadOK := False;
3169 end
3170 else // save checksum
3171 CL.Put(Command, Player, CheckSum, @Data);
3172{$IFDEF TEXTLOG}
3173 CmdInfo := '';
3174 if CheckSum and 7 <> Subject and 7 then
3175 CmdInfo := Format('***ERROR (Food %d) ',
3176 [(CheckSum and 7 - Subject and 7 + 12) mod 8 - 4]) + CmdInfo;
3177 if CheckSum shr 3 and 7 <> Subject shr 3 and 7 then
3178 CmdInfo := '***ERROR (Prod) ' + CmdInfo;
3179 if CheckSum shr 6 and 7 <> Subject shr 6 and 7 then
3180 CmdInfo := '***ERROR (Research) ' + CmdInfo;
3181 if CheckSum shr 9 and 7 <> Subject shr 9 and 7 then
3182 CmdInfo := '***ERROR (Work) ' + CmdInfo;
3183{$ENDIF}
3184 if 1 shl pTurn and GAlive <> 0 then
3185 begin
3186 AfterTurn;
3187 if Mode < moPlaying then
3188 InsertTerritoryUpdateCommands;
3189 // if bix[pTurn]=bixTerm then UpdateBorderHelper;
3190 end;
3191
3192 repeat
3193 pTurn := (pTurn + 1) mod nPl;
3194 if pTurn = 0 then
3195 Inc(GTurn);
3196 if Assigned(bix[pTurn]) and ((1 shl pTurn) and GAlive = 0) then
3197 begin // already made extinct -- continue statistics
3198 Stat[stExplore, pTurn, GTurn] := 0;
3199 Stat[stPop, pTurn, GTurn] := 0;
3200 Stat[stTerritory, pTurn, GTurn] := 0;
3201 Stat[stScience, pTurn, GTurn] := 0;
3202 Stat[stWork, pTurn, GTurn] := 0;
3203 Stat[stMil, pTurn, GTurn] := 0;
3204 end;
3205 until (pTurn = 0) or ((1 shl pTurn and (GAlive or GWatching) <> 0) and
3206 (GWinner = 0));
3207 if (Mode = moLoading_Fast) and
3208 ((GTurn = LoadTurn) or (GTurn = LoadTurn - 1) and (pTurn > 0)) then
3209 Mode := moLoading;
3210 if Mode = moPlaying then
3211 begin
3212 CCCommand := cTurn;
3213 CCPlayer := pTurn;
3214 Notify(ntNextPlayer);
3215 end
3216 else
3217 begin
3218 if GTurn = 0 then
3219 BeforeTurn0
3220 else
3221 BeforeTurn;
3222 if (Mode = moMovie) and (pTurn = 0) then
3223 begin
3224 Inform(pTurn);
3225 CallPlayer(cMovieTurn, 0, nil^);
3226 end;
3227 end;
3228{$IFDEF TEXTLOG}CmdInfo := CmdInfo + Format('---Turn %d P%d---', [GTurn, pTurn]); {$ENDIF}
3229 end;
3230 end; // sTurn
3231
3232 sBreak, sResign, sNextRound, sReload:
3233 if Mode = moMovie then
3234 MovieStopped := True
3235 else
3236 begin
3237 if Command = sReload then
3238 begin
3239 Ok := (Difficulty[0] = 0) and (bix[0].Kind <> btNoTerm) and
3240 (Integer(Data) >= 0) and (Integer(Data) < GTurn);
3241 for p1 := 1 to nPl - 1 do
3242 if bix[p1].Kind = btTerm then begin
3243 Ok := False;
3244 Break;
3245 end;
3246 // allow reload in AI-only games only
3247 end
3248 else
3249 Ok := Player = 0;
3250 if Ok then
3251 begin
3252 if (Command = sBreak) or (Command = sResign) then
3253 Notify(ntBackOn);
3254 for I := 0 to Brains.Count - 1 do
3255 if Brains[I].Initialized then
3256 begin
3257 if Brains[I].Kind = btAI then
3258 Notify(ntDeinitModule, I);
3259 CallClient(I, cBreakGame, nil^);
3260 end;
3261 Notify(ntEndInfo);
3262 if (Command = sBreak) or (Command = sReload) then
3263 begin
3264 LogCityTileChanges;
3265 LogChanges;
3266 SaveGame(LogFileName, False);
3267 end;
3268 DeleteFile(SavePath + '~' + LogFileName);
3269 EndGame;
3270 case Command of
3271 sBreak:
3272 Notify(ntStartGoRefresh);
3273 sResign:
3274 Notify(ntStartGo);
3275 sNextRound:
3276 StartNewGame(SavePath, LogFileName, MapFileName, lx, ly,
3277 LandMass, MaxTurn);
3278 sReload:
3279 LoadGame(SavePath, LogFileName, Integer(Data), False);
3280 end;
3281 end
3282 else
3283 Result := eInvalid;
3284 end;
3285
3286 sAbandonMap, sSaveMap:
3287 if Player = 0 then
3288 begin
3289 if Command = sSaveMap then
3290 SaveMap(MapFileName);
3291 Notify(ntBackOn);
3292 BrainTerm.Client(cBreakGame, -1, nil^);
3293 ReleaseMapEditor;
3294 if Command = sSaveMap then
3295 Notify(ntStartGoRefreshMaps)
3296 else
3297 Notify(ntStartGo);
3298 end
3299 else
3300 Result := eInvalid;
3301
3302 scContact .. scContact + (nPl - 1) shl 4, scContact - sExecute .. scContact
3303 - sExecute + (nPl - 1) shl 4:
3304 if (pDipActive >= 0) or (1 shl (Command shr 4 and $F) and GAlive = 0) then
3305 Result := eInvalid
3306 else if GWinner > 0 then
3307 Result := eViolation // game end reached
3308 else if RW[Player].Treaty[Command shr 4 and $F] = trNoContact then
3309 Result := eNoPreq
3310 else if GTurn < GColdWarStart + ColdWarTurns then
3311 Result := eColdWar
3312 else if RW[Player].Government = gAnarchy then
3313 Result := eAnarchy
3314 else if RW[Command shr 4 and $F].Government = gAnarchy then
3315 begin
3316 Result := eAnarchy;
3317 LastEndClientCommand := scReject; // enable cancel treaty
3318 pContacted := Command shr 4 and $F;
3319 end
3320 else if Command >= sExecute then
3321 begin // contact request
3322 pContacted := Command shr 4 and $F;
3323 pDipActive := pContacted;
3324 Assert(Mode = moPlaying);
3325 Inform(pDipActive);
3326 ChangeClientWhenDone(scContact, pDipActive, pTurn, 4);
3327 end;
3328
3329 scReject, scReject - sExecute:
3330 if LastEndClientCommand and $FF0F = scContact then
3331 begin
3332 if Command >= sExecute then
3333 begin // contact requested and not accepted yet
3334 pDipActive := -1;
3335 Assert(Mode = moPlaying);
3336 ChangeClientWhenDone(cContinue, pTurn, nil^, 0);
3337 end;
3338 end
3339 else
3340 Result := eInvalid;
3341
3342 scDipStart, scDipStart - sExecute:
3343 if LastEndClientCommand and $FF0F = scContact then
3344 begin
3345 if Command >= sExecute then
3346 begin // accept contact
3347 pContacted := pDipActive;
3348 RW[pContacted].EnemyReport[pTurn].Credibility :=
3349 RW[pTurn].Credibility;
3350 pDipActive := pTurn;
3351 Assert(Mode = moPlaying);
3352 IntServer(sIntHaveContact, pTurn, pContacted, nil^);
3353 ChangeClientWhenDone(scDipStart, pDipActive, nil^, 0);
3354 end;
3355 end
3356 else
3357 Result := eInvalid;
3358
3359 scDipNotice, scDipAccept, scDipCancelTreaty, scDipBreak,
3360 scDipNotice - sExecute, scDipAccept - sExecute,
3361 scDipCancelTreaty - sExecute, scDipBreak - sExecute:
3362 if pDipActive >= 0 then
3363 begin
3364 Assert(Mode = moPlaying);
3365 if pDipActive = pTurn then
3366 p1 := pContacted
3367 else
3368 p1 := pTurn;
3369 if (Command and not sExecute = scDipBreak and not sExecute) and
3370 (LastEndClientCommand <> scDipBreak) then // Ok
3371 else if (Command and not sExecute = scDipNotice and not sExecute) and
3372 ((LastEndClientCommand = scDipCancelTreaty) or
3373 (LastEndClientCommand = scDipBreak)) then // Ok
3374 else if (Command and not sExecute = scDipAccept and not sExecute) and
3375 (LastEndClientCommand = scDipOffer) then
3376 with LastOffer do
3377 begin
3378 // check if offer can be accepted
3379 if nDeliver + nCost = 0 then
3380 Result := eOfferNotAcceptable;
3381 for I := 0 to nDeliver + nCost - 1 do
3382 if Price[I] = opChoose then begin
3383 Result := eOfferNotAcceptable;
3384 Break;
3385 end;
3386 for I := 0 to nCost - 1 do
3387 if not PayPrice(pDipActive, p1, Price[nDeliver + I], False) then
3388 Result := eOfferNotAcceptable;
3389 if (Command >= sExecute) and (Result >= rExecuted) then
3390 begin
3391 IntServer(sIntPayPrices + nDeliver + nCost, p1, pDipActive,
3392 LastOffer);
3393 // CheckContact;
3394
3395 // tell other players about ship part trades
3396 HasShipChanged := False;
3397 FillChar(ShowShipChange, SizeOf(ShowShipChange), 0);
3398 for I := 0 to nDeliver + nCost - 1 do
3399 if Price[I] and opMask = opShipParts then
3400 begin
3401 HasShipChanged := True;
3402 if I >= nDeliver then
3403 begin // p1 has demanded from pDipActive
3404 ShowShipChange.Ship1Change[Price[I] shr 16 and 3] :=
3405 +Integer(Price[I] and $FFFF);
3406 ShowShipChange.Ship2Change[Price[I] shr 16 and 3] :=
3407 -Integer(Price[I] and $FFFF);
3408 end
3409 else
3410 begin // p1 has delivered to pDipActive
3411 ShowShipChange.Ship1Change[Price[I] shr 16 and 3] :=
3412 -Integer(Price[I] and $FFFF);
3413 ShowShipChange.Ship2Change[Price[I] shr 16 and 3] :=
3414 +Integer(Price[I] and $FFFF);
3415 end;
3416 end;
3417 if HasShipChanged then
3418 begin
3419 ShowShipChange.Reason := scrTrade;
3420 ShowShipChange.Ship1Owner := p1;
3421 ShowShipChange.Ship2Owner := pDipActive;
3422 for p2 := 0 to nPl - 1 do
3423 if (p2 <> p1) and (p2 <> pDipActive) and
3424 (1 shl p2 and (GAlive or GWatching) <> 0) then
3425 begin
3426 Move(GShip, RW[p2].Ship, SizeOf(GShip));
3427 if 1 shl p2 and GWatching <> 0 then
3428 CallPlayer(cShowShipChange, p2, ShowShipChange);
3429 end;
3430 end;
3431 end;
3432 end
3433 else if (Command and not sExecute = scDipCancelTreaty and not sExecute)
3434 and (RW[pDipActive].Treaty[p1] >= trPeace) then
3435 begin
3436 if (ServerVersion[pDipActive] >= $010100) and
3437 (GTurn < RW[pDipActive].LastCancelTreaty[p1] + CancelTreatyTurns)
3438 then
3439 Result := eCancelTreatyRush
3440 else if Command >= sExecute then
3441 begin
3442 IntServer(sIntCancelTreaty, pDipActive, p1, nil^);
3443 for p2 := 0 to nPl - 1 do
3444 if (p2 <> p1) and (1 shl p2 and PeaceEnded <> 0) then
3445 begin
3446 I := p1 shl 4 + pDipActive;
3447 CallPlayer(cShowSupportAllianceAgainst, p2, I);
3448 end;
3449 for p2 := 0 to nPl - 1 do
3450 if (p2 <> p1) and (1 shl p2 and PeaceEnded <> 0) then
3451 begin
3452 I := p2;
3453 CallPlayer(cShowCancelTreatyByAlliance, pDipActive, I);
3454 end;
3455 end;
3456 end
3457 else
3458 Result := eInvalid;
3459 if (Command >= sExecute) and (Result >= rExecuted) then
3460 if LastEndClientCommand = scDipBreak then
3461 begin // break negotiation
3462 pDipActive := -1;
3463 CallPlayer(cShowEndContact, pContacted, nil^);
3464 ChangeClientWhenDone(cContinue, pTurn, nil^, 0);
3465 end
3466 else
3467 begin
3468 if (GTestFlags and tfUncover <> 0) or (Difficulty[0] = 0) then
3469 with ShowNegoData do
3470 begin // display negotiation in log window
3471 pSender := pDipActive;
3472 pTarget := p1;
3473 Action := Command;
3474 bix[0].Client(cShowNego, 1 shl 16 + 3, ShowNegoData);
3475 end;
3476 pDipActive := p1;
3477 ChangeClientWhenDone(Command, pDipActive, nil^, 0);
3478 end;
3479 end
3480 else
3481 Result := eInvalid;
3482
3483 scDipOffer, scDipOffer - sExecute:
3484 if (pDipActive >= 0) and (LastEndClientCommand <> scDipCancelTreaty) and
3485 (LastEndClientCommand <> scDipBreak) then
3486 if (LastEndClientCommand = scDipOffer) and
3487 (LastOffer.nDeliver + LastOffer.nCost + TOffer(Data).nDeliver +
3488 TOffer(Data).nCost = 0) then
3489 begin
3490 if Command >= sExecute then
3491 begin // agreed discussion end
3492 pDipActive := -1;
3493 CallPlayer(cShowEndContact, pContacted, nil^);
3494 Assert(Mode = moPlaying);
3495 ChangeClientWhenDone(cContinue, pTurn, nil^, 0);
3496 end;
3497 end
3498 else
3499 begin
3500 // check if offer can be made
3501 if pDipActive = pTurn then
3502 p1 := pContacted
3503 else
3504 p1 := pTurn;
3505 if RW[pDipActive].Treaty[p1] < trPeace then
3506 begin // no tribute allowed!
3507 for I := 0 to TOffer(Data).nDeliver + TOffer(Data).nCost - 1 do
3508 if (TOffer(Data).Price[I] and opMask = opTribute) then begin
3509 Result := eInvalidOffer;
3510 Break;
3511 end;
3512 for I := 0 to TOffer(Data).nDeliver + TOffer(Data).nCost - 1 do
3513 if (TOffer(Data).Price[I] = opTreaty + trPeace) then begin
3514 Result := eOK;
3515 Break;
3516 end;
3517 end;
3518 for I := 0 to TOffer(Data).nDeliver - 1 do
3519 if (TOffer(Data).Price[I] <> opChoose) and
3520 not PayPrice(pDipActive, p1, TOffer(Data).Price[I], False) then
3521 Result := eInvalidOffer;
3522 if CountPrice(TOffer(Data), opTreaty) > 1 then
3523 Result := eInvalidOffer;
3524 for I := 0 to nShipPart - 1 do
3525 if CountPrice(TOffer(Data), opShipParts + I shl 16) > 1 then begin
3526 Result := eInvalidOffer;
3527 Break;
3528 end;
3529 if CountPrice(TOffer(Data), opMoney) > 1 then
3530 Result := eInvalidOffer;
3531 if CountPrice(TOffer(Data), opTribute) > 1 then
3532 Result := eInvalidOffer;
3533 case CountPrice(TOffer(Data), opChoose) of
3534 0:
3535 ;
3536 1:
3537 if (TOffer(Data).nCost = 0) or (TOffer(Data).nDeliver = 0) then
3538 Result := eInvalidOffer;
3539 else
3540 Result := eInvalidOffer;
3541 end;
3542
3543 // !!! check here if cost can be demanded
3544
3545 if (Command >= sExecute) and (Result >= rExecuted) then
3546 begin
3547 OfferFullySupported := (TOffer(Data).nDeliver <= 2) and
3548 (TOffer(Data).nCost <= 2); // >2 no more allowed
3549 for I := 0 to TOffer(Data).nDeliver + TOffer(Data).nCost - 1 do
3550 begin
3551 if TOffer(Data).Price[I] and opMask = opTribute then
3552 OfferFullySupported := False;
3553 // tribute no more part of the game
3554 if (TOffer(Data).Price[I] and opMask = opTreaty) and
3555 (TOffer(Data).Price[I] - opTreaty <= RW[pDipActive].Treaty[p1])
3556 then
3557 OfferFullySupported := False;
3558 // agreed treaty end no more part of the game
3559 if TOffer(Data).Price[I] = opTreaty + trCeaseFire then
3560 OfferFullySupported := False;
3561 // ceasefire no more part of the game
3562 end;
3563 if not OfferFullySupported then
3564 begin
3565 // some elements have been removed from the game -
3566 // automatically respond will null-offer
3567 LastOffer.nDeliver := 0;
3568 LastOffer.nCost := 0;
3569 ChangeClientWhenDone(scDipOffer, pDipActive, LastOffer,
3570 SizeOf(LastOffer));
3571 end
3572 else
3573 begin
3574 if (GTestFlags and tfUncover <> 0) or (Difficulty[0] = 0) then
3575 with ShowNegoData do
3576 begin // display negotiation in log window
3577 pSender := pDipActive;
3578 pTarget := p1;
3579 Action := Command;
3580 Offer := TOffer(Data);
3581 bix[0].Client(cShowNego, 1 shl 16 + 3, ShowNegoData);
3582 end;
3583 LastOffer := TOffer(Data);
3584 // show offered things to receiver
3585 for I := 0 to LastOffer.nDeliver - 1 do
3586 ShowPrice(pDipActive, p1, LastOffer.Price[I]);
3587 pDipActive := p1;
3588 Assert(Mode = moPlaying);
3589 ChangeClientWhenDone(scDipOffer, pDipActive, LastOffer,
3590 SizeOf(LastOffer));
3591 end;
3592 end;
3593 end
3594 else
3595 Result := eInvalid;
3596
3597 {
3598 General Commands
3599 ____________________________________________________________________
3600 }
3601 sClearTestFlag:
3602 if Player = 0 then
3603 begin
3604{$IFDEF TEXTLOG}CmdInfo := Format('ClearTestFlag %x', [Subject]); {$ENDIF}
3605 ClearTestFlags(Subject);
3606 end
3607 else
3608 Result := eInvalid;
3609
3610 sSetTestFlag:
3611 if Player = 0 then
3612 begin
3613{$IFDEF TEXTLOG}CmdInfo := Format('SetTestFlag %x', [Subject]); {$ENDIF}
3614 SetTestFlags(Player, Subject);
3615 // CheckContact;
3616 end
3617 else
3618 Result := eInvalid;
3619
3620 sSetGovernment, sSetGovernment - sExecute:
3621 begin
3622{$IFDEF TEXTLOG}CmdInfo := Format('SetGovernment P%d: %d', [Player, Subject]); {$ENDIF}
3623 if RW[Player].Happened and phChangeGov = 0 then
3624 Result := eViolation
3625 else if RW[Player].Government = Subject then
3626 Result := eNotChanged
3627 else if (Subject >= nGov) then
3628 Result := eInvalid
3629 else if (Subject >= gMonarchy) and
3630 (RW[Player].Tech[GovPreq[Subject]] < tsApplicable) then
3631 Result := eNoPreq
3632 else if Command >= sExecute then
3633 begin
3634 RW[Player].Government := Subject;
3635 for p1 := 0 to nPl - 1 do
3636 if (p1 <> Player) and ((GAlive or GWatching) and (1 shl p1) <> 0)
3637 then
3638 RW[p1].EnemyReport[Player].Government := Subject;
3639 end;
3640 end;
3641
3642 sSetRates, sSetRates - sExecute:
3643 begin
3644{$IFDEF TEXTLOG}CmdInfo := Format('SetRates P%d: %d/%d', [Player, Subject and $F * 10, Subject shr 4 * 10]); {$ENDIF}
3645 if Subject and $F + Subject shr 4 > 10 then
3646 Result := eInvalid
3647 else if (RW[Player].TaxRate = Subject and $F * 10) and
3648 (RW[Player].LuxRate = Subject shr 4 * 10) then
3649 Result := eNotChanged
3650 else if Command >= sExecute then
3651 begin
3652 RW[Player].TaxRate := Subject and $F * 10;
3653 RW[Player].LuxRate := Subject shr 4 * 10;
3654 end;
3655 end;
3656
3657 sRevolution:
3658 begin
3659{$IFDEF TEXTLOG}CmdInfo := Format('Revolution P%d', [Player]); {$ENDIF}
3660 if RW[Player].Government = gAnarchy then
3661 Result := eInvalid
3662 else
3663 begin
3664 RW[Player].Government := gAnarchy;
3665 for p1 := 0 to nPl - 1 do
3666 if (p1 <> Player) and ((GAlive or GWatching) and (1 shl p1) <> 0)
3667 then
3668 RW[p1].EnemyReport[Player].Government := gAnarchy;
3669 RW[Player].AnarchyStart := GTurn;
3670 end;
3671 end;
3672
3673 sSetResearch, sSetResearch - sExecute:
3674 with RW[Player] do
3675 begin
3676{$IFDEF TEXTLOG}CmdInfo := Format('SetResearch P%d: %d', [Player, Subject]);
3677 {$ENDIF}
3678 if (Happened and phTech <> 0) and
3679 ((Subject < nAdv) or (Subject = adMilitary)) then
3680 begin
3681 if (Mode = moPlaying) and (Subject = adMilitary) and
3682 (DevModelTurn[Player] <> GTurn) then
3683 Result := eNoModel
3684 else if Subject <> adMilitary then
3685 begin
3686 if Subject = futComputingTechnology then
3687 begin
3688 if Tech[Subject] >= MaxFutureTech_Computing then
3689 Result := eInvalid;
3690 end
3691 else if Subject in FutureTech then
3692 begin
3693 if Tech[Subject] >= MaxFutureTech then
3694 Result := eInvalid;
3695 end
3696 else if Tech[Subject] >= tsApplicable then
3697 Result := eInvalid; // already discovered
3698 if Tech[Subject] <> tsSeen then // look if preqs met
3699 if AdvPreq[Subject, 2] <> preNone then
3700 begin // 2 of 3 required
3701 I := 0;
3702 for J := 0 to 2 do
3703 if Tech[AdvPreq[Subject, J]] >= tsApplicable then
3704 Inc(I);
3705 if I < 2 then
3706 Result := eNoPreq;
3707 end
3708 else if (AdvPreq[Subject, 0] <> preNone) and
3709 (Tech[AdvPreq[Subject, 0]] < tsApplicable) or
3710 (AdvPreq[Subject, 1] <> preNone) and
3711 (Tech[AdvPreq[Subject, 1]] < tsApplicable) then
3712 Result := eNoPreq;
3713 end;
3714 if (Result = eOK) and (Command >= sExecute) then
3715 begin
3716 if (Mode = moPlaying) and (Subject = adMilitary) then
3717 IntServer(sIntSetDevModel, Player, 0, DevModel.Kind);
3718 // save DevModel, because sctModel commands are not Logged
3719 ResearchTech := Subject;
3720 end;
3721 end
3722 else
3723 Result := eViolation;
3724 end;
3725
3726 sStealTech, sStealTech - sExecute:
3727 begin
3728{$IFDEF TEXTLOG}CmdInfo := Format('StealTech P%d: %d', [Player, Subject]);
3729 {$ENDIF}
3730 if RW[Player].Happened and phStealTech = 0 then
3731 Result := eInvalid
3732 else if (Subject >= nAdv) or (Subject in FutureTech) or
3733 (RW[Player].Tech[Subject] >= tsSeen) or
3734 (RW[GStealFrom].Tech[Subject] < tsApplicable) then
3735 Result := eInvalid
3736 else if Command >= sExecute then
3737 begin
3738 SeeTech(Player, Subject);
3739 Dec(RW[Player].Happened, phStealTech);
3740 end;
3741 end;
3742
3743 sSetAttitude .. sSetAttitude + (nPl - 1) shl 4,
3744 sSetAttitude - sExecute .. sSetAttitude - sExecute + (nPl - 1) shl 4:
3745 begin
3746 p1 := Command shr 4 and $F;
3747{$IFDEF TEXTLOG}CmdInfo := Format('SetAttitude P%d to P%d: %d', [Player, p1, Subject]); {$ENDIF}
3748 if (Subject >= nAttitude) or (p1 >= nPl) or
3749 (RW[Player].EnemyReport[p1] = nil) then
3750 Result := eInvalid
3751 else if RW[Player].Treaty[p1] = trNoContact then
3752 Result := eNoPreq
3753 else if RW[Player].Attitude[p1] = Subject then
3754 Result := eNotChanged
3755 else if Command >= sExecute then
3756 begin
3757 RW[Player].Attitude[p1] := Subject;
3758 RW[p1].EnemyReport[Player].Attitude := Subject;
3759 end;
3760 end;
3761
3762 sCancelTreaty, sCancelTreaty - sExecute:
3763 if (LastEndClientCommand <> scReject) or
3764 (RW[Player].Treaty[pContacted] < trPeace) then
3765 Result := eInvalid
3766 else if (ServerVersion[Player] >= $010100) and
3767 (GTurn < RW[Player].LastCancelTreaty[pContacted] + CancelTreatyTurns)
3768 then
3769 Result := eCancelTreatyRush
3770 else if Command >= sExecute then
3771 begin
3772 CallPlayer(cShowCancelTreaty, pContacted, Player);
3773 IntServer(sIntCancelTreaty, Player, pContacted, nil^);
3774 for p2 := 0 to nPl - 1 do
3775 if (p2 <> pContacted) and (1 shl p2 and PeaceEnded <> 0) then
3776 begin
3777 I := pContacted shl 4 + Player;
3778 CallPlayer(cShowSupportAllianceAgainst, p2, I);
3779 end;
3780 for p2 := 0 to nPl - 1 do
3781 if (p2 <> pContacted) and (1 shl p2 and PeaceEnded <> 0) then
3782 begin
3783 I := p2;
3784 CallPlayer(cShowCancelTreatyByAlliance, Player, I);
3785 end;
3786 LastEndClientCommand := sTurn;
3787 end;
3788
3789 {
3790 Model Related Commands
3791 ____________________________________________________________________
3792 }
3793 sCreateDevModel, sCreateDevModel - sExecute:
3794 begin
3795{$IFDEF TEXTLOG}CmdInfo := Format('CreateDevModel P%d', [Player]); {$ENDIF}
3796 if Subject >= 4 then
3797 Result := eInvalid
3798 else if (upgrade[Subject, 0].Preq <> preNone) and
3799 (RW[Player].Tech[upgrade[Subject, 0].Preq] < tsApplicable) then
3800 Result := eNoPreq
3801 else if Command >= sExecute then
3802 begin
3803 with RW[Player].DevModel do
3804 begin
3805 Domain := Subject;
3806 MStrength := 0;
3807 MTrans := 0;
3808 MCost := 0;
3809 Upgrades := 0;
3810 FutureMCost := 0;
3811 for I := 0 to nUpgrade - 1 do
3812 with upgrade[Domain, I] do
3813 if (Preq = preNone) or (Preq >= 0) and
3814 ((RW[Player].Tech[Preq] >= tsApplicable) or
3815 (Preq in FutureTech) and (RW[Player].Tech[Preq] >= 0)) then
3816 begin
3817 if Preq in FutureTech then
3818 begin
3819 J := RW[Player].Tech[Preq];
3820 Inc(FutureMCost, J * Cost);
3821 end
3822 else
3823 begin
3824 J := 1;
3825 if Cost > MCost then
3826 MCost := Cost;
3827 end;
3828 Inc(Upgrades, 1 shl I);
3829 Inc(MStrength, J * Strength);
3830 Inc(MTrans, J * Trans);
3831 end;
3832 Inc(MCost, FutureMCost);
3833 FillChar(Cap, SizeOf(Cap), 0);
3834 Cap[mcOffense] := 2;
3835 Cap[mcDefense] := 1;
3836 for I := 0 to nFeature - 1 do
3837 with Feature[I] do
3838 if (1 shl Domain and Domains <> 0) and
3839 ((Preq = preNone) or (Preq = preSun) and
3840 (GWonder[woSun].EffectiveOwner = Player) or (Preq >= 0) and
3841 (RW[Player].Tech[Preq] >= tsApplicable)) and (I in AutoFeature)
3842 then
3843 Cap[I] := 1;
3844 MaxWeight := 5;
3845 if (WeightPreq7[Domain] <> preNA) and
3846 (RW[Player].Tech[WeightPreq7[Domain]] >= tsApplicable) then
3847 MaxWeight := 7;
3848 if (WeightPreq10[Domain] <> preNA) and
3849 (RW[Player].Tech[WeightPreq10[Domain]] >= tsApplicable) then
3850 if Domain = dSea then
3851 MaxWeight := 9
3852 else
3853 MaxWeight := 10;
3854 end;
3855 CalculateModel(RW[Player].DevModel);
3856 DevModelTurn[Player] := GTurn;
3857 end
3858 end;
3859
3860 sSetDevModelCap .. sSetDevModelCap + $3F0,
3861 sSetDevModelCap - sExecute .. sSetDevModelCap - sExecute + $3F0:
3862 begin
3863{$IFDEF TEXTLOG}CmdInfo := Format('SetDevModelCap P%d', [Player]); {$ENDIF}
3864 if Subject >= nFeature then
3865 Result := eInvalid
3866 else if DevModelTurn[Player] = GTurn then
3867 begin
3868 NewCap := Command shr 4 and $3F; { new value }
3869 with RW[Player].DevModel do
3870 if 1 shl Domain and Feature[Subject].Domains = 0 then
3871 Result := eDomainMismatch
3872 else if not ((Feature[Subject].Preq = preNone) or
3873 (Feature[Subject].Preq = preSun) and
3874 (GWonder[woSun].EffectiveOwner = Player) or
3875 (Feature[Subject].Preq >= 0) and
3876 (RW[Player].Tech[Feature[Subject].Preq] >= tsApplicable)) then
3877 Result := eNoPreq
3878 else
3879 begin
3880 if (Subject in AutoFeature) or (Subject = mcDefense) then
3881 MinCap := 1
3882 else
3883 MinCap := 0; { MinCap - minimum use of feature }
3884 if Subject >= mcFirstNonCap then
3885 MaxCap := 1
3886 else if Subject = mcDefense then
3887 begin
3888 if Domain = dGround then
3889 MaxCap := 2
3890 else
3891 MaxCap := 3;
3892 if RW[Player].Tech[adSteel] >= tsApplicable then
3893 Inc(MaxCap);
3894 end
3895 else
3896 MaxCap := 8; { MaxCap - maximum use of this feature }
3897 if (Domain = dGround) and (Subject = mcDefense) then
3898 CapWeight := 2
3899 else
3900 CapWeight := Feature[Subject].Weight;
3901 if (NewCap < MinCap) or (NewCap > MaxCap) or
3902 (Weight + (NewCap - Cap[Subject]) * CapWeight > MaxWeight) then
3903 Result := eViolation
3904 else if Command >= sExecute then
3905 begin
3906 Cap[Subject] := NewCap;
3907
3908 // mutual feature exclusion
3909 case Subject of
3910 mcSub:
3911 begin
3912 if ServerVersion[Player] >= $010103 then
3913 Cap[mcSeaTrans] := 0;
3914 Cap[mcArtillery] := 0;
3915 Cap[mcCarrier] := 0;
3916 if Cap[mcDefense] > 2 then
3917 Cap[mcDefense] := 2;
3918 end;
3919 mcSeaTrans:
3920 begin
3921 if ServerVersion[Player] >= $010103 then
3922 Cap[mcSub] := 0;
3923 end;
3924 mcCarrier:
3925 Cap[mcSub] := 0;
3926 mcArtillery:
3927 Cap[mcSub] := 0;
3928 mcAlpine:
3929 begin
3930 Cap[mcOver] := 0;
3931 Cap[mcMob] := 0;
3932 end;
3933 mcOver:
3934 Cap[mcAlpine] := 0;
3935 mcMob:
3936 begin
3937 Cap[mcAlpine] := 0;
3938 end;
3939 end;
3940
3941 CalculateModel(RW[Player].DevModel);
3942 end;
3943 end;
3944 end
3945 else
3946 Result := eNoModel;
3947 end;
3948
3949 {
3950 Unit Related Commands
3951 ____________________________________________________________________
3952 }
3953 sRemoveUnit, sRemoveUnit - sExecute:
3954 begin
3955{$IFDEF TEXTLOG}CmdInfo := Format('RemoveUnit P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
3956 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
3957 Result := eInvalid
3958 else
3959 begin
3960 Result := eRemoved;
3961 Loc0 := RW[Player].Un[Subject].Loc;
3962 if RealMap[Loc0] and fCity <> 0 then { check utilize }
3963 begin
3964 SearchCity(Loc0, Player, cix1);
3965 with RW[Player].City[cix1] do
3966 begin
3967 if (RW[Player].Model[RW[Player].Un[Subject].mix].Kind = mkCaravan)
3968 and ((Project and cpImp = 0) or
3969 (Imp[Project and cpIndex].Kind <> ikShipPart)) or
3970 (Project and cpImp = 0) and
3971 (RW[Player].Model[Project and cpIndex].Kind <> mkCaravan) then
3972 Result := eUtilized;
3973 if Command >= sExecute then
3974 begin
3975 if Result = eUtilized then
3976 begin
3977 with RW[Player].Un[Subject] do
3978 begin
3979 Cost := Integer(RW[Player].Model[mix].Cost) * Health *
3980 BuildCostMod[Difficulty[Player]] div 1200;
3981 if RW[Player].Model[mix].Cap[mcLine] > 0 then
3982 Cost := Cost div 2;
3983 end;
3984 if Project and (cpImp + cpIndex) = cpImp + imTrGoods then
3985 Inc(RW[Player].Money, Cost)
3986 else
3987 begin
3988 Inc(Prod, Cost * 2 div 3);
3989 Project0 := Project0 and not cpCompleted;
3990 if Project0 and not cpAuto <> Project and not cpAuto then
3991 Project0 := Project;
3992 Prod0 := Prod;
3993 end
3994 end;
3995 RemoveUnit_UpdateMap(Player, Subject);
3996 end;
3997 end;
3998 end
3999 else if Command >= sExecute then
4000 RemoveUnit_UpdateMap(Player, Subject);
4001 end
4002 end;
4003
4004 sSetUnitHome, sSetUnitHome - sExecute:
4005 begin
4006{$IFDEF TEXTLOG}CmdInfo := Format('SetUnitHome P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
4007 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4008 Result := eInvalid
4009 else
4010 begin
4011 Loc0 := RW[Player].Un[Subject].Loc;
4012 if RealMap[Loc0] and fCity = 0 then
4013 Result := eInvalid
4014 else
4015 begin
4016 SearchCity(Loc0, Player, cix1);
4017 if RW[Player].City[cix1].Flags and chCaptured <> 0 then
4018 Result := eViolation
4019 else if Command >= sExecute then
4020 RW[Player].Un[Subject].Home := cix1;
4021 end;
4022 end;
4023 end;
4024
4025 sSetSpyMission .. sSetSpyMission + (nSpyMission - 1) shl 4,
4026 sSetSpyMission - sExecute .. sSetSpyMission - sExecute +
4027 (nSpyMission - 1) shl 4:
4028 if Command >= sExecute then
4029 SpyMission := Command shr 4 and $F;
4030
4031 sLoadUnit, sLoadUnit - sExecute:
4032 begin
4033{$IFDEF TEXTLOG}CmdInfo := Format('LoadUnit P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
4034 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4035 Result := eInvalid
4036 else
4037 Result := LoadUnit(Player, Subject, Command < sExecute);
4038 end;
4039
4040 sUnloadUnit, sUnloadUnit - sExecute:
4041 begin
4042{$IFDEF TEXTLOG}CmdInfo := Format('UnloadUnit P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
4043 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4044 Result := eInvalid
4045 else
4046 Result := UnloadUnit(Player, Subject, Command < sExecute);
4047 end;
4048
4049 sSelectTransport, sSelectTransport - sExecute:
4050 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4051 Result := eInvalid
4052 else
4053 with RW[Player].Model[RW[Player].Un[Subject].mix] do
4054 begin
4055 if Cap[mcSeaTrans] + Cap[mcAirTrans] + Cap[mcCarrier] = 0 then
4056 Result := eInvalid
4057 else if Command >= sExecute then
4058 uixSelectedTransport := Subject;
4059 end;
4060
4061 sCreateUnit .. sCreateUnit + (nPl - 1) shl 4,
4062 sCreateUnit - sExecute .. sCreateUnit - sExecute + (nPl - 1) shl 4:
4063 if (GTestFlags and tfUncover <> 0) or (Difficulty[Player] = 0)
4064 then { supervisor only command }
4065 begin
4066 p1 := Command shr 4 and $F;
4067 Loc1 := Integer(Data);
4068 if (Occupant[Loc1] >= 0) and (p1 <> Occupant[Loc1]) or
4069 (RealMap[Loc1] and fCity <> 0) and
4070 (RealMap[Loc1] shr 27 <> Cardinal(p1)) or
4071 (RW[p1].Model[Subject].Domain < dAir) and
4072 ((RW[p1].Model[Subject].Domain = dSea) <> (RealMap[Integer(Data)] and
4073 fTerrain < fGrass)) then
4074 Result := eViolation
4075 else if Command >= sExecute then
4076 begin
4077 CreateUnit(p1, Subject);
4078 RW[p1].Un[RW[p1].nUn - 1].Loc := Integer(Data);
4079 PlaceUnit(p1, RW[p1].nUn - 1);
4080 UpdateUnitMap(Integer(Data));
4081 end;
4082 end
4083 else
4084 Result := eInvalid;
4085
4086 sMoveUnit + (0 + 6 * 8) * 16, sMoveUnit + (1 + 7 * 8) * 16,
4087 sMoveUnit + (2 + 0 * 8) * 16, sMoveUnit + (1 + 1 * 8) * 16,
4088 sMoveUnit + (0 + 2 * 8) * 16, sMoveUnit + (7 + 1 * 8) * 16,
4089 sMoveUnit + (6 + 0 * 8) * 16, sMoveUnit + (7 + 7 * 8) * 16,
4090 sMoveUnit - sExecute + (0 + 6 * 8) * 16, sMoveUnit - sExecute +
4091 (1 + 7 * 8) * 16, sMoveUnit - sExecute + (2 + 0 * 8) * 16,
4092 sMoveUnit - sExecute + (1 + 1 * 8) * 16, sMoveUnit - sExecute +
4093 (0 + 2 * 8) * 16, sMoveUnit - sExecute + (7 + 1 * 8) * 16,
4094 sMoveUnit - sExecute + (6 + 0 * 8) * 16, sMoveUnit - sExecute +
4095 (7 + 7 * 8) * 16:
4096 begin
4097 dx := (Command shr 4 + 4) and 7 - 4;
4098 dy := (Command shr 7 + 4) and 7 - 4;
4099{$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}
4100 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4101 Result := eInvalid
4102 else
4103 Result := MoveUnit(Player, Subject, dx, dy, Command < sExecute);
4104 end;
4105
4106 {
4107 Settlers Related Commands
4108 ____________________________________________________________________
4109 }
4110 sAddToCity, sAddToCity - sExecute:
4111 begin
4112{$IFDEF TEXTLOG}CmdInfo := Format('AddToCity P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
4113 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4114 Result := eInvalid
4115 else if not(RW[Player].Model[RW[Player].Un[Subject].mix].Kind
4116 in [mkSettler, mkSlaves]) and
4117 (RW[Player].Un[Subject].Flags and unConscripts = 0) then
4118 Result := eViolation
4119 else
4120 begin
4121 Loc0 := RW[Player].Un[Subject].Loc;
4122 if RealMap[Loc0] and fCity = 0 then
4123 Result := eInvalid
4124 else
4125 begin
4126 SearchCity(Loc0, Player, cix1);
4127 with RW[Player].City[cix1] do
4128 if not CanCityGrow(Player, cix1) then
4129 Result := eMaxSize
4130 else if Command >= sExecute then
4131 begin { add to city }
4132 if Mode = moPlaying then
4133 SavedTiles[cix1] := 0; // save in every case
4134 if CanCityGrow(Player, cix1) then
4135 CityGrowth(Player, cix1);
4136 if (RW[Player].Model[RW[Player].Un[Subject].mix]
4137 .Kind = mkSettler) and CanCityGrow(Player, cix1) then
4138 CityGrowth(Player, cix1);
4139 RemoveUnit_UpdateMap(Player, Subject);
4140 end;
4141 end;
4142 end;
4143 end;
4144
4145 sStartJob .. sStartJob + $3F0, sStartJob - sExecute .. sStartJob + $3F0
4146 - sExecute:
4147 begin
4148 Loc0 := RW[Player].Un[Subject].Loc;
4149 I := Command shr 4 and $3F; // new job
4150{$IFDEF TEXTLOG}CmdInfo := Format('StartJob P%d Mod%d Loc%d: %d', [Player, RW[Player].Un[Subject].mix, Loc0, I]); {$ENDIF}
4151 if (Subject >= RW[Player].nUn) or (Loc0 < 0) then
4152 Result := eInvalid
4153 else if I >= nJob then
4154 Result := eInvalid
4155 else
4156 begin
4157 Result := StartJob(Player, Subject, I, Command < sExecute);
4158 if Result = eCity then
4159 begin // new city
4160 cix1 := RW[Player].nCity - 1;
4161 AddBestCityTile(Player, cix1);
4162 if Mode = moPlaying then
4163 with RW[Player].City[cix1] do
4164 begin
4165 // SavedResourceWeights[cix1]:=ResourceWeights;
4166 SavedTiles[cix1] := 0; // save in every case
4167 end;
4168 if Mode >= moMovie then { show new city in interface modules }
4169 for p1 := 0 to nPl - 1 do
4170 if (1 shl p1 and GWatching <> 0) and (p1 <> Player) and
4171 (ObserveLevel[Loc0] and (3 shl (2 * p1)) > 0) then
4172 CallPlayer(cShowCityChanged, p1, Loc0);
4173 end;
4174 end;
4175 end;
4176
4177 {
4178 City Related Commands
4179 ____________________________________________________________________
4180 }
4181 sSetCityProject, sSetCityProject - sExecute:
4182 begin
4183 NewProject := Integer(Data) and not cpAuto;
4184{$IFDEF TEXTLOG}CmdInfo := Format('SetCityProject P%d Loc%d: %d', [Player, RW[Player].City[Subject].Loc, NewProject]); {$ENDIF}
4185 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4186 then
4187 Result := eInvalid
4188 else
4189 with RW[Player].City[Subject] do
4190 begin
4191 if NewProject = Project then
4192 Result := eNotChanged
4193 else
4194 begin
4195 pt0 := ProjectType(Project0);
4196 pt1 := ProjectType(NewProject);
4197 if NewProject and cpImp = 0 then
4198 begin
4199 if NewProject and cpIndex >= RW[Player].nModel then
4200 Result := eInvalid
4201 else if (NewProject and cpConscripts <> 0) and
4202 not ((RW[Player].Tech[adConscription] >= tsApplicable) and
4203 (RW[Player].Model[NewProject and cpIndex].Domain = dGround)
4204 and (RW[Player].Model[NewProject and cpIndex].Kind < mkScout))
4205 then
4206 Result := eViolation
4207 // else if (RW[Player].Model[NewProject and cpIndex].Kind=mkSlaves)
4208 // and (GWonder[woPyramids].EffectiveOwner<>Player) then
4209 // result:=eNoPreq
4210 end
4211 else if NewProject and cpIndex >= nImp then
4212 Result := eInvalid
4213 else
4214 begin
4215 Preq := Imp[NewProject and cpIndex].Preq;
4216 for I := 0 to nImpReplacement - 1 do
4217 if (ImpReplacement[I].OldImp = NewProject and cpIndex) and
4218 (Built[ImpReplacement[I].NewImp] > 0) then begin
4219 Result := eObsolete;
4220 Break;
4221 end;
4222 if Result = eObsolete then
4223 else if Preq = preNA then
4224 Result := eInvalid
4225 else if (Preq >= 0) and (RW[Player].Tech[Preq] < tsApplicable)
4226 then
4227 Result := eNoPreq
4228 else if Built[NewProject and cpIndex] > 0 then
4229 Result := eInvalid
4230 else if (NewProject and cpIndex < nWonder) and
4231 (GWonder[NewProject and cpIndex].CityID <> WonderNotBuiltYet) then
4232 Result := eViolation // wonder already exists
4233 else if (NewProject and cpIndex = imSpacePort) and
4234 (RW[Player].NatBuilt[imSpacePort] > 0) then
4235 Result := eViolation // space port already exists
4236 else if (NewProject = cpImp + imBank) and (Built[imMarket] = 0)
4237 or (NewProject = cpImp + imUniversity) and
4238 (Built[imLibrary] = 0) or (NewProject = cpImp + imResLab) and
4239 (Built[imUniversity] = 0) or (NewProject = cpImp + imMfgPlant)
4240 and (Built[imFactory] = 0) then
4241 Result := eNoPreq;
4242 case NewProject - cpImp of
4243 woLighthouse, woMagellan, imCoastalFort, imHarbor, imPlatform:
4244 begin { city at ocean? }
4245 Preq := 0;
4246 V8_to_Loc(Loc, Adjacent);
4247 for V8 := 0 to 7 do
4248 begin
4249 Loc1 := Adjacent[V8];
4250 if (Loc1 >= 0) and (Loc1 < MapSize) and
4251 (RealMap[Loc1] and fTerrain = fShore) then
4252 Inc(Preq);
4253 end;
4254 if Preq = 0 then
4255 Result := eNoPreq;
4256 end;
4257 woHoover, imHydro:
4258 begin { city at river or mountains? }
4259 Preq := 0;
4260 V8_to_Loc(Loc, Adjacent);
4261 for V8 := 0 to 7 do
4262 begin
4263 Loc1 := Adjacent[V8];
4264 if (Loc1 >= 0) and (Loc1 < MapSize) and
4265 ((RealMap[Loc1] and fTerrain = fMountains) or
4266 (RealMap[Loc1] and fRiver <> 0)) then
4267 Inc(Preq);
4268 end;
4269 if Preq = 0 then
4270 Result := eNoPreq;
4271 end;
4272 woMIR, imShipComp, imShipPow, imShipHab:
4273 if RW[Player].NatBuilt[imSpacePort] = 0 then
4274 Result := eNoPreq;
4275 end;
4276 if (GTestFlags and tfNoRareNeed = 0) and
4277 (Imp[NewProject and cpIndex].Kind = ikShipPart) then
4278 if RW[Player].Tech[adMassProduction] < tsApplicable then
4279 Result := eNoPreq
4280 else
4281 begin // check for rare resources
4282 if NewProject and cpIndex = imShipComp then
4283 J := 1
4284 else if NewProject and cpIndex = imShipPow then
4285 J := 2
4286 else { if NewProject and cpIndex=imShipHab then }
4287 J := 3;
4288 // j = rare resource required
4289 Preq := 0;
4290 V21_to_Loc(Loc, Radius);
4291 for V21 := 1 to 26 do
4292 begin
4293 Loc1 := Radius[V21];
4294 if (Loc1 >= 0) and (Loc1 < MapSize) and
4295 (RealMap[Loc1] shr 25 and 3 = Cardinal(J)) then
4296 Inc(Preq);
4297 end;
4298 if Preq = 0 then
4299 Result := eNoPreq;
4300 end;
4301 end;
4302
4303 if (Command >= sExecute) and (Result >= rExecuted) then
4304 begin
4305 if pt0 <> ptSelect then
4306 if NewProject and (cpImp or cpIndex) = Project0 and
4307 (cpImp or cpIndex) then
4308 Prod := Prod0
4309 else if (pt1 = ptTrGoods) or (pt1 = ptShip) or (pt1 <> pt0)
4310 and (pt0 <> ptCaravan) then
4311 begin
4312 Inc(RW[Player].Money, Prod0);
4313 Prod := 0;
4314 Prod0 := 0;
4315 Project0 := cpImp + imTrGoods;
4316 end
4317 else
4318 Prod := Prod0 * 2 div 3;
4319 Project := NewProject;
4320 end;
4321 end;
4322 end;
4323 end;
4324
4325 sBuyCityProject, sBuyCityProject - sExecute:
4326 begin
4327{$IFDEF TEXTLOG}CmdInfo := Format('BuyCityProject P%d Loc%d', [Player, RW[Player].City[Subject].Loc]); {$ENDIF}
4328 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4329 then
4330 Result := eInvalid
4331 else
4332 with RW[Player].City[Subject] do
4333 if (RW[Player].Government = gAnarchy) or (Flags and chCaptured <> 0)
4334 then
4335 Result := eOutOfControl
4336 else if (Project and cpImp <> 0) and
4337 ((Project and cpIndex = imTrGoods) or
4338 (Imp[Project and cpIndex].Kind = ikShipPart)) then
4339 Result := eInvalid // don't buy colony ship
4340 else
4341 begin
4342 CityReport.HypoTiles := -1;
4343 CityReport.HypoTax := -1;
4344 CityReport.HypoLux := -1;
4345 GetCityReport(Player, Subject, CityReport);
4346 Cost := CityReport.ProdCost;
4347 NextProd := CityReport.ProdRep - CityReport.Support;
4348 if (CityReport.Working - CityReport.Happy > Size shr 1) or
4349 (NextProd < 0) then // !!! change to new style disorder
4350 NextProd := 0;
4351 Cost := Cost - Prod - NextProd;
4352 if (GWonder[woMich].EffectiveOwner = Player) and
4353 (Project and cpImp <> 0) then
4354 Cost := Cost * 2
4355 else
4356 Cost := Cost * 4;
4357 if Cost <= 0 then
4358 Result := eNotChanged
4359 else if Cost > RW[Player].Money then
4360 Result := eViolation
4361 else if Command >= sExecute then
4362 IntServer(sIntBuyMaterial, Player, Subject, Cost);
4363 // need to save material/cost because city tiles are not correct
4364 // when loading
4365 end;
4366 end;
4367
4368 sSellCityProject, sSellCityProject - sExecute:
4369 begin
4370{$IFDEF TEXTLOG}CmdInfo := Format('SellCityProject P%d Loc%d', [Player, RW[Player].City[Subject].Loc]); {$ENDIF}
4371 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4372 then
4373 Result := eInvalid
4374 else if Command >= sExecute then
4375 with RW[Player].City[Subject] do
4376 begin
4377 Inc(RW[Player].Money, Prod0);
4378 Prod := 0;
4379 Prod0 := 0;
4380 end;
4381 end;
4382
4383 sSellCityImprovement, sSellCityImprovement - sExecute:
4384 begin
4385{$IFDEF TEXTLOG}CmdInfo := Format('SellCityImprovement P%d Loc%d: %d', [Player, RW[Player].City[Subject].Loc, Integer(Data)]); {$ENDIF}
4386 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4387 then
4388 Result := eInvalid
4389 else
4390 with RW[Player].City[Subject] do
4391 if Built[Integer(Data)] = 0 then
4392 Result := eInvalid
4393 else if (RW[Player].Government = gAnarchy) or
4394 (Flags and chCaptured <> 0) then
4395 Result := eOutOfControl
4396 else if Flags and chImprovementSold <> 0 then
4397 Result := eOnlyOnce
4398 else if Command >= sExecute then
4399 begin
4400 Inc(RW[Player].Money, Imp[Integer(Data)].Cost * BuildCostMod
4401 [Difficulty[Player]] div 12);
4402 Built[Integer(Data)] := 0;
4403 if Imp[Integer(Data)].Kind in [ikNatLocal, ikNatGlobal] then
4404 begin
4405 RW[Player].NatBuilt[Integer(Data)] := 0;
4406 case Integer(Data) of
4407 imGrWall:
4408 GrWallContinent[Player] := -1;
4409 imSpacePort:
4410 DestroySpacePort_TellPlayers(Player, -1);
4411 end;
4412 end;
4413 Inc(Flags, chImprovementSold);
4414 end;
4415 end;
4416
4417 sRebuildCityImprovement, sRebuildCityImprovement - sExecute:
4418 begin
4419 OldImp := Integer(Data);
4420{$IFDEF TEXTLOG}CmdInfo := Format('RebuildCityImprovement P%d Loc%d: %d', [Player, RW[Player].City[Subject].Loc, OldImp]); {$ENDIF}
4421 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4422 then
4423 Result := eInvalid
4424 else
4425 begin
4426 if (OldImp < 0) or (OldImp >= nImp) or
4427 not(Imp[OldImp].Kind in [ikCommon, ikNatLocal, ikNatGlobal]) then
4428 Result := eInvalid
4429 else
4430 with RW[Player].City[Subject] do
4431 if (Built[OldImp] = 0) or (Project and cpImp = 0) or
4432 not(Imp[Project and cpIndex].Kind in [ikCommon, ikNatLocal,
4433 ikNatGlobal]) then
4434 Result := eInvalid
4435 else if (RW[Player].Government = gAnarchy) or
4436 (Flags and chCaptured <> 0) then
4437 Result := eOutOfControl
4438 else if Flags and chImprovementSold <> 0 then
4439 Result := eOnlyOnce
4440 else if Command >= sExecute then
4441 begin
4442 Inc(Prod, Imp[OldImp].Cost * BuildCostMod[Difficulty[Player]]
4443 div 12 * 2 div 3);
4444 Project0 := Project0 and not cpCompleted;
4445 if Project0 and not cpAuto <> Project and not cpAuto then
4446 Project0 := Project;
4447 Prod0 := Prod;
4448 Built[OldImp] := 0;
4449 if Imp[OldImp].Kind in [ikNatLocal, ikNatGlobal] then
4450 begin // nat. project lost
4451 RW[Player].NatBuilt[OldImp] := 0;
4452 case OldImp of
4453 imGrWall:
4454 GrWallContinent[Player] := -1;
4455 imSpacePort:
4456 DestroySpacePort_TellPlayers(Player, -1);
4457 end;
4458 end;
4459 Inc(Flags, chImprovementSold);
4460 end;
4461 end;
4462 end;
4463
4464 sSetCityTiles, sSetCityTiles - sExecute:
4465 begin
4466{$IFDEF TEXTLOG}CmdInfo := Format('SetCityTiles P%d Loc%d: %x', [Player, RW[Player].City[Subject].Loc, Integer(Data)]); {$ENDIF}
4467 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4468 then
4469 Result := eInvalid
4470 else
4471 Result := SetCityTiles(Player, Subject, Integer(Data),
4472 Command < sExecute);
4473 end;
4474
4475 {
4476 Client Exclusive Commands
4477 ____________________________________________________________________
4478 }
4479 else
4480 if Command >= cClientEx then
4481 begin
4482{$IFDEF TEXTLOG}CmdInfo := Format('ClientEx%x P%d', [Command, Player]);
4483 {$ENDIF}
4484 if ProcessClientData[Player] or (Mode = moPlaying) then
4485 CallPlayer(Command, Player, Data)
4486 end
4487 else
4488 Result := eUnknown;
4489 end; { case command }
4490
4491 // do not log invalid and non-relevant commands
4492 if Result = eZOC_EnemySpotted then
4493 begin
4494 Assert(Mode = moPlaying);
4495 CL.State := FormerCLState;
4496 IntServer(sIntDiscoverZOC, Player, 0, ZOCTile);
4497 end
4498 else if Result and rEffective = 0 then
4499 if Mode < moPlaying then
4500 begin
4501{$IFDEF TEXTLOG}CmdInfo := Format('***ERROR (%x) ', [Result]) + CmdInfo;
4502 {$ENDIF}
4503 LoadOK := False;
4504 end
4505 else
4506 begin
4507 if Logged then
4508 CL.State := FormerCLState;
4509 if (Result < rExecuted) and (Command >= sExecute) then
4510 PutMessage(1 shl 16 + 1, Format('INVALID: %d calls %x (%d)',
4511 [Player, Command, Subject]));
4512 end;
4513
4514 if (Command and (cClientEx or sExecute or sctMask) = sExecute or sctEndClient)
4515 and (Result >= rExecuted) then
4516 LastEndClientCommand := Command;
4517{$IFOPT O-}Dec(nHandoverStack, 2); {$ENDIF}
4518end;
4519
4520
4521initialization
4522
4523FindFirst(ParamStr(0), $21, ExeInfo);
4524FindClose(ExeInfo);
4525
4526{$IFOPT O-}nHandoverStack := 0; {$ENDIF}
4527
4528end.
Note: See TracBrowser for help on using the repository browser.