Don't like ads? PRO users don't see any ads ;-)
Guest

Untitled

By: a guest on Jun 27th, 2012  |  syntax: ActionScript 3  |  size: 14.14 KB  |  hits: 19  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. package com.pblabs.core
  2. {
  3.     import com.pblabs.pb_internal;
  4.     import com.pblabs.property.PropertyManager;
  5.     import com.pblabs.util.TypeUtility;
  6.    
  7.     import flash.utils.Dictionary;
  8.     import com.pblabs.eh.*;
  9.  
  10.     use namespace pb_internal;
  11.    
  12.     /**
  13.      * Container class for PBComponent. Most game objects are made by
  14.      * instantiating PBGameObject and filling it with one or more PBComponent
  15.      * instances.
  16.      */
  17.     public class PBGameObject extends PBObject
  18.     {
  19.         private var _deferring:Boolean = true;
  20.         private var _components:Dictionary = new Dictionary();
  21.         private var componentHead:PBComponent;
  22.  
  23.         /**
  24.          * [CH] The core that this group is a part of. While its possible for
  25.          * a group such as a prefab to have multiple cores, there is always only
  26.          * one 'owning' core.
  27.         **/
  28.         public var core:PBCore;
  29.  
  30.  
  31.         public function PBGameObject(_name:String = null)
  32.         {
  33.             super(_name);
  34.         }
  35.  
  36.         public function get deferring():Boolean
  37.         {
  38.             return _deferring;
  39.         }
  40.        
  41.         /**
  42.          * If true, then components that are added aren't registered until
  43.          * deferring is set to false. This is used when you are adding a lot of
  44.          * components, or you are adding components with cyclical dependencies
  45.          * and need them to all be present on the PBGameObject before their
  46.          * onAdd methods are called.
  47.          */
  48.         public function set deferring(value:Boolean):void
  49.         {
  50.             /**
  51.              * [CH] I'm commenting this out and removing the deferral code in
  52.              * general. I'm not really sure what value this presents at this time.
  53.              * I think that the 'initialize' code here initializes the components
  54.              * without the addition behaviors with ! in front of them, etc.
  55.             **/
  56.             /*
  57.             if(_deferring && value == false)
  58.             {
  59.                 // Loop as long as we keep finding deferred stuff, the
  60.                 // dictionary delete operations can mess up ordering so we have
  61.                 // to check to avoid missing stuff. This is a little lame but
  62.                 // our previous implementation involved allocating lots of
  63.                 // temporary helper objects, which this avoids, so there you go.
  64.                 var foundDeferred:Boolean = true;
  65.                
  66.                 while(foundDeferred)
  67.                 {
  68.                     foundDeferred = false;
  69.                    
  70.                     // Initialize deferred components.
  71.                     for(var key:String in _components)
  72.                     {
  73.                         // Normal entries just have alphanumeric.
  74.                         if(key.charAt(0) != "!")
  75.                             continue;
  76.                        
  77.                         // It's a deferral, so init it...
  78.                         doInitialize(_components[key] as PBComponent);
  79.                        
  80.                         // ... and nuke the entry.
  81.                         _components[key] = null;
  82.                         delete _components[key];
  83.                        
  84.                         // Indicate we found stuff so keep looking. Otherwise
  85.                         // we may miss some.
  86.                         foundDeferred = true;
  87.                     }
  88.                 }
  89.             }
  90.             */
  91.            
  92.             _deferring = value;
  93.         }
  94.  
  95.         /**
  96.          * [CH] Not really proud of this code. Perhaps there's a better way.
  97.          * Essentially this is a hairy linked list priority queue. Why didn't I
  98.          * use simple priority queue? I thought the allocs would be too much.
  99.          * I mean, do I need to alloc an obj, dictionary, *AND* array in order
  100.          * to create a linked priority queue? Hell no!
  101.          *
  102.         **/
  103.         public function sortComponents():void
  104.         {
  105.             //trace("[CH] sortComponents "+this.name+" ---------------");
  106.            
  107.             componentHead = null;
  108.             for(var key:String in _components){
  109.                 // [CH] only sort items without a ! in front...lame.
  110.                 //if(key.charAt(0) != "!")
  111.                 //            continue;
  112.  
  113.                 var component:PBComponent = _components[key];
  114.                 //trace("[CH] attempting to insert:"+component.name);
  115.                 if(componentHead == null){
  116.                     // [CH] Init our head.
  117.                     //trace("[CH] setting the head to "+component.name);
  118.                     componentHead = component;
  119.                     componentHead.next = null;
  120.                 }else{
  121.                     // [CH] Find our spot in the list.
  122.                     var done:Boolean = false;
  123.                     var c2:PBComponent = componentHead;
  124.                     var c1:PBComponent = null;
  125.                     while(!done){
  126.                         if(component.priority <= c2.priority)
  127.                         {
  128.                             //trace("[CH] adding before "+c2.name);
  129.                             component.next = c2;
  130.                             if(c1){
  131.                                 //trace("[CH] adding in front of "+c1.name);
  132.                                 c1.next = component;
  133.                             }
  134.                             if(c2 == componentHead){
  135.                                 //trace("[CH] setting to component head.");
  136.                                 componentHead = component;
  137.                             }
  138.                             break;
  139.                         }else{
  140.                             // [CH] priority is higher. are we at the end?
  141.                             if(c2.next == null){
  142.                                 //trace("[CH] end of list, adding as tail");
  143.                                 // [CH] we're the highest.
  144.                                 c2.next = component;
  145.                                 component.next = null;
  146.                                 break;
  147.                             }
  148.                         }
  149.                         c1 = c2;
  150.                         c2 = c2.next;
  151.                         if(c2 == c2.next){
  152.                             done = true;
  153.                             //trace("[CH] We set a circular loop!"+c2);
  154.                         }
  155.                         //trace("[CH] moving on, next item: "+c2.name);
  156.  
  157.                         if(c2 == null){
  158.                             //trace("[CH] end of list??");
  159.                             break;
  160.                         }
  161.                     }
  162.                 }
  163.             }
  164.             /**
  165.              * [CH] This can be pretty useful for debugging but ridiculously
  166.              * spammey.
  167.             **/
  168.            //this.outputComponentList();
  169.         }
  170.  
  171.         public function outputComponentList():void
  172.         {
  173.             var c:PBComponent = componentHead;
  174.             var str:String = "";
  175.             while(c != null){
  176.                 str += c.name + ":"+c.priority+",";
  177.                 c = c.next;
  178.             }
  179.             trace("[CH] components by priority:"+str);
  180.         }
  181.        
  182.         protected function doInitialize(component:PBComponent):void
  183.         {
  184.             component._owner = this;
  185.             owningGroup.injectInto(component);
  186.             component.doAdd();
  187.         }
  188.        
  189.         /**
  190.          * Add a component to the PBGameObject. Subject to the deferring flag,
  191.          * the component will be initialized immediately.
  192.          *
  193.          * If there is a public var on this PBGameObject (ie, you've subclassed
  194.          * PBGameObject) with the same name as the component has, it will be
  195.          * populated with a reference to the component. This way you can get
  196.          * typed access to components on your game objects.
  197.          */
  198.         public function addComponent(component:PBComponent, name:String = null):void
  199.         {
  200.             if(name)
  201.                 component.name = name;
  202.            
  203.             if(component.name == null || component.name == "")
  204.                 throw new Error("Can't add component with no name.");
  205.            
  206.             // Stuff in dictionary.
  207.             _components[component.name] = component;
  208.            
  209.             // Set component owner.
  210.             component._owner = this;
  211.            
  212.             // Directly set field if present.
  213.             if(hasOwnProperty(component.name))
  214.                 this[component.name] = component;
  215.            
  216.             // Defer or add now.
  217.             if(_deferring){
  218.                 //_components["!" + component.name] = component;
  219.             }
  220.             else{
  221.                 doInitialize(component);
  222.             }
  223.         }
  224.        
  225.         /**
  226.          * Remove a component from this game object.
  227.          */
  228.         public function removeComponent(component:PBComponent):void
  229.         {
  230.             if(component.owner != this)
  231.                 throw new Error("Tried to remove a component that does not belong to this PBGameObject.");
  232.            
  233.             if(this.hasOwnProperty(component.name) && this[component.name] == component)
  234.                 this[component.name] = null;
  235.            
  236.             _components[component.name] = null;
  237.             delete _components[component.name];            
  238.             component.doRemove();
  239.             component._owner = null;
  240.         }
  241.        
  242.         /**
  243.          * Look up a component by name.
  244.          */
  245.         public function lookupComponent(name:String):*
  246.         {
  247.             return _components[name] as PBComponent;
  248.         }
  249.        
  250.         /**
  251.          * Get a fresh Vector with references to all the components in this
  252.          * game object.
  253.          */
  254.         public function getAllComponents():Vector.<PBComponent>
  255.         {
  256.             var out:Vector.<PBComponent> = new Vector.<PBComponent>();
  257.             for(var key:String in _components)
  258.                 out.push(_components[key]);
  259.             return out;
  260.         }
  261.        
  262.         /**
  263.          * Initialize the game object! This is done in a couple of stages.
  264.          *
  265.          * First, the PBObject initialization is performed.
  266.          * Second, we look for any components in public vars on the PBGameObject.
  267.          * This allows you to get at them by direct typed references instead of
  268.          * doing lookups. If we find any, we add them to the game object.
  269.          * Third, we turn off the deferring flag, so any components you've added
  270.          * via addComponent get initialized.
  271.          * Fourth, dependency injection is performed on ourselves and our components.
  272.          * Finally, we call applyBindings to make sure we have the latest data
  273.          * for any registered data bindings.
  274.          */
  275.         public override function initialize():void
  276.         {
  277.             super.initialize();
  278.            
  279.  
  280.             /**
  281.              * [CH] We're assuming that we've got to sort the components and
  282.              * initialize based upon their priority. After initialization, priority
  283.              * really doesn't mean anything.
  284.             **/
  285.             this.sortComponents();
  286.             var nc:PBComponent = this.componentHead;
  287.             //for each(var key:String in TypeUtility.getListOfPublicFields(this))
  288.             while(nc != null)
  289.             {
  290.                 // Only consider components.
  291.                 //if(!(this[key] is PBComponent))
  292.                 //    continue;
  293.                
  294.                 // Don't double initialize.
  295.                 //if(this[key].owner != null)
  296.                 //    continue;
  297.                
  298.                 // OK, add the component.
  299.                 //const nc:PBComponent = this[key] as PBComponent;
  300.                
  301.                 //if(nc.name != null && nc.name != "" && nc.name != key)
  302.                 //    throw new Error("PBComponent has name '" + nc.name + "' but is set into field named '" + key + "', these need to match!");
  303.                
  304.                 //nc.name = key;
  305.                 doInitialize(nc);
  306.                 nc = nc.next;
  307.             }
  308.            
  309.             /**
  310.              * [CH] The following is currently turned off, with the expectation that
  311.              * the PBGroup object will be used, and this object is an abstract
  312.              * base class.
  313.             **/
  314.             // Inject ourselves.
  315.             //owningGroup.injectInto(this);
  316.            
  317.             // Stop deferring and let init happen.
  318.             deferring = false;
  319.            
  320.             // Propagate bindings on everything.
  321.             nc = componentHead;
  322.             while(nc != null)
  323.             //for(var key2:String in _components)
  324.             {
  325.                 if(!nc.propertyManager)
  326.                     throw new Error("Failed to inject component properly.");
  327.                 nc.applyBindings();
  328.                 nc = nc.next;
  329.             }
  330.         }
  331.        
  332.         /**
  333.          * Removes any components on this game object, then does normal PBObject
  334.          * destruction (ie, remove from any groups or sets).
  335.          */
  336.         public override function destroy():void
  337.         {
  338.             for(var key:String in _components)
  339.                 removeComponent(_components[key]);
  340.            
  341.             super.destroy();
  342.         }
  343.        
  344.         /**
  345.          * Get a value from this game object in a data driven way.
  346.          * @param property Property string to look up, ie "@componentName.fieldName"
  347.          * @param defaultValue A default value to return if the desired property is absent.
  348.          */
  349.         public function getProperty(property:String, defaultValue:* = null):*
  350.         {
  351.             return owningGroup.getManager(PropertyManager).getProperty(this, property, defaultValue);
  352.         }
  353.        
  354.         /**
  355.          * Set a value on this game object in a data driven way.
  356.          * @param property Property string to look up, ie "@componentName.fieldName"
  357.          * @param value Value to set if the property is found.
  358.          */
  359.         public function setProperty(property:String, value:*):void
  360.         {
  361.             owningGroup.getManager(PropertyManager).setProperty(this, property, value);            
  362.         }
  363.     }
  364. }