/*    /lib/genetics.c
 *    from the Dead Souls LPC Library
 *    handles genetic characteristics
 *    created by Descartes of Borg 950122
 *    Version: @(#) genetics.c 1.4@(#)
 *    Last Modified: 96/11/11
 */

#include <vision.h>
#include <function.h>
#include <damage_types.h>
#include "include/genetics.h"

class blindness {
    int count;
    mixed end;
}

private class blindness Blind            = 0;
private int             CustomStats      = 15;
private int array       LightSensitivity = ({ 25, 75 });
private mapping         Resistance       = ([]);
private mapping         Stats            = ([]);
private static mapping  StatsBonus       = ([]);
private static int      VisionBonus      = 0;

// abstract methods
string GetName();
varargs void eventPrint(string message, mixed args...);
// end abstract methods

static void create(){
    Resistance = ([ "low" : 0, "medium" : 0, "high" : 0, "immune" : 0 ]);
    Resistance["none"] = ALL_DAMAGE;
}

int GetBlind(){
    if( Blind ){
        return 1;
    }
    else {
        return 0;
    }
}

static void RemoveBlindness(){
    mixed val = Blind->end;

    Blind = 0;
    if( arrayp(val) ){
        send_messages(val[0], val[1], this_object());
    }
    else if( functionp(val) && !(functionp(val) & FP_OWNER_DESTED) ){
        evaluate(val, this_object());
    }
    else {
        eventPrint("You can see again.");
    }
}

varargs mixed eventBlind(object who, int amt, mixed end){
    Blind = new(class blindness);
    Blind->count = amt;
    Blind->end = end;
    return 1;
}

mixed eventCustomizeStat(string stat, int amount){
    if( amount < 1 ) return "That is not a valid amount.";
    if( amount > CustomStats )
        return "You do not have enough points to spend on customization.";
    if( !Stats[stat] ) return "You have no such stat.";
    if( Stats[stat]["level"] + amount > 100 )
        return "You cannot make a stat exceed 100.";
    Stats[stat]["level"] += amount;
    Stats[stat]["points"] = 0;
    CustomStats -= amount;
    return Stats[stat]["level"];
}

mixed eventRestoreSight(object who, int amt){
    if( !Blind ){
        return GetName() + " is not blind!";
    }
    Blind->count -= amt;
    if( Blind->count < 1 ){
        RemoveBlindness();
        return 1;
    }
    return 0;
}

varargs void SetStat(string stat, int level, int classes){
    if(!stat) return;
    if(level < 1) level = 1;
    if(!classes) classes = 1;
    Stats[stat] = ([ "points" : 0, "level" : level, "class" : classes ]);
}

varargs void AddStat(string stat, int base, int cls){
    int level;
    if( userp(this_object()) ) level = 1;
    else level = GetLevel();
    if( !stat || cls < 1 || cls > 5 ) return;
    base += ((5 - cls) * random(10)) + (3 * (level + 1))/(cls * 4);
    if( userp(this_object()) && base > 90 ) base = 90;
    else if( base > 100 ) base = 100;
    SetStat(stat, base, cls);
}

varargs void RemoveStat(string stat){
    if(!stat) return;
    if(Stats[stat]) map_delete(Stats, stat);
}

mapping GetStat(string stat){
    mapping ret = ([]);
    if(!stat) return 0;
    ret = copy(Stats[stat]);
    if(!sizeof(ret)) return 0;
    if(ret["level"] < 0) ret["level"] = 0;
    return ret;
}

int GetStatClass(string stat){
    if(!Stats[stat]) return 0;
    else return Stats[stat]["class"];
}

int GetBaseStatLevel(string stat){
    if(!Stats[stat]) return 0;
    else return Stats[stat]["level"];
}

int GetStatLevel(string stat){
    int x;

    x = (GetBaseStatLevel(stat) + GetStatBonus(stat));
    switch(stat){
    case "coordination": case "wisdom":
        x -= GetAlcohol();
    }
    return x;
}

int AddStatPoints(string stat, int x){
    int y;

    if( !Stats[stat] ) return 0;
    Stats[stat]["points"] += x;
    while( Stats[stat]["points"] < 0 ){
        if( Stats[stat]["level"] == 1 ) Stats[stat]["points"] = 0;
        else {
            int tmp;

            tmp = --Stats[stat]["level"];
            Stats[stat]["points"] += GetMaxStatPoints(stat, tmp);
        }
    }
    while(Stats[stat]["points"] > (y = GetMaxStatPoints(stat,
          Stats[stat]["level"]))){
        if(Stats[stat]["level"] >= GetLevel()*4) Stats[stat]["points"] = y;
        else {
            Stats[stat]["level"]++;
            Stats[stat]["points"] -= y;
        }
    }
    return Stats[stat]["level"];
}

string *GetStats(){ return keys(Stats); }
mapping GetStatsMap(){ return copy(Stats); }

int GetMaxStatPoints(string stat, int level){
    if( !Stats[stat] ) return 0;
    else {
        int cl, x;

        if( !(cl = Stats[stat]["class"]) ) return level * 600;
        x = level;
        while( cl-- ) x *= level;
        return x * 600;
    }
}

void AddStatBonus(string stat, mixed f){
    if(!StatsBonus[stat]) StatsBonus[stat] = ([]);
    StatsBonus[stat][previous_object()] = f;
}

varargs void RemoveStatBonus(string stat, object ob){
    if(!StatsBonus[stat]) return;
    if(!ob) ob = previous_object();
    if(!ob || !StatsBonus[stat][ob]) return;
    map_delete(StatsBonus[stat], ob);
}

int GetStatBonus(string stat){
    object *obs;
    int i, x = 0;

    if( !StatsBonus[stat] ) return 0;
    i = sizeof(obs = keys(StatsBonus[stat]));
    while(i--){
        if( !obs[i] ) map_delete(StatsBonus[stat], obs[i]);
        else x += (int)evaluate(StatsBonus[stat][obs[i]], stat);
    }
    return x;
}

/* string SetResistance(int type, string level) 
 * int type - the type being set (you can do a bitwise | on them)
 * string level "none", "low", "medium", "high", and "immune"
 *
 * description
 * you can have a several levels of resistence for given types of damage
 * setting a resistence level here unsets any other levels of resistence
 */

varargs string SetResistance(int type, string level){
    if(level == "none") Resistance["none"] |= type;
    else Resistance["none"] &= ~type;
    if(level == "low") Resistance["low"] |= type;
    else Resistance["low"] &= ~type;
    if(level == "medium") Resistance["medium"] |= type;
    else Resistance["medium"] &= ~type;
    if(level == "high") Resistance["high"] |= type;
    else Resistance["high"] &= ~type;
    if(level == "immune") Resistance["immune"] |= type;
    else Resistance["immune"] &= ~type;
}

string GetResistance(int type){
    if(Resistance["low"] & type) return "low";
    else if(Resistance["medium"] & type) return "medium";
    else if(Resistance["high"] & type) return "high";
    else if(Resistance["immune"] & type) return "immune";
    else return "none";
}

mapping GetResistanceMap(){
    return copy(Resistance);
}

int GetCustomStats(){ return CustomStats; }

varargs mixed GetEffectiveVision(mixed location, int raw_score){
    int array l;
    object env, rider;
    int bonus = GetVisionBonus();
    int a, y, x = 0;

    if(raw_score && !intp(raw_score)){
        location = raw_score;
        raw_score = 0;
    }

    if(sizeof(get_livings(this_object())) && 
      rider = get_random_living(this_object())){
        if(rider->GetProperty("mount") == this_object() &&
          environment(this_object())){
            return rider->GetEffectiveVision(environment(this_object()));
        }
    }

    if(location){
        if(objectp(location)) env = location;
        if(stringp(location)){
            if(!file_exists(location)) location += ".c";
            if(!file_exists(location)) return 0;
            env = load_object(location);
            if(!env) return 0;
        }
    }
    if( Blind && !raw_score){
        return VISION_BLIND;
    }
    if( !location ){
        env = environment();
    }
    x = GetRadiantLight(0);
    a = env->GetAmbientLight();
    if(x) x = x/2;
    x += GetRadiantLight(a) + a;
    l = GetLightSensitivity();
    l[0] -= bonus;
    l[1] += bonus;

    if(raw_score && !location){
        return "Low: "+l[0]+", High: "+l[1];
    } 
    if( x >= l[0] && x <= l[1] ) return VISION_CLEAR;
    y = l[0]/3;
    if( x < y ) return VISION_TOO_DARK;
    if( x < (2*y) ) return VISION_DARK;
    if( x < l[0] ) return VISION_DIM;
    y = l[1]/3;
    if( x < (l[1] + y) ) return VISION_LIGHT;
    if( x < (l[1] + (2*y)) ) return VISION_BRIGHT;
    return VISION_TOO_BRIGHT;
}

int array GetLightSensitivity(){
    if( !LightSensitivity ) return ({ 25, 75 });
    else return LightSensitivity;
}

varargs static int array SetLightSensitivity(mixed array val...){
    if( !val ) error("Null argument to SetLightSensitivity().\n");
    if( sizeof(val) == 1 ) val = val[0];
    if( sizeof(val) != 2 )
        error(sprintf("Invalid arguments to SetLightSensitivity(): %O\n",  val));
    return (LightSensitivity = val);
}

int AddVisionBonus(int x){
    VisionBonus += x;
    return VisionBonus;
}

int GetVisionBonus(){
    return VisionBonus;
}

static void heart_beat(){
    if( Blind ){
        Blind->count--;
        if( Blind->count < 1 ){
            RemoveBlindness();
        }
    }
}
