Update: this project won’t be developed any further
Few days ago i was re-reading something about parse-combinators, so i came up with a kind of combinator system for the logical part of my game (btw an horror game in the sense that if you have played anything else than you will end up screaming “oh my god, this is horrible!”).
I don’t know if it is a viable system or if it has been already implemented somewhere, I found the idea interesting and i like to share ideas.
The system is based on a Controller - TriggerSystem - (they can be more than one, i just use one for now). The controller has a list of Trigger. A trigger has a Condition and a Reaction. If the condition holds, controller executes the reaction. And the framework ends here (i like complicated stuff :D).
How it works in a concrete example. We start with an empty trigger:
Trigger trigger = new Trigger();
The game has pickable elements (batteries). A battery is picked up when the player goes near it, if the energy level is below its maximum. So we have two conditions combined toghether:
Condition nearPlayer = new SpatialsAreNear(playerSpatial, batterySpatial, new Threshold(0.5f));
Conditon energyMax = new Condition() {
public boolean hold(float tpf) { return gui.getEnergyLevel() == 100; }
};
Condition condition = nearPlayer.andNot(energyMax);
When the battery is picked up, the audioRenderer plays a sound, the energy bar is increased and the trigger is removed.
Reaction removeBattery = new DetachSpatial(batterySpatial);
Reaction playSound = new PlaySound(dingNode, audioRenderer);
Reaction increaseEnergyBar = new Reaction() {
public boolean act(float tpf) { gui.setEnergyLevel(gui.getEnergyLevel() + 25); }
};
Reaction removeTrigger = new RemoveTrigger(trigger);
The reactions are combined to be executed simultaneously:
Reaction pickup = playSound.and(removeBattery).and(increaseEnergyBar).and(removeTrigger);
Condition and reaction are used to setup the Trigger:
trigger.setCondition(condition);
trigger.setReaction(reaction);
And the trigger is passed to the controller:
triggerSystem.addTrigger(trigger);
The batteries are "animated" by rotating them around the y axis when the player is near them:
Condition condition = new SpatialsAreNear(player, battery, new Threshold(10f));
Reaction rotation = new RotateAround(battery, Vector3f.UNIT_Y, new Velocity(1f));
Trigger trigger = new Trigger(condition, rotation);
triggerSystem.addTrigger(trigger);
The doors are more interesting. There are a few automatic doors. When the player is near the door, the door opens. When the player is gone, the door closes. When the player moves, the system plays a sound.
triggerSystem.addTrigger(
new SpatialsAreNear(player, door, new Threshold(4f)),
new MoveTo(door, door.getWorldTranslation().add(0, 8, 0), new Velocity(2f), new Threshold(0.1f)));
triggerSystem.addTrigger(
new SpatialIsMoving(door),
new PlaySound(doorOpen, audioRenderer));
triggerSystem.addTrigger(
new SpatialIsNotMoving(door),
new StopSound(doorOpen, audioRenderer));
triggerSystem.addTrigger(
new SpatialsAreNear(player, door, new Threshold(10f)).negate(),
new MoveTo(door, door.getWorldTranslation(), new Velocity(1f), new Threshold(0.1f)));
One of the funniest thing is the fact that combinators can be shared so one could write a little library made with small "logic" bricks and the user could eventually combine those bricks to create a complicated logic (in the same way one can represent a complicated grammar using many little parser combinators).
What do you think? Am I totally crazy?