unit Player;

interface

uses
  NewtonImport,
  JEN_Header,
  JEN_Math,
  Trace;

type
  TWeapon = class
  private
    class var
      FHitrange : Single;
      FHitPower : Single;
      FHitNormal : TVec3f;
      FHitParam : Single;
      HitPoint : Tvec3f;
      BeginPoint, EndPoint : Tvec3f;
  public
    var
     FLastShot : LongInt;
     Scattering : Single;
     SpeedScatt : Single;
    class function RayCastFilter(const Body: PNewtonBody; const HitNormal: PFloat; CollisionID: Int; UserData: Pointer; IntersetParam: Float): Float; static; cdecl;
    procedure Shoot; virtual;
    procedure OnUpdate(dt : LongInt); virtual; abstract;
  end;

  TPistol = class(TWeapon)
    procedure Shoot; override;
    procedure OnUpdate(dt : LongInt); override;
  end;

  TSmg = class(TWeapon)
    procedure Shoot; override;
    procedure OnUpdate(dt : LongInt); override;
  end;

  TShotGun = class(TWeapon)
    procedure Shoot; override;
    procedure OnUpdate(dt : LongInt); override;
  end;

  TPlayer = class
  constructor Create;
  destructor Destroy; override;
  private
    FPos : TVec3f;
  public
    Speed : TVec3f;
    Heals : Single;
    Camera : ICamera3D;
    Weapon : TWeapon;
    function GetPos : TVec3f;
    procedure OnUpdate(dt: LongInt);
    procedure Hit(Power: Single);
  end;

implementation

uses
  Main,
  Enemies;

class function TWeapon.RayCastFilter(const Body: PNewtonBody; const HitNormal: PFloat; CollisionID: Int; UserData: Pointer; IntersetParam: Float): Float;
var
  Data : Pointer;
begin
  Data := NewtonBodyGetUserData(Body);
  if Assigned(Data) then
    TEnemy(Data).Hit(FHitPower);

  FHitNormal := PVec3f(HitNormal)^;
  FHitParam := IntersetParam;
  Result := IntersetParam;
end;

procedure TWeapon.Shoot;
var
  Dir : TVec3f;
  tAngle : Single;
begin
  Dir := Game.Player.Camera.Dir;
  Dir.y := -0.5;

  BeginPoint := Game.Player.GetPos + Dir+ Dir.Cross(Vec3f(0, 1, 0))*0.4;

  SinCos(Random * Pi * 2, Dir.x, Dir.y);
  tAngle  := deg2rad * FHitrange * Random;
  Dir    := Dir * sqrt(Abs(sqr(sin(tAngle))));
  Dir    := Vec3f(Dir.x, Dir.y, -Cos(tAngle));

  EndPoint := Game.Player.GetPos + Render.Matrix[mtView].GetRotPart.Inverse *Dir*1000;

  FHitParam := 1.0;
  NewtonWorldRayCast(Game.World, @BeginPoint, @EndPoint, @RayCastFilter, nil, nil);
end;

procedure TPistol.Shoot;
begin
  if (Utils.Time-FLastShot<400) then Exit;
  FHitPower := 30+random*10;
  FHitrange := Scattering + SpeedScatt;
  Scattering :=Scattering + (0.9-Scattering);
  inherited;
  FLastShot := Utils.Time;
  Game.TraceMng.Add(BeginPoint, BeginPoint + (EndPoint-BeginPoint) * FHitParam, 800, 0.02);
end;

procedure TPistol.OnUpdate(dt : LongInt);
begin
  Scattering := max(Scattering - dt/700, 0.4);
  SpeedScatt := Game.Player.Speed.Length/3;
end;

procedure TSmg.Shoot;
begin
  if (Utils.Time-FLastShot<100) then Exit;
  FHitPower := 12+random*5;
  FHitrange := Scattering + SpeedScatt;
  Scattering :=Scattering + (1.8-Scattering)*0.4;
  inherited;
  FLastShot := Utils.Time;
  Game.TraceMng.Add(BeginPoint, BeginPoint + (EndPoint-BeginPoint) * FHitParam, 800, 0.014);
end;

procedure TSmg.OnUpdate(dt : LongInt);
begin
  Scattering := max(Scattering - dt/500, 0.5);
  SpeedScatt := Game.Player.Speed.Length/2.0;
end;

procedure TShotGun.Shoot;
var
  i : integer;
begin
  if (Utils.Time-FLastShot<700) then Exit;
  FHitPower := 22+random*5;
  FHitrange := Scattering + SpeedScatt;
  Scattering :=Scattering + (4.0-Scattering)*1.2;
  for I := 0 to 5 do
  begin
  inherited;
  Game.TraceMng.Add(BeginPoint, BeginPoint + (EndPoint-BeginPoint) * FHitParam, 800, 0.025);
  end;

  FLastShot := Utils.Time;
end;

procedure TShotGun.OnUpdate(dt : LongInt);
begin
  Scattering := max(Scattering - dt/900, 3.0);
  SpeedScatt := Game.Player.Speed.Length/4.0;
end;

constructor TPlayer.Create;
begin
  Heals := 100;
  FPos := Game.Terrain.GetCenter+ vec3f(0,100,0);
  Camera := Helpers.CreateCamera3D;
  Camera.Pos := FPos ;
  Weapon := TPistol.Create;
end;

destructor TPlayer.Destroy;
begin
  Weapon.Free;
  inherited;
end;

function TPlayer.GetPos: TVec3f;
begin
  Result := FPos;
end;

procedure TPlayer.OnUpdate(dt: LongInt);
var
  s,c : Single;
  tAngle : Single;
  Dir : TVec3f;
  VSpeed : TVec3f;
begin
  SinCos(pi - Camera.Angle.y * deg2rad, s, c);
  Dir := Vec3f(s,0,c);

  VSpeed := Vec3f(0, 0, 0);
  if Input.Down[ikW] then VSpeed := VSpeed + Dir;
  if Input.Down[ikS] then VSpeed := VSpeed - Dir;
  if Input.Down[ikA] then VSpeed := VSpeed - Dir.Cross(Vec3f(0, 1, 0));
  if Input.Down[ikD] then VSpeed := VSpeed + Dir.Cross(Vec3f(0, 1, 0));

  if (Game.Terrain.GetCenter - FPos).Length > 55 then
   VSpeed:= (Game.Terrain.GetCenter-FPos).Normal;

  Speed := Speed.Lerp(VSpeed.Normal * 0.6, dt/100);
  if Speed.Length < 0.001 then
    Speed := Vec3f(0, 0, 0);
  FPos := FPos + Speed*(dt/100);
  FPos.y := Game.Terrain.GetHeight(FPos.x, FPos.z) + 2;

  if (Input.Down[ikMouseL]) then
    Weapon.Shoot;

  Camera.Pos := FPos;
  Camera.onUpdate(dt);
  Weapon.OnUpdate(dt);
end;

procedure TPlayer.Hit(Power: Single);
begin
  Heals := Max(Heals - Power, 0);
end;

end.
