/* * MusicMUD - Banks and Shops module * Copyright (C) 1998-2003 Abigail Brady * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "musicmud.h" #include "misc.h" #include "pflags.h" #include "verbs.h" #include "util.h" #include "trap.h" #include "events.h" #include "units.h" #include "match.h" #include "emsg.h" #include "href.h" #include "nations.h" #define MODULE "trade" static int strfind(int argc, const char **argv, const char *what) { for (int i = 0 ; i < argc ; i++) { if (streq(argv[i], what)) return i; } return 0; } static void tell(MudObject *what, MudObject *who, const string &txt) { who->interpretf("tell %s %s", what->id, txt.c_str()); } struct shopinfo_t { NewWorld choices; MudObject *room; MudObject *list; MudObject *mob; Nation *cur; shopinfo_t(MudObject *where, MudObject *who, int *argc, const char **argv, const char *prep="from") { MudObject *tomatch=0; cur = 0; if (argc && argv) { TeleWorld m1 = multi_match(who, (*argc)-1, argv+1, 0, LOOK_ROOM, 0, 0, prep); int had = 0; if (m1.prep) { *argc = (strfind((*argc)-1, argv+1, prep))+1; argv[*argc] = 0; had = 1; } if (m1.txt == "here") { list = room = where; mob = where->get_object("shopmob"); choices.add(mob); return; } if (had && m1.getsize()==1) { tomatch = m1.get_nth(0); } else if (had) { throw emsg("Trade with who?"); } } room = 0; list = 0; mob = 0; int i; MudObject *o; foreach(where->children, o, i) { if (o->get_flag(FL_STORE) && !o->get_flag(FL_ROOM) && (!tomatch || tomatch==o)) { list = mob = o; choices.add(mob); } } if ((!list || where->get_flag(FL_STORE)) && (!tomatch || tomatch==where->get_object("shopmob"))) { list = room = where; mob = where->get_object("shopmob"); choices.add(mob); } if (choices.getsize()>1) { if (argc) throw emsg(ssprintf("Trade with who? Choices are %W. (You need to use '%s'.)", &choices, prep)); else throw emsg(ssprintf("Trade with who? Choices are %W.", &choices)); } if (choices.getsize()==0) { throw emsg(ssprintf("There is nobody to trade with here.")); } if (choices.getsize()==1 && mob) { if ((mob->wander_time > 10)) mob->wander_time = now + mob->get_int("wander.time", 8)+30; } cur = currency(mob); } }; static bool names_match(MudObject *what, const char *str, int cat_only=0, int namesonly=0); static MudObject *empty_of(MudObject *of) { MudObject *o = of->get_object("empty"); if (o) return o; return of; } enum svt_t { SVT_TABLE, SVT_SIZE, SVT_VALUE, }; void rec_carry(NewWorld &w, MudObject *o) { w.add(*o); MudObject *p; int i; foreach(o->children, p, i) { w.add(p); } } struct name_data { string name; int value; int mass; name_data(const name_data &d) : name(d.name), value(d.value), mass(d.mass) { } name_data(const string &s, int v, int m) : name(s), value(v), mass(m) { } bool operator<(const name_data &w) const { if (name < w.name) return 1; if (name > w.name) return 0; if (value < w.value) return 1; if (value > w.value) return 0; if (mass < w.mass) return 1; if (mass > w.mass) return 0; return 0; } }; static void sizevaltable(MudObject *who, NewWorld &of, svt_t type, int yescash) { int total = 0, i, stotal = 0; int h = 0; MudObject *o; NewWorld what; foreach((&of), o, i) rec_carry(what, o); map<name_data, int> m; foreach((&what), o, i) { if (!name(o)) continue; name_data w(name(o), object_value(o)+totalcash(o), mass_in_grams(o, 0)); total += w.value; stotal += w.mass; if (m.find(w)!=m.end()) { m[w]++; } else { m[w] = 1; } } Nation *cur = currency(who); if (type==SVT_TABLE) { map<name_data, int>::iterator it = m.begin(); while (it != m.end()) { if (!(h++)) who->printf("%s\n\n", title_for("Size / Value", who).c_str()); who->printf("%30s * %2i - %10s %12s\n", it->first.name.c_str(), it->second, formatvalue(it->first.value, cur, 0), format_grams(it->first.mass, who)); it++; } } long long capacity = mass_capacity_in_grams(who); if (type==SVT_TABLE) { if (h) { if (h>1) { who->printf("\n %30s - %10s %12s (max %s) \n\n", "total", formatvalue(total, cur, 0), format_grams(stotal, who), format_grams(capacity, who)); } else { who->printf("\n"); } if (yescash) if (int amt=totalcash(who)) { who->printf(" %30s - %10s\n\n", "cash", formatvalue(amt, cur, 0)); } who->printf("%s\n", footer_for(who).c_str()); } else { if (yescash) { if (totalcash(who)) { who->printf("You have %s in cash, but no possessions worth anything.\n", formatvalue(totalcash(who), cur, 1)); } else { who->printf("You have nothing of value in your possession.\n"); } } } return; } if (type==SVT_SIZE) { string blah; if (stotal) { blah = ssprintf("You are carrying %s", format_grams(stotal, who).c_str()); } else { blah = "You are carrying nothing"; } if (capacity == SIZE_INFINITE) blah += " (you can carry anything)"; else blah += ssprintf(" (you can carry %s)", format_grams(capacity, who).c_str()); who->printf("%s\n", blah.c_str()); return; } if (type==SVT_VALUE) { if (!total && !totalcash(who)) { who->printf("You have nothing of value.\n"); } else if (total && !totalcash(who)) { who->printf("You have possessions worth %K.\n", total); } else if (!total && totalcash(who)) { who->printf("You have %K in cash.\n", totalcash(who)); } else if (total && totalcash(who)) { who->printf("You have %K in cash, and other possessions worth %K.\n", totalcash(who), total); } return; } return; } static bool verb_value(MudObject *who, int argc, const char *argv[]) { NewWorld w; Divert d(who, "value"); MudObject *o; int i; foreach((who->children), o, i) { w.add(*o); } if (argc==2 && streq(argv[1], "all")) { sizevaltable(who, w, SVT_TABLE, 1); } else if (argc==1) { sizevaltable(who, w, SVT_VALUE, 1); } else { w = match(who, argc-1, argv+1, 0, LOOK_BOTH|IGNORE_EXITS|ALLOW_ME); if (w.getsize()==0) { who->printf("Value what?\n"); return true; } if (w.getsize()==1) { MudObject *what = w.get_nth(0); int value = object_value(what) + totalcash(what); who->printf("%#P %[is/are] worth %K.\n", what, value); return true; } sizevaltable(who, w, SVT_TABLE, 0); } return true; } static bool verb_size(MudObject *player, int argc, const char *argv[]) { NewWorld w; Divert d(player, "size"); MudObject *o; int i; foreach((player->children), o, i) { w.add(*o); } if (argc < 2) { sizevaltable(player, w, SVT_SIZE, 1); return true; } if (argc==2 && streq(argv[1], "all")) { sizevaltable(player, w, SVT_TABLE, 0); return true; } w = match(player, argc-1, argv+1, 0, LOOK_BOTH|IGNORE_EXITS|ALLOW_ME); MudObject *what = 0; if (w.getsize()==1) { what = w.get_nth(0); } else if (w.getsize()) { sizevaltable(player, w, SVT_TABLE, 0); return true; } if (!what) { player->printf("Size what?\n", argv[1]); return true; } int m = mass_in_grams(what, 0); if (m || !what->get_flag(FL_FIXED)) { player->printf("%#Y %[masses/mass] %s.\n", what, format_grams(mass_in_grams(what, 0),player).c_str()); } else if (what->get_flag(FL_FIXED)) { player->printf("%#Y %[seems/seem] immovable.\n", what); } if (int w=mass_used_in_grams(what)) { player->printf("%#Y %[contains/contain] %s.\n", what, format_grams(w, player).c_str()); } MudObject *can = empty_of(what); if (!can) can = what; int volume = can->get_int("volume", -1); if (volume>0) { if (!(volume%PINT)) player->printf("It can contain %,i mL (%i pint%s).\n", volume, volume/PINT, volume==PINT?"":"s"); else if (volume>10000) player->printf("It can contain %,i L.\n", volume/1000); else player->printf("It can contain %,i mL.\n", volume); return true; } if (is_container(what) || (mass_capacity_in_grams(what, 0))) { int capacity = mass_capacity_in_grams(what); if (capacity != SIZE_INFINITE) player->printf("Its maximum load is %s.\n", format_grams(capacity, player).c_str()); else player->printf("Its has no maximum load.\n"); } return true; } static int cost_atfor(MudObject *object, MudObject *shop, MudObject *who) { int ripoff = shop->get_int("shop.ripoff", 100); int cost = object->get_int("cost", 0); int levelcost = object->get_int("levelcost", 0); levelcost *= privs_of(who); cost += levelcost; return cost * ripoff / 100; } static int value_at(MudObject *object, MudObject *shop, MudObject *who) { double v = cost_atfor(object, shop, who); if (object->get_int("value")!=-1) { v = object->get_int("value"); double so = shop->get_int("shop.offer", 90) / 90.0; v *= so; return (int)v; } // return (v * shop->get_int("shop.offer", 90))/100; double so = shop->get_int("shop.offer", 90)/100.0; v *= so; return (int)v; } static const char *remove_prefixes(const char *a) { const char *bnam = a; if (strncmp(bnam, "a pair of", 9)==0) { bnam+=10; } if (strncmp(bnam, "a ", 2)==0) { bnam+=2; } if (strncmp(bnam, "an ", 3)==0) { bnam+=3; } if (strncmp(bnam, "some ", 5)==0) { bnam+=5; } return bnam; } static void output_item(int idx, MudObject *b, int stok, int cost, MudObject *player, MudObject *keeper, MudObject *cur) { const char *bnam = "[Eh]"; const char *nm = b->get("long"); if (!nm) nm = b->get("name"); if (nm) { bnam = remove_prefixes(nm); } const char *done = " "; if (b->get_int("holoid", -1) != -1) { int hid = b->get_int("holoid", -1); int hpt = player->get_int("holodone", 0); if (hpt & (1 << hid)) { done = "^Y*^n"; } } if (MudObject*qu=b->get_object("quest")) { if (quest_done(player, qu)) { done = "^Y*^n"; } } string sstr= player_knows(player, 0x221e)?"^#x221e;":"many"; if (stok>0) { if (stok>99) sstr = "lots"; else sstr = ssprintf("%i", stok); } if (stok == 0) { sstr = "none"; } string coststr = "^Yfree^n"; if (cost) coststr = formatcash(convertcash(cost, NULL, cur), cur, 0); string link = ssprintf("\3send href='buy %i from %s|preview %i from %s'\4%s\3/send\4", idx, genref(player, keeper).c_str(), idx, genref(player, keeper).c_str(), bnam); player->printf("^C^| ^w%2i ^C^|^n %-31s %s ^C^|^n %9s ^C^|^n %5s ^C^|^n\n" , idx, link.c_str(), done, coststr.c_str(), sstr.c_str()); } static bool verb_list(MudObject *player, int argc, const char **argv) { if (player->owner->get("dock") && argc==1) { player->interpret("dslist"); return true; } int i = player->owner->get_int("floor.count"); if (i>=1) { for (int j=0;j<=i;j++) { char name[100]; sprintf(name, "floor.%i", j); MudObject *floor = planet->get(player->owner->get(name)); if (floor) player->printf("%i : %s\n", j, floor->get("name")); } return true; } shopinfo_t shop(player->owner, player, &argc, argv); if (!shop.list || !shop.list->get_flag(FL_STORE)) { player->printf("There is no list here.\n"); return true; } if(!shop.mob || (shop.room && shop.mob->owner != shop.room)) { player->printf("The proprietor seems to be missing.\n"); return true; } int items = shop.list->array_size("shop"); if (!items && shop.list->get_int("$shop.count",0)==0) { player->printf("%#P %[has/have] nothing to sell.\n", shop.mob); return true; } Divert d(player, "list"); player->spec_printf("^C^/^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^\\\n"); player->spec_printf("^C^| ^RProprietor^r: ^g%-46M ^C^|\n", shop.mob); player->spec_printf("^C^>^-^-^-^-^!^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^!^-^-^-^-^-^-^-^-^-^-^-^!^-^-^-^-^-^-^-^<\n"); player->spec_printf("^C^| ^GID ^C^| ^G%-33s ^C^| ^GCost ^C^| ^GStock ^C^|\n", argc==1?"Item Name":ssprintf("Items Matching '%s'", argv[1]).c_str()); player->spec_printf("^C^>^-^-^-^-^+^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^+^-^-^-^-^-^-^-^-^-^-^-^+^-^-^-^-^-^-^-^<\n"); int need = 0; int matched = 0; int forsale = 0; for (int i=0;i<items;i++) { MudObject *b = shop.list->array_get_object("shop", i); if (!b) continue; if (dotrap(E_ONWONTSELLTO, player, b, shop.mob)) { /* ::: wont_sellto o1==object, o2==shopkeeper; return 1 to not be allowed to sell. Invoked by list. Must be silent. */ continue; } int cost = cost_atfor(b, shop.list, player); int stok = shop.list->array_get_int("$stock", i); if (argc==1 || names_match(b, argv[1], 0, 0)) { output_item(i+1, b, stok, cost, player, shop.mob, shop.cur); matched++; need = 1; } forsale++; } int idx = items; items = shop.list->array_size("$shop"); for (int i=0;i<items;i++) { MudObject *b = shop.list->array_get_object("$shop", i); idx++; if (!b) continue; if (dotrap(E_ONWONTSELLTO, player, b, shop.mob)) continue; int cost = cost_atfor(b, shop.list, player); int stok = shop.list->array_get_int("$shop", i, "stock"); if (!stok) continue; if (argc==1 || names_match(b, argv[1], 0, 0)) { if (need) { player->printf("^C^>^-^-^-^-^+^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^+^-^-^-^-^-^-^-^-^-^-^-^+^-^-^-^-^-^-^-^<\n"); need = 0; } output_item(idx, b, stok, cost, player, shop.mob, shop.cur); matched++; } forsale++; } if (matched == 0) { player->cancel_printf(); if (forsale) player->printf("No items for sale matching '%s'.\n", argv[1]); else player->printf("%#P %[doesn't/don't] seem to have anything for sale.\n", shop.mob); return true; } player->printf("^C^[^-^-^-^-^~^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^~^-^-^-^-^-^-^-^-^-^-^-^~^-^-^-^-^-^-^-^]^n\n"); if (player->owner->get("shop.note")) { player->printf("%s\n", player->owner->get("shop.note")); } return true; } static bool stocks(MudObject *store, MudObject *item) { if (item->get_object("cloneof")) item = item->get_object("cloneof"); int items = store->array_size("shop"); for (int i=0;i<items;i++) { MudObject *o = store->array_get_object("shop", i); if (o==item) { return 1; } } return 0; } static bool offer_stocks(MudObject *store, MudObject *item) { if (!item->get_object("cloneof")) return true; item = item->get_object("cloneof"); if (item->id[0]=='@') return true; int items = store->array_size("shop"); for (int i=0;i<items;i++) { MudObject *o = store->array_get_object("shop", i); if (o==item) { if (store->array_get_int("$stock", i)!=-1) { store->array_set("$stock", i, store->array_get_int("$stock", i)+1); } return true; } } items = store->array_size("$shop"); for (int i=0;i<items;i++) { MudObject *o = store->array_get_object("$shop", i); if (o==item) { store->array_set("$shop", i, "stock", store->array_get_int("$shop", i, "stock")+1); return true; } } store->array_set("$shop", items, item); store->array_set("$shop", items, "stock", 1); store->set("$shop.count", items+1); return false; } static bool verb_sell(MudObject *player, int argc, const char **argv) { shopinfo_t shop(player->owner, player, &argc, argv, "to"); if (!shop.list || !shop.list->get_flag(FL_STORE)) { player->printf("There is no list here.\n"); return true; } if(!shop.mob || (shop.room && shop.mob->owner != shop.room)) { player->printf("The proprietor seems to be missing.\n"); return true; } if (argc < 2) { player->printf("Sell what?\n"); return true; } MudObject *keeper = shop.mob; NewWorld tosell = match(player, argc-1, argv+1, 0, LOOK_INV|IGNORE_EXITS|ALLOW_ME); MudObject *o; int i; foreach((&tosell), o, i) if (is_player(o) || is_mobile(o)) { tell(player, keeper, lang("What do you take me for, a slave trader?", keeper)); return true; } if (!keeper->get_flag(FL_CONTRABAND)) foreach((&tosell), o, i) if (o->get_flag(FL_CONTRABAND)) { tell(player, keeper, lang("I'm not some kind of smuggler, you know!", keeper)); return true; } if (tosell.getsize()==0) { player->printf("Sell what?\n"); return true; } MudObject *sell=0; NewWorld willbuy; int total=0; foreach((&tosell), sell, i) { int value = value_at(sell, shop.list, player); int dontwant = 0; if (sell->get_int("food")!=-1) dontwant = 2; if (sell->get_int("alcohol")!=-1) dontwant = 2; if (const char *s =shop.list->get("stype")) { if (streq(s, "none")) { dontwant = 3; } if (!names_match(sell, s, 1)) { if (dontwant != 2) dontwant = 1; } } else { if (dontwant != 2) dontwant = !stocks(shop.list, sell); } sell->set("!donstock", 0); sell->unset("!offered"); if (dotrap(E_ONSELL, player, keeper, sell) || dotrap(E_BEFORESOLD, player, sell, keeper)) { /* ::: sell o1==shopkeeper, o2==object offered; if the shopkeeper wants the item, return 1 and set !offered on o2 to the offer, if he doesn't want it, just return 1 */ /* ::: before_sold o1==object, o2==shopkeeper; if the shopkeeper wants the item, return 1 and set !offered on o2 to the offer, if he doesn't want it, just return 1 */ value = sell->get_int("!offered", 0); if (value) { dontwant = 0; } else { dontwant = 1; } } sell->unset("!offered"); if (value && !dontwant) { willbuy.add(*sell); total += value; } } if (willbuy.getsize()==0) { if (tosell.getsize()==1) tell(player, keeper, lang("Sorry, I don't want that.", keeper)); else tell(player, keeper, lang("Sorry, I don't want anything from that.", keeper)); return true; } keeper->printf("%#M %[gives/give] %W to you.\n", player, &willbuy); keeper->oprintf(cansee && avoid(player), "%#M %[gives/give] %W to %M.\n", player, &willbuy, keeper); player->printf("You give %W to %M.\n", &willbuy, keeper); tell(player, keeper, ssprintf(lang("Thank you. Here is %s. " "Have a nice day.", keeper), formatvalue(total, shop.cur, 1)).c_str()); set_cash(player, shop.cur, cash(player, shop.cur) + convertcash(total, NULL, shop.cur)); foreach((&willbuy), sell, i) { if (dotrap(E_AFTERSOLD, player, sell, keeper)) { /* ::: after_sold o1==object, o2==keeper; return 1 to NOT VANISH the object and not stock the shop with this object */ continue; } vanish(sell); if (sell->get_int("!dontstock", 0)==0) { offer_stocks(shop.list, sell); } } return true; } struct stockitem { const char *error; MudObject *shop; const char *aname; int idx; int atonce; stockitem() { error = 0; shop = 0; aname = 0; idx = 0; atonce = 0; } stockitem(const char *why) { error = why; shop = 0; aname = 0; idx = 0; atonce = 0; } int stock() { if (streq(aname, "$shop")) { return shop->array_get_int(aname, idx, "stock"); } else if (streq(aname, "shop")) { return shop->array_get_int("$stock", idx); } return -1; } MudObject *obj() { return shop->array_get_object(aname, idx); } void destock(MudObject *who, int by) { if (stock() != -1) { if (streq(aname, "shop")) { shop->array_set("$stock", idx, stock()-by); } else { shop->array_set(aname, idx, "stock", stock()-by); } } if (streq(aname, "$shop")) { if (stock()==0) { int i=0; for (i=idx+1;i<shop->array_size(aname);i++) { shop->array_set(aname, i-1, shop->array_get_object(aname, i)); shop->array_set(aname, i-1, "stock", shop->array_get_int(aname, i, "stock")); } shop->set("$shop.count", shop->array_size("$shop")-1); char foo[100]; sprintf(foo, "$shop.%i", shop->get_int("$shop.count")); shop->unset(foo); sprintf(foo, "$shop.%i.stock", shop->get_int("$shop.count")); shop->unset(foo); } } } }; const stockitem naughtforsale = stockitem("The shop has nothing to sell."); const stockitem notthatmany = stockitem("The shop doesn't have that many items to sell."); const stockitem notstock = stockitem("The shop doesn't have that in stock."); static stockitem find_stock(MudObject *shop, int idx); static stockitem find_stock(MudObject *store, MudObject *what) { int i; for (i=0;i<store->array_size("shop");i++) { MudObject *w = store->array_get_object("shop", i); if (w == what) { stockitem si; si.shop = store; si.aname = "shop"; si.idx = i; si.atonce = store->array_get_int("shop", i, "atonce", 0); if (w->get_flag(FL_ONEPERSON)) { si.atonce = 1; } return si; } } for (i=0;i<store->array_size("$shop");i++) { MudObject *w = store->array_get_object("$shop", i); if (w == what) { stockitem si; si.shop = store; si.aname = "$shop"; si.idx = i; si.atonce = 0; return si; } } return notstock; } static stockitem find_stock(MudObject *shop, int idx) { if (shop->array_size("shop")<1 && shop->array_size("$shop")<1) return naughtforsale; if (idx < shop->array_size("shop")) { MudObject *b = shop->array_get_object("shop", idx); if (b) { stockitem i; i.shop = shop; i.aname = "shop"; i.idx = idx; i.atonce = shop->array_get_int("shop", idx, "atonce", 0); if (b->get_flag(FL_ONEPERSON)) { i.atonce = 1; } return i; } } idx -= shop->array_size("shop"); if (idx < shop->array_size("$shop")) { MudObject *b = shop->array_get_object("$shop", idx); if (b) { stockitem i; i.shop = shop; i.aname = "$shop"; i.idx = idx; return i; } } return notthatmany; } static bool verb_preview(MudObject *player, int argc, const char **argv) { shopinfo_t shop(player->owner, player, &argc, argv); if (!shop.list || !shop.list->get_flag(FL_STORE)) { player->printf("There is no list here.\n"); return true; } if(!shop.mob || (shop.room && shop.mob->owner != shop.room)) { player->printf("The proprietor seems to be missing.\n"); return true; } if (shop.mob->get_flag(FL_STUNNED)) { player->printf("%#M has been stunned.\n"); return true; } if (argc < 2) { player->printf("Syntax : preview <what>.\n"); return true; } int which = 1; char *w=0; int item = strtol(argv[which], &w, 10); stockitem si; MudObject *b = 0; if (*w) { NewWorld wot; for (int i=0;i<shop.list->array_size("shop");i++) { MudObject *s = shop.list->array_get_object("shop", i); if (s) wot.add(*s); } for (int i=0;i<shop.list->array_size("$shop");i++) { MudObject *s = shop.list->array_get_object("$shop", i); if (s) wot.add(*s); } NewWorld wh = match(player, argc-which, argv+which, 0, LOOK_SPECIAL, 0, &wot); if (wh.getsize()) { b = wh.get_nth(0); } if (wh.getsize()>1) { player->printf("You can only preview one thing at a time.\n"); return true; } if (!b) { si = notstock; } if (si.error) { player->printf("%s\n", si.error); return true; } si = find_stock(shop.list, b); } else { if (!b) { item--; if (item < 0) { player->printf("Items only have ID numbers greater than one\n"); return true; } si = find_stock(shop.list, item); if (si.error) { player->printf("%s\n", si.error); return true; } b = si.obj(); } } if (!b) { player->printf("Cannot find that item to buy...\n"); return true; } player->printf("%#M %[shows/show] %M to you.\n", shop.mob, b); player->printf("%s\n", b->get("desc")); string ab = abilities(b); if (ab.length()) { if (ab[ab.length()-1]==',') ab.erase(ab.length()-1); player->printf("^WNotes: %s.^n\n", ab.c_str()); } return true; } static bool verb_buy(MudObject *player, int argc, const char **argv) { shopinfo_t shop(player->owner, player, &argc, argv); if (argc < 2) { player->printf("Buy what?\n"); return true; } int howmany = 1, all = 0; int which = 1; if (argc > 2) { char *w; if (streq(argv[which], "all")) { howmany = 1; all = 1; which = 2; } else { howmany = strtol(argv[which], &w, 10); if (!*w) { which = 2; } else { howmany = decardinator(argv[which]); if (howmany==-1) { howmany = 1; } else { which = 2; } } } } if (howmany < 0) { player->printf("Use sell to sell items.\n"); return true; } if (howmany == 0) { player->printf("You buy nothing.\n"); return true; } MudObject *mfor = player; if (strhas(argc, argv, "for")) { TeleWorld wfor = multi_match(player, argc, argv, 0, LOOK_ROOM, 0, 0, "for"); if (wfor.getsize()==0) { player->printf("Buy for who?\n"); return true; } if (wfor.getsize()>1) { player->printf("You can only buy for one person at a time.\n"); return true; } mfor = wfor.get_nth(0); if (!is_player(mfor)) { player->printf("You can only buy for players.\n"); return true; } } if (!shop.list || !shop.list->get_flag(FL_STORE)) { player->printf("There is no list here.\n"); return true; } if(!shop.mob || (shop.room && shop.mob->owner != shop.room)) { player->printf("The proprietor seems to be missing.\n"); return true; } if (shop.mob->get_flag(FL_STUNNED)) { player->printf("%#M has been stunned.\n"); return true; } char *w; int item = strtol(argv[which], &w, 10); stockitem si; MudObject *b = 0; if (*w) { NewWorld wot; for (int i=0;i<shop.list->array_size("shop");i++) { MudObject *s = shop.list->array_get_object("shop", i); if (s) wot.add(*s); } for (int i=0;i<shop.list->array_size("$shop");i++) { MudObject *s = shop.list->array_get_object("$shop", i); if (s) wot.add(*s); } NewWorld wh = match(player, argc-which, argv+which, 0, LOOK_SPECIAL, 0, &wot); if (wh.getsize()) { b = wh.get_nth(0); } if (wh.getsize()>1) { player->printf("You can only buy one type of thing at a time.\n"); return true; } if (!b) { si = notstock; } if (si.error) { player->printf("%s\n", si.error); return true; } si = find_stock(shop.list, b); } if (!b) { item--; if (item < 0) { player->printf("Items only have ID numbers greater than one\n"); return true; } si = find_stock(shop.list, item); if (si.error) { player->printf("%s\n", si.error); return true; } b = si.obj(); } if (!b) { player->printf("Cannot find that item to buy...\n"); return true; } int toopricy = 0; if (all==1) { howmany = si.stock(); if (howmany == -1) { howmany = 100000; toopricy = 1; } } if (si.stock() != -1 && (howmany>si.stock() || (si.stock()==0))) { if (si.stock()==0) { tell(player, shop.mob, lang("We are out of stock of those.", shop.mob)); } else { tell(player, shop.mob, ssprintf(lang("We only have %i of those in stock.", shop.mob), si.stock()).c_str()); } return true; } int unit_cost = cost_atfor(b, shop.list, player); int cost = convertcash(unit_cost * howmany, NULL, shop.cur); int kash = cash(player, shop.cur); if ((cost && (kash - cost) < 0)||toopricy) { tell(player, shop.mob, lang("You haven't got enough cash.", shop.mob)); return true; } if (dotrap(E_ONWONTSELLTO, player, b)) { tell(player, shop.mob, lang("I can't sell that to you.", shop.mob)); return true; } int mass = mass_in_grams(b) * howmany; if (mass > mass_capacity_left_in_grams(mfor)) { if (howmany>1 || b->get_flag(FL_PLURAL)) { tell(player, shop.mob, lang("They'd be too heavy.", shop.mob)); } else tell(player, shop.mob, lang("It'd be too heavy.", shop.mob)); return true; } int hands = freehands(mfor); if (hands < howmany) { if (howmany>1 || b->get_flag(FL_PLURAL)) { tell(player, shop.mob, lang("Wouldn't be able to carry so many.", shop.mob)); } else { tell(player, shop.mob, lang("Wouldn't be able to carry so much.", shop.mob)); } return true; } int maxlimit = si.atonce?si.atonce:10; if (howmany>maxlimit) { tell(player, shop.mob, ssprintf(lang("You can only buy %i of those at a time.", shop.mob), maxlimit).c_str()); return true; } if (dotrap(E_ONBUY, player, b, shop.mob)) { /* ::: buy o1==template, o2==shopkeeper; return 1 to abort. before anything is actually cloned. set !virtrade to 1 on o2 if you want it to not actually clone the items, but to do the paying thing. */ return true; } if (dotrap(E_BEFOREVEND, player, shop.mob, b)) { /* ::: before_vend o1==shopkeeper, o2==object to buy; return 1 to abort. */ return true; } if (b->get_int("!notrade", 0)) { return true; } NewWorld things; if (b->get_int("!virtrade", 0)==0) { for (int i=0;i<howmany;i++) { MudObject *thing = clone_object(b, mfor, 0); thing->set_bflag(FL_NOSAVE, 0); thing->set("zone", "@auto"); thing->set("$clonedat", now); thing->set("$clonedby", shop.mob->id); if (!dotrap(E_ONBOUGHT, player, thing, shop.mob, 0)) { /* ::: bought o1==actual item, o2==shopkeeper; return 1 if you don't want the shopkeeper to 'give' item to player */ things.add(*thing); if (thing->get_flag(FL_FIXED)) set_owner(thing, shop.mob->owner); if (!cost) { if (thing) { thing->set("value", 0); } } } } if (things.getsize()) { if (!b->get_flag(FL_FIXED)) { if (shop.mob == mfor) { player->oprintf(cansee && avoid(shop.mob), "%#M %[conjures up/conjure up] %s for %s.\n", shop.mob, give_number(b, howmany).c_str(), himself_or_herself(mfor)); shop.mob->printf("You conjure up %s for yourself.\n", give_number(b, howmany).c_str(), player); } else { mfor->printf("%#M %[gives/give] %s to you.\n", shop.mob, give_number(b, howmany).c_str()); mfor->oprintf(cansee && avoid(shop.mob), "%#M %[gives/give] %s to %M.\n", shop.mob, give_number(b, howmany).c_str(), mfor); shop.mob->printf("You give %s to %M.\n", give_number(b, howmany).c_str(), mfor); if (mfor==player) { set_prons(player, things); } } } else { shop.mob->printf("You drop %s.\n", give_number(b, howmany).c_str()); shop.mob->oprintf(cansee, "%#M %[drops/drop] %s.\n", shop.mob, give_number(b, howmany).c_str()); set_prons(player, things); } } } if (!cost) { if (howmany>1 || b->get_flag(FL_PLURAL)) { if (shop.mob != player) tell(player, shop.mob, lang("You can have those for free.", shop.mob)); } else { if (shop.mob != player) tell(player, shop.mob, lang("You can have that for free.", shop.mob)); } } else { if (shop.mob != player) tell(player, shop.mob, ssprintf(lang("That will be %s thanks.", shop.mob), formatcash(cost, shop.cur, 1)).c_str()); if (!IS_CAPTAIN(player)) { if (streq(si.aname, "shop")) { shop.list->array_set("$sold", si.idx, shop.list->array_get_int("$sold", si.idx, 0)+howmany); } } } set_cash(player, shop.cur, kash - cost); dotrap(E_AFTERBUY, player, b, shop.mob); /* ::: after_buy o1==template bought, o2==shopkeeper; after everything but before object removed from stock */ si.destock(player, howmany); return true; } static bool names_match(MudObject *what, const char *str, int catonly, int namesonly) { if (!catonly) { if (what->get("name") && names_match(what->get("name"), str)) return 1; if (what->get("short") && names_match(what->get("short"), str)) return 1; if (what->get("altshort") && names_match(what->get("altshort"), str)) return 1; } if (namesonly) return 0; if (streq(str, "anything")) { return 1; } if (streq(str, "junk")) { if (what->get_int("food")!=-1 || what->get_int("alcohol")!=-1) { return 0; } return 1; } if (category_match(what, str)) return 1; return 0; } static bool verb_shopfind(MudObject *who, int argc, const char **argv) { Divert d(who, "shopfind"); MudObject *sought=0; int se = 0; if (argc>=2) { sought = planet->get(argv[1]); se = 1; } MudObject *o; int i; foreach_alpha(planet, o, i) if (o->get_flag(FL_STORE)) if (se) { int max = o->array_size("shop"); for (int j=0;j<max;j++) { if (sought) { if (o->array_get_object("shop", j)==sought) { who->printf(" %#-30M (%s).\n", o, o->id); } } else { MudObject *s = o->array_get_object("shop", j); if (s && s->get("name") && (names_match(s, argv[1]))) { who->printf(" %30M at %-30M (%s %s)\n", s, o, s->id, o->id); } } } } else { who->printf("%#-30M %10s %s\n", o, o->get("stype")?o->get("stype"):"", o->id); } return true; } static bool verb_restock(MudObject *who, int argc, const char **argv) { MudObject *shop = who->owner; if (!shop->get_flag(FL_STORE)) { who->printf("This is not a shop.\n"); return true; } if (!IS_CAPTAIN(who) && shop->get_object("shopmob") != who) { who->printf("You don't own this shop.\n"); return true; } int stock = 0; MudObject *of=0; for (int i=0;i<shop->array_size("shop");i++) { if (shop->array_get_int("$stock", i) < shop->array_get_int("shop", i, "istock")) { stock++; of = shop->array_get_object("shop", i); shop->array_set("$stock", i, shop->array_get_int("shop", i, "istock")); } } if (stock) { if (stock == 1) { who->printf("You order in more %s.\n", plural_name(of).c_str()); who->oprintf(cansee, "%#M orders in more %s.\n", who, plural_name(of).c_str()); } else { who->oprintf(cansee, "%#M orders in more stock.\n", who); who->printf("You order in more stock.\n"); } } else { who->printf("Everything is fully in stock.\n"); } return true; } static bool verb_tradestats(MudObject *who, int argc, const char **argv) { Divert d(who, "tradestats"); int i; MudObject *o; int income=0; int expend=0; foreach_alpha(planet, o, i) if (o->get_flag(FL_STORE)) { int j=0; income += o->get_int("$income",0); expend += o->get_int("$expend",0); for (j=0;j<o->array_size("shop");j++) { int sold = o->array_get_int("$sold", j); MudObject *it = o->array_get_object("shop", j); if (sold != -1 && it) { who->printf("%-30M:%30M:%3i:%6i:%10i\n", o, it, sold, it->get_int("cost",0), it->get_int("cost",0)*sold); } } } return true; } #include "verbmodule.h" void startup() { AUTO_VERB(value, 2, 0, PFL_AWAKE); AUTO_VERB(list, 3, 0, PFL_AWAKE); AUTO_VERB(preview, 3, 0, PFL_AWAKE); AUTO_VERB(buy, 3, 0, PFL_AWAKE); AUTO_VERB(sell, 3, 0, PFL_AWAKE); AUTO_VERB(size, 2, 0, PFL_AWAKE); AUTO_VERB(restock, 6, 0, PFL_AWAKE); AUTO_VERB(tradestats, 6, 0, PFL_GOTO); AUTO_VERB(shopfind, 5, 0, PFL_GOTO); }