/**
* The standard inheritable object for player-run shop storerooms.
* This object defines the section of the shop responsible for
* holding the stock, and contains the stock cabinets. The majority
* of player-shop functionality is handled from within the main office.
* See the header file for a complete description of the shop's workings.
* @see /include/player_shop.h
* @see /std/shops/player_shop/office.c
* @see /std/shops/player_shop/mgr_office.c
* @see /std/shops/player_shop/counter.c
* @see /std/shops/player_shop/shop_front.c
* @see /std/shops/player_shop/shopkeeper.c
* @author Ringo
* @started 1st August 1999
*/
#include <player_shop.h>
#include <move_failures.h>
#include <money.h>
#define ITEM_COST "player_shop_item_cost"
inherit ROOM_OBJ;
string get_list( mixed *, string );
int do_list( object *, object * );
int do_add( object *, object *, int );
int do_remove( string );
private nosave string _office = "",
_counter = "",
_mgr_office = "",
_shop_front = "",
_office_dir = "",
_counter_dir = "",
_shop_dir = "";
private nosave object *_cabinets = ({});
private nosave int _num_cabinets;
/** @ignore yes */
void create()
{
do_setup++;
::create();
do_setup--;
add_property("no burial", 1);
add_property( "determinate", "" );
// add_item( "cabinet", "There are several cabinets in the room, holding "
// "the entire stock of the shop. If you are authorised to do so, you "
// "may \"list\" the stock, as well as \"add\" and \"remove\" "
// "items from the cabinets." );
add_item( ({ "roll", "bags", "bag" }), ({ "long", "You could probably \"pull\""
" a bag off the roll and give it to a customer with their purchase.\n"
"Bags should only be handed out with purchases of 10 or more items.",
"pull", ({ this_object(), "pull_roll" }) }) );
if ( !do_setup )
{
this_object()->setup();
this_object()->reset();
}
add_extra_look( this_object() );
}
/* create() */
/** @ignore yes */
void init()
{
::init();
if ( !_office || _office == "" )
{
return;
}
if ( _office->query_employee( this_player()->query_name() ) ||
creatorp(this_player()) )
{
add_command( "add", "<indirect:object:me'item(s)'>", (: do_add($1, ({}), 0) :) );
add_command( "add", "<indirect:object:me'item(s)'> for <number'cost'>",
(: do_add($1, ({}), $4[sizeof($4)-1]) :) );
add_command( "add", "<indirect:object:me'item(s)'> to <indirect:object:here'cabinet(s)'>",
(: do_add($1[0], $1[1], 0) :) );
add_command( "add", "<indirect:object:me'item(s)'> to <indirect:object:here'cabinet(s)'> for <number'cost'>",
(: do_add($1[0], $1[1], $4[0]) :) );
add_command( "list", "", (: do_list($1, ({})) :) );
}
if ( _office->query_supervisor( TP->query_name() ) ||
_office->query_manager( TP->query_name() ) ||
creatorp( TP ) ) {
/* add_command( "remove", ({
"<number> <string'item(s)'> from stock",
"<string'item'> <number> from stock",
"<number> <string'item(s)'> from cabinet <number'cabinet'>",
"<string'item'> <number> from cabinet <number'cabinet'>", }),
(: do_remove($4,$5) :) );*/
add_command("remove", "<string'object(s)'> from stock",
(: do_remove($4[0]) :));
}
if ( _office->query_retired( this_player()->query_name() ) )
{
add_command( "list", ({ "", "cabinet <number'cabinet'>",
"<string'item'>", "<string'item'> in cabinet <number'cabinet'>" }),
(: do_list($4,$5) :) );
}
} /* init() */
/**
* Extra text for long description.
* This function advises of the bag roll & cabinets in the room.
* Mask this function to amend the extra_look.
*/
string extra_look( object thing )
{
if ( thing == this_object() )
{
return "There are "+query_num(sizeof(_cabinets))+" cabinets in the "
"room, holding the entire stock of the shop.\n";
}
}
/* extra_look() */
/** @ignore yes */
private void init_cabinets()
{
object cabinet;
int i;
string cab_name;
for ( i = 0; i < _num_cabinets; i++ )
{
cab_name = "cabinet"+ i;
cabinet = clone_object( CABINET );
// cabinet->set_name( cab_name );
cabinet->set_save_file( SAVE_DIR+ _office->shop_very_short()+
"_"+ cab_name );
_cabinets += ({ cabinet });
add_hidden_object(cabinet);
}
}
/* init_cabinets() */
/**
* Set the path of the main office.
* @param path The full path & filename.
*/
void set_office( string path )
{
_office = path;
_counter = _office->query_counter();
_mgr_office = _office->query_mgr_office();
_shop_front = _office->query_shop_front();
_num_cabinets = _office->query_num_cabinets();
init_cabinets();
}
/* set_office() */
/** @ignore yes */
int add_cabinet()
{
object cabinet;
string cab_name;
if ( ( _num_cabinets ) == MAX_CABINETS )
{
return 0;
}
cab_name = "cabinet"+ _num_cabinets++;
cabinet = clone_object( CABINET );
cabinet->set_name( cab_name );
cabinet->set_save_file( SAVE_DIR+ _office->shop_very_short()+
"_"+ cab_name );
_cabinets += ({ cabinet });
return 1;
}
/* add_cabinet() */
/** @ignore yes */
string remove_cabinet()
{
object cabinet;
if ( ( _num_cabinets ) == MIN_CABINETS )
{
return "";
}
_num_cabinets--;
cabinet = _cabinets[_num_cabinets];
_cabinets -= ({ cabinet });
cabinet->move( "/room/rubbish" );
return cabinet->query_name();
}
/* remove_cabinet() */
/**
* Query the current number of items in stock.
* @param item The item to query.
* @param cabinet The cabinet to look in (0 to look at all stock).
* @return The number of that item in stock.
*/
int query_num_items( string item, int cabinet )
{
int num_items = 0;
if ( cabinet )
{
return sizeof( match_objects_for_existence( item, ({ _cabinets[cabinet-1] }) ) );
}
//for ( cabinet = 0; cabinet < _num_cabinets; cabinet++ )
//{
num_items += sizeof( match_objects_for_existence( item, _cabinets ) );
//}
return num_items;
}
/* query_num_items() */
/**
* Query the current stock.
* @param cabinet The cabinet to look in (0 to look at all stock).
* @return The stock.
*/
object *query_stock( int cabinet )
{
object *items = ({});
int i;
if ( cabinet )
{
return copy( all_inventory( _cabinets[cabinet-1] ) );
}
for ( i = 0; i < _num_cabinets; i++ )
{
items += all_inventory( _cabinets[i] );
}
return copy(items);
}
/* query_stock() */
/** @ignore yes */
string get_list( mixed *args, string pattern )
{
string ret;
object cabinet, ob;
mixed *texts;
int i, flag;
object *items = ({ });
switch(pattern) {
case "" :
//Searching for *everything*
foreach(cabinet in _cabinets) {
items += INV(cabinet);
}
ret = "Items listing for "+_office->query_shop_name();
break;
case "cabinet <number'cabinet'>" :
//Searching in the one cabinet.
items += INV(_cabinets[args[0]-1]);
break;
ret = "Items listing for "+_office->query_shop_name()+
", in cabinet "+query_num(args[0]);
case "<string'item'>" :
//All items matching specified string, in any cabinet.
foreach(cabinet in _cabinets) {
items += INV(cabinet);
}
filter(items, (: $1->id($2) :), args[0]);
ret = "\""+args[0]+"\""+" listing for "+_office->query_shop_name();
break;
case "<string'item'> in cabinet <number'cabinet'>" :
//Matching specified string in specified cabinet.
items += INV(_cabinets[args[1]-1]);
filter(items, (: $1->id($2) :), args[0]);
ret = "\""+args[0]+"\""+" listing for "+_office->query_shop_name()+
", in cabinet "+query_num(args[1]);
break;
}
ret += "\n";
texts = ({ });
foreach(ob in items) {
flag = 0;
for(i = 0; i < sizeof(texts); i++) {
if(arrayp(texts[i]) && sizeof(texts[i])) {
if(ob->query_short() == texts[i][0]->query_short() &&
ob->query_property(ITEM_COST) ==
texts[i][0]->query_property(ITEM_COST)) {
texts[i] += ({ ob });
flag = 1;
}
}
}
if(!flag) {
texts += ({ ({ ob }) });
}
}
for(i = 0; i < sizeof(texts); i++) {
ret += " "+query_multiple_short(texts[i])+" for "+
MONEY_HAND->money_value_string(
texts[i][0]->query_property(ITEM_COST), _office->query_place())+
(sizeof(texts[i]) > 1 ? " each " : "") + "\n";
}
return ret;
}
/** @ignore yes */
int do_list( object *items, object *cabinets )
{
string list;
object thing;
mapping things = ([]);
if(!sizeof(cabinets)) cabinets = _cabinets;
if(!sizeof(items)) {
items = ({ });
foreach(thing in cabinets) {
items += INV(thing);
}
}
foreach(thing in items) {
if(member_array(thing->query_property(ITEM_COST), keys(things)) == -1) {
things += ([ thing->query_property(ITEM_COST) : ({ thing }) ]);
}
else {
things[thing->query_property(ITEM_COST)] += ({ thing });
}
}
list = "Items on sale for " + _office->query_shop_name() + ":\n";
if(!sizeof(things)) {
tell_object(TP, list + "There is nothing on sale at the moment.\n");
return 1;
}
foreach(int key in keys(things)) {
list += (query_multiple_short(things[key]) + " for " +
MONEY_HAND->money_value_string(key, _office->query_place()) +
(sizeof(things[key])>1?" each":"")+"\n");
}
tell_object(TP, list);
return 1;
}
/* do_list() */
/** @ignore yes */
int do_add( object *items, object *cabinets, int cost)
{
object cabinet;
object *succeed, *failed, *tmp_failed;
if(!sizeof(cabinets)) cabinets = _cabinets;
succeed = items;
failed = items;
tmp_failed = ({});
foreach(cabinet in cabinets) {
if(sizeof(items)) {
tmp_failed = cabinet->add_items(items, TP, (cost?cost*sizeof(items):0));
items -= tmp_failed;
succeed -= tmp_failed;
}
}
if(cost) {
cost *= sizeof(succeed);
}
else {
foreach(cabinet in succeed) { //Yeah, yeah, I know...
cost += cabinet->query_value();
}
}
failed -= succeed;
if(sizeof(succeed)) {
if(sizeof(failed)) {
tell_object(TP, "You add "+query_multiple_short(succeed)+" to the "
"inventory for the value of "+cost+
", but you cannot add "+
query_multiple_short(failed)+" for some reason.\n");
}
else {
tell_object(TP, "You add "+query_multiple_short(succeed)+" to the "
"inventory for the value of "+cost+".\n");
}
TP->save();
}
else {
tell_object(TP, "You fail to add "+query_multiple_short(failed)+" to "
"the shop's inventory.\n");
}
return 1;
}
/* do_add() */
/** @ignore yes */
int do_remove( string thing )
{
object *items, cabinet, *cab_items, *failed;
items = ({ });
failed = ({ });
items = match_objects_for_existence(thing, _cabinets);
foreach(cabinet in _cabinets) {
cab_items = filter(items, (: ENV($1) == $2 :), cabinet);
failed += cabinet->remove_items(cab_items, TP);
}
items -= failed;
if(sizeof(failed)) {
tell_object(TP, sprintf("You remove %s from the shop's stock, but you cannot "
"carry %s, so you leave %s here.\n", query_multiple_short(items),
query_multiple_short(failed), (sizeof(failed)>1?"them":"it")));
}
else {
tell_object(TP, sprintf("You remove %s from the shop's stock.\n",
query_multiple_short(items)));
}
return 1;
}
/* object *items, *failed, *test;
object cabinet;
int i;
items = ({ });
switch(pat) {
case "<number> <string'item(s)'> from stock" :
foreach(cabinet in _cabinets) {
i -= 1;
test = filter(INV(cabinet), (: $1->id($2) :), args[1]);
if(i < sizeof(test) && i > -1) {
test = test[0..i];
}
if(i > -1) {
items += test;
failed += cabinet->remove_items( items, TP );
}
}
items -= failed;
break;
/* "<string'item'> <number> from stock",
"<number> <string'item(s)'> from cabinet <number'cabinet'>",
"<string'item'> <number> from cabinet <number'cabinet'>" */
/*
}
return 1;
*/
/* int i, number, cab_no = 0;
object *items, *stock, *failed, *test;
string plural;
return notify_fail("This sucks. Fix it. New syntax, etc, updated for "
"the new behaviour. Do it!\n");
failed = ({});
add_succeeded_mess("");
if ( sizeof( args ) > 2 )
{
cab_no = args[2];
}
if ( query_num_items( args[1], cab_no ) < args[0] )
{
tell_object( this_player(), "The stock does not contain "+
args[0] + " "+ args[1]+ " to remove.\n" );
return 1;
}
items = ({});
if ( !cab_no )
{
number = args[0];
for( i = sizeof( _cabinets ); i > 0; i-- )
{
stock = match_objects_for_existence( args[1], ({ _cabinets[i-1] }) );
if ( sizeof(stock) > number )
{
stock = stock[ 0 .. (number-1) ];
}
items += stock;
failed += _cabinets[i-1]->remove_items( stock, this_player() );
number -= sizeof( stock );
if ( !number )
{
break;
}
}
}
else
{
items = match_objects_for_existence( args[0]+ " "+ args[1], ({ _cabinets[cab_no-1] }) );
failed = _cabinets[cab_no - 1]->remove_items( items, this_player() );
}
items -= failed;
if ( sizeof( failed ) )
{
tell_room( environment( this_player() ), this_player()->query_short()+
" accidentally drops "+ query_multiple_short( failed )+
" on the floor.\n", ({ this_player() }) );
tell_object( this_player() , "You accidentally drop "+
query_multiple_short( failed )+ "on the floor.\n" );
}
if ( sizeof( items ) )
{
_office->shop_log( SALE, this_player()->query_name(),
this_player()->convert_message( query_multiple_short( items ) )+
" removed from stock" );
foreach ( plural in _office->query_list_array() )
{
test = ({});
parse_command( plural, items, "%i", test );
if ( !test || !sizeof( test ) )
{
continue;
}
_office->adjust_sold( plural, sizeof( test ) - 1 );
}
add_succeeded_mess( "$N $V "+ query_multiple_short( items )+
" from the stock.\n");
}
return 1;*/
/* do_remove() */
/** @ignore yes */
void dest_me()
{
object cabinet;
foreach( cabinet in _cabinets )
{
cabinet->dest_me();
}
::dest_me();
}
/* dest_me() */
/**
* Set the directions to other parts of the shop.
* This function is used by the npc shopkeeper to navigate
* around the shop, using the exits at the given directions.
* These directions should be the standard "north", "southeast" etc.
* @param office The direction to the office.
* @param counter The direction to the counter.
* @param shop The direction to the shop front.
*/
void set_directions( string office, string counter, string shop )
{
_office_dir = office;
_counter_dir = counter;
_shop_dir = shop;
}
/* set_directions() */
/**
* Query the direction to another part of the shop.
* This function is used by the npc shopkeeper to navigate
* around the shop.
* @param place The full path to the destination.
* @return The direction, or "here" if already there.
*/
string directions_to( string place )
{
if ( place == _counter )
{
return copy(_counter_dir);
}
if ( place == _office )
{
return copy(_office_dir);
}
if ( place == _shop_front )
{
return copy(_shop_dir);
}
return "here";
}
/* directions_to() */
/**
* Someone has entered the room.
* This function will automatically fire an employee if they have
* teleported to this room. To avoid this happening, or to modify,
* mask this function.
*/
void event_enter( object ob, string message, object from )
{
_office->event_enter( ob, message, from );
}
/* event_enter() */
/**
* Someone has died.
* This function will automatically fire an employee if they have
* killed someone whilst on duty. It will also make a note of anyone
* who has killed an on-duty employee (including the npc shopkeeper). To
* avoid this happening, or to modify, mask this function.
*/
void event_death( object killed, object *others, object killer,
string rmess, string kmess )
{
_office->event_death( killed, others, killer );
}
/* event_death() */
/**
* Query the path to the office.
* @return The path to the office.
*/
string query_office() { return copy( _office ); }
/**
* @ignore yes
* Get a shopping bag
*/
int pull_roll()
{
object bag;
string message, day, day2, month;
bag = clone_object( SHOP_BAG );
sscanf( mudtime(time()), "%*s %s %s %s", day, day2, month );
if ( member_array( month, ({ "Offle", "February", "March",
"April", "May", "June", "Grune", "August", "Spune", "Sektober",
"Ember", "December", "Ick" }) ) == -1 )
{
message = sprintf( "A very happy %s %s %s\n\nfrom everyone at\n\n%s, %s",
day, day2, month, _office->query_shop_name(), _office->query_place() );
}
else
{
message = sprintf( "With the compliments of\n\n%s, %s.",
_office->query_shop_name(), _office->query_place() );
}
bag->set_read_mess( message );
if ( (int)bag->move( this_player() ) != MOVE_OK )
{
bag->move( this_object() );
tell_object( this_player(),
"You drop the bag as you're carrying too much.\n" );
}
this_player()->add_succeeded_mess( this_object(),
"$N $V the roll and $I comes off.\n", ({ bag }) );
return 1;
}
/* pull_roll() */
/**
* @ignore yes
* Add standard stuff
*/
void set_long( string long_desc )
{
long_desc += "Employees can \"add\" something to, \"remove\" "
"something from and \"list\" the stock.\nThere is a roll of "
"shopping bags conveniently located on one wall.\n";
::set_long( long_desc );
}
/* set_long() */
/** @ignore yes */
object *query_cabinets() {
return _cabinets;
}