Is it possible to add/hide export variables during run time? - godot

I'm trying to add or hide script variables in tool mode like this:
tool
...
export(int) var number_of_appendages=0 setget number_of_appendages_changed
func number_of_appendages_changed(new_val):
number_of_appendages=new_val;
_get_property_list()
func _get_property_list():
var variables_list=[]
for i in number_of_appendages:
for j in 2:
variables_list.append({
"hint": PROPERTY_HINT_RANGE if j==1 else PROPERTY_HINT_NONE,
"hint_string":"-360, 360,30" if j==1 else "",
"usage": PROPERTY_USAGE_DEFAULT,
"name": "appendage "+String(i+1)+"/"+("name" if j==0 else "length"),
"type": TYPE_REAL if j==1 else TYPE_STRING
})
return variables_list
obviously this won't work, but what if I create a max number of appendages and then only toggle the visibility accordingly?
pseudo code:
tool
...
export(int) var number_of_appendages=0 setget number_of_appendages_changed
const max_appendages=5
func number_of_appendages_changed(new_val):
number_of_appendages=new_val;
for i in max_appendages:
if(i>number_of_appendages):
HIDE_GROUP("Appendage "+String(i+1))
func _get_property_list():
var variables_list=[];
for i in max_appendages:
for j in 2:
variables_list.append({
"hint": PROPERTY_HINT_RANGE if j==1 else PROPERTY_HINT_NONE,
"hint_string":"-360, 360,30" if j==1 else "",
"usage": PROPERTY_USAGE_DEFAULT,
"name": "appendage "+String(i+1)+"/"+("name" if j==0 else "length"),
"type": TYPE_REAL if j==1 else TYPE_STRING
})
return variables_list
is something like this possible? or is there any other way to achieve what I'm trying to accomplish ?

What you have is close to what you have to do. Unlike the pseudo code you came up with.
As you are aware Godot calls _get_property_list to figure out what to show in the inspector panel.
However, when you call it like this:
func number_of_appendages_changed(new_val):
number_of_appendages=new_val;
_get_property_list()
It does nothing. Yes, _get_property_list is running, but what happens with its result? It is getting descartad.
Instead, do this:
func number_of_appendages_changed(new_val):
number_of_appendages=new_val;
property_list_changed_notify()
And with that call property_list_changed_notify you are telling Godot to call _get_property_list and use its result to update the inspector panel.

Related

Invalid operands 'Dictionary' and 'Array' in operator '+'.Godot3.5

I am using v3.5.stable.official [991bb6ac7] Godot Engine I was also using version 3.4, that is also the reason I have decided to update, since I can't get passed by that error.
Invalid operands 'Dictionary' and 'Array' in operator '+'
extends Area2D
onready var timer = $Timer
onready var plant = $Sprite
var stage = 0
var PlantNum
func _ready():
PlantNum = Game.Plot.size()
Game.Plot += [{
"Seed": "Corn",
"TimeLeft": timer.time_left,
"Stage": stage,
}]
Utils.save_game()
timer.start()
plant.frame = 1
This piece of code is for my game project that I am trying to make. The code serves for the save and load, so that the plants stage will stay the same as when I join again.
The global script which is connected to the upper one.
extends Node
var Plot = [
{
"Seed":"",
"TimeLeft":0,
"Stage":0,
},
]
Expected output
[{Seed:, Stage:0, TimeLeft:0}, {Seed:Corn ,Stage:0, TimeLeft:0}]
Any help would be much appreciated.
I don't know why you are getting that particular error. My first guess is that somewhere along the line the type was lost… Either somewhere you set a Dictionary to Plot (after all, it is Variant), or we are looking at a bug in Godot.
Either way, you can declare Plot as an Array (so it is not Variant). Either implicitly:
var Plot := [
{
"Seed":"",
"TimeLeft":0,
"Stage":0,
},
]
Or explicitly
var Plot:Array = [
{
"Seed":"",
"TimeLeft":0,
"Stage":0,
},
]
On the other hand, using using append_array is better than += (it does not allocate a new Array), so I would prefer that:
Game.Plot.apppend_array([{
"Seed": "Corn",
"TimeLeft": timer.time_left,
"Stage": stage,
}])
Furthermore, you are just adding one element, thus you can use append:
Game.Plot.apppend({
"Seed": "Corn",
"TimeLeft": timer.time_left,
"Stage": stage,
})
Thanks for the post.
I have done this
func _ready():
PlantNum = Game.Plot.size()
Game.Plot.append({
"Seed": "Corn",
"TimeLeft": timer.time_left,
"Stage": stage,
})
And this,
var Plot := [
{
"Seed":"",
"TimeLeft":0,
"Stage":0,
}]
I got a new error by doing so,
Parser Error: The assigned value's type (Dictionary) doesn't match the variable's type (Array).
func _ready():
var date = OS.get_datetime()
Game.Plot = OS.get_datetime() # Error is in this line.
self.text = "Time: " + str(date["hour"]) + " : " + str(date["minute"])

Lex & Lambda: Slots Being Set Manually Reset to Null

Building a bot in which we fulfill an intent in a Lambda function. In this function, based on a user's selection, we have to set two slots to certain values.
The slots in question are "userResponse," "chatType," and "segment."
The code attempts to fill the latter slots based on the userResponse slot. Looks something like this:
let requestedService = event.currentIntent.slots.userResponse;
if (requestedService === "a") {
event.currentIntent.slots.segment = 'a';
event.currentIntent.slots.chatType = 'a';
} else {
event.currentIntent.slots.segment = 'b';
event.currentIntent.slots.chatType = 'b';
}
Thus, upon code running, the slots may look like this:
{
"userResponse": "a",
"chatType": "a",
"segment" "a"
}
Now that we've fulfilled all of our slots, we pass the fulfill intent dialog action back to Lex.
callback(null, {
"sessionAttributes": event.sessionAttributes,
"dialogAction": {
"type": "Close",
"fulfillmentState": "Fulfilled",
"message": {
"contentType": "PlainText",
"content": "Please wait"
}
}
});
The problem is, when I send this information back to Lex, the slots that I've just manually set are somehow wiped out and returned to Lex as null, so that the slots are like this:
{
"userResponse": "a",
"chatType": null
"segment" null
}
No other code is present between setting the slot values and then calling the dialog action. In this Lambda function's CloudWatch log, I've console logged the event just prior to the dialog action call and it shows the slots as being fulfilled. And yet...somehow, Lex is not getting those slots back with the correct data.
Is there something I'm missing? Why is Lex receiving null values when they're not null in Lambda?
According to the document, I think you have to introduce the slots in the response object:
callback(null, {
"sessionAttributes": event.sessionAttributes,
"dialogAction": {
"type": "Close",
"fulfillmentState": "Fulfilled",
"stols": event.currentIntent.slots, // this change
"message": {
"contentType": "PlainText",
"content": "Please wait"
}
}
});

Best way to navigate throught a JSON in Node while validating the path

I'm trying to get some info out of a API call in Nodejs, structured something like a JSON:
{
"generated":"2019-11-04T09:34:11+00:00",
"event":{
"id":"19040956",
"start_":"2019-11-16T11:30:00+00:00",
"event_context":{
"sport":{
"id":"1",
"name":"Soccer"
}
}
}
}
I'm not sure about the presence of none of these fields(Json could be incomplete).
Is there a better way to get the value of "name" in JSON.event.event_context.sport.name without an ugly if to not get errors like "cannot get field 'sport' of undefined"?
Currently, I'm doing
if(json.event && json.event.event_context && json.event.event_context.sport) {
return json.event.event_context.sport.name;
}
Is there a better way?
Thank you!
what do you mean by saying "I'm not sure about the presence of none of these fields"?
i don't understand what your'e trying to achieve.
Looks like there is also an interesting package that will allow more conditions on searching json :
https://www.npmjs.com/package/jspath
let getNested = (path, obj) => {
return path.split(".").reduce( getPath, obj);
}
let getPath = (path, key) => {
return (path && path[key]) ? path[key] : null
}
let test = {
"foo": "bar",
"baz": { "one": 1, "two": ["to", "too", "two"] },
"event": { "event_context": { "sport": { "name": "soccer" } } }
}
console.log(getNested("none", test))
console.log(getNested("baz.one", test))
console.log(getNested("baz.two", test))
console.log(getNested("event.event_context.sport.name", test))
You can use lodash get to get a potentially deeply-nested value, and also specify a default in case it doesnt exist.
Example
const _ = require('lodash');
const my_object = {
"generated":"2019-11-04T09:34:11+00:00",
"event":{
"id":"19040956",
"start_":"2019-11-16T11:30:00+00:00",
"event_context":{
"sport":{
"id":"1",
"name":"Soccer"
}
}
};
_.get(my_object, 'event.event_context.sport.name'); // "Soccer"
_.get(my_object, 'event.event_context.sport.nonExistentField', 'default val'); // "default val"
Article: https://medium.com/#appi2393/lodash-get-or-result-f409e73e018b
You can check by using a function to check object keys like :
function checkProperty(checkObject, checkstring){
if(!checkstring)
return false;
var propertiesKeys = checkstring.split('.');
propertiesKeys.forEach(element => {
if(!checkObject|| !checkObject.hasOwnProperty(element)){
return false;
} else {
checkObject= checkObject[element];
}
})
return true;
};
var objectToCheck = {
"generated":"2019-11-04T09:34:11+00:00",
"event":{
"id":"19040956",
"start_":"2019-11-16T11:30:00+00:00",
"event_context":{
"sport":{
"id":"1",
"name":"Soccer"
}
}
}
}
if (checkProperty(objectToCheck ,'event.event_context.sport.name'))
console.log('object to find is : ', objectToCheck .event.event_context.sport.name;)
Yeah there are better ways!
For example, you could use lodash's get() method to reach a nested value.
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.get(object, 'a[0].b.c');
// => 3
But there is also a native solution.
Currently (11.2019) only Babel can handle this.
I am speaking of Optional chaining. It's new in the Ecmascript world.
Why I like it? Look here!
// Still checks for errors and is much more readable.
const nameLength = db?.user?.name?.length;
What happens when db, user, or name is undefined or null? With the optional chaining operator, JavaScript initializes nameLength to undefined instead of throwing an error.
If you are using Babel as a compiler then you could use it now.
Related link: https://v8.dev/features/optional-chaining

Elastic Search: How to write multi statement scripts?

I have values stored on a document in an Elasticsearch index.
I need to do some date manipulation on the values and return a boolean value to be used in a filter.
The script covers several lines, and I can't get it to run.
I've written other single scripts that work fine, however I know less than nothing about Groovy and very little about Elastic search.
Each and every sample I can find with a script has one line and only one line.
So basically how would I take this perfectly valid script
"script": {
"script": "doc['state'].value == 'completed' && doc['lastStateUpdate'].value < doc['dueDate'].value"
}
And turn it into some thing like
"script": {
"script": "def isCompleted = doc['state'].value == 'completed'
def preSLA = doc['lastStateUpdate'].value < doc['dueDate'].value
return isCompleted && preSLA"
}
I'm not mad about the idea of creating a write-only one liner that expresses the logic, I can see more of these coming down the line and while this one is relatively straight-forward, a "one liner" isn't going to cut it.
The alternative here is to do some preprocessing on the document before it's indexed, and add extra data to it. However this has drawbacks in that it's rather inflexible and we'd need to reindex all the data to change these aggregations, which we'd rather not do.
You simply need to separate each statement with a semicolon:
"script": {
"script": "isCompleted = doc['state'].value == 'completed'; preSLA = doc['lastStateUpdate'].value < doc['dueDate'].value; return isCompleted && preSLA;"
}
Make sure to not add line breaks inside your script string, though, as it would not be valid JSON.
If you want to break your script into multiple lines you have to surrond your script with """ docs
`"query": {
"function_score": {
"script_score": {
"script": {
"lang": "painless",
"source": """
int total = 0;
for (int i = 0; i < doc['goals'].length; ++i) {
total += doc['goals'][i];
}
return total;
"""
}
}
}
}
}`
Update: For some versions of Elasticsearch source should be replaced with inline docs

Is There a way to check if(do.condion=='11') in Groovy DSL

Is There a way to check if(do.condion=='11') in Groovy DSL.
if(object.member == '2') //then do my logic
I am not able to use ==. its not throwing any error it just going to next statement
If I'm understanding your DSL correctly, formatted for clarity it looks like this:
AcceptILPN {
input scanCase
if(workflowParameters.reserveVerificationModeParm.equals("1")) next AcceptSKUQuantity
if(workflowParameters.validateCarton.equals("1")) next AcceptOLPNOrTote
if(workflowDO.nextDtlPresent) next AcceptILPN else next AcceptPickCart
}
To see what's happening, here's the same code with a more formal syntax:
AcceptILPN {
input(scanCase)
if(workflowParameters.reserveVerificationModeParm.equals("1")) {
next(AcceptSKUQuantity)
}
if(workflowParameters.validateCarton.equals("1")) {
next(AcceptOLPNOrTote)
)
if(workflowDO.nextDtlPresent) {
next(AcceptILPN)
} else {
next(AcceptPickCart)
}
}
As you can see, even when the first if expression evaluates to true the following if blocks will execute because there's nothing (at least visible in the DSL) that exits the Closure prematurely. It seems you're looking for something like this:
AcceptILPN {
input(scanCase)
if(workflowParameters.reserveVerificationModeParm.equals("1")) {
next(AcceptSKUQuantity)
}
else if(workflowParameters.validateCarton.equals("1")) {
next(AcceptOLPNOrTote)
)
else if(workflowDO.nextDtlPresent) {
next(AcceptILPN)
} else {
next(AcceptPickCart)
}
}

Resources