Sign in to follow this  

Is my object-structure bad? [Related to Text RPG]

This topic is 835 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm building a text-RPG and, while everything's working fine for now, I'm concerned that my structure sucks. The primary objects in play are:

  • Characters
  • Rooms
  • Items
  • Notifiers
  • Handlers (Attack, Item, and Movement handlers)
  • World (stores handlers, notifiers and events)

The basic process is something like this:

  1. Character.move() from Room A to Room B
  2. World registers a move-event
  3. MoveNotifier.notify()
  4. Registered handlers receive notification and do their thing (i.e. - MoveHandler takes the pointer to Character stored in a member of Room A and moves it to the respective member in Room B. DescriptionHandler prints out the appropriate Room B description, what items it contains, etc.)

(I know the notifier-handler thing may be a bit overkill for a single-player text-RPG, but this is all practice for the big picture—please think of this as early practice for creating a larger, multi-player game.)

 

Here's my worry: Many of these objects store pointers to other objects. Character has a pointer to the Room in which it stands, while Room has a vector of pointers to every Character contained within it. This doesn't feel right. Part of me thinks that Character should know what Room it stands in, but another part of me wants every object to be totally independent and self contained. If everything should be self-contained, then my question is:

 

Is there a standard line of containment? Should Room know what Character is in it? Should a Character know what Item it has equipped? Should an Item know what Enhancements it's infused with?

 

If nothing should be aware of anything else, then should everything be stored in a relational database? I've been looking into object storage w/ MySQL or some SQL variant anyway.

Share this post


Link to post
Share on other sites

I'm currently implementing an action system, and while not explicitly tailored to text adventures, it uses the same basics like Inform, TADS, and companions. Part of it are interrelations between objects (where "object" means the nouns in action sentences) where the information content is whether or not the interrelation exists. To avoid the bi-lateral (or even multi-lateral) referencing Josh has mentioned, interrelations are implemented in look-up tables with the ordered set of object IDs as composed key. For example:

Interrelation<2> presence;
bool playerIsInRoom = presence.exists( player.id, room.id );
bool itemIsInRoom = presence.exists( item.id, room.id );
id_t where = presence.resolve( character.id );
Edited by haegarr

Share this post


Link to post
Share on other sites

Excellent! Thank you, this was exactly the type of advice I was looking for (especially the one-way references). 

 

It's totally doable to get rid of most of the Character verbs and group everything up in the handler. My purpose for having them was moreso for programmer-intuition. Something like: If the player types in "get item," then the resulting command sent to to the game would be Character->get(item). The Character.get method tries to associate the "item" string that the player typed in with an item-pointer in the inventory. If that is successful, it really does just pass along a message—and the handler does all the work of removing the item-pointer from the Room and putting it in Character->inventory_,  printing out a message like "You get [item]," etc. Otherwise, we get something like "You don't have that item."

 

I can similarly get rid of the World class entirely in favor of the Character class directly creating events. Or even having the Character class itself do all the work that the handler would otherwise do (but I don't think this translates well into multi-player, and don't really like the idea of the main classes [Room, Character, Item] having direct access to one another). 

 

Maybe the best way to go about it would be:

  • Rooms know what Character they contain (vector of pointers to const)
  • Characters know what items they have (vector of pointers to const)
  • Notifiers notify
  • Handlers do the dirty work
  • Characters directly create events (move events, item events, etc.)

The most direct way would be just for Character A to call a method in Character B, but I don't like this idea (nor do I think it scales well to multiple characters interacting). The World class existed to be the structure in which the notifiers/handlers existed, but there's nothing wrong with them just free-floating.

 

Maybe I'm just thinking too much about structure? (I love thinking about structure, but at the same time I want to progress!)

 

haegarr, that sounds pretty awesome, actually. Let me make sure I have the idea straight:

 

  • Define an Interrelation container which just holds pairs of unique IDs, and call an instance of this "presence," for example.
  • For a character to contain an item, the ID of the character and the ID of the item are combined and put in the table.
  • For a room to contain an item, the ID of the room and the ID of the item are combined and put in the table.
  • To check if a room contains a character, just see if the ID-ID key is in the table.

Is this correct?

Share this post


Link to post
Share on other sites

[…] Let me make sure I have the idea straight:
 
Define an Interrelation container which just holds pairs of unique IDs, and call an instance of this "presence," for example.
For a character to contain an item, the ID of the character and the ID of the item are combined and put in the table.
For a room to contain an item, the ID of the room and the ID of the item are combined and put in the table.
To check if a room contains a character, just see if the ID-ID key is in the table.

 

Is this correct?

Yes, indeed. Doing so has (at least for me) many advantages, up to kind of knowledge representation and the ability to let the player ask questions about relations.

 

BTW: The mentioned way is not new either. It is used e.g. in relational databases all day. It resembles how terms in propositional / predicate logic can be notated, and how associations in topic maps can be modeled. 

Edited by haegarr

Share this post


Link to post
Share on other sites

It's funny. The natural way I would model this in code would be just like you have, a room has a list of what it holds, NPC's have a list of what they hold, etc. But if I was modeling the same thing in SQL, I'd do the exact opposite. Since GameObjects can only be possessed by one character and a character can only be inside one room, I'd probably have a foreign key on GameObject called ContainedByID. When we model the data this way, the data doesn't allow us to mess up and have a copy of the pointer in two places. 

 

But SQL also facilitates accessing the data this way. Writing a query to get everything contained by game object (#42) is trivial. In code, the naive solution would be to have a big list of ALL game objects, iterate through them all, and construct a list of everything with a ContainedByID == 42. Technically this works, but it won't scale. It might be perfectly sufficient for all your text-game needs, but if you had to put this logic in a render loop, I think it'd start choking.

Share this post


Link to post
Share on other sites


But SQL also facilitates accessing the data this way. Writing a query to get everything contained by game object (#42) is trivial. In code, the naive solution would be to have a big list of ALL game objects, iterate through them all, and construct a list of everything with a ContainedByID == 42. Technically this works, but it won't scale. It might be perfectly sufficient for all your text-game needs, but if you had to put this logic in a render loop, I think it'd start choking.

Of course. Hence I'm not using it as a general purpose solution but especially in player control / AI (the said action sub-system) and dialog (for knowledge and interrogation). This either means that the size of tables is small (or made small in some  pre-filtering steps) or their application is not time critical, at least in almost all use cases I've hit yet. Specialization could also help. E.g. instead of using a generic in( A, B ) you can split it into located( A, room ) and contained( item, container ). And with n-ary relations things get funny anyway, e.g. jobBearing( person, profession, location ). 

 

As always: Use the tool that fits the problem.

Share this post


Link to post
Share on other sites

This topic is 835 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this