As you probably guessed, having a bunch of if
statements in your code is just about the worst thing you could do. What you actually need, is to abstract different functionality in different "units" - be they classes, functions, scripts or whatever - and make them as self-contained as possible.
Here's how we do that. In our current game, characters can be either in an instance, or in town. We have so-called services, that run in their own threads and process players' requests and game logic. We have one town service and many instance services - one for each instance active on the server.
Thus, town service has logic to deal with town tasks, such as visiting a shop; instance service has logic to deal with instance tasks, such as casting spells. These are not the only services - for example, quests and achievements can happen both in town and in instance, and they're handled in a special quest service, that towns and instances communicate with.
But instance service doesn't contain all game logic pertaining to instances (for one thing, it would be enormous if it did). For example, there's MoveController
that handles, you guessed it, movement in instance. This allows us to (theoretically) add some new logic to movement - like, for example, add flying - by just creating another MoveController
-derived class. Not that we actually tried (-8
An example that is closer to your question: at start of development, we only had PvE instances, that closed whenever all mobs or all characters died. However, at some time we had to add PvP, which had a bunch of different rules about who won and who lost and when. That is, the game logic in PvP was the same as in PvE, except the finishing part. This is what we did: we created an object called ExitStrategy
, whose responsibility was to decide whether instance was to end, and what characters received rewards afterwards. Than we made several different strategies: PvEExitStrategy
, DuelExitStrategy
, TeamBattleExitStrategy
, etc. The instance service has an object of type ExitStrategy
, that is created during set-up, based on what type of instance it is. Thus, there's only one place with a bunch of if
's (switch
actually), neatly tucked in a short method in the startup sequence.
I want to emphasize that we didn't subclass InstanceService
instead. While this might seem like a logical thing to do, subclassing is actually bad decision in this case. Suppose we'd later want to add another type of instance - say, with some "mission objectives" to reach. If we used subclassing, we wouldn't be able to add this to both PvE and PvP instances without some code duplication. However, with aggregation we can easily add some ObjectiveManager
or something.
To reiterate: you want to have different logic in different places, as independent as possible?, and combine it with aggregation. If you do, you can use whatever "common" game logic you need in the instance, and add or override parts you don't need.
Now, if you're anything like me, this approach would actually leave you with a lot of components that are just not independent enough, and tend to break if you push them too far, especially on first try. But that's still way better than huge inheritance hierarchy, code duplication or, heaven forbid, bunches of if
's everywhere.