Cslib_object: a dispatch from the trenches

Last year, with help, I added cslib_object to cslib, to help simulate objects. I’ve been using it for a while now and I thought I’d report back on where it works well and where it sucks.

Feel free to ignore this post if you aren’t interested in objects :slight_smile:

First, the explanation

As an example, here’s a ‘monster’ object:

First define your fields:

*create monster_field_1                     "name"
*create monster_field_2                     "tagline"
*create monster_field_3                     "description"
*create monster_field_4                     "type"
*create monster_field_5                     "location_id"
*create monster_field_6                     "strength"
*create monster_field_7                     "madness"
*create monster_field_8                     "damage"
*create monster_field_9                     "run"
*create monster_field_max                   9

Then create the ‘master’ object: this is what you will reference in your code:

*create monster_id                          0
*create monster_name                        ""
*create monster_tagline                     ""
*create monster_description                 ""
*create monster_type                        ""
*create monster_location_id                 0
*create monster_strength                    0
*create monster_madness                     0
*create monster_damage                      0
*create monster_run                         0

Now create various instances:

*create MONSTER_STONE_GARGOYLE              1
*create monster_1_name                      "Stone Gargoyle"
*create monster_1_tagline                   "An ambulant stone horror with wings"
*create monster_1_description               "Description TBD"
*create monster_1_type                      "Lesser Monster"
*create monster_1_location_id               0
*create monster_1_strength                  40
*create monster_1_madness                   3
*create monster_1_damage                    5
*create monster_1_run                       90

*create MONSTER_ANCIENT_VAMPIRE             2
*create monster_2_name                      "Ipswich Cobb"
*create monster_2_tagline                   "An ancient vampire"
*create monster_2_description               "Description TBD"
*create monster_2_type                      "Greater Monster"
*create monster_2_location_id               0
*create monster_2_strength                  35
*create monster_2_madness                   4
*create monster_2_damage                    4
*create monster_2_run                       90

*create monster_count                       2

Usage example

Let’s say I have a scene with a monster but it might be one of several. As an example, let’s say it is the vampire Ipswich Cobb.

First we ‘get’ the monster:

gosub_scene cslib_object get "monster" MONSTER_ANCIENT_VAMPIRE

Then we simply do something like:

You encounter ${monster_name},  ${monster_tagline}. 

*if (monster_type = "Greater Monster")
    Your amulet of banishing is useless against a ${monster_type}.
*else
    Your amulet of banishing has a ${50 - monster_strength}% chance of success.

When This Works Well

I’ve found this to be really successful in examples like the above where you have quite detailed ‘objects’, with only one of each type appearing in a scene (if coding this for real, the amulet would also be an object)

While there is a fair bit of work in the setup, the syntax is really simple and easy to follow in the code itself.

When This Sucks

This completely breaks down when you need to check more than one object of the same type in a scene, because there is only a single instance and one will overwrite the other. (you can create more than one instance by defining monster1, monster2 etc upfront but it is confusing and a real pain)

Where this failed spectacularly for me was when I wanted to have conditional menu items. Because you will have multiple menu choices in a single scene (and a different number depending on the scene), it quickly gets difficult to handle. This isn’t even counting the fairly restrictive way that *choice works.

So in this case, I found arrays by far the better option. For example, each location will have an associated menu item with a status of visible/selectable/etc

*create UNKNOWN                             0
*create UNAVAILABLE                         1
*create VISIBLE                             2
*create SELECTABLE                          3
*create COMPLETE                            4

*create STATUS                              1
*create NAME                                2
*create TAGLINE                             3

*create LOC_RAILWAY                         1
*create loc_1_1                             2
*create loc_1_2                             "The Railway Station"
*create loc_1_3                             "the tracks are rusty and overgrown with weeds"

*create LOC_TOWNSQUARE                      2
*create loc_2_1                             3
*create loc_2_2                             "The Town Square"
*create loc_2_3                             "a depressing, windswept place"

*create LOC_TOURIST_BUREAU                  3
*create loc_3_1                             1
*create loc_3_2                             "The Tourist Bureau"
*create loc_3_3                             "the place looks almost derelict"

Each menu item then becomes:

*if (loc[LOC_TOURIST_BUREAU][STATUS] = SELECTABLE)
    #Visit ${loc[LOC_TOURIST_BUREAU][NAME]} -  ${loc[LOC_TOURIST_BUREAU][TAGLINE]}
        *gosub_scene touristbureau

which is reasonably easy to follow. (It also guarantees that if the same menu item appears in different scenes, the text will always be the same)

Takeaway

I remember now what every beginning programmer has to learn: you can’t use objects for everything (looking at you,Java). But where they work, they work well.

TL;DR

Works well for small numbers of large objects. Not so good for more than one object of the same type in a single scene.

7 Likes

That is quite interesting aproach.

To be able to do it using a language whithout support for classes makes it even more amazing!

Hmm.

I see. Whe got two “active” objects, but can “acess” just one each time. So if I am acessing, for example, a monster, a weapon and a item in the same scene. Everytime I switch from an “active” monster to an “active” item or other object.

I would have to “save” the state of the active monster, then load the active item object to acess it’s values before saving it again so it can load back the monster… I get it right?

This parallelism in instances reminds me back in the day when limitations in hardware forced the programmers to “flick” sprites in the screen to make machines that could only show 3 sprite objects in screen to show a bigger number!

You can’t have two objects of the same type and name (see later in this reply), but you can have more than one type of object. In my example, the amulet would be a different type of object - say, “magic_items”, which has a different set of properties.

gosub_scene cslib_object get "monster" MONSTER_ANCIENT_VAMPIRE
gosub_scene cslib_object get "magic_item" ITEM_AMULET

The ‘get’ tells you the name of the object to use (monster, magic_item). The object has to be pre-defined of course.

Now, there is nothing to stop you defining two master objects of the same type, say “monster” and “another_monster”. You can then:

gosub_scene cslib_object get "monster" MONSTER_ANCIENT_VAMPIRE
gosub_scene cslib_object get "another_monster" MONSTER_STONE_GARGOYLE

but I find this a pain to keep track of. You can do it, but each master object must be pre-defined via create.

1 Like

Do you think Extend cslib_object's get_by_field and get (by index) methods to take a custom main instance prefix · Issue #44 · ChoicescriptIDE/cslib · GitHub would help?
Sorry I’m late to the party here!

1 Like

Yes, absolutely. In fact, I’ve found it necessary. I’ve implemented it on my machine but haven’t had the time to do it properly for the library. I’ll try and do so in my copious free time.

1 Like

Awesome! I realised we’d never actually done a formal release with the cslib_object module (though most people probably just copy & paste the .txt files anyway). Still, it’d be nice to do. I’ll hold off until we have that enhancement!