source: tags/1.3.1/GameServer.pas

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