/*    /daemon/race.c
 *    from the Dead Souls  Object Library
 *    handles race configuration and administration
 *    created by Descartes of Borg 960108
 *    Version: @(#) races.c 1.4@(#)
 *    Fixed by Ashon @ Stargate Atlantis 16 March 2006
 */

#include <lib.h>
#include <save.h>
#include <privs.h>
#include <armor_types.h>
#include <size_types.h>
#include "include/races.h"

inherit LIB_DAEMON;

private mapping Races = ([]);
static private mapping Resistances = ([]);
static private mapping Armors = ([]);
static private mapping Sizes = ([]);
static private mapping Btypes = ([]);
string array FlyingRaces = ({});
string array LimblessCombatRaces = ({});
string array LimblessRaces = ({});
string array NonBitingRaces = ({});
string array SwimmingRaces = ({});
string array NonMeatRaces = ({});

static void create() {
    daemon::create();

    if( unguarded((: file_size(SAVE_RACES __SAVE_EXTENSION__) :)) > 0 )
	unguarded((: restore_object(SAVE_RACES) :));

    if( !Races ) Races = ([]);
    if(!FlyingRaces) FlyingRaces = ({});
    if(!LimblessCombatRaces) LimblessCombatRaces = ({});
    if(!LimblessRaces) LimblessRaces = ({});
    if(!NonBitingRaces) NonBitingRaces = ({});
    if(!NonMeatRaces) NonMeatRaces = ({});
}

static private void validate() {
    if( !((int)master()->valid_apply(({ PRIV_ASSIST }))) )
	error("Illegal attempt to modify race data");
}

int CanFly(string str){
    if( !Races[str] ) return 0;
    if(member_array(str, FlyingRaces) != -1) return 1;
    else return 0;
}

int GetNonMeatRace(string str){
    if(member_array(str,NonMeatRaces) == -1) return 0;
    else return 1;
}

int GetLimblessCombatRace(string str){
    if(member_array(str,LimblessCombatRaces) == -1) return 0;
    else return 1;
}

int GetLimblessRace(string str){
    if(member_array(str,LimblessRaces) == -1) return 0;
    else return 1;
}

int SetNonMeatRace(string str){
    if(member_array(str,NonMeatRaces) != -1) return 0;
    NonMeatRaces += ({ lower_case(str) });
    return 1;
}

int SetLimblessCombatRace(string str){
    if(member_array(str,LimblessCombatRaces) != -1) return 0;
    LimblessCombatRaces += ({ lower_case(str) });
    return 1;
}

int SetLimblessRace(string str){
    if(member_array(str,LimblessRaces) != -1) return 0;
    LimblessRaces += ({ lower_case(str) });
    return 1;
}

int SetFlyingRace(string str){
    FlyingRaces += ({ str });
    return 1;
}

int SetNonBitingRace(string str){
    NonBitingRaces += ({ str });
    return 1;
}

int SetSwimmingRace(string str){
    SwimmingRaces += ({ str });
    return 1;
}

int GetSwimmingRace(string str){
    if(member_array(str,SwimmingRaces) != -1) return 1;
    else return 0;
}

string *GetNonMeatRaces(){
    return copy(NonMeatRaces);
}

string *GetSwimmingRaces(){
    return copy(SwimmingRaces);
}

string *GetLimblessCombatRaces(){
    return copy(LimblessCombatRaces);
}

string *GetLimblessRaces(){
    return copy(LimblessRaces);
}

string *GetFlyingRaces(){
    return copy(FlyingRaces);
}

int GetBitingRace(string str){
    if(member_array(str,NonBitingRaces) == -1) return 1;
    else return 0;
}

int RemoveRaceVars(string str){
    if(previous_object() != this_object()) return 0;
    FlyingRaces  -= ({ str });
    LimblessCombatRaces -= ({ str });
    LimblessRaces -= ({ str });
    NonBitingRaces -= ({ str }); 
    NonBitingRaces -= ({ str });
    SwimmingRaces -= ({ str });
    return 1;
}

int GetRaceMass(string str){
    int Mass = Races[str]->Mass;
    if(Mass) return Mass;
    else return 0;
}

int GetRaceSize(string str){
    int Size = Races[str]->Size;
    if(Size) return Size;
    else return 0;
}

int GetRaceBodyType(string str){
    int Btype = Races[str]->Btype;
    if(Btype) return Btype;
    else return 0;
}

int GetRaceRespirationType(string str){
    int Btype = Races[str]->Btype;
    if(Btype) return Btype;
    else return 0;
}

void AddRace(string file, int player) {
    class Race res;
    string array tmp, parts;
    string race, test_string;
    int x;
    mixed array limb = allocate(4);
    mixed array tmp_limb = allocate(4);
    class Stat s; 

    res = new(class Race);

    res->Resistance = ([]);
    res->Skills = ([]);
    res->Stats = ([]);
    res->Limbs = ({});
    res->Mass = 0;
    res->Size = 0;
    res->Btype = 0;
    res->Rtype = 0;

    validate();

    if( !file_exists(file) ) error("No such file: " + file);
    race = last_string_element(file,"/");

    res->Fingers = ([]);


    foreach(string line in explode(read_file(file),"\n")){
	test_string = first_string_element(line," ");
	if(!test_string || !sizeof(test_string)) test_string = line;

	switch(test_string){
	    string type = "";

	case "FLYING_RACE":
	    line = trim(replace_string(line, "FLYING_RACE", ""));
	    if(sizeof(line) && atoi(line) < 1) break;
	    else SetFlyingRace(race);
	    break;

	case "NOT_MEAT":
	    line = trim(replace_string(line, "NOT_MEAT", ""));
	    if(sizeof(line) && atoi(line) < 1) break;
	    else SetNonMeatRace(race);
	    break;

	case "LIMBLESS_RACE":
	    line = trim(replace_string(line, "LIMBLESS_RACE", ""));
	    if(sizeof(line) && atoi(line) < 1) break;
	    else SetLimblessRace(race);
	    break;

	case "LIMBLESS_COMBAT_RACE":
	    line = trim(replace_string(line, "LIMBLESS_COMBAT_RACE", ""));
	    if(sizeof(line) && atoi(line) < 1) break;
	    else SetLimblessCombatRace(race);
	    break;

	case "NONBITING_RACE":
	    line = trim(replace_string(line, "NONBITING_RACE", ""));
	    if(sizeof(line) && atoi(line) < 1) break;
	    else SetNonBitingRace(race);
	    break;

	case "SWIMMING_RACE":
	    line = trim(replace_string(line, "SWIMMING_RACE", ""));
	    if(sizeof(line) && atoi(line) < 1) break;
	    else SetSwimmingRace(race);
	    break;

	case "RACE":
	    race = replace_string(line, "RACE ", "");
	    if( Races[race] ) error(race+": Race already exists");
	    break;

	case "SENSITIVITY":
	    line = replace_string(line, "SENSITIVITY ", "");
	    res->Sensitivity = map(explode(line, ":"), (: to_int :));
	    break;

	case "PLAYER_RACE":
	    line = replace_string(line, "PLAYER_RACE ", "");
	    if(!player && atoi(line) > 0) player = 1;
	    else player = 0;
	    break;

	case "LANGUAGE":
	    //TODO: This should be a Language array to handle multiple 
	    //languages but further research is required first.
	    res->Language = replace_string(line, "LANGUAGE ", "");
	    break;

	case "RESISTANCE":			  
	    tmp = explode(replace_string(line, "RESISTANCE ", ""), ":");
	    x = to_int(tmp[0]);
	    if( x == 0 && tmp[0] != "0" ) x = GetResistance(tmp[0]);
	    res->Resistance[x] = tmp[1];
	    break;

	case "SKILL":      
	    tmp = explode(replace_string(line, "SKILL ", ""), ":");
	    res->Skills[tmp[0]] = ({ tmp[1], tmp[2], tmp[3], tmp[4] });
	    break;

	case "MASS":
	    x = 0;
	    sscanf(line, "MASS %d",x);
	    if(x) res->Mass = x;
	    break;

	case "SIZE":
	    type = "";
	    x = 0;
	    if(sscanf(line, "SIZE %s",type)) res->Size = GetSize(type);
	    else res->Size = x;
	    break;

	case "BODY_TYPE":
	    type = "";
	    x = 0;
	    if(sscanf(line, "BODY_TYPE %s",type)) res->Btype = GetBodyType(type);
	    else res->Btype = x;
	    break;

	case "RESPIRATION_TYPE":
	    type = "";
	    x = 0;
	    if(sscanf(line, "RESPIRATION_TYPE %s",type)) res->Btype = GetRespirationType(type);
	    else res->Btype = x;
	    break;

	case "STATS":
	    tmp = ({});
	    s = new (class Stat);
	    tmp = explode(replace_string(line, "STATS ",""), ":");
	    s->Average = copy(to_int(tmp[1]));
	    s->Class = copy(to_int(tmp[2]));
	    res->Stats[tmp[0]] = s;
	    break;

	case "LIMB":
	    limb = ({ ({}), ({}), ({}), ({}) });
	    tmp_limb = explode(replace_string(line, "LIMB ",""), ":");
	    limb[0] = tmp_limb[0];
	    limb[1] = (tmp_limb[1] == "0" ? 0 : tmp_limb[1]);
	    limb[2] = to_int(tmp_limb[2]);
	    limb[3] = map(explode(tmp_limb[3], ","), function(string str) {
		  int x = to_int(str);
		  if( x == 0 && str != "0" ) { return GetArmor(str); }
		  return x;
		});

	      res->Limbs = ({ res->Limbs..., limb });
	      res->Limbs += ({limb});
	      break;

	      case "HAND":
	      parts = explode(replace_string(line, "HAND ",""), ":");
	      res->Fingers[parts[0]] = to_int(parts[1]);
	      break;

	      default:
	      break;
	  } 
	}  

	res->Complete = 1;

	if( player ) {
	    res->PlayerFlag = 1;
	}

	else {
	    res->PlayerFlag = 0;
	}

	Races[race] = res;
	save_object(SAVE_RACES);
    } 

    void RemoveRace(string race) {
	validate();
	map_delete(Races, race);
	RemoveRaceVars(race);
	if(Races[race]) 
	    save_object(SAVE_RACES);
    }

    string ConvertPipe(string str){
	str = replace_string(str," ","");
	str = replace_string(str,"|","+");
	return str;
    }

    int GetArmor(string foo) {
	string str = ConvertPipe(foo);
	string file = DIR_DAEMONS "/tmp/" + str + ".c";

	if( !unguarded((: file_exists($(file)) :)) ) {
	    unguarded((: write_file($(file), "#include <armor_types.h>\n" +
		  "int armor() { return " + $(str) + "; }\n") :));
	}
	return call_other(file, "armor"); 
    }

    int GetSize(string foo) {
	string str = ConvertPipe(foo);
	string file = DIR_DAEMONS "/tmp/" + str + ".c";

	if( !unguarded((: file_exists($(file)) :)) ) {
	    unguarded((: write_file($(file), "#include <size_types.h>\n" +
		  "int size() { return " + $(str) + "; }\n") :));
	}
	return call_other(file, "size"); 
    }

    int GetBodyType(string foo) {
	string str = ConvertPipe(foo);
	string file = DIR_DAEMONS "/tmp/" + str + ".c";

	if( !unguarded((: file_exists($(file)) :)) ) {
	    unguarded((: write_file($(file), "#include <body_types.h>\n" +
		  "int btype() { return " + $(str) + "; }\n") :));
	}
	return call_other(file, "btype"); 
    }

    int GetRespirationType(string foo) {
	string str = ConvertPipe(foo);
	string file = DIR_DAEMONS "/tmp/" + str + ".c";

	if( !unguarded((: file_exists($(file)) :)) ) {
	    unguarded((: write_file($(file), "#include <respiration_types.h>\n" +
		  "int rtype() { return " + $(str) + "; }\n") :));
	}
	return call_other(file, "rtype"); 
    }

    int GetResistance(string str) {
	string file = DIR_DAEMONS "/tmp/" + str + ".c";

	if( !unguarded((: file_exists($(file)) :)) ) {
	    unguarded((: write_file($(file), "#include <damage_types.h>\n" +
		  "int damage() { return " + $(str) + "; }\n") :));
	}
	return call_other(file, "damage"); 
    }

    varargs mapping GetRemoteRaces(string str) {
	mapping mp = ([]);
	mapping foo = ([]);

	if(str && Races[str]) foo[str] = Races[str];
	else foo = copy(Races);

	foreach(string race, class Race res in foo) {
	    mapping stats = ([]);

	    mp[race] = ([]);
	    mp[race]["limbs"] = res->Limbs;
	    mp[race]["resistance"] = res->Resistance;

	    foreach(string stat, class Stat st in res->Stats) {
		stats[stat] = ([]);
		stats[stat]["class"] = st->Class;
		stats[stat]["average"] = st->Average;
	    }

	    mp[race]["stats"] = stats;
	    mp[race]["fingers"] = res->Fingers;
	    mp[race]["sensitivity"] = res->Sensitivity;
	    mp[race]["player"] = res->PlayerFlag;
	    mp[race]["language"] = res->Language;

	}
	return mp;
    }

    void SetComplete(string race) {
	class Race res;

	validate();

	if( !Races[race] ) error("No such race");
	else res = Races[race];
	res->Complete = 1;
	save_object(SAVE_RACES);
    }

    void SetLightSensitivity(string race, int array sensitivity) {
	class Race res;

	validate();

	if( !Races[race] ) error("No such race");
	else res = Races[race];
	if( sensitivity[0] < 1 ) error("Invalid sensitivity value");
	if( sensitivity[1] > 99 ) error("Invalid sensitivity value");
	if( sensitivity[0] > sensitivity[1] ) error("Invalid sensitivity value");
	res->Sensitivity = sensitivity;
	save_object(SAVE_RACES);
    }

    void SetCharacterLimbs(string race, mixed array args) {
	class Race res = Races[race];
	mixed array tmp = ({});

	if( !res || !res->Complete || sizeof(args) != 2 ) return;
	args[0] = copy(res->Limbs);
	foreach(string finger, int count in res->Fingers)
	tmp = ({ tmp..., ({ finger, count }) });
	args[1] = tmp;
    }

    void SetCharacterRace(string race, mixed array args) {
	class Race res = Races[race];
	mixed array tmp;
	mapping StatMap;
	string schluss;

	if( !res || !res->Complete || sizeof(args) != 5 ) return;
	tmp = ({});
	foreach(int key, string val in res->Resistance)
	tmp = ({ tmp..., ({ key, val }) });
	args[0] = tmp;
	tmp = ({});
	StatMap = copy(res->Stats);
	schluss = "";
	foreach(schluss in keys(StatMap)){
	    tmp = ({ tmp..., ({ schluss, StatMap[schluss]->Average, StatMap[schluss]->Class }) });
	}
	args[1] = tmp;
	args[2] = res->Language;
	args[3] = res->Sensitivity;
	args[4] = res->Skills; 
    }

    varargs string array GetRaces(int player_only) {

	return filter(keys(Races), function(string race, int player_only) {
	      class Race res = Races[race];

	      if( !res->Complete ) return 0;
	      if( player_only && !res->PlayerFlag )
		  return 0;
	      return 1;
	  }, player_only);
    }

    string GetHelp(string race) {
	class Race res = Races[race];
	string array limbs;
	string help = "Race: " + race + "\n\n";
	string tmp, h_file;
	int x;

	if( !res ) return 0;
	h_file = "/doc/help/races/"+lower_case(race);
	if(file_exists(h_file)) return read_file(h_file); 
	limbs = map(res->Limbs, (: $1[0] :));
	limbs = singular_array(limbs);
	help += "Limbs:\n";
	help += capitalize(item_list(map(limbs, (: add_article :)))) + ".\n";
	help += "\nFingered limbs:\n";
	foreach(string finger, int count in res->Fingers)
	help += "\t" + finger + " (" + count + ")\n";
	limbs = regexp(limbs, ".* wing");
	if( sizeof(limbs) ) {
	    help += "\nFlying\n";
	}

	else {
	    help += "\nNon-flying\n";
	}

	x = res->Sensitivity[0];
	if( x < 11 ) tmp = "excellent";
	else if( x < 16 ) tmp = "above average";
	else if( x < 21 ) tmp = "good";
	else if( x < 26 ) tmp = "average";
	else if( x < 31 ) tmp = "below average";
	else if( x < 36 ) tmp = "very poor";    
	else tmp = "extremely poor";
	help += "\nNight vision: " + tmp + "\n";
	x = res->Sensitivity[1];
	if( x < 61 ) tmp = "extremely poor";
	else if( x < 66 ) tmp = "very poor";
	else if( x < 71 ) tmp = "below average";
	else if( x < 76 ) tmp = "average";
	else if( x < 81 ) tmp = "good";
	else if( x < 86 ) tmp = "above average";
	else tmp = "excellent";
	help += "Day vision: " + tmp + "\n\n";
	return help;
    }

    public mapping GetResistances() {
	return copy(Resistances);
    }



    public mapping GetArmors() {
	return copy(Armors);
    }


