unit ObjectsUnit;

interface
Uses
  GraphUtils,
  Direct3D9,
  MathUnit;

TYPE  
  TGameObject = class
    protected
      Game   : TGame; //Игровой объект
      Mesh   : TMesh; //Модель объекта

      MTransform : TD3DMatrix;  //матрица трансформаций положения (V-Матрица)
      VPOS       : TD3DVector;  //координаты центра масс
      VRIGHT     : TD3DVector;  //Вектор Right
      VUP        : TD3DVector;  //Вектор Up
      VDIR       : TD3DVector;  //Вектор Dir
      VANG       : TD3DVector;  //Вектор поворота
      VANGCur    : TD3DVector;  //Вектор поворота действующий
      VSIZE      : TD3DVector;  //масштаб
      VSpeed     : TD3DVector;  //Вектор скорости движения

      Enabled    : LongBool;     //Флаг использованности
      Visible    : LongBool;     //Флаг видимости(изменяет Frustum)
      SyncUpate  : Longint;     //Время последней обработки объекта
      SyncTime   : Longint;     //Интервал времени синхронизации объекта

      CollisionRec  : TCollisionRec; //Данные кубического размера меша и максимального радиуса

      Life       : Longint; //Жизнь

    private

      Procedure SetPos(const v: TD3DVector);

    public

      Property Pos : TD3DVector Read VPOS Write SetPos;
      Property GetVANG : TD3DVector Read VANG;
      Property SetVANG : TD3DVector Write VANG;
      Property ReadyToUpdate : LongBool Read Enabled;
      Property CollisionBox : TCollisionRec Read CollisionRec;
      Property SetLife : Longint Write Life;
      Property GetLife : Longint Read Life;

      Constructor Create(GameObj : TGame; MeshObj : TMesh; WorldCoord : TD3DVector); Virtual;
      Procedure Free; Virtual;
      Procedure Draw; Virtual;
      Procedure Process; Virtual;

      Procedure RollX(Angle : Single);
      Procedure RollY(Angle : Single);
      Procedure RollZ(Angle : Single);

      Procedure Speed(S : Single);

      Procedure Scale(X,Y,Z : Single);
  end;


  //Данные отдельного спрайта/объекта:
  PMyObject = ^TMyObject;
  TMyObject = record
    Pred, Next : PMyObject;  //Указатель на предыдущий и следующий объект
    GameObject : TGameObject;
    Flags      : Cardinal; //Флаги состояния (0)
  end;

  TGameObjects = class
    protected
      Game   : TGame; //Игровой объект

      Start,Finish : PMyObject; //Указатель на первый и последний объект в цепочке
      Count        : Longint; //Кол-во спрайтов в цепочке (Изменяется автоматически при добавлении и удалении)
    private
    public
      Property GetCount : Longint Read Count;
      Property GetStart : PMyObject Read Start;

      Constructor Create(GameObj : TGame);
      Procedure Free;
      Procedure Draw;
      Procedure Process;

      Procedure Clear;

      Function Add(Var GameObject : TGameObject) : PMyObject;
      Procedure Del(Var MyObject : PMyObject);

      Function GetLength(Pos1,Pos2 : TD3DVector) : Single;
      Function GetCollision(GameObject : TGameObject) : PMyObject; Overload;
      Function GetCollision(Pos : TD3DVector; R : Single) : PMyObject; Overload;
      Function GetCollision(FromObject : PMyObject; Pos : TD3DVector; R : Single) : PMyObject; Overload;
      Function GetAngle(GameObject1,GameObject2 : TGameObject) : Single; Overload;
      Function GetAngle(x1,y1,x2,y2 : Single) : Single; Overload;
  end;

implementation

Uses
  Math;
  
//============================================================================//
//============================================================================//
//============================================================================//

Constructor TGameObject.Create(GameObj : TGame; MeshObj : TMesh; WorldCoord : TD3DVector);
begin
  Inherited Create;

  Enabled    := True;

  Game:=GameObj;
  Mesh:=MeshObj;

  MTransform := IdentityMatrix;  //матрица трансформаций положения (V-Матрица)
  CollisionRec:=Mesh.CollisionBox;
  SetPos(WorldCoord);

  VRIGHT     := ZeroVector;  //Вектор Right
  VUP        := ZeroVector;  //Вектор Up
  VDIR       := ZeroVector;  //Вектор Dir
  VANG       := ZeroVector;  //постоянная составляющая угловой скорости
  VSpeed     := ZeroVector;

  VSIZE      := OneVector;  //масштаб
  Enabled    := True;        //Флаг использованности
  Visible    := True;        //Флаг видимости(изменяет Frustum)
  SyncUpate  := 0;//Game.GetCurrentTime; //Время последней обработки объекта
  SyncTime   := 20;          //Интервал времени синхронизации объекта

  VANGCur    := ZeroVector;  //Вектор поворота действующий

  Scale(0.8,0.75,0.8);

  Life:=100;

end;

//============================================================================//

Procedure TGameObject.Free;
begin
  Inherited;
end;

//============================================================================//

Procedure TGameObject.Draw;
begin
  if Mesh<>nil then
  begin
    Game.GetDevice.SetTransform(D3DTS_WORLD, MTransform);
    Mesh.Render;
  end;
end;

//============================================================================//

Procedure TGameObject.Process;
Var
 Mat, MRotX,MRotY,MRotZ : TD3DMatrix;
 VecMove : TD3DVector;
begin
  Enabled:=Game.GetCurrentTime-SyncUpate>=SyncTime;
  if not Enabled then Exit;

  MTransform:=IdentityMatrix;

  VecMove:=VSpeed;

  if VANGCur.X<>0 then
  begin
    VANG.x:=VANG.x+VANGCur.x;
    SetRotateXMatrix(MRotX, VAng.X);
    MTransform:=MatrixMul(MTransform, MRotX);
  end;

  if VANGCur.Y<>0 then
  begin
    VANG.y:=VANG.y+VANGCur.y;
    if VANG.y>(360 * PIover180) then VANG.y:=VANG.y-(360 * PIover180);
    if VANG.y<-(360 * PIover180) then VANG.y:=VANG.y+(360 * PIover180);

    SetRotateYMatrix(MRotY, VAng.Y);
    MTransform:=MatrixMul(MTransform, MRotY);
  end;

  if VANGCur.Z<>0 then
  begin
    VANG.z:=VANG.z+VANGCur.z;
    SetRotateZMatrix(MRotZ, VAng.Z);
    MTransform:=MatrixMul(MTransform, MRotZ);
  end;

  SetScaleMatrix(mat,VSIZE);
  MTransform:=MatrixMul(MTransform, Mat);

  if VecMove.z<>0 then
  begin
    SetRotateYMatrix(MRotY, -VAng.Y);
    VecMove:=MatrixVectorMul(VecMove,MRotY);
    VPOS:=VectorAdd(VPOS,VecMove);
  end;

  SetPos(VPOS);

  SyncUpate:=Game.GetCurrentTime;
end;

//============================================================================//

Procedure TGameObject.SetPos(const v: TD3DVector);
begin
  MTransform._41 := v.x;
  MTransform._42 := v.y;
  MTransform._43 := v.z;
  VPOS           := v;
end;

//============================================================================//

Procedure TGameObject.RollX(Angle : Single);
begin
  VANGCur.x:=AngleToRadian(Angle);
end;

//============================================================================//

Procedure TGameObject.RollY(Angle : Single);
begin
  VANGCur.Y:=AngleToRadian(Angle);
end;

//============================================================================//

Procedure TGameObject.RollZ(Angle : Single);
begin
  VANGCur.Z:=AngleToRadian(Angle);
end;

//============================================================================//

Procedure TGameObject.Scale(X,Y,Z : Single);
Var
 Mat : TD3DMatrix;
 Max : Single;
begin
  VSIZE.x:=X;
  VSIZE.y:=Y;
  VSIZE.z:=Z;
  //Соответственно увеличиваем размер данных коробки столкновения:
  With Mesh.CollisionBox do
  begin
  CollisionRec.Up:=Up*Y;
  CollisionRec.Down:=Down*Y;
  CollisionRec.Left:=Left*X;
  CollisionRec.Right:=Right*X;
  CollisionRec.Front:=Front*Z;
  CollisionRec.Back:=Back*Z;
  end;
  Max:=0;

  if Max<Abs(CollisionRec.Up) then Max:=Abs(CollisionRec.Up);
  if Max<Abs(CollisionRec.Down) then Max:=Abs(CollisionRec.Down);
  if Max<Abs(CollisionRec.Right) then Max:=Abs(CollisionRec.Right);
  if Max<Abs(CollisionRec.Left) then Max:=Abs(CollisionRec.Left);
  if Max<Abs(CollisionRec.Back) then Max:=Abs(CollisionRec.Back);
  if Max<Abs(CollisionRec.Front) then Max:=Abs(CollisionRec.Front);
  CollisionRec.MaxRadius:=Max;
end;

//============================================================================//

Procedure TGameObject.Speed(S : Single);
begin
  VSpeed.z:=-S;
//  VSpeed.x:=S;
end;

//============================================================================//
//============================================================================//
//============================================================================//

{ TGameObjects }

constructor TGameObjects.Create(GameObj: TGame);
begin
  Inherited Create;
  Game:=GameObj;
  Start:=nil;
  Finish:=nil;
  Count:=0;
end;

procedure TGameObjects.Free;
begin
  Clear;
  Inherited;
end;

procedure TGameObjects.Draw;
Var
  MyObject : PMyObject;
begin
  MyObject:=Start;
  While MyObject<>nil do
  begin
    MyObject^.GameObject.Draw;
    MyObject:=MyObject^.Next;
  end;
end;

procedure TGameObjects.Process;
Var
  MyObject : PMyObject;
begin
  MyObject:=Start;
  While MyObject<>nil do
  begin
    MyObject^.GameObject.Process;
    //Удаление умершего объекта:
    if MyObject^.GameObject.Enabled then
    if MyObject^.GameObject.GetLife<=0 then Del(MyObject) else
    MyObject:=MyObject^.Next else MyObject:=MyObject^.Next;      
  end;
end;

function TGameObjects.Add(var GameObject : TGameObject): PMyObject;
Var
  MyObject : PMyObject;
begin
  if nil=GameObject then Exit;

  New(MyObject); //Выделение памяти
  MyObject^.Flags:=0;

  //Если спрайт с созданием в цепочке, то записать туда свои данные:
  MyObject^.Pred:=Finish;
  MyObject^.Next:=nil;

  If nil=MyObject^.Pred then
  begin //Если запись первая то присвоить первую и последнюю запись
    Start:=MyObject;
    Finish:=MyObject;
  end else //Если записи уже есть в списке, то назначить указатели
  begin
    Finish^.Next:=MyObject;
    Finish:=MyObject;
  end;

  MyObject^.GameObject:=GameObject;

  Inc(Count); //Увеличиваем счётчик созданных спрайтов в данной цепочке

  Result:=MyObject;
end;

procedure TGameObjects.Del(var MyObject: PMyObject);
Var
  TempMyObject : PMyObject;
begin
  if MyObject=nil then exit;

  TempMyObject:=MyObject^.Next;

  //Склеиваем цепочку до и после текущего спрайта:
  if MyObject^.Pred<>Nil then MyObject^.Pred^.Next:=MyObject^.Next;
  if MyObject^.Next<>nil then MyObject^.Next^.Pred:=MyObject^.Pred;

  if MyObject=Start then
  begin
    if Start^.Next<>nil then Start^.Next^.Pred:=MyObject^.Pred;
    Start:=Start^.Next;
  end;
  if MyObject=Finish then
  begin
    if Finish^.Pred<>nil then Finish^.Pred^.Next:=MyObject^.Next;
    Finish:=Finish^.Pred;
  end;

  Dec(Count); //Уменьшаем счётчик созданных спрайтов в данной цепочке

  MyObject.GameObject.Free;

  Dispose(MyObject);  //Освобождение занимаемой памяти
  MyObject:=TempMyObject; //Присваиваем указателю на спрайт следующий спрайт за удалённым
end;

procedure TGameObjects.Clear;
Var
  MyObject : PMyObject;
begin
  MyObject:=Start;
  While MyObject<>nil do
  begin
    Del(MyObject);
  end;
  Start:=nil;
  Finish:=nil;

  Inherited;
end;

function TGameObjects.GetCollision(GameObject: TGameObject): PMyObject;
Var
  MyObject : PMyObject;
  L : Double;
begin
  Result:=nil;
  MyObject:=Start;
  While MyObject<>nil do
  begin
    L:=GetLength(MyObject.GameObject.VPOS, GameObject.VPOS);
    if L < (GameObject.CollisionBox.MaxRadius+MyObject.GameObject.CollisionBox.MaxRadius) then
    begin
      if GameObject<>MyObject.GameObject then //Самого себя считать не нужно
      begin
        Result:=MyObject;
        Exit;
      end;
    end;
    MyObject:=MyObject.Next;
  end;
end;

function TGameObjects.GetCollision(Pos: TD3DVector; R: Single): PMyObject;
Var
  MyObject : PMyObject;
  L : Single;
begin
  Result:=nil;
  MyObject:=Start;
  While MyObject<>nil do
  begin
    L:=GetLength(MyObject.GameObject.VPOS, Pos);
    if L < (R+MyObject.GameObject.CollisionBox.MaxRadius) then
    begin
      Result:=MyObject;
      Exit;
    end;
    MyObject:=MyObject.Next;
  end;
end;

function TGameObjects.GetCollision(FromObject: PMyObject; Pos: TD3DVector; R: Single): PMyObject;
Var
  MyObject : PMyObject;
  L : Single;
begin
  Result:=nil;
  if FromObject=nil then FromObject:=Start;
  if FromObject=nil then Exit;
  MyObject:=FromObject^.Next;
  While MyObject<>nil do
  begin
    L:=GetLength(MyObject.GameObject.VPOS, Pos);
    if L < (R+MyObject.GameObject.CollisionBox.MaxRadius) then
    begin
      Result:=MyObject;
      Exit;
    end;
    MyObject:=MyObject.Next;
  end;
end;

function TGameObjects.GetAngle(GameObject1, GameObject2: TGameObject): Single;
Var
  X1,Y1,X2,Y2 : Single;
begin
  X1:=GameObject1.VPOS.x;
  Y1:=GameObject1.VPOS.z;
  X2:=GameObject2.VPOS.x;
  Y2:=GameObject2.VPOS.z;

  Result:=ArcSin(Sqrt((X2-X1)*(X2-X1))/Sqrt((X1-X2)*(X1-X2)+(Y1-Y2)*(Y1-Y2)));
end;

function TGameObjects.GetAngle(x1, y1, x2, y2: Single): Single;
begin
  Result :=(180*PIover180)-ArcTan2( (X1 - X2),(Y1 - Y2) );
end;

function TGameObjects.GetLength(Pos1, Pos2: TD3DVector): Single;
Var
  X,Y,Z : Single;
begin
    X:=(POS1.x-Pos2.x);
    Y:=(POS1.y-Pos2.y);
    Z:=(POS1.z-Pos2.z);
    Result:=(SQRT((X*X+Y*Y+Z*Z)));
end;

end.

//============================================================================//
//============================================================================//
//============================================================================//

