You are correct that it's not the View's job in an MVC architecture to implement any game mechanics like friendly fire.
But whether it's the controller's job or the model's job to implement game mechanics is a matter of debate.
There are basically two philosphies here:
In a thin model MVC architecture, the model is just a dumb data-structure and the game mechanics are implemented in the controller(s):
- The view should just visualize what happens in the model and provide the UI through which the player gives commands.
- The model should not implement game mechanics either. It should just be concerned with storing and maintaining the state of the game.
- Implementing the actual game mechanics should be the job of the controller.
For example, when the user makes an input through the view like "My character A uses attack B on enemy C", that input is handled by the
BattleController. The battle controller calculates the damage and calls the appropriate methods on the model, like
DoDamage(enemyC, 10) and
In a fat model MVC architecture, the model is smart and implements the game mechanics itself:
- The view still visualizes what happens in the model and provides the UI.
- The controller acts like a glue between view and model. It receives user input from the view and translates it into commands for the model.
- The model accepts those commands, handles the game mechanics, modifies the game state and informs the view about it.
For example, the user does some clicks on the view, the controller interprets these clicks and realizes they mean: "Player wants character A to perform attack B on enemy C" and sends that command to the model by calling a method like
model.doAttack(attackB, characterA, enemyC);.
The model then determines what that means. It looks up what attack B does, sees that it does 5 damage on the user and 10 damage on the target, calculates that damage, applies it to these entities, checks if they are still alive and then tells the view to visualize all that.
Which one you choose is up to you. Personally I would prefer to use a thin model with smarter controllers when I had a game with lots of complicated game mechanics but where all these mechanics end up interacting with all game entities in the same way (I would also call these controllers "Systems" and claim I am doing ECS). But if I had rather simple basic game mechanics but lots of gimmicky entities which twist these rules by using unique mechanics, I would prefer to put more logic in the model so the entities can handle their own logic through polymorphy.
Also keep in mind that design patterns are just suggestions how you can structure your game, not immutable laws. If you feel that diverting from them makes for a cleaner architecture in your specific case, by all means, divert from them.
My BattleSerializable class can handle the friendly fire logic, but now I have to return a complex custom return object instead of a simple integer to handle all the state that changed. This seems needlessly complex or, at the very least, in-eloquent.
The communication from the view to the model shouldn't happen through return values at all.
You don't know what model changes a player action will trigger. It is possible that it results in a whole cascade of changes. An AoE attack damages one enemy, poisons another, kills a third, the killed enemy explodes, the explosion kills off the first enemy, etc. etc.. How are you going to tell that story to the view? You would have to return a whole array of complex custom objects. But wait, what about model changes which are not triggered by the player? The enemy actions, for example? How do you communicate those to the view when the view wasn't the one who made the method call?
What you should do instead is have the model communicate with the view explicitly. There are different ways to do that:
- Push-Event communication: When something happens in the model, the model calls methods from the view to inform it about these events.
- Pull-Event communication: The model keeps a queue of events. The view empties that event queue and visualizes the actions it finds in it.
- Polling: The view iterates the whole model and updates its visualization of the game state. This often requires that the view keeps a separate model so it can detect changes. This pattern is called Model - View - ViewModel.