unit uGame;

interface

uses uGLobal, Windows, Math, Graphics, uPlayer, uMap, uWeapon, VectorGeometry,
  VectorTypes, uObjects, uMaterials;

var
  //main
  Camera, Light: integer;

  //Player
  Players: array of TPlayer;
  Fire: integer;

  CurrentBuilding: integer;
  DialogShowed: boolean = false;

  //Environment
  LensFlare: integer;

  //gui
  Cross, Font, skin, Label1: integer;

  //Camera
  CamAngX, CamAngY: Double;
  CamDist: Double = 28;

  //other
  LastFPSUpdate: integer;
  Keys: array[0..255] of boolean;
  MLD, MRD: boolean;

  Time: Double;
  NPCStep: integer;

  WindLoop: integer = 0;

  Points: integer;

var
  MX: integer = 320;
  MY: integer = 240;

procedure GameInit; stdcall;
function GameProcess(Params: array of variant): variant; stdcall;

function GameOnKeyDown(Params: array of variant): variant; stdcall;
function GameOnKeyUp(Params: array of variant): variant; stdcall;
function GameOnMouseDown(Params: array of variant): variant; stdcall;
function GameOnMouseUp(Params: array of variant): variant; stdcall;
function GameOnMouseMove(Params: array of variant): variant; stdcall;

function building_dialog_ok(Params: array of variant): variant; stdcall;

implementation

function GameOnKeyDown(Params: array of variant): variant; stdcall;
begin
  Keys[integer(Params[0])]:=true;

  if (integer(Params[0])=ord('E'))and(CurrentBuilding<>-1) then
  begin
    DialogShowed:=true;
    call('glGUIVisible', ['building_dialog', true]);

    if CurrentBuilding=0 then
      call('glGUIText',['building_dialog_label','Приветсвую тебя, странник.'#13#10 +
      'Вероятно ты хочешь прикупить оружия?'#13#10   +
      'К сожалению, у механика была сессия,'#13#10  +
      'так что твой пулемет лучшее(и единственное)'#13#10   +
      'оружие в игре.']);
    if CurrentBuilding=1 then
      call('glGUIText',['building_dialog_label','Приветсвую тебя, путник.'#13#10 +
      'Тут возле города носится какой-то'#13#10   +
      'долбанутый домик, можешь пострелять в него.']);

    ShowCursor(true);
  end;

  if integer(Params[0])=VK_ESCAPE then call('shutdown', []);
end;

function GameOnKeyUp(Params: array of variant): variant; stdcall;
begin
  Keys[integer(Params[0])]:=false;
end;

function GameOnMouseDown(Params: array of variant): variant; stdcall;
begin
  if TMouseButton(Params[0])=mbLeft then MLD:=true;
  if TMouseButton(Params[0])=mbRight then MRD:=true;
end;

function GameOnMouseUp(Params: array of variant): variant; stdcall;
begin
  if TMouseButton(Params[0])=mbLeft then MLD:=false;
  if TMouseButton(Params[0])=mbRight then MRD:=false;
end;

function GameOnMouseMove(Params: array of variant): variant; stdcall;
begin
end;

function building_dialog_ok(Params: array of variant): variant; stdcall;
begin
  DialogShowed:=false;
  call('glGUIVisible', ['building_dialog', false]);
  SetCursorPos(Mx, My);
  ShowCursor(false);
end;

procedure OnCollusion(BuildingID, Player: integer);
begin
  if player<>0 then exit;
  CurrentBuilding:=BuildingID;
end;

procedure OnUnCollusion(BuildingID, Player: integer);
begin
  if player<>0 then exit;
  CurrentBuilding:=-1;
end;

procedure GameInit; stdcall;
var
  X, Y: array of double;
  I: Integer;
begin
  call('glClearColor', [255, 255, 255]);
  Call('glOnProcess',['GameProcess']);
  call('glEvent', [caOnKeyDown, 'GameOnKeyDown']);
  call('glEvent', [caOnKeyUp, 'GameOnKeyUp']);
  call('glEvent', [caOnMouseDown, 'GameOnMouseDown']);
  call('glEvent', [caOnMouseUp, 'GameOnMouseUp']);
  call('glEvent', [caOnMouseMove, 'GameOnMouseMove']);

  SStep:=call('alLoadSound',['Data/SFX/Step.wav']);
  SSMGFire:=call('alLoadSound',['Data/SFX/smg_fire.wav']);
  WoodBullet:=call('alLoadSound',['Data/SFX/wood_bullet.wav']);

  LoadMaterials;
  LoadPFX;
  MapInit;
     {
  SetLength(X, 4);
  X[0]:=0.82; x[1]:=0.7; x[2]:=0.6; x[3]:=0.01;
  call('glFog', [true, 300, 1000, x, fmExp2]);   }

  Label1:=call('glGUIAdd', [gtLabel]);
  call('glGUIFont', [Label1, Font]);
  call('glGUIPosition', [Label1, 0,0]);
  call('glGUISize', [Label1, 0,40]);

  //Player
  SetLength(Players, 2);
  for I := 0 to length(Players) - 1 do
  begin
    Players[i]:=TPlayer.Create(mtBiped);
    Players[i].Weapons[0].RotationX:=-2;
    Players[i].Weapons[1].RotationX:=2;
  end;
  Players[1].WeaponRotationY:=20;
  Players[1].Position[0]:=-15;
    Wind:=call('alLoadSound',['Data/SFX/wind.mp3']);
  i:=call('alObjectSound', [camera , Wind]);
  call('alLoops', [camera , 999, i]);
  call('alVolume', [camera, 0.5, i]);
  call('alPlay', [camera, true, i]);

  Cross:=call('glObjectAdd', [otHUDSprite, 'cross',-1]);
  call('glObjectMaterial', [Cross, 'cross']);
  call('glPosition', [Cross, WndSize[0]/2-2, WndSize[1]/2.35, 0]);
  call('glObjectParams', [Cross, 15, 15]);

  //call('alPlayDirect', [Wind]);

  call('glCameraTarget',[Camera, call('poaObjectID', [Players[0].BodyModel, 1])]);

  //Start
  LastFPSUpdate:=-1;
  ShowCursor(false);

  x:=call('glWndMove', []);
  y:=call('glWndSize', []);
  mx:=round(x[0]+y[0]/2);
  my:=round(x[1]+y[1]/2);

  i:=AddBuilding;
  Buildings[i].Model.Model:=call('model_saloon', []);
  Buildings[i].Model.Radius:=35;
  Buildings[i].Portal.Radius:=45;
  Buildings[i].IconLoad('icon_saloon');
  Buildings[i].Place(30,0,30);
  Buildings[i].OnCollusion:=OnCollusion;
  Buildings[i].OnUnCollusion:=OnUnCollusion;

  i:=AddBuilding;
  Buildings[i].Model.Model:=call('model_jail', []);
  Buildings[i].Model.Rotation[1]:=90;
  Buildings[i].Model.Radius:=35;
  Buildings[i].Portal.Radius:=45;
  Buildings[i].IconLoad('icon_jail');
  Buildings[i].Place(-40,4,-50);     
  Buildings[i].OnCollusion:=OnCollusion;
  Buildings[i].OnUnCollusion:=OnUnCollusion;

  i:=AddObject;
  Objects[i].Model:=call('model_house', []);
  Objects[i].Rotation[1]:=45;
  Objects[i].Radius:=28;
  Objects[i].Place(20,-3,-30);

  i:=AddObject;
  Objects[i].Model:=call('model_house', []);
  Objects[i].Rotation[1]:=150;
  Objects[i].Radius:=28;
  Objects[i].Place(-90,-3,-15);
                      
  i:=AddObject;
  Objects[i].Model:=call('model_house', []);
  Objects[i].Rotation[1]:=180;
  Objects[i].Radius:=28;
  Objects[i].Place(-100,0,40);

  i:=AddObject;
  Objects[i].Model:=call('model_house', []);
  Objects[i].Rotation[1]:=45;
  Objects[i].Radius:=28;
  Objects[i].Place(20,-3,-30);

  i:=AddObject;
  Objects[i]:=TMapObject.Create;
  Objects[i].Model:=call('model_cactus', []);
  Objects[i].Rotation[1]:=90;
  Objects[i].Radius:=5;
  Objects[i].Place(10,0,0);

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

  //Create Form
  call('glGUIAdd',[gtForm, 'building_dialog']);
  call('glGUIStyle',['building_dialog','form']);
  call('glGUISize',['building_dialog',400,200]);
  call('glGUIPosition',['building_dialog',WndSize[0]/2-400/2,WndSize[1]/2-200/2]);
  call('glGUIAlpha', ['building_dialog', 1]);
  call('glGUIVisible', ['building_dialog', false]);

  //create button
  call('glGUIAdd',[gtButton, 'building_dialog_ok']);
  call('glGUIStyle',['building_dialog_ok','button_up','button_down']);
  call('glGUISize',['building_dialog_ok',360,30]);
  call('glGUIText',['building_dialog_ok','OK']);
  call('glObjectParent', ['building_dialog_ok', 'building_dialog']);
  call('glGUIPosition', ['building_dialog_ok', 20, 200-50]);
  call('glGUIEvent',['building_dialog_ok', caOnClick, 'building_dialog_ok']);

  //create button
  call('glGUIAdd',[gtLabel, 'building_dialog_label']);
  call('glGUIStyle',['building_dialog_label','button_up','button_down']);
  call('glGUISize',['building_dialog_label',180,150]);
  call('glObjectParent', ['building_dialog_label', 'building_dialog']);
  call('glGUIPosition', ['building_dialog_label', 20, 20]);

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

  call('glLogMaterials', []);
  call('glLogPFX', []);
  call('glLogObjects', []);
end;

var
  Scr: integer = 0;

procedure CameraMove;
var
  dx, dy: integer;
  P: TPoint;
  CamPos: array[0..2] of Double;
begin
  GetCursorPos(P);
  dx:=MX-P.X;
  dy:=MY-P.Y;

  CamAngX:=CamAngX+dx;
  CamAngY:=CamAngY+dy;

  if CamAngY>50 then  CamAngY:=50;
  if CamAngY<-30 then  CamAngY:=-30;
  if CamAngX<-90+Players[0].Rotation then CamAngX:=-90+Players[0].Rotation;
  if CamAngX>90+Players[0].Rotation then CamAngX:=90+Players[0].Rotation;

  SetCursorPos(Mx, My);

  CamPos[0]:=Players[0].Position[0]+CamDist;
  CamPos[1]:=Players[0].Position[1]+CamDist;
  CamPos[2]:=Players[0].Position[2];

  call('glPosition', [Camera, CamPos[0], CamPos[1], CamPos[2]]);
  call('glCameraMoveAroundTarget', [Camera, CamAngX, CamAngY]);

  Players[0].WeaponRotationX:=CamAngX;
  Players[0].WeaponRotationY:=CamAngY+10;
end;

procedure AI(deltaTime: Double);
var
  v1, v2: TVector3f;
  vc: Double;
  NewAngle, AngleToPlayer, DistanceToPlayer: Double;
begin
  v1:=vector3fmake( Players[0].Position[0], Players[0].Position[1], Players[0].Position[2]);
  v2:=vector3fmake( Players[1].Position[0], Players[1].Position[1], Players[1].Position[2]);
  v1:=VectorSubtract(v1, v2);
  DistanceToPlayer:=VectorLength(v1);
  NormalizeVector(v1);
  vc:=-v1[0];
  AngleToPlayer:=radtodeg(arccos(vc))*(abs(v1[2])/v1[2]);
  NewAngle:=AngleToPlayer;
  if NewAngle<-90+(Players[1].Rotation-(trunc(Players[1].Rotation/360)*360)) then NewAngle:=-90+(Players[1].Rotation-(trunc(Players[1].Rotation/360)*360))-180;
  if NewAngle>90+(Players[1].Rotation-(trunc(Players[1].Rotation/360)*360)) then NewAngle:=90+(Players[1].Rotation-(trunc(Players[1].Rotation/360)*360))-180;

  if ((AngleToPlayer+30>=NewAngle)and(AngleToPlayer-30<=NewAngle))AND(DistanceToPlayer<100) then
  begin
    Players[1].Shot(deltaTime, 0);
  end;
  Players[1].WeaponRotationX:=NewAngle;
end;

function GameProcess(Params: array of variant): variant; stdcall;
var
  x: Integer;
begin
  call('pfxAcceleration', ['smoke', sin(Params[1]+random(10)),1,cos(Params[1]+random(10))]);

  Players[0].Movement:=[];

  if not DialogShowed then
  begin
    if Keys[ord('W')] then Players[0].Movement:=Players[0].Movement+[mdForward];
    if Keys[ord('S')] then Players[0].Movement:=Players[0].Movement+[mdBackward];
    if Keys[ord('A')] then Players[0].Movement:=Players[0].Movement+[mdLeft];
    if Keys[ord('D')] then Players[0].Movement:=Players[0].Movement+[mdRight];

    Players[1].Movement:=[mdForward, mdLeft];

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

    if MLD then
    begin
      Players[0].Shot(Params[0], 0);
      call('pfxEnabled', [Players[0].Effects[2], true]);
    end else call('pfxEnabled', [Players[0].Effects[2], false]);
    if MRD then
    begin
      Players[0].Shot(Params[0], 1);
      call('pfxEnabled', [Players[0].Effects[3], true]);
    end else call('pfxEnabled', [Players[0].Effects[3], false]);

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

    CameraMove;
  end;
  AI(Params[0]);
  WeaponProcess(Params[0]);
  for x := 0 to length(Players) - 1 do Players[x].Draw(Params[0]);

  //===================================
  if Params[1]>LastFPSUpdate+1 then
  begin
    call('glFPS', ['OpenGL [%s]']);
    LastFPSUpdate:=Params[1];
  end;
  call('glGUIText', [Label1, 'NPC Health: '+inttostr(round(Players[1].Health))+#13#10+'Your Health: '+inttostr(round(Players[0].Health))]);

  Time:=Params[1];
end;

end.
