/* /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);
}