|
@@ -1,16 +1,50 @@
|
|
|
package hxd.prefab;
|
|
|
|
|
|
+/**
|
|
|
+ Prefab is an data-oriented tree container capable of creating instances of Heaps objects.
|
|
|
+**/
|
|
|
@:keepSub
|
|
|
class Prefab {
|
|
|
|
|
|
+ /**
|
|
|
+ The type of prefab, allows to identify which class it should be loaded with.
|
|
|
+ **/
|
|
|
public var type(default, null) : String;
|
|
|
+
|
|
|
+ /**
|
|
|
+ The name of the prefab in the tree view
|
|
|
+ **/
|
|
|
public var name(default, set) : String;
|
|
|
+
|
|
|
+ /**
|
|
|
+ The parent of the prefab in the tree view
|
|
|
+ **/
|
|
|
public var parent(default, set) : Prefab;
|
|
|
+
|
|
|
+ /**
|
|
|
+ The associated source file (an image, a 3D model, etc.) if the prefab type needs it.
|
|
|
+ **/
|
|
|
public var source(default, set) : String;
|
|
|
+
|
|
|
+ /**
|
|
|
+ The list of children prefab in the tree view
|
|
|
+ **/
|
|
|
public var children(default, null) : Array<Prefab>;
|
|
|
+
|
|
|
+ /**
|
|
|
+ Tells if the prefab will create an instance when calling make() or be ignored. Also apply to this prefab children.
|
|
|
+ **/
|
|
|
public var enabled : Bool = true;
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ A storage for some extra properties
|
|
|
+ **/
|
|
|
public var props : Any;
|
|
|
|
|
|
+ /**
|
|
|
+ Creates a new prefab with the given parent.
|
|
|
+ **/
|
|
|
public function new(?parent) {
|
|
|
this.parent = parent;
|
|
|
children = [];
|
|
@@ -34,13 +68,23 @@ class Prefab {
|
|
|
}
|
|
|
|
|
|
#if editor
|
|
|
+
|
|
|
+ /**
|
|
|
+ Allows to customize how the prefab object is edited within Hide
|
|
|
+ **/
|
|
|
public function edit( ctx : hide.prefab.EditContext ) {
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ Allows to customize how the prefab object is displayed / handled within Hide
|
|
|
+ **/
|
|
|
public function getHideProps() : hide.prefab.HideProps {
|
|
|
return { icon : "question-circle", name : "Unknown" };
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ Allows to customize how the prefab instance changes when selected/unselected within Hide
|
|
|
+ **/
|
|
|
public function setSelected( ctx : hide.prefab.Context, b : Bool ) {
|
|
|
var materials = ctx.shared.getMaterials(this);
|
|
|
|
|
@@ -62,31 +106,55 @@ class Prefab {
|
|
|
}
|
|
|
#end
|
|
|
|
|
|
+ /**
|
|
|
+ Iterate over children prefab
|
|
|
+ **/
|
|
|
public inline function iterator() : Iterator<Prefab> {
|
|
|
return children.iterator();
|
|
|
}
|
|
|
|
|
|
- public function load( v : Dynamic ) {
|
|
|
+ /**
|
|
|
+ Override to implement your custom prefab data loading
|
|
|
+ **/
|
|
|
+ function load( v : Dynamic ) {
|
|
|
throw "Not implemented";
|
|
|
}
|
|
|
|
|
|
- public function save() : {} {
|
|
|
+ /**
|
|
|
+ Override to implement your custom prefab data saving
|
|
|
+ **/
|
|
|
+ function save() : {} {
|
|
|
throw "Not implemented";
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ Creates an instance for this prefab only (and not its children).
|
|
|
+ Use make(ctx) to creates the whole instances tree;
|
|
|
+ **/
|
|
|
public function makeInstance( ctx : Context ) : Context {
|
|
|
return ctx;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ Allows to customize how an instance gets updated when a property name changes.
|
|
|
+ You can also call updateInstance(ctx) in order to force whole instance synchronization against current prefab data.
|
|
|
+ **/
|
|
|
public function updateInstance( ctx : Context, ?propName : String ) {
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ Removes the created instance for this prefab only (not is children).
|
|
|
+ If false is returned, the instance could not be removed and the whole context scene needs to be rebuilt
|
|
|
+ **/
|
|
|
public function removeInstance( ctx : Context ) : Bool {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- public function saveRec() : {} {
|
|
|
+ /**
|
|
|
+ Save the whole prefab data and its children.
|
|
|
+ **/
|
|
|
+ @:final public function saveData() : {} {
|
|
|
var obj : Dynamic = save();
|
|
|
obj.type = type;
|
|
|
if( !enabled )
|
|
@@ -96,13 +164,38 @@ class Prefab {
|
|
|
if( source != null )
|
|
|
obj.source = source;
|
|
|
if( children.length > 0 )
|
|
|
- obj.children = [for( s in children ) s.saveRec()];
|
|
|
+ obj.children = [for( s in children ) s.saveData()];
|
|
|
if( props != null && obj.props == null )
|
|
|
obj.props = props;
|
|
|
return obj;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ Load the whole prefab data and creates its children.
|
|
|
+ **/
|
|
|
+ @:final public function loadData( v : Dynamic ) {
|
|
|
+ type = v.type;
|
|
|
+ name = v.name;
|
|
|
+ enabled = v.enabled == null ? true : v.enabled;
|
|
|
+ props = v.props;
|
|
|
+ source = v.source;
|
|
|
+ load(v);
|
|
|
+ if( children.length > 0 )
|
|
|
+ children = [];
|
|
|
+ var children : Array<Dynamic> = v.children;
|
|
|
+ if( children != null )
|
|
|
+ for( v in children )
|
|
|
+ loadPrefab(v, this);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Updates in-place the whole prefab data and its children.
|
|
|
+ **/
|
|
|
public function reload( p : Dynamic ) {
|
|
|
+ name = p.name;
|
|
|
+ enabled = p.enabled == null ? true : p.enabled;
|
|
|
+ props = p.props;
|
|
|
+ source = p.source;
|
|
|
load(p);
|
|
|
var childData : Array<Dynamic> = p.children;
|
|
|
if( childData == null ) {
|
|
@@ -121,34 +214,30 @@ class Prefab {
|
|
|
prev.reload(v);
|
|
|
newchild.push(prev);
|
|
|
} else {
|
|
|
- newchild.push(loadRec(v,this));
|
|
|
+ newchild.push(loadPrefab(v,this));
|
|
|
}
|
|
|
}
|
|
|
children = newchild;
|
|
|
}
|
|
|
|
|
|
- public static function loadRec( v : Dynamic, ?parent : Prefab ) {
|
|
|
+ /**
|
|
|
+ Creates the correct prefab based on v.type and load its data and children.
|
|
|
+ If one the prefab in the tree is not registered, a hxd.prefab.Unkown is created instead.
|
|
|
+ **/
|
|
|
+ public static function loadPrefab( v : Dynamic, ?parent : Prefab ) {
|
|
|
var pcl = @:privateAccess Library.registeredElements.get(v.type);
|
|
|
var pcl = pcl == null ? null : pcl.cl;
|
|
|
if( pcl == null ) pcl = hxd.prefab.Unknown;
|
|
|
var p = Type.createInstance(pcl, [parent]);
|
|
|
- p.type = v.type;
|
|
|
- p.name = v.name;
|
|
|
- if(v.enabled != null)
|
|
|
- p.enabled = v.enabled;
|
|
|
- p.props = v.props;
|
|
|
- if( v.source != null )
|
|
|
- p.source = v.source;
|
|
|
- p.load(v);
|
|
|
- var children : Array<Dynamic> = v.children;
|
|
|
- if( children != null )
|
|
|
- for( v in children )
|
|
|
- loadRec(v, p);
|
|
|
+ p.loadData(v);
|
|
|
return p;
|
|
|
}
|
|
|
|
|
|
- public function makeInstanceRec( ctx : Context ) : Context {
|
|
|
- if(!enabled)
|
|
|
+ /**
|
|
|
+ Creates an instance for this prefab and its children.
|
|
|
+ **/
|
|
|
+ public function make( ctx : Context ) : Context {
|
|
|
+ if( !enabled )
|
|
|
return ctx;
|
|
|
if( ctx == null ) {
|
|
|
ctx = new Context();
|
|
@@ -156,11 +245,14 @@ class Prefab {
|
|
|
}
|
|
|
ctx = makeInstance(ctx);
|
|
|
for( c in children )
|
|
|
- c.makeInstanceRec(ctx);
|
|
|
+ c.make(ctx);
|
|
|
return ctx;
|
|
|
}
|
|
|
|
|
|
#if castle
|
|
|
+ /**
|
|
|
+ Returns which CDB model this prefab props represents
|
|
|
+ **/
|
|
|
public function getCdbModel( ?p : Prefab ) : cdb.Sheet {
|
|
|
if( p == null )
|
|
|
p = this;
|
|
@@ -170,6 +262,9 @@ class Prefab {
|
|
|
}
|
|
|
#end
|
|
|
|
|
|
+ /**
|
|
|
+ Search the prefab tree for the prefab matching the given name, returns null if not found
|
|
|
+ **/
|
|
|
public function getPrefabByName( name : String ) {
|
|
|
if( this.name == name )
|
|
|
return this;
|
|
@@ -181,17 +276,15 @@ class Prefab {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ Simlar to get() but returns null if not found.
|
|
|
+ **/
|
|
|
public function getOpt<T:Prefab>( cl : Class<T>, ?name : String ) : T {
|
|
|
- var parts = name == null ? null : name.split(".");
|
|
|
+ if( name == null || this.name == name ) {
|
|
|
+ var cval = to(cl);
|
|
|
+ if( cval != null ) return cval;
|
|
|
+ }
|
|
|
for( c in children ) {
|
|
|
- if( (name == null || c.name == name) ) {
|
|
|
- var cval = c.to(cl);
|
|
|
- if( cval != null ) return cval;
|
|
|
- }
|
|
|
- if( parts != null && parts.length > 1 && c.name == parts[0] ) {
|
|
|
- parts.shift();
|
|
|
- return c.getOpt(cl, parts.join("."));
|
|
|
- }
|
|
|
var p = c.getOpt(cl, name);
|
|
|
if( p != null )
|
|
|
return p;
|
|
@@ -199,6 +292,10 @@ class Prefab {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ Search the prefab tree for the prefab matching the given prefab class (and name, if specified).
|
|
|
+ Throw an exception if not found. Uses getOpt() to return null instead.
|
|
|
+ **/
|
|
|
public function get<T:Prefab>( cl : Class<T>, ?name : String ) : T {
|
|
|
var v = getOpt(cl, name);
|
|
|
if( v == null )
|
|
@@ -206,22 +303,43 @@ class Prefab {
|
|
|
return v;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ Return all prefabs in the tree matching the given prefab class.
|
|
|
+ **/
|
|
|
public function getAll<T:Prefab>( cl : Class<T>, ?arr: Array<T> ) : Array<T> {
|
|
|
return findAll(function(p) return p.to(cl));
|
|
|
}
|
|
|
|
|
|
- public function findAll<T>( f : Prefab -> Null<T>, ?arr : Array<T> ) : Array<T> {
|
|
|
- if(arr == null)
|
|
|
- arr = [];
|
|
|
- for( c in children ) {
|
|
|
- var v = f(c);
|
|
|
- if( v != null )
|
|
|
- arr.push(v);
|
|
|
- c.findAll(f, arr);
|
|
|
+ /**
|
|
|
+ Find a single prefab in the tree by calling `f` on each and returning the first not-null value returned, or null if not found.
|
|
|
+ **/
|
|
|
+ public function find<T>( f : Prefab -> Null<T> ) : Null<T> {
|
|
|
+ var v = f(this);
|
|
|
+ if( v != null )
|
|
|
+ return v;
|
|
|
+ for( p in children ) {
|
|
|
+ var v = p.find(f);
|
|
|
+ if( v != null ) return v;
|
|
|
}
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Find several prefabs in the tree by calling `f` on each and returning all the not-null values returned.
|
|
|
+ **/
|
|
|
+ public function findAll<T>( f : Prefab -> Null<T>, ?arr : Array<T> ) : Array<T> {
|
|
|
+ if( arr == null ) arr = [];
|
|
|
+ var v = f(this);
|
|
|
+ if( v != null )
|
|
|
+ arr.push(v);
|
|
|
+ for( o in children )
|
|
|
+ o.findAll(f,arr);
|
|
|
return arr;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ Returns all prefabs in the tree matching the specified class.
|
|
|
+ **/
|
|
|
public function flatten<T:Prefab>( ?cl : Class<T>, ?arr: Array<T> ) : Array<T> {
|
|
|
if(arr == null)
|
|
|
arr = [];
|
|
@@ -237,13 +355,9 @@ class Prefab {
|
|
|
return arr;
|
|
|
}
|
|
|
|
|
|
- public function visitChildren(func: Prefab->Bool) {
|
|
|
- for(c in children) {
|
|
|
- if(func(c))
|
|
|
- c.visitChildren(func);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
+ /**
|
|
|
+ Returns the first parent in the tree matching the specified class or null if not found.
|
|
|
+ **/
|
|
|
public function getParent<T:Prefab>( c : Class<T> ) : Null<T> {
|
|
|
var p = parent;
|
|
|
while(p != null) {
|
|
@@ -254,20 +368,32 @@ class Prefab {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ Converts the prefab to another prefab class.
|
|
|
+ Returns null if not of this type.
|
|
|
+ **/
|
|
|
public function to<T:Prefab>( c : Class<T> ) : Null<T> {
|
|
|
return Std.instance(this, c);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ Returns the absolute name path for this prefab
|
|
|
+ **/
|
|
|
public function getAbsPath() {
|
|
|
var p = this;
|
|
|
var path = [];
|
|
|
while(p.parent != null) {
|
|
|
- path.unshift(p.name);
|
|
|
+ var n = p.name;
|
|
|
+ if( n == null ) n = getDefaultName();
|
|
|
+ path.unshift(n);
|
|
|
p = p.parent;
|
|
|
}
|
|
|
return path.join('.');
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ Returns the default name for this prefab
|
|
|
+ **/
|
|
|
public function getDefaultName() : String {
|
|
|
if(source != null) {
|
|
|
var f = new haxe.io.Path(source).file;
|
|
@@ -277,8 +403,11 @@ class Prefab {
|
|
|
return type.split(".").pop();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ Clone this prefab and all its children
|
|
|
+ **/
|
|
|
public function clone() : Prefab {
|
|
|
- var obj = saveRec();
|
|
|
- return loadRec(haxe.Json.parse(haxe.Json.stringify(obj)));
|
|
|
+ var obj = saveData();
|
|
|
+ return loadPrefab(haxe.Json.parse(haxe.Json.stringify(obj)));
|
|
|
}
|
|
|
}
|