C2AppHelper.pas
上传用户:yj_qiu
上传日期:2022-08-08
资源大小:23636k
文件大小:28k
源码类别:

游戏引擎

开发平台:

Delphi

  1. (*
  2.  @Abstract(CAST II Engine applications helper unit)
  3.  (C) 2006-2007 George "Mirage" Bakhtadze. <a href="http://www.casteng.com">www.casteng.com</a> <br>
  4.  The source code may be used under either MPL 1.1 or LGPL 2.1 license. See included license.txt file <br>
  5.  Unit contains a class which performs some usual tasks like menu system etc
  6. *)
  7. {$Include GDefines.inc}
  8. {$Include C2Defines.inc}
  9. unit C2AppHelper;
  10. interface
  11. uses
  12.   SysUtils,
  13.   TextFile,
  14.   AppsInit, AppHelper,  
  15.   BaseTypes, Basics, BaseMsg, Props,
  16.   BaseClasses, Resources, BaseGraph,
  17.   C2Render,
  18.   CAST2, C2Core, C2Visual, C2VisItems, C2Materials, C22D, C2Particle, C2Affectors,
  19.   {$IFDEF DIRECT3D8} C2DX8Render, {$ENDIF} {$IFDEF OpenGL} COGLRender, {$ENDIF}
  20.   {$IFDEF Audio} C2Sound, {$ENDIF}
  21.   {$IFDEF USE_DI} DInput {$ELSE} WInput {$ENDIF},
  22.   {$IFDEF NETSUPPORT} C2Net, {$ENDIF}
  23.   C2Res;
  24. const
  25.   // User profile file extension
  26.   ProfileFileExtension = '.pfl';
  27.   // Key binding prefix on config
  28.   KeyBindPrefix = 'Key_';
  29.   // AWSD camera control modes
  30. //  ccNone = 0; ccMove = 1; ccRotate = 2; ccMoveZoom = 3; ccXMoveZoom = 4;
  31. type
  32.   // Camera modes
  33.   TCameraMode = (// Move camera with mouse
  34.                  cmMove,
  35.                  // Rotate camera with mouse
  36.                  cmRotate,
  37.                  // zoom camera with mouse
  38.                  cmZoom);
  39.   // This message is generated when full screen mode switched on or off
  40.   TFullScreenToggleMsg = class(BaseMSg.TSystemMessage)
  41.   end;
  42.   // This message is generated when main menu switched on or off
  43.   TMenuToggleMsg = class(BaseMSg.TSystemMessage)
  44.   end;
  45.   // This message is generated when help screen switched on or off
  46.   THelpToggleMsg = class(BaseMSg.TSystemMessage)
  47.   end;
  48.   { @Abstract(Base class for applications which uses CAST II engine)
  49.   }
  50.   TCast2App = class(TApp)
  51.   private
  52.     function MatchVideoMode(const VM: TVideoMode; const Str: string): Boolean;
  53.     procedure ActionActivateCallback(BindData: Integer; CustomData: SmallInt);
  54.     procedure ActionDeactivateCallback(BindData: Integer; CustomData: SmallInt);
  55.   protected
  56. //    procedure StartCameraMode(Mode: TCameraMode); virtual;
  57. //    procedure EndCameraMode(Mode: TCameraMode); virtual;
  58.     // Binds default controls for some standard actions
  59.     procedure BindStandardControls; virtual;
  60.     // Applies controls
  61.     procedure ApplyControls; virtual;
  62.     // Enumerate video modes
  63.     procedure EnumModes;
  64.     // Enumerate video modes and users
  65.     procedure EnumAll;
  66.     // Returns profile file name by a user name
  67.     function UserNameToFileName(const UserName: string): string;
  68.   public
  69.     // CAST II core reference
  70.     Core: C2Core.TCore;
  71.     {$IFDEF AUDIO}
  72.     // Audio manager reference
  73.     Audio: TAudioManager;
  74.     {$ENDIF}
  75.     {$IFDEF NETSUPPORT}
  76.     // Network manager reference
  77.     Net: TNet;
  78.     // Host name for game server
  79.     GameHostName: string;
  80.     {$ENDIF}
  81.     constructor Create(const AProgramName: string; AStarter: TAppStarter); override;
  82.     destructor Destroy; override;
  83.     // Creates engine core of the specified class and registers standard item classes
  84.     procedure CreateCore(CoreClass: C2Core.CCore); virtual;
  85.     // Loads a scene from the specified file and returns <b>True</b> if success
  86.     function LoadScene(const FileName: string): Boolean;
  87.     { Binds an action or message to the specified in <b>AActivateBinding</b> input event or a sequence of events.
  88.       If <b>Msg</b> is not <b>nil</b> the message will be generated when the specified in <b>AActivateBinding</b> set of input events will occur.
  89.       Otherwise an action named <b>AName</b> will be activated when input will match <b>AActivateBinding</b> and deactivated when input will match <b>ADeactivateBinding</b>. }
  90.     procedure BindAction(Msg: CMessage; const AName, AActivateBinding, ADeactivateBinding: string);
  91.     // Delete the specified action
  92.     procedure DeleteAction(const AName: string);
  93.     // Logs on a user with the specified name and loads its profile
  94.     procedure LogOn(UserName: string);
  95.     // Saves current user's profile and logs off the user
  96.     procedure LogOff;
  97.     // Opens the specified URL in system default browser. If application is operating in full screen mode it's minimized.
  98.     procedure GotoURL(const URLFileName: string);
  99.     { Applies the specified option set. An option set is a grouped by category set of options.
  100.       For example "VIDEOOPTIONS" set includes video mode options, gamma control options and so on. }
  101.     procedure ApplyOptionSet(const OptionSet: string); virtual;
  102.     // Applies all option sets
  103.     procedure ApplyOptions; virtual;
  104.     // Applies video option set ("VIDEOOPTIONS")
  105.     function ApplyVideoOptions: Boolean; virtual;
  106.     // Temporarily applies the specified value to the specified option for preview purposes
  107.     procedure PreviewOptions(OptionName, Value: string); virtual;
  108.     // Fills <b>X</b> and <b>Y</b> with X and Y of current viewport center
  109.     procedure ObtainViewportCenter(out X, Y: Integer); virtual;
  110.     // Messages handler. Handles full screen toggling, forced quit, etc
  111.     procedure HandleMessage(const Msg: TMessage); virtual;
  112.     // Performs OS message processing (calls <b>Starter.Process</b>) and engine core processing. Should be called in main application cycle.
  113.     procedure Process; virtual;
  114.   end;
  115. implementation
  116. uses Base3D, OSUtils;
  117. const
  118.   StrNoDX = 'DirectX 8 or greater is not installed!'#13#10'You can download it at http://www.microsoft.com/directx';
  119. //  StrUIError = 'Initialization error. File corrupt or version mismatch.'#13#10'Reinstalling the application should fix this problem.';
  120. //  ZeroGUID: TGUID = '{00000000-0000-0000-0000-000000000000}';
  121. { TCast2App }
  122. function TCast2App.MatchVideoMode(const VM: TVideoMode; const Str: string): Boolean;
  123. begin
  124.   Result := (SysUtils.Format('%Dx%Dx%D', [VM.Width, VM.Height, GetBitsPerPixel(VM.Format)-1]) = Str) or          // For 15 and 16 bit modes compatibility
  125.             (SysUtils.Format('%Dx%Dx%D', [VM.Width, VM.Height, GetBitsPerPixel(VM.Format)+1]) = Str) or
  126.             (SysUtils.Format('%Dx%Dx%D', [VM.Width, VM.Height, GetBitsPerPixel(VM.Format)])   = Str);
  127. end;
  128. procedure TCast2App.ActionActivateCallback(BindData: Integer; CustomData: SmallInt);
  129. begin
  130.   FActions[CustomData].Active := True;
  131. end;
  132. procedure TCast2App.ActionDeactivateCallback(BindData: Integer; CustomData: SmallInt);
  133. begin
  134.   FActions[CustomData].Active := False;
  135. end;
  136. procedure TCast2App.EnumModes;
  137. var CurVM, i: Integer; s: string;
  138. begin
  139.   if (Core = nil) or (Core.Renderer = nil) then Exit;
  140.   CurVM := 0;
  141.   s := '';
  142.   for i := 0 to Core.Renderer.TotalVideoModes-1 do with Core.Renderer.VideoMode[i] do begin
  143.     if i > 0 then s := s + StringDelimiter;
  144.     s := s + SysUtils.Format('%Dx%Dx%D', [Width, Height, GetBitsPerPixel(Format)]);
  145.     if MatchVideoMode(Core.Renderer.VideoMode[i], Config['VideoMode']) then CurVM := i;
  146.   end;
  147.   Config.AddEnumerated('VideoMode', [], CurVM, s);
  148. end;
  149. procedure TCast2App.EnumAll;
  150.   procedure EnumUsers;
  151.   var i, CurUser: Integer; s: string; SR: TSearchRec;
  152.     procedure AddUser(const AName: string);
  153.     begin
  154.       if AName = '' then Exit;
  155.       if s <> '' then s := s + StringDelimiter;
  156.       s := s + AName;
  157.       if AName = Config['UserName'] then CurUser := i;
  158.       Inc(i);
  159.     end;
  160.   begin
  161.     s := ''; i := 0; CurUser := -1;
  162.     if FindFirst(Starter.ProgramWorkDir + '*' + ProfileFileExtension, faReadOnly or faHidden or faSysFile or faArchive, SR) = 0 then begin
  163.       repeat AddUser(GetFileName(SR.Name)); until FindNext(SR) <> 0;
  164.       SysUtils.FindClose(SR);
  165.     end;
  166.     if CurUser = -1 then AddUser(Config['UserName']);
  167.     Config.AddEnumerated('UserName', [], CurUser, s);
  168.   end;
  169. begin
  170.   EnumUsers;
  171.   EnumModes;
  172. end;
  173. function TCast2App.UserNameToFileName(const UserName: string): string;
  174. begin
  175.   Result := Starter.ProgramWorkDir + UserName + ProfileFileExtension;
  176. end;
  177. (*procedure TCast2App.StartCameraMode(Mode: TCameraMode);
  178. var CenterX, CenterY: Integer;
  179. begin
  180.   case Mode of
  181.     cmMove: if not MoveMode then MoveMode := True else Exit;
  182.     cmRotate: if not RotateMode then RotateMode := True else Exit;
  183.     cmZoom: if not ZoomMode then ZoomMode := True else Exit;
  184.   end;
  185.   {$IFDEF DEBUGMODE}
  186.   Log.Log('** Anchor set by StartCameraMode');
  187.   {$ENDIF}
  188.   ObtainViewportCenter(CenterX, CenterY);
  189.   Core.Input.SetMouseAnchor(CenterX, CenterY);
  190. end;
  191. procedure TCast2App.EndCameraMode(Mode: TCameraMode);
  192. begin
  193.   case Mode of
  194.     cmMove: if MoveMode then MoveMode := False else Exit;
  195.     cmRotate: if RotateMode then RotateMode := False else Exit;
  196.     cmZoom: if ZoomMode then ZoomMode := False else Exit;
  197.   end;
  198.   {$IFDEF DEBUGMODE}
  199.   Log.Log('** Anchor reset by EndCameraMode');
  200.   {$ENDIF}
  201.   Core.Input.SetMouseAnchor(-1, -1);
  202. end; *)
  203. procedure TCast2App.BindStandardControls;
  204. var i: Integer;
  205. begin
  206.   BindAction(TForceQuitMsg,  'Quit', 'Alt+Q', '');
  207.   BindAction(TMenuToggleMsg, 'Menu', 'SPACE', '');
  208.   BindAction(THelpToggleMsg, 'Help', 'F1', '');
  209.   BindAction(TFullScreenToggleMsg, 'FullScreenToggle', 'Alt+Enter', '');
  210.   BindAction(nil, 'Forward',  'W+', 'W-');
  211.   BindAction(nil, 'Backward', 'S+', 'S-');
  212.   BindAction(nil, 'Left',     'A+', 'A-');
  213.   BindAction(nil, 'Right',    'D+', 'D-');
  214.                          
  215.   for i := 0 to High(FActions) do begin
  216.     if FActions[i].Message <> nil then begin
  217.       if Config[KeyBindPrefix + FActions[i].Name] = '' then
  218.         Config.Add(KeyBindPrefix + FActions[i].Name, vtString, [], FActions[i].ActivateBinding, '', '');
  219.     end else if Config[KeyBindPrefix + FActions[i].Name + OnOffStr[True]] = '' then begin
  220.       Config.Add(KeyBindPrefix + FActions[i].Name + OnOffStr[True],  vtString, [], FActions[i].ActivateBinding,  '', '');
  221.       Config.Add(KeyBindPrefix + FActions[i].Name + OnOffStr[False], vtString, [], FActions[i].DeactivateBinding, '', '');
  222.     end;
  223.   end;
  224.   ApplyControls;
  225. end;
  226. procedure TCast2App.ApplyControls;
  227. var i: Integer;
  228. begin
  229.   if not Assigned(Core) or not Assigned(Core.Input) then begin
  230.     {$IFDEF LOGGING} Log.Log(ClassName + '.ApplyControls: Core or Core.Input is not assigned', lkError); {$ENDIF}
  231.     Exit;
  232.   end;
  233.   Core.Input.UnBindAll;
  234.   for i := 0 to High(FActions) do begin
  235.     if Config[KeyBindPrefix + FActions[i].Name] <> '' then
  236.       BindAction(FActions[i].Message, FActions[i].Name, Config[KeyBindPrefix + FActions[i].Name], '');
  237.     if Config[KeyBindPrefix + FActions[i].Name + OnOffStr[True]] <> '' then
  238.       BindAction(FActions[i].Message, FActions[i].Name, Config[KeyBindPrefix + FActions[i].Name + OnOffStr[True]],
  239.                                                         Config[KeyBindPrefix + FActions[i].Name + OnOffStr[False]]);
  240.   end;
  241. (*
  242. {$IFDEF SCREENSAVER}
  243.     BindCommand(NewBinding(btKeyDown, IK_UP), cmdWDown);    BindPointer(NewBinding(btKeyUp, IK_UP),    atBooleanOff, @WPressed);
  244.     BindCommand(NewBinding(btKeyDown, IK_DOWN), cmdSDown);  BindPointer(NewBinding(btKeyUp, IK_DOWN),  atBooleanOff, @SPressed);
  245.     BindCommand(NewBinding(btKeyDown, IK_LEFT), cmdADown);  BindPointer(NewBinding(btKeyUp, IK_LEFT),  atBooleanOff, @APressed);
  246.     BindCommand(NewBinding(btKeyDown, IK_RIGHT), cmdDDown); BindPointer(NewBinding(btKeyUp, IK_RIGHT), atBooleanOff, @DPressed);
  247. {$ELSE}
  248. {$IFNDEF ONLINEBUILD}
  249.     BindCommand(NewBinding(btKeyClick, IK_GRAVE), cmdMinimize);
  250.     BindPointer(NewBinding(btKeyDown, IK_Q, NewBinding(btKeyDown, IK_U, NewBinding(btKeyDown, IK_I, NewBinding(btKeyDown, IK_T)))), atBooleanOn, @Starter.Finished, 0, 1000);
  251.     BindPointer(NewBinding(btKeyDown, IK_LALT, NewBinding(btKeyDown, IK_Q)), atBooleanOn, @Starter.Finished, 0, 0, NewBinding(btKeyUp, IK_LALT));
  252.     BindCommand(NewBinding(btKeyDown, IK_LALT, NewBinding(btKeyClick, IK_RETURN)), cmdToggleFullscreen, 0, NewBinding(btKeyUp, IK_LALT));
  253.     BindCommand(NewBinding(btKeyDown, IK_MOUSELEFT), cmdMZoomDown); Input.BindCommand(NewBinding(btKeyUp, IK_MOUSELEFT), cmdZoomUp);
  254.     BindCommand(NewBinding(btKeyDown, IK_MOUSERIGHT), cmdMRotateDown); Input.BindCommand(NewBinding(btKeyUp, IK_MOUSERIGHT), cmdRotateUp);
  255. {$ENDIF}
  256.     BindCommand(NewBinding(btKeyDown, IK_LALT), cmdRotateDown); Input.BindCommand(NewBinding(btKeyUp, IK_LALT), cmdRotateUp);
  257.     BindCommand(NewBinding(btKeyDown, IK_LSHIFT), cmdZoomDown); Input.BindCommand(NewBinding(btKeyUp, IK_LSHIFT), cmdZoomUp);
  258.     BindCommand(NewBinding(btKeyDown, IK_MOUSEMIDDLE), cmdMoveDown); Input.BindCommand(NewBinding(btKeyUp, IK_MOUSEMIDDLE), cmdMoveUp);
  259.     BindCommand(NewBinding(btKeyDown, IK_CONTROL), cmdMoveDown); Input.BindCommand(NewBinding(btKeyUp, IK_CONTROL), cmdMoveUp);
  260.     BindCommand(NewBinding(btKeyDown, IK_LCONTROL), cmdMoveDown); Input.BindCommand(NewBinding(btKeyUp, IK_LCONTROL), cmdMoveUp);
  261.     BindCommand(NewBinding(btMouseMove, 0), cmdMouseMove);
  262.     BindCommand(NewBinding(btMouseRoll, 0), cmdMouseRoll);
  263. {$ENDIF}
  264. {$IFDEF DEBUGMODE}
  265.     BindPointer(NewBinding(btKeyClick, IK_B), atBooleanToggle, @Core.DebugOut);
  266. //    BindPointer(NewBinding(btKeyClick, IK_8), atSetLongWord, @World.Renderer.FillMode, fmSolid);
  267. //    BindPointer(NewBinding(btKeyClick, IK_9), atSetLongWord, @World.Renderer.FillMode, fmWire);
  268. //    BindPointer(NewBinding(btKeyClick, IK_0), atSetLongWord, @World.Renderer.FillMode, fmPoint);
  269.     BindCommand(NewBinding(btKeyClick, IK_F5), cmdSpeedLo);
  270.     BindCommand(NewBinding(btKeyClick, IK_F6), cmdSpeedNormal);
  271.     BindCommand(NewBinding(btKeyClick, IK_F7), cmdSpeedHi);
  272. {$ENDIF}
  273. //    BindCommand(NewBinding(btKeyDown, IK_MOUSEMIDDLE), cmdMinimize);*)
  274. //  MouseInGUI := False;
  275.   Core.Input.MouseCapture := not Core.Input.SystemCursor;
  276. end;
  277. constructor TCast2App.Create(const AProgramName: string; AStarter: TAppStarter);
  278.   {$IFDEF DIRECT3D8}  
  279.   procedure SetDeviceType(const AName: string);
  280.   var i: Integer; DTStrs: TStringArray;
  281.   begin
  282.     for i := 0 to Split(DeviceTypesEnum, StringDelimiter, DTStrs, False)-1 do
  283.       if AName = DTStrs[i] then (Core.Renderer as TDX8Renderer).SetDeviceType(i);
  284.     DTStrs := nil;
  285.     EnumModes;
  286.   end;
  287.   {$ENDIF}
  288. function SetAdapter(const AName: string): Boolean;
  289. var i: Integer;
  290. begin
  291.   Result := True;
  292.   if AName = '' then Exit;
  293.   for i := 0 to Core.Renderer.TotalAdapters-1 do
  294.     if AName = Core.Renderer.AdapterName[i] then begin
  295.       Core.Renderer.SetVideoAdapter(i);
  296.       Exit;
  297.     end;
  298.   Result := False;      
  299. end;
  300. {$IFDEF NETSUPPORT} var i, j: Integer; {$ENDIF}
  301. begin
  302.   inherited;
  303.   if Starter.Terminated then Exit;                           // Error occured
  304.   Starter.Terminated := True;
  305.   LogOn(Config['UserName']);
  306.   CreateCore(TCore);
  307.   Core.MessageHandler := HandleMessage;
  308.   Starter.MessageHandler := Core.HandleMessage;
  309. // Render
  310.   {$IFDEF OPENGL}
  311.   Core.Renderer := TOGLRenderer.Initialize(World.ResourceManager, CommandQueue);
  312. //  World.Renderer := TOGLDispListRenderer.Initialize(World.ResourceManager, CommandQueue {$IFDEF Logging} , Log {$ENDIF});
  313.   {$ELSE}
  314.   Core.Renderer := TDX8Renderer.Create(Core);
  315.   SetDeviceType(Config['DeviceType']);
  316.   {$ENDIF}
  317.   if not SetAdapter(Config['VideoAdapter']) then begin
  318.     {$IFDEF LOGGING} Log.Log(ClassName + '.Create: Video adapter "' + Config['VideoAdapter'] + '" not found. Default video adapter used', lkError); {$ENDIF}
  319.   end;
  320.   if Core.Renderer.State = rsNotInitialized then begin
  321.     {$IFDEF LOGGING} Log.Log('Can''t start renderer', lkFatalError); {$ENDIF}
  322.     Starter.PrintError(StrNoDX, lkFatalError);
  323.     Exit;
  324.   end;
  325. //
  326.   {$IFDEF SCREENSAVER}
  327.   CFG.SetOptionIndex('VideoMode', 0);
  328.   {$ENDIF}
  329.   {$IFNDEF ONLINEBUILD}
  330.   if not ActivateWindow(Starter.WindowHandle) then begin
  331.     {$IFDEF LOGGING} Log.Log('Failed to activate main window', lkWarning); {$ENDIF}
  332.   end;
  333.   {$ENDIF}
  334.   Core.Input := TOSController.Create(Starter.WindowHandle, Core.HandleMessage);
  335. //  SkipInputPoll     := False;
  336. //  SkipCameraControl := False;
  337.   {$IFDEF AUDIO}
  338.   Audio := TDX8AudioManager.Initialize(Starter.WindowHandle, World.ResourceManager);
  339.   {$ENDIF}
  340. {$IFDEF NETSUPPORT}
  341.   Net := TDX8Net.Initialize(AppGUID, World.NetMessages);
  342.   SetLength(NetModeVariants, Net.TotalServiceProviders);
  343.   for i := 0 to Net.TotalServiceProviders-1 do begin
  344.     s := Net.SPInfo[i].pwszName;
  345.     j := Pos('DirectPlay8', s);
  346.     if j > 0 then Delete(s, j, Length('DirectPlay8'));
  347.     j := Pos('Service Provider', s);
  348.     if j > 0 then Delete(s, j, Length('Service Provider'));
  349.     s := Trim(s);
  350.     s := Upcase(s[1]) + Copy(s, 2, Length(s));
  351.     NetModeVariants[i] := s;
  352.     if Pos('TCP/IP', UpperCase(s)) > 0 then Net.SetServiceProvider(i);
  353.   end;
  354.   GameHostName := 'CAST multiplayer host';
  355. {$ENDIF}
  356.   if ApplyVideoOptions then Starter.Terminated := False;
  357. //  Core.Renderer.MaxTextureWidth := 64;
  358. //  Core.Renderer.MaxTextureHeight := 64;
  359.   if not (Starter is TScreenSaverStarter) or not (Starter as TScreenSaverStarter).PreviewMode then BindStandardControls;
  360. //  MouseSensitivity := 0.1;
  361. end;
  362. procedure TCast2App.CreateCore(CoreClass: CCore);
  363. begin
  364.   Core := CoreClass.Create;
  365.   Core.RegisterItemClass(TItem);
  366.   Core.RegisterItemClass(TDummyItem);
  367.   Core.RegisterItemClass(TRootItem);
  368.   Core.RegisterItemClass(TProcessing);
  369.   Core.RegisterItemClass(TCamera);
  370.   Core.RegisterItemClass(TLookAtCamera);
  371.   Core.RegisterItemClass(TMaterial);
  372.   Core.RegisterItemClass(TTechnique);
  373.   Core.RegisterItemClass(TRenderPass);
  374.   Core.RegisterItemClass(TLight);
  375. // Resources
  376.   Core.RegisterItemClass(TResource);
  377.   Core.RegisterItemClass(TImageResource);
  378.   Core.RegisterItemClass(TTextureResource);
  379.   Core.RegisterItemClass(TVerticesResource);
  380.   Core.RegisterItemClass(TIndicesResource);
  381.   Core.RegisterItemClass(TUVMapResource);
  382.   Core.RegisterItemClass(TCharMapResource);
  383.   Core.RegisterItemClass(TAudioResource);
  384.   Core.RegisterItemClass(TScriptResource);
  385. // Visible
  386.   Core.RegisterItemClass(TVisible);
  387.   Core.RegisterItemClass(TMesh);
  388.   Core.RegisterItemClass(C2VisItems.TPlane);
  389.   Core.RegisterItemClass(TCircle);
  390.   Core.RegisterItemClass(TDome);
  391.   Core.RegisterItemClass(TSky);
  392. // Particle
  393.   Core.RegisterItemClass(TParticleSystem);
  394.   Core.RegisterItemClass(T2DParticleSystem);
  395.   Core.RegisterItemClass(T3DParticleSystem);
  396.   Core.RegisterItemClass(TEmitter);
  397.   Core.RegisterItemClass(TPSAbsorber);
  398.   Core.RegisterItemClass(TPSMover);
  399.   Core.RegisterItemClass(TPSAttractor);
  400.   Core.RegisterItemClass(TPSColorInterpolator);
  401.   Core.RegisterItemClass(TPSForce);
  402.   Core.RegisterItemClass(TSphericalEmitter);
  403. // 2D
  404.   Core.RegisterItemClass(TFont);
  405.   Core.RegisterItemClass(TBitmapFont);
  406.   Core.CatchAllInput := True;
  407. end;
  408. function TCast2App.LoadScene(const FileName: string): Boolean;
  409. var Stream: TFileStream;
  410. begin
  411.   Result := False;
  412.   Stream := TFileStream.Create(Filename);
  413.   if not Core.LoadScene(Stream) then begin
  414.     {$IFDEF LOGGING}
  415.     Log.Log(Self.ClassName + '.Create: Error loading file "' + FileName + '"', lkError);
  416.     {$ENDIF}
  417.     Stream.Free;
  418.     Exit;
  419.   end;
  420.   
  421.   Result := True;
  422. end;
  423. procedure TCast2App.BindAction(Msg: CMessage; const AName, AActivateBinding, ADeactivateBinding: string);
  424. var Index: Integer;
  425. begin
  426.   Index := GetActionIndex(AName);
  427.   if AActivateBinding = '' then begin                   // Delete binding
  428.     Log.Log(Format('%S.%S: Activation binding not specified for action "%S"', [ClassName, 'BindAction', AName]), lkWarning);
  429.   end;
  430.   if Index = -1 then begin
  431.     Index := Length(FActions);
  432.     SetLength(FActions, Index+1);
  433.   end;
  434.   FActions[Index].Name              := AName;
  435.   FActions[Index].ActivateBinding   := AActivateBinding;
  436.   FActions[Index].DeactivateBinding := ADeactivateBinding;
  437.   FActions[Index].Message           := Msg;
  438.   FActions[Index].Active            := False;
  439.   if not Assigned(Core) or not Assigned(Core.Input) then begin
  440.     Log.Log(ClassName + '.BindAction: Core or Core.Input is not assigned', lkError);
  441.     Exit;
  442.   end;
  443.   if Msg = nil then begin                               // No message to bind - using activated/deactivated semantics
  444.     Core.Input.BindDelegate(AActivateBinding, ActionActivateCallback, Index);
  445.     if ADeactivateBinding <> '' then
  446.       Core.Input.BindDelegate(ADeactivateBinding, ActionDeactivateCallback, Index) else begin
  447.         Log.Log(ClassName + '.BindAction: Message to bind and deactivation binding both undefined for action "' + AName + '"', lkWarning);
  448.       end;
  449.   end else Core.Input.BindCommand(AActivateBinding, Msg);
  450. end;
  451. procedure TCast2App.DeleteAction(const AName: string);
  452. var Index: Integer;
  453. begin
  454.   Index := GetActionIndex(AName);
  455.   if Index = -1 then Exit;
  456.   FActions[Index] := FActions[High(FActions)];
  457.   SetLength(FActions, Length(FActions)-1);
  458. end;
  459. procedure TCast2App.LogOff;
  460. begin
  461.   if Config['UserName'] = '' then Exit;
  462.   {$IFDEF LOGGING} Log.Log(ClassName + '.LogOff: User "' + Config['UserName'] + '"', lkNotice); {$ENDIF}
  463.   Config.SaveAs(UserNameToFileName(Config['UserName']));
  464.   Config['UserName'] := '';
  465. end;
  466. procedure TCast2App.LogOn(UserName: string);
  467. begin
  468.   if UserName = '' then UserName := 'Player';
  469.   {$IFDEF LOGGING} Log.Log(ClassName + '.LogOn: User "' + UserName + '"', lkNotice); {$ENDIF}
  470.   if not Config.LoadFrom(UserNameToFileName(UserName)) then begin
  471.     {$IFDEF LOGGING} Log.Log(ClassName + '.LogOn: User profile file "' + UserNameToFileName(UserName) + '"not found', lkWarning); {$ENDIF}
  472.   end;
  473.   Config['UserName'] := UserName;
  474.   EnumAll;
  475.   ApplyOptions;
  476. end;
  477. procedure TCast2App.ApplyOptionSet(const OptionSet: string);
  478. begin
  479.   if OptionSet = 'VIDEOOPTIONS' then ApplyVideoOptions else
  480.     if OptionSet = 'PLAYERLIST' then begin
  481.       LogOn(Config['UserName']);
  482.     end;
  483. end;
  484. procedure TCast2App.ApplyOptions;
  485. begin
  486.   ApplyControls;
  487.   ApplyVideoOptions;
  488. end;
  489. function TCast2App.ApplyVideoOptions: Boolean;
  490. var i: Integer; CurVideoMode: Cardinal; ModifyViewport: Boolean;
  491. begin
  492.   Result := True;
  493.   if (Core = nil) or (Core.Renderer = nil) then Exit;
  494.   Exclude(Core.Renderer.AppRequirements.Flags, arForceVSync);
  495.   Exclude(Core.Renderer.AppRequirements.Flags, arForceNoVSync);
  496.   if Config['VSync'] = OnOffStr[True] then
  497.     Include(Core.Renderer.AppRequirements.Flags, arForceVSync) else
  498.       if Config['VSync'] = OnOffStr[False] then
  499.         Include(Core.Renderer.AppRequirements.Flags, arForceNoVSync);          
  500.   CurVideoMode := 0;
  501.   for i := 0 to Core.Renderer.TotalVideoModes-1 do if MatchVideoMode(Core.Renderer.VideoMode[i], Config['VideoMode']) then begin
  502.     CurVideoMode := i;
  503.     Break;
  504.   end;
  505. // Skip viewport restoring if nothing was changed
  506.   ModifyViewport := (Core.Renderer.State = rsNotReady) or
  507.                     (Core.Renderer.FullScreen <> (Config.GetAsInteger('Windowed') = 0)) or
  508.                     (Core.Renderer.FullScreen and (Core.Renderer.CurrentVideoMode <> CurVideoMode));
  509. //                    (not Core.Renderer.FullScreen and (Config.GetAsInteger('Windowed') > 0) or
  510. //                         Core.Renderer.FullScreen and (Config.GetAsInteger('Windowed') = 0) and (Core.Renderer.CurrentVideoMode = CurVideoMode));
  511.   if Core.Renderer.State = rsNotReady then begin
  512.     Result := Core.Renderer.CreateDevice(Starter.WindowHandle, CurVideoMode, Config.GetAsInteger('Windowed') = 0);
  513.   end else if ModifyViewport then
  514.     Result := Core.Renderer.RestoreDevice(CurVideoMode, Config.GetAsInteger('Windowed') = 0);
  515.   if not Result then begin
  516.     Starter.PrintError(PChar('Can''t start renderer...'), lkError);
  517.     Exit;
  518.   end;
  519.   Core.HandleMessage(TWindowResizeMsg.Create(0, 0, Core.Renderer.RenderWidth, Core.Renderer.RenderHeight));
  520.   Core.Renderer.SetGamma(StrToIntDef(Config['Gamma'], 50)/50, StrToIntDef(Config['Contrast'], 50)/50, StrToIntDef(Config['Brightness'], 50)/50);
  521.   Result := True;
  522. end;
  523. procedure TCast2App.PreviewOptions(OptionName, Value: string);
  524. var Gamma, Contrast, Brightness: Single;
  525. begin
  526. // Video
  527.   if OptionName = 'GAMMA'      then Gamma      := StrToIntDef(Value, 50)/50 else Gamma      := StrToIntDef(Config['Gamma'],      50)/50;
  528.   if OptionName = 'CONTRAST'   then Contrast   := StrToIntDef(Value, 50)/50 else Contrast   := StrToIntDef(Config['Contrast'],   50)/50;
  529.   if OptionName = 'BRIGHTNESS' then Brightness := StrToIntDef(Value, 50)/50 else Brightness := StrToIntDef(Config['Brightness'], 50)/50;
  530.   Core.Renderer.SetGamma(Gamma, Contrast, Brightness);
  531. // User profiles  
  532.   if OptionName = 'USERNAME'   then LogOff;
  533. end;
  534. procedure TCast2App.ObtainViewportCenter(out X, Y: Integer);
  535. var Rect: TRect;
  536. begin
  537.   if not Assigned(Core) or not Assigned(Core.Renderer) then Exit;
  538.   if Core.Renderer.FullScreen then begin
  539.     X := Core.Renderer.RenderWidth div 2;
  540.     Y := Core.Renderer.RenderHeight div 2;
  541.   end else begin
  542.     GetWindowRect(Starter.WindowHandle, Rect);
  543.     X := (Rect.Left + Rect.Right) div 2;
  544.     Y := (Rect.Top + Rect.Bottom) div 2;
  545.   end;
  546. end;
  547. procedure TCast2App.HandleMessage(const Msg: TMessage);
  548. const SC_KEYMENU = 61696;
  549. begin
  550.   if Msg = nil then Exit;
  551.   Starter.CallDefaultMsgHandler := True;
  552.   if not Starter.Terminated then begin
  553. //    if Assigned(Core) then Core.HandleMessage(Msg);
  554.   end;
  555. // -- OS Messages
  556.   if Msg.ClassType = TWindowMenuCommand then begin
  557.     with TWindowMenuCommand(Msg) do if Command and $FFF0 = SC_KEYMENU then Starter.CallDefaultMsgHandler := False;
  558.   end else if (Msg.ClassType = TWindowActivateMsg) or (Msg.ClassType = TWindowDeactivateMsg) then begin
  559. //    Core.Paused := Msg.ClassType = TWindowActivateMsg;
  560.     if Assigned(Core.Input) then begin
  561. //      EndCameraMode(cmRotate);
  562. //      MoveMode := False; RotateMode := False; ZoomMode := False;
  563.     end;
  564.     if Assigned(Core.Input) then Core.Input.ProcessInput([]);
  565.   end else if Msg.ClassType = TWindowResizeMsg then begin
  566. //    if Assigned(Input) then if Input.MouseAnchored then Input.SetMouseAnchor(GetViewportCenter(vpcX), GetViewportCenter(vpcY));
  567.   end else if Msg.ClassType = TWindowMinimizeMsg then begin
  568. //    Core.Paused := True;
  569.   end else
  570. // -- System messages
  571.   if Msg is TForceQuitMsg then
  572.     Starter.Terminated := True else
  573.   if Msg.ClassType = TOptionsApplyMsg then with TOptionsApplyMsg(Msg) do begin
  574.     ApplyOptionSet(OptionSet);
  575.   end else if (Msg.ClassType = TOptionsPreviewMsg) or (Msg.ClassType = TOptionsApplyNotifyMsg) then begin
  576.     with TOptionsPreviewMsg(Msg) do PreviewOptions(OptionName, Value);
  577.   end else
  578. // -- Miscellaneous messages
  579.   if Msg.ClassType = TFullScreenToggleMsg then begin
  580.     if Config.GetAsInteger('Windowed') > 0 then
  581.       Config.Add('Windowed', vtBoolean, [], OnOffStr[False], '', '') else
  582.         Config.Add('Windowed', vtBoolean, [], OnOffStr[True], '', '');
  583.     Core.Renderer.FullScreen := Config.GetAsInteger('Windowed') = 0;
  584.   end;
  585. end;
  586. procedure TCast2App.Process;
  587. //var LRMove, FBMove: Single; 
  588. begin
  589.   Starter.Process;
  590.   if not Starter.Terminated and Assigned(Core) then begin
  591.     Core.Process;
  592. {    LRMove := (- Ord(Action['Left'])     + Ord(Action['Right'])   ) * MouseSensitivity;
  593.     FBMove := (- Ord(Action['Backward']) + Ord(Action['Forward']) ) * MouseSensitivity;
  594.     with Core.Renderer.MainCamera do begin
  595.       if Abs(LRMove) + Abs(FBMove) > epsilon then
  596.         Position := AddVector3s(Location.XYZ, AddVector3s(ScaleVector3s(RightVector, LRMove), ScaleVector3s(ForwardVector, FBMove)));
  597.     end;}
  598.   end;
  599. end;
  600. destructor TCast2App.Destroy;
  601. var Subsys: TSubsystem;
  602. begin
  603.   Config.Save;
  604.   LogOff;
  605. {$IFDEF NETSUPPORT} if Net <> nil then Net.Free; Net := nil; {$ENDIF}
  606.   Subsys := Core.Renderer;
  607.   Core.Renderer := nil;
  608.   if Assigned(Subsys) then Subsys.Free;
  609.   Subsys := Core.Input;
  610.   Core.Input := nil;
  611.   if Assigned(Subsys) then Subsys.Free;
  612.   if Core <> nil then Core.Free; Core := nil;
  613.   inherited;
  614. end;
  615. procedure TCast2App.GotoURL(const URLFileName: string);
  616. begin
  617.   if Assigned(Core) then begin
  618.     if Assigned(Core.Renderer) and Core.Renderer.FullScreen then OSUtils.MinimizeWindow(Starter.WindowHandle);
  619.   end;
  620.   OSUtils.OpenURL(URLFileName);
  621.   inherited;
  622. end;
  623. end.