Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

Okay I think I got to the right stuff where I can easily check what I want

but en route I ran across a weird output formatting?  I'm gonna scrap this code, it was just to pin down the right details, but I am baffled as to how this produces the output.  This isn't even a question really, just a "wtf??"

(NOTE: the line break in the one text line is for readability here and not in the actual code)

CODE:
text += "Not naked!  "
var clothes = globals.state.unstackables.get(person.gear.costume)
var accessory = globals.state.unstackables.get(person.gear.accessory)
 
text += "clothes: " + clothes + ", type: " + typeof(clothes)
    + "; accessory: " + accessory + ", type: " + typeof(accessory)
text += "\nClothes Code: " + clothes.code + "\n Accessory Code: " + accessory.code
 
RESULTS:
1393, type: ; accessory:, type: 1393, type: ; accessory:, type:
Accessory Code: acchandcuffs

So it did spit out the right details, so now I know how to get it.  And I need some error-catching to make sure I'm handling actual variables and not making messy code when they're not wearing anything.  And the actual variables there translate to numbers hence the need for a thing to look them up and all (and 1393 appears to be the null entry? I guess?).

but... why did "clothes: " disappear? why did "Clothes Code: " disappear?  Do mishandled null entries actually erase part of existing strings???

The string values need to be used in the dictionary "globals.state.unstackables" to find the dictionary typed item data for that specific item.
GDScript does not allow you to add/concatenate non-string values to strings. The only reason you are getting anything at all is because in non-debug mode minor errors will be effectively omitted from execution, which is causing data corruption in your "text" variable. I recommended the print() function because it will automatically convert each argument into a string and then concatenate those strings. If you want to create your own string, then you must convert the dictionary to a string using str( clothes ) before you concatenate it.

Just to be sure I understand this:

You're specifically referencing the part that didn't work and corrupted the string, and saying that if I want to turn that into a string, then I need to take an extra step.

As opposed to "so the rest of your code may look like it's working, but it's not working right, and you really do need to take this extra step on all the things that so far seem to be working."  Which is currently this:

var accessory = globals.state.unstackables.get(person.gear.accessory)
var acc = getaccessory(accessory.code)
 
func getaccessory(code):
    var acc = null
    match code:
        "accslavecollar":
            acc = "collar"
        "acchandcuffs":
            acc = "handcuffs"
        _:
            acc = null
    return acc

...with variations for costumes and underwear.

I'm not sure if it's possible to shortcut the first two lines to avoid an extra variable that gets immediately discarded?  Something like this:

var acc = getaccessory(globals.state.unstackables.get(person.gear.accessory).code)

I debated about trying that but decided that despite the throwaway variable (three of them, all told), the code just looks nicer and more readable when I separate those two steps.

Anyway, I've gotten to the point of being able to reference and sort by these values, and it's all working pretty smoothly now that I've put it behind "if x != null" walls (which checks if the variable isn't set -- in this case, if the character isn't wearing anything, as opposed to wearing a different thing).  I presume I could do a similar thing using typeof() to make sure it's a string, which is likely a good habit to get into (sanitizing input), but at present all I am looking for is whether it's null.

(P.S. I understand print() functions to display text on the screen (pop-up box); does it have a different function or multiple functionality in Godot?)

Also, on the up side, I've gotten to the point where nearly all the problems get washed away in a few rounds of basic troubleshooting (e.g. pull the newly added code out to a text file, make sure the base isn't broken, add it back in in pieces, find the part that throws a wobbly, then look extra carefully to see if I've forgotten a quote or colon somewhere or misspelled a variable or the like (Notepad++ is actually really good at reducing the (already low) chance that I'll misspell a variable I've used elsewhere in the code), or if the issue is another round of misunderstanding how the code actually works).

So now it's just... ye gods, that's an ambitious level of fine-tuning my brain wants to do to the script!  I'm not even to the fancier parts of the code, just the description you see when you look at a slave and what the slave says to you when you talk to them.  I've condensed parts of the description to lines like "You see Wisteria Smithee, a towering, strikingly gorgeous Halfkin Wolf youth with her hands bound behind her" (which also omits the average part, unless both height and beauty are average, in which case it says "unremarkable" for humans and "typical" for non-humans) and fancy descriptions for costumes based on whether the character is handcuffed or wearing a collar (and the collar description changes when the character is more loyal).

Tomorrow, if all goes well, I'm gonna tackle separating the upper body description from the lower body description so that the eye flows seamlessly down the body.  And then figure out which parts of the character traits I'd like to weave into the description and where.  It'd be nice to see bits of description that combine e.g. beauty and strength (Strong, Frail), or agility and charm to add up to gracefulness, or like have a foul-mouthed character in handcuffs with low obedience throw you the bird.

(1 edit)

The debug mode will generally tell you exactly what line had the error and give a decent short explanation of the error. Your error happened at this point

"clothes: " + clothes

because the + operation does not accept a string and a dictionary as arguments. If the first operand is a string, then the second operand must also be a string. The correct form would be

"clothes: " + str(clothes)


Yes, it is possible to combine everything into a single line, but it starts to get cumbersome if you wish to include proper contingency management. The "get" function will attempt to retrieve the value corresponding to the given key, but if that key is not in the dictionary then it will return a null. The square bracket operation ( dictionary[key] ) will do basically the same thing but it will cause an error if the key is not in the dictionary. The dot operation for objects and dictionaries is simply an alias for the square bracket operation with a string key. Therefore these two lines are equivalent.

globals.state.unstackables.get(person.gear.accessory).code
globals['state']['unstackables'].get(person['gear']['accessory'])['code']

When an item slot is empty there is a null stored in that slot and there is no null in the "unstackables" dictionary, so get() is used to avoid errors. However, nulls have no indexing operations like get() or [], so "null.code" will cause an error. There is an optional second argument to get() that replaces the null with another value, which if passed a dictionary will allow the indexing without an error, like this:

globals.state.unstackables.get(person.gear.accessory, {}).code

The downside to this approach is that it creates and discards the blank dictionary every time the line runs. Since dictionaries are part of the core structure of GDScript, you will have skipped creating another entry in an existing dictionary for your variable in favor of creating an entire new dictionary.

Your getaccessory() is correct as far as I can see, but if you are looking for shortcuts then there are several you could use here. Since "acc" is simply a temporary storage for the return value, you could skip the variable and return immediately.

func getaccessory(code):
    match code:
        "accslavecollar":
            return "collar"
        "acchandcuffs":
            return "handcuffs"
    return null

However your entire function is simply searching for a key and returning the corresponding value, which is exactly what a dictionary does but slower.

var accessoryTextDict = {'accslavecollar':"collar", 'acchandcuffs':"handcuffs"}
var accessory = accessoryTextDict.get( globals.state.unstackables.get(person.gear.accessory, {}).code )

If you would rather avoid the empty dictionary, then use an if statement:

var accessoryTextDict = {'accslavecollar':"collar", 'acchandcuffs':"handcuffs"}
var accessory = globals.state.unstackables.get(person.gear.accessory)
if accessory:
    accessory = accessoryTextDict.get( accessory.code )

Including "!= null" in the if statement adds clarity, but null converts to false so it is optional in this case. The accessoryTextDict can be put in file scope so that it is re-used for the duration of the file rather than created anew each time a function is run. You may use typeof() checks whenever you want, but generally we refrain from having the code protected against every conceivable problem in favor of designing with the expectation that the data is well controlled. It may be a bit more risky, but development time is a larger problem around here.

The print() function will put text in the standard output stream which will be displayed in the terminal window if you are using the Debug mod or in the output panel if you are running the game through the Godot editor. If you are not using either of those, then everything in the output stream is ignored.

It's gonna take me a while to digest all this, but this is super, super useful.  Especially this part: "The dot operation for objects and dictionaries is simply an alias for the square bracket operation with a string key."  (Is this the case in Visual Basic, I wonder?  If it is, either I never got taught it explicitly, or I've forgotten it in the intervening years.)

I appreciate you taking the time to show me the incremental steps to get down to the most efficient version, so that I can more easily conceptualize what's going on and why.  As a self-taught programmer with what is likely ADHD, I find it all too easy to gloss over bits that seem surface-level "duh" or "I can look this up whenever", but explanations that are succinct enough to focus on but also detailed enough to help me grasp the underlying principles (instead of just the surface-level appearance) are exceedingly useful yet difficult to find.

P.S. I presume the debug mode would be a better way of handling the troubleshooting process than pulling out bits of code like I've been doing... so I just gotta get past the inertia/resistance of "but I don't want to figure out the New Thing, I'm working on this thing, and the irritation of doing this poorly is less of a problem than the effort it takes to do things better" brain mode.  Same reason it took me like five years to get to the point of modding Skyrim, and now I can't imagine playing without mods.  Brain inertia is fun (sigh).

(1 edit)

I haven't used Visual Basic in at least a decade so I don't remember, but in most languages I know the dot operator is only used to access member functions of the class or member variables of the object.

In GDScript, objects and dictionaries use C++ compiled functions in similar-ish ways to compiled languages, but it includes more steps as the data is evaluated and handled in realtime due to the lack of strict data typing. If the string after the dot operator doesn't correspond to any compiled preset then it is processed as a key for a lookup.

In compiled languages member variables of an object are accessed by converting the name into a memory offset from the starting memory address of the object. In GDScript, member variables are string keys used to access values in an internal dictionary, so the relative offsets of variables in memory are not constant. Compiled languages don't use complex lookups like dictionaries as a core part of their design, so I would only expect such a feature to exist in an interpreted scripting languages where convenience is more important than efficiency.

Edit: It might be time to start a new chain of posts as this one is getting quite deep and less convenient to find.