source: branches/delphi/GameServer.pas

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