// shop.c // A basic shop with a basic storeroom. // Written by Mobydick@TMI-2, 8-25-92 #include <move.h> #include <mudlib.h> #include <money.h> inherit ROOM ; inherit COINVALUE ; object storeroom ; object ob,ob2 ; void create() { seteuid(getuid()) ; ::create() ; /* Force the warehouse to load, if it's not loaded */ CENTSTORE->frog() ; } void init() { add_action ("list", "list") ; add_action ("value", "value") ; add_action ("buy", "buy") ; add_action ("sell", "sell") ; add_action ("enter_storeroom", "storeroom") ; } int list() { int i,j,k ; string str, foo; string *shorts, *coins ; int *number, *price ; mixed *value ; object *frog ; write ("The following objects are for sale:\n") ; #define NUM_STR ({ "Zero", "One", "Two", "Three", "Four", "Five", "Six", \ "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", \ "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", \ "Eighteen", "Nineteen", "Twenty" }) frog = all_inventory(storeroom) + all_inventory(find_object_or_load(CENTSTORE)) ; shorts = ({ }) ; coins = ({ }) ; number = ({ }) ; price = ({ }) ; for (i=0;i<sizeof(frog);i++) { str = (string)frog[i]->query("short") ; if (str[0..1]=="a ") str=str[2..<1] ; if (str[0..2]=="an ") str=str[3..<1] ; if (str[0..3]=="the ") str=str[4..<1] ; value = (mixed *)frog[i]->query("value") ; if (!value || sizeof(value)!=2) continue ; j = -1 ; for (k=0;k<sizeof(shorts);k++) { if (str==shorts[k] && value[0]==price[k] && value[1]==coins[k]) { j = k ; continue ; } } if (j==-1) { shorts += ({ str }) ; number += ({ 1 }) ; value = frog[i]->query("value") ; price += ({ value[0] }) ; coins += ({ value[1] }) ; } else { number[j] += 1 ; } } for (i=0;i<sizeof(shorts);i++) { if (number[i]>20) { foo = "Many" ; } else { foo = NUM_STR[number[i]] ; } foo += " " ; if (number[i]==1) { foo += shorts[i] ; } else { foo += pluralize(shorts[i]) ; } write (sprintf(" %-42s %d %s\n",foo,price[i],coins[i])) ; } return 1 ; } int buy(string str) { int number, i, j, k, l, has, res ; string type ; object ob ; string *types ; mixed *value ; int oldval, newval, newnum, leftnum ; /* Look for the object, first in central storage then in local storage. */ if (!str) { notify_fail ("What would you like to buy?\n") ; return 0 ; } ob = present(str,find_object(CENTSTORE)) ; if (!ob) 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 ; } // Check to see if the player has exact change. i = this_player()->query("wealth/"+type) ; if (i>=number) { this_player()->set("wealth/"+type,i-number) ; j = this_player()->query("capacity") ; this_player()->set("capacity", j+number) ; // See if the player can carry the object. res = ob->move(this_player()) ; if (res==MOVE_OK) { write ("The shopkeeper hands you "+(string)ob->query("short")+".\n") ; say (this_player()->query("cap_name")+" buys a "+ob->query("short")+".\n") ; return 1 ; } else { // Give him his money back. this_player()->set("wealth/"+type,i) ; this_player()->set("capacity",j) ; write ("You cannot carry that object.\n") ; return 0 ; } } // The rest of the procedure tries to let the player use other combinations // of coins to buy the object. Clever players can use this to convert coins // for free, by buying a 50 silver coin object for 5 gold and then selling // it back for 50 silver. If you don't want this to happen, either have the // shops return less than exact value on sales, or rip out the following // code. // Check to see if this is a convertable type of coin. If it isn't then // the player cannot buy the object. types = cointypes() ; j = member_array (type,types) ; if (j==-1) { notify_fail ("You don't have "+number+" "+type+" and the shop cannot convert that to other coins.\n") ; return 0 ; } // Check to see if he has enough total wealth to buy the object. If he // doesn't we can save a lot of CPU time by giving up now. res = this_player()->total_wealth() ; k = this_player()->query("wealth/"+type) ; oldval = coinvalue(type) ; if (res<k*oldval) { write ("You can't afford that!\n") ; return 0 ; } // First check higher-valued coins, lowest to highest, to see if the player // can buy the object exactly for some combination of the higher-valued // coin and the stated coin. for (i=j-1;i>=0;i--) { newval = coinvalue(types[i]) ; newnum = (oldval*number)/newval ; leftnum = ((oldval*number)-(newnum*newval))/oldval ; // If the player has enough of the new type, he buys the item. has = this_player()->query("wealth/"+types[i]) ; if (has>=newnum && k>=leftnum) { this_player()->set("wealth/"+types[i],has-newnum) ; this_player()->set("wealth/"+type,k-leftnum) ; l = this_player()->query("capacity") ; this_player()->set("capacity",l+newnum+leftnum) ; res = ob->move(this_player()) ; if (res==MOVE_OK) { write ("The shopkeeper hands you "+(string)ob->query("short")+".\n") ; say (this_player()->query("cap_name")+" buys a "+ob->query("short")+".\n") ; return 1 ; } else { this_player()->set("wealth/"+types[i],has) ; this_player()->set("wealth/"+type,k) ; this_player()->set("capacity",l) ; write ("You cannot carry that object.\n") ; return 1 ; } } // If that didn't work, try using all the player has of the stated coin, plus // as many of the new coin type as necessary. newnum = (oldval*(number-k))/newval ; if ((oldval*number)-(newval*newnum)>(k*oldval)) newnum = newnum + 1 ; leftnum = ((oldval*number)-(newnum*newval))/oldval ; has = this_player()->query("wealth/"+types[i]) ; if (has>=newnum && k>=leftnum) { this_player()->set ("wealth/"+types[i],has-newnum) ; this_player()->set("wealth/"+type,k-leftnum) ; l = this_player()->query("capacity") ; this_player()->set("capacity",l+newnum+leftnum) ; res = ob->move(this_player()) ; if (res==MOVE_OK) { write ("The shopkeeper hands you "+(string)ob->query("short")+".\n") ; say (this_player()->query("cap_name")+" buys a "+ob->query("short")+".\n") ; return 1 ; } else { this_player()->set("wealth/"+types[i],has) ; this_player()->set("wealth/"+type,k) ; this_player()->set("capacity",l) ; write ("You cannot carry that object.\n") ; return 1 ; } } // If neither of those two worked, give up and try the next coin type. } // Higher types didn't work. Now we try all less valuable coin types and // see if we can buy it for all he possesses of the stated type plus some // of the lower-valued type. for (i=j+1;i<sizeof(types);i++) { newval = coinvalue(types[i]) ; newnum = ((number-k)*oldval)/newval ; has = this_player()->query("wealth/"+types[i]) ; if (has>=newnum) { this_player()->set("wealth/"+types[i],has-newnum) ; this_player()->set("wealth/"+type,0) ; l = this_player()->query("capacity") ; this_player()->set("capacity",l+k+newnum) ; res = ob->move(this_player()) ; if (res==MOVE_OK) { write ("The shopkeeper hands you "+(string)ob->query("short")+".\n") ; say (this_player()->query("cap_name")+" buys a "+ob->query("short")+".\n") ; return 1 ; } else { this_player()->set("wealth/"+types[i],has) ; this_player()->set("wealth/"+type,k) ; this_player()->set("capacity",l) ; write ("You cannot carry that object.\n") ; return 1 ; } } // If that didn't work, try the next coin type. } // OK, it looks like he can't buy it for exact change. Now, try all the // higher types again, this time giving whatever change is necessary. for (i=j-1;i>=0;i--) { newval = coinvalue(types[i]) ; newnum = (oldval*number)/newval ; if ((oldval*number)-(newval*newnum)>0) newnum = newnum + 1 ; leftnum = ((newval*newnum)-(oldval*number))/oldval ; has = this_player()->query("wealth/"+types[i]) ; if (has>=newnum) { this_player()->set("wealth/"+types[i],has-newnum) ; this_player()->set("wealth/"+type,k+leftnum) ; l = this_player()->query("capacity") ; this_player()->set("capacity",l+newnum-leftnum) ; res = ob->move(this_player()) ; if (res==MOVE_OK) { write ("The shopkeeper hands you "+(string)ob->query("short")+".\n") ; say (this_player()->query("cap_name")+" buys a "+ob->query("short")+".\n") ; return 1 ; } else { this_player()->set("wealth/"+types[i],has) ; this_player()->set("wealth/"+type,k) ; this_player()->set("capacity",l) ; write ("You cannot carry that object.\n") ; return 1 ; } } // That didn't work, so try the next type. } // That's enough of this. We give up: he can't buy it from us. notify_fail ("You don't have the right coins to buy that.\n") ; return 0 ; } int value(string str) { object *ob, obj ; string word ; string *types ; int i, number, changenum ; string type, changetype ; mixed *value ; int newval, oldval, newnum, j ; if (!str) { ob = all_inventory(this_player()) ; if (!ob) { notify_fail("You don't have any belongings.\n") ; return 0 ; } for (i=0;i<sizeof(ob);i++) { value = ob[i]->query("value") ; if (!value) { write (ob[i]->query("short")+" has no value.\n") ; continue ; } type = value[1] ; number=value[0] ; if (number==0) { write (ob[i]->query("short")+" has no value.\n") ; continue ; } // If it's a coin we can make change for, we multiply by the sales fraction // and calculate how much to give back of the stated coin and the lower- // valued coin. If it's the lowest value coin, or if it's not list with // coinvalue.c, we just take off the fraction and sell for that. types = cointypes() ; j = member_array(type,types) ; if (j==-1 || j==sizeof(types)-1) { newnum = number*SHOP_SALES_FRAC ; if (newnum==0) newnum=1 ; if (newnum==1) word="coin" ; else word="coins" ; write (ob[i]->query("short")+" is worth "+newnum+" "+type+" "+word+".\n") ; } else { changetype = types[j+1] ; oldval = coinvalue(type) ; newval = coinvalue(changetype) ; newnum = (number*oldval*SHOP_SALES_FRAC)/oldval; changenum = ((number*oldval*SHOP_SALES_FRAC)-(newnum*oldval))/newval ; if (newnum==1) word="coin" ; else word="coins" ; write (ob[i]->query("short")+" is worth "+newnum+" "+type+" "+word) ; if (changenum==0) { write (".\n") ; } else { if (changenum==1) word="coin" ; else word="coins" ; write (" and "+changenum+" "+changetype+" "+word+".\n") ; } } } return 1 ; } 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 ("That object is not worth anything.\n") ; return 0 ; } type = value[1] ; number = value[0] ; if (number==0) { notify_fail ("That object is not worth anything.\n") ; return 0 ; } types = cointypes() ; j = member_array(type,types) ; if (j==-1 || j==sizeof(types)-1) { newnum = number*SHOP_SALES_FRAC ; if (newnum==0) newnum=1 ; if (newnum==1) word="coin" ; else word="coins" ; write (obj->query("short")+" is worth "+newnum+" "+type+" "+word+".\n") ; } else { changetype = types[j+1] ; oldval = coinvalue(type) ; newval = coinvalue(changetype) ; newnum = (number*oldval*SHOP_SALES_FRAC)/oldval; changenum = ((number*oldval*SHOP_SALES_FRAC)-(newnum*oldval))/newval ; if (newnum==1) word="coin" ; else word="coins" ; write (obj->query("short")+" is worth "+newnum+" "+type+" "+word) ; if (changenum==0) { write (".\n") ; } else { if (changenum==1) word="coin" ; else word="coins" ; write (" and "+changenum+" "+changetype+" "+word+".\n") ; } } return 1 ; } int sell(string str) { int i, j, l, res ; int newnum, changenum ; int oldval, newval ; string changetype ; object ob ; string *types ; object *obs ; string type, word ; mixed *value ; int number ; if (!str) { notify_fail("What do you want to sell?\n") ; return 0 ; } if (str=="all") { obs = all_inventory(this_player()) ; for (i=0;i<sizeof(obs);i++) { value = obs[i]->query("value") ; if (!value) continue ; number = value[0] ; if (number<1) continue ; if (obs[i]->query("prevent_drop")) continue ; res=(int)obs[i]->move(storeroom) ; if (res!=MOVE_OK) continue ; type = value[1] ; res=(int)this_player()->query("wealth/"+type) ; types = cointypes() ; j = member_array(type,types) ; if (j==-1 || j==sizeof(types)-1) { newnum = number*SHOP_SALES_FRAC ; if (newnum==0) newnum=1 ; l = this_player()->query("capacity") ; if (l<newnum) { write ("You can't carry the coins from selling "+obs[i]->query("short")+".\n") ; obs[i]->move(this_player()) ; continue ; } if (newnum==1) word="coin" ; else word="coins" ; write ("You sell "+obs[i]->query("short")+" for "+newnum+" "+type+" "+word+".\n") ; say (this_player()->query("cap_name")+" sells a "+obs[i]->query("short")+".\n") ; this_player()->set("wealth/"+type,newnum+res) ; this_player()->set("capacity",l-newnum) ; } else { changetype = types[j+1] ; oldval = coinvalue(type) ; newval = coinvalue(changetype) ; newnum = (number*oldval*SHOP_SALES_FRAC)/oldval; changenum = ((number*oldval*SHOP_SALES_FRAC)-(newnum*oldval))/newval ; l = this_player()->query("capacity") ; if (l<newnum+changenum) { write ("You can't carry the coins from selling "+obs[i]->query("short")+".\n") ; obs[i]->move(this_player()) ; continue ; } if (newnum==1) word="coin" ; else word="coins" ; write ("You sell "+obs[i]->query("short")+" for "+newnum+" "+type+" "+word) ; say (this_player()->query("cap_name")+" sells a "+obs[i]->query("short")+".\n") ; this_player()->set("wealth/"+type,newnum+res) ; if (changenum==0) { write (".\n") ; } else { if (changenum==1) word="coin" ; else word="coins" ; write (" and "+changenum+" "+changetype+" "+word+".\n") ; res = this_player()->query("wealth/"+changetype) ; this_player()->set("wealth/"+changetype,res+changenum) ; } this_player()->set("capacity",l-newnum-changenum) ; } } return 1 ; } 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 ("That has no value.\n") ; return 0 ; } number=value[0] ; if (number<1) { notify_fail ("That has no value.\n") ; return 0 ; } if (ob->query("prevent_drop")) { notify_fail ("You cannot bear to part with it.\n") ; return 0 ; } type = value[1] ; res = ob->move(storeroom) ; if (res != MOVE_OK) { notify_fail ("You can't sell that!\n") ; return 0 ; } res = (int)this_player()->query("wealth/"+type) ; if (number==0) { notify_fail ("That object is not worth anything.\n") ; return 0 ; } types = cointypes() ; j = member_array(type,types) ; if (j==-1 || j==sizeof(types)-1) { newnum = number*SHOP_SALES_FRAC ; if (newnum==0) newnum=1 ; l = this_player()->query("capacity") ; if (l<newnum) { write ("You can't carry the coins you'd get for selling "+ob->query("short")+".\n") ; ob->move(this_player()) ; return 1 ; } if (newnum==1) word="coin" ; else word="coins" ; write ("You sell "+ob->query("short")+" for "+newnum+" "+type+" "+word+".\n") ; say (this_player()->query("cap_name")+" sells a "+ob->query("short")+".\n") ; this_player()->set("wealth/"+type,newnum+res) ; this_player()->set("capacity",l-newnum) ; } else { changetype = types[j+1] ; oldval = coinvalue(type) ; newval = coinvalue(changetype) ; newnum = (number*oldval*SHOP_SALES_FRAC)/oldval; changenum = ((number*oldval*SHOP_SALES_FRAC)-(newnum*oldval))/newval ; l = this_player()->query("capacity") ; if (l<newnum+changenum) { write ("You can't carry the coins you'd get for selling "+ob->query("short")+".\n") ; ob->move(this_player()) ; return 1 ; } if (newnum==1) word="coin" ; else word="coins" ; write ("You sell "+ob->query("short")+" for "+newnum+" "+type+" "+word) ; say (this_player()->query("cap_name")+" sells a "+ob->query("short")+".\n") ; this_player()->set("wealth/"+type,newnum+res) ; if (changenum==0) { write (".\n") ; } else { if (changenum==1) word="coin" ; else word="coins" ; write (" and "+changenum+" "+changetype+" "+word+".\n"); res = this_player()->query("wealth/"+changetype) ; this_player()->set("wealth/"+changetype,changenum+res) ; } this_player()->set("capacity",l-newnum-changenum) ; } return 1 ; } int enter_storeroom() { if (!wizardp(this_player())) return 0 ; this_player()->move(storeroom) ; return 1 ; }