/**
* This is the item object for add items in rooms.
* @author Someone from DW, most likely.
* @change Added built in day/night support and
* cleaned up the command parsing stuff - Sandoz, April 2002.
*/
#include <parse_command.h>
#include <obj_parser.h>
#include <room.h>
#define POSITION_SIZE 2
#define POSITION_DESC 0
#define POSITION_MULT 1
// Item descriptions.
private string *_shrt;
private mixed _lng;
// Optional things.
private mapping _verb, _plural, _other, _pattern, _gather, _pos_stuff;
// Item indexes.
private int *_cur_desc, *_day, *_night;
// The room that ownz us.
nosave object _room;
/** @ignore yes */
void set_my_room( object ob ) { _room = ob; }
/** @ignore yes */
object query_my_room() { return _room; }
/** @ignore yes */
int query_item_available( int i ) {
if( WEATHER_H->query_day() )
return member_array( i, _night ) == -1;
return member_array( i, _day ) == -1;
} /* query_item_available() */
/** @ignore yes */
int query_common_item( int i ) {
return member_array( i, ( _day + _night ) ) == -1;
} /* query_day_item() */
/** @ignore yes */
int query_day_item( int i ) {
return member_array( i, _day ) != -1;
} /* query_day_item() */
/** @ignore yes */
int query_night_item( int i ) {
return member_array( i, _night ) != -1;
} /* query_day_item() */
/** @ignore yes */
string print_stuff() {
return sprintf("lng = %O\nshrt = %O\nverb = %O\nplural = %O\nother = %O\n"
"pattern = %O\ncur_desc = %O\ngather = %O\nday = %O\n"
"night = %O\n", _lng, _shrt, _verb, _plural, _other, _pattern,
_cur_desc, _gather, _day, _night );
} /* print_stuff() */
/** @ignore yes */
void init() {
string name;
mapping rest;
foreach( name, rest in _other )
if( sizeof( filter( keys(rest), (: query_item_available($1) :) ) ) )
add_command( name, _pattern[name] );
} /* init() */
/**
* This method is called whenever an item is added with a command
* pattern, this way if we add an item while someone is already
* in the room, they will get the command immediately as well.
* This should also be called whenever day turns to night and
* vice versa.
*/
void do_init() {
object *obs;
string name;
mapping rest;
if( !_room || !sizeof( obs = filter( INV(_room), (: living($1) :) ) ) )
return;
obs->remove_object2(TO);
foreach( name, rest in _other )
if( sizeof( filter( keys(rest), (: query_item_available($1) :) ) ) )
obs->add_command( name, TO, _pattern[name] );
} /* do_init() */
/** @ignore yes */
void create() {
_lng = ({""});
_shrt = ({""});
_gather = ([ ]);
_verb = ([ ]);
_plural = ([ ]);
_pattern = ([ ]);
_other = ([ ]);
_day = ({ });
_night = ({ });
_cur_desc = ({ });
} /* create() */
/** @ignore yes */
int query_visible( object thing ) { return 1; }
/** @ignore yes */
string hide_invis_string() { return ""; }
/**
* This method returns the base array of shorts to be processed in the
* other short methods. This is the short without the leading
* 'the', 'a' whatever...
* @return the array of shorts
*/
string *query_short_array() { return map( _cur_desc, (: _shrt[$1] :) ); }
/** @ignore yes */
string short() {
return query_multiple_short(query_short_array());
} /* short() */
/** @ignore yes */
string pretty_short() {
string *ret;
ret = query_short_array();
return ( sizeof(ret) ? query_multiple_short(ret) : "something");
} /* pretty_short() */
/** @ignore yes */
string a_short() {
return implode( map( query_short_array(),
(: "$mirror_short:"+add_a($1)+"$" :) ), "");
} /* a_short() */
/** @ignore yes */
string the_short() {
return implode( map( query_short_array(),
(: "$mirror_short:the "+$1+"$" :) ), "");
} /* the_short() */
/** @ignore yes */
string one_short() {
return implode( map( query_short_array(),
(: "$mirror_short:the "+$1+"$" :) ), "");
} /* one_short() */
/** @ignore yes */
string poss_short() {
return implode( map( query_short_array(),
(: "$mirror_short:the "+$1+"$" :) ), "");
} /* poss_short() */
/**
* @ignore yes
* This is used to make the reads of the items not fail.
*/
string query_read_short() {
return "$name$";
} /* query_read_short() */
/** @ignore yes */
string query_plural() {
return query_multiple_short( map( _cur_desc,
(: pluralize( _shrt[$1] ) :) ) );
} /* query_plural() */
/** @ignore yes */
string pretty_plural() {
int i;
foreach( i in _cur_desc ) {
tell_creator("sandoz", "%s called pretty_plural() on %s, "
"returning %s.", file_name(PO), file_name(TO),
pluralize(_shrt[i]) );
return pluralize(_shrt[i]);
}
return 0;
} /* query_plural() */
/** @ignore yes */
string long( string name, int dark ) {
int i;
string *ret;
ret = ({ });
foreach( i in _cur_desc ) {
if( !_lng[ i ] )
continue;
if( functionp(_lng[ i ]) )
ret += ({ evaluate(_lng[ i ]) });
else
ret += ({ _lng[ i ] });
}
if( !sizeof( ret ) )
return "There doesn't appear to be a description for "+
name+", please contact a creator immediately.\n";
return implode( evaluate(ret), "\n")+"\n";
} /* long() */
/**
* This returns the currently matching indexes for the item object.
* @return the array of currently matchig indexes
*/
int *query_cur_desc() { return _cur_desc; }
/**
* This method returns the verbs mapping used in the item object.
* @return the verbs in the item object
*/
mapping query_verbs() { return _verb; }
/**
* This method returns the plurals mapping used in the item object.
* @return the plurals in the item object
*/
mapping query_plurals() { return _plural; }
/**
* This method returns all the long descriptions for the item object. The
* positions in the array correspond to the index used to reference the
* items.
* @return the array of long descriptions
*/
string *query_lng() { return _lng; }
/**
* This method returns all the short descriptions for the item object. The
* positions in the array correspond to the index used to reference the
* items.
* @return the array of long descriptions
*/
string *query_shrt() { return _shrt; }
/**
* This method figures out what gatherables are available for the
* current description set. The array contains the values passed
* into the add_item stuff with the "gather" index.
* @return the current gatherables
*/
mixed query_gather() {
mixed g;
int i;
g = ({ });
foreach( i in _cur_desc )
if( _gather[i])
g += ({ _gather[i] });
return g;
} /* query_gather() */
/** @ignore yes */
int query_item(string str) { return _verb[str]; }
/** @ignore yes */
int *query_day_items() { return _day; }
/** @ignore yes */
int *query_night_items() { return _night; }
/**
* This method setups all the internal stuff, including the long
* description.
* @param bits the bits to look through
* @param index the index which refers to the item in question
*/
private void setup_bits_for_item( int index, mixed bits ) {
int i, j, k;
string str, tmp;
mixed pat;
if( !pointerp(bits) ) {
_lng[index] = bits;
return ;
}
_lng[index] = "You see nothing special.";
j = sizeof(bits);
for( i = 0; i < j; i += 2 ) {
k = sizeof( bits[i+1] );
/*
* Figure out what the pattern should be. We should have a pattern
* specified if the second element of the array is a function
* pointer, or the array is three long.
*/
if( pointerp(bits[i+1]) && ( k == 3 ||
( k == 2 && functionp(bits[i+1][0]) ) ) ) {
pat = bits[i+1][<1];
if( !pointerp(pat) )
pat = ({ pat });
} else {
pat = ({"<direct:object>"});
}
/* Ok. Now check the other bits. */
if( !pointerp(bits[i]) )
bits[i] = ({ bits[i] });
/* Go through and add all the relevant data for the bit in. */
foreach( str in bits[i] ) {
switch( str ) {
case "long" :
_lng[index] = bits[i+1];
continue;
case "gather" :
_gather[index] = bits[i+1];
continue;
case "position" :
case "position multiple" :
if( !_pos_stuff )
_pos_stuff = ([ ]);
if( !_pos_stuff[index] )
_pos_stuff[index] = allocate(POSITION_SIZE);
_pos_stuff[index][ ( str == "position" ?
POSITION_DESC : POSITION_MULT ) ] = bits[i+1];
continue;
default :
// We've new command stuff.
if( find_call_out("do_init") == -1 )
call_out("do_init", 1 );
if( !_other[str] ) {
_pattern[str] = pat;
_other[str] = ([ index : bits[i+1] ]);
continue;
}
foreach( tmp in pat )
if( member_array( tmp, _pattern[str] ) == -1 )
_pattern[str] += ({ tmp });
_other[str][index] = bits[i+1];
}
}
}
} /* setup_bits_for_item() */
/**
* This is a helper function for the delete function, it removes everything
* not in the standard static arrays.
* @param index the index to remove
*/
private void remove_bits_for_item( int index ) {
string str;
mixed value;
int frog, bing, i;
/* Fix up the gather mapping. */
map_delete( _gather, index );
foreach( frog, bing in _gather ) {
if( frog > index ) {
map_delete( _gather, frog );
_gather[frog-1] = bing;
}
}
/* Fix up the position mapping. */
if( _pos_stuff ) {
map_delete( _pos_stuff, index );
foreach( frog, bing in _pos_stuff ) {
if( frog > index ) {
map_delete( _pos_stuff, frog );
_pos_stuff[frog-1] = bing;
}
}
}
/* Fix up the parse_command mapping. */
foreach( str, value in _other ) {
if( value ) {
map_delete( value, index );
if( !sizeof(value) ) {
map_delete( _other, str );
map_delete( _pattern, str );
} else {
foreach( frog, bing in value ) {
if( frog > index ) {
map_delete( value, frog );
value[frog-1] = bing;
}
}
}
if( find_call_out("do_init") == -1 )
call_out("do_init", 1 );
}
}
/* Fix up the verbs array. */
foreach( str, value in _verb ) {
for( i = 0; i < sizeof(value); i += 2 ) {
if( value[i+1] > index ) {
value[i+1]--;
} else if( value[i+1] == index ) {
value = value[0..i-1] + value[i+2..];
i -= 2;
}
}
if( !sizeof(value) )
map_delete( _verb, str );
else
_verb[str] = value;
}
/* Fix up the plural array. */
foreach( str, value in _plural ) {
for( i = 0; i < sizeof(value); i += 2 ) {
if( value[i+1] > index ) {
value[i+1]--;
} else if( value[i+1] == index ) {
value = value[0..i-1] + value[i+2..];
i -= 2;
}
}
if( !sizeof(value) )
map_delete( _plural, str );
else
_plural[str] = value;
}
// Update day items.
i = sizeof(_day);
while( i-- )
if( _day[i] > index )
_day[i]--;
// Update night items.
i = sizeof(_night);
while( i-- )
if( _night[i] > index )
_night[i]--;
} /* remove_bits_for_item() */
/**
* This method sets up all the parse command handling stuff that is needed
* for the given name.
* @param index the index to associate it with
* @param name the name to add in
* @param no_plural do not add in any plurals
*/
private void add_name_reference( int index, string name, int no_plural ) {
string *bits, plural, s;
bits = explode( name, " " );
s = bits[<1];
if( !_verb[s] )
_verb[s] = ({ bits[0..<2], index });
else
_verb[s] += ({ bits[0..<2], index });
if( !no_plural ) {
plural = pluralize(s);
if( !_plural[plural] )
_plural[plural] = ({ bits[0..<2], index });
else
_plural[plural] += ({ bits[0..<2], index });
}
} /* add_name_reference() */
/**
* This method setups the item initially. This is called by the
* add_item method in the room itself.
* @param nam the name(s) to add the item for
* @param long the long description of the item
* @param no_plural do not add a plural flag
* @return the index associated with the item
*/
int setup_item( mixed nam, mixed long, int no_plural, int time ) {
int index;
index = sizeof(_lng);
if( pointerp(nam) ) {
if( sizeof(nam) > 0 )
_shrt += ({ nam[0] });
map( nam, (: add_name_reference( $2, $1, $3 ) :), index, no_plural );
} else {
_shrt += ({ nam });
add_name_reference( index, nam, no_plural );
}
_lng += ({ 0 });
setup_bits_for_item( index, long );
switch( time ) {
case ITEM_DAY :
_day += ({ index });
break;
case ITEM_NIGHT :
_night += ({ index });
break;
default:
}
return index;
} /* setup_item() */
/**
* This method non-destructively modifies the items values. It will not
* remove the value for this item, remember this! If you add again
* and again you will end up with multiple of the same object.
* @see /std/room->modify_item_by_name()
* @param str the name to reference the object by
* @param long the long bits to change on the item
* @param time the item (day/night/common) to modify
* @return 1 on success, 0 on failure
*/
int modify_item_by_index( int index, mixed long, int time ) {
if( index >= sizeof(_lng) )
return 0;
switch( time ) {
case ITEM_DAY :
if( !query_day_item(index) )
return 0;
break;
case ITEM_NIGHT :
if( !query_night_item(index) )
return 0;
break;
default:
if( !query_common_item(index) )
return 0;
}
setup_bits_for_item( index, long );
return 1;
} /* modify_item_by_index() */
/**
* This method non-destructively modifies the items values. It will not
* remove the value for this item, remember this! If you add again
* and again you will end up with multiple of the same object.
* @see /std/room->modify_item_by_name()
* @param str the name to reference the object by
* @param long the long bits to change on the item
* @param time the item (day/night/common) to modify
* @return 1 on success, 0 on failure
*/
int modify_item_by_name( string str, mixed long, int time ) {
int index;
if( ( index = member_array( str, _shrt ) ) == -1 )
return 0;
return modify_item_by_index( index, long, time );
} /* modify_item_by_name() */
/** @ignore yes */
int modify_item( mixed str, mixed long, int time ) {
if( stringp(str) )
return modify_item_by_name( str, long, time );
if( intp(str) )
return modify_item_by_index( str, long, time );
return 0;
} /* modify_item() */
/**
* This method removes an item by the returned index number from the
* add_item method.
* @param index the index number to remove
* @param time the item (day/night/common) to remove
* @return 1 on success, 0 on failure
* @example
* int _item_number;
*
* void bing {
* _item_numer = add_item("frog", "It wombles!");
* } /\* bing() *\/
*
* void remove_bing() {
* remove_item(_item_number);
* } /\* remove_bing() *\/
*/
int remove_item_by_index( int index, int time ) {
if( index >= sizeof(_lng) )
return 0;
switch( time ) {
case ITEM_DAY :
if( !query_day_item(index) )
return 0;
_day -= ({ index });
break;
case ITEM_NIGHT :
if( !query_night_item(index) )
return 0;
_night -= ({ index });
break;
default:
if( !query_common_item(index) )
return 0;
}
_shrt = _shrt[0..index-1] + _shrt[index+1..];
_lng = _lng[0..index-1] + _lng[index+1..];
remove_bits_for_item( index );
return 1;
} /* remove_item_by_index() */
/**
* This method removes an item by the short description.
* @param index the index number to remove
* @param time the item (day/night/common) to remove
* @return 1 on success, 0 on failure
*/
int remove_item_by_name( string str, int time ) {
int index, last = 0, sz = sizeof( _shrt );
while( last < sz && ( index = member_array( str, _shrt, last ) ) != -1 ) {
if( remove_item_by_index( index, time ) )
return 1;
last = index + 1;
}
return 0;
} /* remove_item_by_name() */
/** @ignore yes */
int remove_item( mixed str, int time ) {
if( stringp(str) )
return remove_item_by_name( str, time );
if( intp(str) )
return remove_item_by_index( str, time );
return 0;
} /* remove_item() */
/** @ignore yes */
string *parse_command_id_list() {
tell_creator("sandoz", "%s called parse_command_id_list() on %s.",
file_name(PO), file_name(TO) );
return keys(_verb);
} /* parse_command_id_list() */
/** @ignore yes */
string *parse_command_plural_id_list() {
string *_plu, word;
mixed words;
int i;
tell_creator("sandoz", "%s called parse_command_plural_id_list() on %s.",
file_name(PO), file_name(TO) );
_plu = ({ });
foreach( word, words in _plural ) {
_plu += ({ word });
for( i = 0; i < sizeof(words); i += 2 ) {
if( sizeof(words[i]) )
_plu += ({ implode( words[i], " " )+" "+word });
}
}
return _plu;
} /* parse_command_plural_id_list() */
/** @ignore yes */
string *parse_command_adjective_id_list() {
string *_adjs, word;
mixed words;
int i, j;
tell_creator("sandoz", "%s called parse_command_adjective_id_list() "
"on %s.", file_name(PO), file_name(TO) );
_adjs = ({ });
foreach( word, words in _verb ) {
j = sizeof(words);
for( i = 0; i < j; i += 2 )
if( sizeof(words[i]) )
_adjs += words[i];
}
return uniq_array( _adjs );
} /* parse_command_adjective_id_list() */
/** @ignore yes */
object query_parse_id( mixed arr ) {
string *bits;
mixed stuff;
int i, j, match, all_match;
bits = explode(arr[P_STR], " ");
bits -= ({"a", "an", "the"});
tell_creator("sandoz", "%s called query_parse_id() on %s\n"
"Bits : %O", file_name(PO), file_name(TO), bits );
/* all case */
if( arr[P_THING] == 0 ) {
stuff = _plural[bits[<1]];
if( !stuff ) {
stuff = _verb[bits[<1]];
if( !stuff )
return 0;
}
_cur_desc = ({ });
all_match = 0;
for( j = 0; j < sizeof(stuff); j += 2 ) {
match = 1;
for( i = 0; i < sizeof(bits) - 1; i++ ) {
if( member_array( bits[i], stuff[j] ) == -1 ) {
match = 0;
break;
}
}
if( match && query_item_available(stuff[j+1]) ) {
all_match = 1;
if( member_array( stuff[j+1], _cur_desc ) == -1 )
_cur_desc += ({ stuff[j+1] });
}
}
if( all_match )
return TO;
else
return 0;
}
/* Specific object case. */
if( arr[P_THING] < 0 ) {
stuff = _verb[bits[<1]];
if( !stuff )
return 0;
for( j = 0; j < sizeof(stuff); j += 2 ) {
match = 1;
for( i = 0; i < sizeof(bits) - 1; i++ ) {
if( member_array( bits[i], stuff[j] ) == -1 ) {
match = 0;
break;
}
}
if( ++arr[P_THING] != 0 )
continue;
/* Get the current thingy out of the list. */
if( match && query_item_available(stuff[j+1]) ) {
_cur_desc = ({ stuff[j+1] });
arr[P_THING] = -10321;
return TO;
}
}
return 0;
}
/* Lots of objects case. The objects are specified though. */
stuff = _plural[bits[<1]];
if( !stuff ) {
stuff = _verb[bits[<1]];
if( !stuff )
return 0;
}
_cur_desc = ({ });
for( j = 0; j < sizeof(stuff); j += 2 ) {
match = 1;
for( i = 0; i < sizeof(bits)-1; i++ ) {
if( member_array( bits[i], stuff[j] ) == -1 ) {
match = 0;
break;
}
}
if( match && query_item_available(stuff[j+1]) ) {
if( member_array( stuff[j+1], _cur_desc ) == -1 )
_cur_desc += ({ stuff[j+1] });
arr[P_THING]--;
if( arr[P_THING] <= 0 ) {
arr[P_THING] = -10786;
return TO;
}
}
}
return 0;
} /* query_parse_id() */
/** @ignore yes */
mixed parse_match_object( string *input, object player,
class obj_match_context context ) {
int *stuff, i, j, match, ret, pl, sn, inp;
mixed plural, sing;
inp = sizeof(input);
if( input[<1] == "here" && inp > 1 )
input = input[0..<2];
_cur_desc = filter( _cur_desc, (: query_item_available($1) :) );
/* context */
if( TO == context->it && inp == 1 && input[0] == "it") {
sing = map( _cur_desc, (: explode(_shrt[$1], " ")[<1] :) ) +
({ _cur_desc[0] });
plural = ({ });
} else if( input[<1] == "them" && context->plural &&
member_array( TO, context->plural ) != -1 ) {
plural = map( _cur_desc, (: explode(_shrt[$1], " ")[<1] :) ) +
({ _cur_desc[0] });
sing = ({ });
} else {
plural = _plural[input[<1]];
sing = _verb[input[<1]];
}
pl = sizeof(plural);
sn = sizeof(sing);
if( !pl && !sn )
return 0;
stuff = ({ });
// Check singular matches.
if( sn > 1 ) {
for( i = 0; i < sn; i += 2 ) {
match = 1;
for( j = 0; j < inp - 1; j++ ) {
if( member_array( input[j], sing[i] ) == -1 ) {
match = 0;
break;
}
}
if( match ) {
if( member_array( sing[i+1], stuff ) == -1 &&
query_item_available(sing[i+1]) ) {
stuff += ({ sing[i+1] });
ret |= OBJ_PARSER_MATCH_SINGULAR;
}
}
}
}
// Check plural matches.
for( i = 0; i < pl; i += 2 ) {
match = 1;
for( j = 0; j < inp - 1; j++ ) {
if( member_array( input[j], plural[i] ) == -1 ) {
match = 0;
break;
}
}
if( match ) {
if( member_array( plural[i+1], stuff ) == -1 &&
query_item_available(plural[i+1]) ) {
stuff += ({ plural[i+1] });
ret |= OBJ_PARSER_MATCH_PLURAL;
}
}
}
// We matched, so see if we should have.
if( inp = sizeof(stuff) ) {
if( context->ordinal ) {
if( context->ordinal > inp ) {
context->ordinal -= inp;
return 0;
}
context->ignore_rest = 1;
context->ordinal--;
_cur_desc = stuff[context->ordinal..context->ordinal];
} else if( context->number_included ) {
_cur_desc = stuff[0..context->number_included];
context->number_included -= inp;
if( context->number_included <= 0 )
context->ignore_rest = 1;
} else if( ret & OBJ_PARSER_MATCH_PLURAL ) {
_cur_desc = stuff;
} else {
_cur_desc = stuff[0..0];
}
return ({ ret, ({ TO }) });
}
return 0;
} /* parse_match_object() */
/** @ignore yes */
int command_control( string command, object *indir, string arg1,
string arg2, string *args, string pattern ) {
int desc, num;
mixed tmp;
int i;
if( !_other[command] )
return 0;
foreach( desc in _cur_desc ) {
i = sizeof( tmp = _other[command][desc] );
if( pointerp(tmp) && pattern == tmp[<1] ) {
if( i == 3 || ( i == 2 && !functionp(tmp[0]) ) ) {
// Took out 'command' as the first argument for add_command
// compliance, you can always use query_verb() - Sandoz.
num += call_other( tmp[0], tmp[1], indir, arg1, arg2,
args, pattern );
} else if( i == 2 && functionp(tmp[0]) ) {
// See above.
num += evaluate( tmp[0], indir, arg1, arg2, args, pattern );
}
} else if( pattern == "<direct:object>") {
if( functionp(tmp) ) {
// See above.
num += evaluate( tmp, indir, arg1, arg2, args, pattern );
} else if( stringp(tmp) ) {
add_succeeded_mess( ({ tmp, ""}) );
num++;
} else if( pointerp(tmp) && i == 2 && stringp(tmp[0]) &&
stringp(tmp[1]) ) {
add_succeeded_mess( tmp );
num++;
}
}
}
return num;
} /* command_control() */
/** @ignore yes */
mapping query_other_things() { return _other; }
/** @ignore yes */
mapping query_pattern() { return _pattern; }
/** @ignore yes */
string query_position_string( string pos ) {
int i;
if( !_pos_stuff )
return 0;
foreach( i in _cur_desc )
if( _pos_stuff[i] )
return _pos_stuff[i][POSITION_DESC];
return 0;
} /* query_position_string() */
/** @ignore yes */
int query_position_multiple( string pos ) {
int i;
if( !_pos_stuff )
return 0;
foreach( i in _cur_desc )
if( _pos_stuff[i])
return _pos_stuff[i][POSITION_MULT];
return 0;
} /* query_position_multiple() */
/** @ignore yes */
mapping query_position_stuff() { return _pos_stuff; }
/** @ignore yes */
int drop() { return 1; }
/** @ignore yes */
int get() { return 1; }
/** @ignore yes */
void dest_me() { destruct(TO); }
/** @ignore yes */
void dwep() { destruct(TO); }
/** @ignore yes */
int move() { return 1; }