dsI/bin/
dsI/extra/creremote/
dsI/extra/mingw/
dsI/extra/wolfpaw/
dsI/fluffos-2.7-ds2.018/
dsI/fluffos-2.7-ds2.018/ChangeLog.old/
dsI/fluffos-2.7-ds2.018/Win32/
dsI/fluffos-2.7-ds2.018/compat/
dsI/fluffos-2.7-ds2.018/compat/simuls/
dsI/fluffos-2.7-ds2.018/testsuite/
dsI/fluffos-2.7-ds2.018/testsuite/clone/
dsI/fluffos-2.7-ds2.018/testsuite/command/
dsI/fluffos-2.7-ds2.018/testsuite/data/
dsI/fluffos-2.7-ds2.018/testsuite/etc/
dsI/fluffos-2.7-ds2.018/testsuite/include/
dsI/fluffos-2.7-ds2.018/testsuite/inherit/
dsI/fluffos-2.7-ds2.018/testsuite/inherit/master/
dsI/fluffos-2.7-ds2.018/testsuite/log/
dsI/fluffos-2.7-ds2.018/testsuite/single/
dsI/fluffos-2.7-ds2.018/testsuite/single/tests/compiler/
dsI/fluffos-2.7-ds2.018/testsuite/single/tests/efuns/
dsI/fluffos-2.7-ds2.018/testsuite/single/tests/operators/
dsI/fluffos-2.7-ds2.018/testsuite/u/
dsI/fluffos-2.7-ds2.018/tmp/
dsI/lib/cfg/
dsI/lib/cmds/common/
dsI/lib/cmds/creators/include/
dsI/lib/cmds/creators/include/SCCS/
dsI/lib/daemon/services/
dsI/lib/doc/
dsI/lib/domains/Ylsrim/
dsI/lib/domains/Ylsrim/adm/
dsI/lib/domains/Ylsrim/armour/
dsI/lib/domains/Ylsrim/broken/
dsI/lib/domains/Ylsrim/fish/
dsI/lib/domains/Ylsrim/meal/
dsI/lib/domains/Ylsrim/npc/
dsI/lib/domains/Ylsrim/virtual/
dsI/lib/domains/Ylsrim/weapon/
dsI/lib/domains/default/creator/
dsI/lib/domains/default/etc/
dsI/lib/domains/default/room/
dsI/lib/lib/comp/
dsI/lib/lib/lvs/
dsI/lib/lib/user/
dsI/lib/lib/virtual/
dsI/lib/obj/
dsI/lib/obj/include/
dsI/lib/realms/
dsI/lib/save/kills/a/
dsI/lib/save/kills/b/
dsI/lib/save/kills/f/
dsI/lib/save/kills/m/
dsI/lib/save/kills/q/
dsI/lib/save/kills/r/
dsI/lib/secure/cfg/
dsI/lib/secure/cfg/classes/
dsI/lib/secure/cfg/races/SCCS/
dsI/lib/secure/cmds/creators/include/
dsI/lib/secure/cmds/players/
dsI/lib/secure/cmds/players/include/
dsI/lib/secure/daemon/include/
dsI/lib/secure/lib/
dsI/lib/secure/lib/include/
dsI/lib/secure/lib/net/
dsI/lib/secure/lib/net/include/
dsI/lib/secure/lib/std/
dsI/lib/secure/obj/
dsI/lib/secure/obj/include/
dsI/lib/secure/save/
dsI/lib/spells/
dsI/lib/verbs/admins/include/
dsI/lib/verbs/common/
dsI/lib/verbs/common/include/
dsI/lib/verbs/creators/
dsI/lib/verbs/creators/include/
dsI/lib/verbs/players/include/SCCS/
dsI/lib/verbs/rooms/
dsI/lib/verbs/rooms/include/
dsI/lib/www/
dsI/v22.2b14/
dsI/win32/
/*    /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@(#)
 *    Last modified: 96/11/10
 */

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

inherit LIB_DAEMON;

private mapping Races = ([]);
static private mapping Resistances = ([]);
static private mapping Armours = ([]);

static void create() {
    string array lines;
    
    daemon::create();
    if( unguarded((: file_size(SAVE_RACES __SAVE_EXTENSION__) :)) > 0 )
	unguarded((: restore_object(SAVE_RACES) :));
    if( !Races ) Races = ([]);
    // Hocus pocus to load armour and resistance info
    lines = explode(read_file("/include/armour_types.h"), "\n");
    foreach(string line in lines) {
	string type;

	if( sscanf(line, "#define %s %*s", type) == 2 ) {
	    string file = DIR_DAEMONS "/tmp/" + type + ".c";

	    if( type == "A_MAX_ARMOUR_BIT" ) {
		continue;
	    }
	    if( !file_exists(file) ) {
		unguarded((: write_file($(file), "#include <armour_types.h>\n"
					         "int armour() { return " +
					$(type) + "; }\n") :));
	    }
	    Armours[type] = call_other(file, "armour");
	}
    }
    lines = explode(read_file("/include/damage_types.h"), "\n");
    foreach(string line in lines) {
	string type;

	if( sscanf(line, "#define %s %*s", type) == 2 ) {
	    string file = DIR_DAEMONS "/tmp/" + type + ".c";

	    if( type == "MAX_DAMAGE_BIT" ) {
		continue;
	    }
	    if( !file_exists(file) ) {
		unguarded((: write_file($(file), "#include <damage_types.h>\n"
					         "int damage() { return " +
					$(type) + "; }\n") :));
	    }
	    Resistances[type] = call_other(file, "damage");
	}
    }
}

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

void AddRace(string file, int player) {
    class Race res;
    string array lines, tmp;
    string race;
    
    validate();
    if( !file_exists(file) ) error("No such file: " + file);
    lines = explode(read_file(file), "\n");
    lines = filter(lines, function(string str) {
	                      if( strlen(str) == 0 ) {
				  return 0;
			      }
			      if( str[0] == '#' ) {
				  return 0;
			      }
			      if( str[0] == ' ' || str[0] == '\t' ) {
				  return 0;
			      }
			      return 1;
                          });
    race = lines[0];
    if( Races[race] ) error("Race already exists");
    res = new(class Race);
    res->Sensitivity = map(explode(lines[1], ":"), (: to_int :));
    res->Language = lines[2];
    lines = lines[3..];
    res->Resistance = ([]);
    while(sizeof(tmp = explode(lines[0], ":")) == 2) {
	int x = to_int(tmp[0]);

	if( x == 0 && tmp[0] != "0" ) x = GetResistance(tmp[0]);
	res->Resistance[x] = tmp[1];
	lines = lines[1..];
    }
    res->Stats = ([]);
    while(sizeof(tmp = explode(lines[0], ":")) == 3) {
	class Stat s = new (class Stat);

	s->Average = to_int(tmp[1]);
	s->Class = to_int(tmp[2]);
	res->Stats[tmp[0]] = s;
	lines = lines[1..];
    }
    res->Limbs = ({});
    while(sizeof(tmp = explode(lines[0], ":")) == 4) {
	mixed array limb = allocate(4);

	limb[0] = tmp[0];
	limb[1] = (tmp[1] == "0" ? 0 : tmp[1]);
	limb[2] = to_int(tmp[2]);
	limb[3] = map(explode(tmp[3], ","), function(string str) {
	                                        int x = to_int(str);

						if( x == 0 && str != "0" )
						  return GetArmour(str);
						return x;
				     	    });
	res->Limbs = ({ res->Limbs..., limb });
	if( sizeof(lines) > 1 ) lines = lines[1..];
	else {
	    lines = ({});
	    break;
	}
    }
    res->Fingers = ([]);
    if( sizeof(lines) ) {
	foreach(string hand in lines) {
	    string array parts = explode(hand, ":");

	    res->Fingers[parts[0]] = to_int(parts[1]);
	}
    }
    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);
    save_object(SAVE_RACES);
}

int GetArmour(string str) {
    string file = DIR_DAEMONS "/tmp/" + str + ".c";
    
    if( !unguarded((: file_exists($(file)) :)) ) {
	unguarded((: write_file($(file), "#include <armour_types.h>\n" +
		    "int armour() { return " + $(str) + "; }\n") :));
    }
    return call_other(file, "armour"); 
}

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"); 
}

mapping GetRemoteRaces() {
    mapping mp = ([]);

    foreach(string race, class Race res in Races) {
	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;
    
    if( !res || !res->Complete || sizeof(args) != 4 ) return;
    tmp = ({});
    foreach(int key, string val in res->Resistance)
	tmp = ({ tmp..., ({ key, val }) });
    args[0] = tmp;
    tmp = ({});
    foreach(string key, class Stat stat in res->Stats)
	tmp = ({ tmp..., ({ key, stat->Average, stat->Class }) });
    args[1] = tmp;
    args[2] = res->Language;
    args[3] = ({ res->Sensitivity[0], res->Sensitivity[1] });
}
    
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;
    int x;
    
    if( !res ) return 0;
    limbs = map(res->Limbs, (: $1[0] :));
    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 GetArmours() {
    return copy(Armours);
}