inherit "/std/room";
/* adaptive prices added by Anirudh, March 1996
This is not meant to be exactly a free market system.
Prices are adjusted to control (limit) the flow of items from
the shop into the mud and sales of items to the shop. */
/* Reworked by Hamlet July/August 1995. */
/* New features:
1) DOCUMENTATION!
2) A more sensical scaled_value() (makes more sense to me at least)
3) Ability to force-set the price the shop will offer for an item;
set_resale_value(), adjust_resale_value(), query_resale_value(),
prevent_resale(), and allow_resale() all added to
/std/basic/misc.c (so, /std/object).
4) Stolen items are worth less now. % modifier and
set_stolen_modifier() to alter that modifier. Also
set_stolen_modifier(), query_stolen_modifier(), and
no_sell_if_stolen() in /std/basic/misc.c
5) A shop can be sell-only. The commands "value" and "sell"
are turned off for that. Put sell_only() in setup();
6) You can specify a special function in your shop to be run
ever time something is sold or bought. call is
set_sell_func("func") and set_buy_func("func");
func should look like: void func(object *stuff);
Yes, this means that stuff is an ARRAY of items being bought
or sold.
*/
// Edited 11 SEP 94 by Timion and Taniwha to fix the sell item bug.
/*
* 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.
*/
/* I am not sure whose code is whose any more. This *is* an extensive
rewrite, and a lot of it is now mine. Credits stay, though, since
I don't know which is which. - Hamlet
*/
/* Note: rewrite is not done. A lot of the code is original still.
Don't expect that to be true for long (muhahaha)
*/
#include "money_adjust.h"
#include "money.h"
#include "move_failures.h"
#include "shop.h"
#include "armoury.h"
#include "timestuff.h"
#define MIN_PRICE 10
/* our_table is the table for setting sales goals */
object our_storeroom;
mixed our_table;
string storename;
mixed buy_mess, sell_mess, list_mess, value_mess, browse_mess,
open_cond, *other_shops;
/* the variables for item sales data and adjust time, Anirudh */
mapping market_data, race_prefs;
int last_changed_price,handled_since_last;
int no_price_reduce;
int amount_sold,
amount_bought;
string shop_type;
int stolen_modifier; /* This will be a percent value */
int only_sell = 0;
string sell_func, buy_func;
void set_store_room(mixed ob);
string query_name(object ob);
string query_short(object ob);
int check_inv(object ob, string str);
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);
/* adaptive price functions, Anirudh */
int no_lower_price(int arg) { no_price_reduce = arg; }
int do_weight_min(int amt, int mass, int material, int sellbuy);
int do_cha_adjust(int amt, int cha, int updown);
int do_race_adjust(int amt, string race, int updown);
int set_race_pref(string race, int race_perc);
int query_multiplier(object ob);
void add_bought(object ob);
void add_sold(object ob);
void add_new_item(object ob,string str);
void check_adjust_prices();
void check_shop_table();
void check_cloned_items();
void adjust_prices();
void reset_market();
string query_storename() { return storename; }
void create() {
market_data = ([ ]);
race_prefs = ([ ]);
seteuid((string)"/secure/master"->
creator_file(file_name(this_object())));
restore_object(file_name(this_object()));
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";
stolen_modifier = 33; /* 33% of normal value */
::create();
}
/* These make spiffy messages for various actions. */
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; }
/* These initialize sell_func and buy_func, which are special functions
that can be set to run when something is bought or sold.
*/
void set_sell_func(string str) { sell_func = str; }
void set_buy_func(string str) { buy_func = str; }
mixed query_open_condition() { return open_cond; }
object check_storeroom()
{
if(stringp(our_storeroom) || !our_storeroom)
set_store_room(storename);
return our_storeroom;
}
/* This is whether the shop is open or not. open_cond can be
an int (true/false). Or a string, in which case it 'points'
to a function in the current object to run. OR, it can be
an array. ({ "object/name", "function_name" }) where it runs
"object/name"->function_name() to determine whether the shop is
open.
*/
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]);
}
int sell_only() { only_sell = 1; return 1; }
int set_race_pref(string race, int race_perc) {
int i;
if(!m_sizeof(race_prefs)) race_prefs = ([ ]);
if(race_perc != -1 && race_perc < 100) race_perc = 100;
if(race == "good") {
for(i=0;i<sizeof(GOOD_RACES);i++)
race_prefs[GOOD_RACES[i]] = race_perc;
return race_perc;
}
if(race == "neutral") {
for(i=0;i<sizeof(NEUTRAL_RACES);i++)
race_prefs[NEUTRAL_RACES[i]] = race_perc;
return race_perc;
}
if(race == "evil") {
for(i=0;i<sizeof(EVIL_RACES);i++)
race_prefs[EVIL_RACES[i]] = race_perc;
return race_perc;
}
race_prefs[race] = race_perc;
return race_perc;
}
void init() {
::init();
add_action("sell", "sell");
add_action("buy", "buy");
add_action("list", "list");
add_action("browse", "browse");
add_action("value", "value");
}
/* This determines how much the shopkeeper will offer for an item, according
to its value. It only gets called if set_no_resell() has not been called
and resale_value has not been hand-set to something.
*/
int scaled_value(int n)
{
int *prates;
int i, tot;
tot = 0;
prates = PAY_RATES;
for(i=0;( (i<sizeof(prates)) && (n > prates[i]) );i+=2);
if(i>0)
i-=2;/* prates[i] is now the rate-increment directly below the value. */
tot = (n - prates[i]) * ((prates[i+2] / prates[i+3]) -
(prates[i] / prates[i+1]));
tot /= (prates[i+2] - prates[i]);
tot += (prates[i] / prates[i+1]);
/* For those curious, we just defined a line segment. Basically, we
used y - y1 = m(x - x1). And found the y value for x = n.
Read shop.h for better explanation.
*/
return tot;
}
int set_stolen_modifier(int amt) {
if(amt > 100)
amt = 100;
if(amt < 0)
amt = 0;
stolen_modifier = amt;
return amt;
}
/* This checks the player to make sure they have the item.
I *think* this is a paranoia check, but I'll leave it.
*/
int check_inv(object thing, string str)
{
int i;
object *inv = find_match(str,this_player());
for(i =0; i < sizeof(inv); i++) {
if(inv[i] == thing) return(1);
}
return(0);
}
int sell(string str) {
object *obs, *selling, *cannot;
mixed *m_array;
int i, j, no, amt, material, mass, value, total_amt;
string s;
if (!test_open())
return 0;
if(only_sell) {
tell_object(this_player(),"This shop does not buy merchandise.\n");
return 1;
}
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];
}
check_storeroom();
selling = cannot = ({ });
for (i=0;i<sizeof(obs);i++) {
if( (obs[i]->query_value() > 0) && !obs[i]->do_not_sell() &&
(obs[i]->query_resale_value() != -1) &&
((obs[i]->query_stolen_modifier() != -1) ||
!obs[i]->query_property("stolen")) && !obs[i]->query_in_use()) {
/* O.K. this SHOULD check to see if the item IS in the
inventory
before we move it */
if(check_inv(obs[i],str)) {
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 */
amt = obs[i]->query_resale_value();
if(!amt) {
amt = (int)"/std/shop"->scaled_value((int)obs[i]->query_value());
if(amt > MAX_AMOUNT)
amt = MAX_AMOUNT;
}
/* Let's not let something sell back for more than it was bought
for */
if(amt > obs[i]->query_value())
amt = obs[i]->query_value();
/* "hot" goods lose value! */
if(obs[i]->query_property("stolen")) {
if(obs[i]->query_stolen_modifier() == 0)
amt = amt * stolen_modifier / 100;
else
amt = amt * obs[i]->query_stolen_modifier() / 100;
}
/* Adjusts price by item's adaptive multiplier (in 1/10 %) */
/*Adjust by mudwide scale factor */
amt=amt*MONEY_TRACKER->query_adj_fact(SBFLAG)/1000;
amt=amt*query_multiplier(obs[i])/1000;
amt = do_weight_min(amt,obs[i]->query_weight(),
obs[i]->query_material(),1);
amt = do_cha_adjust(amt,this_player()->query_cha(),1);
if((amt = do_race_adjust(amt,this_player()->query_race(),1))==
-101) { notify_fail("This shop refuses to deal with your "+
"race.\n");
amt = 0;
return 0;
}
/* Counts how many of each item the shop buys */
if(!(this_player()->query_creator()) &&
strsrch(this_player()->query_name(),"test") == -1)
add_bought(obs[i]);
total_amt += amt;
selling += ({ obs[i] });
obs[i]->being_sold();
}else if (obs[i]->short())
cannot += ({ obs[i] });
/* end of test if exists, not that this DOESN'T do the cannot that it should
*/
}
else if (obs[i]->short())
cannot += ({ obs[i] });
}
if (!sizeof(selling)) {
if (sizeof(cannot))
notify_fail("You cannot sell "+query_multiple_short(cannot)+", maybe you are holding or wearing it, or just don't have one.\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)+", maybe you are wearing or holding it, or just don't have one.\n");
do_parse(sell_mess, selling, this_player(),
(string)MONEY_HAND->money_string(m_array), "");
if(sell_func) call_other(this_object(),sell_func,selling,total_amt);
return 1;
}
int buy(string str) {
int i, amt, material, mass, ob_amt, total_cost, high_cost_flag;
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;
}
check_storeroom();
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)) {
/* adjusts price by adaptive multiplier of object */
high_cost_flag = 0;
if((ob_amt=(obs[i]->query_value()))>100000) {
high_cost_flag = 1;
ob_amt /= 100;
}
ob_amt=ob_amt*MONEY_TRACKER->
query_adj_fact(SSFLAG)/1000;
ob_amt=ob_amt*query_multiplier(obs[i])/1000;
/*
if(ob_amt>2000000) {
ob_amt=0;
secure_log_file("ani",this_player()->query_name()+" "+ctime(time())+
"\n");
}
*/
ob_amt = do_weight_min(ob_amt,obs[i]->query_weight(),
obs[i]->query_material(),0);
ob_amt = do_cha_adjust(ob_amt,this_player()->query_cha(),0);
if((ob_amt = do_race_adjust(ob_amt,this_player()->query_race(),0))==
-101) { notify_fail("This shop refuses to deal with your "+
"race.\n");
return 0;
}
if(obs[i]->query_property("playersetvalue")){
high_cost_flag = 0;
ob_amt = obs[i]->query_property("playersetvalue");
// voided my hard work, better have a good reason
secure_log_file("SHOPS",file_name(this_object())+" "+
file_name(obs[i])+" player set value\n");
}
if(high_cost_flag) ob_amt *= 100;
if (ob_amt < 1) {
ob_amt = 1;
secure_log_file("SHOPS",file_name(this_object())+" "+
file_name(obs[i])+" 0 value, "
+this_player()->query_name()+"\n");
}
if (ob_amt > 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());
if(buy_func) call_other(this_object(),buy_func,to_buy,total_cost);
return 1;
}
void do_buy(object *obs, int cost, object pl) {
int i;
mixed fish;
for (i=0;i<sizeof(obs);i++) {
/* counts how many of each item the shop sells */
if(!(this_player()->query_creator()) &&
strsrch(this_player()->query_name(),"test") == -1)
add_sold(obs[i]);
obs[i]->remove_property("playersetvalue");
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) {
if (!test_open())
return 0;
check_storeroom();
if(!our_storeroom)
{
notify_fail("The storeroom has been damaged, the shop is closed for now.\n");
return 0;
}
if (!str || str == "" || str == "all") {
do_parse(list_mess, this_object(), this_player(), "",
shop_list(all_inventory(our_storeroom), 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, ob_amt, mass, material, high_cost_flag;
if (!test_open())
return 0;
if (!str || str == "") {
notify_fail("Usage: browse <objects>\n");
return 0;
}
check_storeroom();
obs = find_match(str, our_storeroom);
if (!sizeof(obs)) {
notify_fail("Cannot find "+str+".\n");
return 0;
}
for (i=0;i<sizeof(obs);i++) {
high_cost_flag = 0;
if((ob_amt=(obs[i]->query_value()))>100000) {
high_cost_flag = 1;
ob_amt /= 100;
}
ob_amt=ob_amt*MONEY_TRACKER->
query_adj_fact(SSFLAG)/1000;
ob_amt=ob_amt*query_multiplier(obs[i])/1000;
/*
if(ob_amt>2000000) {
ob_amt=0;
}
*/
ob_amt = do_weight_min(ob_amt,obs[i]->query_weight(),
obs[i]->query_material(),0);
ob_amt = do_cha_adjust(ob_amt,this_player()->query_cha(),0);
if((ob_amt = do_race_adjust(ob_amt,this_player()->query_race(),0))==
-101) { notify_fail("This shop refuses to deal with your "+
"race.\n");
return 0;
}
if(obs[i]->query_property("playersetvalue")){
high_cost_flag = 0;
ob_amt = obs[i]->query_property("playersetvalue");
}
if(high_cost_flag) ob_amt *= 100;
if (ob_amt < 1) ob_amt = 1;
do_parse(browse_mess, obs[i], this_player(),
(string)MONEY_HAND->money_value_string(ob_amt),
(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, mass, material;
if (!test_open())
return 0;
if(only_sell) {
tell_object(this_player(),"This shop cannot appraise your goods.\n");
return 1;
}
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++) {
/* 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 = obs[i]->query_resale_value();
if(!val) {
val = (int)"/std/shop"->scaled_value((int)obs[i]->query_value());
if (val > MAX_AMOUNT)
val = MAX_AMOUNT;
}
if(val > obs[i]->query_value())
val = obs[i]->query_value();
/* "hot" goods lose value! */
if(obs[i]->query_property("stolen")) {
if(obs[i]->query_stolen_modifier() == 0)
val = val * stolen_modifier / 100;
else
val = val * obs[i]->query_stolen_modifier() / 100;
}
/* adjust price offered by the adaptive multiplier of the item */
val=val*MONEY_TRACKER->query_adj_fact(SBFLAG)/1000;
val = val*query_multiplier(obs[i])/1000;
val = do_weight_min(val,obs[i]->query_weight(),
obs[i]->query_material(),1);
val = do_cha_adjust(val,this_player()->query_cha(),1);
if((val = do_race_adjust(val,this_player()->query_race(),1))==
-101) { notify_fail("This shop refuses to deal with your "+
"race.\n");
return 0;
}
do_parse(value_mess, obs[i], this_player(),
(string)MONEY_HAND->money_value_string(val),
(string)(obs[i]->do_not_sell() ||
(obs[i]->query_resale_value() == -1)) );
}
return 1;
}
string shop_list(mixed arr, int detail) {
mapping inv, costs;
object *list;
string s, mon, *shorts, *vals;
int i, j, mass, material, ob_amt, high_cost_flag;
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++) {
/* lists price after multiplier is applied */
high_cost_flag = 0;
if((ob_amt=(ind[i]->query_value()))>100000) {
high_cost_flag = 1;
ob_amt /= 100;
}
ob_amt=ob_amt*MONEY_TRACKER->
query_adj_fact(SSFLAG)/1000;
ob_amt=ob_amt*query_multiplier(ind[i])/1000;
ob_amt = do_weight_min(ob_amt,ind[i]->query_weight(),
ind[i]->query_material(),0);
ob_amt = do_cha_adjust(ob_amt,this_player()->query_cha(),0);
if((ob_amt = do_race_adjust(ob_amt,this_player()->query_race(),0))==
-101) { notify_fail("This shop refuses to deal with your "+
"race.\n");
return 0;
}
if(ind[i]->query_property("playersetvalue")){
high_cost_flag = 0;
ob_amt = ind[i]->query_property("playersetvalue");
}
if(high_cost_flag) ob_amt *= 100;
if (ob_amt < 1) ob_amt = 1;
mon=(string)MONEY_HAND->money_value_string(ob_amt);
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)) {
storename = ob;
our_storeroom = find_object(ob);
if (!our_storeroom)
call_other(ob, "??");
our_storeroom = find_object(ob);
}
else if(objectp(ob))
{
storename = explode(file_name(ob),"#")[0];
our_storeroom = ob;
}
if(our_table && our_storeroom) check_shop_table();
}
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;
}
/*
* The shop types are:
* Jewelry
* Armory
* Magic
* General
*/
object *query_stock(string type) {
mixed *obs;
int i;
check_storeroom();
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 ({ });
}
mixed *stats() {
return ::stats() + ({
({ "total sold", amount_sold }),
({ "total bought", amount_bought }),
});
}
string query_shop_type() { return shop_type; }
void set_shop_type(string ty) { shop_type = ty; }
/* The new adaptive pricing functions, keep track of buys and sells,
return multipliers, figure out when and how to change multipliers,
save and recall the shop's data */
int do_race_adjust(int amt, string race,int updown) {
int temp;
if(!m_sizeof(race_prefs)) race_prefs = ([ ]);
if(!race_prefs[race]) return amt;
if(race_prefs[race] == -1) return -101;
if(updown) {
temp = amt*100/race_prefs[race]+1;
if(temp > amt) return amt;
return temp;
}
else {
temp = amt*race_prefs[race]/100-1;
if(temp < amt) return amt;
return temp;
}
return amt;
}
int do_weight_min(int amt,int mass, int material, int buysell) {
if(buysell) {
if (material == 2 && amt < mass) amt = mass;
else if (material == 1 && amt < mass/4) amt = mass/4;
else if (material == 3 && amt < mass/7) amt = mass/7;
else if (amt < mass/10) amt = mass/10;
}
else {
if (material == 2 && amt < mass*3/2) amt=mass*3/2;
else if (material == 1 && amt < mass*3/8 ) amt=mass*3/8;
else if (material == 3 && amt < mass*3/14) amt=mass*3/14;
else if (amt < mass*3/20) amt = mass*3/20;
}
return amt;
}
int do_cha_adjust(int amt, int cha, int updown) {
int temp;
if(!cha) return amt;
if(cha > 19) cha = 19;
if(cha > AVG_CHA) {
temp = 10*(cha - AVG_CHA)*CHA_AD/(18-AVG_CHA);
if(updown) {
temp = amt*(1000+temp)/1000-1;
if(temp < amt) return amt;
}
else {
temp = amt*1000/(1000+temp)+1;
if(temp > amt) return amt;
}
return temp;
}
if(cha < AVG_CHA) {
temp = 10*(AVG_CHA-cha)*CHA_AD/(AVG_CHA-3);
if(updown) {
temp = amt*1000/(1000+temp)+1;
if(temp > amt) return amt;
}
else {
temp = amt*(1000+temp)/1000-1;
if(temp < amt) return amt;
}
return temp;
}
return amt;
}
int query_multiplier(object ob) {
int i;
string item_path,*exper;
if (ob->query_property("fixed_price")) {
secure_log_file("SHOPS",file_name(this_object())+" "+file_name(ob)+
" fixed price\n");
return 1000;
}
if (!m_sizeof(market_data)) restore_object(file_name(this_object()),1);
exper = explode(file_name(ob),"#");
item_path = exper[0];
if(!mappingp(market_data))
{
market_data = ([ ]);
return 1000;
}
if (market_data[item_path]) {
if(only_sell && market_data[item_path][4] < 1000 &&
no_price_reduce) {
market_data[item_path][4] = 1000;
secure_log_file("SHOPS",file_name(this_object())+" no reduce\n");
}
return market_data[item_path][4];
}
else
return 1000;
}
/* note: this is the function when the SHOP buys something */
void add_bought(object ob) {
int i;
string item_path;
if (!m_sizeof(market_data))
restore_object(file_name(this_object()),1);
item_path = explode(file_name(ob),"#")[0];
if (market_data[item_path])
market_data[item_path][1]++;
else {
add_new_item(ob,item_path);
if (market_data[item_path])
market_data[item_path][1]++;
}
handled_since_last++;
check_adjust_prices();
if (m_sizeof(market_data))
save_object(file_name(this_object()));
}
/* and this is when the SHOP sells something */
void add_sold(object ob) {
int i;
string item_path;
if (!m_sizeof(market_data)) restore_object(file_name(this_object()),1);
if(!m_sizeof(market_data)) return ; // Taniwha, no shop table.
item_path = explode(file_name(ob),"#")[0];
if (market_data[item_path])
market_data[item_path][2]++;
else {
add_new_item(ob,item_path);
if (market_data[item_path])
market_data[item_path][2]++;
}
handled_since_last++;
if (market_data[item_path][3] && market_data[item_path][2] >=
3*market_data[item_path][3])
adjust_prices();
else
check_adjust_prices();
if (m_sizeof(market_data))
save_object(file_name(this_object()));
}
void add_new_item(object ob, string item_path) {
check_cloned_items();
if (!market_data[item_path]) {
market_data[item_path] = ({ ob->query_value(),0,0,0,1000,0 });
}
}
void set_item_table(mixed ob) {
if (stringp(ob)) {
our_table = find_object(ob);
if (!our_table)
call_other(ob, "??");
our_table = find_object(ob);
}
our_table = ob;
if(our_storeroom && our_table) check_shop_table();
}
object query_item_table() { return our_table; }
/* this will put the numbers in the table into market_data */
void check_shop_table() {
int i, clone_flag;
string *item_paths,ite;
mapping shelves;
if (!m_sizeof(market_data)) restore_object(file_name(this_object()),1);
shelves = our_table->query_shelves();
if(shelves) // Taniwha 1996
item_paths = m_indices(shelves);
if(sizeof(our_storeroom->query_room_clones())) clone_flag = 1;
for (i=0;i<sizeof(item_paths);i++) {
ite = 0;
ite = explode(item_paths[i],".c")[0];
if (!market_data[ite]) add_new_item(load_object(ite),ite);
market_data[ite][3]=shelves[item_paths[i]][0];
if (!clone_flag)
our_storeroom->add_clone(ite,shelves[item_paths[i]][1]);
}
if (!clone_flag)
our_storeroom->reset();
if (m_sizeof(market_data))
save_object(file_name(this_object()));
}
void check_cloned_items() {
int i,count,price;
object dummy;
mixed *cloned_items;
check_storeroom();
cloned_items = our_storeroom->query_room_clones();
for (i=0;i<sizeof(cloned_items);i++) {
if (!cloned_items[i] || objectp(cloned_items[i])) {
count++;
}
else {
dummy = clone_object(cloned_items[i]);
if (dummy) {
cloned_items[i] = explode(cloned_items[i],".c")[0];
price = dummy->query_value();
if (!market_data[cloned_items[i]] ||
market_data[cloned_items[i]][0]!=price) {
if (price < 10000) market_data[cloned_items[i]] =
({ price,0,0,count*3,1000,1 });
else market_data[cloned_items[i]] =
({ price,0,0,count*2,1000,1 });
}
dummy->dest_me();
}
count = 0;
}
}
}
void check_adjust_prices() {
int timelapsed = TIMEKEEPER->query_running_time()-
last_changed_price;
if (timelapsed <= 0 || last_changed_price == 0) {
reset_market();
}
else if ( handled_since_last > 300 || timelapsed > BASE_WEEK) {
adjust_prices();
}
}
void adjust_prices() {
string *item_paths,ite;
int i,goaldif,changemult,goal,timelapsed,devisor;
if(!m_sizeof(market_data)) restore_object(file_name(this_object()),1);
timelapsed = TIMEKEEPER->query_running_time()-last_changed_price;
/* don't want to overflow maxint here (percent of a week here) */
if (timelapsed < 20000000) {
timelapsed = timelapsed*100/BASE_WEEK;
}
else timelapsed = timelapsed*10/BASE_WEEK*10;
item_paths = m_indices(market_data);
for (i=0;i<sizeof(item_paths);i++) {
ite = item_paths[i];
if (market_data[ite][5]) {
market_data[ite][5] = 0;
market_data[ite][1]=market_data[ite][2]=0;
continue;
}
/* going to deal in 100ths of items... */
goal = market_data[ite][3]*timelapsed;
goaldif=100*(market_data[ite][2]-market_data[ite][1])-goal;
if (goal) {
if (goal > 100*market_data[ite][2])
changemult = goaldif*goaldif/goal;
else
{
// Taniwha /0 insanities
if(market_data[ite][2] == 0) changemult = 1;
else changemult = goaldif*goaldif/100/market_data[ite][2];
}
if (goaldif > 0) {
if (changemult > 2000) changemult = 2000;
market_data[ite][4] = market_data[ite][4]*
(1000+changemult)/1000;
}
if (goaldif < 0) {
if (changemult > 1000) changemult = 1000;
market_data[ite][4] = market_data[ite][4]*
1000/(1000+changemult);
if (market_data[ite][4] < MIN_PRICE)
market_data[ite][4]=MIN_PRICE;
}
market_data[ite][1]=market_data[ite][2]=0;
}
else {
goaldif+=100;
devisor = 2*market_data[ite][2]+market_data[ite][1];
if (!devisor) devisor = 10;
changemult = goaldif*goaldif/(100*devisor);
if (goaldif > 0) {
market_data[ite][4] = market_data[ite][4]*11/10;
if (market_data[ite][4] > 1000)
market_data[ite][4] = 1000;
}
if (goaldif < 0) {
if (changemult > 2000) changemult = 2000;
market_data[ite][4] = market_data[ite][4]*
1000/(1000+changemult);
if (market_data[ite][4]<MIN_PRICE)
market_data[ite][4]=MIN_PRICE;
}
market_data[ite][1]=market_data[ite][2]=0;
if (!market_data[ite][3] && market_data[ite][4] == 1000)
market_data = m_delete(market_data,ite);
}
}
last_changed_price = TIMEKEEPER->query_running_time();
handled_since_last = 0;
if (m_sizeof(market_data))
save_object(file_name(this_object()));
}
mapping query_market_data() {
return market_data;
}
void reset_market() {
string *item_paths,ite;
int i;
if (!m_sizeof(market_data)) restore_object(file_name(this_object()),1);
item_paths = m_indices(market_data);
for (i=0;i<sizeof(item_paths);i++) {
ite = item_paths[i];
market_data[ite][5] = market_data[ite][1]=
market_data[ite][2]=0;
}
last_changed_price = TIMEKEEPER->query_running_time();
handled_since_last = 0;
if (m_sizeof(market_data)) save_object(file_name(this_object()));
}
int set_md_value(string which_item,int which_one,int new_value) {
if (!m_sizeof(market_data)) restore_object(file_name(this_object()),1);
if(market_data[which_item]) {
market_data[which_item][which_one] = new_value;
}
if (m_sizeof(market_data)) save_object(file_name(this_object()));
if(market_data[which_item])
return market_data[which_item][which_one];
else return -101;
}
/* clean up the storeroom when we die */
void dest_me()
{
if(our_storeroom) our_storeroom->dest_me();
::dest_me();
}