source: trunk/GameServer.pas

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