Code Search for Developers
 
 
  

GameEngine.pas from Sector-37 at Krugle


Show GameEngine.pas syntax highlighted


                        //**************************//
                        //                          //
                        //   Strange Game Engine    //
                        //                          //
//**********************//**************************//**********************//
//                                                                          //
//        This unit is the core of a real time 3d space strategy engine     //
//                      (www.federation.netfirms.com)                       //
//                                                                          //
//**************************************************************************//

{
Author  : Alexander Federyakov aka Da Stranger
Version : 0.001 ;-]
Date    : 18 December '2005
License : You can use this unit ONLY for educational purposes. You cannot use
          more than 10% of its code in your own personal projects. It cannot
          be used for any commercial  products.
Comments: This is the core of a 3d realtime space strategy game engine.
          Much is still to be done...
Requirements:  GLScene (www.glscene.org)
               see the "uses" section...

Known Bugs and limitations:
          This engine is not yet complete, it's just a demo, so expect everything...
          Use at your own risk.

Contacts:
    Web:    http://www.sector-37.com
    E-mail: datarget@mail.ru
    ICQ:    293-963-070
    Odigo:  8077227

}

unit GameEngine;

interface

uses //standard
     Classes, Windows, IniFiles, Sysutils, Graphics, Dialogs, Controls,
     ExtCtrls, DesignIntf,

     //third-party units
     RXGif, JPG, jpeg,

     //GLScene
     GLVectorFileObjects, VectorTypes, GLParticleFX, GLGeomObjects,
     GLFireFX, GLPerlinPFX, GLThorfx, GLObjects, VectorGeometry, GlScene,
     GLTExture, Forms, GLCadencer, GLWin32Viewer, GLCanvas,
     q3md3 ,glmisc, GLWindows, GLGraph,GLHUDObjects,OpenGL1x,
     GLWindowsFont, GLMaterialMultiProxy, GLWin32FullScreenViewer,
     glMaterialScript, PersistentClasses, GLKeyboard, ApplicationFileIO,

     //independant modules
     StrangeSpace, StrangeCollisionManager, StrangeModelManager, StrangeExplosion, StrangeMouseEmulator,
     StrangeMovementManager, StrangeEngines,GLSmoothNavigator, GLSmoothNavigator, StrangeCameraUtilities,
     StrangeMathUtilities, GLConsole, StrangeTextureUtilities, StrangeIniFiles, StrangeUnitHUD,

     //GameEngine Classes
     GameUnitClasses, GameEconomyClasses, GameStatisticsClasses,
     GameConstants, GameSettings, GameClassAncestors;

type
//Classes that need to be declared before everything...
  TUnitGroup = class;
  TUnit = class;
  TGame = class;
  TPlayer = class;
  TPlanet = class;
  TUnitWeaponGroup = class;

//classes


TCustomGameObject = class
public
  GameObject: TObject;
  Player: TPlayer;
  ObjectType: TGameObjectType;//or byte, 'cause I already declared a constant for it
  ID: integer;
  index: integer;
end;

TGameUnit = class
private
  Game: TGame;
  index: integer;
  ID: integer;
public
  ForceNotExists: Boolean;//is set to True, Exists() will always return False
  GameUnit: TUnit;
  function Exists: Boolean;
  function isAlive: Boolean;//checks for existance first...
  procedure Assign(FromUnit: TUnit); overload;
  procedure Assign(From: TGameUnit); overload;
  constructor Create(FromUnit: TUnit);
end;

TSimpleUnitList = class
private
  FList: TPersistentObjectList;
public
  onUpdate: TNotifyEvent;

  constructor Create;
  destructor Destroy; override;

  procedure AddUnit(WnatUnit: TObject; CheckForExistanceInList: Boolean = False; CallUpdateEvent: Boolean = False);
  procedure RemoveUnit(SomeUnit : TUnit); overload;
  procedure RemoveUnit(index : integer); overload;
  function GetUnit(index : integer): TUnit;
  procedure Clear;
  function LastUnit: integer;
  function UnitCount: integer;
  function Count: integer;//the same as above...
  function GetCenterDot: TVector3f;
  property DaUnit [index: integer]: TUnit read GetUnit; default;

  procedure MassMoveByWaypoints(const Points: array of TVector3f);
  procedure MassPatrol(const Points: array of TVector3f);
  procedure MassMove(const Target: TVector3f);
end;


TUnitList = class
private
  FList: TPersistentObjectList;
  function GetGameUnitX(index: integer): TGameUnit;
public
  constructor Create;
  destructor Destroy;override;

  procedure CleanUpListUnits;

  procedure AddUnit(WnatUnit: TUnit; CheckForExistanceInList: Boolean = False);
  procedure AddUnits(WnatUnits: array of TUnit; CheckForExistanceInList: Boolean = False);
  procedure ReplaceUnits(WnatUnits: array of TUnit; CheckForExistanceInList: Boolean = False);

  procedure RemoveUnit(WnatUnit: TUnit); overload;
  procedure RemoveUnit(index: integer); overload;

  //if unit is not found, it is removed from the list
  function GetUnit(index: integer; CheckForExistance: Boolean = True): TUnit;
  function GetGameUnit(index: integer; CheckForExistance: Boolean= True): TGameUnit;
  function GetAliveUnit(index: integer; CheckForExistance: Boolean= True): TUnit;

  //by ID...
  function UnitExists(index: integer; RemoveIfNotExists: Boolean = True):Boolean;
  function UnitAlive(index: integer; RemoveIfNotAlive: Boolean = True):Boolean;

  function UnitExistsInList(WnatUnit: TUnit): Boolean; overload;
  function UnitExistsInList(WnatUnit: TUnit; var index: integer): Boolean;overload;

  function LastUnit: integer;
  function UnitCount: integer;
  function Count: integer; //the same as UnitCount
  procedure Clear;
  property GameUnit [index:integer]: TGameUnit read GetGameUnitX; default;
end;

TPlanet = class
  public
  Player: TPlayer;
  ID: integer;

  Name: String;
  Buildings: array of TBuildingData;     //your race buildings
  AlienBuildings: array of TBuildingData;//buildings left after an alien race, which you can't use...
  Upgrades: array of TUpgradeData;
  UnitsInside: array of TUnit;
  Mines: array of TMine;
  MaxBuildingSlots: byte;
  Specialty: TPlanetSpecialty;

  PlanetClass: byte;
  NativeRace: byte;
  PolutionLevel: byte;

  Resources: array of single;

  Population: integer;
  MaxPopulation: integer;
  Scientists,
  Workers,
  Engineers: integer;

  TaxRate: byte;
  Morale: byte;

  EstimatedIncome: single;
  EstimatedPopulationIncrease: single;

  ProductionLevel,
  ResearchLevel,
  ImplementationLevel: single;
  // then "speed" of doing things...
end;


TAIPlayerSettings = class//CPU Player settings
private
  Player: TPlayer;
  //
public
  procedure Proceed(Delta, NewTime: single);
  constructor Create;
end;

TRemotePlayerSettings = class//TCP-IP Player settings
private
  Player: TPlayer;
  //
public
  procedure Proceed(Delta, NewTime: single);
  constructor Create;
end;

TLocalPlayerPreferences = record
  MillitarySelectionPriority: Boolean;
  SelectPlanetWhenSelectingUnits: Boolean;
end;

TLocalPlayerSettings = class //Main Players' settings
private
  Player: TPlayer;
  Controls: TPlayerControls;

  IntendedCommand: byte;

  orig_xx, orig_yy,        //where user pressed the mouse button
  xx, yy: integer;          //current mouse position relative
  abs_xx, abs_yy: integer;            //current mouse position absolute
  ShiftState: TShiftState;

  MoveAroundTargetOldX,
  MoveAroundTargetOldY: single;

  TargetDot: TVector2f;
  CurrentHeight: single;// above/beyond Disc height
  Disk: TGLDisk;
  TransparentDisk: TGLDisk;
  MovedCameraWhileSendingUnits: Boolean;
  MovedLeftButton : Boolean;
  NeedToRestoreCursorPosition: Boolean;

  PatrolDots: TStrangeVector3fList;

  FCursorVisible: Boolean;

  procedure MoveUnits;
  procedure PrepareToMove;

  procedure DrawSelectionBox;
  procedure DrawMoveToLine(var rci : TRenderContextInfo);
  procedure DrawMoveByWaypointsLine(var rci : TRenderContextInfo);

  procedure DetachCamera;
  procedure AttachCamera(Target : TGLBaseSceneObject);
    //Viewer Render
  procedure onBeforeRender(Sender: TObject);
  procedure onPostRender(Sender: TObject);
    //Viewer Mouse
  procedure OnMouseDown(Sender: TObject;Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  procedure OnMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
  procedure OnMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  procedure OnDblClick(Sender: TObject);
  procedure OnMouseWheel(Sender: TObject; Shift: TShiftState;WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
  procedure onKeyDown(const Key: word);
    //DirectGL
  procedure OnDirectGLRender (Sender: TObject; var rci : TRenderContextInfo);
  procedure OnConsoleCommandIssued(const ConsoleCommand: TGLConsoleCommand; const Console: TGLCustomConsole; var Command: TStrangeUserInputCommand);

  procedure SetCursorVisible(Value: Boolean);
  procedure Proceed(Delta, NewTime: single);
public
  Camera: TglCamera;
  UI: TGLSmoothUserInterface;
  Navigator: TGLSmoothNavigator;

  Viewer: TGLSceneViewer;
  FullViewer: TGLFullScreenViewer;
  DirectGL:TGLDirectOpenGL;
  FPSCounter: TglHUDText;
  CursorHint: TglHUDText;
  CursorHint2: TglHUDText;
  MouseEmulator: TStrangeMouseEmulator;

  PickList: TSimpleUnitList;
  CurrentPlanet: TPlanet;// for interface
  CurrentObject: TGLBaseSceneObject;    // for Unit hints
  CameraOwner: TGLBaseSceneObject;  // for controling Camera

  Preferences: TLocalPlayerPreferences;

  ControlType: byte; //determines if controlled by GLKeyboard, mouse or joystic
  Console :TGLConsole;
//  Gui: TGLBaseSceneObject;

  function GetClickPosition(xx, yy:integer; height: single): TVector3f; overload;
  function GetClickPosition2f(xx, yy:integer; height: single): TVector2f; overload;
  procedure SelectUnits(x, y:integer; prev_x, prev_y: integer);
  procedure SetCurrentObject(x, y: integer);



  //virtual cursor
  procedure GetCursorPosition(var x, y : integer);//absolute position mouse position
  procedure GetRelativeCursorPosition(var x, y : integer);//relative mouse position (to Viewer)
  procedure SetCursorPosition(const x, y : integer);//absolute mouse position
  procedure SetRelativeCursorPosition(const x, y : integer);//relative mouse position (to Viewer)
  procedure SetRelativeCursorY(const y : integer);//relative mouse position (to Viewer)

  function Moved_A_Little: Boolean;

  procedure SetCursor(CursorType: byte; Update: Boolean = True);
  procedure UpdateCursor;
  function IsShiftDown: Boolean;

  //constructors-destructors
  destructor Destroy; override;
  constructor Create(Game: TGame);

  property CursorVisible: Boolean read FCursorVisible write SetCursorVisible ;
end;


TGameSceneObjects = class
private
  Game: TGame;
  procedure OnGLCadencerProgress(Sender: TObject; const deltaTime, newTime: Double);
  procedure ShowFPS(Sender:TObject);
public
  Owner, KeyEventOwner: TWinControl;
  Scene: TglScene;
  Cadencer: TGLCadencer;
  UnitMaterialLibrary: TglMaterialLibrary;
  MiscMaterialLibrary: TglMaterialLibrary;
  MaterialScripter: TglMaterialScripter;
  PartileRenderer: TglParticleFXRenderer;
  Grid: TglXYZGrid;
  FirstObject,TransparentObject, LastObject, LastTransparentObject: TglBaseSceneObject;//for transparency...
  FPSTimer: TTimer;

  TestHUDSprite: TGLHUDSprite; //for testing reasons { TODO -oDa Stranger -cDebug : Debug }

  SystemFont: TGLWindowsBitmapFont; //Console, FPS... other stuff...
  HintFont : TGLWindowsBitmapFont;  //hint that appears next to a highlighted object
  //KeyPress event handlers
  procedure  OnKeyUp (Sender: TObject; var Key: Word;Shift: TShiftState);
  procedure  OnKeyPress (Sender: TObject; var Key: Char);
  procedure OnKeyDown (Sender: TObject; var Key: Word;Shift: TShiftState);

  //constructors-destructors
  constructor Create(WhatOwner, WhatKeyEventOwner: TWinControl);
  destructor Destroy; override;
end;

//All the rest classes
TCampaign = class
private
  Game: TGame;
public
  Name: string;

  procedure  LoadCampaign;
  destructor Destroy;override;
end;

TMapPlayerData = record
  Location: TVector3f;
  Standard: Boolean;
  OnlyAI: Boolean;
  end;

TMission = class(TBaseGameObject)
private
  Game: TGame;
  procedure onReadFromSpaceIni (Sender: TStrangeSpace; const ini: TStrangeIniFile; SpaceObjectType: string; Section: String; SpaceObjectArrayIndex: integer);
protected
  procedure LoadFromIni (ini: TStrangeIniFile; Section: string); override;
public
  SpaceFileName: string;
  Size: string;
  MapSize: TVector3f;
  Preview: TPicture;

  MaxPlayers: byte;
  MaxHumanPlayers: byte;
  MapPlayerData: array of TMapPlayerData;

  Space: TStrangeSpace;

  procedure  LoadMission;
  constructor Create;
  destructor Destroy; override;
end;

//Class responsible for current status of a weapon
TUnitWeaponData = class
private
  {$Hints off}
  function GetSectorUnit(Sector: TVector3s; index: integer): TUnit;
  function GetSectorObjectNumber(Sector: TVector3s): integer;
  {$Hints on}
  function GetNearbyUnit(index: integer): TUnit;
  function GetNearbyObjectsListCount: Integer;
public
  Owner: TUnit;
  WeaponGroup: TUnitWeaponGroup;

  WeaponCube: TGLBaseSceneObject;
  WeaponProxy: TGLMaterialMultiProxy;
  TipPosition: TGLBaseSceneObject;  //for holding efects  like thor, needed for lazer mode
  DeadWeapon: TglActor;

  ThorFx: TGLThorFXManager;
  GLBthorFX: TGLBthorFX;

  TimeRecharging: single;  //used when recharging
  CurrentBurst: byte;      //used when recharging

  isFiring: Boolean;   //used in LaserMode
  TimeFiringLaser: single;//used in LaserMode

  Target: TGameUnit;

  procedure StopFiringLaser;
  procedure StartFiringWeapon(Where: TVector3f);
  procedure TryToAttack(delta: single);
  procedure FindNextTarget;

  procedure GetDistancesToTarget(const FromPoint: TVector3f; var arr: array of single);
  function GetMaxDistanceToTarget(const FromPoint: TVector3f): single;
  function GetMinDistanceToTarget(const FromPoint: TVector3f): single;

  function inLineofFire(Where: PAffineVector = nil): Boolean;
  function GetAbsoluteWeaponDirection: TVector3f;
  function GetMinimalAngleToTarget(const Direction: TVector3f): single;
  constructor Create;
  destructor Destroy; override;
end;

TUnitWeaponGroup = class
public
  Weapons: array of TUnitWeaponData;
  WeaponSettings: TWeaponSettings;
  WeaponType: TWeaponType;
  function WeaponNumber: byte;
  function LastWeapon: byte;
  constructor Create(UnitTypeWeaponGroup: TUnitTypeWeaponGroup);
  destructor Destroy; override;
end;

  //Commands that Unit recieved FROM a PLAYER and its parameters
TUnitCommand = class
private
  //nodes are used to determine in What stage the attack is
  { TODO 3 -oDa Stranger -cCritical :
   make sure these nodes are correct
  (usually nodes are inserted after calling StartMoving) }
  //should reference nodes, not their indexes!!!!!!!!!!!
  OutPathNode,
  AttackPathNode,
  EvadePathNode: TStrangeMovementNode;

  LastTargetPosition: TVector4f;
public
  Operation:byte; //Command code (constants like OP_STAND, OP_MOVE)

  MoveTo: TVector3f;
  PatrolPoints: TStrangeVector3fList;

  Targets: TUnitList; //Player ordered to attack them
  constructor Create;
  destructor Destroy; override;
end;

TPlayer = class(TPlayerAncestor)
private
  Game: TGame;
  procedure AttachEventHandlers;
  procedure LoadClassStructure;
public
  Researches: array of TResearch;
  Upgrades: array of TUpgradeData;

  LocalSettings: TLocalPlayerSettings;
  RemoteSettings: TRemotePlayerSettings;
  AISettings: TAIPlayerSettings;

  VisibleUnitList: TUnitList;

  function EnemyWith(WhatUnit: TUnit):Boolean;
  function AllyWith(WhatUnit: TUnit):Boolean;

  function UnitCount :integer;
  function LastUnit :integer;
  function GetUnit(id: integer): TUnit;
  function GetUnitByIndex(index: integer): TUnit;

  function AddUnit(WhatUnitType: TPlayerUnitType): TUnit; overload;
  function AddUnit(WhatUnitTypeName: string): TUnit; overload;

  procedure RemoveUnit(ID: integer); overload;
  procedure RemoveUnit(WhatUnit: TUnit); overload;
  procedure RemoveUnitByIndex(Index: integer);

  procedure UpdateVisibleUnitList;

  procedure Proceed(Delta, NewTime: single);
  constructor Create;
  destructor  Destroy; override;
end;

TUnit = class
private
  Torsotags: TMD3TagList;//for MD3 animation support
  DeadActor: TglActor;   //used when unit explodes
  Explosions_delta: single; //used to reduce rendering cost used

  UnitHUD: TStrangeUnitHUD;
  ParentInvariantObject: TStrangeParentInvariantObject;

  FPeople, FHealth, FShields: single;

  ProtectedByList: TSimpleUnitList;

 // Defencive tactic means not to attack enemy FIRST
 // If unit is atacked by the enemy, it will attack it back
 // In any case, all weapons will fire, when they can hit an enemy
 //may be should be part of TUnit.Command
  FAgressiveMode: Boolean;

  NeedXPForNextLevel: single;
  CurrentXP: single;//from last level

  SecondsBeforeHidingDirectGL: single;
  DirectGL: TGLDirectOpenGL;
  procedure onUnitRender(Sender : TObject; var rci : TRenderContextInfo);

  procedure SetShields(Value: single);
  procedure SetHealth(Value: single);
  procedure SetAgressiveMode(Value: Boolean);

  procedure TryToAttack(delta: single);
  procedure UpdateAttackCourse(NewTarget: Boolean);

  function GetFollowedUnitPosition: TVector3f;

  procedure SetGuiVisible(Value: Boolean);
  function  GetGuiVisible: Boolean;

  procedure OnFinishedExploding(Sender : TmyExplosion; TagObject: TObject; Tag: integer);
  procedure OnUnderAttack(Weapon: TUnitWeaponData);
  procedure Think(delta: single; TickCount: integer);
  procedure Proceed(delta: single; NewTime: single; TickCount: integer);
  procedure Regenerate(delta: single);

  function GetEnemyEvadeDistance(CurrentEnemyRadius: single): single;
  function SafeDistanceBeforeAttack: single;

  procedure ApplyShieldSettings(ShieldSettings: TShieldSettings);
  procedure ApplyEveryThing;

  procedure SwitchToAnimation(Animation: byte); overload;
  procedure SwitchToAnimation(Animation: string); overload;
  procedure SwitchToAnimation(Animation: TActorAnimation); overload;

  {$Hints Off}
  procedure ApplyXPLevel;
  procedure ApplyNextXP;
  procedure LevelUp;
  procedure SetXPForNextLevel;
  {$Hints On}
  //Movement Manager stuff
  procedure OnStartMoving(const Sender: TStrangeMovement);
  procedure OnStopMoving(const Sender: TStrangeMovement);
  procedure OnUpdatePathToFollowedObject(const Sender: TStrangeMovement);
  //Collision Manager stuff
  procedure OnChangeCourse(const Sender: TStrangeCMMapObject);


public
  ID: integer; //unique ID of the Unit
  Index: integer;//position in the Game.Units[...] array
  RealName: string;//there will be an option to give a Random Name to a Unit
  Player: TPlayer;
  UnitType: TPlayerUnitType;
  EngineType: TEngineType;
  ShieldType: TShieldType;
  EquipmentTypes: array of TEquipmentType;
  WeaponGroups: array of TUnitWeaponGroup;
  Weapons: array of TUnitWeaponData;//for holding current values of weapons

  Group: TUnitGroup; //don't know how to use it yet...
  BoundingBox: TglCube;

  MaxPeople: single;
  MaxHealth: single;
  MaxShields: single;
  Defence : single;

  HealthRegenerateRate: single;
  ShieldsRegenerateRate: single;

  ViewDistance: single;
  MovementScannerDistance: single;
  AttackDistance: single;

  Explosion: TmyExplosion;
  Engine: TStrangeEngines;

  Command: TUnitCommand; //What the unit is doing and thinking about, orders from Player

  //Loyalty:byte;      //from 0 to 100 in percent - used when capturing unit
  XPLevel: byte;//0 to ... (5)
  Cost: TCost;

  ActorProxy: TGLMaterialMultiProxy;//will be used almost everywhere
  EffectsCube: TGLBaseSceneObject; //for holding effects like blur or trail and Unit Explosion effect
  MovementCube: TGLBaseSceneObject;//for holding ActorProxy...
  Movement: TStrangeMovement;
  ColObject: TStrangeCMMapObject;

  property AgressiveMode: Boolean read FAgressiveMode write SetAgressiveMode;

  property People: single read FPeople write FPeople;
  property Health: single read FHealth write SetHealth;
  property Shields: single read FShields write SetShields;

  property GUIVisible: Boolean read GetGuiVisible write SetGuiVisible;
  function DistanceTo(TargetUnit: TUnit):single;
  function HasTarget(var WeaponID: shortint): boolean; overload;
  function HasTarget: boolean; overload;

  procedure Stop;
  procedure Attack(TargetUnit: TUnit); overload;
  procedure Attack(TargetUnits: array of TUnit); overload;
  procedure Move(Dest: TVector3f);
  procedure MoveByWaypoints(Dest: array of TVector3f);
  procedure Patrol(Dest: array of TVector3f);
  procedure BlowUp;
  procedure StopFiring;
  procedure FollowAndDefend(TargetUnit: TUnit);

  function WeaponGroupNumber: byte;
  function LastWeaponGroup: byte;
  function WeaponNumber :byte;
  function LastWeapon: byte;
  function PrimaryWeapon: TUnitWeaponData;

  constructor Create(WhatPlayer: TPlayer; WhatUnitType: TPlayerUnitType); overload;
  constructor Create(WhatPlayer: TPlayer; WhatUnitType: TPlayerUnitType;
     WhatEngineType: TEngineType; WhatShieldType: TShieldType;
     WhatEquipmentTypes: array of TEquipmentType;
     WhatWeaponTypes: array of TWeaponType); overload;
  destructor Destroy; override;
end;

TFLyingMissile = class
public
  Source: TGameUnit;
  Target: TGameUnit;
  TargetPoint: TVector3f;
  WeaponType: TWeaponType;
  curDist, curTime: single;
  Missile: TGLMaterialMultiProxy;  //for holding objects...
  FireSmallInterval, big_waiting, fireDuration_waiting: single;
  constructor Create;
  destructor Destroy; override;
end;

TUnitGroup = class
public
  Units: array of TUnit;
  Index: byte; //for hotkeys...
//procedure AddUnit(WhatUnit: TUnit);
//procedure RemoveUnit(WhatUnit: TUnit);
  function SelectByType(WhatUnitType: TUnitType): TUnitGroup;//A new UnitGroup is Created with only
                                                   //Units with this type
end;

TGame = class(TGameAncestor)
private
  Tick_count: integer;//For TGame.Proceed procedure

  TheOnePlayer: TPlayer;
  KeyboardPlayer: TPlayer;
  MousePlayer: TPlayer;
  JoystickPlayer: TPlayer;
  RightPlayer: TPlayer;
  LeftPlayer: TPlayer;

  procedure onSpaceRandom(Sender: TStrangeSpace; var RandomNumber: single);
  procedure onSpaceError (Sender: TStrangeSpace; Msg: string);

public
  Units:         array of TUnit;
  Players:       array of TPlayer;
  Planets:       array of TPlanet;
  Missions:      array of TMission; //all avaible Missions
  Campaigns:     array of TCampaign;

  CurrentID: integer; //each new Created unit will have an ID, which only increases.

  CurrentMission: TMission;
  CurrentCampaign: TCampaign;

  SceneObjects: TGameSceneObjects;//all Player-independant Scene objects

  MovementManager: TStrangeMovementManager;
  CollisionManager: TStrangeCollisionManager;
  ModelManager: TStrangeModelManager;


//******************************** Methods *******************************//


  //Mission-campaign operations
  function  LoadMission(MissionName:string):TMission;
  function  GetMission(MissionName:string):TMission;

  function  LoadCampaign(CampaignName:string):TCampaign;
  function  GetCampaign(CampaignName:string):TCampaign;

  //Unit
  function AddUnit(Player: TPlayer; WhatUnitType: TPlayerUnitType): TUnit; overload;
  function AddUnit(Player: TPlayer; WhatUnitTypeName: string): TUnit; overload;
  function AddUnit(Player: TPlayer; WhatUnitType: TPlayerUnitType;
                   WhatEngineType: TEngineType; WhatShieldType: TShieldType;
                   WhatEquipmentTypes: array of TEquipmentType;
                   WhatWeaponTypes: array of TWeaponType):TUnit; overload;

  procedure RemoveUnit(ID: integer); overload;
  procedure RemoveUnit(WhatUnit: TUnit); overload;
  procedure RemoveUnitByIndex(Index: integer);

  function GetUnit(ID: integer): TUnit; //can be used to determine if a unit is nil
  function UnitIDtoIndex(ID: integer): integer;
  function LastUnit: integer;

  //Player
  function AddPlayer(Name: string; WhatPlayerType: byte; WhatRace: TRace): TPlayer;
  procedure SwitchPlayers(index1, index2: integer);

  function GetPlayer(ID: byte): TPlayer; overload;
  function GetPlayer(Nick: string): TPlayer; overload;
  function GetPlayerFromIP(IP: string): TPlayer;
  function LastPlayer: integer;

  //main functions
  procedure Proceed(DeltaTime, NewTime: single);//Should be called by cadencer
  procedure FillDiplomacyMatrix; //temp procedure - makes everyone enemies
  function Init: Boolean;//called after all Players are added
                                          //and all options set
                                          //Result is False if there are incorrent options
  //constructors-sestructors
  constructor Create(Owner, KeyEventOwner: TWinControl);
  destructor Destroy; override;
end;

implementation


{ TGame }

function TGame.LastUnit: integer;
begin
Result := Length(Units) - 1;
end;

function TGame.LastPlayer: integer;
begin
Result := Length(Players) - 1;
end;

function TGame.GetCampaign(CampaignName: string): TCampaign;
var
i: byte;
begin
result := nil;
for i := 0 to length(Campaigns) - 1 do
if UpperCase(CampaignName) = UpperCase(Campaigns[i].Name) then
  begin
  Result := Campaigns[i];
  exit;
  end;
end;

function TGame.LoadCampaign(CampaignName: string): TCampaign;
begin
Result := GetCampaign(CampaignName);
if Result <> nil then result.LoadCampaign;
end;

function TGame.Init: Boolean;

      function CheckForPlayerCorrectness:Boolean;
      var
      i:byte;
      One_Player_number,left_Player_number,right_Player_number:byte;
      begin
      Result := False;
      One_Player_number := 0;
      left_Player_number := 0;
      right_Player_number := 0;
      for i := 0 to length(Players) - 1 do
        begin
        if Players[i].PlayerType = PT_ALL_SCREEN then inc(One_Player_number);
        if Players[i].PlayerType = PT_LEFT_SCREEN then inc(left_Player_number);
        if Players[i].PlayerType = PT_RIGHT_SCREEN then inc(right_Player_number);
        end;
      if (One_Player_number + left_Player_number + right_Player_number<1) or (One_Player_number + left_Player_number + left_Player_number>2) then exit;
      if (One_Player_number = 1) and (left_Player_number + right_Player_number <> 0)  then exit;
      if (left_Player_number + right_Player_number=2) and (One_Player_number <> 0) then exit;

      Result := True;
      end;

      procedure AttachViewer(LocalSettings: TLocalPlayerSettings);
      begin
      with LocalSettings do
        begin
        Viewer := TglSceneViewer.Create(SceneObjects.Owner);
        Viewer.Parent := SceneObjects.Owner;
        Viewer.Camera := Camera;
        Viewer.Buffer.BackgroundColor := clblack;
        //Viewer.FieldOfView := 146.6;
        Console := TGLConsole.CreateAsChild(SceneObjects.LastTransparentObject);
        Console.SceneViewer := Viewer;
        Console.Font := SceneObjects.SystemFont;
        Viewer.Visible := True;
        end;
      end;

      procedure AttachFullViewer(LocalSettings: TLocalPlayerSettings);
      begin
      with LocalSettings do
        begin
        FullViewer := TGLFullScreenViewer.Create(SceneObjects.Owner);
        FullViewer.Camera := Camera;
        FullViewer.Buffer.BackgroundColor := clblack;
        FullViewer.OnKeyUp := SceneObjects.OnKeyUp;
        FullViewer.OnKeyPress := SceneObjects.OnKeyPress;
        FullViewer.OnKeyDown := SceneObjects.OnKeyDown;
        FullViewer.StayOnTop := False;
        FullViewer.RefreshRate := 85;
        if CurrentProfile.VideoSettings.UseCurrentResolution then
          FullViewer.UseCurrentResolution
        else
          begin
          FullViewer.Width := CurrentProfile.VideoSettings.ScreenWidth;
          FullViewer.Height := CurrentProfile.VideoSettings.ScreenHeight;
          end;
        FullViewer.Active  := True;
        Console := TGLConsole.CreateAsChild(SceneObjects.LastTransparentObject);
        Console.SceneViewer := TGLSceneViewer(FullViewer);
        Console.Font := SceneObjects.SystemFont;
        end;
      end;

      procedure AttachConsole(LocalSettings:TLocalPlayerSettings);
      begin
      with LocalSettings do
        begin
        Console.OnCommandIssued := OnConsoleCommandIssued;
        Console.HudSprite.Material.MaterialLibrary := SceneObjects.MiscMaterialLibrary;
        Console.HudSprite.Material.LibMaterialName := str_CONSOLE_PATH;
        Console.AddLine('Welcome to Sector-37 Console!');
        Console.FontColor := clBlue;
        Console.AdditionalCommands.Add('echo');
        Console.AdditionalCommands.Add('exit');
        Console.AdditionalCommands.Add('eval');
        Console.AdditionalCommands.Add('hello');
        Console.RefreshHudSize;
        //Console.LoadFromMemory(); - load controlzzz and settingzzz
        end;
      end;

      procedure AttachPlayers;
      var
      i: byte;
      begin
      //Assign Players to variables...
      for i := 0 to length(Players) - 1 do
        begin
        case Players[i].PlayerType of
          PT_ALL_SCREEN:  TheOnePlayer := Players[i];
          PT_LEFT_SCREEN:   LeftPlayer := Players[i];
          PT_RIGHT_SCREEN: RightPlayer := Players[i];
          end;
        if Players[i].LocalSettings <> nil then
          begin
          case Players[i].LocalSettings.ControlType of
            CT_MOUSE:
              begin
              MousePlayer := Players[i];
              MousePlayer.LocalSettings.Controls := CurrentProfile.MousePlayerControls;
              end;

            CT_KEYBOARD:
              begin
              KeyBoardPlayer := Players[i];
              KeyBoardPlayer.LocalSettings.Controls := CurrentProfile.KeyboardPlayerControls;
              end;

            CT_JOYSTICK:
              begin
              JoystickPlayer := Players[i];
              JoystickPlayer.LocalSettings.Controls := CurrentProfile.JoystickPlayerControls;
              end;
            end;
//          Players[i].LocalSettings.UI.MouseSpeed := Players[i].LocalSettings.Controls.UIMouseSpeed;
          Players[i].LocalSettings.UI.InvertMouse := Players[i].LocalSettings.Controls.UIInvertMouse;
          Players[i].LocalSettings.Navigator.MovementInertia := Players[i].LocalSettings.Controls.MovementInertia;
          Players[i].LocalSettings.Navigator.TurnInertia := Players[i].LocalSettings.Controls.TurnInertia;
          Players[i].LocalSettings.Navigator.MaxTurnAngle := Players[i].LocalSettings.Controls.MaxTurnAngle;
          end;
        end;
      end;

var
i: byte;

begin
Result := False;
if not CheckForPlayerCorrectness then
  begin
  MakeError('Player number or PlayerType setting is wrong', ERR_INVALID_PLAYER_SETTINGS);
  exit;
  end;

AttachPlayers;
try
//load all unit types of Players' race
for i := 0 to length(Players) - 1 do
  Players[i].LoadClassStructure;

if not CurrentProfile.GeneralSettings.SplitScreenMode then
with TheOnePlayer.LocalSettings do
  begin
  //single screen mode
  if not CurrentProfile.GeneralSettings.FullScreenMode then
    begin
    AttachViewer(TheOnePlayer.LocalSettings);
    Viewer.Align := alClient;
    end
  else
    begin
    //FullScreen mode
    AttachFullViewer(TheOnePlayer.LocalSettings);
    end;
  AttachConsole(TheOnePlayer.LocalSettings);
  Player.AttachEventHandlers;
  end
else
  begin
  //SplitScreen Mode
  if LeftPlayer.LocalSettings.ControlType=RightPlayer.LocalSettings.ControlType then
    begin
    MakeError('Two Players can''t have the same controls',ERR_INVALID_PLAYER_SETTINGS);
    exit;
    end;

  //define viewer position
  with LeftPlayer.LocalSettings do
    begin
    AttachViewer(LeftPlayer.LocalSettings);
    Viewer.Width := (sceneobjects.Owner.Width div 2) - 1;
    Viewer.Align  := alleft;
    Viewer.BeforeRender := onBeforeRender;
    Viewer.PostRender := onPostRender;
    AttachConsole(LeftPlayer.LocalSettings);
    end;

  with RightPlayer.LocalSettings do
    begin
    AttachViewer(RightPlayer.LocalSettings);
    Viewer.Width := LeftPlayer.LocalSettings.Viewer.Width;
    Viewer.Left := RightPlayer.LocalSettings.Viewer.Width + 1;
    Viewer.Align := alright;
    AttachConsole(RightPlayer.LocalSettings);
    end;
  LeftPlayer.AttachEventHandlers;
  RightPlayer.AttachEventHandlers;
  end;
SceneObjects.Cadencer.Enabled := True;
SceneObjects.FPSTimer.Enabled := True;
//la-la-la...
  except
  MakeError('Unknown error!',ERR_UNKNOWN);
  end;
//actually information should be loaded into it before init...
FillDiplomacyMatrix;
Result := True;
end;

procedure TGame.FillDiplomacyMatrix;
var
  i, j, l: byte;
begin
  { TODO 3 -oDa Stranger -cStandard :
  for testing reasons this is filled with the same values
  in real life it should be loaded from some kind of database... }

  l := length(Players) - 1;
  for i := 0 to l do
  for j := 0 to l do
  if i <> j then
    begin
    DiplomacyMatrix[i, j] :=  DiplomacyMatrix[i, j] + [ddEnemy];
    DiplomacyMatrix[j, i] :=  DiplomacyMatrix[j, i] + [ddEnemy];
    end;
end;

procedure TGame.Proceed(DeltaTime, NewTime: single);
var
i: integer;
begin
inc(Tick_count);

MovementManager.Proceed(DeltaTime, NewTime);
CollisionManager.Proceed(DeltaTime);
CurrentMission.Space.Proceed(DeltaTime, NewTime);

for i := 0 to length(Players) - 1 do
  Players[i].Proceed(DeltaTime, NewTime);

if length(Units) <> 0 then
for i := 0 to length(Units) - 1 do
if Units[i] <> nil then
  Units[i].Proceed(DeltaTime, NewTime, Tick_Count);
end;

function TGame.LoadMission(MissionName: string): TMission;
begin
Result := GetMission(MissionName);
CurrentMission := Result;
if Result  <> nil then
  result.LoadMission;
end;

function TGame.GetMission(MissionName: string): TMission;
var
i: byte;
begin
result := nil;
for i := 0 to length(Missions) - 1 do
if UpperCase(MissionName) = UpperCase(Missions[i].Filename) then
  begin
  Result := Missions[i];
  exit;
  end;
end;

function TGame.AddUnit(Player: TPlayer; WhatUnitTypeName: string): TUnit;
begin
Result := AddUnit(Player, Player.GetUnitType(WhatUnitTypeName));
end;

function TGame.AddUnit(Player: TPlayer; WhatUnitType: TPlayerUnitType): TUnit;
var
i: integer;
tmp: array of TWeaponType;
begin
setlength(tmp, length(WhatUnitType.WeaponGroups));
if length(WhatUnitType.WeaponGroups) <> 0 then
for i := 0 to length(WhatUnitType.WeaponGroups) - 1 do
  tmp[i] := WhatUnitType.WeaponGroups[i].CurrentWeapon;
Result := AddUnit(Player, WhatUnitType, WhatUnitType.CurrentEngine,
                  WhatUnitType.CurrentShield, WhatUnitType.CurrentEquipment, tmp);
end;


function TGame.AddUnit(Player: TPlayer; WhatUnitType: TPlayerUnitType;
  WhatEngineType: TEngineType; WhatShieldType: TShieldType;
  WhatEquipmentTypes: array of TEquipmentType;
  WhatWeaponTypes: array of TWeaponType): TUnit;
var
i, index: integer;
begin

if (Player = nil) or (WhatUnitType =  nil) then
  begin
  Result := nil;
  exit;
  end;

index := -1;
if length(Units) <> 0 then
  begin
  for i := 0 to length(Units) - 1 do
  if Units[i] = nil then
    begin
    index := i;
    break;
    end;
  end;

//if none were found then add one...
if index = - 1 then
  begin
  setlength(Units,length(Units) + 1);
  index := length(Units) - 1;
  end;

Result := TUnit.Create(Player,WhatUnitType,WhatEngineType,WhatShieldType,WhatEquipmentTypes,WhatWeaponTypes);
Result.Index := index;
Result.ID := CurrentID;
Player.Units.Add(Result);
Units[index] := Result;
inc(CurrentID);
end;

procedure TGame.RemoveUnit(ID: integer);
var
index: integer;
begin
index := UnitIDtoIndex(ID);
if index = -1 then exit;
RemoveUnitByIndex(index);
end;

procedure TGame.RemoveUnit(WhatUnit:TUnit);
begin
if WhatUnit = nil then exit;
RemoveUnitByIndex(UnitIDtoIndex(WhatUnit.ID));
end;

procedure TGame.RemoveUnitByIndex(Index: integer);
var
i: integer;
begin
if Units[index] = nil then exit;

for i := 0 to length(Players) - 1 do
if Players[i].LocalSettings <> nil then
  begin
  Players[i].LocalSettings.PickList.RemoveUnit(Units[index]);
  if (Players[i].LocalSettings.Camera.TargetObject = Units[index].ActorProxy) and
      (Units[index].ActorProxy <> nil) then
    Players[i].LocalSettings.DetachCamera;
  end;

Units[index].Player.Units.Remove(Units[index]);
FreeAndNil(Units[index]);
end;

function TGame.AddPlayer(Name: string; WhatPlayerType: byte; WhatRace: TRace): TPlayer;
begin
setlength(Players,length(Players) + 1);
Players[length(Players) - 1] := TPlayer.Create;
Result := Players[length(Players) - 1];
with Result do
  begin
  id := length(Players) - 1;
  nick := Name;
  Race := WhatRace;
  Game := Self;
  PlayerType := WhatPlayerType;
  if PlayerType<3 then
    begin
    LocalSettings := TLocalPlayerSettings.Create(Game);
    LocalSettings.Player := Result;
    end;
  if (PlayerType >= 3) and (PlayerType <= 5) then
    begin
    RemoteSettings := TRemotePlayerSettings.Create;
    RemoteSettings.Player := Result;
    end;
  if PlayerType>5 then
    begin
    AISettings := TAIPlayerSettings.Create;
    AISettings.Player := Result;
    end;
  end;
end;

destructor TGame.Destroy;
var
i: byte;
begin
SceneObjects.Cadencer.Enabled := False;

if length(Units) <> 0 then
for i := 0 to length(Units) - 1 do
RemoveUnitByIndex(i);
setlength(Units,0);

if length(Profiles) <> 0 then
for i := 0 to length(Profiles) - 1 do Profiles[i].Destroy;

if length(Planets) <> 0 then
for i := 0 to length(Planets) - 1 do Planets[i].Destroy;

if length(Players) <> 0 then
for i := 0 to length(Players) - 1 do Players[i].Destroy;

if length(Missions) <> 0 then
for i := 0 to length(Missions) - 1 do Missions[i].Destroy;

if length(Campaigns) <> 0 then
for i := 0 to length(Campaigns) - 1 do Campaigns[i].Destroy;

CollisionManager.Destroy;
MovementManager.Destroy;
ModelManager.Destroy;

SceneObjects.Destroy;

  inherited Destroy;
end;

constructor TGame.Create(Owner, KeyEventOwner: TWinControl);

  procedure AdjustTexture(Material : TGLMaterial);
  begin
  Material.BlendingMode := bmTransparency;
  Material.Texture.TextureMode := tmReplace;
  Material.Texture.ImageAlpha := tiaTopLeftPointColorTransparent;
  end;

  
var
GlobIni: TStrangeIniFile;
List: TStringList;
UnitFileName: string;
FullFileName: string;

i, l: byte;
iniFileName: string;
begin
  inherited Create;

randomize;
SceneObjects := TGameSceneObjects.Create(Owner, KeyEventOwner);
SceneObjects.Game := Self;

iniFileName := ExtractFilePath(ParamStr(0)) + str_CONFIG_INI;
if not fileexists(iniFileName) then
  MakeError(str_ERROR_READING_FILE + iniFileName + str_IT_DOES_NOT_EXIST,ERR_NOT_FOUND);
Globini := TStrangeIniFile.Create(iniFileName);

List := TStringList.Create;

//******************         Loading Profiles          *************************//

if not Globini.SectionExists(str_PROFILES) then
  MakeError(str_ERROR_READING_SECTION + str_PROFILES + iniFileName +  str_IT_DOES_NOT_EXIST,ERR_BAD_INI);

Globini.ReadSection(str_PROFILES,List);
setlength(Profiles, List.Count);
for i := 0 to List.Count - 1 do
  begin
  FullFileName := str_PROFILES + '\' + List[i] + '\' + str_CONFIG_INI;
  if not fileexists(FullFileName) then
    MakeError(str_ERROR_READING_FILE + FullFileName + str_IT_DOES_NOT_EXIST,ERR_NOT_FOUND);
  Profiles[i] := TProfile.Create;
  Profiles[i].LoadFromFile(FullFileName);
  end;

CurrentProfile := GetProfile(Globini.ReadString(str_GENERAL, str_CURRENT_PROFILE,''));
if CurrentProfile=nil then
  MakeError(str_ERROR_READING + str_CURRENT_PROFILE + str_IN_SECTION + str_GENERAL + str_FROM_FILE + iniFileName + str_INVALID_VALUE, ERR_BAD_INI);


//******************        Loading Missions         *************************//

if not Globini.SectionExists(str_MISSIONS) then
  MakeError(str_ERROR_READING_SECTION + str_MISSIONS + str_FROM_FILE + iniFileName + str_IT_DOES_NOT_EXIST,ERR_BAD_INI);

Globini.ReadSection(str_MISSIONS,List);
setlength(Missions, List.Count);
for i := 0 to List.Count - 1 do
  begin
  Missions[i] := TMission.Create;
  Missions[i].LoadFromFile(str_MISSIONS +'\'+List[i] + '\' + str_DESCRIPTION_DSC,str_GENERAL);
  Missions[i].Game := Self;
  end;

//******************       Loading Race Types       *************************//

if not Globini.SectionExists(str_RACE_TYPES) then
  MakeError(str_ERROR_READING_SECTION + str_RACE_TYPES + str_FROM_FILE + iniFileName + str_IT_DOES_NOT_EXIST,ERR_BAD_INI);

Globini.ReadSection(str_RACE_TYPES,List);
setlength(RaceTypes, List.Count);
for i := 0 to List.Count - 1 do
  begin
  RaceTypes[i] := TRaceType.Create;
  RaceTypes[i].LoadFromFile(str_OBJECTS + '\' + str_RACE_TYPES + '\' + List[i] + '\' + str_DESCRIPTION_DSC,str_GENERAL);
  RaceTypes[i].ID := i;
  end;

//******************         Loading Races          *************************//

if not Globini.SectionExists(str_RACES) then
  MakeError(str_ERROR_READING_SECTION + str_RACES + str_FROM_FILE + iniFileName + str_IT_DOES_NOT_EXIST,ERR_BAD_INI);

Globini.ReadSection(str_RACES,List);
setlength(Races, List.Count);
for i := 0 to List.Count - 1 do
  begin
  Races[i] := TRace.Create;
  Races[i].onRequestRaceType := onRequestRaceType;
  Races[i].LoadFromFile(str_OBJECTS + '\' + str_RACES + '\' + List[i] + '\' + str_DESCRIPTION_DSC,str_GENERAL);
  end;

//******************      Loading Unit Classes      *************************//

if not Globini.SectionExists(str_UNIT_CLASSES) then
  MakeError(str_ERROR_READING_SECTION + str_UNIT_CLASSES + str_FROM_FILE + iniFileName + str_IT_DOES_NOT_EXIST,ERR_BAD_INI);

Globini.ReadSection(str_UNIT_CLASSES,List);
setlength(UnitClasses, List.Count);
for i := 0 to List.Count - 1 do
  begin
  UnitClasses[i] := TUnitClass.Create;
  UnitClasses[i].LoadFromFile(str_OBJECTS + '\' + str_UNIT_CLASSES + '\' + List[i] + '\' + str_DESCRIPTION_DSC,str_GENERAL);
  UnitClasses[i].ID := i;
  end;


//******************         Loading Weapons          *************************//

if not Globini.SectionExists(str_WEAPONS) then
  MakeError(str_ERROR_READING_SECTION + str_WEAPONS + str_FROM_FILE + iniFileName + str_IT_DOES_NOT_EXIST,ERR_BAD_INI);

Globini.ReadSection(str_WEAPONS,List);
setlength(WeaponTypes, List.Count);
for i := 0 to List.Count - 1 do
  begin
  WeaponTypes[i] := TWeaponType.Create;
  WeaponTypes[i].onRequestRaceType := onRequestRaceType;
  WeaponTypes[i].onRequestUnitClass := onRequestUnitClass;
  WeaponTypes[i].LoadFromFile(str_OBJECTS + '\' + str_WEAPONS + '\' + List[i] + '\' + str_DESCRIPTION_DSC,str_GENERAL);
  end;

//******************         Loading Engines          *************************//

if not Globini.SectionExists(str_ENGINES) then
  MakeError(str_ERROR_READING_SECTION + str_ENGINES + str_FROM_FILE + iniFileName + str_IT_DOES_NOT_EXIST,ERR_BAD_INI);

Globini.ReadSection(str_ENGINES,List);
setlength(EngineTypes, List.Count);
for i := 0 to List.Count - 1 do
  begin
  EngineTypes[i] := TEngineType.Create;
  EngineTypes[i].LoadFromFile(str_OBJECTS + '\' + str_ENGINES + '\' + List[i] + '\' + str_DESCRIPTION_DSC,str_GENERAL);
  end;

//******************         Loading Shields          *************************//

if not Globini.SectionExists(str_SHIELDS) then
  MakeError(str_ERROR_READING_SECTION + str_SHIELDS + str_FROM_FILE + iniFileName + str_IT_DOES_NOT_EXIST,ERR_BAD_INI);

Globini.ReadSection(str_SHIELDS,List);
setlength(ShieldTypes, List.Count);
for i := 0 to List.Count - 1 do               
  begin
  ShieldTypes[i] := TShieldType.Create;
  ShieldTypes[i].LoadFromFile(str_OBJECTS + '\' + str_SHIELDS + '\' + List[i] + '\' + str_DESCRIPTION_DSC,str_GENERAL);
  end;

//******************         Loading Equipment        *************************//

if not Globini.SectionExists(str_EQUIPMENT) then
  MakeError(str_ERROR_READING_SECTION + str_EQUIPMENT + str_FROM_FILE + iniFileName + str_IT_DOES_NOT_EXIST,ERR_BAD_INI);

Globini.ReadSection(str_EQUIPMENT,List);
setlength(EquipmentTypes, List.Count);
for i := 0 to List.Count - 1 do
  begin
  EquipmentTypes[i] := TEquipmentType.Create;
  EquipmentTypes[i].LoadFromFile(str_OBJECTS + '\' + str_EQUIPMENT + '\' + List[i] + '\' + str_DESCRIPTION_DSC,str_GENERAL);
  end;


//******************        Loading Unit Types        *************************//

for l := 0 to length(Races) - 1 do
  begin
  setlength(UnitTypes,length(UnitTypes)+ Races[l].UnitTypes.Count);
  if Races[l].UnitTypes.Count <> 0 then
  for i := length(UnitTypes) - Races[l].UnitTypes.Count to length(UnitTypes) - 1 do
    begin
    UnitFileName  := Races[l].UnitTypes[i - length(UnitTypes) + Races[l].UnitTypes.Count];
    FullFileName := str_OBJECTS + '\' + str_Units + '\' + Races[l].Filename + '\' + UnitFileName + '\' + str_DESCRIPTION_DSC;
    if not FileExists(FullFileName) then
      MakeError(str_ERROR_READING_FILE + FullFileName + str_IT_DOES_NOT_EXIST,ERR_NOT_FOUND);

    UnitTypes[i] := TUnitType.Create;
    UnitTypes[i].OnRequestWeaponType := OnRequestWeaponType;
    UnitTypes[i].OnRequestShieldType := OnRequestShieldType;
    UnitTypes[i].OnRequestEquipmentType := OnRequestEquipmentType;
    UnitTypes[i].OnRequestEngineType := OnRequestEngineType;
    UnitTypes[i].onRequestUnitClass := onRequestUnitClass;

    Races[l].UnitTypes.Objects[i - length(UnitTypes) + Races[l].UnitTypes.Count] := UnitTypes[i];
    UnitTypes[i].Race := Races[l];
    UnitTypes[i].LoadFromFile(FullFileName,str_GENERAL);
    end;
  end;

GlobIni.Destroy;
List.Destroy;

//******************        Loading Cursors        *************************//
try
  Screen.Cursors[CUR_MENU] := LoadCursorFromFile(str_MENU_CURSOR + str_CUR);
  Screen.Cursors[CUR_NORMAL] := LoadCursorFromFile(str_NORMAL_CURSOR + str_CUR);
  Screen.Cursors[CUR_ATTACK] := LoadCursorFromFile(str_ATTACK_CURSOR + str_CUR);
  Screen.Cursors[CUR_FOLLOW_AND_DEFEND] := LoadCursorFromFile(str_FOLLOW_AND_DEFEND_CURSOR + str_CUR);

  Screen.Cursors[0] := Screen.Cursors[CUR_MENU];
  Screen.Cursor := Screen.Cursors[0];

  AdjustTexture(SceneObjects.MiscMaterialLibrary.AddTextureMaterial(str_MENU_CURSOR,str_MENU_CURSOR + str_BMP).Material);
  AdjustTexture(SceneObjects.MiscMaterialLibrary.AddTextureMaterial(str_NORMAL_CURSOR,str_NORMAL_CURSOR + str_BMP).Material);
  AdjustTexture(SceneObjects.MiscMaterialLibrary.AddTextureMaterial(str_ATTACK_CURSOR,str_ATTACK_CURSOR + str_BMP).Material);
  AdjustTexture(SceneObjects.MiscMaterialLibrary.AddTextureMaterial(str_FOLLOW_AND_DEFEND_CURSOR,str_FOLLOW_AND_DEFEND_CURSOR + str_BMP).Material);
except
  MakeError('One of the Cursor files was NOT found!' ,ERR_NOT_FOUND);
  end;

//******************        Loading Console textures        *************************//
with SceneObjects.MiscMaterialLibrary.AddTextureMaterial(str_CONSOLE_PATH,str_CONSOLE_PATH).Material do
  begin
  BlendingMode := bmTransparency;
  Texture.TextureMode := tmDecal;
  Texture.ImageAlpha := tiaOpaque;
  FrontProperties.Diffuse.Alpha := 0.65;
  end;

//***************         Doing all the rest       ******************//

MovementManager := TStrangeMovementManager.Create(SceneObjects.Scene.Objects);
CollisionManager := TStrangeCollisionManager.Create(SceneObjects.Scene.Objects);
CollisionManager.PredictionDepth := 10;
CollisionManager.FullPositionUpdateFrequency := 1;
CollisionManager.UpdateInterval := 0.5;
CollisionManager.ProximityMultiplier := 1.1;

ModelManager := TStrangeModelManager.Create(SceneObjects.Scene, SceneObjects.UnitMaterialLibrary, SceneObjects.MaterialScripter);
end;


function TGame.GeTPlayer(ID: byte): TPlayer;
var
  i: byte;
begin
Result := nil;
if length(Players) = 0 then Exit;
for i := 0 to length(Players) - 1 do
if ID = Players[i].ID then
  begin
  Result := Players[i];
  exit;
  end;
end;


function TGame.GetPlayer(Nick: string): TPlayer;
var
  i: byte;
begin
  Result := nil;
  if length(Players) = 0 then Exit;
  for i := 0 to length(Players) - 1 do
  if Nick = Players[i].Nick then
    begin
    Result := Players[i];
    exit;
    end;
end;


function TGame.GeTPlayerFromIP(IP: string): TPlayer;
var
  i: byte;
begin
  Result := nil;
  if length(Players) = 0 then Exit;
  for i := 0 to length(Players) - 1 do
  if Players[i].IP = IP then
    begin
    Result := Players[i];
    exit;
    end;
end;

function TGame.UnitIDtoIndex(ID: integer): integer;
var
  i: integer;
begin
  Result := -1;
  if length(Units) = 0 then Exit;
  for i := 0 to length(Units) - 1 do
  if Units[i] <> nil then
  if ID = Units[i].ID then
    begin
    Result := i;
    exit;
    end;
end;

function TGame.GetUnit(ID: integer): TUnit;
var
i: integer;
begin
Result := nil;
if length(Units) = 0 then Exit;
for i := 0 to length(Units) - 1 do
if Units[i] <> nil then
if ID=Units[i].ID then
  begin
  Result := Units[i];
  exit;
  end;
end;

procedure TGame.onSpaceError(Sender: TStrangeSpace; Msg: string);
begin
MakeError('Space Module Error: ' + Msg, ERR_EXTERNAL_MODULE);
end;

procedure TGame.onSpaceRandom(Sender: TStrangeSpace; var RandomNumber: single);
begin
RandomNumber := myRandom;
end;

procedure TGame.SwitchPlayers(index1, index2: integer);
var
tmp: TPlayer;
tmp_id: integer;
begin
//change references
tmp := Players[index1];
Players[index1] := Players[index2];
Players[index2] := tmp;
//changs ID's
tmp_id := Players[index2].ID;
Players[index2].ID := Players[index1].ID;
Players[index1].ID := tmp_id;
end;

{ TUnitWeaponData }
procedure TUnitWeaponData.FindNextTarget;
var
i: integer;
targetList: array[0..MAX_TARGETS] of TUnit;
pos: byte;
l, l2: integer;
    procedure ScanSector;
    var
    i: integer;
    OtherUnit: TUnit;
    begin
    { TODO 5 -oDa Stranger -cOptimization : NEED to save this data for all Weapons on the ship to share }
    with owner.Player.Game.CurrentMission do
      begin
      l2 := GetNearbyObjectsListcount;
      if l2 <> 0 then
      for i := 0 to l2 - 1 do
        begin
        //if it has positive health
        OtherUnit := GetNearbyUnit(i);
        if OtherUnit <> nil then
        //if it can be seen
        if VectorDistance(owner.MovementCube.Position.AsAffineVector, OtherUnit.MovementCube.Position.AsAffineVector) <= owner.UnitType.ViewDistance then
        //if it is an enemy
        if (ddEnemy) in owner.Player.Game.DiplomacyMatrix[owner.Player.ID, OtherUnit.Player.ID] then
          begin
          TargetList[pos] := OtherUnit;
          //if it is a good idea to attack it
          if WeaponGroup.Weapontype.GetDamageMatrixValue(TargetList[pos].UnitType) > CRITICAL_MAX_MULT then
            begin
            Target.Assign(OtherUnit);
            exit;
            end;
          inc(pos);
          if pos > MAX_TARGETS then break;
          end;
        end;
      end;
    end;

begin
                //it may be a good idea to contimue atacking current target
if Target.isAlive then
if WeaponGroup.Weapontype.GetDamageMatrixValue(Target.GameUnit.UnitType) > CRITICAL_MAX_MULT then
  exit;
                //reset target to nil
Target.ForceNotExists := True;
{ TODO 5 -oDa Stranger -cCritical : Add an option to keep the existing target }

                //at first try to find the best target in your target list
l := owner.Command.Targets.UnitCount;
if l <> 0 then
for i := 0 to l - 1 do
if owner.Command.Targets.UnitAlive(i) then
if WeaponGroup.Weapontype.GetDamageMatrixValue(owner.Command.Targets[i].GameUnit.UnitType) > CRITICAL_MAX_MULT then
  begin
  Target.Assign(owner.Command.Targets[i]);
  exit;
  end;

                //then try to find the best target in the surroundings
pos := 0;
ScanSector;


                //then try to find an average target in your target list
if l <> 0 then
for i := 0 to l - 1 do
if owner.Command.Targets.UnitAlive(i) then
if WeaponGroup.Weapontype.GetDamageMatrixValue(owner.Command.Targets[i].GameUnit.UnitType) > CRITICAL_MIN_MULT then
  begin
  Target.Assign(owner.Command.Targets[i]);
  exit;
  end;

                //then try to find an average target in the surroundings
if pos <> 0 then
for i := 0 to pos - 1 do
if WeaponGroup.Weapontype.GetDamageMatrixValue(TargetList[i].UnitType) > CRITICAL_MIN_MULT then
  begin
  Target.Assign(TargetList[i]);
  exit;
  end;

                //then try to attack any target in your target list
if l <> 0 then
for i := 0 to l - 1 do
if owner.Command.Targets.UnitAlive(i) then
  begin
  Target.Assign(owner.Command.Targets[i]);
  exit;
  end;

                //then try to attack any target in the surroundings
if pos <> 0 then
  begin
  Target.Assign(TargetList[0]);
  exit;
  end;

                //ok, there are realy no targets to attack %)
end;

procedure TUnitWeaponData.StopFiringLaser;
begin
isFiring := False;
ThorFx.GlowSize := 0;
ThorFx.Disabled := True;
end;

procedure TUnitWeaponData.StartFiringWeapon(Where:TVector3f);
begin
if (CurrentBurst>WeaponGroup.WeaponType.WeaponSettings.BurstNumber) then
  CurrentBurst := 1
else if (TimeRecharging<-WeaponGroup.WeaponSettings.RechargeInterval_big) then
  CurrentBurst := 0;
inc(CurrentBurst);
if CurrentBurst=WeaponGroup.WeaponSettings.BurstNumber then
  TimeRecharging := WeaponGroup.WeaponSettings.RechargeInterval_big
else TimeRecharging := WeaponGroup.WeaponSettings.RechargeInterval_small;

if WeaponGroup.WeaponType.LaserMode then
  begin
  isFiring := True;
  TimeFiringLaser := WeaponGroup.WeaponType.TimeFiringLaser;
  ThorFx.target.asaffinevector := TipPosition.AbsoluteToLocal(where);
  ThorFx.GlowSize := WeaponGroup.WeaponType.ThorManager.GlowSize;
  ThorFx.Disabled := False;
  Target.GameUnit.OnUnderAttack(Self);
  end
else
  begin

  //Launch the bullet

  end;

end;


procedure TUnitWeaponData.GetDistancesToTarget(const FromPoint: TVector3f; var arr: array of single);
var
abs: Tvector;
begin
abs := Target.GameUnit.MovementCube.absolutePosition;
arr[0] := VectorDistance(FromPoint,AffineVectorMake(abs));
arr[1] := VectorDistance(FromPoint,AffineVectorMake(abs[0],abs[1] + Target.GameUnit.BoundingBox.cubeheight / 2 * Target.GameUnit.UnitType.scale,abs[2]));
arr[2] := VectorDistance(FromPoint,AffineVectorMake(abs[0],abs[1] - Target.GameUnit.BoundingBox.cubeheight / 2 * Target.GameUnit.UnitType.scale,abs[2]));
arr[3] := VectorDistance(FromPoint,AffineVectorMake(abs[0] + Target.GameUnit.BoundingBox.cubewidth / 2 * Target.GameUnit.UnitType.scale,abs[1],abs[2]));
arr[4] := VectorDistance(FromPoint,AffineVectorMake(abs[0] - Target.GameUnit.BoundingBox.cubewidth / 2 * Target.GameUnit.UnitType.scale,abs[1],abs[2]));
arr[5] := VectorDistance(FromPoint,AffineVectorMake(abs[0],abs[1],abs[2] + Target.GameUnit.BoundingBox.cubedepth / 2 * Target.GameUnit.UnitType.scale));
arr[6] := VectorDistance(FromPoint,AffineVectorMake(abs[0],abs[1],abs[2] - Target.GameUnit.BoundingBox.cubedepth / 2 * Target.GameUnit.UnitType.scale));
end;


function TUnitWeaponData.GetMaxDistanceToTarget(const FromPoint: TVector3f): single;
var
arr: array of single;
begin
setlength(arr, 7);
GetDistancesToTarget(FromPoint, arr);
Result := MaxFloat(arr);
end;

function TUnitWeaponData.GetMinDistanceToTarget(const FromPoint: TVector3f): single;
var
arr: array of single;
begin
setlength(arr,7);
GetDistancesToTarget(FromPoint,arr);
Result := MinFloat(arr);
end;

function TUnitWeaponData.GetMinimalAngleToTarget(const Direction: TVector3f): single;
var
arr: array[0..6] of single;
abs: Tvector;
begin
abs := Target.GameUnit.MovementCube.absolutePosition;

arr[0] := arccos(VectorAngleCosine(Direction,AffineVectorMake(vectorsubtract(abs,owner.MovementCube.absolutePosition))));

arr[1] := arccos(VectorAngleCosine(Direction,AffineVectorMake(vectorsubtract(VectorMake(abs[0],abs[1] + Target.GameUnit.BoundingBox.cubeheight/2*Target.GameUnit.UnitType.scale,abs[2]),owner.MovementCube.absolutePosition))));
arr[2] := arccos(VectorAngleCosine(Direction,AffineVectorMake(vectorsubtract(VectorMake(abs[0],abs[1] - Target.GameUnit.BoundingBox.cubeheight/2*Target.GameUnit.UnitType.scale,abs[2]),owner.MovementCube.absolutePosition))));

arr[3] := arccos(VectorAngleCosine(Direction,AffineVectorMake(vectorsubtract(VectorMake(abs[0] + Target.GameUnit.BoundingBox.cubewidth/2*Target.GameUnit.UnitType.scale,abs[1],abs[2]),owner.MovementCube.absolutePosition))));
arr[4] := arccos(VectorAngleCosine(Direction,AffineVectorMake(vectorsubtract(VectorMake(abs[0] - Target.GameUnit.BoundingBox.cubewidth/2*Target.GameUnit.UnitType.scale,abs[1],abs[2]),owner.MovementCube.absolutePosition))));

arr[5] := arccos(VectorAngleCosine(Direction,AffineVectorMake(vectorsubtract(VectorMake(abs[0],abs[1],abs[2] + Target.GameUnit.BoundingBox.cubedepth/2*Target.GameUnit.UnitType.scale),owner.MovementCube.absolutePosition))));
arr[6] := arccos(VectorAngleCosine(Direction,AffineVectorMake(vectorsubtract(VectorMake(abs[0],abs[1],abs[2] - Target.GameUnit.BoundingBox.cubedepth/2*Target.GameUnit.UnitType.scale),owner.MovementCube.absolutePosition))));

Result := MinFloat(arr);
end;


function TUnitWeaponData.GetAbsoluteWeaponDirection: TVector3f;
begin
Result := AffineVectorMake(VectorSubtract(TipPosition.absolutePosition, TipPosition.Parent.AbsolutePosition));
NormalizeVector(Result);
end;

function TUnitWeaponData.inLineofFire(Where: PAffineVector = nil): Boolean;
var
Direction: TVector3f;
classicVector: TVector;
begin
Result := False;
Direction :=  GetAbsoluteWeaponDirection;
{ TODO 5 -oDa Stranger -cCritical : Do *not* use the 'D' thing!!!!! }
if owner.UnitType.UnitClass.Filename = 'D' then
  begin
  if (GetMinimalAngleToTarget(Direction) < WeaponGroup.WeaponSettings.DeltaAngle) then
    begin
    if Assigned(Where) then
       //realistic
       //SetVector(Where^,VectorAdd(Owner.MovementCube.Position.AsAffineVector,VectorScale(Direction,owner.distanceto(Target.GameUnit))));
       //accurate
       SetVector(Where^, Target.GameUnit.MovementCube.AbsoluteAffinePosition);
    Result := True;
    end;
  end
else
if Target.GameUnit.ActorProxy.RayCastIntersect(WeaponCube.AbsolutePosition,VectorMake(Direction),@classicVector) then
  begin
    Where^ := AffineVectorMake(classicVector);
  Result := True;
  end;
end;


procedure TUnitWeaponData.TryToAttack(delta: single);

  procedure ProceedTimer;
  begin
//  if TimeRecharging <= -1000 then
//    TimeRecharging := -1000
//  else
  if TimeRecharging > 0 then
    TimeRecharging := TimeRecharging - delta;
  end;

var
IntersectPoint: TVector3f;
begin
if not Target.isAlive then
  begin
  if WeaponGroup.WeaponType.LaserMode then
    StopFiringLaser;
  ProceedTimer;
  exit;
  end;
{ TODO 5 -oDa Stranger -cCritical : Must check against the whole ship, not just its center }
//don't attack dead Units
//don't attack Units that are out of WeaponRange of this weapon
//don't attack Units that the weapon does not point to
if (Target.GameUnit.FHealth = 0)
or (vectordistance(Target.GameUnit.MovementCube.AbsolutePosition, Owner.MovementCube.AbsolutePosition) > WeaponGroup.WeaponSettings.WeaponRange)
or (not inLineofFire(@IntersectPoint)) then
  begin
  if WeaponGroup.WeaponType.LaserMode then StopFiringLaser;
  ProceedTimer;
  exit;
  end;

if isFiring then
  begin
  //Proceed LaserMode Events
  ThorFx.Target.asaffinevector := TipPosition.AbsoluteToLocal(intersectPoint);
  { TODO 5 -oDa Stranger -cStandard : Fire in front of itSelf, not in the center of the enemy }
  TimeFiringLaser := TimeFiringLaser - delta;
  if TimeFiringLaser < 0 then StopFiringLaser;
  end
else
  begin
  ProceedTimer;
  if TimeRecharging < 0 then StartFiringWeapon(intersectPoint);
  end;

end;

destructor TUnitWeaponData.Destroy;
begin
if WeaponGroup.WeaponType.EnableThor then
  begin
  ThorFx.Destroy;
  GLBthorFX.Destroy;
  end;
if WeaponGroup.WeaponType.EnablePerlin then
  begin
  //lalala
  end;
if WeaponGroup.WeaponType.EnableFire then
  begin
  //lalala
  end;

TipPosition.Destroy;
WeaponProxy.Free; //may exist or not
DeadWeapon.Free;  //may exist or not

WeaponCube.Destroy;
Target.Destroy;
  inherited;
end;

constructor TUnitWeaponData.Create;
begin
Target := TGameUnit.Create(nil);
end;

function TUnitWeaponData.GetSectorObjectNumber(Sector: TVector3s): integer;
begin
Result := Owner.Player.Game.CollisionManager.GetSector3s(Sector).Count;
end;

function TUnitWeaponData.GetSectorUnit(Sector: TVector3s; index: integer): TUnit;
var
tmp: TGLBaseSceneObject;
begin
tmp := Owner.Player.Game.CollisionManager.GetSectorObject(Sector, index).Actor;
if tmp.tag  <>  GOT_UNIT then
  Result := nil
else
  Result:=TUnit(tmp.TagObject);
end;

function TUnitWeaponData.GetNearbyObjectsListCount: Integer;
begin
Result :=  Owner.ColObject.NearbyObjectsList.Count;
end;

function TUnitWeaponData.GetNearbyUnit(index: integer): TUnit;
var
tmp: TGLBaseSceneObject;
begin
tmp := TGLBaseSceneObject(TStrangeCMMapObject(Owner.ColObject.NearbyObjectsList[index]).Actor);
if tmp.tag  <>  GOT_UNIT then
  Result := nil
else
  Result:=TUnit(tmp.TagObject);
end;


{ TUnit }

function TUnit.HasTarget(var WeaponID: shortint):Boolean;
var
i: integer;
begin
Result := False;
WeaponID := -1;
{ TODO 5 -oDa Stranger -cCritical : SOLVE THIS!!!!!!!!!!!!!!!! }
if length(Weapons) <> 0 then
for i := 0 to length(Weapons) - 1 do
if Weapons[i].Target.isAlive then
  begin
  Result := True;
  WeaponID := i;
  break;
  end;
end;

function TUnit.HasTarget: Boolean;
var
WeaponID: shortint;
begin
Result := HasTarget(WeaponID);
end;

procedure TUnit.SeTagressiveMode(Value: Boolean);
begin
FAgressiveMode := Value;
if Value then
if HasTarget then
UpdateAttackCourse(True);
end;

procedure TUnit.Think(delta: single; TickCount: integer);
var
i: byte;
OldTarget: TGameUnit;
begin
//not too often....
Regenerate(delta);

//shoot if you can
  TryToAttack(delta);

OldTarget := PrimaryWeapon.Target;
//search next Target.GameUnit
if (TickCount mod 5) = 1 then
if length(Weapons) > 0 then
for i := 0 to length(Weapons) - 1 do
  Weapons[i].FindNextTarget;

//if unit has a Target.GameUnit or if in aggressive mode then Update path to Target.GameUnit
//and the unit isn't moving in aggresive mode
if (TickCount mod 20) = 1 then
if Weapons[UnitType.PrimaryWeapon].Target.isAlive then
if (Command.Targets.UnitCount <> 0) or (AgressiveMode and (Command.Operation <> OP_MOVE))then
   UpdateAttackCourse(OldTarget <> Weapons[UnitType.PrimaryWeapon].Target);


//if no current Target.GameUnit then continue doing What it was doing before
if (not Weapons[0].Target.isAlive) then
  begin
  case Command.Operation of
    OP_STAND       : ;
    OP_MOVE        :Move(Command.MoveTo);
    OP_ATTACK      :Stop;
    OP_PATROL      : ;
    OP_SELL        : ;
    OP_EXPLODE     : ;
    OP_FOLLOW_AND_DEFEND: ;//nothing here
    OP_STORAGE_BAY : ;//go there
    end;
  end
end;

procedure TUnit.OnUnderAttack(Weapon: TUnitWeaponData);
begin
//don't attack already dead Units...
if FHealth = 0 then exit;

if not AgressiveMode then
if (Command.Operation = OP_STAND) or (Command.Operation = OP_SELL) or
   (Command.Operation = OP_STORAGE_BAY) then
  begin

  end;

//attack if he is not attacking anyone else
if Command.Targets.UnitCount=0 then
  Attack(Weapon.Owner);


//Health :=  Health-Weapons.WeaponType.Power*Player.Game.DamageMatrix[Weapons.WeaponType.DamageType, UnitType.shipClass];
if Health < 0 then
  begin
  Health := 0;
  BlowUp;
  //Add XP points
  end;
end;

function TUnit.DistanceTo(TargetUnit: TUnit):single;
begin
if TargetUnit <> nil then
  Result := VectorDistance(MovementCube.absolutePosition,TargetUnit.MovementCube.absolutePosition)
else
  Result := -1000;
end;

procedure TUnit.UpdateAttackCourse(NewTarget: Boolean);
  procedure SetNewAttackPath(SkipDistanceCheck: Boolean = False);
  label
  again,
  from_begining;
  var
  DistToTarget: single;
  SafeDistFromTarget: single;
  SafeDistAfterEvading: single;
  dir: TVector3f;
  sign: shortint;
  SafeDist: Single;
  IntersectPoint: TVector;
  begin
  with Weapons[UnitType.PrimaryWeapon] do
    begin
    //2   radiuses are needed to go back and turn around
    //3rd radius is needed to avoid collision after attack
    //
    ColObject.WaypointNodes.Count := 2;
                                       {***} from_begining: {***}
    Movement.ClearNodes;
    Command.LastTargetPosition := Target.GameUnit.MovementCube.AbsolutePosition;

    DistToTarget := DistanceTo(Target.GameUnit);
    SafeDist := SafeDistanceBeforeAttack;

{}  Command.OutPathNode := nil;

    //to prepare for an attack you first have to move away from the target
    //temp is the optimal distance
    if not SkipDistanceCheck then
    if DistToTarget < SafeDist then
      begin //if the unit is facing another, the you need to go more
      if VectorDotProduct(MovementCube.AbsolutePosition,
           VectorNormalize(VectorSubtract(MovementCube.AbsolutePosition,
           Command.LastTargetPosition))) < 0 then
        Movement.ClearAndMoveForward(SafeDist - DistToTarget)
      else
        Movement.ClearAndMoveForward(SafeDist + DistToTarget);

      Movement.GetLastNode.FastTurnMode := (Player.Game.myrandom > 0.5);
{}    Command.OutPathNode := Movement.GetLastNode;
      end;

    Movement.AddTurn(Target.GameUnit.MovementCube.AbsoluteAffinePosition);
    Movement.GetLastNode.FastTurnMode := True;
{}  Command.AttackPathNode := Movement.GetBeforeLastNode;
    ColObject.WaypointNodes[0] := Command.AttackPathNode;

    if SkipDistanceCheck then
      begin
      if Movement.NodeCount > UNIT_ATTACK_MAX_ANGLE / Movement.Manager.TurnByAngleNumber then
        SetNewAttackPath(true)
      //else if {check distance to target to be right}

        //Continue adding move-away-from-the target nodes
      {
      if not Target.GameUnit.ActorProxy.RayCastIntersect(VectorMake(Movement.nodes[Movement.lastnode - 1].position), VectorMake(dir), @IntersectPoint) then
        beep;
      temp := vectordistance(affineVectorMake(IntersectPoint), Movement.nodes[Movement.lastnode - 1].position);
      Movement.GetLastNode.position := vectorLerp(Movement.nodes[Movement.lastnode - 1].position,affineVectorMake(IntersectPoint), (temp - Movement.BigTurnRadius) / temp);
      Movement.GetLastNode.DontDeccelerateAtTurn := True;
      }

      end;

    //my new code
    dir := Movement.EstimateDirectionLastNode(True);
    DistToTarget := Movement.DistancePreLastToLastNode;

    if not Target.GameUnit.BoundingBox.RayCastIntersect(VectorMake(Movement.GetBeforeLastNodePosition, 1), VectorNormalize(VectorMake(dir,1)), @IntersectPoint) then
      Player.Game.MakeError('RayCastIntersect failed!', ERR_UNKNOWN);

    SafeDistFromTarget := GetEnemyEvadeDistance(VectorDistance(Target.GameUnit.ActorProxy.AbsolutePosition,IntersectPoint));
    Movement.GetLastNode.Position := VectorLerp(Movement.GetBeforeLastNodePosition,Movement.GetLastNodePosition, (DistToTarget - SafeDistFromTarget) / DistToTarget);

    if SafeDistFromTarget > WeaponGroup.WeaponSettings.WeaponRange then
      Player.Game.MakeError('Too litle Weapon Range in UnitType "' + UnitType.RealName + '"!', ERR_UNKNOWN);

    case UnitType.GeomStyle of
      //Small
      utgSmall:
        begin
        SafeDistAfterEvading := (UnitType.BoundingRadius + Target.GameUnit.UnitType.BoundingRadius) * UNIT_SAFE_DIST_AFTER_EVADING_MULT;
                                       {***} again: {***}
        case Player.Game.myRandom(6) of
          0: dir := AffineVectorMake(IntersectPoint[0], IntersectPoint[1] - SafeDistAfterEvading, IntersectPoint[2]);
          1: dir := AffineVectorMake(IntersectPoint[0], IntersectPoint[1] + SafeDistAfterEvading, IntersectPoint[2]);
          2: dir := AffineVectorMake(IntersectPoint[0] - SafeDistAfterEvading, IntersectPoint[1], IntersectPoint[2]);
          3: dir := AffineVectorMake(IntersectPoint[0] + SafeDistAfterEvading, IntersectPoint[1], IntersectPoint[2]);
          4: dir := AffineVectorMake(IntersectPoint[0], IntersectPoint[1], IntersectPoint[2] + SafeDistAfterEvading);
          5: dir := AffineVectorMake(IntersectPoint[0], IntersectPoint[1], IntersectPoint[2] - SafeDistAfterEvading);
          end;
        //SafeDist :=RadToDeg(ArcCos(VectorAngleCosine(VectorNormalize(VectorNegate(Movement.GetLastNodeAngle(True))), VectorNormalize(VectorSubtract(dir, Movement.GetLastNode.Position)))));
        if (RadToDeg(ArcCos(VectorAngleCosine(VectorNormalize(VectorNegate(Movement.EstimateDirectionLastNode(True))), VectorNormalize(VectorSubtract(dir, Movement.GetLastNode.Position))))) > UNIT_MAX_EVADE_ANGLE) then
          goto again;

        Movement.AddTurn(dir);
        Movement.GetLastNode.FastTurnMode := True;
        Movement.AddMoveForward(SafeDist);

        if Movement.GetLastNode.Index - Command.AttackPathNode.Index > UNIT_MAX_EVADE_ANGLE_NUMBER / Movement.Manager.TurnByAngleNumber then
          goto from_begining;
        end;

      //Big
      utgBig:
        begin
        //SafeDistAfterEvading := (UnitType.BoundingRadius + Target.GameUnit.UnitType.BoundingRadius) * UNIT_SAFE_DIST_AFTER_EVADING_MULT;
        Player.Game.MakeError('Attack on Big objects is not supported yet!', ERR_UNKNOWN);
        end;

      //Flat
      utgFlat:
        begin
        SafeDistAfterEvading := (UnitType.BoundingDimentions[2] + Target.GameUnit.UnitType.BoundingDimentions[2]) * UNIT_SAFE_DIST_AFTER_EVADING_MULT;

        if Player.Game.myRandom > 0.5 then
          begin
          dir := AffineVectorMake(IntersectPoint[0], IntersectPoint[1], IntersectPoint[2] + SafeDistAfterEvading);
          sign := 1;
          end
        else
          begin
          dir := AffineVectorMake(IntersectPoint[0], IntersectPoint[1], IntersectPoint[2] - SafeDistAfterEvading);
          sign := -1;
          end;

        Movement.AddTurn(dir);
        Movement.GetLastNode.FastTurnMode := True;

        Player.Game.myRandomPointOnSphere(SafeDist, dir, sign);
        Movement.AddTurn(VectorAdd(dir, Target.GameUnit.MovementCube.Position.AsAffineVector));
        end;

      //High
      utgHigh:
        begin
        SafeDistAfterEvading := (UnitType.BoundingWidth + Target.GameUnit.UnitType.BoundingWidth) * UNIT_SAFE_DIST_AFTER_EVADING_MULT;
        case Player.Game.myRandom(4) of
          0: dir := AffineVectorMake(IntersectPoint[0], IntersectPoint[1] - SafeDistAfterEvading, IntersectPoint[2]);
          1: dir := AffineVectorMake(IntersectPoint[0], IntersectPoint[1] + SafeDistAfterEvading, IntersectPoint[2]);
          2: dir := AffineVectorMake(IntersectPoint[0] - SafeDistAfterEvading, IntersectPoint[1], IntersectPoint[2]);
          3: dir := AffineVectorMake(IntersectPoint[0] + SafeDistAfterEvading, IntersectPoint[1], IntersectPoint[2]);
          end;

        Movement.AddTurn(dir);
        Movement.GetLastNode.FastTurnMode := True;
        Movement.AddMoveForward(SafeDist);
        end;

      end;

{}  Command.EvadePathNode := Movement.GetBeforeLastNode;
    ColObject.WaypointNodes[1] := Command.EvadePathNode;
    Movement.StartMoving;
    ColObject.SmoothEvasion := False;
    ColObject.CalculateWaypoints(Movement.GetLastNode);
    end;
  end;

var
interpoint: TVector3f;
temp: single;
begin
with Weapons[UnitType.PrimaryWeapon] do
  begin
  if NewTarget then
    SetNewAttackPath(False)

  //this works really bad, when unit is flying after some other unit and
  //can't catch it or if target is to far away
  //solution - check this really rarely
  //call the whole rocedure really rarely!!!!
  else if (Command.OutPathNode <> nil) then
  if (Movement.CurrentNode <= Command.OutPathNode.Index) then
  if not VectorEquals(Command.LastTargetPosition, Target.GameUnit.MovementCube.AbsolutePosition) then
  if DistanceTo(Target.GameUnit) > SafeDistanceBeforeAttack then
    SetNewAttackPath;

  if (Movement.CurrentNode = Command.AttackPathNode.Index) then
    begin
    if not Weapons[UnitType.PrimaryWeapon].inLineofFire(@interpoint) then
      SetNewAttackPath(True)
    else
      begin
      temp := VectorDistance(interpoint, Command.AttackPathNode.position);
      if (temp < 0.75 * Movement.BigTurnRadius) then
        SetNewAttackPath(True);
      end;
    end

   else if (Movement.CurrentNode > Command.EvadePathNode.Index) {and (Movement.CurrentNode <= Command.EvadePathNode2.Index)} then
    SetNewAttackPath;
  end;
end;

procedure TUnit.StopFiring;
var i: byte;
begin
for i := 0 to length(Weapons) - 1 do
  Weapons[i].StopFiringLaser;
Command.Targets.Clear;
end;


procedure TUnit.BlowUp;
var
MeshList: array of TglBaseSceneObject;
i: byte;
begin
if UNIT_STOP_ON_EXPLOSION then
  Stop     { TODO -oDa Stranger -cDebug : In the Final Release, remove this check }
else
  begin
  Command.Operation := OP_STAND;
  Command.Targets.Clear;
  StopFiring;

  //stop Moving...
  Movement.FollowObjectSettings.FollowTarget := nil;
  end;

setlength(MeshList,1);
Health := 0;

//Remove from PickList and detach any attached cameras
Player.LocalSettings.PickList.RemoveUnit(self);
with Player.Game do
for i := 0 to length(Players) - 1 do
if Players[i].LocalSettings <> nil then
  begin
  if Players[i].LocalSettings.Camera.TargetObject = ActorProxy then
    Players[i].LocalSettings.DetachCamera;
  end;

if UnitType.UsesAnimation then
  begin
  for i := 0 to 2 do
  if i = Player.Game.CurrentProfile.VideoSettings.BlowUpModelQuality then
    begin
    DeadActor := TglActor(ActorProxy.MasterObjects[i].MasterObject);
    DeadActor.MoveTo(MovementCube);
    DeadActor.Material.QuickAssignMaterial(Player.Game.SceneObjects.UnitMaterialLibrary,
      ActorProxy.MasterObjects[i].LibTexture);
    Player.Game.ModelManager.RemoveModel(DeadActor, False);
    end
  else
    Player.Game.ModelManager.RemoveModel(TGLActor(ActorProxy.MasterObjects[i].MasterObject));
  end
else
  begin
  Deadactor := TGLactor(MovementCube.AddNewChild(TGLactor));
  DeadActor.MeshObjects.Assign(TglActor(ActorProxy.MasterObjects[Player.Game.CurrentProfile.VideoSettings.BlowUpModelQuality].MasterObject).MeshObjects);
  DeadActor.Material.QuickAssignMaterial(Player.Game.SceneObjects.UnitMaterialLibrary,
    ActorProxy.MasterObjects[Player.Game.CurrentProfile.VideoSettings.BlowUpModelQuality].LibTexture);
  end;

DeadActor.Matrix := ActorProxy.Matrix;
MeshList[0] := DeadActor;

if length(Weapons) <> 0 then
for i := 0 to length(Weapons) - 1 do
if Weapons[i].WeaponProxy <> nil then //show weapon model enabled
  begin
  if UnitType.SyncronizeWeaponAnimation then
    Weapons[i].DeadWeapon := TGLactor(DeadActor.AddNewChild(TGLactor))
  else
    Weapons[i].DeadWeapon := TGLactor(Weapons[i].WeaponCube.AddNewChild(TGLactor));

  with Weapons[i].DeadWeapon do
    begin
    Material.QuickAssignMaterial(Player.Game.SceneObjects.UnitMaterialLibrary,
      Weapons[i].WeaponProxy.MasterObjects[Player.Game.CurrentProfile.VideoSettings.BlowUpModelQuality].LibTexture);
    MeshObjects.Assign(TglActor(Weapons[i].WeaponProxy.MasterObjects[Player.Game.CurrentProfile.VideoSettings.BlowUpModelQuality].MasterObject).MeshObjects);
    Matrix := Weapons[i].WeaponProxy.Matrix;
    end;
  setlength(MeshList, length(MeshList) + 1);
  MeshList[length(MeshList) - 1] := Weapons[i].DeadWeapon;

  Weapons[i].TipPosition.Visible := False;
  Weapons[i].TipPosition.MoveTo(Weapons[i].WeaponCube);
  Weapons[i].WeaponProxy.Destroy;
  Weapons[i].WeaponProxy := nil;
  end;

ActorProxy.Destroy;
ActorProxy := nil;

Command.Operation := OP_EXPLODE;
Command.Targets.Clear;
Explosion.ReplaceExplosionMeshes(MeshList);
Explosion.BeginExplosion;

//if it defends another then stop doing it
if Command.Operation = OP_FOLLOW_AND_DEFEND then
Command.Targets[0].GameUnit.ProtectedByList.RemoveUnit(Self);

//if it was defended by others, then let them go
if ProtectedByList.Count <> 0 then
for i := 0 to ProtectedByList.LastUnit do
  begin
  ProtectedByList[i].Command.Operation := OP_STAND;
  ProtectedByList[i].Movement.FollowObjectSettings.FollowTarget := nil;
  end;
end;

procedure TUnit.Stop;
begin
Command.Operation := OP_STAND;
Command.Targets.Clear;
StopFiring;

//stop Moving...
Movement.FollowObjectSettings.FollowTarget := nil;
Movement.ClearAndAddStopPath(False);
ColObject.WaypointNodes.Count := 1;
ColObject.CalculateWaypoints(Movement.GetLastNode);
end;

procedure TUnit.TryToAttack(delta: single);
var
i: byte;
begin
if length(Weapons) <> 0 then
for i := 0  to length(Weapons) - 1 do
  Weapons[i].TryToAttack(delta);
end;

procedure TUnit.Attack(TargetUnit: TUnit);
var
i: byte;
begin
Command.Operation := OP_ATTACK;
Command.Targets.Clear;
Command.Targets.AddUnit(TargetUnit);

Movement.FollowObjectSettings.FollowTarget :=  nil;
UpdateAttackCourse(True);

for i := 0 to length(Weapons) - 1 do
  Weapons[i].Target.Assign(TargetUnit);

DirectGL.Visible := True;
SecondsBeforeHidingDirectGL := SHOW_UNIT_MOVEMENT_LINES_FOR;
end;


procedure TUnit.Attack(TargetUnits: array of TUnit);
begin
Command.Operation := OP_ATTACK;
Command.Targets.Clear;
Command.Targets.AddUnits(TargetUnits);

Movement.FollowObjectSettings.FollowTarget :=  nil;
UpdateAttackCourse(True);

{ TODO 5 -oDa Stranger -cCritical : Fill this out!!!!!!!!!!!!!!!!!!!! }

DirectGL.Visible := True;
SecondsBeforeHidingDirectGL := SHOW_UNIT_MOVEMENT_LINES_FOR;
end;


procedure TUnit.MoveByWaypoints(Dest: array of TVector3f);
var
i: integer;
len: integer;
begin
Command.Operation := OP_MOVE_BY_WAYPOINTS;
Command.Targets.Clear;
StopFiring;
Movement.FollowObjectSettings.FollowTarget :=  nil;
Movement.ClearNodes;

len := length(Dest);
setlength(Command.PatrolPoints.List, len);
ColObject.SmoothEvasion := True;
ColObject.WaypointNodes.Count := len;

if len=0 then
  Player.Game.MakeError('Invalid Point Count',ERR_UNVALID_POINT_COUNT)
else
for i := 0 to len - 1 do
  begin
  Command.PatrolPoints[i] := Dest[i];
  Movement.AddTurn(Dest[i]);
  Colobject.WaypointNodes[i] := Movement.GetLastNode;
  end;

Movement.StartMoving;
ColObject.CalculateWaypoints;

DirectGL.Visible := True;
SecondsBeforeHidingDirectGL := SHOW_UNIT_MOVEMENT_LINES_FOR;
end;

procedure TUnit.Patrol(Dest: array of TVector3f);
var
i: integer;
len: integer;
begin
Command.Operation := OP_PATROL;
Command.Targets.Clear;
StopFiring;
Movement.FollowObjectSettings.FollowTarget :=  nil;
Movement.ClearNodes;

len := length(Dest);
setlength(Command.PatrolPoints.List, len);
ColObject.SmoothEvasion := True;
ColObject.WaypointNodes.Count := len + 1;

if len=0 then
  Player.Game.MakeError('Invalid Point Count',ERR_UNVALID_POINT_COUNT)
else
for i := 0 to len - 1 do
  begin
  Command.PatrolPoints[i] := Dest[i];
  Movement.AddTurn(Dest[i]);
  Colobject.WaypointNodes[i] := Movement.GetLastNode;
  end;

Movement.AddTurn(Command.PatrolPoints[0]);
Colobject.WaypointNodes[len] := Movement.GetLastNode;

Movement.StartMoving;
ColObject.CalculateWaypoints;

DirectGL.Visible := True;
SecondsBeforeHidingDirectGL := SHOW_UNIT_MOVEMENT_LINES_FOR;
end;

procedure TUnit.Move(Dest: TVector3f);
begin
if VectorDistance(Dest, MovementCube.AbsoluteAffinePosition) < EPS then exit;

Command.Operation := OP_MOVE;
Command.Targets.Clear;
Command.MoveTo := Dest;

StopFiring;

Movement.FollowObjectSettings.FollowTarget :=  nil;
Movement.ClearNodes;
Movement.AddTurn(Dest);
Movement.StartMoving;

ColObject.SmoothEvasion := True;
ColObject.WaypointNodes.Count := 1;
ColObject.CalculateWaypoints(movement.GetLastNode);

DirectGL.Visible := True;
SecondsBeforeHidingDirectGL := SHOW_UNIT_MOVEMENT_LINES_FOR;
end;

procedure TUnit.SetHealth(Value: single);
begin
FHealth := Value;
UnitHUD.UnitHUDSettings.SetCurrentHealthLevel(Value / MaxHealth);
end;

procedure TUnit.SetShields(Value: single);
begin
FShields := Value;
UnitHUD.UnitHUDSettings.SetCurrentArmorLevel(Value / MaxShields);
end;

constructor TUnit.Create(WhatPlayer: TPlayer; WhatUnitType: TPlayerUnitType;
  WhatEngineType: TEngineType; WhatShieldType: TShieldType;
  WhatEquipmentTypes: array of TEquipmentType; WhatWeaponTypes: array of TWeaponType);
var
i, j: byte;
WeaponIndex: byte;
Path: string;
tmp: TGLActor;
begin
Player := WhatPlayer;
UnitType := WhatUnitType;
EngineType := WhatEngineType;
ShieldType := WhatShieldType;

setlength(EquipmentTypes,length(WhatEquipmentTypes));
if length(WhatEquipmentTypes) <> 0 then
for i := 0 to length(WhatEquipmentTypes) - 1 do
  EquipmentTypes[i] := WhatEquipmentTypes[i];

Path := str_OBJECTS + '\' + str_Units + '\' + UnitType.Race.Filename + '\' + UnitType.Filename + '\';

//if there is any Anmation, swich to one of the main one

MovementCube := TGLBaseSceneObject(Player.Game.SceneObjects.TransparentObject.AddNewChild(TGLBaseSceneObject));
MovementCube.ObjectsSorting := osNone;
MovementCube.TagObject := Self;
MovementCube.Tag := GOT_UNIT;
MovementCube.Up.AsAffineVector := AffineVectorMake(0 ,0, 1);
MovementCube.Direction.AsAffineVector := AffineVectorMake(0, -1, 0);

ActorProxy := TGLMaterialMultiProxy(MovementCube.AddNewChild(TGLMaterialMultiProxy));
ActorProxy.MaterialLibrary := Player.Game.SceneObjects.UnitMaterialLibrary;

if UnitType.UsesAnimation then
  begin
  tmp := Player.Game.ModelManager.CloneModel(UnitType.ActorFar, Player.Game.CurrentID + 1);
  ActorProxy.MasterObjects.Add(tmp, UnitType.TextureFar[DL_LOW], Player.Game.CurrentProfile.VideoSettings.MultiProxyMediumDistance, Player.Game.CurrentProfile.VideoSettings.MultiProxyFarDistance);

  if Player.Game.CurrentProfile.VideoSettings.MeshQuality <> MQ_HIGH then
    tmp :=  UnitType.ActorMedium
  else
    tmp := Player.Game.ModelManager.CloneModel(UnitType.ActorMedium, Player.Game.CurrentID + 1);
  ActorProxy.MasterObjects.Add(tmp, UnitType.TextureMedium[DL_LOW], Player.Game.CurrentProfile.VideoSettings.MultiProxyNearDistance, Player.Game.CurrentProfile.VideoSettings.MultiProxyMediumDistance);

  if Player.Game.CurrentProfile.VideoSettings.MeshQuality <> MQ_HIGH then
    tmp :=  UnitType.ActorNear
  else
    tmp := Player.Game.ModelManager.CloneModel(UnitType.ActorNear, Player.Game.CurrentID + 1);
  ActorProxy.MasterObjects.Add(tmp, UnitType.TextureNear[DL_LOW], 0, Player.Game.CurrentProfile.VideoSettings.MultiProxyNearDistance);

  TorsoTags := TMD3TagList.Create;
  TorsoTags.LoadFromStream(UnitType.AnimationStream);

  SwitchToAnimation(str_MAIN);
  end
else
  begin
  ActorProxy.MasterObjects.Add(UnitType.ActorNear, UnitType.TextureNear[DL_LOW], 0, Player.Game.CurrentProfile.VideoSettings.MultiProxyNearDistance);
  ActorProxy.MasterObjects.Add(UnitType.ActorMedium, UnitType.TextureMedium[DL_LOW], Player.Game.CurrentProfile.VideoSettings.MultiProxyNearDistance, Player.Game.CurrentProfile.VideoSettings.MultiProxyMediumDistance);
  ActorProxy.MasterObjects.Add(UnitType.ActorFar, UnitType.TextureFar[DL_LOW], Player.Game.CurrentProfile.VideoSettings.MultiProxyMediumDistance, Player.Game.CurrentProfile.VideoSettings.MultiProxyFarDistance);
  //ActorProxy.MasterObjects.Add(nil, Player.Game.CurrentProfile.GeneralSettings.MultiProxyFarDistance, Player.Game.CurrentProfile.VideoSettings.MultiProxyFarDistance * 10000);
  end;

ActorProxy.Up.AsAffineVector := AffineVectorMake(1, 0, 0);
ActorProxy.Direction.AsAffineVector := AffineVectorMake(0, 1, 0);
ActorProxy.roll(-90);
ActorProxy.Scale.Scale(UnitType.Scale);
ActorProxy.TagObject := Self;
ActorProxy.Tag := GOT_UNIT;

EffectsCube := TGLBaseSceneObject(MovementCube.AddNewChild(TGLBaseSceneObject));
EffectsCube.ObjectsSorting := osNone;
EffectsCube.Direction.AsVector := YHmgVector;
EffectsCube.Up.AsVector :=  ZHmgVector;

BoundingBox := TglCube(MovementCube.AddNewChild(TglCube));
BoundingBox.Scale.Scale(UnitType.Scale);
BoundingBox.Direction.AsAffineVector := AffineVectormake(0,-1,1);
BoundingBox.Up.AsAffineVector := AffineVectormake(0,0,1);
BoundingBox.CubeWidth := (UnitType.MaxExtents[0] - UnitType.MinExtents[0]);
BoundingBox.CubeHeight := (UnitType.MaxExtents[1] - UnitType.MinExtents[1]);
BoundingBox.CubeDepth := (UnitType.MaxExtents[2] - UnitType.MinExtents[2]);
//BoundingBox.Position.AsAffineVector := VectorLerp(UnitType.MinExtents, UnitType.MaxExtents, 0.5);
BoundingBox.Material.FrontProperties.Ambient.AsWinColor := RGB(0,255,0);
BoundingBox.Material.FrontProperties.Diffuse.AsWinColor := RGB(0,255,0);
BoundingBox.Material.FrontProperties.Emission.AsWinColor := RGB(0,255,0);
BoundingBox.Material.FrontProperties.PolygonMode := pmLines;
BoundingBox.Material.BackProperties.PolygonMode  := pmLines;

ParentInvariantObject := TStrangeParentInvariantObject.CreateAsChild(MovementCube);
UnitHUD := TStrangeUnitHUD.Create(ParentInvariantObject);
UnitHUD.UnitHUDSettings.ObjectHeight := UnitType.BoundingRadius;
UnitHUD.UnitHUDSettings.HUDWidth := UnitType.BoundingRadius;
UnitHUD.UnitHUDSettings.HUDHeight := UnitHUD.UnitHUDSettings.HUDWidth / 10;
UnitHUD.UnitHUDSettings.AddLifeHUD;
UnitHUD.UnitHUDSettings.AddArmorHUD;

WeaponIndex := 0;
setlength(WeaponGroups, length(UnitType.WeaponGroups));

if length(WeaponGroups) <> 0 then
for j := 0 to length(WeaponGroups) - 1 do
  begin
  WeaponGroups[j] := TUnitWeaponGroup.Create(UnitType.WeaponGroups[j]);
  WeaponGroups[j].WeaponType := WhatWeaponTypes[j];
  WeaponGroups[j].WeaponSettings.Multiply(WeaponGroups[j].WeaponType.WeaponSettings);

  for i := 0 to length(WeaponGroups[j].Weapons) - 1 do
    begin
    setlength(Weapons, WeaponIndex + 1);
    Weapons[WeaponIndex] := WeaponGroups[j].Weapons[i];
    inc(WeaponIndex);
    with WeaponGroups[j].Weapons[i] do
      begin
      WeaponGroup := WeaponGroups[j];
      Owner := Self;
      WeaponCube :=  TGLBaseSceneObject.CreateAsChild(MovementCube);
      WeaponCube.ObjectsSorting := osNone;
      WeaponCube.AbsoluteAffinePosition := UnitType.WeaponGroups[j].WeaponParams[i].Position;
      WeaponCube.AbsoluteAffineDirection := UnitType.WeaponGroups[j].WeaponParams[i].Direction;
      WeaponCube.AbsoluteAffineUp := UnitType.WeaponGroups[j].WeaponParams[i].UpVector;

      if WeaponGroup.WeaponType.ShowWeaponModel then
      with WeaponProxy do
        begin
        if UnitType.SyncronizeWeaponAnimation then
          begin
          WeaponProxy := TGLMaterialMultiProxy(ActorProxy.AddNewChild(TGLMaterialMultiProxy));
          //need to have a weapon to test it...
          //Direction.AsAffineVector := UnitType.WeaponGroups[j].WeaponParams[i].Direction;
          //Up.AsAffineVector := UnitType.WeaponGroups[j].WeaponParams[i].UpVector;
          //RollAngle := 180;
          WeaponProxy.Scale.Scale(WeaponGroup.WeaponType.ModelScale / UnitType.Scale);
          TipPosition := TGLBaseSceneObject(WeaponProxy.AddNewChild(TGLBaseSceneObject));
          TipPosition.Scale.Scale(WeaponGroup.WeaponType.EffectScale / UnitType.Scale);
          end
        else
          begin
          WeaponProxy := TGLMaterialMultiProxy(WeaponCube.AddNewChild(TGLMaterialMultiProxy));
          //need to have a weapon to test it...
          //Direction.AsAffineVector := UnitType.WeaponGroups[j].WeaponParams[i].Direction;
          //Up.AsAffineVector := UnitType.WeaponGroups[j].WeaponParams[i].UpVector;
          //RollAngle := 180;
          WeaponProxy.Scale.Scale(WeaponGroup.WeaponType.ModelScale);
          TipPosition := TGLBaseSceneObject(WeaponProxy.AddNewChild(TGLBaseSceneObject));
          TipPosition.Scale.Scale(WeaponGroup.WeaponType.EffectScale);
          end;

        TipPosition := TGLBaseSceneObject(WeaponProxy.AddNewChild(TGLBaseSceneObject));
        TipPosition.AbsoluteAffinePosition := UnitType.WeaponGroups[j].WeaponParams[i].TipPosition;
        TipPosition.AbsoluteAffinePosition := VectorScale(VectorNormalize(VectorSubtract(TipPosition.AbsoluteAffinePosition, TipPosition.Parent.AbsoluteAffinePosition)), WeaponGroup.WeaponType.WeaponLength);

        with WeaponGroup.WeaponType do
          begin
          WeaponProxy.MaterialLibrary := Player.Game.SceneObjects.UnitMaterialLibrary;
          WeaponProxy.MasterObjects.Add(ActorNear, TextureNear, 0, Player.Game.CurrentProfile.VideoSettings.MultiProxyNearDistance);
          WeaponProxy.MasterObjects.Add(ActorMedium, TextureMedium, Player.Game.CurrentProfile.VideoSettings.MultiProxyNearDistance, Player.Game.CurrentProfile.VideoSettings.MultiProxyMediumDistance);
          WeaponProxy.MasterObjects.Add(ActorFar, TextureFar, Player.Game.CurrentProfile.VideoSettings.MultiProxyMediumDistance, Player.Game.CurrentProfile.VideoSettings.MultiProxyFarDistance);
          //MasterObjects.Add(nil, Player.Game.CurrentProfile.GeneralSettings.MultiProxyFarDistance, Player.Game.CurrentProfile.VideoSettings.MultiProxyFarDistance * 10000);
          end;
        end
      else
        begin
        TipPosition := TGLBaseSceneObject(MovementCube.AddNewChild(TGLBaseSceneObject));
        TipPosition.AbsoluteAffinePosition := UnitType.WeaponGroups[j].WeaponParams[i].TipPosition;
        TipPosition.Scale.Scale(WeaponGroup.WeaponType.EffectScale);
        end;
      TipPosition.ObjectsSorting := osNone;

      if WeaponGroup.WeaponType.LaserMode then
        begin
        if WeaponGroup.WeaponType.EnableThor then
          begin
          ThorFx := TglThorFXManager.Create(nil);
          AssignThorFXManager(WeaponGroup.WeaponType.ThorManager, ThorFx);
          ThorFx.Cadencer :=  Player.Game.SceneObjects.Cadencer;
          GLBthorFX := TGLBthorFX(TipPosition.Effects[TipPosition.Effects.Add(TGLBthorFX.Create(nil))]);
          GLBthorFX.Manager :=  ThorFx;
          end;
        end
      else
        begin
        //else effects should be added to TFlyingMissile
        end;
      end;
    end;
  end;
//Calculate Parameters...

Movement := Player.Game.MovementManager.AddMovement(MovementCube);
Movement.TagObject := Self;
Movement.Tag := GOT_UNIT;
Movement.FollowObjectSettings.UpdateFollowObjectPathPeriod := 1;
Movement.OnStartMoving := OnStartMoving;
Movement.OnStopMoving := OnStopMoving;
Movement.FollowObjectSettings.OnUpdatePathToFollowedObject := OnUpdatePathToFollowedObject;

Explosion := TmyExplosion.Create(Player.Game.SceneObjects.Cadencer, EffectsCube, EffectsCube, nil, Player.Game.SceneObjects.PartileRenderer, '', Player.Game.SceneObjects.MiscMaterialLibrary);
Explosion.LoadFromMemory(TMemIniFile(UnitType.ExplosionMemIniFile));
Explosion.OnFinishExploding := OnFinishedExploding;
Explosion.TagObject := Self;
Explosion.Tag := GOT_UNIT;

ColObject := Player.Game.CollisionManager.AddMapObject(Movement);
ColObject.OnChangeCourse := OnChangeCourse;
ColObject.Radius := UnitType.BoundingRadius;
ColObject.TagObject := Self;
ColObject.Tag := GOT_UNIT;

Engine := TStrangeEngines.CreateInitialized(EffectsCube, Player.Game.SceneObjects.Cadencer, Player.Game.SceneObjects.PartileRenderer,
  Player.Game.SceneObjects.PartileRenderer);
Engine.LoadFromMemIni(UnitType.EngineMemIniFile);
Engine.StopEngines;

ApplyEveryThing;
SetXPForNextLevel;

//Current Stuff...
People := MaxPeople;
Health := MaxHealth;
Shields := MaxShields;

//Cost will be set from outside
Cost := TCost.Create;

//Command stuff
Command := TUnitCommand.Create;
//Command.AgressiveMode := True;

DirectGL := TGLDirectOpenGL(Player.Game.SceneObjects.LastObject.AddNewChild(TGLDirectOpenGL));
DirectGL.OnRender := onUnitRender;
DirectGL.Visible := False;

ProtectedByList := TSimpleUnitList.Create;

GuiVisible := False;
end;

constructor TUnit.Create(WhatPlayer: TPlayer; WhatUnitType: TPlayerUnitType);
var
tmp: array of TWeaponType;
i: byte;
begin
setlength(tmp,length(WhatUnitType.WeaponGroups));
if length(WhatUnitType.WeaponGroups) <> 0 then
for i := 0 to length(WhatUnitType.WeaponGroups) - 1 do
  tmp[i] := WhatUnitType.WeaponGroups[i].CurrentWeapon;
Create(WhatPlayer, WhatUnitType, WhatUnitType.CurrentEngine, WhatUnitType.CurrentShield, WhatUnitType.CurrentEquipment, tmp);
end;

destructor TUnit.Destroy;
var
i: byte;
begin
DirectGL.Destroy;

BoundingBox.Destroy;
UnitHUD.Destroy;

ParentInvariantObject.Destroy;

Engine.Destroy;
Explosion.Destroy;
ColObject.Destroy;
Movement.Destroy;

if WeaponGroupNumber <> 0 then
for i := 0 to LastWeaponGroup do
  WeaponGroups[i].Destroy;

if UnitType.UsesAnimation then
  Player.Game.ModelManager.RemoveModel(DeadActor)
else
  DeadActor.Free;

TorsoTags.Free;
EffectsCube.Destroy;
ActorProxy.Free;
MovementCube.Destroy;
Cost.Destroy;
Command.Destroy;

ProtectedByList.Destroy;
end;

procedure TUnit.OnFinishedExploding(Sender: TmyExplosion; TagObject: TObject; Tag: integer);
begin
Player.Game.RemoveUnit(Self);
end;

function TUnit.LastWeapon: byte;
begin
Result := length(Weapons) - 1;
end;

function TUnit.LastWeaponGroup: byte;
begin
Result := length(WeaponGroups) - 1;
end;

function TUnit.WeaponGroupNumber: byte;
begin
Result := length(WeaponGroups);
end;

function TUnit.WeaponNumber: byte;
begin
Result := length(Weapons);
end;

function TUnit.PrimaryWeapon: TUnitWeaponData;
begin
Result := Weapons[UnitType.PrimaryWeapon];
end;

procedure TUnit.ApplyShieldSettings(ShieldSettings: TShieldSettings);
begin
Defence := Defence * ShieldSettings.DefenceBonus;

MaxHealth  :=  MaxHealth * ShieldSettings.HealthBonusMult + ShieldSettings.HealthBonusAdd;
MaxShields  :=  MaxShields * ShieldSettings.ShieldsBonusMult + ShieldSettings.ShieldsBonusAdd;
HealthRegenerateRate  :=  HealthRegenerateRate * ShieldSettings.HealthRegenerateRateBonusMult + ShieldSettings.HealthRegenerateRateBonusAdd;
ShieldsRegenerateRate  :=  ShieldsRegenerateRate * ShieldSettings.ShieldsRegenerateRateBonusMult + ShieldSettings.ShieldsRegenerateRateBonusAdd;
end;

procedure TUnit.ApplyEveryThing;
var
i, j: integer;
begin
Movement.StrangeAssign(UnitType.MovementSettings,True,True);
Movement.MultiplyByMovement(EngineType.MovementSettings);
Engine.Mask.LoadFromMemIni(EngineType.myEnginesSettings);

Defence := UnitType.Defence;
MaxHealth := UnitType.MaxHealth;
MaxShields := UnitType.MaxShields;
MaxPeople := UnitType.MaxPeople;
HealthRegenerateRate := UnitType.HealthRegenerateRate;
ShieldsRegenerateRate := UnitType.ShieldsRegenerateRate;
ApplyShieldSettings(ShieldType.ShieldSettings);

if length(EquipmentTypes) <> 0 then
for i := 0 to length(EquipmentTypes) - 1 do
  begin
  Movement.MultiplyByMovement(EquipmentTypes[i].MovementSettings);
  ApplyShieldSettings(EquipmentTypes[i].ShieldSettings);
  if WeaponGroupNumber <> 0 then
  for j := 0 to LastWeaponGroup do
    UnitType.WeaponGroups[j].WeaponSettings.Multiply(EquipmentTypes[i].WeaponSettings);
  end;
end;

procedure TUnit.ApplyNextXP;
var
i: integer;
begin
Movement.MultiplyByMovement(UnitType.UnitClass.MovementSettings);
ApplyShieldSettings(UnitType.UnitClass.ShieldSettings);
if WeaponGroupNumber <> 0 then
for i := 0 to LastWeaponGroup do
  UnitType.WeaponGroups[i].WeaponSettings.Multiply(UnitType.UnitClass.WeaponSettings);
end;

procedure TUnit.ApplyXPLevel;
var
i: integer;
begin
if XPLevel <> 0 then
for i := 1 to XPLevel do
  ApplyNextXP;
end;

procedure TUnit.LevelUp;
begin
ApplyNextXP;
inc(XPLevel);
CurrentXP := CurrentXP - NeedXPForNextLevel;
SetXPForNextLevel;
end;

procedure TUnit.SetXPForNextLevel;
begin
NeedXPForNextLevel := Player.Race.XPNeededForNextLevel * UnitType.XPCost * Power(2, XPLevel + 1);
end;

procedure TUnit.SetGuiVisible(Value: Boolean);
begin
BoundingBox.Visible := Value;
UnitHUD.Visible := Value;
end;

function TUnit.GetGuiVisible: Boolean;
begin
Result := UnitHUD.Visible;
end;

procedure TUnit.OnStartMoving(const Sender: TStrangeMovement);
begin
Engine.StartEngines;
end;

procedure TUnit.OnStopMoving(const Sender: TStrangeMovement);
var
i: integer;
begin
Engine.StopEngines;
case Command.Operation of
  OP_MOVE: Command.Operation := OP_STAND;
  OP_STAND: ;
  OP_ATTACK: if Command.Targets.UnitCount <> 0 then UpdateAttackCourse(True);
  OP_PATROL:
    begin
    //make him continue patroling...
    Movement.ClearNodes;
    Colobject.WaypointNodes.Count := Command.PatrolPoints.Count;
    for i := 0 to Command.PatrolPoints.Last do
      begin
      Movement.AddTurn(Command.PatrolPoints[i]);
      Colobject.WaypointNodes[i] := Movement.GetLastNode;
      end;

    Movement.StartMoving;
    ColObject.CalculateWaypoints;
    end;

  OP_SELL  :;
  OP_EXPLODE:;
  OP_FOLLOW_AND_DEFEND:;//
  OP_STORAGE_BAY :;
  OP_MOVE_AND_ATTACK:;
  OP_MOVE_BY_WAYPOINTS : Command.Operation := OP_STAND;
  OP_FACE_Direction :;
  end;
end;

procedure TUnit.onUnitRender(Sender : TObject; var rci : TRenderContextInfo);
var
tmp: integer;
i: integer;
begin
//Draw a move-to line
if Movement.NodeCount = 0 then exit;
glDisable(GL_LIGHTING);
glColor3b(127,0,127);

case Command.Operation of
  OP_MOVE, OP_STAND:
    begin
    glbegin(GL_LINES);
      glVertex3f(MovementCube.AbsolutePosition[0], MovementCube.AbsolutePosition[1], MovementCube.AbsolutePosition[2]);
      glVertex3f(Movement.GetLastNode.Position[0], Movement.GetLastNode.Position[1], Movement.GetLastNode.Position[2]);
    glend;
    end;

  OP_MOVE_BY_WAYPOINTS:
    begin
    tmp := ColObject.GetNextWaypointIndex;
    glBegin(GL_LINE_STRIP);
      glVertex3f(MovementCube.AbsolutePosition[0], MovementCube.AbsolutePosition[1], MovementCube.AbsolutePosition[2]);
      for i := tmp to ColObject.LastWaypoint do
        glVertex3f(ColObject.WaypointNodes[i].Position[0], ColObject.WaypointNodes[i].Position[1], ColObject.WaypointNodes[i].Position[2]);
    glEnd;
    end;

  OP_PATROL:
    begin
    glBegin(GL_LINE_LOOP);
      for i := 0 to Command.PatrolPoints.Last do
        glVertex3f(Command.PatrolPoints[i][0], Command.PatrolPoints[i][1], Command.PatrolPoints[i][2]);
    glEnd;
    tmp := ColObject.GetNextWaypointIndex;
//    if ColObject.WaypointCount <> Command.PatrolPoints.Count then
//    if tmp = 0 then
      begin
      glColor3b(127,127,10);
      glBegin(GL_LINES);
        glVertex3f(MovementCube.AbsolutePosition[0],MovementCube.AbsolutePosition[1],MovementCube.AbsolutePosition[2]);
        glVertex3f(Command.PatrolPoints[tmp][0], Command.PatrolPoints[tmp][1], Command.PatrolPoints[tmp][2]);
      glEnd;
      end;
    end;

  OP_ATTACK:
  if PrimaryWeapon.Target.isAlive then
    begin
    glColor3b(127,5,5);
    glBegin(GL_LINES);
      glVertex3f(MovementCube.AbsolutePosition[0], MovementCube.AbsolutePosition[1], MovementCube.AbsolutePosition[2]);
      glVertex3f(PrimaryWeapon.Target.GameUnit.MovementCube.AbsolutePosition[0], PrimaryWeapon.Target.GameUnit.MovementCube.AbsolutePosition[1], PrimaryWeapon.Target.GameUnit.MovementCube.AbsolutePosition[2]);
    glEnd;
    end;

  OP_FOLLOW_AND_DEFEND:
    begin
    glColor3b(107,52,5);
    glBegin(GL_LINES);
      glVertex3f(MovementCube.AbsolutePosition[0], MovementCube.AbsolutePosition[1], MovementCube.AbsolutePosition[2]);
      glVertex3f(GetFollowedUnitPosition[0], GetFollowedUnitPosition[1], GetFollowedUnitPosition[2]);
    glEnd;
    end;

  end;
glEnable(GL_LIGHTING);
end;


procedure TUnit.Regenerate(delta: single);
begin
if FHealth < MaxHealth then
  begin
  Health := FHealth + delta * HealthRegenerateRate;

  if FHealth > MaxHealth then Health :=  MaxHealth;
  end;

if FShields < MaxShields then
  begin
  Shields := FShields + delta * HealthRegenerateRate;

  if FShields > MaxShields then Shields :=  MaxShields;
  end;

if (Fhealth / MaxHealth) > 0.6 then
  begin
  ActorProxy.MasterObjects[0].LibTexture := UnitType.TextureNear[DL_LOW];
  ActorProxy.MasterObjects[1].LibTexture := UnitType.TextureMedium[DL_LOW];
  ActorProxy.MasterObjects[2].LibTexture := UnitType.TextureFar[DL_LOW];
  end
else if (Fhealth / MaxHealth) > 0.3 then
  begin
  ActorProxy.MasterObjects[0].LibTexture := UnitType.TextureNear[DL_MEDIUM];
  ActorProxy.MasterObjects[1].LibTexture := UnitType.TextureMedium[DL_MEDIUM];
  ActorProxy.MasterObjects[2].LibTexture := UnitType.TextureFar[DL_MEDIUM];
  end
else
  begin
  ActorProxy.MasterObjects[0].LibTexture := UnitType.TextureNear[DL_HIGH];
  ActorProxy.MasterObjects[1].LibTexture := UnitType.TextureMedium[DL_HIGH];
  ActorProxy.MasterObjects[2].LibTexture := UnitType.TextureFar[DL_HIGH];
  end
end;

function TUnit.GetEnemyEvadeDistance(CurrentEnemyRadius: single): single;
begin
Result :=  sqrt(sqr((UnitType.BoundingRadius + Movement.BigTurnRadius + CurrentEnemyRadius) * Player.Game.CollisionManager.ProximityMultiplier * Player.Game.CollisionManager.ProximityMultiplierExternal) - sqr(Movement.BigTurnRadius)) * UNIT_ENEMY_EVADE_DISTANCE_MULT;
end;

function TUnit.SafeDistanceBeforeAttack: single;
begin
Result := (Weapons[UnitType.PrimaryWeapon].WeaponGroup.WeaponSettings.WeaponRange + Movement.BigTurnRadius) * UNIT_ATTACK_MOVE_AWAY_MULTIPLYER;
end;

procedure TUnit.OnChangeCourse(const Sender: TStrangeCMMapObject);
begin
if (Command.Operation = OP_ATTACK) then
  begin
  //make sure it triggers UpdateAttackCourse(True)  next time path is updated
  Command.OutPathNode := nil;
  Command.AttackPathNode := Movement.GetLastNode;
  Command.EvadePathNode := Movement.Nodes[0];
  end;
end;

procedure TUnit.FollowAndDefend(TargetUnit: TUnit);
begin
if TargetUnit = Self then exit;
Command.Operation := OP_FOLLOW_AND_DEFEND;
Command.Targets.Clear;
ColObject.WaypointNodes.Count := (1);
ColObject.SmoothEvasion := True;
Movement.FollowObjectSettings.FollowTarget :=  TargetUnit.MovementCube;
Movement.FollowObjectSettings.DistanceToFollowObjectMin := (UnitType.BoundingRadius + TargetUnit.UnitType.BoundingRadius) * FOLLOW_AND_DEFEND_DISTANCE_MULT_MIN;
Movement.FollowObjectSettings.DistanceToFollowObjectMax := (UnitType.BoundingRadius + TargetUnit.UnitType.BoundingRadius) * FOLLOW_AND_DEFEND_DISTANCE_MULT_MAX;
DirectGL.Visible := True;
SecondsBeforeHidingDirectGL := SHOW_UNIT_MOVEMENT_LINES_FOR;
end;

procedure TUnit.OnUpdatePathToFollowedObject(const Sender: TStrangeMovement);
begin
ColObject.CalculateWaypoints(Sender.GetLastNode);
end;

function TUnit.GetFollowedUnitPosition: TVector3f;
begin
//Result := TUnit(Movement.FollowObjectSettings.FollowTarget.TagObject).MovementCube.AbsoluteAffinePosition;
end;

procedure TUnit.Proceed(delta, NewTime: single; TickCount: integer);
var
  j: integer;
  m1, m2: TMatrix;
begin
if FHealth > 0 then
  begin
  Think(delta, TickCount);

  //show command lines
  if SecondsBeforeHidingDirectGL <> 0 then
    begin
    SecondsBeforeHidingDirectGL := SecondsBeforeHidingDirectGL - delta;
    if SecondsBeforeHidingDirectGL < 0 then
      begin
      SecondsBeforeHidingDirectGL := 0;
      DirectGL.Visible := False;
      end;
    end;

  //syncronize weapon animation only if unit is alive
  if length(Weapons) <> 0 then
  for j := 0 to length(Weapons) - 1 do
  if (Weapons[j].WeaponGroup.WeaponType.ShowWeaponModel) and UnitType.SyncronizeWeaponAnimation then
    begin
    m1 := TorsoTags.GetTransform('tag_weapon',Tglactor(ActorProxy.MasterObjects[0].MasterObject).CurrentFrame);
    m2 := TorsoTags.GetTransform('tag_weapon',Tglactor(ActorProxy.MasterObjects[0].MasterObject).NextFrameIndex);
    Weapons[j].WeaponProxy.Matrix := MatrixLerp(m1, m2, Tglactor(ActorProxy.MasterObjects[0].MasterObject).CurrentFrameDelta);
    Weapons[j].WeaponProxy.Scale.Scale(Weapons[j].WeaponGroup.WeaponType.ModelScale);
    end;
  end
else
  begin
  //every 6th tick perform Unit explode operations...
  if (TickCount mod UNIT_EXPLOSIONS_FREQUENCY) = 1 then
    begin
    Explosion.Proceed(Explosions_delta);
    Explosions_delta := 0;
    end
  else
    Explosions_delta := Explosions_delta + delta;
  end;
end;

procedure TUnit.SwitchToAnimation(Animation: byte);
begin
if UnitType.UsesAnimation then
  SwitchToAnimation(TGLActor(ActorProxy.MasterObjects[0].MasterObject).Animations[Animation]);
end;

procedure TUnit.SwitchToAnimation(Animation: string);
begin
if UnitType.UsesAnimation then
  SwitchToAnimation(TGLActor(ActorProxy.MasterObjects[0].MasterObject).Animations.FindName(Animation));
end;

procedure TUnit.SwitchToAnimation(Animation: TActorAnimation);
var
  i: integer;
begin
if UnitType.UsesAnimation then
for i := 0 to 2 do
with TGLActor(ActorProxy.MasterObjects[i].MasterObject) do
  begin
  EndFrame := Animation.EndFrame;
  SwitchToAnimation(Animation, True);
  end;
end;

{ TMission }

constructor TMission.Create;
begin
Preview := TPicture.Create;
end;

procedure TMission.LoadMission;
begin
Game.CollisionManager.SetSectorNumber(Game.CurrentProfile.GeneralSettings.SectorSize,MapSize);
//creates its own Model Manager and Material Library
Space := TStrangeSpace.Create(Game.SceneObjects.FirstObject,Game.SceneObjects.Cadencer,Game.SceneObjects.TransparentObject, Game.MovementManager, nil,Game.SceneObjects.PartileRenderer, nil,Game.SceneObjects.MaterialScripter,'Maps\' + SpaceFileName);
Space.OnRandomRequest := Game.onSpaceRandom;
Space.OnError := Game.onSpaceError;
//Space.OnReadFromIni := onReadFromSpaceIni;
end;

destructor TMission.Destroy;
begin
  inherited;
Preview.Destroy;
Space.Free;
end;

procedure TMission.LoadFromIni(ini: TStrangeIniFile; Section: string);
var
i: byte;
begin
  inherited;

Preview.LoadFromFile(ExtractFilePath(ini.Filename) + 'preview.jpg');

SpaceFileName := ini.ReadString(str_GENERAL,'SpaceFileName','');
Size := ini.ReadString(str_GENERAL,'Size','');

MapSize[0] := ini.ReadFloat(str_GENERAL,str_MAP_SIZE + 'X',0);
MapSize[1] := ini.ReadFloat(str_GENERAL,str_MAP_SIZE + 'Y',0);
MapSize[2] := ini.ReadFloat(str_GENERAL,str_MAP_SIZE + 'Z',0);

MaxPlayers := ini.ReadInteger(str_GENERAL,'Max' + str_PlayerS,0);
MaxHumanPlayers := ini.ReadInteger(str_GENERAL,'MaxHuman' + str_PlayerS,0);

setlength(MapPlayerData,MaxPlayers);

for i := 1 to MaxPlayers do
  begin
  MapPlayerData[i-1].Location[0] := ini.ReadInteger(str_MAP_PLAYER_DATA + inttostr(i),str_LOCATION + 'X',0);
  MapPlayerData[i-1].Location[1] := ini.ReadInteger(str_MAP_PLAYER_DATA + inttostr(i),str_LOCATION + 'Y',0);
  MapPlayerData[i-1].Location[2] := ini.ReadInteger(str_MAP_PLAYER_DATA + inttostr(i),str_LOCATION + 'Z',0);
  MapPlayerData[i-1].Standard := ini.ReadBool(str_MAP_PLAYER_DATA + inttostr(i),'Standard',True);
  MapPlayerData[i-1].OnlyAI := ini.ReadBool(str_MAP_PLAYER_DATA + inttostr(i),'OnlyAI',True);
  end;

end;

procedure TMission.onReadFromSpaceIni(Sender: TStrangeSpace; const ini: TStrangeIniFile;
              SpaceObjectType, Section: String; SpaceObjectArrayIndex: integer);
begin
{ TODO 5 -oDa Stranger -cCritical : Fill this out!!!!!!!!!!!!!!!!!!!! }
end;

{ TLocalPlayerSettings }

procedure TLocalPlayerSettings.OnMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Player.Game.SceneObjects.Cadencer.CurrentTime = 0 then exit;

if Button = mbMiddle then
if PickList.UnitCount <> 0 then
  AttachCamera(PickList[0].ActorProxy);

orig_xx := x;
orig_yy := y;

ShiftState := Shift;

GetCursorPosition(abs_xx,abs_yy);

//don't remember, What it is for...
if not (ssRight in Shift) then
if (IntendedCommand = OP_MOVE) or (IntendedCommand = OP_MOVE_BY_WAYPOINTS) or
   (IntendedCommand = OP_PATROL) or (IntendedCommand = OP_MOVE_AND_ATTACK) then
  TargetDot := GetClickPosition2f(x, y, PickList.GetCenterDot[2]);

yy := y;
xx := x;

if (Button = mbLeft) and (not UI.MouseLookActive) and
  ((IntendedCommand = OP_STAND) or (IntendedCommand = OP_FOLLOW_AND_DEFEND)) then
  begin
  IntendedCommand := OP_DRAW_SELECTION_BOX;
  CursorVisible := True;
  CurrentHeight := 0;
  if IntendedCommand = OP_FOLLOW_AND_DEFEND then
    SetCursor(CUR_NORMAL);
  end;
end;

procedure TLocalPlayerSettings.OnMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
tmp2: TVector3f;
begin
if Player.Game.SceneObjects.Cadencer.CurrentTime = 0 then exit;
ShiftState := Shift;

if (ssLeft in Shift) and
   (IntendedCommand = OP_DRAW_SELECTION_BOX) then
  begin
  if not (ssRight in Shift) then
    SelectUnits(orig_xx,orig_yy,x,y)
  else
    IntendedCommand := OP_STAND;
  end;

xx := x;
yy := y;

MovedCameraWhileSendingUnits := (ssRight in Shift) and {Moved_A_Little and}
   ((IntendedCommand = OP_MOVE) or (IntendedCommand = OP_MOVE_BY_WAYPOINTS) or
   (IntendedCommand = OP_PATROL) or (IntendedCommand = OP_MOVE_AND_ATTACK));

if not MovedCameraWhileSendingUnits then
if not ((ssShift in Shift) or (ssLeft in Shift))  then
  begin
  if NeedToRestoreCursorPosition then
    begin
    if ((IntendedCommand = OP_MOVE_BY_WAYPOINTS) or (IntendedCommand = OP_PATROL)) and (PatrolDots.Count <> 0) then
      tmp2 := Viewer.Buffer.WorldToScreen(AffineVectorMake(TargetDot[0],TargetDot[1],PatrolDots.LastDot[2]))
    else
      tmp2 := Viewer.Buffer.WorldToScreen(AffineVectorMake(TargetDot[0],TargetDot[1],PickList.GetCenterDot[2]));

    SetRelativeCursorPosition(trunc(tmp2[0]), trunc(tmp2[1]));
    NeedToRestoreCursorPosition := False;
    end
  else
    begin
    if ((IntendedCommand = OP_MOVE_BY_WAYPOINTS) or (IntendedCommand = OP_PATROL)) and (PatrolDots.Count <> 0) then
      TargetDot := GetClickPosition2f(x, y, PatrolDots.LastDot[2])
    else
      TargetDot := GetClickPosition2f(x, y, PickList.GetCenterDot[2]);
    GetCursorPosition(abs_xx, abs_yy);
    //memorizzze the cursor position ONLY if it has changed!
    end;
  end;
end;

procedure TLocalPlayerSettings.MoveUnits;
var
tmp: TVector3f;
begin
tmp := AffineVectorMake(TargetDot[0],TargetDot[1],PickList.GetCenterDot[2] + CurrentHeight);
SetCursor(CUR_NORMAL);
if not Player.Game.CollisionManager.VectorIsInsideMap(tmp) then
  Player.Game.MakeError('Player wanted to send units outside the map, stupid bastard ;)', ERR_PLAYER_SENT_UNITS_OUTSIDE_MAP )
else
  begin
  if (Picklist.UnitCount <> 0) then
    Picklist.MassMove(tmp);
  IntendedCommand := OP_STAND;
  CursorVisible := True;
  end;
end;

procedure TLocalPlayerSettings.OnMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
i: integer;
tmp2: TVector3f;
begin
if Player.Game.SceneObjects.Cadencer.CurrentTime = 0 then exit;

if not ((Button = mbLeft) or (Button = mbRight)) then exit;

if NeedToRestoreCursorPosition then
  begin
  if ((IntendedCommand = OP_MOVE_BY_WAYPOINTS) or (IntendedCommand = OP_PATROL)) and (PatrolDots.Count <> 0) then
    tmp2 := Viewer.Buffer.WorldToScreen(AffineVectorMake(TargetDot[0],TargetDot[1],PatrolDots.LastDot[2]))
  else
    tmp2 := Viewer.Buffer.WorldToScreen(AffineVectorMake(TargetDot[0],TargetDot[1],PickList.GetCenterDot[2]));

  SetRelativeCursorPosition(round(tmp2[0]), round(tmp2[1]));
  end;


case IntendedCommand of

  OP_DRAW_SELECTION_BOX:
  if Button = mbLeft then
    begin
    IntendedCommand := OP_STAND;
    if (orig_xx=X) and (orig_yy=Y) then
      SelectUnits(orig_xx,orig_yy,x,y);
    end;

  OP_MOVE:
    begin
    if not (MovedCameraWhileSendingUnits or MovedLeftButton) then
      MoveUnits
    else
      begin
      MovedCameraWhileSendingUnits := False;
      UI.MouseLookActive := False;
      end;
    end;

  OP_MOVE_BY_WAYPOINTS, OP_PATROL:
    begin
    if not (MovedCameraWhileSendingUnits or MovedLeftButton) then
      begin
      if PatrolDots.Count = 0  then
        begin
        tmp2 := AffineVectorMake(TargetDot[0] ,TargetDot[1], PickList.GetCenterDot[2] + CurrentHeight);
        if not Player.Game.CollisionManager.VectorisInsideMap(tmp2) then
          Player.Game.MakeError(str_ERR_PLAYER_PLACED_WAYPOINT_OUTSIDE_MAP, ERR_PLAYER_PLACED_WAYPOINT_OUTSIDE_MAP)
        else
          begin
          PatrolDots.Add(tmp2);
          CurrentHeight := 0;
          end;
        end
      else
        begin
        tmp2 := AffineVectorMake(TargetDot[0], TargetDot[1], PatrolDots.LastDot[2] + CurrentHeight);
        if not Player.Game.CollisionManager.VectorisInsideMap(tmp2) then
          Player.Game.MakeError(str_ERR_PLAYER_PLACED_WAYPOINT_OUTSIDE_MAP , ERR_PLAYER_PLACED_WAYPOINT_OUTSIDE_MAP)
        else
          begin
          PatrolDots.Add(tmp2);
          CurrentHeight := 0;
          end;
        end;
      end
    else
      begin
      MovedCameraWhileSendingUnits := False;
      UI.MouseLookActive := False;
      end;
    end;

  OP_FOLLOW_AND_DEFEND:
    begin
    if UI.MouseLookActive then
      begin
      CursorVisible := True;
      UI.MouseLookActive := False;
      end
    else
      begin
      SetCursor(CUR_NORMAL);
      IntendedCommand := OP_STAND;
      if Button = mbRight then
      if Picklist.UnitCount <> 0 then
      if CurrentObject <> nil then
      if CurrentObject.Tag = GOT_UNIT then
      if not Player.EnemyWith(TUnit(CurrentObject.TagObject)) then
      for i := 0 to PickList.LastUnit do
        PickList[i].FollowAndDefend(TUnit(CurrentObject.TagObject));
      end;
    end;

  OP_STAND:
  if Button = mbRight then
    begin
    if UI.MouseLookActive then
      begin
      CursorVisible := True;
      UI.MouseLookActive := False;
      end
    else
    if Picklist.Count <> 0 then
      begin
      if CurrentObject <> nil then
        begin
        case CurrentObject.Tag of
          GOT_UNIT:  if Player.EnemyWith(TUnit(CurrentObject.TagObject)) then
                       for i := 0 to Picklist.LastUnit do
                         Picklist[i].Attack(TUnit(CurrentObject.TagObject))
                       else
                         Picklist.MassMove(CurrentObject.AbsoluteAffinePosition);
            end;
        IntendedCommand := OP_STAND;
        CursorVisible := True;
        end
      else
        PrepareToMove;
      end;
    end;
  end;

MovedLeftButton := False;
ShiftState := Shift;
xx := x;
yy := y;
end;

procedure TLocalPlayerSettings.OnDblClick(Sender: TObject);
begin
if CurrentObject <> nil then
  AttachCamera(CurrentObject);
end;

procedure TLocalPlayerSettings.OnMouseWheel(Sender: TObject; Shift: TShiftState;WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
if Camera.TargetObject <> nil then
  begin
  if WheelDelta < 0 then
    Camera.AdjustDistanceToTarget(1.1)
  else
    Camera.AdjustDistanceToTarget( 1 / 1.1);
  end
else
  begin
  if WheelDelta < 0 then
    Camera.Move(10)
  else
    Camera.Move(-10);
  end;
end;

function TLocalPlayerSettings.GetClickPosition(xx, yy: integer; height: single): TVector3f;
begin
if not Player.Game.CurrentProfile.GeneralSettings.fullscreenMode then
  begin
  if (Camera.CameraStyle = csInfinitePerspective) or (Camera.CameraStyle = csPerspective) then
    Result := XY(Camera.AbsoluteAffinePosition,Viewer.Buffer.PixelRayToWorld(xx, yy), height)
  else
    Result := XY(Camera.AbsoluteAffinePosition,Viewer.Buffer.OrthoScreenToWorld(xx, yy), height);
  end
else
  begin
  if (Camera.CameraStyle = csInfinitePerspective) or (Camera.CameraStyle = csPerspective) then
    Result := XY(Camera.AbsoluteAffinePosition,FullViewer.Buffer.PixelRayToWorld(xx, yy), height)
  else
    Result := XY(Camera.AbsoluteAffinePosition,FullViewer.Buffer.OrthoScreenToWorld(xx, yy), height);
  end
end;

procedure TLocalPlayerSettings.DrawSelectionBox;
var
glc: TGLCanvas;
begin

if not Player.Game.CurrentProfile.GeneralSettings.fullscreenMode then
  glc := TGLCanvas.Create(Viewer.Width,Viewer.Height)
else
  glc := TGLCanvas.Create(FullViewer.Width,FullViewer.Height);

glc.PenWidth := 2;
glc.PenColor := RGB(250,255,20);
glc.FrameRect(xx,yy,orig_xx,orig_yy);
glc.PenAlpha := 0.1;
glc.PenColor := RGB(60,90,220);
glc.FillRect(xx,yy,orig_xx,orig_yy);
glc.Destroy;

{glDisable(GL_LIGHTING);
glLineWidth(2);
glColor3f(250,255,20);
glBegin(GL_QUADS);
  glVertex2i(xx, yy);
  glVertex2i(orig_xx, yy);
  glVertex2i(orig_xx, orig_yy);
  glVertex2i(xx, orig_yy);
glend;
glDisable(GL_LIGHTING);}
end;

procedure TLocalPlayerSettings.DrawMoveToLine(var rci : TRenderContextInfo);
var
CenterDot: TVector3f;
Target: TVector3f;
begin
if PickList.Count = 0 then exit;
CenterDot := PickList.GetCenterDot;
glDisable(GL_LIGHTING);
Target := AffineVectorMake(TargetDot[0], TargetDot[1], CurrentHeight + CenterDot[2]);
if not Player.Game.CollisionManager.VectorIsInsideMap(Target) then
  glColor3b(127,0,0)
else
  glColor3b(127,127,0);
glbegin(GL_LINE_LOOP);
  glVertex3f(CenterDot[0], CenterDot[1], CenterDot[2]);
  glVertex3f(TargetDot[0], TargetDot[1], CurrentHeight + CenterDot[2]);
  glVertex3f(TargetDot[0], TargetDot[1], CenterDot[2]);
glend;
glEnable(GL_LIGHTING);

Disk.Position.AsAffineVector :=  CenterDot;
Disk.OuterRadius := VectorDistance(CenterDot,AffineVectorMake(TargetDot[0],TargetDot[1],CenterDot[2]));
Disk.Render(rci);

TransparentDisk.Position.AsAffineVector :=  CenterDot;
TransparentDisk.OuterRadius := Disk.OuterRadius;
TransparentDisk.Render(rci);
end;


procedure TLocalPlayerSettings.DrawMoveByWaypointsLine(var rci: TRenderContextInfo);
var
CenterDot, Target: TVector3f;
i: integer;
begin
if PickList.Count=0 then exit;
CenterDot := PickList.GetCenterDot;

glDisable(GL_LIGHTING);
glColor3b(100,43,83);
glbegin(GL_LINE_STRIP);
  glVertex3f(CenterDot[0], CenterDot[1], CenterDot[2]);

if PatrolDots.Count <> 0 then
  begin
  for i := 0 to PatrolDots.Last do
    glVertex3f(PatrolDots.List[i][0], PatrolDots.List[i][1], PatrolDots.List[i][2]);

  Target := AffineVectorMake(TargetDot[0], TargetDot[1], CurrentHeight + PatrolDots.LastDot[2]);
  if not Player.Game.CollisionManager.VectorIsInsideMap(Target) then
    glColor3b(127,0,0);

  glVertex3f(TargetDot[0], TargetDot[1], PatrolDots.LastDot[2]);
  glVertex3f(TargetDot[0], TargetDot[1], CurrentHeight + PatrolDots.LastDot[2]);
  glVertex3f(PatrolDots.LastDot[0], PatrolDots.LastDot[1], PatrolDots.LastDot[2]);
  end
else
  begin
  Target := AffineVectorMake(TargetDot[0], TargetDot[1], CurrentHeight + CenterDot[2]);
  if not Player.Game.CollisionManager.VectorIsInsideMap(Target) then
    glColor3b(127,0,0);

  glVertex3f(TargetDot[0], TargetDot[1], CenterDot[2]);
  glVertex3f(TargetDot[0], TargetDot[1], CurrentHeight + CenterDot[2]);
  glVertex3f(CenterDot[0], CenterDot[1], CenterDot[2]);
  end;

glend;
glEnable(GL_LIGHTING);

if PatrolDots.Count = 0 then
  begin
  Disk.Position.AsAffineVector :=  CenterDot;
  Disk.OuterRadius := VectorDistance(CenterDot, AffineVectorMake(TargetDot[0], TargetDot[1],CenterDot[2]));
  end
else
  begin
  Disk.Position.AsAffineVector :=  PatrolDots.LastDot;
  Disk.OuterRadius := VectorDistance(PatrolDots.LastDot,AffineVectorMake(TargetDot[0],TargetDot[1],PatrolDots.LastDot[2]));
  end;

Disk.Render(rci);

TransparentDisk.Position.AsAffineVector :=  Disk.Position.AsAffineVector;
TransparentDisk.OuterRadius := Disk.OuterRadius;
TransparentDisk.Render(rci);
end;

procedure TLocalPlayerSettings.OnDirectGLRender (Sender : TObject; var rci : TRenderContextInfo);
begin
case IntendedCommand of
  OP_DRAW_SELECTION_BOX: DrawSelectionBox;
  OP_MOVE: DrawMoveToLine(rci);
  OP_MOVE_BY_WAYPOINTS,
  OP_PATROL: DrawMoveByWaypointsLine(rci);
  end;
end;

procedure TLocalPlayerSettings.SetCurrentObject(x, y: integer);
var
RectX: TRect;
List: TGLPickList;
i: byte;
begin
if IntendedCommand <> OP_FOLLOW_AND_DEFEND then
  SetCursor(CUR_NORMAL, False)
else
  SetCursor(CUR_FOLLOW_AND_DEFEND, False);

CursorHint.Visible := False;
CursorHint2.Visible := False;

//deal with the last object
if CurrentObject <> nil then
if (CurrentObject.Tag = GOT_UNIT) then
  TUnit(CurrentObject.TagObject).DirectGL.Visible := TUnit(CurrentObject.TagObject).SecondsBeforeHidingDirectGL <> 0;

//select a new object
CurrentObject := nil;
rectx := Rect(x + CURSOR_SELECTION_RADIUS,y + CURSOR_SELECTION_RADIUS,x - CURSOR_SELECTION_RADIUS,y - CURSOR_SELECTION_RADIUS);
if not Player.Game.CurrentProfile.GeneralSettings.FullScreenMode then
  list := Viewer.Buffer.GetPickedObjects(rectx)
else
  list := FullViewer.Buffer.GetPickedObjects(rectx);

if  (list.Count <> 0) then
for i := 0 to list.Count - 1 do
  begin
  if (list[i].Tag = GOT_UNIT) then
  if  (TUnit(list[i].TagObject).FHealth > 0) then  //that have positive health
    begin
    CurrentObject := list[i];

    //show movement line
    TUnit(CurrentObject.TagObject).DirectGL.Visible := True;

    //show hints
    CursorHint.Visible := True;
    CursorHint.Position.X := X;
    CursorHint.Position.Y := Y + 15;
    CursorHint.Text := TUnit(list[i].TagObject).UnitType.RealName;

    CursorHint2.Visible := True;
    CursorHint2.Position.X := X;
    CursorHint2.Position.Y := Y + 27;
    CursorHint2.Text := TUnit(list[i].TagObject).Player.Nick;
    if Player.EnemyWith(TUnit(list[i].TagObject)) then
      CursorHint2.ModulateColor.AsWinColor := clRed
    else
    if Player.AllyWith(TUnit(list[i].TagObject)) then
      CursorHint2.ModulateColor.AsWinColor := clBlue
    else
      CursorHint2.ModulateColor.AsWinColor := CURSOR_NORMAL_COLOR;

    //change cursor
    if Player.EnemyWith(TUnit(list[i].TagObject)) and (PickList.Count <> 0) then
      SetCursor(CUR_ATTACK, False);
    break;
    end;
  if (list[i].Tag = GOT_PLANET) then
    begin
    //
    break;
    end;
  end;

UpdateCursor;
List.Destroy;
end;

procedure TLocalPlayerSettings.SelectUnits(x, y: integer; prev_x ,prev_y: integer);
var
RectX: TRect;
List : TGLPickList;
i: byte;
begin
if x = prev_x then inc(x);
if y = prev_y then inc(y);
RectX := Rect(x, y, prev_x, prev_y);

if not Player.Game.CurrentProfile.GeneralSettings.FullScreenMode then
  list := Viewer.Buffer.GetPickedObjects(RectX)
else
  list := FullViewer.Buffer.GetPickedObjects(RectX);

PickList.clear;

if  (list.Count <> 0) then
for i := 0 to list.Count - 1 do
  begin
  if (list[i].Tag = GOT_UNIT) then
     if (TUnit(list[i].TagObject).Player = Player) and //select only Units that belong to this Player, ignore other Players' Units
         (TUnit(list[i].TagObject).FHealth > 0) then  //that have positive health
    PickList.AddUnit(list[i].TagObject);
  end;
if Assigned(PickList.onUpdate) then PickList.onUpdate(Self);

if not Player.Game.CurrentProfile.GeneralSettings.SplitScreenMode then

List.Destroy;
end;

constructor TLocalPlayerSettings.Create(Game: TGame);
begin
Camera := TglCamera(Game.SceneObjects.Scene.Objects.AddNewChild(TglCamera));
Camera.DepthOfView := 800;
Camera.FocalLength := 50;
Camera.SceneScale := 1;
Camera.NearPlaneBias := 1;
Camera.Direction.SetVector(0,1,0);
Camera.Up.SetVector(0,0,1);
Camera.Position.AsAffineVector := AffineVectorMake(10,20,10);

Navigator := TGLSmoothNavigator.Create(nil);
Navigator.MoveUpWhenMovingForward := False; //togle to turn on free flight mode
Navigator.InvertHorizontalSteeringWhenUpsideDown := False; //togle to turn on free flight mode
Navigator.MovingObject := Camera;
Navigator.UseVirtualUp := True;
Navigator.VirtualUp.AsAffineVector := AffineVectorMake(0,0,1);
Navigator.AngleLock := False;

UI:=TGLSmoothUserInterface.Create(nil);
UI.GLNavigator := Navigator;

DirectGL := TGLDirectOpenGL(Game.SceneObjects.LastObject.AddNewChild(TGLDirectOpenGL));
Directgl.OnRender := OnDirectGLRender;

Disk := TGLDisk(Game.SceneObjects.LastObject.AddNewChild(TGLDisk));
Disk.Material.FrontProperties.Ambient.SetColor (0.1, 0.3, 0.8, 0.7);
Disk.Material.FrontProperties.Diffuse.SetColor (0, 0, 0, 0.5);
Disk.Material.FrontProperties.PolygonMode := pmLines;
Disk.Material.BackProperties.Ambient.SetColor (0.1, 0.3, 0.8, 0.7);

Disk.Material.BackProperties.Diffuse.SetColor (0, 0, 0, 0.5);
Disk.Material.BackProperties.PolygonMode := pmLines;
Disk.Material.BlendingMode := bmTransparency;
Disk.Material.FaceCulling := fcNoCull;
Disk.Slices := 8;
Disk.Visible := False;

TransparentDisk := TGLDisk(Game.SceneObjects.TransparentObject.AddNewChild(TGLDisk));
TransparentDisk.Material.FrontProperties.Ambient.SetColor (0.1, 0.3, 0.4, 0.7);
TransparentDisk.Material.FrontProperties.Diffuse.SetColor (0, 0, 0, 0.5);
TransparentDisk.Material.BackProperties.Ambient.SetColor (0.1, 0.3, 0.4, 0.7);
TransparentDisk.Material.BackProperties.Diffuse.SetColor (0, 0, 0, 0.5);
TransparentDisk.Material.BlendingMode := bmTransparency;
TransparentDisk.Material.FaceCulling := fcNoCull;
TransparentDisk.Slices := 360;
TransparentDisk.Visible := False;

Picklist := TSimpleUnitList.Create;
PatrolDots:= TStrangeVector3fList.Create;

FPSCounter := TglHUDText(Game.SceneObjects.LastObject.AddNewChild(TglHUDText));
FPSCounter.BitmapFont := Game.SceneObjects.SystemFont;

CursorHint := TglHUDText(Game.SceneObjects.LastObject.AddNewChild(TglHUDText));
CursorHint.BitmapFont := Game.SceneObjects.HintFont;
CursorHint.ModulateColor.AsWinColor := CURSOR_NORMAL_COLOR;

CursorHint2 := TglHUDText(Game.SceneObjects.LastObject.AddNewChild(TglHUDText));
CursorHint2.BitmapFont := Game.SceneObjects.HintFont;
CursorHint2.ModulateColor.AsWinColor := CURSOR_NORMAL_COLOR;

ControlType := CT_MOUSE;
//Gui := TGLBaseSceneObject(Game.SceneObjects.TransparentObject.AddNewChild(TGLBaseSceneObject));

FCursorVisible := True;
end;

destructor TLocalPlayerSettings.Destroy;
begin
Console.Destroy;
//Gui.Destroy;
FPSCounter.Destroy;
CursorHint.Destroy;
CursorHint2.Destroy;

PatrolDots.Destroy;
Picklist.Destroy;

Disk.Destroy;
TransparentDisk.Destroy;

if not Player.Game.CurrentProfile.GeneralSettings.FullScreenMode then
  begin
  Viewer.Camera := nil;
  Viewer.Destroy;
  end
else
  begin
  FullViewer.Camera := nil;
  FullViewer.Destroy;
  end;
UI.Destroy;
Navigator.Destroy;
MouseEmulator.Free;
DirectGL.Destroy;
Camera.Destroy;
end;

procedure TLocalPlayerSettings.onBeforeRender(Sender: TObject);
var
i: integer;
begin
FPSCounter.Visible := True;
Directgl.Visible := True;

if PickList.UnitCount <> 0 then
for i := 0 to PickList.LastUnit do
  picklist[i].GuiVisible := True;

with Player.Game.RighTPlayer.LocalSettings do
  begin
  fpscounter.Visible := False;
  directgl.Visible := False;
  if PickList.UnitCount <> 0 then
  for i := 0 to PickList.LastUnit do
    picklist[i].GuiVisible := False;
  end;

if Player.Game.KeyboardPlayer <> nil then
with Player.Game.KeyboardPlayer.LocalSettings do
  begin
  if Player.Game.KeyboardPlayer=Player.Game.RighTPlayer then
    MouseEmulator.Visible := False
  else
    MouseEmulator.Visible := FCursorVisible;
  end;

end;

procedure TLocalPlayerSettings.onPostRender(Sender: TObject);
var
i: integer;
begin
Fpscounter.Visible := False;
Directgl.Visible := False;

if PickList.UnitCount <> 0 then
for i := 0 to PickList.LastUnit do
  PickList[i].GuiVisible := False;

with Player.Game.RighTPlayer.LocalSettings do
  begin
  fpscounter.Visible := True;
  directgl.Visible := True;
  if PickList.UnitCount <> 0 then
  for i := 0 to PickList.LastUnit do
    PickList[i].GuiVisible  :=  True;
  end;

if Player.Game.KeyboardPlayer <> nil then
with Player.Game.KeyboardPlayer.LocalSettings do
  begin
  if Player.Game.KeyboardPlayer=Player.Game.RighTPlayer then
    MouseEmulator.Visible := FCursorVisible
  else
    MouseEmulator.Visible := False;
  end;

end;

procedure TLocalPlayerSettings.AttachCamera(Target: TGLBaseSceneObject);
var
tmp : TVector;
begin
tmp := Camera.AbsolutePosition;
Camera.MoveTo(T