/* -*- LPC -*- */
/*
* $Locker: $
* $Id: armoury.c,v 1.34 2002/11/10 02:17:42 ceres Exp $
*/
/**
* Keeps track of all the standard armours, weapons and clothing.
*
* @see /include/armoury.h
* @index armour
* @index weapon
* @index scabard
* @index clothing
* @index clothes
* @index jewellery
* @index misc
* @index plants
* @author Furball
* @changes Many many, by many people
* @changes 4/1/97 Ceres
* The discworld armoury was rewritten by Ceres on 4/1/97 to handle
* subdirectories within the standard dirs.
* @changes 5/2/2001 Pinkfish
* Changed to allow the various domains to have their own armoury
* subdirectories that can be found and specified with a flag to the
* creation sequences.
* @changes 23 Apr 2001 Ceres
* Removed the recycling. It was a lot of overhead for little benefit.
* @changes 24 Aug 2001 Siel
* added /obj/misc/, /obj/plants/ and /obj/furnitures/ to the request list
*/
#include <move_failures.h>
#include <armoury.h>
/* two week timeout */
#define MAX_RECYCLE 6
inherit "/std/room/basic_room";
#define DEFAULT_RECYCLE_AREA "default"
#define SAVE "/save/armoury"
#define TYPES ({"armours", "clothes", "weapons", "plants", "foods", "scabbards", "jewelleries", "misc" })
private nosave int _total_requests;
private mapping _smalls;
private mapping _armour_list;
private mapping _weapon_list;
private mapping _clothing_list;
private mapping _jewellery_list;
private mapping _scabbard_list;
private mapping _food_list;
private mapping _misc_list;
private mapping _plant_list;
private mapping _areas;
private mapping forbidden;
private void rehash_all();
int rehash(string place);
string* walk_directory (string);
/** @ignore yes */
void save_me() {
unguarded( (: save_object(SAVE, 3) :));
} /* save_me() */
/** @ignore yes */
void load_me() {
unguarded( (: restore_object(SAVE, 1) :));
} /* load_me() */
void setup() {
set_short( "" + mud_name() + " Armoury" );
add_property( "determinate", "the " );
set_light( 50 );
set_long( "You are in the " + mud_name() + " Armoury. From here you can list "
"all of the \"weapons\", \"armour\", \"clothing\", \"jewellery\", "
"\"food\", \"plants\", \"misc\" "
"and \"scabbards\" that are available, and \"request\" one of "
"them. The armoury stores its list of items in mappings, which "
"can be updated with \"rehash\". The list of forbidden items "
"can be listed out or added to with \"forbid\".\n" );
_smalls = ([ ]);
_areas = ([ ]);
_armour_list = _weapon_list = _clothing_list = _plant_list = _misc_list =
_jewellery_list = _scabbard_list = _food_list = ([ ]);
// If it is in the boot sequence
load_me();
rehash_all();
} /* setup() */
/**
* This returns the list of small objects available for the area. These
* are things that can be fgound in gutters and so on.
* @param area the area of the recycling
* @return the array of small objects
*/
object *query_smalls(string area) {
if (!area) {
area = DEFAULT_RECYCLE_AREA;
}
return _smalls[area];
}
/**
* Returns the list of domain armoury items.
* @param domain the domain/area to get the items from.
* @return the area sub-mapping.
*/
mapping query_area(string domain) {
if (_areas[domain]) {
return _areas[domain];
}
return ([ ]);
} /* query_area() */
int request(string, int, string);
int forbid(string);
/** @ignore yes */
void init() {
string comm;
::init();
add_command("rehash", "{foods|clothes|weapons|armours|misc|jewelleries|"
"scabbards|plants}",
(:rehash($4[0]) :));
add_command("rehash", "domain <string'domain'>",
(:rehash($4[0]) :));
add_command("request", "<string'item name'> <number'percent'>",
(:request($4[0], $4[1], 0):));
add_command("request", "<string'item name'> <number'percent'> in <string'area'>",
(:request($4[0], $4[1], $4[2]):));
add_command("forbid", "", (:forbid, "":));
add_command("forbid", "<string'name'>", (:forbid($4[0]):));
foreach(comm in ({"weapons", "armour", "clothing", "jewellery", "plant",
"misc", "scabbards", "food", })) {
add_command(comm, "", (:call_other(this_object(), $(comm)):));
}
} /* init() */
/* Internal Functions */
// create a list of armours/whatever. base_dir is the dir to look in,
// extensions is the file extensions to look for. The base_dir will be
// recursed to find all files in sub-dirs as well.
/** @ignore yes */
mapping make_list(string base_dir, string *extensions) {
string *dirs, dir, extension, file_name;
// mixed *tmp, *file;
mapping list;
dirs = walk_directory (base_dir);
// make a mapping of names to filenames
list = ([ ]);
foreach(dir in dirs) {
foreach(extension in extensions) {
foreach(file_name in unguarded((: get_dir, dir+"*"+extension :))) {
if (file_name[0] != '.') {
list[ replace_string(explode(file_name, ".")[0], "_", " ") ] =
dir + "" + file_name;
}
}
}
}
return list;
} /* make_list() */
/** @ignore yes */
int clean_up(int i) { return 0; }
// External functions
/**
* This will choose a small item from the specific area.
* @param area the are to check in
* @return the small object to choose
*/
object choose_small_item(string area) {
object ob;
if(!area)
area = DEFAULT_RECYCLE_AREA;
if(!_smalls || !_smalls[area] || !arrayp(_smalls[area]) || !_smalls[area][0])
return 0;
ob = clone_object(_smalls[area][0]);
_smalls[area] = _smalls[area][1..];
return ob;
}
/**
* General item retreival function. It returns a new item
* it handles any sort of item. The percentage is the
* percentage condition in which it is returned.
* The "word" parameter is the name of the requested item, essentially
* the filename without directory and with "_"'s changed to " "'s. The
* "percent" parameter is used as measure of how good it
* is compared to a fully functional one. So a 50% dagger is only 50% as
* good as a 100% one.
* <p>
* The file armoury.h defines the ARMOURY variable. You need to include
* this into your file to use it. You should include this file and
* uyse ythis define rather than the full path to the armoury, as
* otherwise your code could stuff up :)
* @param word the name of the item to retreive
* @param percent the percentage condition to be in
* @param area the area to choose the item in
* @see /include/armoury.h
* @return the requested object, or 0 on failure
* @example
* // get a slightly worn long sword and give it to the npc.
* ARMOURY->request_item("long sword", 85)->move(npc);
* @example
* // Get a random condition armour.
* armour = ARMOURY->request_item("leather jacket", random(100));
*/
object request_item(string word, int percent, string area) {
int add_area;
object thing;
string filename;
if(!word) {
return 0;
}
if (!area) {
area = DEFAULT_RECYCLE_AREA;
}
if (_areas[area] &&
_areas[area][word]) {
filename = _areas[area][word];
add_area = 1;
} else if(_weapon_list[word]) {
filename = _weapon_list[word];
} else if(_armour_list[word]) {
filename = _armour_list[word];
} else if(_clothing_list[word]) {
filename = _clothing_list[word];
} else if(_jewellery_list[word]) {
filename = _jewellery_list[word];
} else if(_scabbard_list[word]) {
filename = _scabbard_list[word];
} else if(_misc_list[word]) {
filename = _misc_list[word];
} else if(_plant_list[word]) {
filename = _plant_list[word];
} else if(_food_list[word]) {
filename = _food_list[word];
} else {
if(base_name(previous_object()) != "/cmds/creator/req_uest") {
printf( "No file for item \"%s\".\n", word );
log_file( "ARMOURY", "%s No file for %s (%s)\n", ctime(time())[4..9],
word, file_name( previous_object() ) );
}
return 0;
}
if(file_size( filename ) > 0)
thing = clone_object(filename);
if(!thing) {
printf( "Cannot find item \"%s\".\n", word );
log_file( "ARMOURY", "Cannot find %s (%s)\n", word,
file_name( previous_object() ) );
return 0;
}
_total_requests++;
if(!_smalls[area])
_smalls[area] = ({ });
// If there's less than 20 items in the smalls list and this item meets
// all the criteria then add it.
if(sizeof(_smalls[area]) < 20 &&
thing->query_weight() < roll_MdN(5, 20) && // must be small
thing->query_value() < random(4000) && // not too valuable
!thing->query_liquid() && // must not be a liquid
!sizeof((mapping)thing->query_value_info()) &&
strsrch(filename, "_pt") == -1 && // not in pt
strsrch(filename, "_dev") == -1 && // not in dev
!thing->query_property("no recycling")) { // must be recyclable
_smalls[area] += ({ filename });
}
if (add_area)
thing->add_property(ARMOURY_RECYCLING_AREA_PROP, area);
thing->set_percentage( percent );
return thing;
} /* request_item() */
/**
* This method tries to find a match for the object if it has moved between
* directories.
* @param path the old path
* @return the new path
*/
string remap_file_path(string path) {
string* bits;
mapping list;
string new_fname;
bits = explode(path, "/");
if (bits[0] != "obj") {
return path;
}
switch (bits[1]) {
case "armours" :
list = _armour_list;
break;
case "weapons" :
list = _weapon_list;
break;
case "clothes" :
list = _clothing_list;
break;
case "jewellery" :
list = _jewellery_list;
break;
case "scabbards" :
list = _scabbard_list;
break;
case "food" :
list = _food_list;
break;
case "misc" :
list = _misc_list;
break;
case "plants" :
list = _plant_list;
break;
}
if (list) {
new_fname = list[replace_string(explode(bits[<1], ".")[0], "_", " ")];
if (new_fname) {
return new_fname;
}
}
return path;
} /* remap_file_path() */
// Action functions
// Update the list of known items
/** @ignore yes */
int rehash(string thing) {
string special;
int found;
switch (thing) {
case "armours" :
_armour_list=make_list("/obj/armours/", ({".arm", ".c"}));
break;
case "clothes" :
_clothing_list=make_list("/obj/clothes/", ({".clo", ".c"}));
break;
case "weapons" :
_weapon_list = make_list("/obj/weapons/", ({".wep", ".c"}));
break;
case "scabbards" :
_scabbard_list = make_list( "/obj/scabbards/", ({ ".sca", ".c" }));
break;
case "jewellery" :
case "jewelleries" :
_jewellery_list = make_list( "/obj/jewellery/", ({ ".arm", ".clo", ".c" }));
break;
case "food" :
case "foods" :
_food_list = make_list( "/obj/food/", ({ ".ob", ".food", ".c" }));
break;
case "misc" :
_misc_list = make_list( "/obj/misc/", ({ ".ob", ".c" }));
break;
case "plants" :
_plant_list = make_list( "/obj/plants/", ({ ".ob", ".food", ".c" }));
break;
default :
// Rehash the domains...
if (file_size("/d/" + thing + "/items") == -2) {
_areas[thing] = make_list( "/d/" + thing + "/items/",
({ ".arm", ".c", ".clo", ".wep", ".sca",
".ob", ".food" }));
found = 1;
}
if (file_size("/d/" + thing + "/armoury") == -2) {
foreach (special in get_dir("/d/" + thing + "/armoury")) {
if (file_size("/d/" + thing + "/armoury/" + special + "/") == -2) {
_areas[special] = make_list( "/d/" + thing + "/armoury/" + special + "/",
({ ".arm", ".c", ".clo", ".wep", ".sca" }));
}
}
return 0;
}
}
save_me();
write("Rehash of " + thing + " complete.\n");
return 1;
}
/**
* This method rehashes a specific directory of things. At the moment we
* fudge this a little.
* @param dir the directory to rehash
* @return 1 if it found something to rehash
*/
int rehash_specific_dir(string dir) {
return rehash(explode(dir, "/")[1]);
}
/**
* This will only be called in the bootup sequence -- yea right! I've made it
* work a little more slowly.
*/
private void rehash_all() {
string thing;
int i;
foreach(thing in TYPES + "/secure/master"->query_domains()) {
i += 5;
call_out("rehash", i, thing);
}
} /* rehash_all() */
/** @ignore yes */
string *query_types() { return TYPES + keys(_areas); }
/** @ignore yes */
mapping query_items(string type, string filter) {
mapping items;
string item;
switch(type) {
case "armours":
items = _armour_list;
break;
case "weapons":
items = _weapon_list;
break;
case "clothes":
items = _clothing_list;
break;
case "jewellery":
case "jewelleries":
items = _jewellery_list;
break;
case "scabbards":
items = _scabbard_list;
break;
case "food":
case "foods":
items = _food_list;
break;
case "misc":
items = _misc_list;
break;
case "plants":
items = _plant_list;
break;
default:
if(member_array(type, "/secure/master"->query_domains()) != -1 &&
_areas[type])
items = _areas[type];
else
return 0;
}
items = copy (items);
if (sizeof (filter)) {
foreach(item in keys(items))
if(strsrch(item, filter) == -1)
map_delete(items, item);
}
return items;
}
// Return an item for someone
/** @ignore yes */
int request( string word, int percentage, string area ) {
object thing;
thing = request_item( word, percentage, area );
if ( !thing ) {
add_failed_mess(word + " not found.\n");
return 0;
}
if ( (int)thing->move( this_player() ) != MOVE_OK ) {
write( (string)thing->a_short() +" has been placed in here.\n" );
thing->move( this_object() );
} else
write( (string)thing->a_short() +
" has been placed in your inventory.\n" );
return 1;
} /* request() */
/** @ignore yes */
mixed stats() {
string tmp;
mixed *ret;
ret = ({ });
foreach(tmp in keys(_areas)) {
ret += ({ tmp, sizeof(_areas[tmp]) });
}
return ::stats() + ({
({ "item requests", _total_requests, }),
({ "armours", sizeof(_armour_list) }),
({ "clothes", sizeof(_clothing_list) }),
({ "weapons", sizeof(_weapon_list) }),
({ "scabbards", sizeof(_scabbard_list) }),
({ "jewellery", sizeof(_jewellery_list) }),
({ "food", sizeof(_food_list) }),
({ "misc", sizeof(_misc_list) }),
({ "plants", sizeof(_plant_list) }),
ret });
} /* stats() */
/** @ignore yes */
string* walk_directory (string dir) {
string *tmp, *dirs = ({ }), *tmp2;
mixed *file;
tmp = get_dir(dir, -1);
if (sizeof (tmp))
dirs += ({dir});
foreach(file in tmp) {
if(file[1] == -2) {
tmp2 = walk_directory (dir + file [0] + "/");
if (sizeof (tmp2))
dirs += tmp2;
}
}
return dirs;
}