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;
Cam