/**
* This is to handle a place where players can sell a number of neato
* craft items they have made up. It will be extended to deal with
* books as well.
* <p>
* It keeps track of who sold each item and how much they will
* receive when they pay for it. Then tracks the royalties so
* they can come back and collect them.
* <p>
* This will be run as a handler object so that npcs and rooms can
* both run as this sort of shop.
* <p>
* NB: This object is not saved using save_object. You need to set the
* save and load functions for this system to work.
* @author Pinkfish
* @started Thu Feb 5 15:39:57 CST 1998
*/
#include <obj_parser.h>
#include <money.h>
#include <move_failures.h>
#include <player.h>
#include <shops/craft_shop.h>
/**
* This keeps track of the object information itself.
* @element cost the cost of the object when it is sold
* @element cap_owner the capitalised owner of the object
* @element short the short describtions of the object
* @element ob_num the number of the object
*/
class craft_object {
int cost;
string cap_owner;
int ob_num;
string category;
string short;
}
/**
* This class is the main sellable list.
*/
class craft_sellable {
/**
* The list of objects, it is indexed on the owners name.
*/
mapping objects;
}
/**
* This is the class with information about the category in it.
*/
class craft_category {
int state;
string description;
string information;
string* voted;
int yes;
int no;
int abstain;
int timeout;
}
#define SELL_OBJECT_NAME_PROP "sell name"
#define SELL_OBJECT_OWNER_PROP "sell owner"
#define SELL_OBJECT_CLASS_PROP "sell class"
#define SELL_OBJECT_ID_PROP "sell id"
/*
* This will index on the name tag and will have an object, an array of owners
* and
* a number left count as the value in an array. The object is
* a dummy object to match on. The real object will be created from
* the save information.
*/
private nosave mapping _sellables;
/*
* This is the contained containingt the objects to sell. We keep them in
* a container so that we can do find_match matches.
*/
private nosave object _sell_list;
/* The royalties to be payed to each player. */
private nosave mapping _royalties;
/* The current id to use for the save files... */
private nosave int _current_save_num;
/* The categories we are using, the default is none. */
private nosave mapping _categories;
private nosave int _category_callout;
/* If not set then we add a name on by default if it doesn't exist. */
private nosave int _category_dont_use_name;
/* The functions to save and load the files... */
private nosave function _save_function;
private nosave function _load_function;
private nosave function _category_function;
private nosave int *_current_ids;
private nosave int _has_loaded;
void load_it();
void save_it();
private void update_sellable(string name);
class craft_object create_craft_object(object ob,
string owner,
int cost,
string category);
void adjust_royalty(string player, int amt);
class craft_sellable create_craft_sellable();
void adjust_royalty(string player, int amt);
class craft_object query_class_of_shop_object(object ob);
object find_shop_object(class craft_object frog);
object *query_sell_list_obs();
string query_id_of_shop_object(object ob);
void remove_shop_id(string id);
private void setup_timeout_call();
object* query_items_with_shop_id(string id);
void create() {
/* Create a nice container to put our sell list in. */
_sell_list = clone_object("/std/container");
_current_save_num = 1;
_current_ids = ({ });
_categories = ([ ]);
} /* create() */
/**
* This method is called by the controled object onto here to control
* if the owners name is added to shop objects. If this is set to 1
* then the owners name will not be added by default.
* @param flag the flag to set
*/
void set_dont_use_name(int flag) {
_category_dont_use_name = flag;
} /* set_dont_use_name() */
/**
* This method is used to determine the status of the flag which controls
* adding the owners name to shop objects by default.
* If this is set to 1
* then the owners name will not be added by default.
* @return 1 if the object owners name is not used, 0 if it is
*/
int query_dont_use_name() {
return _category_dont_use_name;
} /* query_dont_use_name() */
/**
* This method creates a save file for the specified objects autoloading
* capability. If the number to write to is non-null then it will
* write to that object.
* @param ob the object to get an autoload number for
* @param fixed_num the file number to write to
* @return the auto load number
* @see save_it()
*/
protected int create_auto_load_file(mixed ob, int fixed_num) {
mixed *auto_load;
mixed tmp;
if (objectp(ob)) {
if (this_player()) {
catch(auto_load = this_player()->create_auto_load(({ ob })));
}
if (!auto_load) {
catch(auto_load = PLAYER_OB->create_auto_load(({ ob })));
}
if (!auto_load) {
// Make it error on the last one...
auto_load = AUTO_LOAD_OB->create_auto_load(({ ob }));
}
} else if (pointerp(ob)) {
auto_load = ob;
} else {
printf("Error! Dammit!\n");
}
if (!fixed_num) {
do {
tmp = evaluate(_load_function,
CRAFT_SHOP_DATA_SAVE_FILE,
"" + _current_save_num);
if (tmp) {
_current_save_num++;
}
} while (tmp);
fixed_num = _current_save_num;
}
evaluate(_save_function,
CRAFT_SHOP_DATA_SAVE_FILE,
auto_load,
"" + fixed_num);
return fixed_num;
} /* create_auto_load_file() */
/**
* This method creates a real object from the save file number.
* @param num the save file number
* @return the nice shiny new object
* @see create_auto_load_file()
*/
protected object create_real_auto_load_object(int num, object player) {
mixed *auto_load;
object *obs;
auto_load = evaluate(_load_function,
CRAFT_SHOP_DATA_SAVE_FILE,
"" + num);
if (userp(player)) {
obs = player->load_auto_load_to_array(auto_load, player);
} else if (this_player()) {
obs = this_player()->load_auto_load_to_array(auto_load, player);
} else {
obs = PLAYER_OB->load_auto_load_to_array(auto_load, player);
}
if (sizeof(obs)) {
return obs[0];
}
return 0; // clone_object("/std/object");
} /* create_object_from_auto_load() */
/**
* This method removes the auto load stuff after it is no longer needed.
* @param num the file number to remove
* @see create_object_from_auto_load()
*/
protected void remove_auto_load_file(int num) {
evaluate(_save_function,
CRAFT_SHOP_REMOVE_DATA_SAVE_FILE,
0,
"" + num);
} /* remove_auto_load_file() */
/**
* This method adds a selable into the current sellable array.
* @param name the name the object willb elisted under
* @param owner the owner of the object being sold
* @param ob the objects being sold
* @see add_sell_list_object()
* @see change_name_of_object()
*/
protected void add_to_sellables(string name,
string owner,
class craft_object *ob) {
class craft_sellable craft_sell;
if (_sellables[name]) {
craft_sell = (class craft_sellable)_sellables[name];
} else {
craft_sell = create_craft_sellable();
_sellables[name] = craft_sell;
}
/*
* This seemingly weird way of handling the craft objects is so we have
* an easily returnable handle to the class which we can pass to the
* dummy shop objects.
*/
if (!craft_sell->objects[owner]) {
craft_sell->objects[owner] = ({ });
}
craft_sell->objects[owner] += ob;
update_sellable(name);
} /* add_to_selllist() */
/**
* This will allow us to add an object into the inventory of the
* craft shop. If the name already exists in the inventory we
* just increment the number left by the number passed in here.
* If the number left goes below 0 then the item is remove
* from the inventory.
* @param ob the object to add
* @param name the tag to use for it
* @param num the number to add
* @param category the category to add the item to
* @param dont_update_cost allows the same name to have multiple prices
* @see remove_list_object()
* @see add_sell_list_name()
*/
int add_list_object(object ob,
string name,
int cost,
string owner,
string category,
int dont_update_cost) {
class craft_object craft_ob;
string cap_owner;
if (!objectp(ob) || !stringp(name) || !intp(cost)) {
return 0;
}
cap_owner = owner;
owner = lower_case(owner);
add_to_sellables(name,
owner,
({ create_craft_object(ob, cap_owner, cost, category) }));
if (!dont_update_cost) {
foreach (craft_ob in ((class craft_sellable)_sellables[name])->objects[owner]) {
craft_ob->cost = cost;
craft_ob->category = category;
}
}
save_it();
return 1;
} /* add_list_object() */
/**
* This method removes a single object from the current object list.
* @param name the name of object to remove
* @param owner the owner of the object to remove
* @param ob the craft object pointer itself we want removed
* @see add_list_object()
*/
void remove_list_object(string name, string owner, class craft_object ob) {
int i;
object us;
class craft_object *data;
if (_sellables[name] &&
((class craft_sellable)_sellables[name])->objects[owner]) {
data = ((class craft_sellable)_sellables[name])->objects[owner];
for (i = 0; i < sizeof(data); i++) {
if (data[i] == ob) {
break;
}
}
//i = member_array(ob, (class craft_object)_sellables[name]->objects[owner]);
if (i < sizeof(data)) {
((class craft_sellable)_sellables[name])->objects[owner] -= ({ ob });
if (!sizeof(((class craft_sellable)_sellables[name])->objects[owner])) {
map_delete(((class craft_sellable)_sellables[name])->objects, owner);
if (!sizeof(((class craft_sellable)_sellables[name])->objects)) {
map_delete(_sellables, name);
us = find_shop_object(ob);
remove_shop_id(query_id_of_shop_object(us));
}
}
save_it();
remove_auto_load_file(ob->ob_num);
us = find_shop_object(ob);
us->dest_me();
} else {
printf("Unable to find the object to remove? %O, %O\n", name, owner);
}
}
} /* remove_list_object() */
/**
* This method turns a craft_object class into an actual object.
* @param craft the craft object to create
* @return the newly formed craft object
* @see create_all_real_objects()
*/
object create_real_object(object player, class craft_object craft) {
return create_real_auto_load_object(craft->ob_num, player);
} /* create_real_object() */
/**
* This method creates real objects for all the passed in dummy objects.
* Remember to dest the objects after you are finished with them.
* @param obs the dummy objects to find real ones of
* @return the real object values
* @see create_real_object()
* @example
* real_obs = create_real_objects(this_player(), obs);
* foreach (ob in real_obs) {
* ret += ob->the_short + ":\n" + ob->long() + "\n";
* }
*/
object *create_all_real_objects(object player, object *obs) {
object *ret;
object ob;
class craft_object craft;
object new_ob;
ret = ({ });
foreach (ob in obs) {
craft = query_class_of_shop_object(ob);
new_ob = create_real_object(player, craft);
if (!new_ob) {
ret->move("/room/rubbish");
return ({ });
}
ret += ({ new_ob });
}
return ret;
} /* create_all_real_objects() */
/**
* This method creates a craft object from the input actual object.
* @param ob the real input object
* @param owner the owner of the object
* @param cost the cost of the object
* @param category the category of the object
* @return a nice craft object
*/
class craft_object create_craft_object(object ob,
string owner,
int cost,
string category) {
class craft_object craft;
int num;
craft = new (class craft_object);
num = create_auto_load_file(ob, 0);
craft->ob_num = num;
craft->cost = cost;
craft->cap_owner = owner;
craft->category = category;
craft->short = ob->query_short();
return craft;
} /* create_craft_object() */
/**
* This method creates a sellable class.
* @return a new sellable class
*/
class craft_sellable create_craft_sellable() {
class craft_sellable craft;
craft = new(class craft_sellable);
craft->objects = ([ ]);
return craft;
} /* create_craft_sellable() */
/**
* This method returns the owner associated with the shop
* object.
* @param ob the shop object to query the owner of
* @return the owner of the shop object
*/
string query_owner_of_shop_object(object ob) {
return ob->query_property(SELL_OBJECT_OWNER_PROP);
} /* query_owner_of_shop_object() */
/**
* This method returns the list name associated with the shop
* object.
* @param ob the shop object to query the list name of
* @return the owner of the shop object
*/
string query_name_of_shop_object(object ob) {
return ob->query_property(SELL_OBJECT_NAME_PROP);
} /* query_name_of_shop_object() */
/**
* This method returns the id associated with the shop object.
* This is the one letter id used for buying selling etc.
* @param ob the shop object to query the id of
* @return the id of the shop object
*/
string query_id_of_shop_object(object ob) {
return ob->query_property(SELL_OBJECT_ID_PROP);
} /* query_id_of_shop_object() */
/**
* This method sets the current id of the shop objects.
* @param obs the objects to set the id for
* @param id the new id for them all
*/
void set_id_of_shop_objects(object *obs, string id) {
obs->add_property(SELL_OBJECT_ID_PROP, id);
obs->add_alias(id);
} /* set_id_of_shop_objects() */
/**
* This method returns the category of the speficied shop object.
* @param ob the object to find the category of
* @return the category of the object
*/
string query_category_of_shop_object(object ob) {
class craft_object craft;
craft = query_class_of_shop_object(ob);
if (craft) {
return craft->category;
}
return 0;
} /* query_category_of_shop_object() */
/**
* This method returns the possible short descriptions of the shop objects.
* @param ob the object to find the shorts of
* @return the short descriptions
*/
string query_short_of_shop_object(object ob) {
class craft_object craft;
craft = query_class_of_shop_object(ob);
if (craft) {
if (sizeof(craft) == 5) {
return craft->short;
}
return "womble";
}
return 0;
} /* query_shorts_of_shop_object() */
/**
* This method updates the internal representation of the specified
* craft object. It replaces the saved auto load info with the new
* stuff.
* @param craft the craft object containing the info to replace
* @param replacement_ob the new object
*/
void update_craft_object(object craft_ob, object replacement_ob) {
class craft_object craft;
craft = query_class_of_shop_object(craft_ob);
if (craft) {
create_auto_load_file(replacement_ob, craft->ob_num);
}
} /* update_craft_object() */
/**
* This method attempts to figure out what the next id would
* be for the object. It will allocate this id as well as returning.
* @return the next id for the shop
* @see remove_shop_id()
* @see update_sellables()
*/
string query_next_shop_id() {
int num;
num = 0;
while (member_array(num, _current_ids) != -1) {
num++;
}
_current_ids += ({ num });
return sprintf("%c%c", (num / 26) + 'a',
(num % 26) + 'a');
} /* query_next_shop_id() */
/**
* This method removes an id when that book has been taken from the
* shop.
* @param id the id to remove
* @see query_next_shop_id()
*/
void remove_shop_id(string id) {
int real_id;
real_id = (id[0] * 26) - 'a' + id[1] - 'a';
_current_ids -= ({ real_id });
} /* remove_shop_id() */
/**
* This method searches the current sell list to see if the object
* passed corresponds to any of the existing things in our
* sell list.
* @param ob the object to check
* @return the craft_object class pointed to by the object
* @see add_object()
* @see remove_object()
* @see create_real_object()
*/
class craft_object query_class_of_shop_object(object ob) {
return ob->query_property(SELL_OBJECT_CLASS_PROP);
} /* query_class_of_shop_object() */
/**
* This method figured out what the real object is for the class.
* @param frog the class to find the object for
* @return the dummy object for the class
*/
object find_shop_object(class craft_object frog) {
object ob;
foreach (ob in query_sell_list_obs()) {
if (query_class_of_shop_object(ob) == frog) {
return ob;
}
}
return 0;
} /* find_shop_object() */
/**
* This method changes the value of the shop object.
* @param ob the shop object
* @param value the new value
*/
void change_value_of_shop_object(object ob, int value) {
string owner;
string name;
class craft_object wombat;
//object frog;
//int old_cost;
object* obs;
obs = query_items_with_shop_id(query_id_of_shop_object(ob));
foreach (ob in obs) {
owner = query_owner_of_shop_object(ob);
name = query_name_of_shop_object(ob);
wombat = query_class_of_shop_object(ob);
wombat->cost = value;
ob->set_value(value);
//old_cost = wombat->cost;
/*
foreach (wombat in ((class craft_sellable)_sellables[name])->objects[owner]) {
if (wombat->cost == old_cost) {
wombat->cost = value;
frog = find_shop_object(wombat);
frog->set_value(value);
}
}
*/
}
save_it();
} /* change_value_of_shop_object() */
/**
* This method changes the name of the shop object.
* @param ob the shop object to change
* @param new_name the new name of the shop object
* @param dont_update_cost allows the names to merge and not update costs
* @return 1 on success, 0 on failure
*/
int change_name_of_shop_object(object ob, string new_name,
int dont_update_cost) {
string owner;
string name;
object* obs;
string* names;
class craft_object wombat;
int new_cost;
class craft_object* fluff;
int i;
obs = query_items_with_shop_id(query_id_of_shop_object(ob));
names = ({ });
foreach (ob in obs) {
owner = query_owner_of_shop_object(ob);
name = query_name_of_shop_object(ob);
wombat = query_class_of_shop_object(ob);
if (!_sellables[name]) {
tell_creator("pinkfish", "The name %O does not exist.\n", name);
return 0;
}
if (!wombat) {
tell_creator("pinkfish", "Unable to find the class.\n");
return 0;
}
if (!dont_update_cost &&
_sellables[new_name] &&
(((class craft_sellable)_sellables[new_name])->objects[owner])) {
new_cost = (((class craft_sellable)_sellables[new_name])->objects[owner])[0]->cost;
wombat->cost = new_cost;
}
//
// First, add this thingy to the new spot.
//
//add_to_sellables(new_name, owner, ({ ob }));
if (!_sellables[new_name]) {
_sellables[new_name] = create_craft_sellable();
}
if (!(((class craft_sellable)_sellables[new_name])->objects[owner])) {
(((class craft_sellable)_sellables[new_name])->objects[owner]) = ({ });
}
(((class craft_sellable)_sellables[new_name])->objects[owner]) += ({ wombat });
//
// Next, remove it from where it came from.
//
fluff = ((class craft_sellable)_sellables[name])->objects[owner];
for (i = 0; i < sizeof(fluff); i++) {
if (fluff[i] == wombat) {
fluff = fluff[0..i-1] + fluff[i+1..];
}
}
if (sizeof(fluff)) {
((class craft_sellable)_sellables[name])->objects[owner] = fluff;
} else {
map_delete(((class craft_sellable)_sellables[name])->objects, owner);
}
if (!sizeof(((class craft_sellable)_sellables[name])->objects)) {
map_delete(_sellables, name);
}
names |= ({ name });
}
if (sizeof(names)) {
update_sellable(name);
update_sellable(new_name);
save_it();
return 1;
}
return 0;
} /* change_name_of_shop_object() */
/**
* This method changes the category of the shop object.
* @param ob the shop object to change
* @param new_category the new category of the shop object
*/
int change_category_of_shop_object(object ob, string new_category) {
string owner;
string name;
class craft_object bing;
object* obs;
int do_save;
obs = query_items_with_shop_id(query_id_of_shop_object(ob));
foreach (ob in obs) {
owner = query_owner_of_shop_object(ob);
name = query_name_of_shop_object(ob);
if (!_sellables[name] ||
!((class craft_sellable)_sellables[name])->objects[owner]) {
return 0;
}
foreach (bing in ((class craft_sellable)_sellables[name])->objects[owner]) {
bing->category = new_category;
do_save = 1;
}
}
if (do_save) {
save_it();
}
return 1;
} /* change_name_of_shop_object() */
/**
* This method removes the real data behind the specified shop object.
* The object passed in must be one of the dummy objects used for
* listing the shops inventory.
* @param ob the object to remove
* @see sell_objects()
* @see query_class_of_shop_object()
*/
void remove_shop_object(object ob) {
string name;
string owner;
owner = query_owner_of_shop_object(ob);
name = query_name_of_shop_object(ob);
remove_list_object(name, owner, query_class_of_shop_object(ob));
} /* remove_shop_object() */
/**
* This method finds the objects which matched the passed in pattern.
* Note, this returns the dummy pointer objects not the actual
* objects, they need to be changed into real objects before they
* are really sold.
* @param str the pattern to match the objects on
* @return the matching objects
* @see query_class_of_shop_object()
* @see value_of_objects()
* @see sell_objects()
*/
class obj_match find_matching_objects(string str) {
class obj_match obs;
obs = (class obj_match)match_objects_in_environments(str, ({ _sell_list }));
return obs;
} /* find_matching_objects() */
/**
* This method determines the price of all the shop objects in the
* array.
* @return the price of the shop objects
* @param obs the objects to price
* @see find_matching_objects()
* @see query_class_of_shop_object()
* @see sell_objects()
*/
int value_of_objects(object *obs) {
object ob;
class craft_object craft;
int value;
foreach (ob in obs) {
craft = query_class_of_shop_object(ob);
if (craft) {
value += craft->cost;
}
}
return value;
} /* value_of_shop_objects() */
/**
* This method sells the specified shop objects to the player. This
* will make the payments to the player. The objects will all attempt
* to be moved into the player. The cut must be between 0 and 100.
* <p>
* The royalties from selling the object will be placed into the
* correct place by the function, so they can be picked up at a
* later date. The 'cut' will be taken out of the sale.
* @param obs the objects to sell
* @param place the place to sell the objects in
* @param player the player to sell the objects too
* @param cut the cut the manager takes from the sale (percentage)
* @return the array of sold objects
* @see value_of_objects()
* @see find_matching_objects()
* @see adjust_royalty()
*/
object *sell_objects(object *obs, string place, object player, int cut) {
object ob;
object this_ob;
object *sold;
class craft_object craft;
sold = ({ });
if (cut < 0) {
cut = 0;
}
if (cut > 100) {
cut = 100;
}
foreach (ob in obs) {
craft = query_class_of_shop_object(ob);
if (craft) {
if (player->query_value_in(place) >= craft->cost) {
// Create the object.
this_ob = create_real_object(player, craft);
if (this_ob && this_ob->move(player) == MOVE_OK) {
sold += ({ this_ob });
// Make the player pay for it.
player->pay_money(
MONEY_HAND->create_money_array(craft->cost, place),
place);
// Adjust the players current royalties.
adjust_royalty(query_owner_of_shop_object(ob),
craft->cost - (craft->cost * cut) / 100);
// Remove the object from our inventory.
remove_shop_object(ob);
}
}
}
}
return sold;
} /* sell_objects() */
/**
* This method deletes the specified list entry. The player
* owner will not be payed.
* @param id The list entry to delete
*/
void delete_objects( string id ) {
object ob, *obs;
obs = all_inventory( _sell_list );
obs = filter( obs,
(: $1->query_property( SELL_OBJECT_ID_PROP ) == $(id) :) );
foreach( ob in obs ) {
remove_shop_object( ob );
}
} /* delete_objects() */
/**
* This method will buy the specified objects and place them into our
* current potential sell list. This will destroy the objects after
* it has been added into the sell list correctly.
* @param obs the object to sell
* @param name the name of the object to sell
* @param cost the cost of the object being sold
* @param owner the person to who the object belongs
* @param category the category of the object
* @param dont_update_costs allows the system to handle objects with the same
* name and different costs
* @see sell_objects()
*/
object *buy_objects(object *obs,
string name,
int cost,
string owner,
string category,
int dont_update_costs) {
object ob;
object *bought;
bought = ({ });
foreach (ob in obs) {
if (add_list_object(ob, name, cost, owner, category, dont_update_costs)) {
ob->move("/room/rubbish");
bought += ({ ob });
}
}
return bought;
} /* buy_objects() */
/**
* This method creates a dummy object for use when selling items. This
* allows use to use alias names for the potions when buying/selling
* and not the real name of the container. When bought these names
* will be added to the thingy as aliases.
* @param owner the owner of the object
* @param name the sell list name of the object
* @return a dummy object to place into the sell list
*/
protected object create_dummy_object(string owner, string name, int cost,
class craft_object craft) {
object new_name;
string *bits;
new_name = clone_object("/std/object");
new_name->reset_get();
bits = explode(lower_case(name), " ");
if (!sizeof (bits)) {
new_name->set_name("error");
}
else {
new_name->set_name(bits[<1]);
new_name->add_adjective(bits[0..<2]);
}
if (member_array(lower_case(owner), bits) == -1) {
new_name->add_adjective(owner);
}
if (!_category_dont_use_name &&
member_array(lower_case(owner) + "'s", bits) == -1) {
new_name->add_adjective(lower_case(owner) + "'s");
new_name->set_short(craft->cap_owner + "'s " + name);
} else {
new_name->set_short(capitalize(name));
}
new_name->add_property(SELL_OBJECT_NAME_PROP, name);
new_name->add_property(SELL_OBJECT_OWNER_PROP, owner);
new_name->add_property(SELL_OBJECT_CLASS_PROP, craft);
new_name->add_property("determinate", "");
new_name->set_value(cost);
return new_name;
} /* create_dummy_object() */
/**
* This method adds an object into the current sell list.
* @param ob the object to add
*/
private void add_sell_object(object ob) {
if (ob->move(_sell_list) != MOVE_OK) {
printf("Unable to move %O into the sell list (%O).\n", ob, _sell_list);
}
} /* add_sell_object() */
/**
* This method returns all the current objects in the sell list.
* @return all the current objects in the sell list
*/
object *query_sell_list_obs() {
return all_inventory(_sell_list);
} /* query_sell_list_obs() */
/**
* This method returns the container associated with the sell list.
* @return the container for the sell list
*/
object query_sell_list() {
return _sell_list;
} /* query_sell_list() */
/**
* This method returns all the objects which match the specified
* id of the shop object.
* @param id the shop id to match on
* @return the objects which match it
*/
object* query_items_with_shop_id(string id) {
return filter(query_sell_list_obs(), (: query_id_of_shop_object($1) == $2 :),
id);
} /* query_items_with_shop_id() */
/**
* This method updates the specified named set of sellables. This
* removes and creates the nessessary dummy objects needed to
* handle the selling code. It will also set the ids for objects
* which need them, creating a new id if nessessary.
* @param name the name of the sellables list to update
* @see query_sell_list_obs()
*/
private void update_sellable(string name) {
string owner;
class craft_sellable bing;
mixed *data;
class craft_object womble;
class craft_object craft;
object shop_ob;
string id;
mixed *need_ids;
int cost;
mapping costs;
int pos;
if (_sellables[name]) {
bing = _sellables[name];
costs = ([ ]);
foreach (owner, data in bing->objects) {
id = 0;
data = filter(data, (: $1 :));
bing->objects[owner] = data;
pos = 0;
foreach (womble in data) {
if (sizeof(womble) == 4) {
craft = new(class craft_object);
craft->ob_num = womble->ob_num;
craft->cost = womble->cost;
craft->cap_owner = womble->cap_owner;
craft->category = womble->category;
craft->short = "womble";
data[pos] = craft;
}
pos++;
shop_ob = find_shop_object(womble);
if (!shop_ob ||
query_name_of_shop_object(shop_ob) != name) {
if (shop_ob) {
shop_ob->move("/room/rubbish");
}
add_sell_object(create_dummy_object(owner, name,
womble->cost, womble));
}
shop_ob = find_shop_object(womble);
if (!costs[womble->cost]) {
costs[womble->cost] = ({ 0, ({ }) });
}
if (!costs[womble->cost][0]) {
id = query_id_of_shop_object(shop_ob);
if (id) {
costs[womble->cost][0] = id;
}
}
if (!costs[womble->cost][0] ||
query_id_of_shop_object(shop_ob) != costs[womble->cost][0]) {
costs[womble->cost][1] += ({ shop_ob });
}
}
if (!_category_dont_use_name) {
foreach (cost, need_ids in costs) {
if (!need_ids[0]) {
need_ids[0] = query_next_shop_id();
}
set_id_of_shop_objects(need_ids[1], need_ids[0]);
}
}
}
if (_category_dont_use_name) {
foreach (cost, need_ids in costs) {
if (!need_ids[0]) {
need_ids[0] = query_next_shop_id();
}
set_id_of_shop_objects(need_ids[1], need_ids[0]);
}
}
}
} /* update_sellables() */
/**
* This method returns the list of sellables that the owner currently
* has in the shop. This will potentially return more than one of the
* same name if the owner has more than one of the same sort of object
* being sold.
* @param owner the owner to check
* @return the array of all the possible sellables
* @see query_list_object_cost()
*/
string *query_owner_sellables(string owner) {
class craft_sellable sell;
mixed *womble;
string *ret;
string name;
ret = ({ });
foreach (name, sell in _sellables) {
if (sell->objects[owner]) {
womble = sell->objects[owner];
ret += allocate(sizeof(womble), (: $(name) :) );
}
}
return ret;
} /* query_owner_sellables() */
/**
* This method returns the cost of the specified type of object.
* @param name the name of the object
* @param owner the owner of the object
* @return the cost of the object, 0 if it is not known
* @see query_owner_sellables()
*/
int query_list_object_cost(string name, string owner) {
class craft_sellable sell;
class craft_object wombat;
owner = lower_case(owner);
if (_sellables[name]) {
sell = _sellables[name];
if (sell->objects[owner]) {
wombat = sell->objects[owner][0];
return wombat->cost;
}
}
return 0;
} /* query_list_object_cost() */
/**
* This method sets the save function on the class. The save function will
* be called with two parameters, one is the file name to be saved and
* the other is the text to be saved to it.
* @param func the new save function
*/
void set_save_function(function func) {
_save_function = func;
} /* set_save_function() */
/**
* This method sets the load function on the class. The restore function
* will be called with one parameter and is expected to return a string.
* The parameter is the file name to load.
* @param func the new load function
*/
void set_load_function(function func) {
_load_function = func;
} /* set_load_function() */
/**
* This method is called when categories complete each of their phases to
* see if the idea was passed by enough majority or whatever to go onto
* the next one.
* @param func the new category function
*/
void set_category_function(function func) {
_category_function = func;
} /* set_category_function() */
/**
* This method saves the current state of the system.
* @see set_save_function()
* @see load_it()
*/
void save_it() {
if (_has_loaded) {
evaluate(_save_function, CRAFT_SHOP_MAIN_SAVE_FILE,
({ _sellables, _royalties, _current_save_num, _categories }) );
}
} /* save_it() */
/**
* This method loads the current state of the system.
* @see set_load_function()
*/
void load_it() {
mixed *map;
string name;
_has_loaded = 1;
map = evaluate(_load_function, CRAFT_SHOP_MAIN_SAVE_FILE);
if (pointerp(map) && sizeof(map) == 3) {
_sellables = map[0];
_royalties = map[1];
_current_save_num = map[2];
foreach (name in keys(_sellables)) {
reset_eval_cost();
update_sellable(name);
}
} else if (pointerp(map) && sizeof(map) == 4) {
_sellables = map[0];
_royalties = map[1];
_current_save_num = map[2];
_categories = map[3];
// This does need to be run even if there are no categories. What has
// no categories anyway?
foreach (name in keys(_sellables)) {
reset_eval_cost();
update_sellable(name);
}
} else {
_sellables = ([ ]);
_royalties = ([ ]);
}
setup_timeout_call();
} /* load_it() */
/**
* This method adjusts the current royalties for the player.
* @param player the name of the player to pay the royalties to
* @param amt the amount to adjust the value by
* @see query_royalties()
* @see pay_out_royalties()
*/
void adjust_royalty(string player, int amt) {
_royalties[player] += amt;
save_it();
} /* adjust_royalities() */
/**
* This method returns the current royalties for the specified person.
* @param player the player to get the royalties for
* @return the current royalties
* @see adjust_royalties()
* @see pay_out_royalties()
*/
int query_royalty(string player) {
return _royalties[player];
} /* query_royalties() */
/**
* This method returns the royalty mapping on the object. This is only
* use for debug information.
* @return the royalties mapping
*/
mapping query_all_royalties() {
return copy(_royalties);
} /* query_all_royalties() */
/**
* This method pays out the royalties owed to the specified player.
* @param player the player to pay out
* @param place the place in which the royalties are to occur
* @param amount the amount to pay out, 0 for everything
* @see query_royalties()
* @see adjust_royalties()
*/
void pay_out_royalty(string player, string place, int amount) {
object pl;
pl = find_player(player);
if (pl && _royalties[player]) {
if (!amount) {
amount = _royalties[player];
}
pl->adjust_money(MONEY_HAND->create_money_array(amount, place));
_royalties[player] -= amount;
if (!_royalties[player]) {
map_delete(_royalties, player);
}
save_it();
}
} /* pay_out_royalties() */
//
// All the category handling code goes after this point.
//
/**
* This method checks to make sure that the category is valid. This
* means a category that has been properly accepted.
* @param category the category to check
*/
int is_valid_category(string category) {
if (_categories[category] &&
(_categories[category]->state == CRAFT_CATEGORY_STATE_ACCEPTED ||
_categories[category]->state == CRAFT_CATEGORY_STATE_DELETEING)) {
return 1;
}
return 0;
} /* is_valid_category() */
/**
* Add a request for a new category.
* @param name the name of the category
* @param player the person who suggested it
* @param timeout the initial timeout for the nomination period
* @return 1 if successful added, 0 if not
*/
int add_category_request(string name,
object player,
int timeout) {
if (!_categories[name]) {
_categories[name] = new(class craft_category);
_categories[name]->state = CRAFT_CATEGORY_STATE_NOMINATING;
_categories[name]->voted = ({ player->query_name() });
_categories[name]->description = 0;
_categories[name]->yes = 0;
_categories[name]->no = 0;
_categories[name]->timeout = time() + timeout;
save_it();
return 1;
}
return 0;
} /* add_category_request() */
/**
* This method removes a category immediately.
* @param name the name of the category
*/
int remove_category_accepted(string category) {
if (_categories[category]) {
map_delete(_categories, category);
save_it();
return 1;
}
return 0;
} /* remove_category_accepted() */
/**
* This method adds a category and sets it as already being accepted.
* @param name the name of the category to add
*/
int add_category_accepted(string name) {
if (!_categories[name]) {
_categories[name] = new(class craft_category);
_categories[name]->state = CRAFT_CATEGORY_STATE_ACCEPTED;
_categories[name]->voted = ({ });
_categories[name]->description = 0;
_categories[name]->yes = 0;
_categories[name]->no = 0;
_categories[name]->timeout = 0;
save_it();
return 1;
}
return 0;
} /* add_category_accepted() */
/**
* This method sets the description of the category.
* @param cat the category to set the description of
* @param desc the description of the category
*/
void set_category_description(string cat, string desc) {
if (_categories[cat]) {
_categories[cat]->description = desc;
save_it();
}
} /* set_category_description() */
/**
* This method returns the category description.
* @param cat the cateogryt to find the description for
* @return 0 for no description, the main description otherwise
*/
string query_category_description(string cat) {
if (_categories[cat]) {
if (stringp(_categories[cat]->description)) {
return _categories[cat]->description;
}
}
return 0;
} /* query_category_description() */
/**
* This method sets the information of the category.
* @param cat the category to set the information of
* @param info the information of the category
*/
void set_category_information(string cat, string info) {
if (_categories[cat]) {
_categories[cat]->information = info;
save_it();
}
} /* set_category_information() */
/**
* This method returns the category information.
* @param cat the cateogryt to find the information for
* @return 0 for no information, the main information otherwise
*/
string query_category_information(string cat) {
if (_categories[cat]) {
if (stringp(_categories[cat]->information)) {
return _categories[cat]->information;
}
}
} /* query_category_information() */
/**
* This method returns the category associated with this name/category
* set.
*/
string query_category_of(string name, string owner) {
if (_sellables[name] &&
sizeof( ((class craft_sellable)_sellables[name])->objects[owner])) {
return ((class craft_sellable)_sellables[name])->objects[owner][0]->category;
}
return 0;
} /* query_category_of() */
/**
* This method checks to see if the player has voted (or nominated) for a
* specified category already.
* @param name the name of the category
* @param player the player being checked
* @return 1 if they have voted/nominated 0 if they have not
*/
int has_voted_for_category(string name,
object player) {
return _categories[name] &&
member_array(player->query_name(), _categories[name]->voted) != -1;
} /* has_player_voted() */
/**
* This method returns the time the current category has left before it
* times out.
* @param name the name of the cateogyr
* @return the time it timesout
*/
int query_category_timeout(string name) {
if (_categories[name]) {
return _categories[name]->timeout;
}
return CRAFT_CATEGORY_ERROR;
} /* query_category_timeout() */
/**
* This method allows the player to vote for the specified category.
* @param name the name of the category
* @param player the player voting
* @param choice their choice
* @return 1 if the vote was successful, 0 if not
*/
int vote_for_category(string name,
object player,
int choice) {
if (_categories[name]) {
if (!has_voted_for_category(name, player)) {
if (choice == CRAFT_CATEGORY_CHOICE_YES ||
choice == CRAFT_CATEGORY_CHOICE_NO ||
choice == CRAFT_CATEGORY_CHOICE_ABSTAIN) {
switch (choice) {
case CRAFT_CATEGORY_CHOICE_YES :
_categories[name]->yes++;
break;
case CRAFT_CATEGORY_CHOICE_NO :
_categories[name]->no++;
break;
case CRAFT_CATEGORY_CHOICE_ABSTAIN :
_categories[name]->abstain++;
break;
}
_categories[name]->voted += ({ player->query_name() });
save_it();
}
}
}
return 0;
} /* vote_for_category() */
/**
* This method returns the list of categories for the current shop.
* @return the list of categories
*/
string* query_categories() {
return filter(keys(_categories), (: is_valid_category($1) :));
} /* query_categories() */
/**
* This method is used to go through the categories and see which ones
* need to timeout.
*/
private void do_timeout_categories() {
string name;
class craft_category cat;
int timeout;
foreach (name, cat in _categories) {
if (cat->state != CRAFT_CATEGORY_STATE_ACCEPTED &&
cat->timeout &&
cat->timeout < time()) {
//
// Timed out!
//
timeout = evaluate(_category_function,
cat->state,
cat->voted,
cat->yes,
cat->no,
cat->abstain);
if (timeout) {
//
// Ok, do something groovy based on the state.
//
cat->voted = ({ });
cat->yes = 0;
cat->no = 0;
switch (cat->state) {
case CRAFT_CATEGORY_STATE_NOMINATING :
cat->state = CRAFT_CATEGORY_STATE_VOTING;
cat->timeout = time() + timeout;
break;
case CRAFT_CATEGORY_STATE_VOTING :
cat->state = CRAFT_CATEGORY_STATE_ACCEPTED;
cat->timeout = 0;
break;
case CRAFT_CATEGORY_STATE_DELETEING :
map_delete(_categories, name);
break;
}
} else {
switch (cat->state) {
case CRAFT_CATEGORY_STATE_VOTING :
case CRAFT_CATEGORY_STATE_NOMINATING :
map_delete(_categories, name);
break;
case CRAFT_CATEGORY_STATE_DELETEING :
cat->state = CRAFT_CATEGORY_STATE_ACCEPTED;
cat->timeout = 0;
cat->voted = ({ });
cat->yes = 0;
cat->no = 0;
break;
}
}
}
}
save_it();
setup_timeout_call();
} /* do_timeout_categories() */
/**
* This method sets up a timeout call based on the next thing to timeout.
*/
private void setup_timeout_call() {
string name;
class craft_category cat;
int bing;
foreach (name, cat in _categories) {
if (cat->state != CRAFT_CATEGORY_STATE_ACCEPTED &&
cat->timeout != 0 &&
cat->timeout < bing) {
bing = cat->timeout;
}
}
if (_category_callout) {
remove_call_out(_category_callout);
}
if (bing) {
_category_callout = call_out((: do_timeout_categories() :), bing);
}
} /* setup_timeout_call() */
/** @ignore yes */
void dest_me() {
if(_sell_list) {
all_inventory(_sell_list)->move("/room/rubbish");
_sell_list->dest_me();
}
destruct(this_object());
} /* dest_me() */