Page 1 of 2

Coding Workshop

Posted: Mon 07 Jun, 2021, 3:14 pm
by InspectorCaracal
I realize I'm probably gonna be talking to myself here lol but I need/want a place to think out things I'm stumped on with code projects and potentially get input, so I figured I'd start a general thread for it.

Feel free to ask for help with your own stuff too, that's what it's for. 8)

Re: Coding Workshop

Posted: Mon 07 Jun, 2021, 3:44 pm
by InspectorCaracal
Okay so today's dilemma is an ongoing one re: my dream MUD I'm building in Evennia. This one's about clothing, technically, but it bleeds into other systems 'cause that's how I roll. Also it's gonna be a lengthy read so bear with me lol

So! One of the key design elements for this game is that the crafting system is highly modular - not in a programming sense, in a normal sense. Like, since we're on the subject of clothing, if you get into tailoring then you could design your own clothing items by (to simplify) which pieces of fabric you join together in what ways. Sort of like building a new clothing design on a dressmaker's mannequin.

Okay, back to the code part. The relevant bit of this is it means you can't sort clothing on the back-end into simple "clothing type" slots, because they won't all cover the same bits - like real life, right? A long-sleeve button-up doesn't cover the same amount of you as a sleeveless crop top, which doesn't cover the same as a long-sleeved fishnet shirt.

So instead of clothing slots, I have this concept of body-part slots, layers, and opacity. The problem I'm having is implementation. I think it's pretty solid that I'm going to have a handler Script object attached to all characters that track the collection of body-part slots. And I know I need a way to define which parts are layered over by a clothing item, and to attach to each slot whether or not you can still see what's underneath. e.g. a glove versus a bracelet, a ski mask vs a veil.

I think I also want the body parts to be in a "hierarchy", which means that there's several large top-level sections that each break down into smaller sections. So, there's a "face" slot, but then that's divided into "eyes", "nose", "mouth", "forehead", "jaw", "cheeks", and then some of those have a further tier down of "left eye" and "right eye", "left cheek" and "right cheek". Which means you can cover all of the sub-parts of "face" by covering the whole face, or you could cover just "eyes" - which gets both eyes - or you could cover just "right eye".

So, implementation. My core problem here is I don't know how to translate this into Python's available data structures in a way that will preserve the hierarchy and the order of the parts on the character model, allow me to define a miniature "coverage" structure for an individual clothing item, and then add, remove, and check the layering status of a piece of clothing on the character model. I feel like it'll probably have to be some combination of lists and dicts? but I'm not sure what the best approach is and my brain keeps trying to arrange it as PHP arrays.

... Although, thinking about it, since I already determined the model is going to be handled by a Script object, which means there'll be a specific class for the model, so I guess I could save the individual body parts as attributes and then handle the hierarchy through methods? I'm not entirely sure how to translate that to clothing items, though - I can't build the coverage of an individual clothing item into the Clothing class, since the flexible granularity of coverage is the whole point

Re: Coding Workshop

Posted: Tue 08 Jun, 2021, 12:33 am
by noelle
This is a purely idle thought, but you might find an advantage in encoding this into binary, where each location gets a place in the number. So, for example:

Forward scalp: (place) 1
Rear scalp: 2
Left side scalp: 3
Right side scalp: 4
Left ear: 5
Right ear: 6
Forehead: 7
Left eye: 8
Right eye: 9
Left temple: 10
Right temple: 11
Left cheek: 12
Right cheek: 13
Left jaw: 14
Right jaw: 15

So a full-face mask that has eyeholes: 111100001000000 (Right jaw, left jaw, right cheek, left cheek, forehead)

A yarmulke: 000000000000010 (rear scalp)

An eyepatch over the right eye: 000000100000000 (right eye)

And every coverage option translates to a unique decimal number. The full-face mask would be 30784, the yarmulke would be 2, the eyepatch would be 256. It would be easy to decode these decimal numbers into binary strings, and then you could use the string as a series of flags to determine what areas the clothing covers.

Re: Coding Workshop

Posted: Tue 08 Jun, 2021, 1:11 am
by InspectorCaracal
noelle wrote:
Tue 08 Jun, 2021, 12:33 am
This is a purely idle thought, but you might find an advantage in encoding this into binary, where each location gets a place in the number. So, for example:

Forward scalp: (place) 1
Rear scalp: 2
Left side scalp: 3
Right side scalp: 4
Left ear: 5
Right ear: 6
Forehead: 7
Left eye: 8
Right eye: 9
Left temple: 10
Right temple: 11
Left cheek: 12
Right cheek: 13
Left jaw: 14
Right jaw: 15

So a full-face mask that has eyeholes: 111100001000000 (Right jaw, left jaw, right cheek, left cheek, forehead)

A yarmulke: 000000000000010 (rear scalp)

An eyepatch over the right eye: 000000100000000 (right eye)

And every coverage option translates to a unique decimal number. The full-face mask would be 30784, the yarmulke would be 2, the eyepatch would be 256. It would be easy to decode these decimal numbers into binary strings, and then you could use the string as a series of flags to determine what areas the clothing covers.
It's an interesting idea, but my first thought is it would be a code comprehension nightmare. >.> I'd have to keep a reference table in the docstring for the .py lmao

I'm not really sure how you'd use this for putting clothes on or taking them off, either. Manually define which flag goes into which list in a custom parser? For like, what, it'd be a few dozen slots at least

Re: Coding Workshop

Posted: Tue 08 Jun, 2021, 2:48 am
by noelle
Ultimately, I think you're going to need a reference table regardless unless you're very verbose with your variable names.

As for putting on/taking off clothes, I was assuming that any given item of clothing would be associated with an integer that determined what parts of the body it covered, with a flag to determine what section of the body it applied to. So you might have something like:

Code: Select all

{position: 0, coverage: 30784}
And that would tell you position: 0 is "head", coverage: 30784 is "Right jaw, left jaw, right cheek, left cheek, forehead".

And what the clothing actually looks like is a different string entirely in the dictionary (e.g.

Code: Select all

{position: 0, coverage: 30784, description: "A stone mask with holes at the eyes. It has angry eyebrows and a frown at the mouth."}
).

But again, it's an idle thought.

Re: Coding Workshop

Posted: Tue 08 Jun, 2021, 4:03 am
by InspectorCaracal
noelle wrote:
Tue 08 Jun, 2021, 2:48 am
Ultimately, I think you're going to need a reference table regardless unless you're very verbose with your variable names.

As for putting on/taking off clothes, I was assuming that any given item of clothing would be associated with an integer that determined what parts of the body it covered, with a flag to determine what section of the body it applied to. So you might have something like:

Code: Select all

{position: 0, coverage: 30784}
And that would tell you position: 0 is "head", coverage: 30784 is "Right jaw, left jaw, right cheek, left cheek, forehead".

And what the clothing actually looks like is a different string entirely in the dictionary (e.g.

Code: Select all

{position: 0, coverage: 30784, description: "A stone mask with holes at the eyes. It has angry eyebrows and a frown at the mouth."}
).

But again, it's an idle thought.
I mean, I was just going to name them, like, "right_eye" <.<

I get the bitmask as a concept but I'm not making the mental connection to actual implementation in the game, still. Like I can follow setting the flags in the data for the clothing item, but I can't figure out how this works when clothing items interact with each other in an outfit, if that makes sense?

Like say you're wearing a set of underwear - let's say underpants and a sports bra - and then you put on a dress that covers your shoulders, chest, lower back, hips, butt, and thighs. So the game needs to know that people can see the bra (in the open upper back) and the dress. Then you put on a bolero jacket that covers your shoulders, upper back, upper arms, lower arms. Now people can see the jacket and the dress, but not the bra.

(I think this is the main thing I'm stuck on here in general, tbh? Implementing clothing interactions.)

Re: Coding Workshop

Posted: Tue 08 Jun, 2021, 6:20 pm
by noelle
Ah, I get you. Hm. I think in that circumstance I'd just implement it as a list, and walk the list to see what's ultimately visible. So in your example, you'd have

Code: Select all

["sports bra", "dress", "bolero jacket"]
"sports bra" has ["upper back", "chest", "shoulders"], so it's visible in those slots.
then "dress" has ["chest", "belly", "sides"], so it covers the sports bra in the "chest" slot.
then "bolero jacket" has ["sides", "shoulders", "upper back"], so it covers the dress in the "sides" slot and the sports bra in the "shoulders" and "upper back" slots.

Maybe that only makes sense to me, and maybe I'm making it too simple for what you want to do. >_> I can work up a demo if you like?

Re: Coding Workshop

Posted: Wed 09 Jun, 2021, 4:29 am
by InspectorCaracal
noelle wrote:
Tue 08 Jun, 2021, 6:20 pm
Ah, I get you. Hm. I think in that circumstance I'd just implement it as a list, and walk the list to see what's ultimately visible. So in your example, you'd have

Code: Select all

["sports bra", "dress", "bolero jacket"]
"sports bra" has ["upper back", "chest", "shoulders"], so it's visible in those slots.
then "dress" has ["chest", "belly", "sides"], so it covers the sports bra in the "chest" slot.
then "bolero jacket" has ["sides", "shoulders", "upper back"], so it covers the dress in the "sides" slot and the sports bra in the "shoulders" and "upper back" slots.

Maybe that only makes sense to me, and maybe I'm making it too simple for what you want to do. >_> I can work up a demo if you like?
That sounds like the idea I'm going for but I still can't make the jump from concept to code lol

I don't think it needs to be a whole demo but some explanatory pseudocode would probably be hugely helpful

Re: Coding Workshop

Posted: Fri 11 Jun, 2021, 4:35 am
by InspectorCaracal
Been chewing on this some more to try to figure out how things go together

The player uses the "wear" command. This calls the at_wear method on the clothing object and passes the caller. That part's a given; the question is what exactly at_wear does and how the slot info is passed around and referenced. So:
  • The clothing object has to know what slots it covers.
  • The caller has an outfit object which has to know what slots the caller has and what's on them.
  • The at_wear method needs to tell the outfit object what slots to put the clothing object in.
  • The outfit object needs to add the clothing object onto those slots.
  • The outfit object needs to display the most recently added clothing object for each slot, maintaining the order of the slots, without repeating clothing items.
I'm thinking... the outfit's data is a dict of lists? The list for each dict entry maintains order and I think Python probably has some easy way to do LIFO with lists. And dicts are a pain in the butt to do ordering with but I could just have the slot order hard-coded into the method that turns the slots data into an outfit description and check the dict values by key.

Okay, I'm getting that far and getting stalled again by the opacity question. i.e. whether or not you can see the item underneath. Maybe instead of a list of clothing object references it's a list of tuples? First item in the tuple is the clothing, second is a true/false opacity flag? And then when it walks through the dict keys in the outfit to generate the description it steps down in each list until it either finds None or a tuple with a False as the second item. I think that'd work?

Re: Coding Workshop

Posted: Mon 21 Jun, 2021, 6:13 am
by noelle
I'm sorry it took me so long to respond to this. -_- Right now it is Very Late and it would be a mistake for me to do any real coding, but for now I can direct you to Python's collections module (which is built-in; you can just import it without having to deal with pip), which provides structures like deque (double-ended queue, basically an expanded list that makes LIFO easy) and OrderedDict (which, as its name suggests, is a dict that pays attention to the order of its elements).

Tomorrow I'll try to work up some sample code.