Append a number to a variable name in GDScript - godot

I'd like to append an integer to the end of several variable names in GDSCript.
I'm working on a roguelike and I've decided to organise themed tilesets and NPC's and group them in folders by number (ie, theme 1 may be a crypt filled with undead, theme 2 a forest filled with animals).
The idea is that at the start of level generation I can randomly select a number, generate a level and fill it with corresponding enemies.
For example (assuming the random number is 1)
tileset_to_use = tileset_1
NPC_mid_boss = folder_1/mid_boss
NPC_end_boss = folder_1/end_boss
Aside from a series of nested IF statements along the lines of:
if RNG = 1:
tileset_to_use = tileset_1
NPC_mid_boss = folder_1/mid_boss
NPC_end_boss = folder_1/end_boss
elif RNG = 2:
tileset_to_use = tileset_2
etc...
...what would be a more efficient way of doing this? Something like tileset+RNG
I've looked into using dictionaries but, unless I've misunderstood them, they seem to be used for accessing values rather than generating variable names.

If all your themes share the same exact structure, you can do something similar to what Christopher Bennett proposes. Another option that may give you more flexibility, at the expense of possibly more repetition, is something like this:
# Defined at class level
const THEMES = [
# Theme 1
{
tileset = 'tileset_1',
NPC_mid_boss = 'folder_1/mid_boss',
NPC_end_boss = 'folder_1/end_boss',
# ...
},
# Theme 2
{
tileset = 'tileset_2',
NPC_mid_boss = 'folder_2/mid_boss',
NPC_end_boss = 'folder_2/end_boss',
# ...
},
# ...
]
func my_func():
# Pick a random theme
var theme = THEMES[randi() % THEMES.size()]
tileset_to_use = theme.tileset
# ...
This also allows you to add more properties like arbitrary strings (e.g. theme name) or other things, and can be externalized to something like a JSON document if you want. But again, it requires more manual setup.

Sorry, I think I misunderstood your question when I posted my first comment. Would something like this work?
var RNG = randi()%1-(total number of tilesets);
var tileset_to_use = (str("tileset_",RNG));
NPC_mid_boss = (str("folder_",RNG,"/mid_boss"));

Related

How do I autopopulate a tkinter table using a loop

I'm trying to auto-populate a tkinter table with the names of folders in a directory and details about their properties
grpdata_name = listdir(r"PATH")
grpdata_path = r"PATH\{}".format(grpdata_name[0])
grpdata_groupcount = -1
for x in grpdata_name :
grpdata_groupcount = grpdata_groupcount +1
grpdata_groupcurrent = 'grpdata_name{}{}{}'.format('[',grpdata_groupcount,']')
GUI_Table.insert(parent='',index='end',iid=0,text='',
values=('ID',grpdata_groupcurrent,'TIME CREATED','TIME MODIFIED','DEVICES'))
My current method is to change the selected element in a string. This creates a working cycle through each part of the string ( grpdata_name[0] , grpdata_name[1] etc)
I can't figure out how to use the contents of grpdata_groupcurrent as a variable, rather than a string.
This method isn't very efficient overall, so please let me know if there is a better way to do this.

I want to make a dropdown container in my script

I want to create a dropdown container to organize my export variable. Is it possible to create a custom dropdown container in the script?
Like this:
This is another approach to do this. It also requires the script to be tool.
What we need for this approach as a common prefix for the variables you want to group. The advantage is that we don't need _get and _set:
tool
extends Node
var custom_position:Vector2
var custom_rotation_degrees:float
var custom_scale:Vector2
func _get_property_list():
return [
{
name = "Custom",
type = TYPE_NIL,
hint_string = "custom_",
usage = PROPERTY_USAGE_GROUP
},
{
name = "custom_position",
type = TYPE_VECTOR2
},
{
name = "custom_rotation_degrees",
type = TYPE_REAL
},
{
name = "custom_scale",
type = TYPE_VECTOR2
}
]
As you can see we define a category with a name that will appear in the Inspector panel, and the hint_string is the prefix we will use. It is important to put the category before the properties in the array.
See: Adding script categories
Addendum: Using PROPERTY_USAGE_CATEGORY will produce a named header, similar to the one that says "Node2D" on the picture on the question. Use PROPERTY_USAGE_GROUP to make a collapsible group.
Yes, you can do this, but (in my opinion) it is a bit ugly and clutters up your script. You need to mark your script as a tool script and override the _get, _set, and _get_property_list functions.
An example based on your screenshot (not 100% sure this works exactly as-is; I'm also basing it on a recent project where I have since removed it and somewhat reorganized the project/code/node because the slightly nicer UI wasn't worth the additional clutter in the script):
tool
extends Node2D
# Note that these are NOT exported
var actual_position: Vector2
var actual_rotation: float
var actual_scale: Vector2
# Function to enumerate the properties to list in the editor
# - Not actually directly/automatically backed by variables
# - Note the naming pattern - it is {group heading}/{variable}
func _get_property_list():
var props = []
props.append({name="transform/position", type=TYPE_VECTOR2})
props.append({name="transform/rotation deg", type=TYPE_FLOAT}) # might not exist; look at docs to determine appropriate type hints for your properties
props.append({name="transform/scale", type=TYPE_VECTOR2})
return props
# Now the get/set functions to map the names shown in the editor to actual script variables
# Property names as input here will match what is displayed in the editor (what is enumerated in _get_property_list); just get/set the appropriate actual variable based on that
func _get(property: String):
if property == "transform/position":
return actual_position
if property == "transform/rotation deg":
return actual_rotation
if property == "transform/scale":
return actual_scale
func _set(property: String, value):
if property == "transform/position":
actual_position = value
return true
if property == "transform/rotation deg":
actual_rotation = value
return true
if property == "transform/scale":
actual_scale = value
return true
# Not a supported property
return false
Note that this answer is based on Godot 3.4. I'm not sure if a simpler approach is (or will be) available in Godot 4.

List all "unique paths" to a node

I have a representation of a process trough something that is very much like a DAG (Directed Acyclic Graph). This graph is represented with an adjacency table, but not like a "regular" adjacency table, there are few differences:
Each entry in the table is a list of lists,
Each "inner" list states the predecessor nodes required.
The idea for this data structure is to hold requirements of steps within a process. So, for example:
P = {1:[[]], 2:[[1]], 3:[[2]], 4:[[3]], 5:[[2]], 6:[[]], 7: [[4,6],[8,5]], 8:[[]]}
For process P, step 1 doesn't require any predecessor, step requires step 1,..., step 6 also doesn't require any predecessor, step 7 requires steps (4 and 6) OR (8 and 5).
Each step has a state (some ID reference) that determines if the next step can be executed or not, or if the process can be terminated or not.
In the example above, I would not be able to execute step 2 if step 1 didn't fulfill some specific condition regarding the state the same for step 5, which requires step 2 with state=something specific. And for step 7, the only way to execute it, would be if step 4&6 OR 5&8 have their corresponding state=something specific.
What I need is a way to get all the unique paths that lead to a certain step, so later I can check against this paths if the conditions are met. For step 7 it would be :
paths = [[1,2,3,4,6],[1,2,5,8]]
I've checked:
Python get all paths from graph
How to implement the search for all paths inside a directed graph in JavaScript? (reversing this??)
Depth first search list paths to all end nodes
How to find the nodes that leads to node A without traversing all the graph (directed graph)
Most of the information around points to some sort of modified DFS or some kind of enhanced Dijkstra. For what I've checked and tested none of the above gives me what I need which is a list of all "unique paths" that lead to a node that may be reached from "different paths".
The question is not language specific, so any example in any language would be appreciated :)
EDIT: 04/01/22
Further clarifications:
The steps are one way, meaning that node 1 is connected to step 2 by a distance of 1, to step 3 a distance of 2, and so on. But step/node 1 is not conntected with 6 or 8.
All graphs have a unique starting point and ending point. In the example 1 and 7.
Yes, node 5 should be connected to node 7. Img updated.
The number of nodes will always be <100.
How big is your graph? What is your performance requirement?
For a small graph like your example, Dijsktra is almost instant. So you do not need to store all the paths.
Set cost of all links to 1
Set cost of links that lead to nodes that are NOT in the required state to 10^10
Run Dijkstra to find shortest path from source to destination through nodes in required state.
I think I've managed to get what I needed, nevertheless I think the answer is overly complex.
Function to populate a tracker object with all the possible paths.
const tracker = {};
function getPaths (step, branchRef) {
const currentStepRequires = getStepRequires(step); // func that gets the array of arrays of current step
const oldBranchRef = branchRef;
const hasBranches = currentStepRequires.length > 1;
for (const branch of currentStepRequires) {
if (branch.length === 0) {
return;
}
if (!hasBranches && !branchRef) {
tracker[branch] = [];
}
if (!branchRef) branchRef = branch;
if (hasBranches) {
if (oldBranchRef && oldBranchRef !== branchRef) {
tracker[branch] = [...tracker[oldBranchRef]];
}
else if (tracker[branchRef]) {
tracker[branch] = [...tracker[branchRef]];
branchRef = branch;
}
else {
tracker[branch] = [];
}
}
for (const step of branch) {
tracker[branchRef].push(step);
getPaths(step, branchRef);
}
if (hasBranches) branchRef = '';
}
}
After the tracker object has been populated I need to remove the paths that are contained within the other paths.
I'm using lodash here to simplify the filtering, checking and adding the paths
const paths = [];
_.forEach(_.sortBy(tracker, path => path.length * -1), branch => {
const isSubpath = _.some(paths, path => _.isEqual(branch, _.intersection(path, branch)));
if (!isSubpath) {
paths.push(branch);
}
});
For the example above, this returns the following:
[[4,3,2,1,6], [8,5,2,1]]
I've also tested with more "branching", like example:
P = {1:[[]], 2:[[1]], 3:[[2]], 4:[[3]], 5:[[2]], 6:[[]], 7: [[4,6],[8],[5]], 8:[[6],[3]]}
Which returns:
[[4,3,2,1,6],[8,6],[8,3,2,1],[5,2,1]]
For now its working, but....as I said, I think its more complicated than it needs to be. So, any improvements are welcome.

Get the label of a MathJax equation

How can I get the label of an equation? I'm attempting to reprocess an equation with a label, but I have to delete the label from MathJax.Extension["TeX/AMSmath"].labels first, for which the label must be known...
I know I can scan through the source text for the label MathJax.Hub.getAllJax("mathDiv")[0}.SourceElement().find("\label(") (...), but this seems needlessly complicated. Is there a better way?
There's no built-in API for this.
If you don't need to keep labels, then the reset in the comment above is probably the best way to go about it:
MathJax.Extension["TeX/AMSmath"].labels = {}
A quick and dirty way to get the IDs is to leverage the fact that they end up in the output. So you can just get all the IDs in the output, e.g.,
const math = MathJax.Hub.getAllJax()[0];
const nodesWithIds = document.getElementById(math.root.inputID).previousSibling.querySelectorAll('[id]');
const ids = [];
for (node of nodesWithIds) ids.push(node.id);
A cleaner and perhaps conceptually easier way would be to leverage MathML (which is essentially the internal format): the \label{} always ends up on an mlabeledtr. The trouble is that you'd have to re-parse that, e.g.,
const temp = document.createElement('span');
temp.innerHTML = math.root.toMathML();
const nodesWithIds = temp.querySelectorAll('mlabeledtr [id]');
const ids = [];
for (node of nodesWithIds) ids.push(node.id);
This will make sure the array only has relevant IDs in them (and the contents of the nodes should correspond to \label{}.
I suppose with helper libraries it might be easier to dive into the math.root object directly and look for IDs recursively (in its data key).

Is there a way to change the text of checked/unchecked MCheckBox states?

How would I go about changing the default MCheckBox state text (currently I/0) to, for example, YES/NO or ON/OFF?
Mr. Daniel Kurka is the author for all the widget classes in MGWT. If the look & feel is not
fulfilling our requirement, We can edit those classes and rewrite them according to our requirement.Because they are open source. I done this on many classes like CellList,FormListEntry and MCheckBox. code for ON/OFF instead of I/O
public MyOwnCheckBox(CheckBoxCss css) {
this.css = css;
css.ensureInjected();
setElement(DOM.createDiv());
addStyleName(css.checkBox());
onDiv = DOM.createDiv();
onDiv.setClassName(css.on());
onDiv.setInnerText("ON");
getElement().appendChild(onDiv);
middleDiv = DOM.createDiv();
middleDiv.setClassName(css.middle());
Element middleContent = DOM.createDiv();
middleContent.setClassName(css.content());
middleDiv.appendChild(middleContent);
getElement().appendChild(middleDiv);
offDiv = DOM.createDiv();
offDiv.setClassName(css.off());
offDiv.setInnerText("OFF");
getElement().appendChild(offDiv);
addTouchHandler(new TouchHandlerImplementation());
setValue(true, false);
}
Write a new class like MyOwnCheckBox.just copy the code in MCheckBox and paste in your class MyOwnCheckBox, find and replace the MCheckBox with MyOwnCheckBox in the code(change constructor's name). do the following changes.
onDiv.setInnerText("ON");
offDiv.setInnerText("OFF");
and finally create object to MyOwnCheckBox rather MCheckBox, it'll shows MCheckBox with ON/OFF.
Right now there is no way to do that, but there is no real reasons that checkbox does not implement HasText other than we might need to update the css so that big text will not break the layout.
If you think mgwt should implement this go and vote for this issue: http://code.google.com/p/mgwt/issues/detail?id=171
Well, an easy way to accomplish the same thing, without creating a new class that mimics MCheckBox, is to do something like the code below:
CheckBoxCss css = MGWTStyle.getTheme().getMGWTClientBundle().getCheckBoxCss();
String offClass = css.off();
String onClass = css.on();
NodeList<Node> checkBoxElems;
mIsSingleSkuBox = new MCheckBox(css);
checkBoxElems = mIsSingleSkuBox.getElement().getChildNodes();
for( int i = 0; i < checkBoxElems.getLength(); i++ )
{
Element openElem = (Element) checkBoxElems.getItem(i);
String className = openElem.getClassName();
if( className.equals( offClass))
{
openElem.setInnerText("No" );
}
else if( className.equals( onClass))
{
openElem.setInnerText("Yes" );
}
}
It will probably have space problems with anything longer than 3 characters, but it works consistently with "Yes" and "No" for me.

Resources