source: tags/1.3.2/GameServer.pas

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