source: tags/1.3.8/GameServer.pas

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