I've been writing a Component Entity System in javascript for a while now but I keep returning to a root issue.
How do you handle entity specific - that is to say a single instance or single type - functionality?
Here's the current situation:
The way I have structured things, when an item entity is stored in inventory by another entity it isn't destroyed, merely stripped of most of its components. This is so that if it is dropped, or perhaps retrieved for use, it can be reactivated with its old state. The components that are stripped are stored in an InstanceDataComponent attached to the entity (this is just a JSON object).
There is a small system for managing the internals of whether an item can be picked up and adding an inventory record with a hash, quantity, and id for the thing being stored but something needs to manage the transformation of that entity from its "item" state to its "stored" state. What should do this? It seems to me that the details of which component to remove and what data to alter will need to be nearly unique for each item.
Suppose that in the future I want an entity to switch between two behaviors on the fly. For example, to pace to and fro until it is disturbed then pathfind to the player. What will handle that transition?
I get the feeling I've got a fundamental misunderstanding of the issues and typical architecture here. What would be a clean way to handle this? Should I perhaps add a component for each set of behavior transitions? Wouldn't that end up with far too many components that are glorified callback wrappers? Or am I missing something about how an entity should be altered when it is stored in inventory?
Some thoughts for others who might be going through this situation.
My current solution (after several iterations) is to fire a global event e.g. itemPickupSystem:storedItem
and an entity can attach handlers for any events inside its factory method. This isn't scalable, for a number of reasons. I've been considering moving those events into a queue to be executed later.
My factory methods have turned into a hodgepodge of callback definitions and things are degrading into callback hell. In addition, this events system has to go, it is the only part of the entire system that breaks the serial nature of the game loop. Until now each system fired in a defined order and all logic resided inside those systems. Now I can't guarantee that an entity will be in a specific state because those callbacks could have been fired at different points. Finally, because execution is being turned over carte blanche to code that isn't part of the core functionality there is no way to know how large that call stack will get when an event is fired.
Sometimes it's easiest to think of this problem in terms of pragmatic network replication, and the boundaries between components come naturally.
Let's say that your actor can both wield, and store, a sword.
I would not anticipate a 'transform' from an inventorySword into a presentationSword, but rather that there's a natural delineation between an 'inventoryItem' and a 'swordPresentation'.
On the server, each player would be assigned a list of items in their inventory. Each item would have a unique id for the world. The inventory item might be derived as 'SwordItem' and have properties for SwordType and Condition%
On the client, a 'swordPresentation' component might handle the job of which mesh to display, which socket to attach animation data to when displayed via a 1st person camera, and how to smooth animation transitions. But none of that matters to the actual state of the game, it's simply how our client is seeing the world.
Potentially, if you were distributing the state of the game to each client all that you would pass over the network would be the current player's inventory, and for the other players, which item each player has currently equipped and where they are (assuming they're in eyesight)
So, consider creating a factory that creates a 'swordPresentation' based off of an inventory item, finding the bare minimum you can pass in as parameters to create a representation of the component (maybe it's sword type, sword % condition, etc).
Whatever that bare minimum is what you want to serialize as your inventory item.
Establishing a clear delineation between replicated data means better performance, fewer vulnerabilities when you're writing a multiplayer game. When you're writing a single-player game, it'll help you understand what goes into a save file.