/** * This is the handler for all things clubby, a club being a group of * players. Each club must have a unique name. It also handles elections * for various club positions. * @author Pinkfish * @started Sun Sep 27 03:35:42 EDT 1998 */ #include <clubs.h> #include <broadcaster.h> #include <player_handler.h> /** * The basic club information class. * @member actual_name the actual name of the club * @member founder the founder of the club * @member recruiters the people who can recruit for the club * @member members the members of the club * @member type the type of the club * @member accounts the accounts in the club * @member last_paid when the balance was last paid * @member last_touched when the club was last touched * @member extra_data specific data for each type of club * @member region the region the club is in */ class club_info { string actual_name; string founder; string *recruiters; string *members; int type; mapping accounts; int last_paid; /** * This keeps track of when the club was last fiddled with. * It will be used to check the timeout stuff. */ int last_touched; mixed extra_data; // Description of the club for others to enjoy. string description; string region; } #define CLUB_CACHE_SIZE 20 // The saved stuff... private mapping _club_names; private string *_observers; // The cache stuff. private nosave int _no_cache_hits; private nosave int _no_cache_requests; private nosave int _no_cache_miss; private nosave int _cache_call_out; private nosave mapping _cache; private nosave string *_cache_order; #define SAVE_FILE_NAME "/save/clubs" #define SAVE_FILE_DIR "/save/clubs/" protected void save_club(string name); protected void load_main(); protected void save_main(); int is_club(string club_name); int remove_recruiter(string name, string recruiter); int is_recruiter_of(string name, string recruiter); int query_club_type(string name); void check_extra_information(string club_name, string member, int login); int is_family(string name); int disband_club(string name); protected void set_club_changed(string name); protected void send_broadcast_message(string club, string message); protected void send_observer_event(string event_name, string *args ...); void create() { seteuid(master()->creator_file(file_name())); _club_names = ([ ]); _cache = ([ ]); _cache_order = ({ }); _observers = ({ }); load_main(); } /* create() */ /** @ignore yes */ string query_cap_name() { return "Club controller"; } /* query_cap_name() */ /** * This method loads the data from the disk. */ protected void load_main() { unguarded( (: restore_object(SAVE_FILE_NAME, 1) :) ); } /* load_me() */ /** * This method loads the data from the disk. */ protected void save_main() { unguarded( (: save_object(SAVE_FILE_NAME, 1) :) ); } /* load_me() */ /** * This method normalises the name for lookups so that we don't * get names too confused. Thanks to Terano for this idea. * @param name the name to normalise * @return the normalised name */ string normalise_name(string name) { return replace_string(lower_case(name), " ", "_"); } /* normalise_name() */ /** * Make the cache to the correct size. */ private void fixup_cache() { int i; if (sizeof(_cache_order) > CLUB_CACHE_SIZE) { for (i = sizeof(_cache_order) - CLUB_CACHE_SIZE; i >= 0; i--) { if (_club_names[_cache_order[i]]) { save_club(_cache_order[i]); } map_delete(_cache, _cache_order[i]); } _cache_order = _cache_order[sizeof(_cache_order) - CLUB_CACHE_SIZE + 1..]; } } /* fixup_cache() */ /** * This method either loads the data into the cache or it * reads it from the cache. */ protected class club_info query_club_info(string name) { class club_info bing; name = normalise_name(name); _no_cache_requests++; if (_cache[name]) { _no_cache_hits++; return _cache[name]; } if (unguarded( (: file_size(SAVE_FILE_DIR + $(name)) :)) > 0) { _cache[name] = unguarded( (: restore_variable(read_file(SAVE_FILE_DIR + $(name))) :) ); _cache_order += ({ name }); if (intp(_cache[name]->accounts)) { _cache[name]->accounts = ([ CLUB_DEFAULT_ACCOUNT_NAME : _cache[name]->accounts ]); } if (sizeof(_cache[name]) == 10) { bing = new(class club_info); bing->actual_name = _cache[name]->actual_name; bing->founder = _cache[name]->founder; bing->recruiters = _cache[name]->recruiters; bing->members = _cache[name]->members; bing->type = _cache[name]->type; bing->accounts = _cache[name]->accounts; bing->last_paid = _cache[name]->last_paid; bing->last_touched = _cache[name]->last_touched; bing->extra_data = _cache[name]->extra_data; bing->description = _cache[name]->description; bing->region = "Ankh-Morpork"; _cache[name] = bing; set_club_changed(name); } fixup_cache(); return _cache[name]; } _no_cache_miss++; return 0; } /* query_club_info() */ /** * This method saves the data to the disk. */ protected void save_club(string name) { name = normalise_name(name); if (_cache[name]) { _club_names[name] = 0; unguarded( (: write_file(SAVE_FILE_DIR + $(name), save_variable(_cache[$(name)]), 1) :) ); } } /* save_club() */ /** * This method checks the cache and then saves anything changed to the * disk... */ protected void save_cache() { string name; class club_info data; foreach (name, data in _cache) { if (_club_names[name]) { save_club(name); } } } /* save_cache() */ /** * This method marks the club as being changed. */ protected void set_club_changed(string name) { name = normalise_name(name); if (!undefinedp(_club_names[name])) { if (find_call_out(_cache_call_out) == -1) { _cache_call_out = call_out((: save_cache :), 0); } _club_names[name] = 1; } } /* set_club_changed() */ /** * This method adds a club to the system. */ private void add_club(string name, class club_info data) { name = normalise_name(name); _cache[name] = data; _club_names[name] = 0; set_club_changed(name); } /* add_club() */ /** @ignore yes */ string the_short() { return "Club Control"; } /* the_short() */ protected void create_extra_data(string name) { class club_info data; data = query_club_info(name); data->extra_data = 0; set_club_changed(name); } /* create_extra_data() */ /** * This method creates a club. The founder and the recruiter set is * initialy set to the founder. * @param name the name of the club * @param founder the founder of the club * @param region the region of the club * @return 1 was able to create the club, 0 if unable to create the club * @see disband_club() * @see change_club_type() */ int create_club(string name, string founder, int type, string region) { class club_info info; if (!stringp(name) || !stringp(founder)) { return 0; } info = new(class club_info); info->actual_name = name; if (type != CLUB_FAMILY) { info->recruiters = ({ founder }); info->members = ({ founder }); } else { info->recruiters = ({ }); info->members = ({ }); } info->founder = founder; info->last_touched = time(); info->type = type; info->last_paid = time(); info->description = 0; info->accounts = ([ CLUB_DEFAULT_ACCOUNT_NAME : 0 ]); info->region = region; add_club(name, info); create_extra_data(name); set_club_changed(name); add_club(name, info); save_main(); return 1; } /* create_club() */ /** * This method changes the type of the club. * @param name the name of the club to change * @param type the new type of the club * @return 1 on success, 0 on failure * @see create_club() * @see disband_club() * @see query_club_type() */ int change_club_type(string name, int type) { int club_type; class club_info info; if (is_club(name)) { club_type = query_club_type(name); info = query_club_info(name); if (club_type != type) { info->type = (club_type & CLUB_FLAGS_MASK) | type; set_club_changed(name); create_extra_data(name); return 1; } } return 0; } /* change_club_type() */ /** * This method returns the club type of the club. * @param name the name of the club to get the type of * @return the type of the club * @see change_club_type() * @see create_club() */ int query_club_type(string name) { class club_info data; if (is_club(name)) { data = query_club_info(name); if (!data) { disband_club(name); } else { return data->type & CLUB_TYPE_MASK; } } } /* query_club_type() */ /** * This method makes a clubs membership secret. * @param name the name of the club to make secret * @return 1 on success, 0 on failure * @see query_club_secret() * @see reset_club_secret() */ int set_club_secret(string name) { class club_info data; if (is_club(name)) { data = query_club_info(name); data->type |= CLUB_SECRET_FLAG; set_club_changed(name); return 1; } return 0; } /* set_club_secret() */ /** * This method makes a clubs membership open. * @param name the name of the club to make open * @return 1 on success, 0 on failure * @see query_club_secret() * @see set_club_secret() */ int reset_club_secret(string name) { class club_info data; if (is_club(name)) { data = query_club_info(name); data->type &= ~CLUB_SECRET_FLAG; set_club_changed(name); return 1; } return 0; } /* reset_club_secret() */ /** * This method checks to see if the club is secret or not. * @param name the name of the club to check for secrecy * @return 1 if the club is secret, 0 if not * @see set_club_secret() */ int query_club_secret(string name) { class club_info data; if (is_club(name)) { data = query_club_info(name); return (data->type & CLUB_SECRET_FLAG) != 0; } } /* query_club_secret() */ /** * This method returns the region of the club. * @param name the name of the club to check * @return the club name */ string query_club_region(string name) { class club_info data; if (is_club(name)) { data = query_club_info(name); return data->region; } return 0; } /* query_club_region() */ /** * This method disbands the club. The club will be totaly zapped and * everything about it efficently munched. * @param name the name of the club to disband * @return 1 on success, 0 on failure * @see create_club() * @see check_extra_infromation() */ int disband_club(string name) { class club_info data; name = normalise_name(name); if (is_club(name)) { data = query_club_info(name); if (data) { log_file("CLUB", ctime(time()) + ": disbanded '" + this_object()->query_club_name(name) + "'; balance = " + this_object()->query_balance(name, CLUB_DEFAULT_ACCOUNT_NAME) + "; fees due = " + ctime(this_object()->query_time_fees_due(name)) + "\n"); } else { log_file("CLUB", ctime(time()) + " disbanded '" + name + "' " "which has a bad data file.\n"); } map_delete(_club_names, name); map_delete(_cache, name); unguarded( (: rm(SAVE_FILE_DIR + $(name)) :) ); save_main(); send_observer_event("club_event_disband_club", name); return 1; } return 0; } /* disband_club() */ /** * This method returns the names of all the clubs currently in the list * of clubs. * @return the list of current clubs * @see create_club() * @see disband_club() */ string *query_clubs() { return keys(_club_names); } /* query_clubs() */ /** * This method returns the insignia object associated with the club. * @param name the name of the club for the insignia object * @return the path of the club insignia object * @see create_club() */ string query_insignia_path(string name) { return "/obj/misc/club_badge"; } /* query_insignia_path() */ /** * This method touches the club and resets the timeout date. This should * be done now and then by the club to make sure it does not timeout. * @param name the name of the club to reset the timeout for * @see check_clubs() */ void touch_club(string name) { class club_info data; if (is_club(name)) { data = query_club_info(name); data->last_touched = time(); set_club_changed(name); } } /* touch_club() */ /** * This method returns the recruiters of the club. * @param name the club name to get the recruiters of * @return the recruiters of the club * @see add_recruiter() * @see remove_recruiter() */ string *query_recruiters(string name) { class club_info data; if (is_club(name)) { data = query_club_info(name); return data->recruiters; } return ({ }); } /* query_recruiters() */ /** * This method returns the founder of the club. * @param name the club name to get the founder of * @return the founder of the club * @see create_club() */ string query_founder(string name) { class club_info data; if (is_club(name)) { data = query_club_info(name); return data->founder; } return 0; } /* query_founder() */ /** * This method returns the members of the club. * @param name the members of the club * @return the members of the club * @see add_member() * @see remove_member() */ string *query_members(string name) { class club_info data; if (is_club(name)) { data = query_club_info(name); return data->members; } return ({ }); } /* query_members() */ /** * This method adds a recruiter to the club. A recruiter can only be added if * they are already a member. * @param name the club name to add the recruiter to * @param recruiter the recruiter of the club to add * @return 1 on success, 0 on failure * @see remove_recruiter() * @see query_recruiters() * @see add_member() */ int add_recruiter(string name, string recruiter) { class club_info data; if (is_club(name)) { data = query_club_info(name); if (member_array(recruiter, data->members) != -1 && member_array(recruiter, data->recruiters) == -1) { data->recruiters += ({ recruiter }); set_club_changed(name); touch_club(name); if (!is_family(name)) { send_broadcast_message(name, capitalize(recruiter) + " becomes a recruiter for the club."); } return 1; } } return 0; } /* add_recruiter() */ /** * This method adds a member to the club. * @param name the name of the club to add the recruiter to * @param member the member of the club to add * @return 1 on success, 0 on failure * @see add_recruiter() * @see query_recruiters() * @see query_members() * @see remove_member() */ int add_member(string name, string member) { class club_info data; if (is_club(name)) { data = query_club_info(name); if (member_array(member, data->members) == -1) { data->members += ({ member }); set_club_changed(name); touch_club(name); if (!is_family(name)) { send_broadcast_message(name, capitalize(member) + " joins the club."); } return 1; } } return 0; } /* add_member() */ /** * This method removes a member from the club. * @param name the name of the club to remove a member from * @param member the members name to remove * @return 1 on success, 0 on failure * @see query_members() * @see add_member() * @see remove_member() */ int remove_member(string name, string member) { class club_info data; if (is_club(name)) { data = query_club_info(name); if (member_array(member, data->members) != -1) { if (is_recruiter_of(name, member)) { remove_recruiter(name, member); } data->members -= ({ member }); set_club_changed(name); check_extra_information(name, member, 0); if (!is_family(name)) { send_broadcast_message(name, capitalize(member) + " leaves the club."); } send_observer_event("club_event_remove_member", name, member); return 1; } } return 0; } /* remove_member() */ /** * This method removes a recruiter from the club. * @param name the name of the club to remove the member from * @param recruiter the recruiter to remove * @return 1 on success, 0 on failure * @see add_recruiter() * @see query_recruiters() * @see add_member() */ int remove_recruiter(string name, string recruiter) { class club_info data; if (is_club(name)) { data = query_club_info(name); if (member_array(recruiter, data->recruiters) != -1) { data->recruiters -= ({ recruiter }); set_club_changed(name); if (!is_family(name)) { send_broadcast_message(name, capitalize(recruiter) + " stops being a recruiter for the club."); } return 1; } } return 0; } /* remove_recruiter() */ /** * This method returns the capitalised and un messed name of the club. * @param club_name the name of the club * @return the un messed name of the club * @see is_club() */ string query_club_name(string club_name) { class club_info data; if (is_club(club_name)) { data = query_club_info(club_name); if (!data) { return club_name; } return data->actual_name; } return 0; } /* query_club_name() */ /** * This method returns the description of the club. * @param club_name the name of the club to get the description of * @return the club description, 0 if the club is not found * @see query_club_name() * @see create_club() * @see set_club_description() */ string query_club_description(string club_name) { class club_info data; if (is_club(club_name)) { data = query_club_info(club_name); return data->description; } return 0; } /* query_club_description() */ /** * This method sets the description of the club. * @param club_name the name of the club to set the description of * @param description the new description of the club * @return 1 on success, 0 on failure * @see query_club_description() * @see create_club() */ int set_club_description(string club_name, string description) { class club_info data; if (is_club(club_name)) { data = query_club_info(club_name); data->description = description; set_club_changed(club_name); return 1; } return 0; } /* set_club_description() */ /** * This method returns the time at which the club dues are again due. * @param club_name the name of the club to get the date for * @see check_clubs() * @see query_club_cost_per_period() */ int query_time_fees_due(string club_name) { class club_info data; if (is_club(club_name)) { data = query_club_info(club_name); return data->last_paid + CLUB_PAY_PERIOD; } return 0; } /* query_time_fees_due() */ /** * This method determines how much the club will cost to run each * pay period. * @param club_name the name of the club to get the fees for * @return the amount the club will cost in the next pay period * @see query_time_fees_due() */ int query_club_cost_per_period(string club_name) { if (is_club(club_name)) { return CLUB_COST_PER_YEAR + sizeof(query_members(club_name)) * CLUB_COST_PER_MEMBER_PER_YEAR; } return 0; } /* query_club_cost_per_period() */ /** * This method checks to see if the specified club exists. * @param name the name of the club to check for existance * @return 1 if it is a club, 0 if not * @see query_club_name() */ int is_club(string name) { name = normalise_name(name); if (!undefinedp(_club_names[name])) { return 1; } return 0; } /* is_club() */ /** * This method checks to see if the specified club exists and is an * elected club. * @param name the name of the club to check to see for an elected type * @return 1 if the club is an elected type */ int is_elected_club(string name) { name = normalise_name(name); if (is_club(name)) { return query_club_type(name) == CLUB_ELECTED; } return 0; } /* is_elected_club() */ /** * This method checks to see if the specified club exists and is an * personal club. * @param name the name of the club to check to see for an personal type * @return 1 if the club is an personal type */ int is_personal_club(string name) { if (is_club(name)) { return query_club_type(name) == CLUB_PERSONAL; } return 0; } /* is_personal_club() */ /** * This method checks to see if the club type is actually a family. * @param name the name of the family to check * @return 1 if the club is a family */ int is_family(string name) { if (is_club(name)) { return query_club_type(name) == CLUB_FAMILY; } return 0; } /* is_family() */ /** * This method will determine if the specified person is a member of the * club. * @param name the name of the club to find the member of * @param member the member to check for the existance of * @return 1 if they are a member, 0 if they are not */ int is_member_of(string name, string member) { if (is_club(name)) { return member_array(member, query_members(name)) != -1; } return 0; } /* is_member_of() */ /** * This method will determine if the specified person is a recruiter of the * club. * @param name the name of the club to find the recruiter of * @param recruiter the person is check for the recruiter * @return 1 if they are a recruiter, 0 if they are not * @see add_recruiter() * @see remove_recruiter() */ int is_recruiter_of(string name, string recruiter) { if (is_club(name)) { return member_array(recruiter, query_recruiters(name)) != -1; } return 0; } /* is_recruiter_of() */ /** * This method will determine if the specified person is the founder of * the club. * @param name the name of the club to check the founder of * @param founder the person to check for being the founder * @return 1 if they are in the position, 0 if not * @see create_club() */ int is_founder_of(string name, string founder) { name = normalise_name(name); if (is_club(name)) { return query_founder(name) == founder; } return 0; } /* is_founder_of() */ /** * This method creates an account in the club. * @param name the name of the club * @param account the name of the account */ int create_account(string name, string account) { class club_info data; if (!account) { account = CLUB_DEFAULT_ACCOUNT_NAME; } if (is_club(name)) { data = query_club_info(name); if (undefinedp(data->accounts[account])) { data->accounts[account] = 0; touch_club(name); set_club_changed(name); return 1; } } return 0; } /* create_account() */ /** * This method will pay a certain amount of money to club. This will be * how long the club is payed until. The club will cost a certain * amount for each member as well as a base cost. * @param name the name of the club * @param amount the amount to change the balance by * @param type the tyope of the transaction * @param person the person removeing the money * @param account the account the money is coming from * @return the amount of money not able to be placed in the account * @see remove_money() * @see query_balance() * @see query_transactions() */ int add_money(string name, int amount, int type, string person, string account) { class club_info data; if (!account) { account = CLUB_DEFAULT_ACCOUNT_NAME; } if (is_club(name) && amount > 0) { data = query_club_info(name); if (!undefinedp(data->accounts[account])) { data->accounts[account] += amount; touch_club(name); set_club_changed(name); return 1; } } return 0; } /* add_money() */ /** * This method removes money from the account. * @param name the name of the club * @param amount the amount to change the balance by * @param type the tyope of the transaction * @param person the person removeing the money * @param account the account the money is coming from * @return 1 if the removal is a success * @see pay_money() * @see query_balance() * @see query_transactions() */ int remove_money(string name, int amount, int type, string person, string account) { class club_info data; if (!account) { account = CLUB_DEFAULT_ACCOUNT_NAME; } if (is_club(name) && amount > 0) { data = query_club_info(name); if (!undefinedp(data->accounts[account])) { data->accounts[account] -= amount; touch_club(name); set_club_changed(name); return 1; } } return 0; } /* remove_money() */ /** * This method returns the balance of the club. * @param name the name of the club * @param account the name of the account * @return the current balance of the club * @see pay_money() * @see remove_money() */ int query_balance(string name, string account) { class club_info data; if (!account) { account = CLUB_DEFAULT_ACCOUNT_NAME; } if (is_club(name)) { data = query_club_info(name); return data->accounts[account]; } return 0; } /* query_balance() */ /** * This method returns the names of all the accounts in the club. * @param club_name the name of the club * @return the names of all the accounts */ string* query_account_names(string name) { class club_info data; if (is_club(name)) { data = query_club_info(name); if (data) { return keys(data->accounts); } } return ({ }); } /* query_account_names() */ /** * This method checks to see if the account exists for the club. * @param club_name the name of the name * @param account the name of the account to checlk * @return 1 if it exists, 0 if it does not */ int is_account_of(string club_name, string account) { return member_array(account, query_account_names(club_name)) != -1; } /* is_account_of() */ /** * This method determines if the club is a creator club or not. A * club is considered a creator club if the founder is a creator. * @param club_name * @return 1 if is a creator club, 0 if not */ int is_creator_club(string club_name) { if (is_club(club_name)) { if (PLAYER_HANDLER->test_creator(query_founder(club_name))) { return 1; } } return 0; } /* is_creator_club() */ /** * This method checks to see if the specified thingy is an observer. * @param obs the observer to check * @return 1 on success, 0 on failure */ int is_observer(string obs) { if (member_array(obs, _observers) != -1) { return 1; } return 0; } /* is_observer() */ /** * Adds an objec to the list to be informed of changes about the * clubs. * @param obs the name of the object to inform of changes * @return 1 on success, 0 on failure */ int add_observer(string obs) { if (!is_observer(obs) && file_size(obs) > 0) { _observers += ({ obs }); save_main(); return 1; } return 0; } /* add_observer() */ /** * This method removes an observer. * @param obs the obeserver to remove * @return 1 on success, 0 on failure */ int remove_observer(string obs) { if (is_observer(obs)) { _observers -= ({ obs }); save_main(); return 1; } return 0; } /* remove_observer() */ /** * This method returns the current list of observers. * @return the current list of observers */ string *query_observers() { return _observers; } /* query_observers() */ /** * This method calls a function on all the observers to tell them * when an event has taken place. * @param event_name the name of the event * @param args the arguments to the event */ protected void send_observer_event(string event_name, string *args ...) { string bing; foreach (bing in _observers) { if (file_size(bing) > 0) { call_out((: call_other($1, $2, $3 ...) :), 0, event_name, bing, args ...); } else { remove_observer(bing); } } } /* send_observer_event() */ /** * This method sends a broadcast to the clubs talker channel. * @param club the name of the club to send the message to * @param mess the message to send */ protected void send_broadcast_message(string club, string message) { BROADCASTER->broadcast_to_channel(this_object(), lower_case(query_club_name(club)), ({ message, 0 })); } /* send_club_message() */ /** * This method returns all the stats of the object. Things about cache * hits and stuff. * @ignore yes */ mixed *stats() { return ({ ({ "cache hits", _no_cache_hits }), ({ "cache requests", _no_cache_requests }), ({ "cache miss", _no_cache_miss }), ({ "percentage", _no_cache_hits * 100 / _no_cache_requests }), }); } /* stats() */