Passing array as hint in string export? - godot

I'm trying to pass an array as a hint in string export, something like this:
extends Node
var things_list=["thing_1", "thing_2", "thing_3"]
export(String,*things_list) var current_thing=things_list[0]
why am I doing this?
I want to reuse the things_list & to do so I have to tediously write all the same values twice,
once in this list itself & once in export like this:
extends Node
var things_list=["thing_1", "thing_2", "thing_3"]
export(String,"thing_1", "thing_2", "thing_3") var current_thing=things_list[0]
this becomes really annoying with longer lists,
So is there anything like ...array in js
or something like *args in python? (as shown in the first example)
or are the values somehow stored in the export variable itself?
maybe like current_thing.export_hints[1] #thing_2

Those values are stored internally and there is no scripting access. And no, the parser does not understand any syntax for an array in export (nor array spread elsewhere for that matter).
However, there is something similar. You can define an enum:
enum things {thing_1, thing_2, thing_3}
export(things) var current_thing = things.thing_1
As you might remember, if you have an enum, you have a Dictionary. So you can still get the list of things like this:
var things_list = things.keys()
However, the enum will be of int, not of String. Which I don't know if it will be an issue. You can still get the corresponding String like this:
var things_list = things.keys()
print(things_list[current_thing])
And, of course, your other option is to use _get_property_list. It is not overly complicated:
var things_list := ["thing_1", "thing_2", "thing_3"]
var current_thing:String = things_list[0]
func _get_property_list() -> Array:
return [
{
"name": "current_thing",
"type": TYPE_STRING,
"hint": PROPERTY_HINT_ENUM,
"hint_string": PoolStringArray(things_list).join(",")
}
]

Related

Haxe - use string as variable name with DynamicAccess

I am trying to use a string ('npcName') as a variable name. So far I have tried casting dialogMap into a DynamicAccess object, but it gives me the error 'Invalid array access' when I try this:
var npcName:String = 'TestNPC';
var casted = (cast Registry.dialogMap:haxe.DynamicAccess<Dynamic>);
var tempname = casted[root.npcName[0].message];
trace(tempname);
'dialogMap' is an empty map which I want to fill like so:
Registry.dialogMap['message'] = root.npcName[0].message;
How can I use npcName, a string, in the above line of code? Is there a way to transform the string into something usable? Any help would be appreciated.
The haxe.DynamicAccess doesn't have array access (like map[key]), but is an abstract type for working with anonymous structures that are intended to hold collections of objects by the string key. It is designed to work with map.get(key) and map.set(key). It is basically a nicer wrapper around Reflect.field and Reflect.setField and does some safety checks with Reflect.hasField.
var variable = "my_key";
var value = 123;
var dynamicMap = new haxe.DynamicAccess<Dynamic>();
dynamicMap.set(variable, value);
I'm noticing you are doing very much cast and dynamic, so untyped code, which is a bit of contradiction in a typed language. What is the actual type of dialogMap?
Not sure you are aware of it but, Haxe has its own maps, which are fully typed, so you don't need casts.
var map = new Map<String, Int>();
map[variable] = value;
I think this article helps understanding how to work with dynamic (untyped) objects.
Tip; for testing such small functionalities you can doodle around on the try.haxe site : http://try.haxe.org/#4B84E
Hope this helps, otherwise here is some relevant documentation:
http://api.haxe.org/haxe/DynamicAccess.html
https://haxe.org/manual/std-reflection.html
https://haxe.org/manual/types-dynamic.html
http://code.haxe.org/category/beginner/string-variable-reflection.html

Array access not allowed on OpenFL movieclips

UDATED
How do I go about this?
I got this from Main.hx:
function onMouseOver(e:MouseEvent){
if(Std.is(e.currentTarget, MovieClip)){
initializer (cast e.currentTarget,["scaleX",1.5,"scaleY",1.5])
}
}
Then this is the pointed function in my Animation Class
//here if i set mc:Dynamic everything goes great! but when this one
function initializer(mc:MovieClip, vars:Array<Dynamic>){
var varsLength:Int = Math.round(vars.length/2);
for(m in 0...varsLength){
ini[m] = mc[vars[2*m]];
}
}
then when i compile it, an error appears:
Error: Array access is not allowed in flash.display.MovieClip
How do I resolve this?
EDIT:
vars: are properties of the MovieClip, for example when I pass these parameters:
initializer (mcClip1,["scaleX",1.5,"scaleY",1.5])
so:
vars = ["scaleX",1.5,"scaleY",1.5]
and:
ini[m] will store "scaleX" and "scaleY"`
X-Ref: https://groups.google.com/forum/#!topic/haxelang/_hkyt__Rrzw
In AS3, you can access fields of an object via their String name using [] (array access). This is called Reflection.
In Haxe, Reflection works differently - you need to make use of the Reflect API.
It's considered bad practice - it's not type-safe, which means the compiler can do very little to help you with error messages, and it's quite slow as well. This is why the usage makes it very explicit that Reflection is actually going on (while in AS3, this fact is somewhat hidden). Consider if there are other ways of solving this problem that don't require Reflection.
Now, to get back to your example, here's what it would look like in Haxe:
function onMouseOver(e:MouseEvent){
if (Std.is(e.currentTarget, MovieClip)) {
initializer(cast e.currentTarget, ["scaleX", 1.5, "scaleY", 1.5])
}
}
function initializer(mc:MovieClip, vars:Array<Dynamic>) {
for (m in 0...Std.int(vars.length / 2)) {
ini[m] = Reflect.getProperty(mc, vars[2*m]);
}
}
Btw, your loop was running for too long since you only use half of the values in the array - if you don't divide it by two like I did, you'll end up with [scaleX, scaleY, null, null] instead of the desired [scaleX, scaleY].

Create [NS]Dictionary from single string in Swift

I have a string var dictAsString:String = '["foo" : 123, "bar" : 456]' that I want to convert to a Dictionary (or NSDictionary, I'm not particular.) I've tried
var dictAsObj:AnyObject = dictAsString as AnyObject
var dictAsDict:NSDictionary = dictAsObj as NSDictionary
but that doesn't work. I've also tried
var dictAsDict:NSDictionary = NSDictionary(objectsAndKeys: dictAsString)
and
var dictAsObj:AnyObject = dictAsString as AnyObject
var dictAsDict:NSDictionary = NSDictionary(objectsAndKeys: dictAsObj)
Nothing seems to work, and I can't seem to find any help in the documentation. Any ideas?
That string resembles a JSON object.
You could replace the square brackets with curly brackets and use NSJSONSerialization class to get a dictionary out of it.
Worst case scenario, you should write a little parser.
I suggest using Ragel.
Both tasks are an overkill for a string like that, though.

take multiple array fields as parameter

I am trying to refactor some code. All along the code base, I have code that stores user-related informations like this :
this.users[user][field] = data ;
Note that there could be an arbitrary number of subfields (including none), like this:
this.users[user][field][subfield1][subfield2] = data ;
Theses informations are retrieved like this :
var result = this.users[user][field] ;
In production, the actual data will be stored into Redis.
To prepare for this, I would like to refactor those access into two functions, say function storeUserData(user, fields, data) and function retrieveUserData(user, field).
I can do it trivially if there is only one field. But how can I write those two functions to allow for an arbitrary number of subfields (ideally none as well) ?
Edit : the long-term goal is to blur the difference between redis keys and node.js arrays.
That way I could for instance access a node subfield like this : 'users.user.id' and also have it in redis like this :users.user.*.id. Does that seem feasible ?
You can pass the fields argument as an Array. Then in your read function do something like
function retrieveUserData(user, fields) {
// imagine fields is ['field', 'subfield1']
var fieldVariable = this.users[user]
for (f = 0; f < fields.length; ++f) {
fieldVariable = fieldVariable[fields[f]];
}
// in this line fieldVariable will point to this.users[user]['field']['subfield1']
return fieldVariable;
}
Hope it helps!

Vala: Pass String as Class

Scenario:
I have x number of classes. Lets say 10; Each class does different UI Functions. When a user loads a file, that extension tells the program the classname to load; but it's in the form of a string.
Is there anyway to pass a string off as a classname? Something to the effect of.
var classname = "Booger";
var nose = new classname(){ //classname really means "Booger"
//Do Operation
}
You can reflect a type by name using var t = Type.from_name(classname);, however, this works on all types, including enums and structs and it might be the type Type.INVALID. You should probably do some checks, like t.is_a(typeof(MyParentClass)).
You can then instantiate a copy using var obj = Object.new(t);. The whole thing would look like:
var classname = "Booger";
var t = Type.from_name(classname);
if (t.is_a(typeof(MyParentClass)))
return Object.new(t);
else
return null;
It's also worth noting that the run-time type names have the namespace prepended, so you might want to do "MyNs" + classname. You can check in either the generated C or doing typeof(MyClass).name().
I've had the same problem as the OP in regards to getting an assertion error against null. If you take a look at the Glib documentation (in C) it mentions you have to register your class by actually specifying the class name first before you can actually use a string representation of your class name.
In other words you have to use your class first BEFORE you can instantiate a copy of your class with Glib.Type.from_name ("ClassName").
You can use your class first by instantiating a class instance or by getting type information for your class.
var type = typeof (MyClass);
var type_from_string = Type.from_name ("MyClass");
Furthermore, when you use Object.new to create a class there are two things you need to be aware of:
1) You need to cast the return value to get your specific class or base class.
var instance_of_my_class = Object.new (type) as MyClass;
2) Constructors for your class will no longer be called (I don't why). You will need to use the GObject style constructor inside your class:
construct {
pizza = 5;
}

Resources