source: tags/1.3.0/GameServer.pas

Last change on this file was 385, checked in by chronos, 3 years ago
  • Added: Allow to change key bindings from Settings dialog.
  • Added: Allow to reset settings in Settings dialog.
File size: 151.5 KB
Line 
1{$INCLUDE Switches.inc}
2// {$DEFINE TEXTLOG}
3// {$DEFINE LOADPERF}
4unit GameServer;
5
6interface
7
8uses
9 Protocol, Database, dynlibs, Platform, dateutils, fgl, LazFileUtils,
10 Graphics, 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; { StartGame }
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; // LoadGame
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; { BeforeTurn }
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; // Afterturn
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; // ExecuteMove
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; // ExecuteAttack
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; // with
2614end; { MoveUnit }
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;
2701
2702begin { >>>server }
2703 if Command = sTurn then
2704 begin
2705 p2 := -1;
2706 for p1 := 0 to nPl - 1 do
2707 if (p1 <> Player) and (1 shl p1 and GWatching <> 0) then
2708 CallPlayer(cShowTurnChange, p1, p2);
2709 end;
2710
2711 assert(MapSize = lx * ly);
2712 assert(Command and (sctMask or sExecute) <> sctInternal or sExecute);
2713 // not for internal commands
2714 if (Command < 0) or (Command >= $10000) then
2715 begin
2716 result := eUnknown;
2717 exit;
2718 end;
2719
2720 if (Player < 0) or (Player >= nPl) or
2721 ((Command and (sctMask or sExecute) <> sctInfo) and
2722 ((Subject < 0) or (Subject >= $1000))) then
2723 begin
2724 result := eInvalid;
2725 exit;
2726 end;
2727
2728 if (1 shl Player and (GAlive or GWatching) = 0) and
2729 not((Command = sTurn) or (Command = sBreak) or (Command = sResign) or
2730 (Command = sGetAIInfo) or (Command = sGetAICredits) or
2731 (Command = sGetVersion) or (Command and $FF0F = sGetChart)) then
2732 begin
2733 PutMessage(1 shl 16 + 1, Format('NOT Alive: %d', [Player]));
2734 result := eNoTurn;
2735 exit;
2736 end;
2737
2738 result := eOK;
2739
2740 // check if command allowed now
2741 if (Mode = moPlaying) and not((Command >= cClientEx) or (Command = sMessage)
2742 or (Command = sSetDebugMap) or (Command = sGetDebugMap) or
2743 (Command = sGetAIInfo) or (Command = sGetAICredits) or
2744 (Command = sGetVersion) or (Command = sGetTechCost) or
2745 (Command = sGetDefender) or (Command = sGetUnitReport) or
2746 (Command = sGetCityReport) or (Command = sGetCityTileInfo) or
2747 (Command = sGetCity) or (Command = sGetEnemyCityReport) or
2748 (Command = sGetEnemyCityAreaInfo) or (Command = sGetCityReportNew) or
2749 (Command and $FF0F = sGetChart) or (Command and $FF0F = sSetAttitude))
2750 // commands always allowed
2751 and not((Player = pTurn) and (Command < $1000))
2752 // info request always allowed for pTurn
2753 and ((pDipActive < 0) and (Player <> pTurn) // not his turn
2754 or (pDipActive >= 0) and (Player <> pDipActive)
2755 // not active in negotiation mode
2756 or (pDipActive >= 0) and (Command and sctMask <> sctEndClient)) then
2757 // no nego command
2758 begin
2759 PutMessage(1 shl 16 + 1, Format('No Turn: %d calls %x',
2760 [Player, Command shr 4]));
2761 result := eNoTurn;
2762 exit;
2763 end;
2764
2765 // do not use EXIT hereafter!
2766
2767{$IFOPT O-}
2768 HandoverStack[nHandoverStack] := Player + $1000;
2769 HandoverStack[nHandoverStack + 1] := Command;
2770 inc(nHandoverStack, 2);
2771
2772 InvalidTreatyMap := 0;
2773 // new command, sIntExpandTerritory of previous command was processed
2774{$ENDIF}
2775 if (Mode = moPlaying) and (Command >= sExecute) and
2776 ((Command and sctMask <> sctEndClient) or (Command = sTurn)) and
2777 (Command and sctMask <> sctModel) and (Command <> sCancelTreaty) and
2778 (Command <> sSetCityTiles) and (Command <> sBuyCityProject) and
2779 ((Command < cClientEx) or ProcessClientData[Player]) then
2780 begin { log command }
2781 FormerCLState := CL.State;
2782 CL.Put(Command, Player, Subject, @Data);
2783 logged := true;
2784 end
2785 else
2786 logged := false;
2787
2788 case Command of
2789
2790 {
2791 Info Request Commands
2792 ____________________________________________________________________
2793 }
2794 sMessage:
2795 bix[0].Client(cDebugMessage, Subject, Data);
2796
2797 sSetDebugMap:
2798 DebugMap[Player] := @Data;
2799
2800 sGetDebugMap:
2801 pointer(Data) := DebugMap[Subject];
2802
2803 { sChangeSuperView:
2804 if Difficulty[Player]=0 then
2805 begin
2806 for i:=0 to nBrain-1 do if Brain[i].Initialized then
2807 CallClient(i, cShowSuperView, Subject)
2808 end
2809 else result:=eInvalid; }
2810
2811 sRefreshDebugMap:
2812 bix[0].Client(cRefreshDebugMap, -1, Player);
2813
2814 sGetChart .. sGetChart + (nStat - 1) shl 4:
2815 if (Subject >= 0) and (Subject < nPl) and Assigned(bix[Subject]) then
2816 begin
2817 StopTurn := 0;
2818 if (Difficulty[Player] = 0) or (GTestFlags and tfUncover <> 0)
2819 // supervisor
2820 or (Subject = Player) // own chart
2821 or (GWinner > 0) // game end chart
2822 or (1 shl Subject and GAlive = 0) then // chart of extinct nation
2823 if Subject > Player then
2824 StopTurn := GTurn
2825 else
2826 StopTurn := GTurn + 1
2827 else if RW[Player].Treaty[Subject] > trNoContact then
2828 if Command shr 4 and $F = stMil then
2829 StopTurn := RW[Player].EnemyReport[Subject].TurnOfMilReport + 1
2830 else
2831 StopTurn := RW[Player].EnemyReport[Subject].TurnOfCivilReport + 1;
2832 move(Stat[Command shr 4 and $F, Subject]^, Data,
2833 StopTurn * SizeOf(integer));
2834 FillChar(TChart(Data)[StopTurn], (GTurn - StopTurn) *
2835 SizeOf(integer), 0);
2836 end
2837 else
2838 result := eInvalid;
2839
2840 sGetTechCost:
2841 integer(Data) := TechCost(Player);
2842
2843 sGetAIInfo:
2844 if AIInfo[Subject] = '' then
2845 pchar(Data) := nil
2846 else
2847 pchar(Data) := @AIInfo[Subject][1];
2848
2849 sGetAICredits:
2850 if AICredits = '' then
2851 pchar(Data) := nil
2852 else
2853 pchar(Data) := @AICredits[1];
2854
2855 sGetVersion:
2856 integer(Data) := CevoVersion;
2857
2858 sGetGameChanged:
2859 if Player <> 0 then
2860 result := eInvalid
2861 else if (CL <> nil) and (CL.State.nLog = nLogOpened) and
2862 (CL.State.MoveCode = 0) and not HasCityTileChanges and
2863 not HasChanges(Player) then
2864 result := eNotChanged;
2865
2866 sGetTileInfo:
2867 if (Subject >= 0) and (Subject < MapSize) then
2868 result := GetTileInfo(Player, -2, Subject, TTileInfo(Data))
2869 else
2870 result := eInvalid;
2871
2872 sGetCityTileInfo:
2873 if (Subject >= 0) and (Subject < MapSize) then
2874 result := GetTileInfo(Player, -1, Subject, TTileInfo(Data))
2875 else
2876 result := eInvalid;
2877
2878 sGetHypoCityTileInfo:
2879 if (Subject >= 0) and (Subject < MapSize) then
2880 begin
2881 if (TTileInfo(Data).ExplCity < 0) or
2882 (TTileInfo(Data).ExplCity >= RW[Player].nCity) then
2883 result := eInvalid
2884 else if ObserveLevel[Subject] shr (Player * 2) and 3 = 0 then
2885 result := eNoPreq
2886 else
2887 result := GetTileInfo(Player, TTileInfo(Data).ExplCity, Subject,
2888 TTileInfo(Data));
2889 end
2890 else
2891 result := eInvalid;
2892
2893 sGetJobProgress:
2894 if (Subject >= 0) and (Subject < MapSize) then
2895 begin
2896 if ObserveLevel[Subject] shr (Player * 2) and 3 = 0 then
2897 result := eNoPreq
2898 else
2899 result := GetJobProgress(Player, Subject, TJobProgressData(Data));
2900 end
2901 else
2902 result := eInvalid;
2903
2904 sGetModels:
2905 if (GTestFlags and tfUncover <> 0) or (Difficulty[Player] = 0)
2906 then { supervisor only command }
2907 begin
2908 for p1 := 0 to nPl - 1 do
2909 if (p1 <> Player) and (1 shl p1 and GAlive <> 0) then
2910 for mix := 0 to RW[p1].nModel - 1 do
2911 TellAboutModel(Player, p1, mix);
2912 end
2913 else
2914 result := eInvalid;
2915
2916 sGetUnits:
2917 if (Subject >= 0) and (Subject < MapSize) and
2918 (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) then
2919 integer(Data) := GetUnitStack(Player, Subject)
2920 else
2921 result := eNoPreq;
2922
2923 sGetDefender:
2924 if (Subject >= 0) and (Subject < MapSize) and (Occupant[Subject] = Player)
2925 then
2926 Strongest(Subject, integer(Data), d, i, j)
2927 else
2928 result := eInvalid;
2929
2930 sGetBattleForecast, sGetBattleForecastEx:
2931 if (Subject >= 0) and (Subject < MapSize) and
2932 (ObserveLevel[Subject] and (3 shl (Player * 2)) > 0) then
2933 with TBattleForecast(Data) do
2934 if (1 shl pAtt and GAlive <> 0) and (mixAtt >= 0) and
2935 (mixAtt < RW[pAtt].nModel) and
2936 ((pAtt = Player) or (RWemix[Player, pAtt, mixAtt] >= 0)) then
2937 begin
2938 result := GetBattleForecast(Subject, TBattleForecast(Data), uix1,
2939 cix1, AStr, DStr, ABaseDamage, DBaseDamage);
2940 if Command = sGetBattleForecastEx then
2941 begin
2942 TBattleForecastEx(Data).AStr := (AStr + 200) div 400;
2943 TBattleForecastEx(Data).DStr := (DStr + 200) div 400;
2944 TBattleForecastEx(Data).ABaseDamage := ABaseDamage;
2945 TBattleForecastEx(Data).DBaseDamage := DBaseDamage;
2946 end;
2947 if result = eOK then
2948 result := eInvalid; // no enemy unit there!
2949 end
2950 else
2951 result := eInvalid
2952 else
2953 result := eInvalid;
2954
2955 sGetUnitReport:
2956 if (Subject < 0) or (Subject >= RW[Player].nUn) or
2957 (RW[Player].Un[Subject].Loc < 0) then
2958 result := eInvalid
2959 else
2960 GetUnitReport(Player, Subject, TUnitReport(Data));
2961
2962 sGetMoveAdvice:
2963 if (Subject < 0) or (Subject >= RW[Player].nUn) or
2964 (RW[Player].Un[Subject].Loc < 0) then
2965 result := eInvalid
2966 else
2967 result := GetMoveAdvice(Player, Subject, TMoveAdviceData(Data));
2968
2969 sGetPlaneReturn:
2970 if (Subject < 0) or (Subject >= RW[Player].nUn) or
2971 (RW[Player].Un[Subject].Loc < 0) or
2972 (RW[Player].Model[RW[Player].Un[Subject].mix].Domain <> dAir) then
2973 result := eInvalid
2974 else
2975 begin
2976 if CanPlaneReturn(Player, Subject, TPlaneReturnData(Data)) then
2977 result := eOK
2978 else
2979 result := eNoWay;
2980 end;
2981
2982 sGetCity:
2983 if (Subject >= 0) and (Subject < MapSize) and
2984 (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
2985 (RealMap[Subject] and fCity <> 0) then
2986 with TGetCityData(Data) do
2987 begin
2988 Owner := Player;
2989 SearchCity(Subject, Owner, cix1);
2990 c := RW[Owner].City[cix1];
2991 if (Owner <> Player) and (c.Project and cpImp = 0) then
2992 TellAboutModel(Player, Owner, c.Project and cpIndex);
2993 end
2994 else
2995 result := eInvalid;
2996
2997 sGetCityReport:
2998 if (Subject < 0) or (Subject >= RW[Player].nCity) or
2999 (RW[Player].City[Subject].Loc < 0) then
3000 result := eInvalid
3001 else
3002 result := GetCityReport(Player, Subject, TCityReport(Data));
3003
3004 sGetCityReportNew:
3005 if (Subject < 0) or (Subject >= RW[Player].nCity) or
3006 (RW[Player].City[Subject].Loc < 0) then
3007 result := eInvalid
3008 else
3009 GetCityReportNew(Player, Subject, TCityReportNew(Data));
3010
3011 sGetCityAreaInfo:
3012 if (Subject < 0) or (Subject >= RW[Player].nCity) or
3013 (RW[Player].City[Subject].Loc < 0) then
3014 result := eInvalid
3015 else
3016 GetCityAreaInfo(Player, RW[Player].City[Subject].Loc,
3017 TCityAreaInfo(Data));
3018
3019 sGetEnemyCityReport:
3020 if (Subject >= 0) and (Subject < MapSize) and
3021 (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
3022 (RealMap[Subject] and fCity <> 0) then
3023 begin
3024 p1 := Occupant[Subject];
3025 if p1 < 0 then
3026 p1 := 1;
3027 SearchCity(Subject, p1, cix1);
3028 TCityReport(Data).HypoTiles := -1;
3029 TCityReport(Data).HypoTax := -1;
3030 TCityReport(Data).HypoLux := -1;
3031 GetCityReport(p1, cix1, TCityReport(Data));
3032 end
3033 else
3034 result := eInvalid;
3035
3036 sGetEnemyCityReportNew:
3037 if (Subject >= 0) and (Subject < MapSize) and
3038 (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
3039 (RealMap[Subject] and fCity <> 0) then
3040 begin
3041 p1 := Occupant[Subject];
3042 if p1 < 0 then
3043 p1 := 1;
3044 SearchCity(Subject, p1, cix1);
3045 TCityReport(Data).HypoTiles := -1;
3046 TCityReport(Data).HypoTax := -1;
3047 TCityReport(Data).HypoLux := -1;
3048 GetCityReportNew(p1, cix1, TCityReportNew(Data));
3049 end
3050 else
3051 result := eInvalid;
3052
3053 sGetEnemyCityAreaInfo:
3054 if (Subject >= 0) and (Subject < MapSize) and
3055 (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
3056 (RealMap[Subject] and fCity <> 0) then
3057 begin
3058 p1 := Occupant[Subject];
3059 if p1 < 0 then
3060 p1 := 1;
3061 SearchCity(Subject, p1, cix1);
3062 GetCityAreaInfo(p1, Subject, TCityAreaInfo(Data));
3063 end
3064 else
3065 result := eInvalid;
3066
3067 sGetCityTileAdvice:
3068 if (Subject < 0) or (Subject >= RW[Player].nCity) or
3069 (RW[Player].City[Subject].Loc < 0) then
3070 result := eInvalid
3071 else
3072 GetCityTileAdvice(Player, Subject, TCityTileAdviceData(Data));
3073
3074 {
3075 Map Editor Commands
3076 ____________________________________________________________________
3077 }
3078 sEditTile:
3079 if Player = 0 then
3080 with TEditTileData(Data) do
3081 EditTile(Loc, NewTile)
3082 else
3083 result := eInvalid;
3084
3085 sRandomMap:
3086 if (Player = 0) and MapGeneratorAvailable then
3087 begin
3088 CreateElevation;
3089 PreviewElevation := false;
3090 CreateMap(false);
3091 FillChar(ObserveLevel, MapSize * 4, 0);
3092 DiscoverAll(Player, lObserveSuper);
3093 end
3094 else
3095 result := eInvalid;
3096
3097 sMapGeneratorRequest:
3098 if not MapGeneratorAvailable then
3099 result := eInvalid;
3100
3101 {
3102 Client Deactivation Commands
3103 ____________________________________________________________________
3104 }
3105 sTurn, sTurn - sExecute:
3106 begin
3107 AllHumansDead := true;
3108 for p1 := 0 to nPl - 1 do
3109 if (1 shl p1 and GAlive <> 0) and (bix[p1].Kind = btTerm) then
3110 AllHumansDead := false;
3111 if (pDipActive >= 0) // still in negotiation mode
3112 or (pTurn = 0) and ((GWinner > 0) or (GTurn = MaxTurn) or
3113 (Difficulty[0] > 0) and AllHumansDead) then // game end reached
3114 result := eViolation
3115 else if Command >= sExecute then
3116 begin
3117 if Mode = moPlaying then
3118 begin
3119 CL.State := FormerCLState;
3120 LogCityTileChanges;
3121{$IFNDEF SCR}
3122 if pTurn = 0 then
3123 begin
3124 LogChanges;
3125 SaveGame('~' + LogFileName, true);
3126 end;
3127{$ENDIF}
3128 end
3129 else if (Mode = moMovie) and (pTurn = 0) then
3130 CallPlayer(cMovieEndTurn, 0, nil^);
3131 GWatching := GWatching and GAlive or 1;
3132 RW[pTurn].Happened := 0;
3133 uixSelectedTransport := -1;
3134 SpyMission := smSabotageProd;
3135 if 1 shl pTurn and GAlive <> 0 then
3136 begin
3137 // calculate checksum
3138 TotalFood := 0;
3139 TotalProd := 0;
3140 for i := 0 to RW[pTurn].nCity - 1 do
3141 if RW[pTurn].City[i].Loc >= 0 then
3142 begin
3143 inc(TotalFood, RW[pTurn].City[i].Food);
3144 inc(TotalProd, RW[pTurn].City[i].Prod);
3145 end;
3146 CheckSum := TotalFood and 7 + TotalProd and 7 shl 3 +
3147 RW[pTurn].Money and 7 shl 6 + Worked[pTurn] div 100 and 7 shl 9;
3148 end
3149 else
3150 CheckSum := 0;
3151
3152 if Mode < moPlaying then // check checksum
3153 begin
3154 if CheckSum <> Subject then
3155 LoadOK := false;
3156 end
3157 else // save checksum
3158 CL.Put(Command, Player, CheckSum, @Data);
3159{$IFDEF TEXTLOG}
3160 CmdInfo := '';
3161 if CheckSum and 7 <> Subject and 7 then
3162 CmdInfo := Format('***ERROR (Food %d) ',
3163 [(CheckSum and 7 - Subject and 7 + 12) mod 8 - 4]) + CmdInfo;
3164 if CheckSum shr 3 and 7 <> Subject shr 3 and 7 then
3165 CmdInfo := '***ERROR (Prod) ' + CmdInfo;
3166 if CheckSum shr 6 and 7 <> Subject shr 6 and 7 then
3167 CmdInfo := '***ERROR (Research) ' + CmdInfo;
3168 if CheckSum shr 9 and 7 <> Subject shr 9 and 7 then
3169 CmdInfo := '***ERROR (Work) ' + CmdInfo;
3170{$ENDIF}
3171 if 1 shl pTurn and GAlive <> 0 then
3172 begin
3173 AfterTurn;
3174 if Mode < moPlaying then
3175 InsertTerritoryUpdateCommands;
3176 // if bix[pTurn]=bixTerm then UpdateBorderHelper;
3177 end;
3178
3179 repeat
3180 pTurn := (pTurn + 1) mod nPl;
3181 if pTurn = 0 then
3182 inc(GTurn);
3183 if Assigned(bix[pTurn]) and ((1 shl pTurn) and GAlive = 0) then
3184 begin // already made extinct -- continue statistics
3185 Stat[stExplore, pTurn, GTurn] := 0;
3186 Stat[stPop, pTurn, GTurn] := 0;
3187 Stat[stTerritory, pTurn, GTurn] := 0;
3188 Stat[stScience, pTurn, GTurn] := 0;
3189 Stat[stWork, pTurn, GTurn] := 0;
3190 Stat[stMil, pTurn, GTurn] := 0;
3191 end;
3192 until (pTurn = 0) or ((1 shl pTurn and (GAlive or GWatching) <> 0) and
3193 (GWinner = 0));
3194 if (Mode = moLoading_Fast) and
3195 ((GTurn = LoadTurn) or (GTurn = LoadTurn - 1) and (pTurn > 0)) then
3196 Mode := moLoading;
3197 if Mode = moPlaying then
3198 begin
3199 CCCommand := cTurn;
3200 CCPlayer := pTurn;
3201 Notify(ntNextPlayer);
3202 end
3203 else
3204 begin
3205 if GTurn = 0 then
3206 BeforeTurn0
3207 else
3208 BeforeTurn;
3209 if (Mode = moMovie) and (pTurn = 0) then
3210 begin
3211 Inform(pTurn);
3212 CallPlayer(cMovieTurn, 0, nil^);
3213 end;
3214 end;
3215{$IFDEF TEXTLOG}CmdInfo := CmdInfo + Format('---Turn %d P%d---', [GTurn, pTurn]); {$ENDIF}
3216 end;
3217 end; // sTurn
3218
3219 sBreak, sResign, sNextRound, sReload:
3220 if Mode = moMovie then
3221 MovieStopped := true
3222 else
3223 begin
3224 if Command = sReload then
3225 begin
3226 ok := (Difficulty[0] = 0) and (bix[0].Kind <> btNoTerm) and
3227 (integer(Data) >= 0) and (integer(Data) < GTurn);
3228 for p1 := 1 to nPl - 1 do
3229 if bix[p1].Kind = btTerm then
3230 ok := false;
3231 // allow reload in AI-only games only
3232 end
3233 else
3234 ok := Player = 0;
3235 if ok then
3236 begin
3237 if (Command = sBreak) or (Command = sResign) then
3238 Notify(ntBackOn);
3239 for i := 0 to Brains.Count - 1 do
3240 if Brains[i].Initialized then
3241 begin
3242 if Brains[i].Kind = btAI then
3243 Notify(ntDeinitModule, i);
3244 CallClient(i, cBreakGame, nil^);
3245 end;
3246 Notify(ntEndInfo);
3247 if (Command = sBreak) or (Command = sReload) then
3248 begin
3249 LogCityTileChanges;
3250 LogChanges;
3251 SaveGame(LogFileName, false);
3252 end;
3253 DeleteFile(SavePath + '~' + LogFileName);
3254 EndGame;
3255 case Command of
3256 sBreak:
3257 Notify(ntStartGoRefresh);
3258 sResign:
3259 Notify(ntStartGo);
3260 sNextRound:
3261 StartNewGame(SavePath, LogFileName, MapFileName, lx, ly,
3262 LandMass, MaxTurn);
3263 sReload:
3264 LoadGame(SavePath, LogFileName, integer(Data), false);
3265 end;
3266 end
3267 else
3268 result := eInvalid;
3269 end;
3270
3271 sAbandonMap, sSaveMap:
3272 if Player = 0 then
3273 begin
3274 if Command = sSaveMap then
3275 SaveMap(MapFileName);
3276 Notify(ntBackOn);
3277 BrainTerm.Client(cBreakGame, -1, nil^);
3278 ReleaseMapEditor;
3279 if Command = sSaveMap then
3280 Notify(ntStartGoRefreshMaps)
3281 else
3282 Notify(ntStartGo);
3283 end
3284 else
3285 result := eInvalid;
3286
3287 scContact .. scContact + (nPl - 1) shl 4, scContact - sExecute .. scContact
3288 - sExecute + (nPl - 1) shl 4:
3289 if (pDipActive >= 0) or (1 shl (Command shr 4 and $F) and GAlive = 0) then
3290 result := eInvalid
3291 else if GWinner > 0 then
3292 result := eViolation // game end reached
3293 else if RW[Player].Treaty[Command shr 4 and $F] = trNoContact then
3294 result := eNoPreq
3295 else if GTurn < GColdWarStart + ColdWarTurns then
3296 result := eColdWar
3297 else if RW[Player].Government = gAnarchy then
3298 result := eAnarchy
3299 else if RW[Command shr 4 and $F].Government = gAnarchy then
3300 begin
3301 result := eAnarchy;
3302 LastEndClientCommand := scReject; // enable cancel treaty
3303 pContacted := Command shr 4 and $F;
3304 end
3305 else if Command >= sExecute then
3306 begin // contact request
3307 pContacted := Command shr 4 and $F;
3308 pDipActive := pContacted;
3309 assert(Mode = moPlaying);
3310 Inform(pDipActive);
3311 ChangeClientWhenDone(scContact, pDipActive, pTurn, 4);
3312 end;
3313
3314 scReject, scReject - sExecute:
3315 if LastEndClientCommand and $FF0F = scContact then
3316 begin
3317 if Command >= sExecute then
3318 begin // contact requested and not accepted yet
3319 pDipActive := -1;
3320 assert(Mode = moPlaying);
3321 ChangeClientWhenDone(cContinue, pTurn, nil^, 0);
3322 end;
3323 end
3324 else
3325 result := eInvalid;
3326
3327 scDipStart, scDipStart - sExecute:
3328 if LastEndClientCommand and $FF0F = scContact then
3329 begin
3330 if Command >= sExecute then
3331 begin // accept contact
3332 pContacted := pDipActive;
3333 RW[pContacted].EnemyReport[pTurn].Credibility :=
3334 RW[pTurn].Credibility;
3335 pDipActive := pTurn;
3336 assert(Mode = moPlaying);
3337 IntServer(sIntHaveContact, pTurn, pContacted, nil^);
3338 ChangeClientWhenDone(scDipStart, pDipActive, nil^, 0);
3339 end;
3340 end
3341 else
3342 result := eInvalid;
3343
3344 scDipNotice, scDipAccept, scDipCancelTreaty, scDipBreak,
3345 scDipNotice - sExecute, scDipAccept - sExecute,
3346 scDipCancelTreaty - sExecute, scDipBreak - sExecute:
3347 if pDipActive >= 0 then
3348 begin
3349 assert(Mode = moPlaying);
3350 if pDipActive = pTurn then
3351 p1 := pContacted
3352 else
3353 p1 := pTurn;
3354 if (Command and not sExecute = scDipBreak and not sExecute) and
3355 (LastEndClientCommand <> scDipBreak) then // ok
3356 else if (Command and not sExecute = scDipNotice and not sExecute) and
3357 ((LastEndClientCommand = scDipCancelTreaty) or
3358 (LastEndClientCommand = scDipBreak)) then // ok
3359 else if (Command and not sExecute = scDipAccept and not sExecute) and
3360 (LastEndClientCommand = scDipOffer) then
3361 with LastOffer do
3362 begin
3363 // check if offer can be accepted
3364 if nDeliver + nCost = 0 then
3365 result := eOfferNotAcceptable;
3366 for i := 0 to nDeliver + nCost - 1 do
3367 if Price[i] = opChoose then
3368 result := eOfferNotAcceptable;
3369 for i := 0 to nCost - 1 do
3370 if not PayPrice(pDipActive, p1, Price[nDeliver + i], false) then
3371 result := eOfferNotAcceptable;
3372 if (Command >= sExecute) and (result >= rExecuted) then
3373 begin
3374 IntServer(sIntPayPrices + nDeliver + nCost, p1, pDipActive,
3375 LastOffer);
3376 // CheckContact;
3377
3378 // tell other players about ship part trades
3379 HasShipChanged := false;
3380 FillChar(ShowShipChange, SizeOf(ShowShipChange), 0);
3381 for i := 0 to nDeliver + nCost - 1 do
3382 if Price[i] and opMask = opShipParts then
3383 begin
3384 HasShipChanged := true;
3385 if i >= nDeliver then
3386 begin // p1 has demanded from pDipActive
3387 ShowShipChange.Ship1Change[Price[i] shr 16 and 3] :=
3388 +integer(Price[i] and $FFFF);
3389 ShowShipChange.Ship2Change[Price[i] shr 16 and 3] :=
3390 -integer(Price[i] and $FFFF);
3391 end
3392 else
3393 begin // p1 has delivered to pDipActive
3394 ShowShipChange.Ship1Change[Price[i] shr 16 and 3] :=
3395 -integer(Price[i] and $FFFF);
3396 ShowShipChange.Ship2Change[Price[i] shr 16 and 3] :=
3397 +integer(Price[i] and $FFFF);
3398 end;
3399 end;
3400 if HasShipChanged then
3401 begin
3402 ShowShipChange.Reason := scrTrade;
3403 ShowShipChange.Ship1Owner := p1;
3404 ShowShipChange.Ship2Owner := pDipActive;
3405 for p2 := 0 to nPl - 1 do
3406 if (p2 <> p1) and (p2 <> pDipActive) and
3407 (1 shl p2 and (GAlive or GWatching) <> 0) then
3408 begin
3409 move(GShip, RW[p2].Ship, SizeOf(GShip));
3410 if 1 shl p2 and GWatching <> 0 then
3411 CallPlayer(cShowShipChange, p2, ShowShipChange);
3412 end;
3413 end;
3414 end;
3415 end
3416 else if (Command and not sExecute = scDipCancelTreaty and not sExecute)
3417 and (RW[pDipActive].Treaty[p1] >= trPeace) then
3418 begin
3419 if (ServerVersion[pDipActive] >= $010100) and
3420 (GTurn < RW[pDipActive].LastCancelTreaty[p1] + CancelTreatyTurns)
3421 then
3422 result := eCancelTreatyRush
3423 else if Command >= sExecute then
3424 begin
3425 IntServer(sIntCancelTreaty, pDipActive, p1, nil^);
3426 for p2 := 0 to nPl - 1 do
3427 if (p2 <> p1) and (1 shl p2 and PeaceEnded <> 0) then
3428 begin
3429 i := p1 shl 4 + pDipActive;
3430 CallPlayer(cShowSupportAllianceAgainst, p2, i);
3431 end;
3432 for p2 := 0 to nPl - 1 do
3433 if (p2 <> p1) and (1 shl p2 and PeaceEnded <> 0) then
3434 begin
3435 i := p2;
3436 CallPlayer(cShowCancelTreatyByAlliance, pDipActive, i);
3437 end;
3438 end;
3439 end
3440 else
3441 result := eInvalid;
3442 if (Command >= sExecute) and (result >= rExecuted) then
3443 if LastEndClientCommand = scDipBreak then
3444 begin // break negotiation
3445 pDipActive := -1;
3446 CallPlayer(cShowEndContact, pContacted, nil^);
3447 ChangeClientWhenDone(cContinue, pTurn, nil^, 0);
3448 end
3449 else
3450 begin
3451 if (GTestFlags and tfUncover <> 0) or (Difficulty[0] = 0) then
3452 with ShowNegoData do
3453 begin // display negotiation in log window
3454 pSender := pDipActive;
3455 pTarget := p1;
3456 Action := Command;
3457 bix[0].Client(cShowNego, 1 shl 16 + 3, ShowNegoData);
3458 end;
3459 pDipActive := p1;
3460 ChangeClientWhenDone(Command, pDipActive, nil^, 0);
3461 end;
3462 end
3463 else
3464 result := eInvalid;
3465
3466 scDipOffer, scDipOffer - sExecute:
3467 if (pDipActive >= 0) and (LastEndClientCommand <> scDipCancelTreaty) and
3468 (LastEndClientCommand <> scDipBreak) then
3469 if (LastEndClientCommand = scDipOffer) and
3470 (LastOffer.nDeliver + LastOffer.nCost + TOffer(Data).nDeliver +
3471 TOffer(Data).nCost = 0) then
3472 begin
3473 if Command >= sExecute then
3474 begin // agreed discussion end
3475 pDipActive := -1;
3476 CallPlayer(cShowEndContact, pContacted, nil^);
3477 assert(Mode = moPlaying);
3478 ChangeClientWhenDone(cContinue, pTurn, nil^, 0);
3479 end;
3480 end
3481 else
3482 begin
3483 // check if offer can be made
3484 if pDipActive = pTurn then
3485 p1 := pContacted
3486 else
3487 p1 := pTurn;
3488 if RW[pDipActive].Treaty[p1] < trPeace then
3489 begin // no tribute allowed!
3490 for i := 0 to TOffer(Data).nDeliver + TOffer(Data).nCost - 1 do
3491 if (TOffer(Data).Price[i] and opMask = opTribute) then
3492 result := eInvalidOffer;
3493 for i := 0 to TOffer(Data).nDeliver + TOffer(Data).nCost - 1 do
3494 if (TOffer(Data).Price[i] = opTreaty + trPeace) then
3495 result := eOK;
3496 end;
3497 for i := 0 to TOffer(Data).nDeliver - 1 do
3498 if (TOffer(Data).Price[i] <> opChoose) and
3499 not PayPrice(pDipActive, p1, TOffer(Data).Price[i], false) then
3500 result := eInvalidOffer;
3501 if CountPrice(TOffer(Data), opTreaty) > 1 then
3502 result := eInvalidOffer;
3503 for i := 0 to nShipPart - 1 do
3504 if CountPrice(TOffer(Data), opShipParts + i shl 16) > 1 then
3505 result := eInvalidOffer;
3506 if CountPrice(TOffer(Data), opMoney) > 1 then
3507 result := eInvalidOffer;
3508 if CountPrice(TOffer(Data), opTribute) > 1 then
3509 result := eInvalidOffer;
3510 case CountPrice(TOffer(Data), opChoose) of
3511 0:
3512 ;
3513 1:
3514 if (TOffer(Data).nCost = 0) or (TOffer(Data).nDeliver = 0) then
3515 result := eInvalidOffer;
3516 else
3517 result := eInvalidOffer;
3518 end;
3519
3520 // !!! check here if cost can be demanded
3521
3522 if (Command >= sExecute) and (result >= rExecuted) then
3523 begin
3524 OfferFullySupported := (TOffer(Data).nDeliver <= 2) and
3525 (TOffer(Data).nCost <= 2); // >2 no more allowed
3526 for i := 0 to TOffer(Data).nDeliver + TOffer(Data).nCost - 1 do
3527 begin
3528 if TOffer(Data).Price[i] and opMask = opTribute then
3529 OfferFullySupported := false;
3530 // tribute no more part of the game
3531 if (TOffer(Data).Price[i] and opMask = opTreaty) and
3532 (TOffer(Data).Price[i] - opTreaty <= RW[pDipActive].Treaty[p1])
3533 then
3534 OfferFullySupported := false;
3535 // agreed treaty end no more part of the game
3536 if TOffer(Data).Price[i] = opTreaty + trCeaseFire then
3537 OfferFullySupported := false;
3538 // ceasefire no more part of the game
3539 end;
3540 if not OfferFullySupported then
3541 begin
3542 // some elements have been removed from the game -
3543 // automatically respond will null-offer
3544 LastOffer.nDeliver := 0;
3545 LastOffer.nCost := 0;
3546 ChangeClientWhenDone(scDipOffer, pDipActive, LastOffer,
3547 SizeOf(LastOffer));
3548 end
3549 else
3550 begin
3551 if (GTestFlags and tfUncover <> 0) or (Difficulty[0] = 0) then
3552 with ShowNegoData do
3553 begin // display negotiation in log window
3554 pSender := pDipActive;
3555 pTarget := p1;
3556 Action := Command;
3557 Offer := TOffer(Data);
3558 bix[0].Client(cShowNego, 1 shl 16 + 3, ShowNegoData);
3559 end;
3560 LastOffer := TOffer(Data);
3561 // show offered things to receiver
3562 for i := 0 to LastOffer.nDeliver - 1 do
3563 ShowPrice(pDipActive, p1, LastOffer.Price[i]);
3564 pDipActive := p1;
3565 assert(Mode = moPlaying);
3566 ChangeClientWhenDone(scDipOffer, pDipActive, LastOffer,
3567 SizeOf(LastOffer));
3568 end
3569 end
3570 end
3571 else
3572 result := eInvalid;
3573
3574 {
3575 General Commands
3576 ____________________________________________________________________
3577 }
3578 sClearTestFlag:
3579 if Player = 0 then
3580 begin
3581{$IFDEF TEXTLOG}CmdInfo := Format('ClearTestFlag %x', [Subject]); {$ENDIF}
3582 ClearTestFlags(Subject);
3583 end
3584 else
3585 result := eInvalid;
3586
3587 sSetTestFlag:
3588 if Player = 0 then
3589 begin
3590{$IFDEF TEXTLOG}CmdInfo := Format('SetTestFlag %x', [Subject]); {$ENDIF}
3591 SetTestFlags(Player, Subject);
3592 // CheckContact;
3593 end
3594 else
3595 result := eInvalid;
3596
3597 sSetGovernment, sSetGovernment - sExecute:
3598 begin
3599{$IFDEF TEXTLOG}CmdInfo := Format('SetGovernment P%d: %d', [Player, Subject]); {$ENDIF}
3600 if RW[Player].Happened and phChangeGov = 0 then
3601 result := eViolation
3602 else if RW[Player].Government = Subject then
3603 result := eNotChanged
3604 else if (Subject >= nGov) then
3605 result := eInvalid
3606 else if (Subject >= gMonarchy) and
3607 (RW[Player].Tech[GovPreq[Subject]] < tsApplicable) then
3608 result := eNoPreq
3609 else if Command >= sExecute then
3610 begin
3611 RW[Player].Government := Subject;
3612 for p1 := 0 to nPl - 1 do
3613 if (p1 <> Player) and ((GAlive or GWatching) and (1 shl p1) <> 0)
3614 then
3615 RW[p1].EnemyReport[Player].Government := Subject;
3616 end
3617 end;
3618
3619 sSetRates, sSetRates - sExecute:
3620 begin
3621{$IFDEF TEXTLOG}CmdInfo := Format('SetRates P%d: %d/%d', [Player, Subject and $F * 10, Subject shr 4 * 10]); {$ENDIF}
3622 if Subject and $F + Subject shr 4 > 10 then
3623 result := eInvalid
3624 else if (RW[Player].TaxRate = Subject and $F * 10) and
3625 (RW[Player].LuxRate = Subject shr 4 * 10) then
3626 result := eNotChanged
3627 else if Command >= sExecute then
3628 begin
3629 RW[Player].TaxRate := Subject and $F * 10;
3630 RW[Player].LuxRate := Subject shr 4 * 10;
3631 end
3632 end;
3633
3634 sRevolution:
3635 begin
3636{$IFDEF TEXTLOG}CmdInfo := Format('Revolution P%d', [Player]); {$ENDIF}
3637 if RW[Player].Government = gAnarchy then
3638 result := eInvalid
3639 else
3640 begin
3641 RW[Player].Government := gAnarchy;
3642 for p1 := 0 to nPl - 1 do
3643 if (p1 <> Player) and ((GAlive or GWatching) and (1 shl p1) <> 0)
3644 then
3645 RW[p1].EnemyReport[Player].Government := gAnarchy;
3646 RW[Player].AnarchyStart := GTurn;
3647 end;
3648 end;
3649
3650 sSetResearch, sSetResearch - sExecute:
3651 with RW[Player] do
3652 begin
3653{$IFDEF TEXTLOG}CmdInfo := Format('SetResearch P%d: %d', [Player, Subject]);
3654 {$ENDIF}
3655 if (Happened and phTech <> 0) and
3656 ((Subject < nAdv) or (Subject = adMilitary)) then
3657 begin
3658 if (Mode = moPlaying) and (Subject = adMilitary) and
3659 (DevModelTurn[Player] <> GTurn) then
3660 result := eNoModel
3661 else if Subject <> adMilitary then
3662 begin
3663 if Subject = futComputingTechnology then
3664 begin
3665 if Tech[Subject] >= MaxFutureTech_Computing then
3666 result := eInvalid;
3667 end
3668 else if Subject in FutureTech then
3669 begin
3670 if Tech[Subject] >= MaxFutureTech then
3671 result := eInvalid;
3672 end
3673 else if Tech[Subject] >= tsApplicable then
3674 result := eInvalid; // already discovered
3675 if Tech[Subject] <> tsSeen then // look if preqs met
3676 if AdvPreq[Subject, 2] <> preNone then
3677 begin // 2 of 3 required
3678 i := 0;
3679 for j := 0 to 2 do
3680 if Tech[AdvPreq[Subject, j]] >= tsApplicable then
3681 inc(i);
3682 if i < 2 then
3683 result := eNoPreq;
3684 end
3685 else if (AdvPreq[Subject, 0] <> preNone) and
3686 (Tech[AdvPreq[Subject, 0]] < tsApplicable) or
3687 (AdvPreq[Subject, 1] <> preNone) and
3688 (Tech[AdvPreq[Subject, 1]] < tsApplicable) then
3689 result := eNoPreq;
3690 end;
3691 if (result = eOK) and (Command >= sExecute) then
3692 begin
3693 if (Mode = moPlaying) and (Subject = adMilitary) then
3694 IntServer(sIntSetDevModel, Player, 0, DevModel.Kind);
3695 // save DevModel, because sctModel commands are not logged
3696 ResearchTech := Subject;
3697 end;
3698 end
3699 else
3700 result := eViolation;
3701 end;
3702
3703 sStealTech, sStealTech - sExecute:
3704 begin
3705{$IFDEF TEXTLOG}CmdInfo := Format('StealTech P%d: %d', [Player, Subject]);
3706 {$ENDIF}
3707 if RW[Player].Happened and phStealTech = 0 then
3708 result := eInvalid
3709 else if (Subject >= nAdv) or (Subject in FutureTech) or
3710 (RW[Player].Tech[Subject] >= tsSeen) or
3711 (RW[GStealFrom].Tech[Subject] < tsApplicable) then
3712 result := eInvalid
3713 else if Command >= sExecute then
3714 begin
3715 SeeTech(Player, Subject);
3716 dec(RW[Player].Happened, phStealTech);
3717 end;
3718 end;
3719
3720 sSetAttitude .. sSetAttitude + (nPl - 1) shl 4,
3721 sSetAttitude - sExecute .. sSetAttitude - sExecute + (nPl - 1) shl 4:
3722 begin
3723 p1 := Command shr 4 and $F;
3724{$IFDEF TEXTLOG}CmdInfo := Format('SetAttitude P%d to P%d: %d', [Player, p1, Subject]); {$ENDIF}
3725 if (Subject >= nAttitude) or (p1 >= nPl) or
3726 (RW[Player].EnemyReport[p1] = nil) then
3727 result := eInvalid
3728 else if RW[Player].Treaty[p1] = trNoContact then
3729 result := eNoPreq
3730 else if RW[Player].Attitude[p1] = Subject then
3731 result := eNotChanged
3732 else if Command >= sExecute then
3733 begin
3734 RW[Player].Attitude[p1] := Subject;
3735 RW[p1].EnemyReport[Player].Attitude := Subject;
3736 end;
3737 end;
3738
3739 sCancelTreaty, sCancelTreaty - sExecute:
3740 if (LastEndClientCommand <> scReject) or
3741 (RW[Player].Treaty[pContacted] < trPeace) then
3742 result := eInvalid
3743 else if (ServerVersion[Player] >= $010100) and
3744 (GTurn < RW[Player].LastCancelTreaty[pContacted] + CancelTreatyTurns)
3745 then
3746 result := eCancelTreatyRush
3747 else if Command >= sExecute then
3748 begin
3749 CallPlayer(cShowCancelTreaty, pContacted, Player);
3750 IntServer(sIntCancelTreaty, Player, pContacted, nil^);
3751 for p2 := 0 to nPl - 1 do
3752 if (p2 <> pContacted) and (1 shl p2 and PeaceEnded <> 0) then
3753 begin
3754 i := pContacted shl 4 + Player;
3755 CallPlayer(cShowSupportAllianceAgainst, p2, i);
3756 end;
3757 for p2 := 0 to nPl - 1 do
3758 if (p2 <> pContacted) and (1 shl p2 and PeaceEnded <> 0) then
3759 begin
3760 i := p2;
3761 CallPlayer(cShowCancelTreatyByAlliance, Player, i);
3762 end;
3763 LastEndClientCommand := sTurn;
3764 end;
3765
3766 {
3767 Model Related Commands
3768 ____________________________________________________________________
3769 }
3770 sCreateDevModel, sCreateDevModel - sExecute:
3771 begin
3772{$IFDEF TEXTLOG}CmdInfo := Format('CreateDevModel P%d', [Player]); {$ENDIF}
3773 if Subject >= 4 then
3774 result := eInvalid
3775 else if (upgrade[Subject, 0].Preq <> preNone) and
3776 (RW[Player].Tech[upgrade[Subject, 0].Preq] < tsApplicable) then
3777 result := eNoPreq
3778 else if Command >= sExecute then
3779 begin
3780 with RW[Player].DevModel do
3781 begin
3782 Domain := Subject;
3783 MStrength := 0;
3784 MTrans := 0;
3785 MCost := 0;
3786 Upgrades := 0;
3787 FutureMCost := 0;
3788 for i := 0 to nUpgrade - 1 do
3789 with upgrade[Domain, i] do
3790 if (Preq = preNone) or (Preq >= 0) and
3791 ((RW[Player].Tech[Preq] >= tsApplicable) or
3792 (Preq in FutureTech) and (RW[Player].Tech[Preq] >= 0)) then
3793 begin
3794 if Preq in FutureTech then
3795 begin
3796 j := RW[Player].Tech[Preq];
3797 inc(FutureMCost, j * Cost);
3798 end
3799 else
3800 begin
3801 j := 1;
3802 if Cost > MCost then
3803 MCost := Cost;
3804 end;
3805 inc(Upgrades, 1 shl i);
3806 inc(MStrength, j * Strength);
3807 inc(MTrans, j * Trans);
3808 end;
3809 inc(MCost, FutureMCost);
3810 FillChar(Cap, SizeOf(Cap), 0);
3811 Cap[mcOffense] := 2;
3812 Cap[mcDefense] := 1;
3813 for i := 0 to nFeature - 1 do
3814 with Feature[i] do
3815 if (1 shl Domain and Domains <> 0) and
3816 ((Preq = preNone) or (Preq = preSun) and
3817 (GWonder[woSun].EffectiveOwner = Player) or (Preq >= 0) and
3818 (RW[Player].Tech[Preq] >= tsApplicable)) and (i in AutoFeature)
3819 then
3820 Cap[i] := 1;
3821 MaxWeight := 5;
3822 if (WeightPreq7[Domain] <> preNA) and
3823 (RW[Player].Tech[WeightPreq7[Domain]] >= tsApplicable) then
3824 MaxWeight := 7;
3825 if (WeightPreq10[Domain] <> preNA) and
3826 (RW[Player].Tech[WeightPreq10[Domain]] >= tsApplicable) then
3827 if Domain = dSea then
3828 MaxWeight := 9
3829 else
3830 MaxWeight := 10;
3831 end;
3832 CalculateModel(RW[Player].DevModel);
3833 DevModelTurn[Player] := GTurn;
3834 end
3835 end;
3836
3837 sSetDevModelCap .. sSetDevModelCap + $3F0,
3838 sSetDevModelCap - sExecute .. sSetDevModelCap - sExecute + $3F0:
3839 begin
3840{$IFDEF TEXTLOG}CmdInfo := Format('SetDevModelCap P%d', [Player]); {$ENDIF}
3841 if Subject >= nFeature then
3842 result := eInvalid
3843 else if DevModelTurn[Player] = GTurn then
3844 begin
3845 NewCap := Command shr 4 and $3F; { new value }
3846 with RW[Player].DevModel do
3847 if 1 shl Domain and Feature[Subject].Domains = 0 then
3848 result := eDomainMismatch
3849 else if not((Feature[Subject].Preq = preNone) or
3850 (Feature[Subject].Preq = preSun) and
3851 (GWonder[woSun].EffectiveOwner = Player) or
3852 (Feature[Subject].Preq >= 0) and
3853 (RW[Player].Tech[Feature[Subject].Preq] >= tsApplicable)) then
3854 result := eNoPreq
3855 else
3856 begin
3857 if (Subject in AutoFeature) or (Subject = mcDefense) then
3858 MinCap := 1
3859 else
3860 MinCap := 0; { MinCap - minimum use of feature }
3861 if Subject >= mcFirstNonCap then
3862 MaxCap := 1
3863 else if Subject = mcDefense then
3864 begin
3865 if Domain = dGround then
3866 MaxCap := 2
3867 else
3868 MaxCap := 3;
3869 if RW[Player].Tech[adSteel] >= tsApplicable then
3870 inc(MaxCap);
3871 end
3872 else
3873 MaxCap := 8; { MaxCap - maximum use of this feature }
3874 if (Domain = dGround) and (Subject = mcDefense) then
3875 CapWeight := 2
3876 else
3877 CapWeight := Feature[Subject].Weight;
3878 if (NewCap < MinCap) or (NewCap > MaxCap) or
3879 (Weight + (NewCap - Cap[Subject]) * CapWeight > MaxWeight) then
3880 result := eViolation
3881 else if Command >= sExecute then
3882 begin
3883 Cap[Subject] := NewCap;
3884
3885 // mutual feature exclusion
3886 case Subject of
3887 mcSub:
3888 begin
3889 if ServerVersion[Player] >= $010103 then
3890 Cap[mcSeaTrans] := 0;
3891 Cap[mcArtillery] := 0;
3892 Cap[mcCarrier] := 0;
3893 if Cap[mcDefense] > 2 then
3894 Cap[mcDefense] := 2;
3895 end;
3896 mcSeaTrans:
3897 begin
3898 if ServerVersion[Player] >= $010103 then
3899 Cap[mcSub] := 0;
3900 end;
3901 mcCarrier:
3902 Cap[mcSub] := 0;
3903 mcArtillery:
3904 Cap[mcSub] := 0;
3905 mcAlpine:
3906 begin
3907 Cap[mcOver] := 0;
3908 Cap[mcMob] := 0;
3909 end;
3910 mcOver:
3911 Cap[mcAlpine] := 0;
3912 mcMob:
3913 begin
3914 Cap[mcAlpine] := 0;
3915 end;
3916 end;
3917
3918 CalculateModel(RW[Player].DevModel);
3919 end;
3920 end;
3921 end
3922 else
3923 result := eNoModel;
3924 end;
3925
3926 {
3927 Unit Related Commands
3928 ____________________________________________________________________
3929 }
3930 sRemoveUnit, sRemoveUnit - sExecute:
3931 begin
3932{$IFDEF TEXTLOG}CmdInfo := Format('RemoveUnit P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
3933 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
3934 result := eInvalid
3935 else
3936 begin
3937 result := eRemoved;
3938 Loc0 := RW[Player].Un[Subject].Loc;
3939 if RealMap[Loc0] and fCity <> 0 then { check utilize }
3940 begin
3941 SearchCity(Loc0, Player, cix1);
3942 with RW[Player].City[cix1] do
3943 begin
3944 if (RW[Player].Model[RW[Player].Un[Subject].mix].Kind = mkCaravan)
3945 and ((Project and cpImp = 0) or
3946 (Imp[Project and cpIndex].Kind <> ikShipPart)) or
3947 (Project and cpImp = 0) and
3948 (RW[Player].Model[Project and cpIndex].Kind <> mkCaravan) then
3949 result := eUtilized;
3950 if Command >= sExecute then
3951 begin
3952 if result = eUtilized then
3953 begin
3954 with RW[Player].Un[Subject] do
3955 begin
3956 Cost := integer(RW[Player].Model[mix].Cost) * Health *
3957 BuildCostMod[Difficulty[Player]] div 1200;
3958 if RW[Player].Model[mix].Cap[mcLine] > 0 then
3959 Cost := Cost div 2;
3960 end;
3961 if Project and (cpImp + cpIndex) = cpImp + imTrGoods then
3962 inc(RW[Player].Money, Cost)
3963 else
3964 begin
3965 inc(Prod, Cost * 2 div 3);
3966 Project0 := Project0 and not cpCompleted;
3967 if Project0 and not cpAuto <> Project and not cpAuto then
3968 Project0 := Project;
3969 Prod0 := Prod;
3970 end
3971 end;
3972 RemoveUnit_UpdateMap(Player, Subject);
3973 end;
3974 end;
3975 end
3976 else if Command >= sExecute then
3977 RemoveUnit_UpdateMap(Player, Subject);
3978 end
3979 end;
3980
3981 sSetUnitHome, sSetUnitHome - sExecute:
3982 begin
3983{$IFDEF TEXTLOG}CmdInfo := Format('SetUnitHome P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
3984 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
3985 result := eInvalid
3986 else
3987 begin
3988 Loc0 := RW[Player].Un[Subject].Loc;
3989 if RealMap[Loc0] and fCity = 0 then
3990 result := eInvalid
3991 else
3992 begin
3993 SearchCity(Loc0, Player, cix1);
3994 if RW[Player].City[cix1].Flags and chCaptured <> 0 then
3995 result := eViolation
3996 else if Command >= sExecute then
3997 RW[Player].Un[Subject].Home := cix1;
3998 end;
3999 end;
4000 end;
4001
4002 sSetSpyMission .. sSetSpyMission + (nSpyMission - 1) shl 4,
4003 sSetSpyMission - sExecute .. sSetSpyMission - sExecute +
4004 (nSpyMission - 1) shl 4:
4005 if Command >= sExecute then
4006 SpyMission := Command shr 4 and $F;
4007
4008 sLoadUnit, sLoadUnit - sExecute:
4009 begin
4010{$IFDEF TEXTLOG}CmdInfo := Format('LoadUnit P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
4011 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4012 result := eInvalid
4013 else
4014 result := LoadUnit(Player, Subject, Command < sExecute);
4015 end;
4016
4017 sUnloadUnit, sUnloadUnit - sExecute:
4018 begin
4019{$IFDEF TEXTLOG}CmdInfo := Format('UnloadUnit P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
4020 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4021 result := eInvalid
4022 else
4023 result := UnloadUnit(Player, Subject, Command < sExecute);
4024 end;
4025
4026 sSelectTransport, sSelectTransport - sExecute:
4027 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4028 result := eInvalid
4029 else
4030 with RW[Player].Model[RW[Player].Un[Subject].mix] do
4031 begin
4032 if Cap[mcSeaTrans] + Cap[mcAirTrans] + Cap[mcCarrier] = 0 then
4033 result := eInvalid
4034 else if Command >= sExecute then
4035 uixSelectedTransport := Subject;
4036 end;
4037
4038 sCreateUnit .. sCreateUnit + (nPl - 1) shl 4,
4039 sCreateUnit - sExecute .. sCreateUnit - sExecute + (nPl - 1) shl 4:
4040 if (GTestFlags and tfUncover <> 0) or (Difficulty[Player] = 0)
4041 then { supervisor only command }
4042 begin
4043 p1 := Command shr 4 and $F;
4044 Loc1 := integer(Data);
4045 if (Occupant[Loc1] >= 0) and (p1 <> Occupant[Loc1]) or
4046 (RealMap[Loc1] and fCity <> 0) and
4047 (RealMap[Loc1] shr 27 <> Cardinal(p1)) or
4048 (RW[p1].Model[Subject].Domain < dAir) and
4049 ((RW[p1].Model[Subject].Domain = dSea) <> (RealMap[integer(Data)] and
4050 fTerrain < fGrass)) then
4051 result := eViolation
4052 else if Command >= sExecute then
4053 begin
4054 CreateUnit(p1, Subject);
4055 RW[p1].Un[RW[p1].nUn - 1].Loc := integer(Data);
4056 PlaceUnit(p1, RW[p1].nUn - 1);
4057 UpdateUnitMap(integer(Data));
4058 end;
4059 end
4060 else
4061 result := eInvalid;
4062
4063 sMoveUnit + (0 + 6 * 8) * 16, sMoveUnit + (1 + 7 * 8) * 16,
4064 sMoveUnit + (2 + 0 * 8) * 16, sMoveUnit + (1 + 1 * 8) * 16,
4065 sMoveUnit + (0 + 2 * 8) * 16, sMoveUnit + (7 + 1 * 8) * 16,
4066 sMoveUnit + (6 + 0 * 8) * 16, sMoveUnit + (7 + 7 * 8) * 16,
4067 sMoveUnit - sExecute + (0 + 6 * 8) * 16, sMoveUnit - sExecute +
4068 (1 + 7 * 8) * 16, sMoveUnit - sExecute + (2 + 0 * 8) * 16,
4069 sMoveUnit - sExecute + (1 + 1 * 8) * 16, sMoveUnit - sExecute +
4070 (0 + 2 * 8) * 16, sMoveUnit - sExecute + (7 + 1 * 8) * 16,
4071 sMoveUnit - sExecute + (6 + 0 * 8) * 16, sMoveUnit - sExecute +
4072 (7 + 7 * 8) * 16:
4073 begin
4074 dx := (Command shr 4 + 4) and 7 - 4;
4075 dy := (Command shr 7 + 4) and 7 - 4;
4076{$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}
4077 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4078 result := eInvalid
4079 else
4080 result := MoveUnit(Player, Subject, dx, dy, Command < sExecute);
4081 end;
4082
4083 {
4084 Settlers Related Commands
4085 ____________________________________________________________________
4086 }
4087 sAddToCity, sAddToCity - sExecute:
4088 begin
4089{$IFDEF TEXTLOG}CmdInfo := Format('AddToCity P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
4090 if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
4091 result := eInvalid
4092 else if not(RW[Player].Model[RW[Player].Un[Subject].mix].Kind
4093 in [mkSettler, mkSlaves]) and
4094 (RW[Player].Un[Subject].Flags and unConscripts = 0) then
4095 result := eViolation
4096 else
4097 begin
4098 Loc0 := RW[Player].Un[Subject].Loc;
4099 if RealMap[Loc0] and fCity = 0 then
4100 result := eInvalid
4101 else
4102 begin
4103 SearchCity(Loc0, Player, cix1);
4104 with RW[Player].City[cix1] do
4105 if not CanCityGrow(Player, cix1) then
4106 result := eMaxSize
4107 else if Command >= sExecute then
4108 begin { add to city }
4109 if Mode = moPlaying then
4110 SavedTiles[cix1] := 0; // save in every case
4111 if CanCityGrow(Player, cix1) then
4112 CityGrowth(Player, cix1);
4113 if (RW[Player].Model[RW[Player].Un[Subject].mix]
4114 .Kind = mkSettler) and CanCityGrow(Player, cix1) then
4115 CityGrowth(Player, cix1);
4116 RemoveUnit_UpdateMap(Player, Subject);
4117 end;
4118 end;
4119 end;
4120 end;
4121
4122 sStartJob .. sStartJob + $3F0, sStartJob - sExecute .. sStartJob + $3F0
4123 - sExecute:
4124 begin
4125 Loc0 := RW[Player].Un[Subject].Loc;
4126 i := Command shr 4 and $3F; // new job
4127{$IFDEF TEXTLOG}CmdInfo := Format('StartJob P%d Mod%d Loc%d: %d', [Player, RW[Player].Un[Subject].mix, Loc0, i]); {$ENDIF}
4128 if (Subject >= RW[Player].nUn) or (Loc0 < 0) then
4129 result := eInvalid
4130 else if i >= nJob then
4131 result := eInvalid
4132 else
4133 begin
4134 result := StartJob(Player, Subject, i, Command < sExecute);
4135 if result = eCity then
4136 begin // new city
4137 cix1 := RW[Player].nCity - 1;
4138 AddBestCityTile(Player, cix1);
4139 if Mode = moPlaying then
4140 with RW[Player].City[cix1] do
4141 begin
4142 // SavedResourceWeights[cix1]:=ResourceWeights;
4143 SavedTiles[cix1] := 0; // save in every case
4144 end;
4145 if Mode >= moMovie then { show new city in interface modules }
4146 for p1 := 0 to nPl - 1 do
4147 if (1 shl p1 and GWatching <> 0) and (p1 <> Player) and
4148 (ObserveLevel[Loc0] and (3 shl (2 * p1)) > 0) then
4149 CallPlayer(cShowCityChanged, p1, Loc0);
4150 end;
4151 end;
4152 end;
4153
4154 {
4155 City Related Commands
4156 ____________________________________________________________________
4157 }
4158 sSetCityProject, sSetCityProject - sExecute:
4159 begin
4160 NewProject := integer(Data) and not cpAuto;
4161{$IFDEF TEXTLOG}CmdInfo := Format('SetCityProject P%d Loc%d: %d', [Player, RW[Player].City[Subject].Loc, NewProject]); {$ENDIF}
4162 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4163 then
4164 result := eInvalid
4165 else
4166 with RW[Player].City[Subject] do
4167 begin
4168 if NewProject = Project then
4169 result := eNotChanged
4170 else
4171 begin
4172 pt0 := ProjectType(Project0);
4173 pt1 := ProjectType(NewProject);
4174 if NewProject and cpImp = 0 then
4175 begin
4176 if NewProject and cpIndex >= RW[Player].nModel then
4177 result := eInvalid
4178 else if (NewProject and cpConscripts <> 0) and
4179 not((RW[Player].Tech[adConscription] >= tsApplicable) and
4180 (RW[Player].Model[NewProject and cpIndex].Domain = dGround)
4181 and (RW[Player].Model[NewProject and cpIndex].Kind < mkScout))
4182 then
4183 result := eViolation
4184 // else if (RW[Player].Model[NewProject and cpIndex].Kind=mkSlaves)
4185 // and (GWonder[woPyramids].EffectiveOwner<>Player) then
4186 // result:=eNoPreq
4187 end
4188 else if NewProject and cpIndex >= nImp then
4189 result := eInvalid
4190 else
4191 begin
4192 Preq := Imp[NewProject and cpIndex].Preq;
4193 for i := 0 to nImpReplacement - 1 do
4194 if (ImpReplacement[i].OldImp = NewProject and cpIndex) and
4195 (built[ImpReplacement[i].NewImp] > 0) then
4196 result := eObsolete;
4197 if result = eObsolete then
4198 else if Preq = preNA then
4199 result := eInvalid
4200 else if (Preq >= 0) and (RW[Player].Tech[Preq] < tsApplicable)
4201 then
4202 result := eNoPreq
4203 else if built[NewProject and cpIndex] > 0 then
4204 result := eInvalid
4205 else if (NewProject and cpIndex < nWonder) and
4206 (GWonder[NewProject and cpIndex].CityID <> WonderNotBuiltYet) then
4207 result := eViolation // wonder already exists
4208 else if (NewProject and cpIndex = imSpacePort) and
4209 (RW[Player].NatBuilt[imSpacePort] > 0) then
4210 result := eViolation // space port already exists
4211 else if (NewProject = cpImp + imBank) and (built[imMarket] = 0)
4212 or (NewProject = cpImp + imUniversity) and
4213 (built[imLibrary] = 0) or (NewProject = cpImp + imResLab) and
4214 (built[imUniversity] = 0) or (NewProject = cpImp + imMfgPlant)
4215 and (built[imFactory] = 0) then
4216 result := eNoPreq;
4217 case NewProject - cpImp of
4218 woLighthouse, woMagellan, imCoastalFort, imHarbor, imPlatform:
4219 begin { city at ocean? }
4220 Preq := 0;
4221 V8_to_Loc(Loc, Adjacent);
4222 for V8 := 0 to 7 do
4223 begin
4224 Loc1 := Adjacent[V8];
4225 if (Loc1 >= 0) and (Loc1 < MapSize) and
4226 (RealMap[Loc1] and fTerrain = fShore) then
4227 inc(Preq);
4228 end;
4229 if Preq = 0 then
4230 result := eNoPreq;
4231 end;
4232 woHoover, imHydro:
4233 begin { city at river or mountains? }
4234 Preq := 0;
4235 V8_to_Loc(Loc, Adjacent);
4236 for V8 := 0 to 7 do
4237 begin
4238 Loc1 := Adjacent[V8];
4239 if (Loc1 >= 0) and (Loc1 < MapSize) and
4240 ((RealMap[Loc1] and fTerrain = fMountains) or
4241 (RealMap[Loc1] and fRiver <> 0)) then
4242 inc(Preq);
4243 end;
4244 if Preq = 0 then
4245 result := eNoPreq;
4246 end;
4247 woMIR, imShipComp, imShipPow, imShipHab:
4248 if RW[Player].NatBuilt[imSpacePort] = 0 then
4249 result := eNoPreq;
4250 end;
4251 if (GTestFlags and tfNoRareNeed = 0) and
4252 (Imp[NewProject and cpIndex].Kind = ikShipPart) then
4253 if RW[Player].Tech[adMassProduction] < tsApplicable then
4254 result := eNoPreq
4255 else
4256 begin // check for rare resources
4257 if NewProject and cpIndex = imShipComp then
4258 j := 1
4259 else if NewProject and cpIndex = imShipPow then
4260 j := 2
4261 else { if NewProject and cpIndex=imShipHab then }
4262 j := 3;
4263 // j = rare resource required
4264 Preq := 0;
4265 V21_to_Loc(Loc, Radius);
4266 for V21 := 1 to 26 do
4267 begin
4268 Loc1 := Radius[V21];
4269 if (Loc1 >= 0) and (Loc1 < MapSize) and
4270 (RealMap[Loc1] shr 25 and 3 = Cardinal(j)) then
4271 inc(Preq);
4272 end;
4273 if Preq = 0 then
4274 result := eNoPreq;
4275 end;
4276 end;
4277
4278 if (Command >= sExecute) and (result >= rExecuted) then
4279 begin
4280 if pt0 <> ptSelect then
4281 if NewProject and (cpImp or cpIndex) = Project0 and
4282 (cpImp or cpIndex) then
4283 Prod := Prod0
4284 else if (pt1 = ptTrGoods) or (pt1 = ptShip) or (pt1 <> pt0)
4285 and (pt0 <> ptCaravan) then
4286 begin
4287 inc(RW[Player].Money, Prod0);
4288 Prod := 0;
4289 Prod0 := 0;
4290 Project0 := cpImp + imTrGoods;
4291 end
4292 else
4293 Prod := Prod0 * 2 div 3;
4294 Project := NewProject
4295 end;
4296 end;
4297 end;
4298 end;
4299
4300 sBuyCityProject, sBuyCityProject - sExecute:
4301 begin
4302{$IFDEF TEXTLOG}CmdInfo := Format('BuyCityProject P%d Loc%d', [Player, RW[Player].City[Subject].Loc]); {$ENDIF}
4303 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4304 then
4305 result := eInvalid
4306 else
4307 with RW[Player].City[Subject] do
4308 if (RW[Player].Government = gAnarchy) or (Flags and chCaptured <> 0)
4309 then
4310 result := eOutOfControl
4311 else if (Project and cpImp <> 0) and
4312 ((Project and cpIndex = imTrGoods) or
4313 (Imp[Project and cpIndex].Kind = ikShipPart)) then
4314 result := eInvalid // don't buy colony ship
4315 else
4316 begin
4317 CityReport.HypoTiles := -1;
4318 CityReport.HypoTax := -1;
4319 CityReport.HypoLux := -1;
4320 GetCityReport(Player, Subject, CityReport);
4321 Cost := CityReport.ProdCost;
4322 NextProd := CityReport.ProdRep - CityReport.Support;
4323 if (CityReport.Working - CityReport.Happy > Size shr 1) or
4324 (NextProd < 0) then // !!! change to new style disorder
4325 NextProd := 0;
4326 Cost := Cost - Prod - NextProd;
4327 if (GWonder[woMich].EffectiveOwner = Player) and
4328 (Project and cpImp <> 0) then
4329 Cost := Cost * 2
4330 else
4331 Cost := Cost * 4;
4332 if Cost <= 0 then
4333 result := eNotChanged
4334 else if Cost > RW[Player].Money then
4335 result := eViolation
4336 else if Command >= sExecute then
4337 IntServer(sIntBuyMaterial, Player, Subject, Cost);
4338 // need to save material/cost because city tiles are not correct
4339 // when loading
4340 end;
4341 end;
4342
4343 sSellCityProject, sSellCityProject - sExecute:
4344 begin
4345{$IFDEF TEXTLOG}CmdInfo := Format('SellCityProject P%d Loc%d', [Player, RW[Player].City[Subject].Loc]); {$ENDIF}
4346 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4347 then
4348 result := eInvalid
4349 else if Command >= sExecute then
4350 with RW[Player].City[Subject] do
4351 begin
4352 inc(RW[Player].Money, Prod0);
4353 Prod := 0;
4354 Prod0 := 0;
4355 end;
4356 end;
4357
4358 sSellCityImprovement, sSellCityImprovement - sExecute:
4359 begin
4360{$IFDEF TEXTLOG}CmdInfo := Format('SellCityImprovement P%d Loc%d: %d', [Player, RW[Player].City[Subject].Loc, integer(Data)]); {$ENDIF}
4361 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4362 then
4363 result := eInvalid
4364 else
4365 with RW[Player].City[Subject] do
4366 if built[integer(Data)] = 0 then
4367 result := eInvalid
4368 else if (RW[Player].Government = gAnarchy) or
4369 (Flags and chCaptured <> 0) then
4370 result := eOutOfControl
4371 else if Flags and chImprovementSold <> 0 then
4372 result := eOnlyOnce
4373 else if Command >= sExecute then
4374 begin
4375 inc(RW[Player].Money, Imp[integer(Data)].Cost * BuildCostMod
4376 [Difficulty[Player]] div 12);
4377 built[integer(Data)] := 0;
4378 if Imp[integer(Data)].Kind in [ikNatLocal, ikNatGlobal] then
4379 begin
4380 RW[Player].NatBuilt[integer(Data)] := 0;
4381 case integer(Data) of
4382 imGrWall:
4383 GrWallContinent[Player] := -1;
4384 imSpacePort:
4385 DestroySpacePort_TellPlayers(Player, -1);
4386 end;
4387 end;
4388 inc(Flags, chImprovementSold);
4389 end;
4390 end;
4391
4392 sRebuildCityImprovement, sRebuildCityImprovement - sExecute:
4393 begin
4394 OldImp := integer(Data);
4395{$IFDEF TEXTLOG}CmdInfo := Format('RebuildCityImprovement P%d Loc%d: %d', [Player, RW[Player].City[Subject].Loc, OldImp]); {$ENDIF}
4396 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4397 then
4398 result := eInvalid
4399 else
4400 begin
4401 if (OldImp < 0) or (OldImp >= nImp) or
4402 not(Imp[OldImp].Kind in [ikCommon, ikNatLocal, ikNatGlobal]) then
4403 result := eInvalid
4404 else
4405 with RW[Player].City[Subject] do
4406 if (built[OldImp] = 0) or (Project and cpImp = 0) or
4407 not(Imp[Project and cpIndex].Kind in [ikCommon, ikNatLocal,
4408 ikNatGlobal]) then
4409 result := eInvalid
4410 else if (RW[Player].Government = gAnarchy) or
4411 (Flags and chCaptured <> 0) then
4412 result := eOutOfControl
4413 else if Flags and chImprovementSold <> 0 then
4414 result := eOnlyOnce
4415 else if Command >= sExecute then
4416 begin
4417 inc(Prod, Imp[OldImp].Cost * BuildCostMod[Difficulty[Player]]
4418 div 12 * 2 div 3);
4419 Project0 := Project0 and not cpCompleted;
4420 if Project0 and not cpAuto <> Project and not cpAuto then
4421 Project0 := Project;
4422 Prod0 := Prod;
4423 built[OldImp] := 0;
4424 if Imp[OldImp].Kind in [ikNatLocal, ikNatGlobal] then
4425 begin // nat. project lost
4426 RW[Player].NatBuilt[OldImp] := 0;
4427 case OldImp of
4428 imGrWall:
4429 GrWallContinent[Player] := -1;
4430 imSpacePort:
4431 DestroySpacePort_TellPlayers(Player, -1);
4432 end;
4433 end;
4434 inc(Flags, chImprovementSold);
4435 end;
4436 end;
4437 end;
4438
4439 sSetCityTiles, sSetCityTiles - sExecute:
4440 begin
4441{$IFDEF TEXTLOG}CmdInfo := Format('SetCityTiles P%d Loc%d: %x', [Player, RW[Player].City[Subject].Loc, integer(Data)]); {$ENDIF}
4442 if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
4443 then
4444 result := eInvalid
4445 else
4446 result := SetCityTiles(Player, Subject, integer(Data),
4447 Command < sExecute);
4448 end;
4449
4450 {
4451 Client Exclusive Commands
4452 ____________________________________________________________________
4453 }
4454 else
4455 if Command >= cClientEx then
4456 begin
4457{$IFDEF TEXTLOG}CmdInfo := Format('ClientEx%x P%d', [Command, Player]);
4458 {$ENDIF}
4459 if ProcessClientData[Player] or (Mode = moPlaying) then
4460 CallPlayer(Command, Player, Data)
4461 end
4462 else
4463 result := eUnknown;
4464 end; { case command }
4465
4466 // do not log invalid and non-relevant commands
4467 if result = eZOC_EnemySpotted then
4468 begin
4469 assert(Mode = moPlaying);
4470 CL.State := FormerCLState;
4471 IntServer(sIntDiscoverZOC, Player, 0, ZOCTile);
4472 end
4473 else if result and rEffective = 0 then
4474 if Mode < moPlaying then
4475 begin
4476{$IFDEF TEXTLOG}CmdInfo := Format('***ERROR (%x) ', [result]) + CmdInfo;
4477 {$ENDIF}
4478 LoadOK := false;
4479 end
4480 else
4481 begin
4482 if logged then
4483 CL.State := FormerCLState;
4484 if (result < rExecuted) and (Command >= sExecute) then
4485 PutMessage(1 shl 16 + 1, Format('INVALID: %d calls %x (%d)',
4486 [Player, Command, Subject]));
4487 end;
4488
4489 if (Command and (cClientEx or sExecute or sctMask) = sExecute or sctEndClient)
4490 and (result >= rExecuted) then
4491 LastEndClientCommand := Command;
4492{$IFOPT O-}dec(nHandoverStack, 2); {$ENDIF}
4493end; { <<<server }
4494
4495
4496initialization
4497
4498FindFirst(ParamStr(0), $21, ExeInfo);
4499FindClose(ExeInfo);
4500
4501{$IFOPT O-}nHandoverStack := 0; {$ENDIF}
4502
4503end.
Note: See TracBrowser for help on using the repository browser.