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

游戏引擎

开发平台:

Delphi

  1. (*
  2.  @Abstract(Basic classes 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 basic item-related classes
  6. *)
  7. {$Include GDefines.inc}
  8. unit BaseClasses;
  9. interface
  10. uses
  11.   TextFile,
  12.   SysUtils,
  13.   BaseTypes, Basics, Models, BaseMsg, Props, OTypes;
  14. const
  15.   // Maximum possible item state flags
  16.   MaxStates    = 32;
  17.   // First HiddenStates states will not be visible in editor
  18.   HiddenStates = 4;
  19.   // An item was removed from manager or marked to remove
  20.   isRemoved     = 0;
  21.   // An item was marked to release
  22.   isReleased    = 1;
  23.   // An item hasn't been initialized yet
  24.   isNeedInit    = 2;
  25.   // Visualize item's selection information with a color defined by Globals.PickedBoxColor
  26.   isPicked      = 3;
  27.   // An item should be visualised
  28.   isVisible     = 4;
  29.   // Process method of an item should be called according to its ProcessingClass field
  30.   isProcessing  = 5;
  31.   // Visualize item's debug information (bounding boxes, etc)
  32.   isDrawVolumes = 6;
  33.   // Childs collection capacity increment step
  34.   ChildsCapacityStep = 8;
  35.   // Collections capacity increment step
  36.   CollectionsCapacityStep = 16;
  37.   // Items collection capacity increment step
  38.   ItemsCapacityStep = 16;
  39.   // Hierarchy delimiter
  40.   HierarchyDelimiter = '';
  41.   // A simbol to address upper level of hierarchy in relative item names
  42.   ParentAdressName = '.';
  43. type
  44.   // Main floating point type
  45.   Float = Single;
  46.   // Item flag set
  47.   TItemFlags = TSet32;
  48.   // Item move modes
  49.   TItemMoveMode = (// insert before
  50.                    mmInsertBefore,
  51.                    // insert after
  52.                    mmInsertAfter,
  53.                    // add as first child
  54.                    mmAddChildFirst,
  55.                    // add as last child
  56.                    mmAddChildLast,
  57.                    // move up within the current level
  58.                    mmMoveUp,
  59.                    // move down within the current level
  60.                    mmMoveDown,
  61.                    // move up one level
  62.                    mmMoveLeft,
  63.                    // move down one level
  64.                    mmMoveRight);
  65.   // @Exclude()
  66.   TItem = class;
  67.   // Item class type
  68.   CItem = class of TItem;
  69.   // Used for classes registration
  70.   TClassArray = array of TClass;
  71.   TItemsManager = class;
  72.   // @Exclude()
  73.   TObjectLinkFlag = (lfAbsolute);
  74.   // @Exclude()
  75.   TObjectLinkFlags = set of TObjectLinkFlag;
  76.   // @Exclude() Item link property data
  77.   TObjectLink = record
  78.     Flags: TObjectLinkFlags;
  79.     PropName, ObjectName: AnsiString;
  80.     Item: TItem;
  81.     BaseClass: CItem;
  82.   end;
  83.   // Simple items collection
  84.   TItems = array of TItem;
  85.   // Extract condition function result flags
  86.   TExtractConditionItems = (// condition passed
  87.                             ecPassed,
  88.                             // do not follow current hierarchy
  89.                             ecBreakHierarchy,
  90.                             // completely stop traverse
  91.                             ecBreak);
  92.   // Extract condition function result type
  93.   TExtractCondition = set of TExtractConditionItems;
  94.   // Condition function for conditional extraction
  95.   TExtractConditionFunc = function(Item: TItem): TExtractCondition of object;
  96.   // @Abstract(Abstract compiler class)
  97.   TAbstractCompiler = class(TSubsystem)
  98.   public
  99.     // Description of last compilation error occured
  100.     LastError: string;
  101.     // Translate the given source to an intermediate or binary form
  102.     function Compile(const Source: AnsiString): OTypes.TRTData; virtual; abstract;
  103.   end;
  104.   // Scene loading error type
  105.   TSceneLoadError = class(TError)
  106.   end;
  107.   { @Abstract(Base item class)
  108.     Provides hierarchical structure, saving/loading, properties interface and some service functions. }
  109.   TItem = class(TBaseItem)
  110.   public
  111.     // Item name. Used to reference items by name in a filesystem-like way: RootItemNameParentName
  112.     Name: AnsiString;
  113.   private
  114.     ItemLinks: array of TObjectLink;
  115.     procedure DeAlloc;
  116.     procedure ChangeChildIndex(Child: TItem; NewIndex: Integer);
  117.     // Internal link management
  118.     function GetLinkIndex(const AName: AnsiString): Integer;
  119.     function SetLinkedObjectByIndex(Index: Integer; Linked: TItem): Boolean;                   // Called from ResolveLink
  120.     function ObtainLinkedItemNameByIndex(PropIndex: Integer): AnsiString;
  121.     function DoAddChild(AItem: TItem): TItem;
  122.     function GetChild(Index: Integer): TItem;
  123.   protected
  124.     {$IFDEF DEBUGMODE}
  125.     { This flag is True when the item's internal state is valid and the item can be used or queried from outside.
  126.       If this flag is False no routines expecting that the item is valid should be called.
  127.       Only asyncronous messages allowed to be sent by an item when its FConsistent is False. }
  128.     FConsistent: Boolean;
  129.     {$ENDIF}
  130.     // Childs collection
  131.     FChilds: TItems;
  132.     // Number of childs
  133.     FTotalChilds: Integer;
  134.     // Set of state flags
  135.     FState: TItemFlags;
  136.     // Parent reference
  137.     FParent: TItem;
  138.     // Manager reference. See @Link(TItemsManager)
  139.     FManager: TItemsManager;
  140.     // Index in parent collection for internal use
  141.     IndexInParent: Integer;
  142.     // Sets a new state flags
  143.     procedure SetState(const Value: TItemFlags); virtual;
  144.     // Sets a new parent value
  145.     procedure SetParent(NewParent: TItem); virtual;
  146.     // Sets Parent to nil without removing from hierarchy, etc
  147.     procedure ClearParent;
  148.     // Sets manager for item and propagates the change to all childs if requested
  149.     procedure SetManager(AManager: TItemsManager; SetChilds: Boolean);
  150.     // Calls the specified delegate for all items in the hierarchy starting from Self. Data can be some custom generic data or nil.
  151.     procedure DoForAllChilds(Delegate: TDelegate; Data: Pointer);
  152.     // Calls HandleMessage with the message for all items in the hierarchy starting from Self
  153.     procedure BroadcastMessage(const Msg: TMessage);
  154.     // Sets @Link(mfNotification) flag of the message and calls HandleMessage with the message for all first-level childs
  155.     procedure NotifyChilds(const Msg: TMessage);
  156.     // Sets child and returns AItem if success or nil if index is invalid or impossible to set a child
  157.     function SetChild(Index: Integer; AItem: TItem): TItem; virtual;
  158.     // Inserts a child to the given position in childs collection
  159.     procedure InsertChild(AItem: TItem; Index: Integer);
  160.     // Removes a child with the specified index
  161.     procedure RemoveChildByIndex(Index: Integer); virtual;
  162.       // Link management
  163.     { Adds an item link property with the given name and base class to Properties.
  164.       Use this method in order to add a property which points to another item }
  165.     procedure AddItemLink(Properties: TProperties; const PropName: AnsiString; PropOptions: TPOptions; const BaseClass: AnsiString);
  166.     // Performs initialization of internal data structures. Do not call manually
  167.     procedure BuildItemLinks;
  168.     // Resolves (with class checking) an object link and returns <b>True</b> if a NEW linked item was resolved.
  169.     function ResolveLink(const PropName: AnsiString; out Linked: TItem): Boolean;
  170.     { Sets Linked as resolved linked object for a link property with the given name.
  171.       Returns <b>True</b> if Linked passes type checking }
  172.     function SetLinkedObject(const PropName: AnsiString; Linked: TItem): Boolean;
  173.     // Should be called from @Link(SetProperties) to handle item link property setting
  174.     function SetLinkProperty(const AName, Value: AnsiString): Boolean;
  175.     { Called from default @Link(OnSceneLoaded) event handler.
  176.       Override to resolve all link which needed to be resolved right after scene load }
  177.     procedure ResolveLinks; virtual;
  178.   public
  179.     // Regular constructor
  180.     constructor Create(AManager: TItemsManager); virtual;
  181.     // Constructor used to construct complex objects such as windows with a header area and a client area
  182.     constructor Construct(AManager: TItemsManager); virtual;
  183.     // Returns class reference
  184.     class function GetClass: CItem;
  185.     // Items of abstract classes can not be created in editor
  186.     class function IsAbstract: Boolean; virtual;
  187.     { Returns full size in memory of an item in bytes.
  188.       Descendants should override this method if they have dynamic fields which sizes are not included in TObject.InstanceSize. }
  189.     function GetItemSize(CountChilds: Boolean): Integer; virtual;
  190.     // Sends the specified message according to the specified destination. 
  191.     procedure SendMessage(const Msg: TMessage; Recipient: TItem; Destination: TMessageFlags);
  192.     // Main message handler
  193.     procedure HandleMessage(const Msg: TMessage); override;
  194.       // Events
  195.     // Occurs after object creation and initialization of Root variable
  196.     procedure OnInit; virtual;
  197.     // Occurs when a scene is completely loaded
  198.     procedure OnSceneLoaded; virtual;
  199.     // Occurs when the item added to a scene (usally after loading)
  200.     procedure OnSceneAdd; virtual;
  201.     // Occurs when the item being remove from scene
  202.     procedure OnSceneRemove; virtual;
  203.       // Properties system
  204.     // Do not use this procedure directly. Call @Link(AddProperties) instead
  205.     procedure GetProperties(const Result: TProperties);
  206.     { This procedure is called (by editor for example) to retrieve a list of item's properties and their values.
  207.       Any TItem descendant class should override this method in order to add its own properties. }
  208.     procedure AddProperties(const Result: TProperties); virtual;
  209.     { This procedure is called (by editor for example) to set values of item's properties.
  210.       Any TItem descendant class should override this method to allow its own properties to be set. }
  211.     procedure SetProperties(Properties: TProperties); virtual;
  212.     // Calls @Link(AddProperties) to return a single property identified by AName
  213.     function GetProperty(const AName: AnsiString): AnsiString;
  214.     // Calls @Link(SetProperties) to set a single property identified by AName
  215.     procedure SetProperty(const AName, AValue: AnsiString);
  216.     // Returns name of an item which linked by a property with the given name
  217.     procedure ObtainLinkedItemName(const PropName: AnsiString; out Result: AnsiString);
  218.     { Creates and returns a clone of the item with all properties having the same value as in source.
  219.       Descendants should override this method in order to handle specific fields if any. }
  220.     function Clone: TItem; virtual;
  221.     // Saving/Loading
  222.     // Saves an item to a stream and returns <b>True</b> if success
  223.     function Save(Stream: TStream): Boolean; virtual;
  224.     // Loads an item from a stream and returns <b>True</b> if success
  225.     function Load(Stream: TStream): Boolean; virtual;
  226.         // Hierarchy routines
  227.       //  Childs management
  228.     { Adds and returns a child. Sends a @Link(TAddToSceneMsg) message to all items in scene and to manager (see @Link(TItemsManager) ) }
  229.     function AddChild(AItem: TItem): TItem;
  230.     { Removes the given child item. Sends a @Link(TRemoveFromSceneMsg) message to all items in scene and to manager (see @Link(TItemsManager) ) }
  231.     procedure RemoveChild(AItem: TItem); virtual;
  232.     // Returns all childs of the item
  233.     function GetChilds: TItems;
  234.     // Returns item's parent, skipping the dummy ones
  235.     function GetNonDummyParent: TItem;
  236.     { Finds child next to current assuming childs of dummy childs as own. Pass @nil as current to find the first child.
  237.       If next child found, the function returns <b>True</b> and with Current set to that child.
  238.       Otherwise returns <b>False</b> with Current set to @nil. }
  239.     function FindNextChildInclDummy(var Current: TItem): Boolean;
  240.       //  Node search
  241.     // Returns item's full name in a filesystem-like format: <RootItemName><Parent><Name>
  242.     function GetFullName: AnsiString;
  243.     // Finds a child item by its name. Name is case-sensitive. If SearchChilds is False only first-level childs can be found.
  244.     function GetChildByName(const AName: AnsiString; SearchChilds: Boolean): TItem;
  245.     // Finds a child item by its full name relative to the item. Path is case-sensitive.
  246.     function GetChildByPath(const APath: AnsiString): TItem;
  247.     // Finds an item by the given path. The function supports relative paths as well as absolute ones. Path is case-sensitive.
  248.     function GetItemByPath(const APath: AnsiString): TItem;
  249.     // Finds a child next to CurrentChild
  250.     function GetNextChild(CurrentChild: TItem): TItem;
  251.     // Returns full path of an item specified by its full name relative to the item
  252.     function GetRelativeItemName(const AFullName: AnsiString): AnsiString;
  253.     // Moves a child in hierarchy as specified by Mode (see @Link(TItemMoveMode))
  254.     procedure MoveChild(Child, Target: TItem; Mode: TItemMoveMode);
  255.     // Returns <b>True</b> if item is a child of any level of AParent
  256.     function IsChildOf(AParent: TItem): Boolean;
  257.       // Clean up and destruction
  258.     { Marks item as removed from hierarchy and (if DoNotRelease is <b>False</b>) as released.
  259.       These marks will be handled by @Link(CollectGarbage). }
  260.     procedure MarkAsRemoved(DoNotRelease: Boolean);
  261.     // Frees all childs
  262.     procedure FreeChilds; virtual;
  263.     // Regular destructor. Frees item itself, all it's data and all childs.
  264.     destructor Destroy; override;
  265.     // Specifies number of childs of an item
  266.     property TotalChilds: Integer read FTotalChilds;
  267.     // Item's childs collection
  268.     property Childs[Index: Integer]: TItem read GetChild;
  269.     { Item's parent. You can set this property to move the item within items hierarchy.
  270.       Setting Parent to @nil will remove the item from the hierarchy. }
  271.     property Parent: TItem read FParent write SetParent;
  272.     { A set of state flags.
  273.       See @Link(isRemoved), @Link(isReleased), @Link(isNeedInit), @Link(isPicked), @Link(isVisible), @Link(isProcessing), @Link(isDrawVolumes). }
  274.     property State: TItemFlags read FState write SetState;
  275.   end;
  276.   { @Abstract(Used to group items within a hierarchy)
  277.     Forwards all notification messages to childs }
  278.   TDummyItem = class(TItem)
  279.     // Checks if the message is a notification and forwards it to childs
  280.     procedure HandleMessage(const Msg: TMessage); override;
  281.   end;
  282.     
  283.   { A hierarchy root item should be of this (or a descendant) class.
  284.     @Abstract(Provides some item extraction methods) }
  285.   TRootItem = class(TItem)
  286.   public
  287.     constructor Create(AManager: TItemsManager); override;
  288.     function GetItemByFullName(const AName: AnsiString): TItem;
  289.     { Traverses through the items hierarchy and adds all items passing Condition to Items.
  290.       Returns number of items in Items. }
  291.     function Extract(Condition: TExtractConditionFunc; out Items: TItems): Integer;
  292.     { Traverses through the items hierarchy and adds to Items all items which State contains all flags in Mask.
  293.       If Hierarchical is <b>True</b> childs of non-matching items are not considered. Returns number of items in Items. }
  294.     function ExtractByMask(Mask: TItemFlags; Hierarchical: Boolean; out Items: TItems): Integer;
  295.     { Traverses through the items hierarchy and adds all items of the given class or its descendants to Items.
  296.       Returns number of items in Items. }
  297.     function ExtractByClass(AClass: CItem; out Items: TItems): Integer;
  298.     { Traverses through the items hierarchy and adds all items of the given class or its descendants and with State containing all
  299.       flags in Mask to Items. Childs of items with non-matching state are not considered.
  300.       Returns number of items in Items. }
  301.     function ExtractByMaskClass(Mask: TItemFlags; AClass: CItem; out Items: TItems): Integer;
  302.     procedure HandleMessage(const Msg: TMessage); override;
  303.   end;
  304.   // @Abstract(Base class of all items which periodically updates their state)
  305.   TBaseProcessing = class(TItem)
  306.   private
  307.     // Total time processed with the @Link(Process) method since last call of ResetProcessedTime() in seconds
  308.     FTimeProcessed: TTimeUnit;
  309.   public
  310.     // Processing class specifies how an item should be processed. See @Link(TProcessingClass)
  311.     ProcessingClass: Integer;
  312.     // Resets TimeProcessed to zero
  313.     procedure ResetProcessedTime;
  314.     // Pauses processing of the item
  315.     procedure Pause;
  316.     // Resumes processing of the item
  317.     procedure Resume;
  318.     { This method will be called when an item is to be processed (updated).
  319.       Actual process schedule depends on values if processing class (see @Link(TItemsManager)) to which points ProcessingClass field. }
  320.     procedure Process(const DeltaT: Float); virtual;
  321.     procedure AddProperties(const Result: TProperties); override;
  322.     procedure SetProperties(Properties: TProperties); override;
  323.     // Total time processed with the @Link(Process) method
  324.     property TimeProcessed: TTimeUnit read FTimeProcessed;
  325.   end;
  326. {  IResource = interface
  327.     function GetData: Pointer;
  328.     function GetTotalElements: Integer;
  329.     property TotalElements: Integer read GetTotalElements;
  330.     property Data: Pointer read GetData;
  331.   end;}
  332.   // Item used for time syncronization
  333.   TSyncItem = class(TBaseProcessing)
  334.   protected
  335.     procedure SetState(const Value: TItemFlags); override;
  336.   public
  337.     // Sends TSyncTimeMsg to all sibling items and their child items
  338.     procedure Syncronize;
  339.   end;
  340.   // Items manager state
  341.   TIMState = (// the manager is currently loading items
  342.               imsLoading,
  343.               // the manager is currently shutting down
  344.               imsShuttingDown);
  345.   // Item processing flags
  346.   TProcessingFlag = (// force processing even when pause mode is on
  347.                      pfIgnorePause,
  348.                      // process as frequent as possible ignoring Interval
  349.                      pfDeltaTimeBased);
  350.   // Set of item processing flags                   
  351.   TProcessingFlags = set of TProcessingFlag;
  352.   { Processing options for processing classes (see @Link(TItemsManager)).
  353.     Interval - process interval in seconds.
  354.     Flags - see @Link(TProcessingFlag)
  355.     TimerEventID - an ID of a corresponding timer event. -1 if none }
  356.   TProcessingClass = record
  357.     Interval: Float;
  358.     Flags: TProcessingFlags;
  359.     TimerEventID: Integer;
  360.   end;
  361.   { @Abstract(Contains and manages a hierarchy of items starting with Root)
  362.     Contains all registered item classes. }
  363.   TItemsManager = class
  364.   private
  365.     FItemClasses: array of CItem;
  366.     FTotalItemClasses: Integer;
  367.     function GetProcClassesEnum: AnsiString;
  368.     // Sets a new root item
  369.     procedure SetRoot(const Value: TRootItem);
  370.     function GetItemClass(Index: Integer): CItem;
  371.   protected
  372.     // Root of a hierarchy
  373.     FRoot: TRootItem;
  374.     // Names of all possible state flags
  375.     StateNames: array of AnsiString;
  376.     // Current manager state
  377.     FState: set of TIMState;
  378.     // Should be <b>True</b> if world-editing capabilities are required
  379.     FEditorMode: Boolean;
  380.     // Item processing classes (see @Link(TProcessingClass))
  381.     ProcessingClasses: array of TProcessingClass;
  382.     // Asynchronous messages container
  383.     AsyncMessages: TMessageSubsystem;
  384.     // Returns number of processing classes
  385.     function GetTotalProcessingClasses: Integer;
  386.     // Adds a message to the asyncronous queue to be handled later in @Link(ProcessAsyncMessages)
  387.     procedure SendAsyncMessage(const Msg: TMessage; Recipient: TItem); virtual;
  388.     // Handles items market to remove and release
  389.     procedure CollectGarbage; virtual;
  390.     // This event occurs right before destruction of the manager
  391.     procedure OnDestroy; virtual;
  392.   public
  393.     // Scripting subsystem
  394.     Compiler: TAbstractCompiler;
  395.     constructor Create; virtual;
  396.     destructor Destroy; override;
  397.     // Handles all asyncronous messages
  398.     procedure ProcessAsyncMessages;
  399.     // Sends the specified message according to the specified destination. Can be called as class function with mfRecipient and mfChilds destinations.
  400.     procedure SendMessage(const Msg: TMessage; Recipient: TItem; Destination: TMessageFlags);
  401.     // Default core message handler
  402.     procedure HandleMessage(const Msg: TMessage); virtual;
  403.     // Returns <b>True</b> if a scene is currently loading
  404.     function IsSceneLoading: Boolean;
  405.     // Returns <b>True</b> if manager is shutting down
  406.     function IsShuttingdown: Boolean;
  407.     // Registers an item state flag
  408.     function RegisterState(const AName: AnsiString): Boolean;
  409.     // Registers an item class. Only items of registered classes can be saved/loaded or be linked to via item link property.
  410.     procedure RegisterItemClass(NewClass: CItem);
  411.     // Registers an array of item classes. Only items of registered classes can be saved/loaded or be linked to via item link property.
  412.     procedure RegisterItemClasses(NewClasses: array of TClass); 
  413.     // Returns an item class by its name or @nil if not found
  414.     function FindItemClass(const AName: AnsiString): CItem; virtual;
  415.     { Changes class of an item to <b>NewClass</b>. <br>
  416.       <b>All direct references to the item except via object linking mechanism become invalid.</b> }
  417.     function ChangeClass(Item: TItem; NewClass: CItem): TItem;
  418.     // Removes an item from the manager
  419.     procedure RemoveItem(Item: TItem);
  420.     { Loads an item from a stream specified and adds it to AParent as a child.
  421.       Returns the loaded item. }
  422.     function LoadItem(Stream: TStream; AParent: TItem): TItem;
  423.     // Clears the current scene and loads a new scene from a stream
  424.     function LoadScene(Stream: TStream): Boolean;
  425.     // Saves the current scene to a stream
  426.     function SaveScene(Stream: TStream): Boolean;
  427.     // Clears the current scene
  428.     procedure ClearItems; virtual;
  429.     // Should be set to <b>True</b> if world-editing capabilities are required
  430.     property EditorMode: Boolean read FEditorMode;
  431.     // Number of processing classes
  432.     property TotalProcessingClasses: Integer read GetTotalProcessingClasses;
  433.     // Number of registered item classes
  434.     property TotalItemClasses: Integer read FTotalItemClasses;
  435.     // Registered item classes
  436.     property ItemClasses[Index: Integer]: CItem read GetItemClass;
  437.     // Root of a hierarchy
  438.     property Root: TRootItem read FRoot write SetRoot;
  439.   end;
  440.   // Retuns a list of the specified classes
  441.   function GetClassList(AClasses: array of TClass): TClassArray;
  442.   // Merges the two given class lists
  443.   procedure MergeClassLists(var BaseList: TClassArray; AddOnList: array of TClass);
  444. type
  445.   TClassRec = record
  446.     ItemClass: TClass;
  447.     ModuleName: TShortName;
  448.   end;
  449.   TClassesList = class
  450.   private
  451.     TotalClasses: Integer;
  452.     FClasses: array of TClassRec;
  453.     function GetClasses: TClassArray;
  454.     function GetClassesByModule(const AModuleName: TShortName): TClassArray;
  455.   public
  456.     destructor Destroy; override;
  457.     procedure Add(const AModuleName: TShortName; AClass: TClass); overload;
  458.     procedure Add(const AModuleName: TShortName; AClasses: array of TClass); overload;
  459.     function ClassExists(AClass: TClass): Boolean;
  460.     function FindClass(AClass: TClass): TClassRec;
  461.     function FindClassByName(const AModuleName, AClassName: TShortName): TClassRec;
  462.     property Classes: TClassArray read GetClasses;
  463.     property ClassesByModule[const AModuleName: TShortName]: TClassArray read GetClassesByModule;
  464.   end;
  465. var
  466.   GlobalClassList: TClassesList;
  467. implementation
  468. uses ItemMsg;
  469. function GetClassList(AClasses: array of TClass): TClassArray;
  470. begin
  471.   Result := nil;
  472.   MergeClassLists(Result, AClasses);
  473. end;
  474. procedure MergeClassLists(var BaseList: TClassArray; AddOnList: array of TClass);
  475. var i, OldLen: Integer;
  476. begin
  477.   OldLen := Length(BaseList);
  478.   SetLength(BaseList, OldLen + Length(AddOnList));
  479.   for i := 0 to High(AddOnList) do BaseList[OldLen + i] := AddOnList[i];
  480. end;
  481. { TItemsManager }
  482. procedure TItemsManager.OnDestroy;
  483. begin
  484.   ClearItems;
  485.   StateNames := nil;
  486.   FreeAndNil(AsyncMessages);
  487. end;
  488. constructor TItemsManager.Create;
  489. begin
  490.   AsyncMessages := TMessageSubsystem.Create;
  491.   RegisterItemClass(TItem);
  492.   RegisterItemClass(TRootItem);
  493.   RegisterItemClass(TDummyItem);
  494.   RegisterItemClass(TSyncItem);
  495.   RegisterState('Removed');
  496.   RegisterState('Released');
  497.   RegisterState('Uninitialized');
  498.   RegisterState('Picked');
  499.   RegisterState('Render');
  500.   RegisterState('Process');
  501.   RegisterState('Draw bounds');
  502. end;
  503. destructor TItemsManager.Destroy;
  504. begin
  505.   OnDestroy;
  506.   inherited;
  507. end;
  508. function TItemsManager.GetTotalProcessingClasses: Integer;
  509. begin
  510.   Result := Length(ProcessingClasses);
  511. end;
  512. procedure TItemsManager.SendAsyncMessage(const Msg: TMessage; Recipient: TItem);
  513. begin
  514.   Assert((Recipient = nil) or not (mfRecipient in Msg.Flags), 'TItemsManager.SendAsyncMessage: Invalid recipient');
  515.   Assert([mfRecipient, mfChilds, mfBroadcast, mfCore] * Msg.Flags <> [], 'TItemsManager.SendAsyncMessage: Invalid message flags');;
  516.   if ([mfRecipient, mfChilds] * Msg.Flags <> []) then
  517.     AsyncMessages.Add(TMessageEnvelope.Create(Recipient, Msg)) else
  518.       AsyncMessages.Add(Msg);
  519. end;
  520. procedure TItemsManager.SendMessage(const Msg: TMessage; Recipient: TItem; Destination: TMessageFlags);
  521. begin
  522.   Assert(Assigned(Msg));
  523.   Assert(Destination <> [], 'Invalid destination');
  524. //  Assert((Destination <> []) and
  525. //         (not (mdRecipient in Destination) or not (mdChilds    in Destination)), 'Invalid destination');
  526.   if ([mfRecipient, mfChilds] * Destination <> []) then
  527.     Assert(Assigned(Recipient))
  528.   else
  529.     Recipient := nil;
  530.   if (mfBroadcast in Destination) and not Assigned(Recipient) then begin
  531.     if Assigned(Root) then Recipient := Root else Exclude(Destination, mfBroadcast);
  532.   end;
  533.   Msg.Flags := Destination;
  534. {
  535.   if mdRecipient in Destination then Msg.Flags := Msg.Flags + [mfRecipient];
  536.   if mdBroadcast in Destination then Msg.Flags := Msg.Flags + [mfBroadcast];
  537.   if mdCore      in Destination then Msg.Flags := Msg.Flags + [mfCore];
  538.   if mdChilds    in Destination then Msg.Flags := Msg.Flags + [mfNotification];}
  539.   if mfAsync in Destination then SendAsyncMessage(Msg, Recipient) else begin
  540.     if mfCore      in Destination then HandleMessage(Msg);
  541.     if mfRecipient in Destination then Recipient.HandleMessage(Msg);
  542.     if mfChilds    in Destination then Recipient.NotifyChilds(Msg);
  543.     if mfBroadcast in Destination then Recipient.BroadcastMessage(Msg);
  544.   end;
  545. end;
  546. procedure TItemsManager.HandleMessage(const Msg: TMessage);
  547. begin
  548.   {$IFDEF DEBUGMODE} 
  549.   Assert(mfCore in Msg.Flags);
  550.   {$ENDIF}
  551. //  if (Msg.ClassType = TOperationMsg) and not (ofHandled in TOperationMsg(Msg).Operation.Flags) then TOperationMsg(Msg).Operation.Free;
  552. end;
  553. procedure TItemsManager.ProcessAsyncMessages;
  554. var Msg: TMessage; Recipient: TItem;
  555. begin
  556.   AsyncMessages.BeginHandle;
  557.   while AsyncMessages.ExtractMessage(Msg) do begin
  558.     if (Msg is TMessageEnvelope) then begin
  559.       Recipient := TMessageEnvelope(Msg).Recipient;
  560.       Msg       := TMessageEnvelope(Msg).Message;
  561.     end else Recipient := nil;
  562.     SendMessage(Msg, Recipient, Msg.Flags - [mfAsync]);
  563.   end;
  564.   AsyncMessages.EndHandle;
  565. end;
  566. function TItemsManager.IsSceneLoading: Boolean;
  567. begin
  568.   Result := imsLoading in FState;
  569. end;
  570. function TItemsManager.IsShuttingdown: Boolean;
  571. begin
  572.   Result := imsShuttingDown in FState;
  573. end;
  574. function TItemsManager.RegisterState(const AName: AnsiString): Boolean;
  575. begin
  576.   Result := False;
  577.   if Length(StateNames) >= MaxStates then begin
  578.     Log.Log(Format(ClassName + '.RegisterState: Only %D states allowed', [MaxStates]), lkError);
  579.     Exit;
  580.   end;
  581.   SetLength(StateNames, Length(StateNames)+1);
  582.   StateNames[High(StateNames)] := AName;
  583.   Result := True;
  584. end;
  585. procedure TItemsManager.RegisterItemClass(NewClass: CItem);
  586. begin
  587.   if FindItemClass(AnsiString(NewClass.ClassName)) <> nil then begin
  588.     Log.Log(ClassName + '.RegisterItemClass: Class "' + NewClass.ClassName + '" already registered', lkWarning);
  589.     Exit;
  590.   end;
  591.   SetLength(FItemClasses, TotalItemClasses+1);
  592.   FItemClasses[TotalItemClasses] := NewClass;
  593.   Inc(FTotalItemClasses);
  594. end;
  595. procedure TItemsManager.RegisterItemClasses(NewClasses: array of TClass);
  596. var i: Integer;
  597. begin
  598.   for i := 0 to High(NewClasses) do if NewClasses[i].InheritsFrom(TItem) then RegisterItemClass(CItem(NewClasses[i]));
  599. end;
  600. function TItemsManager.FindItemClass(const AName: AnsiString): CItem;
  601. var i: Integer;
  602. begin
  603.   Result := nil;
  604.   i := TotalItemClasses-1;
  605.   while (i >= 0) and (ItemClasses[i].ClassName <> AName) do Dec(i);
  606.   if i >= 0 then Result := ItemClasses[i];
  607. end;
  608. function TItemsManager.ChangeClass(Item: TItem; NewClass: CItem): TItem;
  609. var i: Integer; Props: TProperties;
  610. begin
  611.   Result := nil;
  612.   if Item = nil then begin
  613.     {$IFDEF LOGGING} Log.Log(ClassName + '.ChangeClass: Item is undefined', lkError); {$ENDIF}
  614.     Exit;
  615.   end;
  616.   Result := NewClass.Construct(Item.FManager);
  617. // Copy childs
  618.   Result.FTotalChilds := Item.TotalChilds;
  619.   SetLength(Result.FChilds, Length(Item.FChilds));
  620.   for i := 0 to High(Item.FChilds) do begin
  621.     Result.FChilds[i] := Item.FChilds[i];
  622.     if Result.FChilds[i] <> nil then Result.FChilds[i].FParent := Result;
  623.   end;
  624. // Copy state and parent
  625.   Result.FState := Item.FState;
  626.   Result.FParent := Item.FParent;
  627.   Result.IndexInParent := Item.IndexInParent;
  628. // Copy object links data
  629.   SetLength(Result.ItemLinks, Length(Item.ItemLinks));
  630.   for i := 0 to High(Item.ItemLinks) do Result.ItemLinks[i] := Item.ItemLinks[i];
  631. // Replace the item in parent's collection
  632.   if Result = FRoot then begin
  633.     if not (Result is TRootItem) then begin
  634.       {$IFDEF LOGGING} Log.Log(ClassName + '.ChangeClass: Root item'' class should be TRootItem or one of its descendants', lkError); {$ENDIF}
  635.       TObject(Result).Destroy;                                          // There is no need to call FreeChilds
  636.       Result := nil;
  637.       Exit;
  638.     end;
  639.     FRoot := Result as TRootItem;
  640.   end else Item.Parent.FChilds[Item.IndexInParent] := Result;
  641.   SendMessage(ItemMsg.TInitMsg.Create, Result, [mfRecipient]);
  642.   Props := TProperties.Create;
  643.   Item.GetProperties(Props);
  644.   Result.SetProperties(Props);
  645.   FreeAndNil(Props);
  646.   SendMessage(ItemMsg.TReplaceMsg.Create(Item, Result), Item, [mfRecipient, mfBroadcast, mfCore]);
  647.   Item.DeAlloc;
  648. end;
  649. procedure TItemsManager.RemoveItem(Item: TItem);
  650. begin
  651.   if Item = nil then Exit;
  652.   if Item.Parent <> nil then Item.Parent.RemoveChild(Item);
  653.   if Item = FRoot then FRoot := nil;
  654. end;
  655. procedure TItemsManager.ClearItems;
  656. begin
  657.   SendMessage(TSceneClearMsg.Create, nil, [mfCore]);
  658.   Include(FState, imsShuttingDown);
  659.   if FRoot <> nil then begin
  660.     FRoot.OnSceneRemove;
  661.     FRoot.FreeChilds;
  662.     FRoot.Free;
  663.     FRoot := nil;
  664.   end;
  665.   Exclude(FState, imsShuttingDown);
  666. end;
  667. function TItemsManager.LoadItem(Stream: TStream; AParent: TItem): TItem;
  668. var s: AnsiString; ItemClass: CItem;
  669. begin
  670.   Result := nil;
  671.   if not LoadString(Stream, s) then Exit;
  672. //  s := 'TItem';
  673. //  if s = 'TCBitmapFont' then s := 'TBitmapFont';
  674. //  if s = 'TGUIItem' then s := 'TLabel';
  675.   ItemClass := FindItemClass(s);
  676.   if ItemClass = nil then begin
  677.     Log.Log(ClassName + '.LoadItem: Unknown item class "' + s + '". Substitued by TItem', lkError);
  678.     ItemClass := TItem;
  679.   end;
  680.   Result := ItemClass.Create(Self);
  681.   if Assigned(AParent) and ((FRoot = nil) or (AParent.FManager <> Self)) then begin
  682.     Log.Log(Format('%S.%S: The specified parent "%S" not found or invalid - discarding', [ClassName, 'LoadItem', AParent.Name]), lkError);
  683.     AParent := nil;
  684.   end;
  685.   if (AParent = nil) then begin
  686.     if Result.InheritsFrom(TRootItem) then begin
  687.       if Assigned(FRoot) then Log.Log(ClassName + '.LoadItem: replacing existing root item', lkWarning);
  688.       FRoot := Result as TRootItem;
  689.     end else begin
  690.       Log.Log(Format('%S.%S: A descendant of TRootItem expected but an item of class "%S" got. Using existing root item.', [ClassName, 'LoadItem', Result.ClassName]), lkWarning);
  691.       if Assigned(FRoot) then
  692.         AParent := FRoot else begin
  693.           ErrorHandler(TSceneLoadError.Create(Format('%S.%S: No root item.', [ClassName, 'LoadItem'])));
  694.           FreeAndNil(Result);
  695.           Exit;
  696.         end;
  697.     end;
  698.   end else if Result.InheritsFrom(TRootItem) then begin
  699.     Log.Log(ClassName + '.LoadItem: Additional root item found', lkNotice);
  700.   end;
  701.   if AParent <> nil then AParent.DoAddChild(Result);
  702.   SendMessage(ItemMsg.TInitMsg.Create, Result, [mfRecipient]);
  703.   if Result.Load(Stream) then SendMessage(ItemMsg.TAddToSceneMsg.Create(Result), Result, [mfCore, mfRecipient]) else Result := nil;
  704. //  if not Result.Load(Stream) then Result := nil;
  705. end;
  706. function TItemsManager.LoadScene(Stream: TStream): Boolean;
  707. var Item: TItem;
  708. begin
  709.   Result := False;
  710.   ClearItems;
  711.   Include(FState, imsLoading);
  712.   Item := LoadItem(Stream, nil);
  713.   Exclude(FState, imsLoading);
  714.   if Item is TRootItem then begin
  715.     FRoot := Item as TRootItem;
  716.     {$IFDEF LOGGING}
  717.     Log.Log(ClassName + '.LoadScene: Scene load successful', lkNotice);
  718.     {$ENDIF}    
  719.     SendMessage(ItemMsg.TSceneLoadedMsg.Create, nil, [mfBroadcast, mfCore]);
  720.     Result := True;
  721.   end;
  722. end;
  723. function TItemsManager.SaveScene(Stream: TStream): Boolean;
  724. begin
  725.   Result := FRoot.Save(Stream);
  726.   {$IFDEF LOGGING}
  727.   if Result then Log.Log(ClassName + '.SaveScene: Scene save successful', lkNotice);
  728.   {$ENDIF}
  729. end;
  730. procedure TItemsManager.CollectGarbage;
  731. var i: Integer; Items: TItems;
  732. begin
  733.   for i := 0 to FRoot.ExtractByMask([isRemoved], False, Items)-1 do begin
  734.     Items[i].Parent.RemoveChild(Items[i]);
  735.     if isReleased in Items[i].State then begin
  736.       Items[i].Free;
  737.     end;
  738.   end;
  739.   Items := nil;
  740. end;
  741. procedure TItemsManager.SetRoot(const Value: TRootItem);
  742. begin
  743.   FRoot := Value;
  744.   FRoot.FManager := Self;
  745. end;
  746. function TItemsManager.GetItemClass(Index: Integer): CItem;
  747. begin
  748.   Result := FItemClasses[Index];
  749. end;
  750. function TItemsManager.GetProcClassesEnum: AnsiString;
  751. var i: Integer;
  752. begin
  753.   Result := 'None';
  754. //  if (Parent <> nil) or (Root <> Self) then Exit;
  755.   for i := 0 to TotalProcessingClasses-1 do begin
  756.     Result := Result + '&' + IntToStrA(i) + ':';
  757.     if pfDeltaTimeBased in ProcessingClasses[i].Flags then
  758.       Result := Result + ' Delta time-based'
  759.     else
  760.       Result := Result + ' Every ' + IntToStrA(Round(ProcessingClasses[i].Interval * 1000)) + ' ms';
  761.     if pfIgnorePause in ProcessingClasses[i].Flags then Result := Result + ', ignore pause';
  762.   end;
  763. end;
  764. { TItem }
  765. type
  766.   TLinkParam = record
  767.     CachedProps: TProperties;                // Cached properties for object links management
  768.     LastCachedPropsClass: TClass;            // Item class of which properties last cached in TempProps
  769.     CurLinkIndex: Integer;                   // Index of current object link
  770.   end;
  771. threadvar
  772.   LinksParams: array of TLinkParam;
  773.   CurrentLinkParam: Integer;
  774. procedure NewLinksParameters;
  775. begin
  776.   SetLength(LinksParams, Length(LinksParams)+1);
  777.   LinksParams[High(LinksParams)].CachedProps := TProperties.Create;
  778. end;
  779. /// Changes childs's index to NewIndex, preserving the order of other childs
  780. /// NewIndex clamps if it is outside childs collection
  781. procedure TItem.ChangeChildIndex(Child: TItem; NewIndex: Integer);
  782. var i: Integer;
  783. begin
  784.   if not Assigned(FChilds) then Exit;
  785.   NewIndex := MaxI(0, MinI(TotalChilds-1, NewIndex));
  786.   if NewIndex = Child.IndexInParent then Exit;
  787. //  0  1  2  3  4  5  6  7
  788. //  0  1  4  2  3  5  6  7
  789. //  0  1  2  3  5  6  4  7
  790.   if NewIndex < Child.IndexInParent then begin
  791.     for i := Child.IndexInParent-1 downto NewIndex do begin
  792.       FChilds[i+1] := FChilds[i];
  793.       FChilds[i+1].IndexInParent := i+1;
  794.     end;
  795.   end else begin
  796.     for i := Child.IndexInParent to NewIndex-1 do begin
  797.       FChilds[i] := FChilds[i+1];
  798.       FChilds[i].IndexInParent := i;
  799.     end;
  800.   end;
  801.   FChilds[NewIndex]   := Child;
  802.   Child.IndexInParent := NewIndex;
  803. end;
  804. function TItem.GetLinkIndex(const AName: AnsiString): Integer;
  805. // Returns an index in ItemLinks array by property's name
  806. begin
  807.   for Result := 0 to High(ItemLinks) do
  808.     if ItemLinks[Result].PropName = AName then Exit;
  809.   Result := -1;
  810. end;
  811. function TItem.SetLinkedObjectByIndex(Index: Integer; Linked: TItem): Boolean;
  812. // Sets Linked as resolved linked object for LinkedObjects[Index]. Returns true if Linked passes type checking
  813. begin
  814.   Result := False;
  815.   if (Linked is ItemLinks[Index].BaseClass) then begin
  816.     ItemLinks[Index].Item := Linked;
  817.     ItemLinks[Index].ObjectName := '';                                             // Prevent unnecessary link resolution
  818.     Result := True;
  819.   end else begin
  820.     {$IFDEF LOGGING}
  821.     Log.Log(Format('%S("%S").%S: Item "%S" not found or not an instance of %S',
  822.             [ClassName, Name, 'SetLinkedObjectByIndex', ItemLinks[Index].ObjectName, ItemLinks[Index].BaseClass.ClassName]) , lkError);
  823.     {$ENDIF}
  824.   end;
  825. end;
  826. function TItem.ObtainLinkedItemNameByIndex(PropIndex: Integer): AnsiString;
  827. begin
  828.   if ItemLinks[PropIndex].Item is ItemLinks[PropIndex].BaseClass then begin
  829.     if lfAbsolute in ItemLinks[PropIndex].Flags then
  830.       Result := ItemLinks[PropIndex].Item.GetFullName else
  831.         Result := GetRelativeItemName(ItemLinks[PropIndex].Item.GetFullName);
  832.   end else Result := ItemLinks[PropIndex].ObjectName;
  833. end;
  834. function TItem.DoAddChild(AItem: TItem): TItem;
  835. begin
  836.   Inc(FTotalChilds);
  837.   if Length(FChilds) < TotalChilds then
  838.     SetLength(FChilds, Length(FChilds) + ChildsCapacityStep);
  839.   Result := SetChild(TotalChilds-1, AItem);
  840.   if Result = nil then begin
  841.     Dec(FTotalChilds);
  842.     {$IFDEF LOGGING} Log.Log(ClassName + '.DoAddChild: Error adding a child', lkError); {$ENDIF}
  843.   end;
  844. end;
  845. procedure TItem.SetState(const Value: TItemFlags);
  846. begin
  847. //  Root.IncludeItem(Self, Value - FTraverseMask);
  848. //  Root.ExcludeItem(Self, FTraverseMask - Value);
  849.   FState  := Value;
  850. //  if OldMask <> Value then Self.BroadcastMessage(TParentStateChangeMsg.Create(OldMask, Value));
  851. end;
  852. procedure TItem.SetParent(NewParent: TItem);
  853. begin
  854.   Assert(NewParent <> Self, 'Can''t attach an item to itself');
  855.   if FParent = NewParent then Exit;
  856.   if Assigned(FParent)   then FParent.RemoveChild(Self);
  857.   if Assigned(NewParent) then NewParent.AddChild(Self);
  858.   FParent := NewParent;
  859. end;
  860. procedure TItem.ClearParent;
  861. begin
  862.   FParent := nil;
  863. end;
  864. procedure TItem.SetManager(AManager: TItemsManager; SetChilds: Boolean);
  865. var i: Integer;
  866. begin
  867.   FManager := AManager;
  868.   if SetChilds then
  869.     for i := 0 to TotalChilds-1 do
  870.       Childs[i].SetManager(AManager, True);
  871. end;
  872. procedure TItem.AddItemLink(Properties: TProperties; const PropName: AnsiString; PropOptions: TPOptions; const BaseClass: AnsiString);
  873. var Index: Integer; Value: AnsiString;
  874. begin
  875.   Index := GetLinkIndex(PropName);
  876.   if Index = -1 then begin
  877.     SetLength(ItemLinks, Length(ItemLinks)+1);
  878.     Index := High(ItemLinks);
  879.     ItemLinks[Index].Flags      := [];
  880.     ItemLinks[Index].ObjectName := '';
  881.     ItemLinks[Index].Item       := nil;
  882.     ItemLinks[Index].PropName := PropName;
  883.   end;
  884.   if FManager = nil then
  885.     ItemLinks[Index].BaseClass := nil else
  886.       ItemLinks[Index].BaseClass := FManager.FindItemClass(BaseClass);
  887.   if ItemLinks[Index].BaseClass = nil then begin
  888.     Log.Log(ClassName + '.AddItemLink: Linked object base class "' + BaseClass + '" not found', lkError);
  889.     ItemLinks[Index].BaseClass := TItem;
  890.   end;
  891.   Value := ObtainLinkedItemNameByIndex(Index);
  892.   if Properties <> nil then Properties.Add(PropName, vtObjectLink, PropOptions, Value, BaseClass);
  893. end;
  894. procedure TItem.BuildItemLinks;
  895. begin
  896.   AddProperties(nil);
  897. end;
  898. function TItem.ResolveLink(const PropName: AnsiString; out Linked: TItem): Boolean;
  899. // Initializes ItemLinks[].Item and resets ItemLinks[].Name to empty string
  900. var Index: Integer;
  901. begin
  902.   Result := False;
  903.   Linked := nil;
  904.   if FManager = nil then begin
  905.     {$IFDEF LOGGING} Log.Log(ClassName + '.ResolveLink: Undefined items manager', lkError); {$ENDIF}
  906.     Exit;
  907.   end;
  908.   Index := GetLinkIndex(PropName);
  909.   if Index = -1 then Exit;
  910. //  Assert(Index <> -1, ClassName + '.ResolveLink: Invalid name "' + PropName + '"');
  911.   Linked := ItemLinks[Index].Item;
  912.   if (ItemLinks[Index].Item = nil) and (ItemLinks[Index].ObjectName <> '') and not FManager.IsSceneLoading then begin            // If link resolution is needed and possible
  913.     if ItemLinks[Index].ObjectName[1] = HierarchyDelimiter then begin
  914.       Include(ItemLinks[Index].Flags, lfAbsolute);
  915.       Linked := FManager.FRoot.GetItemByFullName(ItemLinks[Index].ObjectName);
  916.     end else begin
  917.       Exclude(ItemLinks[Index].Flags, lfAbsolute);
  918.       Linked := GetChildByPath(ItemLinks[Index].ObjectName);
  919.       if Linked = nil then begin
  920.         Linked := FManager.FRoot.GetItemByFullName('' + ItemLinks[Index].ObjectName);
  921.         Include(ItemLinks[Index].Flags, lfAbsolute);
  922.       end;
  923.     end;
  924.     
  925.     {if (Linked <> ObjectLinks[Index].Item) then }Result := SetLinkedObjectByIndex(Index, Linked);
  926.     if not Result then Linked := nil;
  927.   end;
  928. end;
  929. function TItem.SetLinkedObject(const PropName: AnsiString; Linked: TItem): Boolean;
  930. begin
  931.   Assert(GetLinkIndex(PropName) >= 0, Format('%S("%S").%S: Property "%S" not found', [ClassName, Name, 'SetLinkedObject', PropName]));
  932.   Result := SetLinkedObjectByIndex(GetLinkIndex(PropName), Linked);
  933. end;
  934. /// Sets an object link property to "unresolved" state.
  935. /// Returns True if the property is found False otherwise
  936. function TItem.SetLinkProperty(const AName, Value: AnsiString): Boolean;
  937. var Index: Integer;
  938. begin
  939.   Result := False;
  940.   Index := GetLinkIndex(AName);
  941.   Assert(Index <> -1, ClassName + '.SetLinkProperty: Invalid name: ' + AName);
  942.   if Index = -1 then begin
  943.     Log.Log(ClassName + '.SetLinkProperty: Object link property named "' + AName + '" not found', lkError);
  944.     Exit;
  945.   end;
  946.   Result := True;
  947.   ItemLinks[Index].ObjectName := Value;
  948.   ItemLinks[Index].Item       := nil;
  949. end;
  950. procedure TItem.ResolveLinks;
  951. begin
  952. end;
  953. constructor TItem.Create(AManager: TItemsManager);
  954. begin
  955. //  TPersistentObjectsPool.Create(64);
  956.   Name := AnsiString(Copy(ClassName, 2, Length(ClassName)));
  957.   FManager := AManager;
  958.   FChilds := nil; FTotalChilds := 0;
  959.   FParent := nil;
  960.   FState := [isNeedInit];
  961.   BuildItemLinks;
  962.   {$IFDEF DEBUGMODE}
  963.   FConsistent := True;
  964.   {$ENDIF}
  965. end;
  966. constructor TItem.Construct(AManager: TItemsManager);
  967. begin
  968.   Create(AManager);
  969. end;
  970. class function TItem.GetClass: CItem;
  971. begin
  972.   Result := Self;
  973. end;
  974. class function TItem.IsAbstract: Boolean;
  975. begin
  976.   Result := Self = TItem;
  977. end;
  978. function TItem.GetItemSize(CountChilds: Boolean): Integer;
  979. var i: Integer;
  980. begin
  981.   Result := InstanceSize + Length(FChilds) * SizeOf(TItem);
  982.   if Name <> '' then Inc(Result, Length(Name) * SizeOf(Name[1]));
  983.   Inc(Result, Length(ItemLinks) * SizeOf(TObjectLink));
  984.   for i := 0 to High(ItemLinks) do begin
  985.     if ItemLinks[i].PropName   <> '' then Inc(Result, Length(ItemLinks[i].PropName)   * SizeOf(ItemLinks[i].PropName[1]));
  986.     if ItemLinks[i].ObjectName <> '' then Inc(Result, Length(ItemLinks[i].ObjectName) * SizeOf(ItemLinks[i].ObjectName[1]));
  987.   end;
  988.   if CountChilds then for i := 0 to TotalChilds-1 do if Assigned(FChilds[i]) then Inc(Result, FChilds[i].GetItemSize(True));
  989. end;
  990. procedure TItem.HandleMessage(const Msg: TMessage);
  991. begin
  992.   if Msg.ClassType = ItemMsg.TInitMsg then
  993.     OnInit
  994.   else if Msg.ClassType = ItemMsg.TSceneLoadedMsg then begin
  995.     ResolveLinks();
  996.     OnSceneLoaded();
  997.   end else
  998.     if (Msg.ClassType = ItemMsg.TAddToSceneMsg) and (ItemMsg.TAddToSceneMsg(Msg).Item = Self) then OnSceneAdd()
  999.   else if (Msg.ClassType = ItemMsg.TRemoveFromSceneMsg) then begin
  1000.     if (ItemMsg.TRemoveFromSceneMsg(Msg).Item = Self) then
  1001.       OnSceneRemove()
  1002.     else if IsChildOf(ItemMsg.TRemoveFromSceneMsg(Msg).Item) then
  1003.       FManager.SendMessage(ItemMsg.TRemoveFromSceneMsg.Create(Self), nil, [mfBroadcast, mfCore]);
  1004.   end;
  1005. end;
  1006. procedure TItem.DoForAllChilds(Delegate: TDelegate; Data: Pointer);
  1007. var i: Integer;
  1008. begin
  1009.   Assert(Assigned(Delegate), ClassName + '.DoForAllChilds: Can''t call undefined method pointer');
  1010.   Delegate(Data);
  1011.   for i := 0 to TotalChilds-1 do DoForAllChilds(Delegate, Data);
  1012. end;
  1013. procedure TItem.BroadcastMessage(const Msg: TMessage);
  1014. var i: Integer;
  1015. begin
  1016.   if mfInvalid in Msg.Flags then Exit;
  1017.   Assert(mfBroadcast in Msg.Flags, 'Message is not for broadcast');
  1018.   HandleMessage(Msg);
  1019.   i := 0;
  1020.   while (i < TotalChilds) and not (mfInvalid in Msg.Flags) do begin
  1021.     FChilds[i].BroadcastMessage(Msg);
  1022.     Inc(i);
  1023.   end;
  1024. end;
  1025. procedure TItem.NotifyChilds(const Msg: TMessage);
  1026. var i: Integer;
  1027. begin
  1028.   Assert(mfChilds in Msg.Flags, 'TItem.NotifyChilds:Message is not for childs notification');
  1029.   for i := 0 to TotalChilds-1 do FChilds[i].HandleMessage(Msg);
  1030. end;
  1031. procedure TItem.OnInit;
  1032. begin
  1033. //  Assert(FManager <> nil, ClassName + '.OnInit: Manager is undefined');
  1034.   State := State - [isNeedInit];
  1035. end;
  1036. procedure TItem.OnSceneLoaded;
  1037. begin
  1038.   // All necessary work is done in main message handler (see @Link(TSceneLoadedMsg)) to avoid errors in client code (absent inherited call)
  1039. end;
  1040. procedure TItem.OnSceneAdd;
  1041. begin
  1042. end;
  1043. procedure TItem.OnSceneRemove;
  1044. var i: Integer;
  1045. begin
  1046.   for i := 0 to TotalChilds-1 do FChilds[i].OnSceneRemove;
  1047. end;
  1048. procedure TItem.GetProperties(const Result: TProperties);
  1049. begin
  1050.   LinksParams[CurrentLinkParam].CurLinkIndex := 0;                                 // Object links number
  1051. //  ItemLinks := nil;
  1052.   if not Assigned(Result) then begin
  1053.     {$IFDEF LOGGING} Log.Log(ClassName + '.GetProperties: Result should be initialized', lkError); {$ENDIF}
  1054. //    Exit;
  1055.   end else Result.Clear;
  1056.   AddProperties(Result);
  1057. end;
  1058. procedure TItem.AddProperties(const Result: TProperties);
  1059. var i: Integer;
  1060. begin
  1061.   if Result = nil then Exit;
  1062.   Result.Add('Name', vtString, [], Name, '');
  1063.   if FManager <> nil then begin
  1064.     for i := 0 to HiddenStates-1 do
  1065.       Result.Add('Traverse mask' + FManager.StateNames[i], vtBoolean, [poHidden], OnOffStr[i in FState], '');
  1066.     for i := HiddenStates to High(FManager.StateNames) do
  1067.       Result.Add('Traverse mask' + FManager.StateNames[i], vtBoolean, [],         OnOffStr[i in FState], '');
  1068.   end;
  1069. end;
  1070. procedure TItem.SetProperties(Properties: TProperties);
  1071. var i: Integer; NewState: TItemFlags;
  1072. begin
  1073.   LinksParams[CurrentLinkParam].CurLinkIndex := 0;                                 // Object links number
  1074.   if Properties.Valid('Name') then Name := Properties['Name'];
  1075.   NewState := FState;
  1076.   if FManager <> nil then for i := 0 to High(FManager.StateNames) do
  1077.     if Properties.Valid('Traverse mask' + FManager.StateNames[i]) then
  1078.       if Properties.GetAsInteger('Traverse mask' + FManager.StateNames[i]) > 0 then
  1079.         NewState := NewState + [i] else NewState := NewState - [i];
  1080.   State := NewState;
  1081. end;
  1082. function TItem.GetProperty(const AName: AnsiString): AnsiString;
  1083. var Garbage: IRefcountedContainer; Props: TProperties; Prop, LProp: PProperty;
  1084. begin
  1085.   Garbage := CreateRefcountedContainer;
  1086.   Props := TProperties.Create;
  1087.   Garbage.AddObject(Props);
  1088.   GetProperties(Props);
  1089.   Result := Props[AName];
  1090. end;
  1091. procedure TItem.SetProperty(const AName, AValue: AnsiString);
  1092. var Garbage: IRefcountedContainer; Props: TProperties; Prop, LProp: PProperty;
  1093. begin
  1094.   Garbage := CreateRefcountedContainer;
  1095.   Props := TProperties.Create;
  1096.   Garbage.AddObject(Props);
  1097.   GetProperties(Props);
  1098.   Prop := Props.GetProperty(AName);
  1099.   if Assigned(Prop) and ([poReadonly, poDerivative, poDecor] * Prop^.Options = []) then begin
  1100.     New(LProp);
  1101.     Garbage.AddPointer(LProp);
  1102.     CopyProperty(Prop^, LProp^);
  1103.     Props.Clear;
  1104.     Props.Add(AName, LProp^.ValueType, LProp^.Options, AValue, LProp^.Enumeration, LProp^.Description);
  1105.     SetProperties(Props);
  1106.   end else Log.Log(ClassName + '.SetProperty: Try to write to a non-existent or read-only property "' + AName + '"', lkWarning);
  1107. end;
  1108. procedure TItem.ObtainLinkedItemName(const PropName: AnsiString; out Result: AnsiString);
  1109. // Returns in the Result variable full name of linked item referenced by property with the given name without type checking
  1110. var Index: Integer;
  1111. begin
  1112.   Result := '';
  1113.   Index := GetLinkIndex(PropName);
  1114.   if Index = -1 then Exit;
  1115.   Result := ObtainLinkedItemNameByIndex(Index);
  1116. end;
  1117. function TItem.Clone: TItem;
  1118. var Props: TProperties;
  1119. begin
  1120.   Result := GetClass.Construct(FManager);
  1121.   if Assigned(FManager) then FManager.SendMessage(ItemMsg.TInitMsg.Create, Result, [mfRecipient]);
  1122.   Props := TProperties.Create;
  1123.   GetProperties(Props);
  1124.   Result.SetProperties(Props);
  1125.   Props.Free;
  1126. end;
  1127. function TItem.Save(Stream: TStream): Boolean;
  1128. var i: Integer; Properties: TProperties;
  1129. begin
  1130.   Result := SaveString(Stream, ClassName);
  1131.   Properties := TProperties.Create;
  1132.   GetProperties(Properties);
  1133.   Result := Result and Properties.Write(Stream);
  1134.   Properties.Free;
  1135.   Result := Result and
  1136.             Stream.WriteCheck(TotalChilds, SizeOf(TotalChilds));
  1137.   for i := 0 to TotalChilds-1 do if Assigned(Childs[i]) then
  1138.     Result := Result and Childs[i].Save(Stream);
  1139. end;
  1140. function TItem.Load(Stream: TStream): Boolean;
  1141. var i, ATotalChilds: Integer; Properties: TProperties;
  1142. begin
  1143.   {$IFDEF DEBUGLOG}
  1144.   Log.Log(ClassName + '.Load: Loading item "' + Name +'"');
  1145.   {$ENDIF}
  1146.   Properties := TProperties.Create;
  1147.   try
  1148.     Result := Properties.Read(Stream);
  1149.     SetProperties(Properties);
  1150.   finally
  1151.     Properties.Free;
  1152.   end;
  1153. //{$IFDEF DEBUGMODE} Assert(TotalChilds = 0, 'TItem.Load: TotalChilds should be zero'); {$ENDIF}
  1154.   Result := Result and Stream.ReadCheck(ATotalChilds, SizeOf(TotalChilds));
  1155. //  SetLength(FChilds, TotalChilds);
  1156.   for i := 0 to ATotalChilds-1 do Result := Result and (FManager.LoadItem(Stream, Self) <> nil);
  1157. end;
  1158. function TItem.GetChild(Index: Integer): TItem;
  1159. begin
  1160.   Result := FChilds[Index];
  1161. end;
  1162. function TItem.SetChild(Index: Integer; AItem: TItem): TItem;
  1163. begin
  1164.   Result := nil;
  1165.   if (Index < 0) or (Index >= TotalChilds) then Exit;
  1166.   Result := AItem;
  1167.   if (AItem = FChilds[Index]) then Exit;
  1168.   AItem.SetManager(FManager, True);
  1169.   AItem.FParent       := Self;
  1170.   AItem.IndexInParent := Index;
  1171. //  Root.IncludeItem(AItem, AItem.FTraverseMask);
  1172.   FChilds[Index] := AItem;
  1173. end;
  1174. procedure TItem.InsertChild(AItem: TItem; Index: Integer);
  1175. begin
  1176.   AddChild(AItem);
  1177.   ChangeChildIndex(AItem, Index);
  1178. end;
  1179. function TItem.AddChild(AItem: TItem): TItem;
  1180. begin
  1181.   Result := nil;
  1182.   Assert(not ((AItem = nil) or (AItem.IndexInParent >= 0) and (AItem.IndexInParent < TotalChilds) and (FChilds[AItem.IndexInParent] = AItem)));
  1183.   if (AItem = nil) or (AItem.IndexInParent >= 0) and (AItem.IndexInParent < TotalChilds) and (FChilds[AItem.IndexInParent] = AItem) then begin
  1184. {    if AItem = nil then
  1185.       Log.Log(' ****** AItem is nil')
  1186.     else
  1187.       Log.Log(Format(' ****** %s("%s"), IIp: %d, totchlds: %d, ', [AItem.ClassName, AItem.Name, AItem.IndexInParent, TotalChilds]));}
  1188.     Exit;
  1189.   end;
  1190. //  for i := 0 to TotalChilds-1 do if Childs[i] = AItem then Exit;
  1191.   Result := DoAddChild(AItem);
  1192.   if isNeedInit in AItem.FState then
  1193.     FManager.SendMessage(ItemMsg.TInitMsg.Create, AItem, [mfRecipient]);
  1194.   if Assigned(FManager) then
  1195.     FManager.SendMessage(ItemMsg.TAddToSceneMsg.Create(Result), nil, [mfBroadcast, mfCore]);
  1196. end;
  1197. procedure TItem.RemoveChildByIndex(Index: Integer);
  1198. begin
  1199.   Assert((Index >= 0) and (Index < TotalChilds));
  1200.   if Assigned(FManager) and not FManager.IsShuttingdown then                                        // Notify all items and subsystems
  1201.     FManager.SendMessage(ItemMsg.TRemoveFromSceneMsg.Create(FChilds[Index]), nil, [mfBroadcast, mfCore]);
  1202.   FChilds[Index].IndexInParent := -1;
  1203.   FChilds[Index].FParent       := nil;
  1204. //  FManager.FRoot.ExcludeItem(FChilds[Index], FChilds[Index].FTraverseMask);
  1205.   while Index < TotalChilds-1 do begin
  1206.     FChilds[Index] := FChilds[Index+1];
  1207.     FChilds[Index].IndexInParent := Index;
  1208.     Inc(Index);
  1209.   end;
  1210.   FChilds[Index] := nil;
  1211.   Dec(FTotalChilds);
  1212. end;
  1213. procedure TItem.RemoveChild(AItem: TItem);
  1214. var i: Integer;
  1215. begin
  1216.   if AItem = nil then Exit;
  1217.   Assert((AItem.IndexInParent >= 0) and (AItem.IndexInParent < TotalChilds) and (FChilds[AItem.IndexInParent] = AItem), ClassName + '.RemoveChild: AItem.Index is invalid: ' + IntToStr(AItem.IndexInParent));
  1218.   if (AItem.IndexInParent >= 0) and (AItem.IndexInParent < TotalChilds) and (FChilds[AItem.IndexInParent] = AItem) then begin
  1219.     RemoveChildByIndex(AItem.IndexInParent);
  1220.     Exit;
  1221.   end;
  1222.   {$IFDEF LOGGING}
  1223.     Log.Log(Format('%S.%S: "%S".Index is invalid: %D', [ClassName, 'RemoveChild', AItem.Name, AItem.IndexInParent]), lkWarning);
  1224.     Log.Log('  Searching for the item in parent''s childs collection...');
  1225.   {$ENDIF}
  1226.   i := 0;
  1227.   while i < TotalChilds do begin
  1228.     if FChilds[i] = AItem then begin
  1229.       RemoveChildByIndex(i);
  1230.       {$IFDEF LOGGING} Log.Log(Format('  The item found at index %D', [i]), lkWarning); {$ENDIF}
  1231.       Exit;
  1232.     end;
  1233.     Inc(i);
  1234.   end;
  1235.   {$IFDEF LOGGING} Log.Log(Format('  The item not found', []), lkError); {$ENDIF}
  1236. end;
  1237. function TItem.GetChilds: TItems;
  1238. begin
  1239.   Result := FChilds;
  1240. end;
  1241. function TItem.GetFullName: AnsiString;
  1242. var Item: TItem;
  1243. begin
  1244.   Result := HierarchyDelimiter + Name;
  1245.   Item := Self.Parent;
  1246.   while Item <> nil do begin
  1247.     Result := HierarchyDelimiter + Item.Name + Result;
  1248.     Item := Item.Parent;
  1249.   end;
  1250. end;
  1251. function TItem.GetChildByName(const AName: AnsiString; SearchChilds: Boolean): TItem;
  1252. var i: Integer;
  1253. begin
  1254.   Result := nil;
  1255.   for i := 0 to TotalChilds-1 do if Assigned(FChilds[i]) then begin
  1256.     if FChilds[i].Name = AName then begin
  1257.       Result := FChilds[i]; Exit;
  1258.     end;
  1259.   end;
  1260.   // Search in childs
  1261.   if SearchChilds then for i := 0 to TotalChilds-1 do if Assigned(FChilds[i]) then begin
  1262.     Result := FChilds[i].GetChildByName(AName, True);
  1263.     if Result <> nil then Exit;
  1264.   end;
  1265. end;
  1266. function TItem.GetChildByPath(const APath: AnsiString): TItem;
  1267. var Levels: TAnsiStringArray; i, TotalLevels: Integer;
  1268. begin
  1269.   TotalLevels := SplitA(APath, HierarchyDelimiter, Levels, False);
  1270.   i := 0;
  1271.   Result := Self;
  1272.   while (i < TotalLevels) and (Result <> nil) do begin
  1273.     if Levels[i] = ParentAdressName then
  1274.       Result := Result.Parent
  1275.     else
  1276.       Result := Result.GetChildByName(Levels[i], False);
  1277.     Inc(i);
  1278.   end;
  1279. end;
  1280. function TItem.GetItemByPath(const APath: AnsiString): TItem;
  1281. begin
  1282.   if APath = HierarchyDelimiter then
  1283.     Result := FManager.FRoot.GetItemByFullName(APath)
  1284.   else begin
  1285.     Result := GetChildByPath(APath);
  1286.     if Result = nil then
  1287.       Result := FManager.FRoot.GetItemByFullName('' + APath);
  1288.   end;
  1289. end;
  1290. procedure TItem.MoveChild(Child, Target: TItem; Mode: TItemMoveMode);
  1291. var LParent: TItem; Index: Integer;
  1292. begin
  1293.   if Child = nil then Exit;
  1294.   LParent := nil;
  1295.   
  1296.   case Mode of
  1297.     mmInsertBefore, mmInsertAfter:   LParent := Target.Parent;
  1298.     mmAddChildFirst, mmAddChildLast: LParent := Target;
  1299.     mmMoveUp, mmMoveDown:            LParent := Child.Parent;
  1300.     mmMoveLeft: if Assigned(Child.Parent) then LParent := Child.Parent.Parent;
  1301.     mmMoveRight: if Assigned(Child.Parent) then LParent := Child.Parent.GetNextChild(Child);
  1302.     else Assert(False, ClassName + '.MoveChild: Invalid mode');
  1303.   end;
  1304.   if LParent = nil then Exit;
  1305.   Child.SetParent(LParent);
  1306.   Index := 0;
  1307.   case Mode of
  1308.     mmInsertBefore: Index := Target.IndexInParent - Ord(Target.IndexInParent > Child.IndexInParent);
  1309.     mmInsertAfter:  Index := Target.IndexInParent + Ord(Target.IndexInParent < Child.IndexInParent);
  1310.     mmAddChildLast: Index := Target.TotalChilds;
  1311.     mmMoveUp:       Index := MinI(Child.Parent.TotalChilds, MaxI(0, Child.IndexInParent-1));
  1312.     mmMoveDown:     Index := MinI(Child.Parent.TotalChilds, MaxI(0, Child.IndexInParent+1));
  1313.     mmMoveLeft:     Index := Child.Parent.IndexInParent;
  1314.   end;
  1315.   LParent.ChangeChildIndex(Child, Index);
  1316. end;
  1317. function TItem.IsChildOf(AParent: TItem): Boolean;
  1318. var Item: TItem;
  1319. begin
  1320.   Item := Parent;
  1321.   while Assigned(Item) and (Item <> AParent) do Item := Item.Parent;
  1322.   Result := Assigned(Item);
  1323. end;
  1324. procedure TItem.MarkAsRemoved(DoNotRelease: Boolean);
  1325. begin
  1326.   if DoNotRelease then
  1327.     FState := FState + [isRemoved] else
  1328.       FState := FState + [isRemoved, isReleased];
  1329. end;
  1330. procedure TItem.FreeChilds;
  1331. var i: Integer; Item: TItem;
  1332. begin
  1333.   i := TotalChilds - 1;
  1334.   while i >= 0 do begin
  1335.     if Assigned(Childs[i]) then begin
  1336. //      Root.ExcludeItem(FChilds[i], FChilds[i].FTraverseMask);
  1337.       Item := FChilds[i];
  1338.       RemoveChildByIndex(i);
  1339.       Item.Free;
  1340.     end;
  1341.     Dec(i);  
  1342.   end;
  1343.   FChilds := nil; FTotalChilds := 0;
  1344. end;
  1345. procedure TItem.DeAlloc;
  1346. begin
  1347.   inherited Destroy;
  1348. end;
  1349. destructor TItem.Destroy;
  1350. begin
  1351.   FreeChilds;
  1352.   if Assigned(Parent) then Parent := nil;
  1353.   inherited;
  1354. end;
  1355. function TItem.GetNonDummyParent: TItem;
  1356. begin
  1357.   Result := Parent;
  1358.   while Result is TDummyItem do Result := Result.Parent;                        // Skip dummy items
  1359. end;
  1360. function TItem.FindNextChildInclDummy(var Current: TItem): Boolean;
  1361. var Ind: Integer; Done: Boolean;
  1362.   function GetNext(Par, Cur: TItem): TItem;
  1363.   begin
  1364.     repeat
  1365.       if Assigned(Cur) then Ind := Cur.IndexInParent else Ind := -1;
  1366.       Assert(Assigned(Par));
  1367.       if Ind < Par.TotalChilds-1 then begin       // Get next child
  1368.         Result := Par.Childs[Ind+1];
  1369.         if Result is TDummyItem then begin        // If it's dummy go through its childs
  1370.           Done := False;
  1371.           Par := Result;
  1372.           Cur := nil;
  1373.   //        Ind := -1;
  1374.         end else Done := True;
  1375.       end else begin                              // Reached end of the Par's childs
  1376.         Result := nil;
  1377.         Done := Par = Self;
  1378.         if not Done then begin                    //
  1379.           Cur := Par;
  1380.           Par := Par.Parent;        
  1381.         end;
  1382.       end;
  1383.     until Done;
  1384.   end;
  1385. begin
  1386.   if Assigned(Current) then
  1387.     Current := GetNext(Current.Parent, Current) else
  1388.       Current := GetNext(Self, Current);
  1389.   Result := Assigned(Current);
  1390. end;
  1391. function TItem.GetNextChild(CurrentChild: TItem): TItem;
  1392. begin
  1393.   Result := nil;
  1394.   if CurrentChild.IndexInParent < TotalChilds-1 then Result := Childs[CurrentChild.IndexInParent+1];
  1395. end;
  1396. function TItem.GetRelativeItemName(const AFullName: AnsiString): AnsiString;
  1397. var LevelsSelf, LevelsItem: TAnsiStringArray; i, TotalLevelsSelf, TotalLevelsItem, TotalLevelsMin, TotalLevelsEq: Integer;
  1398. begin
  1399. //   a0b0c0
  1400. //   a0b0c1d0
  1401. //   a0b0c0 ..c1d0
  1402.   TotalLevelsSelf := SplitA(GetFullName, HierarchyDelimiter, LevelsSelf, False);
  1403.   TotalLevelsItem := SplitA(AFullName,   HierarchyDelimiter, LevelsItem, False);
  1404.   TotalLevelsMin  := MinI(TotalLevelsSelf, TotalLevelsItem);
  1405.   i := 0;
  1406.   while (i < TotalLevelsMin) and (LevelsSelf[i] = LevelsItem[i]) do Inc(i);
  1407.   Result := '';
  1408.   TotalLevelsEq := i;
  1409.   for i := TotalLevelsSelf-1 downto TotalLevelsEq do Result := Result + ParentAdressName + HierarchyDelimiter;
  1410.   for i := TotalLevelsEq to TotalLevelsItem-1 do Result := Result + LevelsItem[i] + HierarchyDelimiter;
  1411. end;
  1412. procedure TItem.SendMessage(const Msg: TMessage; Recipient: TItem; Destination: TMessageFlags);
  1413. begin
  1414.   {$IFDEF DEBUGMODE} Assert(FConsistent); {$ENDIF}    // Do not send messages when in an invalid state
  1415.   if mfRecipient in Destination then Assert(Assigned(Recipient));
  1416.   if (mfChilds in Destination) and not Assigned(Recipient) then Recipient := Self;
  1417.   if Assigned(FManager) then
  1418.     FManager.SendMessage(Msg, Recipient, Destination)
  1419.   else
  1420.     Log.Log(Format('%S("%S").%S: Stand alone item sending a message of class "%S"', [ClassName, Name, 'SendMessage', Msg.ClassName]), lkWarning);
  1421. (*  Assert((Destination = [mdChilds]) or (Destination = [mdBroadcast]), 'Invalid destination');
  1422.   if mdChilds in Destination then begin
  1423.     Msg.Flags := Msg.Flags + [mfNotification];
  1424.     NotifyChilds(Msg);
  1425.   end else if mdBroadcast in Destination then begin
  1426.     Msg.Flags := Msg.Flags + [mfBroadcast];
  1427.     FManager.Root.BroadcastMessage(Msg);
  1428.   end;
  1429.   Assert(Assigned(Msg));
  1430.   Assert((Destination <> []), 'Invalid destination');
  1431.   if mdRecipient in Destination then Assert(Assigned(Recipient)) else Recipient := nil;
  1432.   if mdBroadcast in Destination then Msg.Flags := Msg.Flags + [mfBroadcast];
  1433.   if mdCore      in Destination then Msg.Flags := Msg.Flags + [mfCore];
  1434.   if mdChilds    in Destination then Msg.Flags := Msg.Flags + [mfNotification];
  1435.   if mdAsync in Destination then SendAsyncMessage(Msg, Recipient) else begin
  1436.     if mdRecipient in Destination then Recipient.HandleMessage(Msg);
  1437.     if mdCore      in Destination then HandleMessage(Msg);
  1438.     if mdChilds    in Destination then NotifyChilds(Msg);
  1439.     if (mdBroadcast in Destination) and Assigned(Root) then Root.BroadcastMessage(Msg);
  1440.   end; *)
  1441. end;
  1442. { TBaseProcessing }
  1443. procedure TBaseProcessing.ResetProcessedTime;
  1444. begin
  1445.   FTimeProcessed := 0;
  1446. end;
  1447. procedure TBaseProcessing.Process(const DeltaT: Float);
  1448. begin
  1449.   FTimeProcessed := FTimeProcessed + DeltaT;
  1450. end;
  1451. procedure TBaseProcessing.AddProperties(const Result: TProperties);
  1452. begin
  1453.   inherited;
  1454.   if not Assigned(Result) then Exit;
  1455.   Result.AddEnumerated('Processing class', [], ProcessingClass+1, FManager.GetProcClassesEnum);
  1456. end;
  1457. procedure TBaseProcessing.SetProperties(Properties: TProperties);
  1458. begin
  1459.   inherited;
  1460.   if Properties.Valid('Processing class') then ProcessingClass := Properties.GetAsInteger('Processing class')-1;
  1461. end;
  1462. procedure TBaseProcessing.Pause;
  1463. begin
  1464.   State := FState - [isProcessing];
  1465. end;
  1466. procedure TBaseProcessing.Resume;
  1467. begin
  1468.   State := FState + [isProcessing];
  1469. end;
  1470. { TDummyItem }
  1471. procedure TDummyItem.HandleMessage(const Msg: TMessage);
  1472. begin
  1473.   inherited;
  1474.   if (mfChilds in Msg.Flags)// and
  1475. //     (Msg.ClassType <> ItemMsg.TInitMsg) and (Msg.ClassType <> ItemMsg.TSceneLoadedMsg)
  1476.      then
  1477.     NotifyChilds(Msg);
  1478. end;
  1479. { TRootItem }
  1480. constructor TRootItem.Create(AManager: TItemsManager);
  1481. begin
  1482.   inherited;
  1483. end;
  1484. function TRootItem.GetItemByFullName(const AName: AnsiString): TItem;
  1485. var Levels: TAnsiStringArray; i, TotalLevels: Integer; Item: TItem;
  1486. begin
  1487.   Result := nil;
  1488.   TotalLevels := SplitA(AName, HierarchyDelimiter, Levels, False);
  1489.   if Name <> Levels[0] then Exit;
  1490.   Item := Self;
  1491.   for i := 1 to TotalLevels-1 do begin
  1492.     Item := Item.GetChildByName(Levels[i], False);
  1493.     if Item = nil then Exit;
  1494.   end;
  1495.   Result := Item;
  1496. end;
  1497. function TRootItem.Extract(Condition: TExtractConditionFunc; out Items: TItems): Integer;
  1498. var LastCond: TExtractCondition;
  1499.   procedure TraverseExtract(Item: TItem);
  1500.   var i: Integer;
  1501.   begin
  1502.     LastCond := Condition(Item);
  1503.     if ecPassed in LastCond then begin
  1504.       if Length(Items) <= Result then SetLength(Items, Length(Items) + ItemsCapacityStep);
  1505.       Items[Result] := Item;
  1506.       Inc(Result);
  1507.     end;
  1508.     if not (ecBreakHierarchy in LastCond) and not (ecBreak in LastCond) then for i := 0 to Item.TotalChilds-1 do begin
  1509.       {$IFDEF DEBUGMODE}
  1510.       Assert(Item.Childs[i] <> nil, 'TRootItem.Extract.TraverseExtract: Childs[i] cannot be nil');
  1511.       {$ENDIF}
  1512.       TraverseExtract(Item.Childs[i]);
  1513.       if ecBreak in LastCond then Exit;
  1514.     end;
  1515.   end;
  1516. var i: Integer;
  1517. begin
  1518.   Result := 0;
  1519.   for i := 0 to TotalChilds-1 do begin
  1520.     {$IFDEF DEBUGMODE}
  1521.     Assert(Childs[i] <> nil, 'TRootItem.Extract: Childs[i] cannot be nil');
  1522.     {$ENDIF}
  1523.     TraverseExtract(Childs[i]);
  1524.   end;
  1525.   {$IFDEF DEBUGMODE}
  1526.   for i := Result to High(Items) do Items[i] := nil;
  1527.   {$ENDIF}
  1528. end;
  1529. function TRootItem.ExtractByMask(Mask: TItemFlags; Hierarchical: Boolean; out Items: TItems): Integer;
  1530.   procedure TraverseExtract(Item: TItem);
  1531.   var i: Integer;
  1532.   begin
  1533.     if Item.FState >= Mask then begin
  1534.       if Length(Items) <= Result then SetLength(Items, Length(Items) + ItemsCapacityStep);
  1535.       Items[Result] := Item;
  1536.       Inc(Result);
  1537.     end;
  1538.     for i := 0 to Item.TotalChilds-1 do begin
  1539.       {$IFDEF DEBUGMODE}
  1540.       Assert(Item.Childs[i] <> nil, 'TRootItem.Extract.TraverseExtract: Childs[i] cannot be nil');
  1541.       {$ENDIF}
  1542.       TraverseExtract(Item.Childs[i]);
  1543.     end;
  1544.   end;
  1545.   procedure TraverseExtractH(Item: TItem);
  1546.   var i: Integer;
  1547.   begin
  1548.     if Item.FState >= Mask then begin
  1549.       if Length(Items) <= Result then SetLength(Items, Length(Items) + ItemsCapacityStep);
  1550.       Items[Result] := Item;
  1551.       Inc(Result);
  1552.     end else if not (Item is TDummyItem) then Exit;
  1553.     for i := 0 to Item.TotalChilds-1 do begin
  1554.       {$IFDEF DEBUGMODE}
  1555.       Assert(Item.Childs[i] <> nil, 'TRootItem.Extract.TraverseExtract: Childs[i] cannot be nil');
  1556.       {$ENDIF}
  1557.       TraverseExtractH(Item.Childs[i]);
  1558.     end;
  1559.   end;
  1560. var i: Integer;
  1561. begin
  1562.   Result := 0;
  1563.   for i := 0 to TotalChilds-1 do begin
  1564.     {$IFDEF DEBUGMODE}
  1565.     Assert(Childs[i] <> nil, 'TRootItem.Extract: Childs[i] cannot be nil');
  1566.     {$ENDIF}
  1567.     if Hierarchical then TraverseExtractH(Childs[i]) else TraverseExtract(Childs[i]);
  1568.   end;
  1569.   {$IFDEF DEBUGMODE}
  1570.   SetLength(Items, Result);
  1571.   {$ENDIF}
  1572. end;
  1573. function TRootItem.ExtractByClass(AClass: CItem; out Items: TItems): Integer;
  1574. // Traverses through the items hierarchy and adds all items matching Mask to Items
  1575.   procedure TraverseExtract(Item: TItem);
  1576.   var i: Integer;
  1577.   begin
  1578.     if Item is AClass then begin
  1579.       if Length(Items) <= Result then SetLength(Items, Length(Items) + ItemsCapacityStep);
  1580.       Items[Result] := Item;
  1581.       Inc(Result);
  1582.     end;// else if not (Item is TDummyItem) then Exit;
  1583.     for i := 0 to Item.TotalChilds-1 do begin
  1584.       {$IFDEF DEBUGMODE}
  1585.       Assert(Item.Childs[i] <> nil, 'TRootItem.Extract.TraverseExtract: Childs[i] cannot be nil');
  1586.       {$ENDIF}
  1587.       TraverseExtract(Item.Childs[i]);
  1588.     end;
  1589.   end;
  1590. var i: Integer;
  1591. begin
  1592.   Result := 0;
  1593.   for i := 0 to TotalChilds-1 do begin
  1594.     {$IFDEF DEBUGMODE}
  1595.     Assert(Childs[i] <> nil, 'TRootItem.Extract: Childs[i] cannot be nil');
  1596.     {$ENDIF}
  1597.     TraverseExtract(Childs[i]);
  1598.   end;
  1599.   {$IFDEF DEBUGMODE}
  1600.   SetLength(Items, Result);
  1601.   {$ENDIF}
  1602. end;
  1603. function TRootItem.ExtractByMaskClass(Mask: TItemFlags; AClass: CItem; out Items: TItems): Integer;
  1604. var i: Integer;
  1605. begin
  1606.   Result := 0;
  1607.   for i := 0 to ExtractByMask(Mask, True, Items)-1 do
  1608.     if Items[i] is AClass then begin
  1609.       Items[Result] := Items[i];
  1610.       Inc(Result);
  1611.     end;
  1612.   {$IFDEF DEBUGMODE}
  1613.   SetLength(Items, Result);
  1614.   {$ENDIF}
  1615. end;
  1616. procedure TRootItem.HandleMessage(const Msg: TMessage);
  1617. begin
  1618.   inherited;
  1619. end;
  1620. { TClassesList }
  1621. function TClassesList.GetClasses: TClassArray;
  1622. var i: Integer;
  1623. begin
  1624.   SetLength(Result, TotalClasses);
  1625.   for i := 0 to TotalClasses-1 do Result[i] := FClasses[i].ItemClass;
  1626. end;
  1627. function TClassesList.GetClassesByModule(const AModuleName: TShortName): TClassArray;
  1628. var i: Integer;
  1629. begin
  1630.   SetLength(Result, 0);
  1631.   for i := 0 to TotalClasses-1 do
  1632.     if FClasses[i].ModuleName = AModuleName then begin
  1633.       SetLength(Result, Length(Result)+1);
  1634.       Result[High(Result)] := FClasses[i].ItemClass;
  1635.     end;
  1636. end;
  1637. destructor TClassesList.Destroy;
  1638. begin
  1639.   SetLength(FClasses, 0);
  1640.   inherited;
  1641. end;
  1642. procedure TClassesList.Add(const AModuleName: TShortName; AClass: TClass);
  1643. begin
  1644.   if Length(FClasses) <= TotalClasses then
  1645.     SetLength(FClasses, Length(FClasses) + CollectionsCapacityStep);
  1646.   Inc(TotalClasses);
  1647. end;
  1648. procedure TClassesList.Add(const AModuleName: TShortName; AClasses: array of TClass);
  1649. var i: Integer;
  1650. begin
  1651.   if Length(FClasses) < TotalClasses + Length(AClasses) then
  1652.     SetLength(FClasses, TotalClasses + Length(AClasses));
  1653.   for i := 0 to High(AClasses) do begin
  1654.     FClasses[TotalClasses + i].ItemClass  := AClasses[i];
  1655.     FClasses[TotalClasses + i].ModuleName := AModuleName;
  1656.   end;
  1657.   Inc(TotalClasses, Length(AClasses));
  1658. end;
  1659. function TClassesList.ClassExists(AClass: TClass): Boolean;
  1660. begin
  1661.   Result := FindClass(AClass).ItemClass <> nil;
  1662. end;
  1663. function TClassesList.FindClass(AClass: TClass): TClassRec;
  1664. var i: Integer;
  1665. begin
  1666.   Result.ItemClass  := nil;
  1667.   Result.ModuleName := '';
  1668.   i := TotalClasses-1;
  1669.   while (i >= 0) and (FClasses[i].ItemClass <> AClass) do Dec(i);
  1670.   if i >= 0 then begin
  1671.     Result.ItemClass  := FClasses[i].ItemClass;
  1672.     Result.ModuleName := FClasses[i].ModuleName;
  1673.   end;
  1674. end;
  1675. function TClassesList.FindClassByName(const AModuleName, AClassName: TShortName): TClassRec;
  1676. var i: Integer;
  1677. begin
  1678.   Result.ItemClass  := nil;
  1679.   Result.ModuleName := '';
  1680.   i := TotalClasses-1;
  1681.   while (i >= 0) and
  1682.         ( (FClasses[i].ItemClass.ClassName <> AClassName) or (FClasses[i].ModuleName <> AModuleName) ) do Dec(i);
  1683.   if i >= 0 then begin
  1684.     Result.ItemClass  := FClasses[i].ItemClass;
  1685.     Result.ModuleName := FClasses[i].ModuleName;
  1686.   end;
  1687. end;
  1688. { TSyncItem }
  1689. procedure TSyncItem.SetState(const Value: TItemFlags);
  1690. var OldState: TItemFlags;
  1691. begin
  1692.   OldState := FState;
  1693.   inherited;
  1694.   if not (isProcessing in OldState) and (isProcessing in Value) then Syncronize();
  1695. end;
  1696. procedure TSyncItem.Syncronize;
  1697. begin
  1698.   Parent.SendMessage(TSyncTimeMsg.Create(), Self, [mfBroadcast]);
  1699. end;
  1700. initialization
  1701.   GlobalClassList := TClassesList.Create;
  1702.   NewLinksParameters;
  1703. finalization
  1704.   CurrentLinkParam := High(LinksParams);
  1705.   while CurrentLinkParam >= 0 do begin
  1706.     LinksParams[CurrentLinkParam].CachedProps.Free;
  1707.     Dec(CurrentLinkParam);
  1708.   end;
  1709.   LinksParams := nil;
  1710. end.