Adding Stats to Characters ... easily

A place for Ren'Py tutorials and reusable Ren'Py code.
Forum rules
Do not post questions here!

This forum is for example code you want to show other people. Ren'Py questions should be asked in the Ren'Py Questions and Announcements forum.
Message
Author
User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Adding Stats to Characters ... easily

#1 Post by Remix »

So, you want to add Stats to your Ren'py Characters...
Perhaps you want your love interest and friends to all have height, weight, blood type and age...
Perhaps you want your companions to have attack level, defence score, hit points and mana points...

The most basic way is to default a unique named variable for each stat you have and hold it independent of the character in question:

Code: Select all

define k = Character("Kaori")
default kaori_height = 156
default kaori_weight = 48
default kaori_blood = "A"
default kaori_age = 19
All perfectly acceptable and works fine within Ren'py, especially for games with only a few characters.
Where it gets somewhat messier is with hoards of characters or hoards of stats per character or "eeek" hoards of characters each with hoards of stats.
pesky person reading this over your shoulder wrote:Isn't there an easier way? A nicer way, some way of doing something like:

Code: Select all

define k = Character("Kaori", height=156, weight=48)
label a:
    "[k.height] : [k.weight]"
Unfortunately not, there simply isn't... it's just not possible... the Character object (or rather the object returned from that factory function which pretends to be a class) is a fussy little rascal and just does not allow keywords that it does not recognize.
person behind the pesky person behind you, also rudely reading over your shoulder wrote:Didn't the great PyTom once mention something about using the character namespace alongside another object and using that object to store statistics?
Odd you should mention that, yes he did in This Post which outlines the concept.
another of pesky person's friends... rudely a reading along with the others wrote:Ok, so job's a goodun, problem sorted, all done. We just do that.
Yup, you can do that, implement a DataObject of your own and shoe-horn it in like shown.
After that you can rollback or save and then come back and read the rest of this thread...
You wrote:So, what's the problem then?
Well, the first problem is: How did you write that without touching your keyboard? You sure it wasn't one of that huge crowd reading over your shoulder?

Anyway, getting back on track...
Ren'py will save/pickle/serialize/whatever an object if the reference to the object changes its value after the initialization phase... If we say 'kaori_age = 19' then say 'kaori_age += 1' the value of kaori_age changes from being integer 19 to integer 20, which is a change so is saved. If we say 'k = DataObject( some params )' and then change an internal value of that object the reference k is still pointing at DataObject, rather than the internal attribute, which does not get recognized as a change and therefore is not saved.
The Ren'py Ducumentation wrote: What Is Saved
The python state consists of the variables in the store that have changed since the game began, and all objects reachable from those variables. Note that it's the change to the variables that matters - changes to fields in objects will not cause those objects to be saved.
...
An impatient member of the 'over the shoulder reading' crowd wrote:Enough with the explanations already. No more dithering. Just show us the code !!!
*cough* I was teaching...
Anyway:
The Base Class ( just leave this bit alone... extend it with a child class (shown below) )

Best advice: Just save this in a separate file called something like 01base_stats.rpy

Code: Select all


          ######################################
          #         The Python Class           #
          ######################################

           # this should go in a separate .rpy

init python:

    class BaseStatsObject(object):
        """
        Base class for defaulting stats and integrating with the store.

        Designed to be extended by just overloading the constants


        Example of extended class

        class EnemyStats(BaseStatsObject):

            # Set the store.{prefix}.character_id value
            STORE_PREFIX = "enemy_stats"

            # Boolean toggle for validation - defaults both True
            VALIDATE_VALUES = True
            COERCE_VALUES = False

            STAT_DEFAULTS = {
                'element' : 'earth',
                'hp' : 50,
                'mp' : 40,
                'rarity' : 0.075,
            }

        """

        STORE_PREFIX = "character_stats"
        VALIDATE_VALUES = True
        COERCE_VALUES = True

        STAT_DEFAULTS = {}

        def __init__(self, id, **kwargs):
            """
            Initialize values from store or kwargs or default

            @param id: A unique id to use in the store. Generally set to 
            the Character reference to allow cross object lookups

            @param **kwargs: Setup values that are not default
            """

            if not isinstance(id, basestring):
                id = str(id) # should raise if not stringable

            self.__dict__['_id'] = id

            self.run_optional_method( '__pre_init__', id, **kwargs )

            store_name = "{prefix}.{suffix}".format(
                prefix = type(self).STORE_PREFIX,
                suffix = self.__dict__['_id'] )

            setattr(store, store_name, {})

            self.__dict__['_store'] = getattr(store, store_name)

            # We use:
                # Store value
                # else kwargs value
                # else default value

            for key, value in kwargs.items():

                if key not in self.__dict__['_store']:

                    setattr(self, key, value)

            for key, value in type(self).STAT_DEFAULTS.items():

                if key not in self.__dict__['_store']:

                    setattr(self, key, value)

            self.run_optional_method( '__post_init__', id, **kwargs )


        def run_optional_method(self, 
                                method_type='post_init', 
                                *args, 
                                **kwargs):
            """
            Run a method of the object if it exists
            """
            try:
                getattr( self, self.__dict__[ method_type ] )( *args,
                                                               **kwargs )
            except:
                pass


        def get_validated_value(self, key, value):
            """
            Return a value after validating where applicable
            """

            if not type(self).VALIDATE_VALUES:
                return value

            if not key in self.__dict__:
                return value

            default_type = type( self.__dict__[key] )

            if isinstance(value, default_type):
                return value

            if type(self).COERCE_VALUES:
                try:
                    return default_type(value)
                except:
                    pass

            raise TypeError, "Supplied value '{0}' for key '{1}' does not " \
                             "match the existing '{2}'".format(
                                value, 
                                key,
                                default_type)


        def __setattr__(self, key, value):

            value = self.get_validated_value(key, value)

            self.__dict__[key] = value

            # Anything not recognized as an attribute of object
            # is placed into the store

            if key not in dir(object):

                self.__dict__['_store'][key] = value


        def __getattr__(self, key):

            try:

                return self.__dict__['_store'][key]
            
            except:
            
                if key in self.__dict__:

                    return self.__dict__[key]
                
                else:

                    try:

                        # try the character object
                        value = getattr(
                                    getattr( character, self._id ), 
                                             key )

                        if key != 'name':
                        
                            return value

                        # substitute the name (for interpolation/translations)
                        return renpy.substitutions.substitute(value)[0]

                    except:
                        
                        pass
 
            return super(BaseStatsObject, self).__getattr__(key)


        def __getattribute__(self, key):

            # Check if the attribute is an @property first

            v = object.__getattribute__(self, key)

            if hasattr(v, '__get__'):

                return v.__get__(None, self)

            # Try the store if the attribute is not in base object

            if key not in dir(object):

                try:
                
                    return self.__dict__['_store'][key]
                
                except:
                
                    pass

            return super(BaseStatsObject, self).__getattribute__(key)
    

        def __setstate__(self, data):
            self.__dict__.update(data)


        def __getstate__(self):
            return self.__dict__


        def __delattr__(self, key):
            del self.__dict__[key]
You could, as suggested, just throw that in a separate .rpy file to keep your code tidy.

Extending the Class and using it

Code: Select all

          ######################################
          #     An Extending Python Class      #
          ######################################


init python:

    class CharacterStats(BaseStatsObject):

        # Set the store.{prefix}.character_id value
        # STORE_PREFIX = "character_stats"

        # Boolean toggle for validation - defaults both True
        # VALIDATE_VALUES = False
        # COERCE_VALUES = False

        STAT_DEFAULTS = {
            'gender' : 'f',
            'age' : 22,
            'location' : 'school',
            'affection' : 1,
            'obedience' : 0.01,
        }


          ######################################
          #   Ren'py Character & Stats Setup   #
          ######################################


default test_name = "Amber"
define character.a = Character("[test_name]", who_color="#F9A")
default a = CharacterStats("a", location='lounge', affection=15, age=21)

define character.e = Character("Eileen")
default e = CharacterStats("e", location='study', affection=5)


          ######################################
          # Basic Test Label to check it works #
          ######################################

label start:

    "Start"

    e "Expected: Eileen 22 study 5 0.01
    \nReceived: [e.name] [e.age] [e.location] [e.affection] [e.obedience]"

    $ e.location = 'hall'
    $ e.obedience += 0.01
    $ e.affection = "10" # coerce to integer (if set)

    e "Expected: Eileen 22 hall 10 0.02
    \nReceived: [e.name] [e.age] [e.location] [e.affection] [e.obedience]"

    $ e.affection += 15.0 # coerce to integer
    $ e.location = 'classroom'
    $ e.obedience += 0.02

    e "Expected: Eileen 22 classroom 25 0.04
    \nReceived: [e.name] [e.age] [e.location] [e.affection] [e.obedience]"

    "Middle"

    a "Expected: Amber 21 lounge 15 0.01
    \nReceived: [a.name] [a.age] [a.location] [a.affection] [a.obedience]"

    $ a.affection += 1
    $ a.location = 'on the sofa (dancing)'

    a "Expected: Amber 21 on the sofa (dancing) 16 0.01
    \nReceived: [a.name] [a.age] [a.location] [a.affection] [a.obedience]"

    "The clock strikes midnight... Happy Birthday Amber"

    $ a.age += 1
    $ test_name = "Dancing Queen Amber"

    a "Expected: Dancing Queen Amber 22 on the sofa (dancing) 16 0.01
    \nReceived: [a.name] [a.age] [a.location] [a.affection] [a.obedience]"

    $ amber_color = a.who_args.get('color', 'Failed')

    "Expected '#F9A' and got '[amber_color]'"

    "End"
Note we use:
define character.a = Character( .....
# that uses the character namespace for that object

# then we default our data object globally ( using the same id ('a' in this case) makes sense )
default a = CharacterStats("a",
# and note the first argument "a" is also the same ( which ties the objects together nicely and allows us to drop the namespace when calling attribute from the Character object ( like a.name rather than character.a.name ) )
The cutest one of the 'shoulder perchers' wrote:Brief scan looks good. Does it actually work with saving, loading, rollback and all that?
Well, I've tested a few saves, those loaded fine, rollback/forward also ran perfectly.
The cute one again, now they found their voice wrote:Oh, I see it also has validation and coercion. Nice touch
Thanks. It is very basic though, just takes the type of the default value and validates/coerces (if the constant is set) any kwargs or value change that occurs.
Same one wrote:Just one more question...
Oh drat, Friday and friends calling to invite me out. ( Yup I do have friends, ones who do not read over my shoulder and even some that aren't imaginary )

Feel free to post any questions... Back later in the weekend.


Addendum and Changes

Validation is now done against the type of the first declared value, meaning you can pass in a parameter of a different type to the default one and it will use that type for validation instead.

All attributes that are not part of python's base object are stored in the store, meaning you can pass in an arbitrary parameter and it will be stored. For example:
default a = CharacterStats("a", non_default_variable_name=17)
will accept the non_default_variable_name parameter and save it for you.

In order to support @property attributes/methods I reworked the getattr/getattribute pair (along with adding calls to __pre_init__ and __post_init__ methods if they exist).
The @property methods could be quite useful if you are looking to bind a value to a particular range, e.g:

Code: Select all

init python:

    class CharacterStats(BaseStatsObject):

        STAT_DEFAULTS = {
            'affection' : 35
        }

        @property
        def affection(self):

            value = self.__dict__['affection']

            if not ( 0 <= value <= 100 ):

                value = max( 0, min( 100, value ) )

                self.affection = value

            return self.__dict__['affection']
Would make sure that your amber.affection value stayed in the range 0 to 100.

You could also overload the __setattr__ method, put known key values into range and then call the super if wanted


A script.rpy with all the above code in one file.
After testing, I suggest moving the base class to its own file to keep your code tidy.
script.rpy
(8.51 KiB) Downloaded 751 times
Last edited by Remix on Fri Aug 02, 2019 8:07 am, edited 5 times in total.
Frameworks & Scriptlets:

ReDZiX
Regular
Posts: 32
Joined: Thu Jan 04, 2018 12:42 pm
Contact:

Re: Adding Stats to Characters ... easily

#2 Post by ReDZiX »

This is rather interesting! Thank you for your hard work and for the easy and very fun to read explanation, I'll be sure to give this a try.

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Adding Stats to Characters ... easily

#3 Post by Remix »

ReDZiX wrote: Mon Feb 05, 2018 2:31 pm This is rather interesting! Thank you for your hard work and for the easy and very fun to read explanation, I'll be sure to give this a try.
Thanks. Hopefully someone will find it useful, perhaps even improve on it :)
Frameworks & Scriptlets:

User avatar
Gryphbear
Regular
Posts: 91
Joined: Wed Jun 16, 2010 11:45 pm
Contact:

Re: Adding Stats to Characters ... easily

#4 Post by Gryphbear »

Fascinating. I'll have to figure out what kind of 'stats' I want to put in this system, if I can get it to work.
WIP: Ring av Guder - (Contemporary Fantasy VN/Dating Sim?)

What do you get when you inherit a mystical ring, meet Norse Dieties, and Werewolves, Satyrs, or Dwarves? Possibilities.

General Completion: 5% - (Still working on story plot.)
Story status: 1% (In progress of revamping)
Scripting: 1% (Prologue is about done)
Character Art: 0% (Would like to find a Bara-type Artist, but will learn to draw them myself if necessary)
BG/CG's: 0% (None so far)

User avatar
isak grozny
Regular
Posts: 46
Joined: Mon Apr 11, 2016 10:17 am
Projects: The Bitter Drop [Modern Fantasy][Gay Romance][BxB]
itch: grrozny
Location: Edinburgh, UK
Contact:

Re: Adding Stats to Characters ... easily

#5 Post by isak grozny »

Hey, I'm using this! It's great, definitely simplifies stat tracking.Thank you very much for this write-up <3

Also, I added a couple of methods and functions to enable calculating stat increases/decreases using inkle's fairmath stats system, which is a more balanced way of tracking story-relevant stats. There's a brief write-up of fairmath here.

I can post what I came up with, if anyone is interested?

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Adding Stats to Characters ... easily

#6 Post by Remix »

isak grozny wrote: Tue Jul 24, 2018 3:32 pm Hey, I'm using this! It's great, definitely simplifies stat tracking.Thank you very much for this write-up <3
My pleasure. Always nice to have someone use some code, especially when that person understands enough to extend or alter it to their needs.
Also, I added a couple of methods and functions to enable calculating stat increases/decreases using inkle's fairmath stats system, which is a more balanced way of tracking story-relevant stats. There's a brief write-up of fairmath here.

I can post what I came up with, if anyone is interested?
I deliberately left the recipe quite sparse so as not to over-complicate things though there are a few changes I might add soon:
  • Alter the super call in getattr to use __getattribute__ so @property lookups work correctly
  • Change validate to use the type of the first set value (so kwargs can alter type if needed - works better if using mixins in multi hierarchy subclasses too)
  • Add optional pre/post methods around init and setattr to aid additional validation (and range bounding) where needed
I'm sure if anyone is looking at fairmath they can send a PM your way; though if you feel the methodology is useful enough then feel free to show some basic methods here.
I think it would also be quite fun to play with Askewnotion's Robust Random Functions inside the Stats class for certain value changes... Just not actually thought of anywhere I'd really really use it.

Anyway, I'm glad you appreciate the recipe and just shout if you need any help or want the *tweaks* done soon...
Frameworks & Scriptlets:

philat
Eileen-Class Veteran
Posts: 1900
Joined: Wed Dec 04, 2013 12:33 pm
Contact:

Re: Adding Stats to Characters ... easily

#7 Post by philat »

First, kudos to Remix since this recipe is well thought-out and includes some nice extra functionality. However, I'm curious as to the claim that save/load/rollback doesn't work with a bog standard creator-defined class object. It seems to work fine for me (as long it's instantiated with default rather than define as in PyTom's post, but that was probably under the assumption that it's a set of read-only data).

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Adding Stats to Characters ... easily

#8 Post by Remix »

In some instances it likely will work philat, especially if you use an object that extends the Ren'Py store in some fashion or in some way alter the referenced bytespace of the object. I might have that entirely wrong, just the documentation seems to imply that referenced classes will not be saved unless they have actually changed.

The Ren'py Ducumentation says, in regards to variables and objects:
What Is Saved
The python state consists of the variables in the store that have changed since the game began, and all objects reachable from those variables. Note that it's the change to the variables that matters - changes to fields in objects will not cause those objects to be saved.

I did try a few ways to do this in the early days and met no success other than by directly manipulating the store within the class, which does seem to work well now. Perhaps I was just making some amateur mistake back then...

On an aside:

I have made some changes to the old class to better support some usage requirements.
Please see the Addendum and Changes part of the initial post.
Frameworks & Scriptlets:

philat
Eileen-Class Veteran
Posts: 1900
Joined: Wed Dec 04, 2013 12:33 pm
Contact:

Re: Adding Stats to Characters ... easily

#9 Post by philat »

Code: Select all

init python:
    class CharData():
        def __init__(self, location, height):
            self.location = location
            self.height = height

define character.t = Character("Testing")
default t = CharData("school", 179)

label start:
    t "Testing 1\n[t.location] / [t.height]"
    $ t.location = "home"
    $ t.height += 10
    t "Testing 2\n[t.location] / [t.height]"
    t "Testing 3\n[t.location] / [t.height]"
I'm not having any issue with rollback/save/load in the above. In any case, it doesn't change that your implementation is a significant upgrade -- just curious about the mechanics of saving an object whose fields have been changed vs. manipulating store directly from the object.

User avatar
trooper6
Lemma-Class Veteran
Posts: 3712
Joined: Sat Jul 09, 2011 10:33 pm
Projects: A Close Shave
Location: Medford, MA
Contact:

Re: Adding Stats to Characters ... easily

#10 Post by trooper6 »

My 2 cents. That language in the documentation about saving is older language--it predates the introduction of the the default keyword and the expansion of the define keyword. Before the default keyword the coder had to do a bit of a complicated thing to get even just variables to work right (they had to declare the variables in an init python block...and then declare them again inside the start label). Now with default all those problems are gone. I suspect the default keyword also solved some of the problems with the saving class objects...but that the documentation doesn't reflect some of these changes. But I could be wrong on that.
A Close Shave:
*Last Thing Done (Aug 17): Finished coding emotions and camera for 4/10 main labels.
*Currently Doing: Coding of emotions and camera for the labels--On 5/10
*First Next thing to do: Code in all CG and special animation stuff
*Next Next thing to do: Set up film animation
*Other Thing to Do: Do SFX and Score (maybe think about eye blinks?)
Check out My Clock Cookbook Recipe: http://lemmasoft.renai.us/forums/viewto ... 51&t=21978

User avatar
isak grozny
Regular
Posts: 46
Joined: Mon Apr 11, 2016 10:17 am
Projects: The Bitter Drop [Modern Fantasy][Gay Romance][BxB]
itch: grrozny
Location: Edinburgh, UK
Contact:

Re: Adding Stats to Characters ... easily

#11 Post by isak grozny »

Hi! Could you please provide an example of how one would use one of the stats as an inventory, i.e.a dictionary of items and their amounts?

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Adding Stats to Characters ... easily

#12 Post by Remix »

I am poised on answering this... just finding the time to test things before making a reply
Frameworks & Scriptlets:

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Adding Stats to Characters ... easily

#13 Post by Remix »

isak grozny wrote: Sun Sep 30, 2018 5:27 am Hi! Could you please provide an example of how one would use one of the stats as an inventory, i.e.a dictionary of items and their amounts?
Though, at a basic level, you could just define each item as a stat with its quantity as value, it maybe better to create the items as objects (thus allowing each to behave differently)
Basic:

Code: Select all

init python:

    class CharacterStats(BaseStatsObject, object):

        STAT_DEFAULTS = {
            'affection' : 25,
            'bananas' : 0,
            'apples' : 0,
        }
with a simple
$ char_ref.apples += 2
or similar to alter it

Slightly more advanced would be to have the value as a list or dict and address each part:

List:

Code: Select all

init python:

    class CharacterStats(BaseStatsObject, object):

        STAT_DEFAULTS = {
            'affection' : 25,
            'bananas' : [0, "Yellow Bananas"],
            'apples' : [0, "Green Apples"],
        }
with a simple
$ char_ref.apples[0] += 2
e "I have [e.apples[0]] [e.apples[1]]" >> I have 2 Green Apples
or similar to alter/show it

Dict:

Code: Select all

init python:

    class CharacterStats(BaseStatsObject, object):

        STAT_DEFAULTS = {
            'affection' : 25,
            'bananas' : {'amount': 0, 'title': "Yellow Bananas"},
            'apples' : {'amount': 0, 'title': "Green Apples"},
        }
with a simple
$ char_ref.apples['amount'] += 2
or similar to alter it... trickier to interpolate though.

Most inventories though like to have a bit more control and so (with a little python experience) it makes sense to create the Inventory as a named 'stat' of the character and have that (or those (multiple)) contain objects representing the items in question.

Rather than fully explain the intricacies, perhaps just try this basic framework and see how things go:
stats_with_inventory.rar
(4.15 KiB) Downloaded 332 times

The rar contains a /lib/ folder with the 4 main files (base stats, inventory, items, character definition) along with a test rpy file to show a label.
Just add them to a project and have a look
Note: Press [Alt] + [A] to access the label automatically or add a jump/call to it in your script
Last edited by Remix on Wed Oct 10, 2018 2:57 pm, edited 1 time in total.
Frameworks & Scriptlets:

User avatar
isak grozny
Regular
Posts: 46
Joined: Mon Apr 11, 2016 10:17 am
Projects: The Bitter Drop [Modern Fantasy][Gay Romance][BxB]
itch: grrozny
Location: Edinburgh, UK
Contact:

Re: Adding Stats to Characters ... easily

#14 Post by isak grozny »

Wow, fantastic! That's exactly what I needed, complete with different containers within the inventory itself. I'll have to update all 27 character definitions (I ... have a large cast ...) but it's not more than an hour's work.

Is there any material way I could express my thanks? Do you have a ko-fi tip jar or do you need some writing or betaing/editing done?

EDIT: One question, though! I understand how to use this code, but for one thing: how do I add a unique inventory for different characters at initiation? Right now, I can only figure out how to add a default inventory to characters, e.g. give every character 3 rolls of scotch tape, and not, e.g. give Eileen 2 needles and give Alice 4 pincushions at initiation.

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Adding Stats to Characters ... easily

#15 Post by Remix »

isak grozny wrote: Wed Oct 10, 2018 8:23 am Wow, fantastic! That's exactly what I needed, complete with different containers within the inventory itself. I'll have to update all 27 character definitions (I ... have a large cast ...) but it's not more than an hour's work.

Is there any material way I could express my thanks? Do you have a ko-fi tip jar or do you need some writing or betaing/editing done?

EDIT: One question, though! I understand how to use this code, but for one thing: how do I add a unique inventory for different characters at initiation? Right now, I can only figure out how to add a default inventory to characters, e.g. give every character 3 rolls of scotch tape, and not, e.g. give Eileen 2 needles and give Alice 4 pincushions at initiation.
I have altered the previous post with some updated files...
( I was holding an 'owner' attribute in the inventory which meant 'self' had to be passed in from the character stats instance, making it awkward to use from a 'type' metaclass (as self does not exist within that). Updated to remove that (it's not really needed) and things seem to work)

Now, as the test_inventory_character.rpy shows, it is easier as you can do:
default test_inventory_character = CharacterStats('test_inventory_character', bag=Inventory(needle=2))

You would likely want to add a few methods down the chain, perhaps a has_item() method in the character stats etc to make some tests easier. Hopefully those are straight-forward enough for you to do yourself ( as the object chain is quite basic ... char.container.item ).

No tip jar at present so no need to pay anything. (I might do a Patreon one day, once I have more useful code snippets)
Frameworks & Scriptlets:

Post Reply

Who is online

Users browsing this forum: No registered users