Your player and your troll are nothing but sets of data, what we call the data Model which describes your world. Life, inventory, attack capabilities, their knowledge of the world even -- all consists in the data model.
Keep a single, main Model object which holds all data describing your world. It will hold general world information such as difficulty, physics parameters etc. It will also hold a list/array of specific entities' data as I've described above. This main model can consist of many subobjects in order to describe your world. Nowhere in your model should you have any functions that control game logic or display logic; getters are the only exception, and would be used only to allow you to get data from the model more readily (if public members don't already do the trick).
Next, create functions in one or more "controller" classes; you can write them all as helper functions in your main class, although this may get a bit large after a while. These will be called every update to act upon the entities' data for different purposes (movement, attack etc.). Keep these functions outside of an entity class is more resource-efficient, and once you know what describes your entity you will automatically know what functions need to act on it.
class Main
{
//...members variables...
var model:GameModel = new GameModel();
//...member functions...
function realTimeUpdate() //called x times per second, on a timer.
{
for each (var entity in model.entities)
{
//command processing
if (entity == player)
decideActionsFromPlayerInput(entity);
else //everyone else is your enemy!
decideActionsThroughDeviousAI(entity);
act(entity);
}
}
//OR
function turnBasedUpdate()
{
if (model.whoseTurn == "player")
{
decideActionsFromInput(model.player); //may be some movement or none at all
act(player);
}
else
{
var enemy;
for each (var entity in model.entities)
{
if (entity != model.player)
{
enemy = entity;
decideActions(enemy);
act(enemy);
}
}
}
}
//AND THEN... (common to both turn-based and real-time)
function decideActionsThroughDeviousAI(enemy)
{
if (distanceBetween(enemy, player) <= enemy.maximumAttackDistance)
storeAttackCommand(enemy, "kidney punch", model.player);
else
storeMoveCommand(player, getVectorFromTo(enemy, model.player));
}
function decideActionsFromPlayerInput(player)
{
//store commands to your player data based on keyboard input
if (KeyManager.isKeyDown("A"))
storeMoveCommand(player, getForwardVector(player));
if (KeyManager.isKeyDown("space"))
storeAttackCommand(player, "groin slam", currentlyHighlightedEnemy);
}
function storeAttackCommand(entity, attackType, target)
{
entity.target = target;
entity.currentAttack = attackType;
//OR
entity.attackQueue.add(attackType);
}
function storeMoveCommand(entity, motionVector)
{
entity.motionVector = motionVector;
}
function act(entity)
{
entity.position += entity.motionVector;
attack(entity.target, entity.currentAttack);
}
}
class GameModel
{
var entities:Array = []; //or List<Entity> or whatever!
var player:Entity; //will often also appear in the entity list, above
var difficultyLevel:int;
var globalMaxAttackDamage:int;
var whoseTurn:Boolean; //if turnbased
//etc.
}
A final note is that it is also useful to keep your display logic separate from your game logic. Display logic would be, "Where do I draw this on the screen and in what colour?" vs. game logic being what I've outlined in the pseudcode above.
(Dev's note: While using classes, this loosely follows a functional programming approach which considers all methods as ideally stateless, allowing for a clean data model and processing approach that minimises bugs caused by retained state. FP is the ultimate MVC, since it achieves MVC's goal of separation of concerns explicitly. See this question.)