/* generic shop equipped with LSC parser */ /* written by square@imperial.mud.lp */ #include <lsc.h> #include <mudlib.h> #include <move.h> #include <money.h> inherit LSC; inherit ROOM ; inherit COINVALUE ; #define NOWEALTH 0 #define NOCHANGE 1 static object office; static object storeroom; static object shopkeeper; static object last_customer; static object customer; static object item; /* goods being asked/valued/bought/sold */ static int item_value; /* estimated true value */ static int desired_buy_price; /* relative to the shop, not customer */ static int desired_sell_price; static int fail_reason; static int *give_coins, *get_coins; /* again, relative to the shop */ static mapping cost; /* object in storeroom->value in copper units */ mapping wealth; string owner; string * money_types ; int * type_value ; int money_type_size; int partial_wealth(mapping w, int level); int sum(int * array, int size); int _end(); int _show_goods(); string Convert_amount_string(int value); int read_lsc_file(); void create() { seteuid(getuid()); lsc2::create(); Reset(); wait_time = 1; _speed = 6; /* shop code shouldn't be run fast */ _max_ch_size = 0; /* can't spawn child processes */ cost = ([]); wealth = (["platinum":2, "gold":10, "silver":8, "copper":100]); set("light",10); set("volume", 10000000); set("capacity", 10000000); set("long", "This is a generic shop.\n"+ "commands: list, value, ask, buy, sell\n"); storeroom = clone_object(STORAGE); shopkeeper = clone_object("obj/orc"); shopkeeper->set_name("The shop keeper"); shopkeeper->set("short", "A shop keeper"); shopkeeper->set("id", ({"shop keeper", "keeper", "man"}) ); shopkeeper->move(this_object()); customer = 0; money_types = ({ "copper", "silver", "gold","platinum" }); type_value = ({ 1, 10, 100, 1000 }); money_type_size = sizeof(money_types); give_coins = allocate(money_type_size); get_coins = allocate(money_type_size); read_lsc_file(); } /***************************************************************/ /* it returns the best coin combination extractable from the */ /* wealth mapping w1 to w2. If it's impossible, 0 is returned. */ /* c1 and c2 are the capacity of the 2 wealths mapping. */ /* change is indicated by negative number in its slot */ /***************************************************************/ int * coin_combination( int amount, mapping w1, mapping w2, int c1,int c2, int level); int * best_coin_combination(int amount, mapping w1, mapping w2, int c1,int c2) { fail_reason = NOCHANGE; if (!w1 || !w2) return 0; return coin_combination(amount, w1, w2, c1, c2, money_type_size); } /********************************************************/ /* it returns the coin combination extractable from the */ /* wealth mapping w. If it's impossible, 0 is returned. */ /********************************************************/ int * coin_combination( int amount, mapping w1, mapping w2, int c1,int c2, int level) { int * coins, i, tmp; int pay_coins; int * alternative; if (partial_wealth(w1, level) < amount) { if (level==money_type_size) fail_reason = NOWEALTH; return 0; } coins = allocate(i=level); while(i--) coins[i] = 0; if (!amount) return coins; pay_coins = amount / type_value[level-1]; if ( w1[money_types[level-1]] < pay_coins) { pay_coins = w1[money_types[level-1]]; } if (alternative = coin_combination(amount-pay_coins*type_value[level-1], w1, w2, c1+pay_coins, c2-pay_coins, level-1)) { tmp=sum(alternative,level-1)+pay_coins; if (tmp>0) { if (tmp <= c2) return alternative + ({ pay_coins }); } else { if (-tmp <= c1) return alternative + ({ pay_coins }); } } if (w1[money_types[level-1]]==pay_coins) return 0; /* see if w2 can make change */ if (alternative = coin_combination((pay_coins+1)*type_value[level-1]-amount, w2, w1, c1+pay_coins+1, c2-pay_coins-1, level-1)) { for(i=0;i<level-1;i++) alternative[i] = -alternative[i]; tmp = pay_coins + 1 + sum(alternative, level-1); if (tmp>0) { if (tmp <= c2) return alternative + ({ pay_coins + 1}); } else { if (-tmp <= c1) return alternative + ({ pay_coins + 1}); } } /* oh well, not possible then */ return 0; } int partial_wealth(mapping w, int level) { int i,ret,tmp; string tmpstr; ret = 0; if (level > money_type_size) level = money_type_size; for(i = 0; i<level ;i++) { ret += type_value[i] * w[money_types[i]]; } return ret; } int sum(int * array, int size) { int ret,i; if (!size) size = sizeof(array); ret = 0; while(size--) ret+=array[size]; return ret; } remove() { if(shopkeeper) shopkeeper->remove(); if(storeroom) storeroom->remove(); ::remove(); } void init() { add_action ("list", "list") ; add_action ("value", "value") ; add_action ("ask", "ask") ; add_action ("buy", "buy") ; add_action ("sell", "sell") ; add_action ("enter_storeroom", "storeroom") ; add_action ("enter_office", "office"); } int enter_storeroom() { mixed owners; if (!storeroom) { notify_fail("where is the storeroom?\n"); return 0; } if (!wizardp(this_player()) && (string) this_player()->query("name") != owner && (undefinedp(owners=_varlist["owners"]) || pointerp(owners) && member_array(this_player()->query("name"), owners)==-1)) { notify_fail("You aren't allowed to enter the storeroom\n"); return 0; } } int shop_busy() { if (!shopkeeper || environment(shopkeeper)!=this_object()) { notify_fail("There's no one around to help you!\n"); return 1; } if (customer) { if (environment(customer) != this_object()) { _end(); return 0; } if (customer == this_player()) { notify_fail( "The shopkeeper says: "+ "I'm already serving you, please be patient.\n"); return 1; } notify_fail( "The shopkeeper says: "+ "I'm now busy with "+customer->query("cap_name")+ ", but I'll be with you in a moment...\n"); return 1; } return 0; } int list() { mixed tmp; if (shop_busy()) return 0; write ("You ask the shopkeeper: What do you have?\n"); say(this_player()->query("cap_name")+ " asks the shopkeeper: What do you have?\n"); customer = this_player(); if (!undefinedp(tmp = _varlist["list"]) && Is_Block(tmp) ) { customer = this_player(); Push("("+this_player()->query("name")+")"); Compile("list"); return 1; } tell_room(this_object(),"The shopkeeper says: hmm... lemme see..\n"); say("The shopkeeper shows the wares to " +this_player()->query("cap_name")+".\n"); _show_goods(); customer = 0; return 1; } int _say() { mixed i; object env, ob; string out; i = Pop(); if (Is_String(i)) { out = Parse_String(i); } else if (Is_Object(i)) { ob = find_object(i[2..strlen(i)-1]); if (!ob) out = "something"; else out = (string) ob->query("short"); if (!out) out = "some weird thing"; } else if (objectp(i)) { if (!i) out = "something"; else out = (string) ob->query("short"); if (!out) out = "some weird thing"; } else if (intp(i)) { out = ""+i; } else if (pointerp(i)) { out = Restore_Array(i); } if (out) { tell_room(this_object(), "The shopkeeper says: "+out+"\n"); } return 0; } int _end() { last_customer = customer; customer = 0; _counter = 0; _stack_ptr = 0; _pending_input = 0; _stack = ({ }); remove_call_out("Compile"); } int _show_goods() { int i,n; object *ob; mixed * value; string type; int number; /* Objects in local storage */ if (!customer || environment(customer)!=this_object()) return 0; ob = all_inventory(storeroom) ; n = sizeof(ob); if (!n) tell_room(this_object(), "The shopkeeper says: we have nothing right now.\n"); for (i=0;i<n;i++) { value = ob[i]->query("value") ; if (!value) continue ; type = value[1] ; number = value[0] ; if (number==0) continue ; tell_object(customer, " "+ob[i]->query("short")+"\n"); } /* this command is too costly, must make LSC to rest for 2 seconds */ _counter = 0; return 0; } int _evaluate() { mixed obj; mixed *value; string type; int number, j; obj = Pop(); if (!objectp(obj)) { error("evaluate: not an object"); return -1; } value = obj->query("value"); /* can add randomness here ... */ type = value[1]; number = value[0]; Push(coinvalue(type) * number); } int _set_desired_buy_price() { mixed price; price = Pop(); if (!intp(price)) { error("set_desired_buy_price: not an integer"); return -1; } desired_buy_price = price; return 0; } int _set_desired_sell_price() { mixed price; price = Pop(); if (!intp(price)) { error("set_desired_sell_price: not an integer"); return -1; } desired_sell_price = price; return 0; } int _convert_amount_str() { mixed value; value = Pop(); if (!intp(value)) { error("convert_amount_str: not an integer"); return -1; } return Push(Convert_amount_string(value)); } string Convert_amount_string(int value) { int i,n,num, mod, first; string str; mixed *types; str = ""; for(i=money_type_size-1;i>=0;i--) { num = value / type_value[i]; mod = value - num * type_value[i]; value = mod; if (!num) continue; if (!value && str != "") { str += " and "; if (num==1) str+="one "+money_types[i]+" coin"; else str+=num+" "+money_types[i]+ " coins"; break; } if (str!="") str += ", "; if (num==1) str+="one "+money_types[i]+" coin"; else str+=num+" "+money_types[i]+" coins"; } return str; } string coins_string(int * coins) { int i; int tmp; tmp = 0; for(i=0;i<money_type_size;i++) tmp+=coins[i] * type_value[i]; return Convert_amount_string(tmp); } int value(string str) { object obj ; string word ; string *types ; int i, number, changenum ; string type, changetype ; mixed *value ; mixed tmp; int newval, oldval, newnum, j ; if (!str) { notify_fail("What do you want to value?\n"); return 0; } if (shop_busy()) return 0; obj = present(str,this_player()) ; if (!obj) { notify_fail ("You don't have a "+str+".\n") ; return 0 ; } value = obj->query("value") ; if (!value) { notify_fail ("The shopkeeper says: "+ "That object is not worth anything.\n") ; return 0 ; } type = value[1] ; number = value[0] ; if (number==0) { notify_fail ("The shopkeeper says: "+ "That object is not worth anything.\n") ; return 0 ; } j = member_array(type,money_types) ; if (j==-1) { write("The shopkeeper says: I don't know how to value this.\n"); return 1; } write("You ask the shopkeeper to value it for you...\n"); say(this_player()->query("cap_name")+" puts forward something and"+ " asks the shopkeeper a question.\n"); if (!undefinedp(tmp = _varlist["value"]) && Is_Block(tmp) ) { item = obj; customer = this_player(); Push("("+this_player()->query("name")+")"); Push(item); Compile("value"); return 1; } write("The shopkeeper says: "+obj->query("short") + " is worth "+ Convert_amount_string(number*type_value[j]*SHOP_SALES_FRAC)); return 1 ; } int _buy_price() { mixed * value; if (!desired_buy_price) { if (!item) { error("buy_price: no reference"); return -1; } error("buy_price: don't know buy price"); return -1; } return Push(desired_buy_price); } int _sell_price() { mixed * value; if (!desired_sell_price) { if (!item) { error("sell_price: no reference"); return -1; } error("sell_price: don't know buy price"); return -1; } return Push(desired_sell_price); } int Do_buy(object ob,int how_much,object from_whom) { int * coins, ncoins, cap, i; string tmpstr; if (ob->move(storeroom)!=MOVE_OK) { tell_room(this_object(), "The shopkeeper says: "+ "Our storeroom can't hold your stuff, sorry\n"); return -1; } /* calculate money to give customer */ coins = best_coin_combination(how_much, wealth, (mapping) from_whom->query("wealth"), (int) storeroom->query("capacity"), cap = (int) from_whom->query("capacity")); if (!coins) { if (fail_reason == NOWEALTH) { tell_room(this_object(), "The shopkeeper says: "+ "I'm running out of coins, sorry\n"); } if (fail_reason == NOCHANGE) { tell_room(this_object(), "The shopkeeper says: "+ "We don't have enough change to make "+ "the deal fair to us\n"); } ob->move(from_whom); return -1; } ncoins = 0; for(i=0;i<money_type_size;i++) { ncoins += coins[i]; if (coins[i]>=0) { give_coins[i] = coins[i]; get_coins[i] = 0; } else { give_coins[i] = 0; get_coins[i] = - coins[i]; } } from_whom->set("capacity", cap+ncoins); for(i=0;i<money_type_size;i++) { int tmp; wealth[money_types[i]] -= coins[i]; tmp = from_whom->query("wealth/"+money_types[i]); from_whom->set("wealth/"+money_types[i], tmp+coins[i]); } tell_object(from_whom,"You get "+coins_string(give_coins)); if ((tmpstr=coins_string(get_coins))=="") tell_object(from_whom,".\n"); else tell_object(from_whom," and give "+tmpstr+" as change.\n"); cost[ob] = how_much; item = 0; return 0; } int _buy_it() { mixed how_much; if (!customer || environment(customer)!=this_object()) return Push(-1); if (!shopkeeper || environment(shopkeeper) != this_object()) return Push(-1); how_much = Pop(); if (!item) { tell_object(customer, "The shopkeeper says: Where's the thing now?\n"); return Push(-1); } if (environment(item) != this_object() && environment(item)!=customer || environment(customer) != this_object() ) return Push(-1); _counter = 0; Push(Do_buy(item, how_much, customer)); } int sell(string str) { int i, j ; object ob ; mixed *value, tmp ; string type, tmpstr; int number ; if (shop_busy()) return 0; if (!str) { notify_fail("What do you want to sell?\n") ; return 0 ; } ob = present(str,this_player()) ; if (!ob) { notify_fail("You don't have one to sell!\n") ; return 0 ; } value = ob->query("value") ; if (value==0) { notify_fail ("The shopkeeper says: That has no value.\n") ; return 0 ; } number=value[0] ; if (number<1) { notify_fail ("The shopkeeper says: That has no value.\n") ; return 0 ; } type = value[1] ; j = member_array(type,money_types) ; if (j==-1) { write("The shopkeeper says: I'm not interested in this.\n"); return 1; } write("You put "+str+" forward to the shopkeeper...\n"); say(this_player()->query("cap_name")+" wants to sell something.\n"); if (!undefinedp(tmp = _varlist["sell"]) && Is_Block(tmp) && !undefinedp(tmp = _varlist["myvalue"]) && Is_Block(tmp) ) { tmpstr = "("+this_player()->query("name")+")"; if (last_customer == this_player() && item == ob) { /* the item has already been valued */ customer = this_player(); Push(tmpstr); Push(item); Compile("sell"); return 1; } customer = this_player(); item = ob; Push(tmpstr); Push(item); Push(tmpstr); Push(item); Compile("myvalue pop sell"); return 1; } Do_buy(ob, number*type_value[j]*SHOP_SALES_FRAC, this_player()); return 1 ; } int _cost() { mixed ob; int i; mixed * value; string type; int number; ob = Pop(); if (!objectp(ob)) { error("cost: not an object"); return -1; } i = cost[ob]; if (undefinedp(i) || !i) { value = ob->query("value"); if (!value) return Push(0); type = value[0]; number = value[1]; i = member_array(type,money_types); if (i=-1) return Push(0); return Push( number * type_value[i]); } return Push(i); } int ask(string str) { object ob ; string type, *types ; int i, number; mixed *value ; mixed tmp; if (!str) { notify_fail("What item's price you want to ask?\n"); return 0; } if (shop_busy()) return 0; ob = present(str, storeroom); if (!ob) { notify_fail("We don't have one of those for sale.\n"); return 0; } value = ob->query("value"); if (!value) { notify_fail ("That object should not have been in the shop.\n"); ob->remove() ; return 0 ; } type = value[1] ; number = value[0] ; if (number==0) { notify_fail ("That object should not have been in the shop.\n"); ob->remove() ; return 0 ; } write("You ask the shopkeeper about the price of "+str+"....\n"); say(this_player()->query("cap_name")+ " asks about the price of something.\n"); if (!undefinedp(tmp = _varlist["ask"]) && Is_Block(tmp) ) { item = ob; customer = this_player(); Push("("+this_player()->query("name")+")"); Push(item); Compile("ask"); return 1; } write("The shopkeeper says: "+ ob->query("short")+" costs "+ Convert_amount_string(cost[ob] * 5/4) +"\n"); return 1; } int Do_sell(object ob,int how_much,object to_whom) { int * coins, ncoins, cap, i; string tmpstr; if (ob->move(to_whom)!=MOVE_OK) { tell_room(this_object(), "The shopkeeper says: "+ "You can't carry anymore, sorry\n"); return -1; } /* calculate money to give customer */ coins = best_coin_combination(how_much, (mapping) to_whom->query("wealth"), wealth, cap = (int) to_whom->query("capacity"), (int) storeroom->query("capacity")); if (!coins) { if (fail_reason == NOWEALTH) { tell_room(this_object(), "The shopkeeper says: "+ "You don't have enough money!\n"); } if (fail_reason == NOCHANGE) { tell_room(this_object(), "The shopkeeper says: "+ "We don't have enough change to make "+ "the deal fair to us\n"); } ob->move(storeroom); return -1; } ncoins = 0; for(i=0;i<money_type_size;i++) { ncoins += coins[i]; if (coins[i]>=0) { get_coins[i] = coins[i]; give_coins[i] = 0; } else { get_coins[i] = 0; give_coins[i] = - coins[i]; } } to_whom->set("capacity", cap-ncoins); for(i=0;i<money_type_size;i++) { int tmp; wealth[money_types[i]] += coins[i]; tmp = to_whom->query("wealth/"+money_types[i]); to_whom->set("wealth/"+money_types[i], tmp-coins[i]); } tell_object(to_whom,"You pay "+coins_string(get_coins)); if ((tmpstr=coins_string(give_coins))=="") tell_object(to_whom,".\n"); else tell_object(to_whom," and get back "+tmpstr+" as change.\n"); map_delete(cost, ob); item = 0; return 0; } int _sell_it() { mixed how_much; if (!customer || environment(customer)!=this_object()) return Push(-1); if (!shopkeeper || environment(shopkeeper) != this_object()) return Push(-1); how_much = Pop(); if (!item) { tell_object(customer, "The shopkeeper says: Oops, where is it?\n"); return Push(-1); } if (environment(item) != storeroom) { tell_object(customer, "The shopkeeper says: We don't have it\n"); return Push(-1); } _counter = 0; Push(Do_sell(item, how_much, customer)); } int buy(string str) { int i, j ; object ob ; mixed *value, tmp ; string type, tmpstr; int number ; if (shop_busy()) return 0; if (!str) { notify_fail("What do you want to buy?\n") ; return 0 ; } ob = present(str,storeroom) ; if (!ob) { notify_fail("You don't see any!\n") ; return 0 ; } value = ob->query("value") ; if (value==0) { notify_fail("The shopkeeper says: That one is not for sale.\n"); return 0 ; } number=value[0] ; if (number<1) { notify_fail("The shopkeeper says: That one is not for sale.\n"); return 0 ; } type = value[1] ; write("You tell the shopkeeper that you want to buy "+str+"...\n"); say(this_player()->query("cap_name")+" wants to buy something.\n"); if (!undefinedp(tmp = _varlist["buy"]) && Is_Block(tmp) && !undefinedp(tmp = _varlist["myask"]) && Is_Block(tmp) ) { tmpstr = "("+this_player()->query("name")+")"; if (last_customer == this_player() && item == ob) { /* the item has already been valued */ customer = this_player(); Push(tmpstr); Push(item); Compile("buy"); return 1; } customer = this_player(); item = ob; Push(tmpstr); Push(item); Push(tmpstr); Push(item); Compile("myask pop buy"); return 1; } Do_sell(ob, cost[ob]*5/4, this_player()); return 1 ; } int read_lsc_file() { int i,n, tmp_wait_time, tmp_speed; string file; string * lines; file = file_name(this_object()); sscanf(file,"%s#%d",file, n); file += ".lsc"; if (file_size(file) <= 0) return 0; lines = explode(read_file(file),"\n"); n = sizeof(lines); tmp_wait_time = wait_time; tmp_speed = _speed; wait_time = 4; _speed = 1; for(i=0;i<n;i++) Compile(lines[i]); wait_time = tmp_wait_time; _speed = tmp_speed; return 1; } error(string str) { tell_room(this_object(),str+"\n"); _status = ERROR; } clean_up() { return; }