Skip to content

Instantly share code, notes, and snippets.

@Xananax
Last active August 29, 2015 14:12
Show Gist options
  • Save Xananax/2984ae74d64c0abd166b to your computer and use it in GitHub Desktop.
Save Xananax/2984ae74d64c0abd166b to your computer and use it in GitHub Desktop.

DeusInMachina (working title)

Template system to create behaviours attached to game objects.

Properties

properties are numbers such as:

  • health
  • resistance (and affiliates, resistance to poison, resistance to cold...)
  • skills (ranged weapons, lockpicking)

Affinities

affinities defines how much a gameObject loves or hates something. That something can be:

  • any property of a game object, for example:
    • type - ex: the gameObject hates orcs
    • health - ex: the gameObject loves weak people
    • ``

Provides

provides are what a gameObject can provide. Provides are dynamic and vary according to state.

  • A cow provides milk, mount, and if dead, meat
  • A human provides the items he has, the information he has, and if subdued, a slave.
  • A rock provides a weapon, and if destroyed, construction material

Some provides can occur by just modifying a number (ex: a human can lose money and remain a human), other convert the gameObject (a dead human is not considered a human anymore, it becomes another game object type).

Needs

Needs define what a gameObject...needs. Needs can be:

  • Time-based and volatile, they go away if unfulfilled (ex: want a drink)
  • Time-based and incremental if unfulfilled (ex: sleep, hunger)
  • State-based (ex: poisoned and needs remedy)
  • Affinity-based (ex: hates a gameObject and wants to attack it, loves reading and wants to sit and read a bit)

Decisions

Decisions are parsed in an array particular to each gameObject. The first decision that matches gets used, and the others discarded. Not all decisions get parsed for all gameObjects; For example, a rock would have no decisions at all, and a cow would not get to decide if it wants to read.

function eat(gameObject,target,callback){
    var hunger = gameObject.hunger;
    if(hunger > 5){
        if(!target){
            target = gameObject.surroundings();
        }
        var possibleTargets = new GameObject();
        target.each(function(target,next,done){
            if(target.provides('hunger')){
                var targetPosition = target.currentPosition();
                var distance = character.currentPosition().distance(targetPosition);
                var animated = target.is('alive') || target.is('undead');
                target.distance = distance;
                target.animated = animated;
                possibleTargets.push(target);
            }
        },function(){
            if(possibleTargets.length){
                possibleTargets.orderBy('animated','distance');
                possibleTargets.each(function(target,next,done){
                    if(target.is('alive')){
                        if(hunger < 10){return next();}
                        decisions.attack(gameObject,target,function(behavior){
                            if(behavior){
                                behavior(function(){
                                    if(target.state.dead){
                                        character.hunger.consume(target);
                                        return done();
                                    }else{
                                        return next();
                                    }
                                });
                            }else{
                                return next();
                            }
                        });
                    }else if(target.is('undead')){
                        return next();
                    }
                    else{
                        character.go(target.currentPosition())
                            .then(function(){
                                character.hunger.consume(target);
                                return done();
                            })
                            .fail(function(){
                                // handle failure state here
                            });
                    }
                },function(){
                    callback();
                });
            }
        });
    }
}

function attack(gameObject,target,callback){
    if(!target){
        target = gameObject.surroundings();    
    }
    var possibleTargets = new GameObject();
    target.each(function(target,next,done){
        var affinity = gameObject.affinity(target);
        if(affinity > 10){return next();}
        var needs = gameObject.needs();
        var needsFullfilled = target.provides(needs);
        var health = gameObject.health;
        var courage = gameObject.courage;
        var willpower = gameObject.willpower;
        if(health < 10 && courage < 10){
            return next();
        }
        var hate = roll(-affinity,courage,willpower,needsFullfilled);
        if(hate > 5){
            target.hate = hate;
            possibleTargets.push(target);
            return next();
        }
        return next();
    },function(){
        if(possibleTargets.length){
            possibleTargets.orderBy('hate');
            return callback(behaviors.attack.curry(gameObject,possibleTargets));
        }else{
            return callback();
        }
    });
}

Behaviors

Behaviors define what a gameObject does. Examples:

function attack(gameObject,target,callback){
    gameObject.canSee(target)
        .then(function(c,o){
            var weapon,destination;
            if(gameObject.intelligence>10){
                weapon = gameObject.getBestWeaponForTarget(target);
            }else{
                weapon = gameObject.getBestWeapon();
            }
            if(weapon.type == 'longdistance'){
                destination = target.currentPosition().substract(weapon.range);
            }else{
                destination = target.currentPosition();
            }
            gameObject.go(destination)
                .then(function(c,o){
                    var hitPoints = roll(weapon.hitPoints,c.strength);
                    o.receiveDamage(hitPoints,weapon);
                    // passing the weapon for any status processing
                    // or particular resistance to type
                    if(o.state.dead){
                        return callback(o);
                    }
                    // at next turn the whole behaviour tree is parsed again to decide if the gameObject attacks again
                })
                .fail(function(c,o){
                    // handle failure, maybe the target has moved,
                    // maybe there' s an obstacle...
                });
        })
        .fail(function(c,o){

        });
}

function find(gameObject,target,callback){
    gameObject.canSee(target)
        .then(function(c,o){
            gameObject.canReach(target)
                .then(function(c,o){
                    callback(gameObject.go(target));
                })
                .fail(function(c,o){
                    // make gameObject go around the obstacle or whatever
                });
        })
        .fail(function(c,o){
            var point = c.currentPosition();
            var destinations = [];
            var test = function(c,o){            
                var currentDestination = c.getRandomDestination();
                while(destinations.indexOf(currentDestination)>=0){
                    currentDestination = c.getRandomDestination();
                }
                gameObject.go(currentDestination)
                    .then(function(c,o){
                        c.canSee(o)
                            .then(function(c,o){find(c,o,callback)});
                            .fail(test);
                    });
                    .fail(test)
            }
        });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment