⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cmdlist.pas

📁 类似文明的游戏源代码。
💻 PAS
字号:
{$INCLUDE switches}

unit CmdList;

interface

uses
Classes;

const
MaxDataSize=1024;

type
TLogData=array[0..999999999] of Byte;

TCmdListState=record
  nLog, {used size of LogData in bytes}
  LoadPos, {position in LogData when loading a game}
  LastMovingUnit: integer;
  MoveCode, LoadMoveCode: Cardinal;
  end;

TCmdList=class
  constructor Create;
  destructor Destroy; override;
  procedure Get(var Command, Player, Subject: integer; var Data: pointer);
  procedure GetDataChanges(Data: pointer; DataSize: integer);
  procedure Put(Command, Player, Subject: integer; Data: pointer);
  procedure PutDataChanges(Command, Player: integer; OldData, NewData: pointer; DataSize: integer);
  procedure LoadFromFile(const f: TFileStream);
  procedure SaveToFile(const f: TFileStream);
  procedure AppendToFile(const f: TFileStream; const OldState: TCmdListState);
  procedure Cut;
  function Progress: integer;
private
  LogAlloc: integer; {allocated size of LogData in bytes}
  LogData: ^TLogData;
  FState: TCmdListState;
  procedure PutData(Data: pointer; Length: integer);
  procedure CompleteMoveCode;
public
  property State: TCmdListState read FState write FState;
  end;

implementation

uses
Protocol;

const
LogGrow=1 shl 18;

type
TData=array[0..MaxDataSize-1] of Cardinal;
PData=^TData;

constructor TCmdList.Create;
begin
inherited Create;
FState.nLog:=0;
LogAlloc:=0;
LogData:=nil;
FState.LastMovingUnit:=-1;
FState.MoveCode:=0;
FState.LoadMoveCode:=0;
end;

destructor TCmdList.Destroy;
begin
ReallocMem(LogData, 0);
inherited Destroy;
end;

procedure TCmdList.Get(var Command, Player, Subject: integer; var Data: pointer);
var
DirCode, code: Cardinal;
begin
if FState.LoadMoveCode>0 then
  begin
  Player:=-1;
  if FState.LoadMoveCode and 1=1 then
    begin // FM
    DirCode:=FState.LoadMoveCode shr 1 and 7;
    Subject:=FState.LastMovingUnit;
    FState.LoadMoveCode:=FState.LoadMoveCode shr 4;
    end
  else
    begin // M
    DirCode:=FState.LoadMoveCode shr 3 and 7;
    Subject:=FState.LoadMoveCode shr 6 and $FFF;
    FState.LoadMoveCode:=FState.LoadMoveCode shr 18;
    FState.LastMovingUnit:=Subject
    end;
  case DirCode of
    0: Command:=sMoveUnit+$090;
    1: Command:=sMoveUnit+$0F0;
    2: Command:=sMoveUnit+$390;
    3: Command:=sMoveUnit+$3F0;
    4: Command:=sMoveUnit+$020;
    5: Command:=sMoveUnit+$060;
    6: Command:=sMoveUnit+$100;
    7: Command:=sMoveUnit+$300;
    end;
  Data:=nil;
  end
else
  begin
  code:=Cardinal((@LogData[FState.LoadPos])^);
  if code and 3=0 then
    begin // non-clientex command
    Command:=code shr 2 and $3FFF +sExecute;
    Player:=code shr 16 and $f;
    Subject:=code shr 20 and $FFF;
    inc(FState.LoadPos,4);
    end
  else if code and 7=2 then
    begin // clientex command
    Command:=code shr 3 and $FFFF;
    Player:=code shr 19 and $f;
    Subject:=0;
    inc(FState.LoadPos,3);
    end
  else
    begin // move command shortcut
    if (code and 1=1) and (code and (7 shl 4)<>6 shl 4) then
      begin FState.LoadMoveCode:=code and $FF; inc(FState.LoadPos) end
    else begin FState.LoadMoveCode:=code and $FFFFFF; inc(FState.LoadPos,3); end;
    Get(Command, Player, Subject, Data);
    exit;
    end;

  if Command and $f=0 then Data:=nil
  else
    begin
    Data:=@LogData[FState.LoadPos];
    inc(FState.LoadPos,Command and $f *4);
    end
  end
end;

procedure TCmdList.GetDataChanges(Data: pointer; DataSize: integer);
var
b0, b1: integer;
Map0, Map1: Cardinal;
begin
Map0:=Cardinal((@LogData[FState.LoadPos])^);
inc(FState.LoadPos,4);
b0:=0;
while Map0>0 do
  begin
  if Map0 and 1<>0 then
    begin
    Map1:=Cardinal((@LogData[FState.LoadPos])^);
    inc(FState.LoadPos,4);
    for b1:=0 to 31 do if 1 shl b1 and Map1<>0 then
      begin
      if b0*32+b1<DataSize then
        PData(Data)[b0*32+b1]:=Cardinal((@LogData[FState.LoadPos])^);
      inc(FState.LoadPos,4);
      end;
    end;
  inc(b0);
  Map0:=Map0 shr 1;
  end
end;

procedure TCmdList.Put(Command, Player, Subject: integer; Data: pointer);
var
DirCode, code: Cardinal;
begin
if Command and $FC00=sMoveUnit then
  begin // move command shortcut
  case Command of
    sMoveUnit+$090: DirCode:=0;
    sMoveUnit+$0F0: DirCode:=1;
    sMoveUnit+$390: DirCode:=2;
    sMoveUnit+$3F0: DirCode:=3;
    sMoveUnit+$020: DirCode:=4;
    sMoveUnit+$060: DirCode:=5;
    sMoveUnit+$100: DirCode:=6;
    sMoveUnit+$300: DirCode:=7;
    end;
  if Subject=FState.LastMovingUnit then code:=1+DirCode shl 1
  else code:=6+DirCode shl 3+Cardinal(Subject) shl 6;
  if FState.MoveCode=0 then FState.MoveCode:=code
  else if FState.MoveCode and 1=1 then
    begin // FM + this
    FState.MoveCode:=FState.MoveCode+code shl 4;
    if code and 1=1 then PutData(@FState.MoveCode, 1) // FM + FM
    else PutData(@FState.MoveCode, 3); // FM + M
    FState.MoveCode:=0;
    end
  else if code and 1=1 then
    begin // M + FM
    FState.MoveCode:=FState.MoveCode+code shl 18;
    PutData(@FState.MoveCode, 3);
    FState.MoveCode:=0;
    end
  else // M + M
    begin
    PutData(@FState.MoveCode, 3);
    FState.MoveCode:=code
    end;
  FState.LastMovingUnit:=Subject;
  end
else
  begin
  CompleteMoveCode;
  if Command>=cClientEx then
    begin
    code:=2+Command shl 3+Player shl 19;
    PutData(@code, 3);
    end
  else
    begin
    code:=Cardinal(Command-sExecute) shl 2+Cardinal(Player) shl 16
      +Cardinal(Subject) shl 20;
    PutData(@code, 4);
    end;
  end;
if Command and $f<>0 then PutData(Data, Command and $f *4);
end;

procedure TCmdList.PutDataChanges(Command, Player: integer; OldData,
  NewData: pointer; DataSize: integer);
var
MapPos, LogPos, b0, b1, RowEnd: integer;
Map0, Map1, code: Cardinal;
begin
if DataSize<=0 then exit;
if DataSize>MaxDataSize then DataSize:=MaxDataSize;
CompleteMoveCode;
MapPos:=FState.nLog+8;
LogPos:=MapPos+4;
Map0:=0;
for b0:=0 to (DataSize-1) div 32 do
  begin
  if LogPos+4*32>LogAlloc then
    begin
    inc(LogAlloc, LogGrow);
    ReallocMem(LogData, LogAlloc);
    end;
  Map0:=Map0 shr 1;
  Map1:=0;
  RowEnd:=DataSize-1;
  if RowEnd>b0*32+31 then RowEnd:=b0*32+31;
  for b1:=b0*32 to RowEnd do
    begin
    Map1:=Map1 shr 1;
    if PData(NewData)[b1]<>PData(OldData)[b1] then
      begin
      Cardinal((@LogData[LogPos])^):=PData(NewData)[b1];
      inc(LogPos,4);
      inc(Map1,$80000000);
      end;
    end;
  if Map1>0 then
    begin
    Map1:=Map1 shr (b0*32+31-RowEnd);
    Cardinal((@LogData[MapPos])^):=Map1;
    MapPos:=LogPos;
    inc(LogPos,4);
    inc(Map0,$80000000);
    end;
  end;
if Map0=0 then exit; // no changes

Map0:=Map0 shr (31-(DataSize-1) div 32);
Cardinal((@LogData[FState.nLog+4])^):=Map0;
code:=Cardinal(Command-sExecute) shl 2+Cardinal(Player) shl 16;
Cardinal((@LogData[FState.nLog])^):=code;
FState.nLog:=MapPos
end;

procedure TCmdList.PutData(Data: pointer; Length: integer);
begin
if FState.nLog+Length>LogAlloc then
  begin
  inc(LogAlloc, LogGrow);
  ReallocMem(LogData, LogAlloc);
  end;
move(Data^, LogData[FState.nLog], Length);
inc(FState.nLog, Length);
end;

procedure TCmdList.CompleteMoveCode;
begin
if FState.MoveCode>0 then
  begin
  if FState.MoveCode and 1=1 then PutData(@FState.MoveCode, 1) // Single FM
  else PutData(@FState.MoveCode, 3); // Single M
  FState.MoveCode:=0;
  end
end;

procedure TCmdList.LoadFromFile(const f: TFileStream);
begin
f.read(FState.nLog, 4);
LogData:=nil;
LogAlloc:=((FState.nLog+2) div LogGrow +1)*LogGrow;
ReallocMem(LogData, LogAlloc);
f.read(LogData^, FState.nLog);
FState.LoadPos:=0;
end;

procedure TCmdList.SaveToFile(const f: TFileStream);
begin
CompleteMoveCode;
f.write(FState.nLog, 4);
f.write(LogData^, FState.nLog)
end;

procedure TCmdList.AppendToFile(const f: TFileStream; const OldState: TCmdListState);
begin
CompleteMoveCode;
f.write(FState.nLog, 4);
f.Position:=f.Position+OldState.nLog;
f.write(LogData[OldState.nLog], FState.nLog-OldState.nLog)
end;

procedure TCmdList.Cut;
begin
FState.nLog:=FState.LoadPos-4; // take back last sTurn
end;

function TCmdList.Progress: integer;
begin
if (FState.LoadPos=FState.nLog) and (FState.LoadMoveCode=0) then
  result:=1000 // loading complete
else if FState.nLog>1 shl 20 then
  result:=(FState.LoadPos shr 8)*999 div (FState.nLog shr 8)
else result:=FState.LoadPos*999 div FState.nLog
end;

{Format Specification:

Non-ClientEx-Command:
  Byte3    Byte2    Byte1    Byte0
  ssssssss sssspppp cccccccc cccccc00
  (c = Command-sExecute, p = Player, s = Subject)

ClientEx-Command:
  Byte2    Byte1    Byte0
  0ppppccc cccccccc ccccc010
  (c = Command, p = Player)

Single Move:
  Byte2    Byte1    Byte0
  000000ss ssssssss ssaaa110
  (a = Direction, s = Subject)

Move + Follow Move:
  Byte2    Byte1    Byte0
  00bbb1ss ssssssss ssaaa110
  (a = Direction 1, s = Subject 1, b = Direction 2)

Follow Move + Move:
  Byte2    Byte1    Byte0
  00ssssss ssssssbb b110aaa1
  (a = Direction 1, b = Direction 2, s = Subject 2)

Single Follow Move:
  Byte0
  0000aaa1
  (a = Direction)

Double Follow Move:
  Byte0
  bbb1aaa1
  (a = Direction 1, b = Direction 2)
}

end.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -