Ruin0x11 / OpenNefia

(Archived) Moddable engine reimplementation of the Japanese roguelike Elona.
MIT License
115 stars 18 forks source link

Determine how to add custom containers to IChara, IItem and similar #149

Open Ruin0x11 opened 3 years ago

Ruin0x11 commented 3 years ago

With #147, the way location is calculated for map objects was changed. One unresolved question is how to add custom nested containers and have the location parenting still work.

Here's what I was trying to do:

  1. Create a shopkeeper's trunk, and put an Inventory inside its params table holding the shop's contents.
  2. Open the trunk and receive items from it.
  3. Expect the containing_map() of the items in the trunk to be the containing map of the parent item.

However, the trunk item has no knowledge of special things added by the params table or similar. Items can have an inv, which is an Inventory provided by the engine itself for items that "look like" containers (as in, those that have the "elona.container" category). The ILocation implementation for items delegates exclusively to this inv field, bypassing any containers we added even if their _parent fields are set correctly.

But what if we wanted to have our own custom containers in addition to inv that can hold more items, or have special behaviors like capacity/weight limits?

Admittedly, just using inv instead of making a params.shop_inventory field was the right way to go in this specific example. That's what vanilla already does, and it's as simple as it gets. But this issue did remind me that wanting custom added containers could be a legitimate use case.

If we decide we want to shoulder the potentially significant complexity this will add, we'd need some way of:

  1. Declaring what fields are visible as containers to the overarching object.
  2. Be able to call :iter_items() or similar on the parent item and have the iterator "see" all the items from both its built-in invand the custom modded container in params.shop_inventory or similar.
  3. Have all the other ILocation methods cooperate with modded containers in a sane way. If there is some object inside an added container that's "registered" with a parent IItem map object, then ILocation:has_object(item) needs to return true by peeking inside not only the item's inv, but inside the other modded containers also.

A small anecdote: the one part of the original codebase that caused me to think that "maybe rewriting Elona from scratch is actually not a bad idea" was the special-case inventory logic for the cooler box. I wondered how it would be possible to mod in your own items like that with their own special container logic using the HSP codebase, but given the severe amount of hardcoding and global state surrounding how the cooler box worked with preventing items from rotting and having weight capacity, I had to wonder how much time it would take until such a cooler box would be possible to implement at all by writing a separate mod. When I couldn't find a good answer to that question, I (proverbially) threw my hands in the air and began to work on what would eventually become OpenNefia.

Ruin0x11 commented 3 years ago

Perhaps an aspect that implements an interface like IAspectStorage would be useful. When iterating the objects inside an aspect holder, any objects inside an aspect implementing such an interface would be joined to the main iterator. This is so you can have more than one aspect that can act like a container on the same object (each concrete aspect interface can only have at most one instance on each object).