/
LIB3/
LIB3/D/ADMIN/
LIB3/D/ADMIN/OBJ/
LIB3/D/ADMIN/ROOM/W/
LIB3/D/HOME/
LIB3/D/HOME/CITY/ARENA/
LIB3/D/HOME/CITY/ITEMS/
LIB3/D/HOME/CITY/POSTOFFI/
LIB3/DOC/
LIB3/GLOBAL/SPECIAL/
LIB3/GLOBAL/VIRTUAL/
LIB3/NET/
LIB3/NET/CONFIG/
LIB3/NET/DAEMON/CHARS/
LIB3/NET/GOPHER/
LIB3/NET/INHERIT/
LIB3/NET/OBJ/
LIB3/NET/SAVE/
LIB3/NET/VIRTUAL/
LIB3/OBJ/B_DAY/
LIB3/OBJ/HANDLERS/TERM_TYP/
LIB3/PLAYERS/B/
LIB3/PLAYERS/N/
LIB3/ROOM/
LIB3/SAVE/
LIB3/SAVE/BOARDS/
LIB3/SAVE/ENVIRON/
LIB3/SAVE/POST/
LIB3/STD/COMMANDS/SHADOWS/
LIB3/STD/CREATOR/
LIB3/STD/DOM/
LIB3/STD/EFFECTS/
LIB3/STD/EFFECTS/HEALING/
LIB3/STD/EFFECTS/OTHER/
LIB3/STD/EFFECTS/POISONS/
LIB3/STD/ENVIRON/
LIB3/STD/GUILDS/
LIB3/STD/LIQUIDS/
LIB3/STD/ROOM/
LIB3/STD/TRIGGER/SHADOW/
LIB3/W/
LIB3/W/BANNOR/
LIB3/W/NEWSTYLE/
inherit "/std/room";

/*
 * Original made who knows when.
 * Modified by bil to make the list a lot nicer.
 * Modified by Pinkfish to give shops types and make them send out
 * reps to sell/buy things from other shops.
 */

#include "money.h"
#include "move_failures.h"
#include "shop.h"
#include "armoury.h"

mixed   our_storeroom;
mixed   buy_mess, sell_mess, list_mess, value_mess, browse_mess, open_cond, *other_shops;
int     amount_sold, amount_bought;
string  shop_type;

string  shop_list( mixed arr, int detail );
void    do_buy( object *ob, int cost, object pl );
string  shop_parse( string str, mixed ob, object client, string money,
		    string extra );
void    do_parse( mixed arr, mixed ob, object client, string money,
		  string extra );

void    create()
{
    buy_mess = ({ "You buy $ob$ for $money$.\n",
		  "$client$ buys $ob$.\n" });
    sell_mess = ({ "You sell $ob$ for $money$.\n",
		   "$client$ sells $ob$.\n" });
    list_mess = "$extra$";
    value_mess = "The $ob$ is valued at $money$.\n";
    browse_mess = "The $ob$ costs $money$, it looks like:\n$extra$";
    open_cond = 1;
    other_shops = ({ });
    shop_type = "general";
    ::create();
}

void    set_sell_message( mixed str )
{
    sell_mess = str;
}
void    set_buy_message( mixed str )
{
    buy_mess = str;
}
void    set_value_message( mixed str )
{
    value_mess = str;
}
void    set_browse_message( mixed str )
{
    browse_mess = str;
}
void    set_list_message( mixed str )
{
    list_mess = str;
}
void    set_open_condition( mixed str )
{
    open_cond = str;
}

mixed   query_sell_mess()
{
    return sell_mess;
}
mixed   query_list_mess()
{
    return list_mess;
}
mixed   query_value_mess()
{
    return value_mess;
}
mixed   query_buy_mess()
{
    return buy_mess;
}
mixed   query_browse_mess()
{
    return browse_mess;
}
mixed   query_open_condition()
{
    return open_cond;
}

int     test_open()
{
    if( stringp( open_cond ) )
	return( int ) call_other( this_object(), open_cond );

    if( intp( open_cond ) )
	return open_cond;
    return( int ) call_other( open_cond[ 0 ], open_cond[ 1 ] );
}

void    init()
{
    ::init();
    add_action( "sell", "sell" );
    add_action( "buy", "buy" );
    add_action( "list", "list" );
    add_action( "browse", "browse" );
    add_action( "value", "value" );
}

int     scaled_value( int n )
{
    int *   fish;
    int     i, tot;

    tot = 0;
    i = 0;
    fish = PAY_RATES;
    while( n && i < sizeof( fish ) )
    {
	if( n >= fish[ i ] )
	{
	    tot += fish[ i + 1 ] * fish[ i ] / 100;
	    n -= fish[ i ];
	}
	else
	{
	    tot += fish[ i + 1 ] * n / 100;
	    n = 0;
	}
	i += 2;
    }
    return tot;
}

int     sell( string str )
{
    object *obs, *selling, *cannot;
    mixed * m_array;
    int     i, j, no, amt, value, total_amt;
    string  s;

    if( !test_open() )
	return 0;

    if( !str || str == "" )
    {
	notify_fail( "Usage: sell <objects>\n" );
	return 0;
    }
    obs = find_match( str, this_player() );
    if( !sizeof( obs ) )
    {
	notify_fail( "Nothing to sell.\n" );
	return 0;
    }
    if( sizeof( obs ) > MAX_OBS )
    {
	write( "The shopkeeper can't cope with all those objects.\n" );
	obs = obs[ 0..MAX_OBS - 1 ];
    }
    selling = cannot = ({ });
    for( i = 0; i < sizeof( obs ); i++ )
    {
	if( obs[ i ]->query_value() > 0 && !obs[ i ]->do_not_sell() )
	{
	    if( obs[ i ]->move( our_storeroom ) )
	    {
		if( obs[ i ]->short() )
		            cannot += ({ obs[ i ] });

		continue;
	    }
/* the call other (buried) in the below is so that we can update /std/shop
   and have all the shops prices fall in line consistently */
	    if( (amt = (int)"/std/shop"->scaled_value( (int)obs[ i ]->query_value() )) > MAX_AMOUNT )
		amt = MAX_AMOUNT;
	    total_amt += amt;
	    selling += ({ obs[ i ] });
	    obs[ i ]->being_sold();
	}
	else
	    if( obs[ i ]->short() )
		        cannot += ({ obs[ i ] });
    }
    if( !sizeof( selling ) )
    {
	if( sizeof( cannot ) )
	    notify_fail( "You cannot sell " + query_multiple_short( cannot ) + ".\n" );
	else
	    notify_fail( "Nothing to sell.\n" );
	return 0;
    }
    amount_sold += total_amt;
    m_array = (mixed *)MONEY_HAND->create_money_array( total_amt );
    this_player()->adjust_money( m_array );
    if( sizeof( cannot ) )
	write( "You cannot sell " + query_multiple_short( cannot ) + ".\n" );
    do_parse( sell_mess, selling, this_player(),
	      (string)MONEY_HAND->money_string( m_array ), "" );
    return 1;
}

int     buy( string str )
{
    int     i, amt, ob_amt, total_cost;
    object *obs, *to_buy, *cannot, *too_much;
    string  s;

    if( !test_open() )
	return 0;

    if( !str || str == "" )
    {
	notify_fail( "Usage: buy <objects>\n" );
	return 0;
    }
    obs = find_match( str, our_storeroom );
    if( !sizeof( obs ) )
    {
	notify_fail( "Cannot find " + str + ".\n" );
	return 0;
    }
    if( sizeof( obs ) > MAX_OBS )
    {
	write( "The shopkeeper can't cope with all those objects.\n" );
	obs = obs[ 0..MAX_OBS - 1 ];
    }
    to_buy = too_much = cannot = ({ });
    amt = (int)this_player()->query_value();
    while( i < sizeof( obs ) )
    {
	if( (ob_amt = (int)obs[ i ]->query_value()) > amt )
	{
	    if( obs[ i ]->short() )
		        too_much += ({ obs[ i ] });

	    obs = delete( obs, i, 1 );
	    continue;
	}
	if( obs[ i ]->move( this_player() ) )
	{
	    if( obs[ i ]->short() )
		        cannot += ({ obs[ i ] });

	    i++;
	    continue;
	}
	obs[ i ]->move( our_storeroom );
	amt -= ob_amt;
	total_cost += ob_amt;
	to_buy += ({ obs[ i ] });
	i++;
    }
    amount_bought += total_cost;
    s = "";
    if( sizeof( cannot ) )
	s += "You cannot pick up " + query_multiple_short( cannot ) + ".\n";
    if( sizeof( too_much ) )
	s += capitalize( query_multiple_short( too_much ) ) + " costs too much.\n";
    if( !sizeof( to_buy ) )
    {
	if( s != "" )
	    notify_fail( s );
	else
	    notify_fail( "Nothing to buy.\n" );
	return 0;
    }
    else
    {
	write( s );
    }
    do_buy( to_buy, total_cost, this_player() );
    return 1;
}

void    do_buy( object *obs, int cost, object pl )
{
    int     i;
    mixed   fish;

    for( i = 0; i < sizeof( obs ); i++ )
	obs[ i ]->move( pl );
    pl->pay_money( fish = (int)MONEY_HAND->create_money_array( cost ) );
    do_parse( buy_mess, obs, pl,
	      (string)MONEY_HAND->money_string( fish ), "" );
}

int     list( string str )
{
    object  ob;

    if( !test_open() )
	return 0;
    if( !str || str == "" || str == "all" )
    {
	if( objectp( our_storeroom ) )
	    ob = our_storeroom;
	else
	{
	    our_storeroom->rabbit_away();
	    ob = find_object( our_storeroom );
	}
	do_parse( list_mess, this_object(), this_player(), "",
		  shop_list( all_inventory( ob ), 0 ) );
	return 1;
    }
    do_parse( list_mess, this_object(), this_player(), "",
	      shop_list( find_match( str, our_storeroom ), 1 ) );
    return 1;
}

int     browse( string str )
{
    object *obs;
    int     i;

    if( !test_open() )
	return 0;

    if( !str || str == "" )
    {
	notify_fail( "Usage: browse <objects>\n" );
	return 0;
    }

    obs = find_match( str, our_storeroom );
    if( !sizeof( obs ) )
    {
	notify_fail( "Cannot find " + str + ".\n" );
	return 0;
    }
    for( i = 0; i < sizeof( obs ); i++ )
	do_parse( browse_mess, obs[ i ], this_player(),
		  (string)MONEY_HAND->money_value_string( obs[ i ]->query_value() ),
		  (string)obs[ i ]->long() );

/*
   write("You look at "+obs[i]->short()+" it costs "+
   MONEY_HAND->money_string(obs[i]->query_money_array())+"\n"+
   obs[i]->long());
 */
    return 1;
}

int     value( string str )
{
    object *obs;
    int     i, val;

    if( !test_open() )
	return 0;

    if( !str || str == "" )
    {
	notify_fail( "Usage: value <object>\n" );
	return 0;
    }

    obs = find_match( str, this_player() );
    if( !sizeof( obs ) )
    {
	notify_fail( "Cannot find " + str + ".\n" );
	return 0;
    }
    for( i = 0; i < sizeof( obs ); i++ )
    {
	val = (int)obs[ i ]->query_value();
/* the call other is so that we can change the PAY_RATES array, and then
   just update /std/shop to immediately and consistently effect all shops */
	val = (int)"/std/shop"->scaled_value( val );
	if( val > MAX_AMOUNT )
	    val = MAX_AMOUNT;
	do_parse( value_mess, obs[ i ], this_player(),
		  (string)MONEY_HAND->money_value_string( val ),
		  (string)obs[ i ]->do_not_sell() );
    }
    return 1;
}

string  shop_list( mixed arr, int detail )
{
    mapping inv, costs;
    object *list;
    string  s, mon, *shorts, *vals;
    int     i, j;
    mixed   ind;

    if( pointerp( arr ) )
	list = arr;
    else
	list = all_inventory( this_object() );
/* only keep track of things with shorts ;) */
    inv = ([ ]);
    for( i = 0; i < sizeof( list ); i++ )
    {
	s = (string)list[ i ]->short();

	if( !s || !list[ i ]->query_value() )
	    continue;
	if( !stringp( s ) )
	    s = "get a creator for this one!";
	if( inv[ s ] )
	    inv[ s ] += ({ list[ i ] });
	else
	    inv[ s ] = ({ list[ i ] });
    }
/* ok print it */
    s = "";
    shorts = m_indices( inv );
    if( !sizeof( shorts ) )
    {
	if( detail )
	    return "The shop is all out of what you wanted.\n";
	else
	    return "The shop is totally out of stock.\n";
    }
    s = "You find on offer:\n";
    for( i = 0; i < sizeof( shorts ); i++ )
    {
	ind = inv[ shorts[ i ] ];
	switch( sizeof( ind ) )
	{
	    case 1:
		s += "Our very last " + shorts[ i ];
		break;
	    case 2..5:
		s += capitalize( query_num( sizeof( ind ), 0 ) + " " +
				 (string)ind[ 0 ]->query_plural() );
		break;
	    default:
		if( detail )
		    s += capitalize( query_num( sizeof( ind ), 0 ) + " " +
				     (string)ind[ 0 ]->query_plural() );
		else
		    s += "A large selection of " +
			(string)ind[ 0 ]->query_plural();
	}
	if( detail )
	{
	    costs = ([ ]);
	    for( j = 0; j < sizeof( ind ); j++ )
	    {
		mon = (string)MONEY_HAND->money_value_string( (int)ind[ j ]->query_value() );
		if( !costs[ mon ] )
		    costs[ mon ] = ({ "" + (j + 1) });
		else
		    costs[ mon ] += ({ "" + (j + 1) });
	    }
	    if( m_sizeof( costs ) == 1 )
	    {
		s += " for " + m_indices( costs )[ 0 ];
		if( sizeof( m_values( costs )[ 0 ] ) > 1 )
		    s += " each.\n";
		else
		    s += ".\n";
	    }
	    else
	    {
		s += ":-\n";
		vals = m_indices( costs );
		for( j = 0; j < sizeof( vals ); j++ )
		    s += "  [#" + implode( costs[ vals[ j ] ], "," ) + "] for " + vals[ j ] +
			".\n";
	    }
	}
	else
	{
	    s += ".\n";
	}
    }
    return s;
}

void    set_store_room( mixed ob )
{
    if( stringp( ob ) )
    {
	our_storeroom = find_object( ob );
	if( !our_storeroom )
	    call_other( ob, "??" );
	our_storeroom = find_object( ob );
    }
    our_storeroom = ob;
}
object  query_store_room()
{
    return our_storeroom;
}

void    do_parse( mixed arr, mixed ob, object client, string money,
		  string extra )
{
    if( stringp( arr ) )
	write( shop_parse( arr, ob, client, money, extra ) );
    else
    {
	write( shop_parse( arr[ 0 ], ob, client, money, extra ) );
	say( shop_parse( arr[ 1 ], ob, client, money, extra ) );
    }
}

string  shop_parse( string str, mixed ob, object client, string money,
		    string extra )
{
    string  s1, s2, s3, rest;

    rest = "";
    while( sscanf( str, "%s$%s$%s", s1, s2, s3 ) == 3 )
	switch( s2 )
	{
	    case "ob":
		if( pointerp( ob ) )
		    str = s1 + query_multiple_short( ob ) + s3;
		else
		    str = s1 + ob->short() + s3;

		break;
	    case "client":
		str = s1 + client->query_cap_name() + s3;
		break;
	    case "extra":
		str = s1 + extra + s3;
		break;
	    case "money":
		str = s1 + money + s3;
		break;
	    default:
		rest = s1 + "$" + s2 + "$";
		str = s3;
		break;
	}
    return rest + str;
}

/* Other shop handling code */
int     add_other_shop( mixed shop )
{
    mixed * co_ord;

    if( pointerp( shop ) )
    {				/* Given us a co-ordinate */
	other_shops += ({ shop });
	return 1;
    }
    co_ord = (mixed *)shop->query_co_ord();
    if( !pointerp( co_ord ) )
	return 0;
    other_shops += ({ co_ord });
}

string  query_shop_type()
{
    return shop_type;
}
void    set_shop_type( string ty )
{
    shop_type = ty;
}

/* Ok, now, sales rep creation... Creates a rep  */
object  create_rep()
{
    object  ob;

    ob = clone_object( "/obj/monster" );
    ob->set_name( "rep" );
    ob->set_short( "sales rep" );
    ob->add_adjective( "sales" );
    ob->set_long( "A tall strong looking sales rep.  He stares at you with " +
		  "bright pierceing eyes.\n" );
    ob->set_guild( "fighter" );
    ob->set_race( "human" );
    ob->adjust_bon_str( 15 );	/* Strong so they can carry all the junk */
    ob->set_level( 300 );	/* Don't want em killed.  They make a lot of money :) */
    ARMOURY->request_weapon( "bastard sword", 100 )->move( ob );
    ARMOURY->request_weapon( "plate mail", 100 )->move( ob );
    ARMOURY->request_weapon( "metal shield", 100 )->move( ob );
    ob->init_equip();
    ob->add_property( "rep type", shop_type );
}

/* Send them out!  Watch them run! */
void    send_out_reps()
{
    int     i;
    object  ob;

    for( i = 0; i < sizeof( other_shops ); i++ )
    {
	ob = (object)this_object()->create_rep();
	ob->add_property( "goto co-ordinate", other_shops[ i ] );
	ob->add_property( "goto property", "shop" );
	ob->move( this_object() );
	ob->add_triggered_action( "froggy", "goto_co_ord", this_object(),
				  "rep_made_it" );
    }
}

void    rep_made_it( int bing )
{
    object *obs;
    int     i, cost;

    if( !bing )
    {
	previous_object()->init_command( "'Oh no!  I am utterly lost" );
	previous_object()->init_command( "sigh" );
	call_out( "set_up_return", 5, previous_object() );
	return;
    }
    obs = (object *)environment( previous_object() )->query_stock( shop_type );
    if( !sizeof( obs ) )
    {
	this_object()->none_to_sell();
	call_out( "set_up_return", 5, previous_object() );
	return;
    }
    for( i = 0; i < sizeof( obs ); i++ )
	cost += (int)obs[ i ]->query_value() * 2 / 3;
    call_out( "do_rep_buy", 5, ({ previous_object(), obs, cost }) );
    previous_object()->adjust_value( cost );
}

/*
 * The shop types are:
 *  Jewelery
 *  Armoury
 *  Magic
 *  General
 */
object *query_stock( string type )
{
    mixed * obs;
    int     i;

    obs = unique_array( all_inventory( our_storeroom ), "query_shop_type" );
    for( i = 0; i < sizeof( obs ); i++ )
	if( (string)obs[ i ][ 0 ]->query_shop_type() == type )
	    return obs[ i ];
    return({ });
}

void    do_rep_buy( mixed *bing )
{
    object  rep, *obs;
    int     i, cost;

    rep = bing[ 0 ];
    obs = bing[ 1 ];
    cost = bing[ 2 ];
    do_buy( obs, cost, rep );
    call_out( "set_up_return", 5, rep );
}

void    set_up_return( object rep )
{
    rep->add_property( "goto co-ordinate", this_object()->query_co_ord() );
    rep->add_triggered_action( "froggy", "goto_co_ord", this_object(),
			       "rep_came_back" );
}

int     rep_came_back()
{
    int     i;
    object *obs;

    obs = all_inventory( previous_object() );
    for( i = 0; i < sizeof( obs ); i++ )
	obs[ i ]->move( our_storeroom );
    previous_object()->dest_me();
}

mixed * stats()
{
    return ::stats() + ({ 
			     ({ "total sold", amount_sold }),
			     ({ "total bought", amount_bought }),
			      });
}