#include <weapon.h>
inherit "/cmds/base";
mapping _weapons;
mapping _items;
mapping _matched;
// Condition modifiers for materials
mapping _conditions = ([ "cloth" : 800, "rubber" : 100, "hide" : 700, "leather" : 600, "wood" : 500, "bone" : 200, "silver" : 300, "copper" : 400, "stone" : 400, "bronze" : 500, "iron" : 700, "steel" : 900, "klatchian steel" : 1100, "octiron" : 1300 ]);
// Damage chance modifiers for materials
mapping _chances = ([ "cloth" : 20, "rubber" : 19, "hide" : 17, "leather" : 15, "wood" : 12, "bone" : 8, "silver" : 15, "copper" : 10, "stone" : 3, "bronze" : 8, "iron" : 6, "steel" : 4, "klatchian steel" : 2, "octiron" : 0 ]);
// Weight modifiers for different materials
mapping _weights = ([ "cloth" : 1, "rubber" : 3, "hide" : 2, "leather" : 2, "wood" : 3, "bone" : 3, "silver" : 6, "copper" : 6, "stone" : 5, "bronze" : 5, "iron" : 5, "steel" : 5, "klatchian steel" : 5, "octiron" : 5 ]);
// Map some weird things to more standard equivalents.
mapping _equivalents = ([ "chocolate" : "rubber",
"earthworm" : "octiron" ]);
// The light & heavy weapon specials.
mapping _specials = ([ "bash" : "smash",
"hack" : "chop",
"stab" : "pierce",
"slash" : "slice" ]);
// Standard attack names/types/skills
mapping _types = ([ "strike" : "blunt",
"bash" : "blunt",
"smash" : "blunt",
"hack" : "sharp",
"chop" : "sharp",
"poke" : "pierce",
"stab" : "pierce",
"pierce" : "pierce",
"cut" : "sharp",
"slash" : "sharp",
"slice" : "sharp",
"spike" : "pierce",
"fire" : "any" ]);
nosave string *_attack_types = ({ "blunt", "sharp", "pierce", "fire" });
void create() {
::create();
_weapons = "/obj/handlers/armoury"->make_list("/obj/nweapons/",
({".c", ".wep"}));
}
string warn(int i) {
switch(i) {
case 2:
return " %^BOLD%^%^RED%^!%^RESET%^ ";
case 1:
return " %^ORANGE%^o%^RESET%^ ";
default:
return " %^GREEN%^-%^RESET%^ ";
}
}
int *calc_rating(object weapon) {
int i, size;
int ave = 0;
int max = 0;
int *damage;
mixed *data;
data = (mixed *)weapon->query_attack_data();
if ( !( size = sizeof( data ) ) )
return ({ 0, 0 });
for ( i = 0; i < size; i += W_ARRAY_SIZE ) {
if ( member_array( data[ i + W_SKILL ], _attack_types ) == -1 )
continue;
damage = data[ i + W_DAMAGE ];
ave += ( data[ i + W_CHANCE ] * ( damage[ F_FIXED ] +
( damage[ F_NUM ] * ( 1 + damage[ F_DIE ] ) ) / 2 ) ) / 100;
if(damage[ F_FIXED ] + damage[ F_NUM ] * damage[ F_DIE ] > max)
max = damage[ F_FIXED ] + damage[ F_NUM ] * damage[ F_DIE ];
}
return ({ ave, max });
}
int calc_ave(mixed damage) {
if(arrayp(damage))
return damage[F_FIXED] + (damage[F_NUM] * (1 + damage[F_DIE])) / 2;
return damage;
}
int calc_max(mixed damage) {
if(arrayp(damage))
return damage[F_FIXED] + (damage[F_NUM] * damage[F_DIE]);
return damage;
}
object *filter(object who) {
object *tmp;
if(who->query_creator())
return ({});
tmp = who->query_weapons();
switch(sizeof(tmp)) {
case 2:
if(tmp[0]->query_property("virtual name")) {
if(tmp[0]->query_property("virtual name") ==
tmp[1]->query_property("virtual name"))
return ({ tmp[0] });
} else {
if(base_name(tmp[0]) == base_name(tmp[1]))
return ({ tmp[0] });
}
break;
default:
}
return tmp;
}
int list(string type) {
mapping weapons;
object wep;
mixed *data;
string *names, *types, str;
int i;
weapons = "/obj/handlers/armoury"->query_items("weapons");
foreach(str in keys(weapons)) {
if(str == "twoedge" || str == "kring" ||
strsrch(weapons[str], "ranged") != -1 ||
strsrch(weapons[str], "misc") != -1)
continue;
wep = "/obj/handlers/armoury"->request_item(str);
if(!wep)
continue;
types = wep->query_attack_types();
if(type && types && member_array(type, types) == -1)
continue;
data = wep->query_attack_data();
names = wep->query_attack_names();
for(i=0; i<sizeof(data); i += W_ARRAY_SIZE) {
printf("%-30s %8s %3d %3d %3d %6s\n",
wep->query_short(),
names[i / W_ARRAY_SIZE],
data[i + W_CHANCE],
calc_ave(data[i+W_DAMAGE]),
calc_max(data[i+W_DAMAGE]),
data[i+W_SKILL]);
write_file("/w/ceres/WEAPONS.csv",
sprintf("%s,%s,%d,%d,%d,%s\n",
wep->query_short(),
names[i / W_ARRAY_SIZE],
data[i + W_CHANCE],
calc_ave(data[i+W_DAMAGE]),
calc_max(data[i+W_DAMAGE]),
data[i+W_SKILL]));
}
wep->dest_me();
}
return 1;
}
int sort_weapons(int wep1, int wep2) {
if(wep1 > wep2)
return -1;
if(wep1 < wep2)
return 1;
return 0;
}
int players() {
mapping weapons;
object wep, *weps;
mixed *data;
string str;
data = map(users(), "filter");
weapons = ([ ]);
foreach(weps in data) {
if(weps) {
foreach(wep in weps) {
if(wep->query_property("virtual name"))
str = (clone_object(wep->query_property("virtual name"))->query_short());
else
str = base_name(wep)->query_short();
if(!weapons[str])
weapons[str] = 1;
else
weapons[str] += 1;
}
}
}
foreach(str in sort_array(keys(weapons),
(: sort_weapons($(weapons)[$1],
$(weapons)[$2]) :)))
if(str && weapons[str])
printf("%d %s\n", weapons[str], str);
return 1;
}
private string *map_materials(string *materials) {
string str;
foreach(str in materials) {
if(_equivalents[str]) {
materials -= ({ str });
materials += ({ _equivalents[str] });
}
}
return materials;
}
void total(object pl) {
write("Total of " + _matched[pl] + " weapons found.\n");
}
void check(int bad_only, string type, string search, string *weapons,
object pl) {
string str, *materials, att, ret;
string *tstr, *anames;
object wep;
int cond, dc, ave, max, weight, tmp, bad, count;
mixed *data;
float wf;
count = 0;
foreach(str in weapons) {
bad = 0;
if(wep)
wep->dest_me();
// These don't load.
if(str == "twoedge" || str == "kring")
continue;
// exclude the weird stuff if they'd checking for bad/warning weapons.
if(bad_only && (strsrch(str, "bow") != -1 ||
str == "satin knife boots" || str == "knife boots" ||
strsrch(_items[pl][str], "ranged") != -1 ||
strsrch(_items[pl][str], "misc") != -1))
continue;
// Search for specific weapons.
if(search != "" && strsrch(str, search) == -1 &&
strsrch(_items[pl][str], search) == -1)
continue;
wep = clone_object(_items[pl][str]);
if(!wep || !wep->query_short())
continue;
materials = map_materials(wep->query_materials());
cond = wep->query_max_cond();
dc = wep->query_damage_chance();
ret = "";
if(search)
ret = sprintf("\n");
ret += sprintf("%s (%s)\n", capitalize(wep->query_short()),
_items[pl][str]);
tstr = ({ });
if(!sizeof(materials) || sizeof(materials - keys(_conditions)))
tstr += ({ "materials" });
else
ret += warn(0) + sprintf("is made of %s\n",
query_multiple_short(materials));
if(wep->query_length() == 1)
tstr += ({ "length" });
if(wep->query_width() == 1)
tstr += ({ "width" });
if(!wep->query_weight())
tstr += ({ "weight" });
if(sizeof(tstr)) {
if(sizeof(tstr) == 1 && tstr[0] == "width") {
ret += warn(1);
//if(bad < 2)
//bad = 1;
} else {
ret += warn(2);
bad = 2;
}
ret += sprintf("has no %s\n", replace(query_multiple_short(tstr),
"and", "or"));
if(member_array("materials", tstr) != -1) {
tell_object(pl, ret);
continue;
}
}
if(wep->query_length() != 1 && wep->query_width() != 1)
ret += warn(0) + sprintf("is %s%d\" (%dcm) by %s%d\" (%dcm) "
"and weighs %.1f%s (%.1fkg)\n",
(wep->query_length() / 12) > 0 ?
(wep->query_length() / 12) + "'" : "",
wep->query_length() % 12,
to_int(wep->query_length() * 2.54),
(wep->query_width() / 12) > 0 ?
(wep->query_width() / 12) + "'" : "",
wep->query_width() % 12,
to_int(wep->query_width() * 2.54),
wep->query_weight() / 9.0,
(wep->query_weight() / 9.0) == 1.0 ? "lb" :
"lbs",
wep->query_weight() / 20.0);
// Calculate the ratio of length+width+material to weight.
wf = 0;
foreach(tstr in materials)
wf += _weights[tstr];
wf /= sizeof(materials);
wf = (wep->query_weight() * (wep->query_weight() / 2)) /
(wep->query_length() * wep->query_width() * wf);
if(wf < 0.5 || wf > 3.0) {
ret += warn(1);
if(bad < 2)
bad = 1;
} else
ret += warn(0);
ret += sprintf("has weight factor of %.1f [0.5/1.0/3.0] "
"(%%^ORANGE%%^Experimental%%^RESET%%^)\n", wf);
switch(type) {
case "weapons":
weight = 10 + 2 * sqrt(wep->query_weight());
break;
case "armours":
weight = 5 + 2 * sqrt(wep->query_weight());
break;
case "clothes":
weight = 2 * sqrt(wep->query_weight());
break;
}
tmp = 0;
foreach(tstr in materials)
tmp += _conditions[tstr];
tmp /= sizeof(materials);
tmp *= weight;
if((cond < tmp - tmp/10) || (cond > tmp + tmp/10)) {
ret += warn(2) + sprintf("has max cond of %d should be [%d/%d/%d]\n",
cond, tmp - tmp/10, tmp, tmp + tmp/10);
tell_object(pl, ret);
continue;
} else if(search)
ret += warn(0) + sprintf("has max cond of %d [%d/%d/%d]\n",
cond, tmp - tmp/10, tmp, tmp + tmp/10);
tmp = 0;
foreach(tstr in materials)
tmp += _chances[tstr];
tmp /= sizeof(materials);
if(dc < tmp -1 || dc > tmp+1) {
ret += warn(2) + sprintf("has damage chance of %d should be "
"[%d/%d/%d]\n",
dc, tmp-1, tmp, tmp+1);
bad = 2;
} else if(search)
ret += warn(0) + sprintf("has damage chance of %d [%d/%d/%d]\n",
dc, tmp-1, tmp, tmp+1);
// Check the common specials
anames = wep->query_attack_names();
if(!sizeof(anames)) {
ret += warn(2) + sprintf("has no attacks.\n");
tell_object(pl, ret);
continue;
}
foreach(att in anames) {
if(!_specials[att] && member_array(att, keys(_types)) == -1) {
ret += warn(1) + sprintf("has non-standard %s attack\n", att);
if(bad < 2)
bad = 1;
} else if(wep->query_weight() < 30 &&
member_array(att, values(_specials)) != -1) {
ret += warn(2) + sprintf("has %s but is too light\n", att);
bad = 2;
} else if(wep->query_weight() > 50 && _specials[att] &&
member_array(_specials[att], anames) == -1) {
ret += warn(1) + sprintf("has %s but not %s\n", att, _specials[att]);
if(bad < 2)
bad = 1;
}
}
// Check type & skill.
data = wep->query_attack_data();
for(tmp=0; tmp < sizeof(data); tmp += W_ARRAY_SIZE) {
if(member_array(data[tmp+W_TYPE], _attack_types) == -1) {
ret += warn(2) + sprintf("has unknown attack type %s\n",
data[tmp+W_TYPE]);
bad = 2;
}
if(member_array(data[tmp+W_SKILL], values(_types)) == -1) {
ret += warn(2) + sprintf("has unknown attack skill %s\n",
data[tmp+W_SKILL]);
bad = 2;
}
if(_types[anames[tmp/W_ARRAY_SIZE]] &&
_types[anames[tmp/W_ARRAY_SIZE]] != "any" &&
_types[anames[tmp/W_ARRAY_SIZE]] != data[tmp+W_SKILL]) {
ret += warn(2) + sprintf("has attack %s with skill of %s\n",
anames[tmp/W_ARRAY_SIZE], data[tmp+W_SKILL]);
bad = 2;
}
if(data[tmp+W_FUNCTION]) {
ret += warn(1) + sprintf("has attack function %s on %s attack\n",
data[tmp+W_FUNCTION],
anames[tmp/W_ARRAY_SIZE]);
bad = 1;
}
}
// Check the axe attacks.
if(strsrch(_items[pl][str], "axe") == -1 &&
strsrch(wep->query_long(), "axe") == -1) {
if(member_array("chop", wep->query_attack_names()) != -1) {
ret += warn(1) + sprintf("has chop attack but is not an axe.\n");
if(bad < 2)
bad = 1;
}
if(member_array("hack", wep->query_attack_names()) != -1) {
ret += warn(1) + sprintf("has chop attack but is not an axe.\n");
if(bad < 2)
bad = 1;
}
} else {
if(member_array("slash", wep->query_attack_names()) != -1) {
ret += warn(1) + sprintf("has slash attack but is an axe.\n");
if(bad < 2)
bad = 1;
}
if(member_array("slice", wep->query_attack_names()) != -1) {
ret += warn(1) + sprintf("has slice attack but is an axe.\n");
if(bad < 2)
bad = 1;
}
}
if(cond) {
data = calc_rating(wep);
ave = data[0];
max = data[1];
if(ave > 140 || max > 300) {
ret += warn(2);
bad = 2;
} else
ret += warn(0);
ret += sprintf("has damage of %d/%d max permitted is [140/300]\n",
ave, max);
tmp = (100 * ave * max) / cond; // damage factor
if(tmp < 75 || tmp > 150) {
ret += warn(1);
if(bad < 2)
bad = 1;
} else
ret += warn(0);
ret += sprintf("has damage factor of %d [75/150] "
"(%%^ORANGE%%^Experimental%%^RESET%%^)\n",
tmp);
}
if(bad >= bad_only) {
tell_object(pl, ret);
count++;
}
}
_matched[pl] += count;
return;
}
int do_check(int bad_only, string type) {
string *tmp, search;
int i;
if(!_items)
_items = ([ ]);
if(!_matched)
_matched = ([ ]);
if(type == "armours" || type == "clothes") {
_items[this_player()] = "/obj/handlers/armoury"->query_items(type);
search = "";
} else if(type == "weapons") {
_items[this_player()] = _weapons;
search = "";
} else {
search = type;
type = "weapons";
_items[this_player()] = _weapons;
}
_matched[this_player()] = 0;
tmp = keys(_items[this_player()]);
if(!sizeof(tmp))
return notify_fail("No items matched.\n");
for(i=0; i<sizeof(tmp); i+= 50) {
if(i+50 < sizeof(tmp)) {
call_out("check", 0, bad_only, type, search, tmp[i..i+49],
this_player());
} else
call_out("check", 0, bad_only, type, search, tmp[i..], this_player());
}
call_out("total", 1, this_player());
return 1;
}
mixed *query_patterns() {
return ({
//"list", (: list("") :),
// "list <string'type'>", (: list($4[0]) :),
"players", (: players() :),
"check", (: do_check(0, "") :),
"check <string'type'>", (: do_check(0, $4[0]) :),
"check bad", (: do_check(2, "") :),
"check bad <string'type'>", (: do_check(2, $4[0]) :),
"check warn", (: do_check(1, "") :),
"check warn <string'type'>", (: do_check(1, $4[0]) :)
});
}