/** * The main player object. Does all the player related stuff. * @author Pinkfish * @started December 1991 */ #include <config.h> #include <board.h> #include <drinks.h> #include <living.h> #include <command.h> #include <player.h> inherit "/global/player/line_ed"; inherit "/global/player/auto_load"; inherit "/global/player/events"; inherit "/global/player/log"; inherit "/global/player/more_string"; inherit "/global/player/finger"; inherit "/global/player/pweath"; inherit "/std/living/living"; inherit "/global/player/psoul"; inherit "/global/player/guild-race"; inherit "/global/player/more_file"; inherit "/global/player/path"; inherit "/global/player/start_position"; inherit "/global/player/family"; inherit "/global/player/channels"; #define MIN_TIME_TO_SAVE 60 #define LEVEL_CACHE 60 class player_data { int _hb_num; int _level; int _level_time; object _snoopee; string _my_ident; mapping _titles; int _update_tmps_call_out; int _save_inhibit; int _last_save; int _quitting; } /* player_data() */ private int time_on; private int channel_silenced; private int all_silenced; private int max_deaths; private int monitor; private int refresh_time; private int start_time; private int creator; private int deaths; private int last_log_on; private int no_logins; private int activity_counter; private string last_on_from; private nosave class player_data _player_data; void start_player(); void public_commands(); int save(); void do_load_auto(); void set_name(string str); void continue_start_player(); int restart_heart_beat(); int brief_verbose(string, string); int quit_alt(); int review(); int toggle_wimpy(string); int do_refresh(int); void create() { if( query_name() ) return; living::create(); events::create(); psoul::create(); channels::channel_commands(); line_ed::create(); more_string::create(); start_position::create(); add_property("determinate", ""); _player_data = new( class player_data ); _player_data->_save_inhibit = 1; _player_data->_titles = ([ ]); time_on = time(); start_time = time(); last_log_on = time(); seteuid("PLAYER"); Str = 8; Dex = 8; Int = 8; Con = 8; Wis = 8; sp = 50; max_sp = 50; wimpy = 20; max_deaths = 7; set_desc(0); add_ac("bing", "blunt", 15 ); add_ac("bing2", "sharp", 15 ); add_ac("bing3", "pierce", 15 ); add_property("player", 1 ); set_cols(79); set_rows(24); race_ob = CONFIG_DEFAULT_RACE; sscanf( file_name(TO), "%s#", my_file_name ); add_language("common"); } /* create() */ /** @ignore yes */ void dest_me() { if( query_name() != "object" && objectp(TP) && TP != TO && file_name(PO) != SHUTDOWN_H && base_name(TP) != LOGIN_OBJ ) { user_event( TO, "inform", CAP( TP->query_name() )+" dests "+ query_cap_name(), "dest"); tell_object( TO, "You were destructed by "+ CAP( TP->query_name() )+".\n"); } CHANNEL_H->remove_user(); ::dest_me(); } /* dest_me() */ /** * This method stops the player from being able to save. This is done while * the inventory is regenerating to stop problems with inventory loss due * to logging off before it has finished. * @see allow_save() * @see query_save_inhibit() */ void disallow_save() { _player_data->_save_inhibit = 1; } /** * This method allows the player to save again after they have been * disallowed. * @see disallow_save() * @see query_save_inhibit() */ void allow_save() { _player_data->_save_inhibit = 0; } /** * This method returns the value of the disallowing save variable. If this * is a non-zero value then the player must not be saved. * @see allow_save() * @see disallow_save() */ int query_save_inhibit() { return _player_data->_save_inhibit; } private int set_quitting() { _player_data->_quitting = 1; } /** * This method queries whether or not the player is in the * middle of quitting. * @return 1 if we are quitting, 0 if not */ int query_quitting() { return _player_data->_quitting; } /** * This method queries whether or not the player has been silenced. * @return 1 if we are silenced, 0 if not */ int query_silenced() { return all_silenced; } /** * This method silences the player. * @return 1 upon success, 0 upon failure */ int set_silenced() { if( !adminp( previous_object(-1) ) || creatorp(TO) ) return 0; all_silenced = 1; return 1; } /* set_silenced() */ /** * This method removes the silencing from the player. * @return 1 upon success, 0 upon failure */ int unset_silenced() { if( !adminp( previous_object(-1) ) || creatorp(TO) ) return 0; all_silenced = 0; return 1; } /* unset_silenced() */ int query_channel_silenced() { return channel_silenced; } int set_channel_silenced() { if( !adminp( previous_object(-1) ) || creatorp(TO) ) return 0; channel_silenced = 1; return 1; } /* set_channel_silenced() */ int unset_channel_silenced() { if( !adminp( previous_object(-1) ) || creatorp(TO) ) return 0; channel_silenced = 0; return 1; } /* unset_channel_silenced() */ /** * This is the date on which the character was started. The very first * time the player ever logged on. * @return the time at which the player first logged on */ int query_start_time() { return start_time; } /** * This method returns the current capitalised name of the player. * @return the capitalised name */ nomask string query_cap_name() { return CAP( query_name() ); } /** * This method is called from within the login code to start up the * new player, set their name and move them into the correct * location on the mud. * @param bong the name of the player * @param new_flag are they a new player? * @param c_name their capitalized name * @param ident the ident from the authorisation code * @param go_invis whether or not we should go invis */ void move_player_to_start( string bong, int new_flag, string c_name, string ident, int go_invis ) { if( base_name(PO) != LOGIN_OBJ ) { write("You don't have clearance to do that.\n"); return; } seteuid( "Root" ); set_name( bong ); _player_data->_my_ident = ident; if( !new_flag ) unguarded( (: restore_object, LOGIN_OBJ->query_player_file_name( query_name() ), 1 :) ); // Reload timed properties. reload_player_properties(); disallow_save(); // Don't allow saves until the autoload has finished. add_property("player", 1 ); set_short( query_cap_name() ); add_property("determinate", ""); if( !query_cols() ) set_cols(79); if( creatorp(TP) ) { seteuid( query_name() ); if( file_size ("/w/"+ query_name() +"/cmds" ) == -2 ) AddSearchPath( ({ "/w/"+ query_name() +"/cmds" }) ); AddSearchPath( ({ DIR_PLAYER_CMDS, DIR_SECURE_PLAYER_CMDS, DIR_CREATOR_CMDS, DIR_SECURE_CREATOR_CMDS,DIR_PENDING_CMDS }) ); } else { seteuid( "PLAYER" ); AddSearchPath( ({ DIR_PLAYER_CMDS, DIR_SECURE_PLAYER_CMDS }) ); } if( playtesterp(TP) ) AddSearchPath( ({ DIR_PLAYTESTER_CMDS}) ); no_logins++; if( time_on > 0 ) time_on = 0; time_on += time(); printf("You last logged in from %s.\n", last_on_from ); last_on_from = query_ip_name(TO)+" ("+query_ip_number(TO)+")"; if( new_flag ) add_property("new player!", 1 ); LOGIN_H->player_logon( bong, query_property("guest"), ( query_ip_name() ? query_ip_name() : query_ip_number() ) ); write( LOGIN_H->get_message("/doc/NEWS") ); call_out( (: continue_start_player :), 0 ); } /* move_player_to_start() */ /** @ignore yes */ protected void move_to_start_pos() { ::move_to_start_pos(); if( query_property("new player!") ) remove_property("new player!"); command( ( query_verbose("look") ? "look" : "glance" ) ); if( !finger_set() ) write("%^BOLD%^Please set your finger information with " "'chfn'.%^RESET%^\n"); no_time_left(); START_PLAYER->logged_on_times(no_logins); } /* move_to_start_pos() */ /** * @ignore yes * This function tracks a players activity to determine whether they're still * active or not. */ protected void update_activity( int logon ) { int offline; // Lower their activity score if they haven't been on for // more than a week. if( last_log_on > 0 ) { offline = time() - last_log_on; if( offline > 0 ) activity_counter -= ( offline / (3600 * 24 * 7) ) * 10; } if( activity_counter <= -55 ) activity_counter = -55; // When they logon give them a couple more activity points. // Give them some more for each hour they were on when they leave. activity_counter += ( logon ? 3 : 2 * ( (time() - last_log_on ) / 3600) ); // Set minima and maxima to the counter. if( activity_counter >= 0 ) activity_counter = 0; last_log_on = time(); catch( PLAYER_H->delete_from_cache( query_name() ) ); } /* update_activity() */ /** * Return the current value of this players activity_counter * (used for debugging) */ int query_activity_counter() { return activity_counter; } /** @ignore yes */ private void check_announcements() { mapping news_rc; mixed stuff; if( !news_rc = BOARD_H->query_newsrc( query_name() ) ) news_rc = ([ ]); stuff = BOARD_H->get_subjects("announcements"); if( sizeof(stuff) && stuff[<1][B_NAME] != query_cap_name() && stuff[<1][B_TIME] > news_rc["announcements"] ) write("\nThere are %^YELLOW%^NEW%^RESET%^ announcements. " "You can read them with \"news\".\n\n"); } /* check_announcements() */ /** @ignore yes */ void continue_start_player() { string title; start_player(); no_time_left(); call_out( (: move_to_start_pos :), 0 ); // Remove passed out property, unless the player is // in a trance, e.g. when contemplating. if( query_property(PASSED_OUT) && !query_property(TRANCE) ) call_out( (: remove_property :), 10 + random(30), PASSED_OUT ); // Check to see if the player has the afk flag and if so remove it and the // afk message if( query_property("afk") ) { remove_property("afk"); remove_property("afk_string"); } remove_property(UNKNOWN_MOVE); remove_property(RUNNING_MOVE); // for effects. init_after_save(); printf("%s", fix_string( MAIL_H->new_mail( query_name() ) ) ); check_announcements(); if( query_property("dead") ) clone_object(DEATH_SHADOW)->setup_shadow( TO ); if( query_property("noregen") ) DEATH->person_died( query_name() ); exec_alias("login", ""); update_activity(1); title = LIBRARY_H->query_title( query_name() ); if( stringp(title) ) _player_data->_titles += (["quest" : title ]); if( query_property("new player!") ) { user_event( TO, "newbie", query_cap_name()+" arrives on "+ mud_name()+" for the first time!"); set_last_pos(CONFIG_NEWBIE_START_LOCATION); START_PLAYER->start_player(); } add_language("common"); check_family_name(); } /* continue_start_player() */ /** @ignore yes */ void start_player() { int number; if( creator && my_file_name != PLAYER_OBJ ) { TP->all_commands(); TP->app_commands(); TP->wiz_commands(); } call_out( (: do_load_auto :), 1 ); reset_get(); enable_commands(); public_commands(); channel_commands(); parser_commands(); force_commands(); race_guild_commands(); soul_commands(); event_commands(); finger_commands(); communicate_commands(); living_commands(); logging_commands(); editor_commands(); set_no_check(1); if( TP->query_property("stats_rearranged") ){ set_con(Con); set_dex(Dex); set_int(Int); set_str(Str); set_wis(Wis); } else { set_con(11); set_dex(11); set_int(11); set_str(11); set_wis(11); add_known_command("rearrange"); } reset_all(); set_current_path( query_home_dir() ); set_heart_beat(1); if( wimpy > 100 ) wimpy = 25; if( contmp || dextmp || inttmp || strtmp || wistmp ) _player_data->_update_tmps_call_out = call_out( (: update_tmps :), 900 ); number = max_sp - query_real_max_sp(); if( number > 0 ) max_sp -= number; BIRTHDAY_H->check_player(TO); } /* start_player() */ /** @ignore yes */ int adjust_hp( int number, object attacker, object weapon, string attack ) { if( !interactive( TO ) ) return query_hp(); return ::adjust_hp( number, attacker, weapon, attack ); } /* adjust_hp() */ /** * Starts the autoloading process. This is called just after a player * logs in to start up the auto loading process. */ void do_load_auto() { load_auto_load_alt( query_auto_load_string(), TO, TO, (: tell_object( TO, "\n%^CYAN%^Inventory regeneration complete.%^RESET%^\n") :) ); set_auto_load_string(0); allow_save(); } /* do_load_auto() */ /** @ignore yes */ void public_commands() { add_command("restart", TO, "", (: restart_heart_beat() :) ); add_command("save", TO, "", (: save() :) ); add_command("quit", TO, "", (: quit_alt() :) ); add_command("review", TO, "", (: review() :) ); add_command("wimpy", TO, "", (: toggle_wimpy(0) :) ); add_command("wimpy", TO, "<word'number'>", (: toggle_wimpy($4[0]) :) ); add_command("refresh", TO, "", (: do_refresh(0) :) ); add_command("refresh", TO, "me", (: do_refresh(1) :) ); } /* public_commands() */ /** * Force the player to glance. Forces the player to glance in a * certain direction. * @param str the direction to glance in * @return 0 ifg the command failed, non zer if it succeeded */ int glance( string str ) { return command("glance" + ( str ? " "+str : "" ) ); } /* glance() */ /** * Force the player to look. Forces the player to look at something * or just around. If the input is set to "" then they look around. * @param str what to look at * @return 0 if it failed, non zero on success * @see efun::command() */ int look_me(string str) { return command( str ? "look" + str : "look"); } /* look_me() */ /** @ignore yes */ int review() { write("Entry : " + query_msgin() + "\n"); write("Exit : " + query_msgout() + "\n"); write("MEntry : " + query_mmsgin() + "\n"); write("MExit : " + query_mmsgout() + "\n"); write("Editor : " + query_editor() + "\n"); return 1; } /* review() */ /** * Forces the player to examine something. This does the same as the look_me * function. * @see look_me() * @return 0 on a failure, non zero on success. * @param arg the thing to look at. */ int examine( string arg ) { return look_me( arg ? " at "+arg : "" ); } /* examine() */ /** @ignore yes */ varargs string short( int dark, int verbose ) { string str, family, title; str = ( !interactive(TO) ? "the net dead statue of " : "" ); family = query_family_name(); family = ( family ? " "+family : "" ); if( verbose ) title = query_player_title(); title = ( title ? title + " " : "" ); return str + title + living::short( dark ) + family; } /* short() */ /** * This method changes the current value of the wimpy variables. * @param str the new value of the wimpy variable * @return 0 on failure and 1 on success */ int toggle_wimpy( string str ) { int number; if( !str ) { write( ( wimpy ? "You are in wimpy mode, you will run away at "+ wimpy+"% of your max hps.\n" : "You are in brave mode.\n" ) ); write("Usage: wimpy <num> (Caution! Please read help wimpy!)\n"); return 1; } if( sscanf( str, "%d", number ) != 1 ) return notify_fail("You must set your wimpy to a number.\n"); if( number < 0 || number > 100 ) return notify_fail("Your wimpy must be between 0 and 100.\n"); TO->set_wimpy( number ); printf("Wimpy set to %d%%\n", wimpy ); return 1; } /* toggle_wimpy() */ /** * This method returns any extra information associated with the score. This * method will mostly be overridden by shadows which wish to place more * information into the score command. * @return the extra information to place in the score */ string extra_score() { return ""; } /** * This method queries whether or not we are old enough to be saved. * @return 1 if we are old enough to save, 0 if too young */ nomask int query_old_enough_to_save() { return -TO->query_time_on() > MIN_TIME_TO_SAVE; } /* query_old_enough_to_save() */ /** * This method is called to save the player to disc. It will print * a message to the player to inform them of the face they have * been saved. * @return always returns 1 * @see save_me() */ nomask int save() { if( query_property("guest") ) { if( query_verb() == "save" ) tell_object( TO, "Not saving for guests... Sorry.\n"); return 1; } if( !query_old_enough_to_save() ) { if( query_verb() == "save") tell_object( TO, "Sorry, you must be at least "+ time_string(MIN_TIME_TO_SAVE)+" old to save.\n"); return 1; } if( my_file_name == PLAYER_OBJ || query_verb() == "save") { if( query_verb() == "save") { if( _player_data->_last_save + MIN_SAVE_REPEAT > time() ) { tell_object( TO, "Please use this command sparingly or it " "lags the mud.\n"); return 1; } _player_data->_last_save = time(); } tell_object( TO, "Saving...\n"); } TO->save_me(); return 1; } /* save() */ /** * Saves the player. Saves the player to disc, doing all the autoload * stuff and such like. * @see save() */ void save_me() { mixed old; if( query_property("guest") ) { if( query_verb() == "save") tell_object( TO, "Not saving for guests... Sorry.\n" ); return; } if( !query_old_enough_to_save() ) { if( query_verb() == "save") tell_object( TO, "Sorry, you must be at least "+ time_string(MIN_TIME_TO_SAVE)+" old to save.\n"); return; } if( query_auto_loading() || query_save_inhibit() ) return; if( race_ob ) race_ob->player_save(TO); old = geteuid(); check_last_pos(); // For effects. effect_freeze(); effects_saving(); // Recreating the autoload stuff. create_auto_load( all_inventory(), 1 ); if( interactive(TO) ) last_on_from = query_ip_name(TO)+" ("+query_ip_number(TO)+")"; // Keep the time_on calculation as close to the saving as possible. time_on -= time(); // Save timed properties. save_player_properties(); catch( unguarded( (: save_object, LOGIN_OBJ->query_player_file_name( query_name() ), 1 :) ) ); // Reload timed properties. reload_player_properties(); time_on += time(); ITEM_H->save_all_item_states( TO->query_name() ); effect_unfreeze(); // Clear the autoload string after saving to save memory. set_auto_load_string(0); } /* save_me() */ void save_with_auto_load( mixed al ) { mixed old; if( query_property("guest") ) { if( query_verb() == "save") tell_object( TO, "Not saving for guests... sorry.\n"); return; } if( !query_old_enough_to_save() ) { if( query_verb() == "save") tell_object( TO, "Sorry, you must be at least "+ time_string(MIN_TIME_TO_SAVE)+" old to save.\n"); return; } if( query_save_inhibit() ) return; if( race_ob ) race_ob->player_save(TO); old = geteuid(); // For effects. effect_freeze(); effects_saving(); set_auto_load_string(al); // Put the time_on calculation as close to the save as possible. time_on -= time(); // Save timed properties. save_player_properties(); catch( unguarded( (: save_object, LOGIN_OBJ->query_player_file_name( query_name() ), 1 :) ) ); // Reload timed properties. reload_player_properties(); time_on += time(); effect_unfreeze(); } /* save_with_auto_load() */ /** * This is the quit code called by the command 'quit' * @return 1 if successful, 0 if failed * @see quit() * @see do_quit() */ int quit_alt() { object frog; if( ENV(TO) && file_name(ENV(TO)) == "/room/departures") { if( TO == TP ) tell_object( TO, "You cannot quit while already in the " "departures room.\n"); return 1; } if( query_auto_loading() ) { if( !interactive(TO) ) dest_me(); else tell_object( TO, "You cannot quit yet: your inventory is still " "being generated.\n"); return 1; } if( sizeof( filter( (object *)TO->query_attacker_list(), (: living( $1 ) :) ) ) && TP == TO && interactive(TO) ) { tell_object( TO, "You cannot quit while in combat. Use 'stop' to " "stop fighting.\n"); return 1; } set_quitting(); check_last_pos(); update_activity(0); // Added to help people tell when the player quits. catch( TO->event_quit(TO) ); catch( editor_check_do_quit() ); if( query_old_enough_to_save() && !query_property("guest") ) tell_object( TO, "Saving your inventory.\n"); event( ENV(TO), "see", query_cap_name()+" leaves the game.\n", TO, ({ TO }) ); catch( move("/room/departures") ); user_event( TO, "inform", query_cap_name()+" leaves "+mud_name(), "logon", TO ); catch( LOGIN_H->player_logout( query_name() ) ); if( race_ob ) catch( race_ob->player_quit(TO) ); frog = clone_object("/obj/monster/greco"); frog->move("/room/departures"); frog->get_rid_of(TO); return 1; } /* quit_alt() */ /** * quits the player. * @return zero if it failed, non zero on success * @see quit() * @see quit_alt() */ int do_quit() { return quit_alt(); } /* do_quit() */ /** * A forced quit. * This is the function called by external objects to quit * a player. It avoids checks for things like being in combat. * @return 0 if it failed, non zero if it succeeded. * @see do_quit() * @see quit_alt() * @see efun::command() */ int quit() { set_quitting(); check_last_pos(); update_activity(0); catch( editor_check_do_quit() ); // Added to help people tell when the player quits. catch( TO->event_quit(TO) ); event( ENV( TO ), "see", query_cap_name()+" leaves the game.\n", TO, ({ TO }) ); user_event( TO, "inform", query_cap_name()+" leaves "+mud_name(), "logon", TO ); tell_object( TO, "Thanks for playing. See you next time.\n"); catch( LOGIN_H->player_logout( query_name() ) ); if( race_ob ) catch( race_ob->player_quit(TO) ); if( query_auto_loading() || query_save_inhibit() ) { tell_object( TO, "Forced to quit whilst auto loading. " "Not saving...\n"); if( catch( dest_me() ) ) destruct(TO); return 1; } if( !query_old_enough_to_save() || query_property("guest") ) { if( catch( dest_me() ) ) destruct(TO); return 1; } catch( save_me() ); catch( INV( TO )->set_tracked_item_status_reason("QUIT") ); catch( INV( TO )->dest_me() ); catch( ITEM_H->save_all_item_states_delete_cache( TO->query_name() )); effects_quiting(); if( catch( dest_me() ) ) destruct(TO); return 1; } /* quit() */ /** * This method returns the mapping of all the title associated with this * player. * @return the current mapping of titles * @see query_title() * @see set_title() * @see remove_title() */ mapping query_titles() { return copy( _player_data->_titles ); } /** * This method returns the list of titles associated with this player. * @return the list of titles * @see query_titles() * @see set_title() * @see remove_title() */ string query_title() { return implode( values( _player_data->_titles ), ", " ); } /* query_title() */ /** * This method sets the current title type to be of a certain name. * @param type the type of title to set * @param title the string to set it to * @see query_title() * @see set_title() * @see remove_title() */ void set_title( string type, string title ) { _player_data->_titles[ type ] = title; } /* set_title() */ /** * This method removes the title of the given type from the title mapping. * @param type the type of title to remove * @see query_title() * @see set_title() * @see remove_title() */ void remove_title( string type ) { map_delete( _player_data->_titles, type ); } /* remove_title() */ /** * This method returns the number of times the player has died. * @return the number of times the player has died * @see adjust_deaths() * @see adjust_max_deaths() */ int query_deaths() { return deaths; } /** * This method changes the number of times the player has died. * @see query_deaths() * @see adjust_max_deaths() */ void adjust_deaths(int i) { deaths += i; } /** @ignore yes */ protected void set_name( string str ) { if( query_name() && query_name() != "object" ) return; ::set_name(str); set_living_name( query_name() ); set_main_plural( query_name() ); } /* set_name() */ /* No idea what these are doing here? */ /** @ignore yes */ void init_static_arg( mapping map ) { return; } /** @ignore yes */ void init_dynamic_arg( mapping map ) { return; } /** @ignore yes */ string long( string str, int dark ) { string s, tmp, title; title = query_player_title(); title = ( title ? title+" " : "" ); s = "You see "+ title + query_short(); if( tmp = query_family_name() ) s += " " + query_family_name(); s += " "+TITLE_H->query_title(TO)+".\n"; if( race_ob ) s += (string)race_ob->query_desc(TO); if( dark == 2 || dark == -2 ) { s += "It is too "; if( dark == 2 ) s += "dark"; else s += "bright"; s += " to see "+HIS+" description or what "+HE+" is wearing or " "carrying.\n"; s += calc_extra_look(); } else { if( query_desc() && query_desc() != "" ) s += CAP(HE) + " " + query_desc() + "\n"; s += CAP(HE) + " " + health_string() + ".\n"; s += CAP(HE) + " is " + query_position_short() + ".\n"; if( query_property( PASSED_OUT ) ) s += CAP(HE)+" appears to be passed out.\n"; s += calc_extra_look(); s += weather_extra_look(); s += query_living_contents(0); } return s; } /* long() */ /** * This method is called when the player dies. It creates the corpse * and handles all the bits and pieces that should be dealth with * when a player dies. * <p> * If this method returns 0 then the automatic death handling code * in the living object is used instead of this. * @return always returns 1 */ mixed second_life() { object corpse; add_property( "dead", time() ); if( !DEATH->someone_died( TO ) ) { if( !creatorp(TO) && !query_property("no die") ) deaths++; } else { add_property( PK_KILLED, 1 ); } effects_thru_death(); corpse = make_corpse(); reset_protectors(); if( deaths > max_deaths ) { tell_object( TO, "You have died your final death. "+ "Your name will be inscribed in the Hall of Legends.\n"); shout( query_cap_name()+" has died for the last time. "+ "All mourn "+HIS+" passing.\n" ); LIBRARY_H->complete_death( query_name() ); corpse->add_property("Died Last Time", time() ); corpse->set_decay( 0 ); } corpse->move( environment() ); call_out( (: save_me :), 0 ); DEATH->person_died( query_name() ); clone_object( DEATH_SHADOW )->setup_shadow( TO ); gp = 0; hp = 0; sp = 0; xp = 0; spells = ([ ]); contmp = dextmp = inttmp = strtmp = wistmp = 0; remove_call_out( _player_data->_update_tmps_call_out ); adjust_tmp_con( -2 ); adjust_tmp_dex( -2 ); adjust_tmp_int( -2 ); adjust_tmp_str( -2 ); adjust_tmp_wis( -2 ); return corpse; } /* second_life() */ /** * This method removes the ghost from the player. This stops them * being a ghost and turns them back into a real person. * @see second_life() */ void remove_ghost() { if( deaths > max_deaths ) { if( TP != TO ) { tell_object( TO, TP->query_cap_name()+" tried to raise you, but " "you are completely dead.\n"); tell_object( TP, query_cap_name()+" is completely dead, you " "cannot raise "+HIM+".\n"); return; } else { tell_object( TO, "You are completely dead. You cannot be " "raised.\n"); say( query_cap_name()+" struggles to appear in a solid form, but " "fails.\n"); return; } } remove_property("dead"); remove_property(PK_KILLED); tell_object( TO, "You reappear in a more solid form.\n"); say( query_cap_name()+" appears in more solid form.\n"); if( query_hp() < 0 ) set_hp( 1 ); set_personal_temp(0); TO->dest_death_shadow(); save_me(); all_inventory()->recovery_from_death(); } /* remove_ghost() */ /** * Called when the player goes net dead. This is called * by the driver when a player goes net dead. Turns them into * a statue and stuff. */ void net_dead() { if( !environment() || file_name( environment() ) == "/room/departures" ) return; if( query_name() == "guest" || query_name() == "root" ) { say( query_cap_name()+" vanishes in a puff of logic.\n"); quit(); } else { event( ENV(TO), "see", query_cap_name()+" goes white, looks very " "chalky and turns into a statue.\n", TO, ({ TO }) ); user_event( TO, "inform", query_cap_name()+" has lost "+HIS+" link", "link-death", TO ); check_last_pos(); LOGIN_H->player_net_dead( query_name() ); save_me(); } } /* net_dead() */ /** * Checks to see if they have idled out. This is called from within * the heart beat code. * @see heart_beat() */ void idle_out() { event( ENV(TO), "see", query_cap_name()+" has been idle for too long, "+ HE+" vanishes in a puff of boredom.\n", TO, ({ TO }) ); tell_object( TO, "You idled out, sorry.\n"); quit(); } /* idle_out() */ /** * The main heart beat function. This is called by the driver * every 2 seconds on the player. Does all the maintence * stuff like fixing up hps and stuff like that. */ void heart_beat() { int idle_time; flush_queue(); idle_time = ( !interactive(TO) ? time() - last_command : query_idle(TO) ); if( idle_time > FULL_IDLE ) { if( ( LOGIN_H->is_discworld_full() || idle_time > MAX_IDLE ) && ( !interactive(TO) || !creatorp(TO) ) ) { call_out( (: idle_out :), 2 ); } } if( !interactive(TO) ) { if( idle_time > 60 ) quit(); return; } living::heart_beat(); adjust_sp( 1 ); living::update_volumes(); // Reset it every hour to stop it rolling over. if( ++_player_data->_hb_num >= 1800 ) _player_data->_hb_num = 0; // Do the news check and personal temperature calculation // every few minutes. if( !( _player_data->_hb_num % 120 ) ) calc_personal_temp(); if( !( _player_data->_hb_num % 900 ) ) check_announcements(); } /* heart_beat() */ /** * Is the monitor turned on? The hit point monitor which is * displyed during combat... * @return 1 if it is on, 0 if not */ int query_monitor() { return monitor; } /** * Set the hit point minitor. This sets the value of the hit point * monitor flag. If it is set to 0, the mionitor is turned off. If * it is set to 1 the monitor is turned on. * @param i the new value for the hit point monitor flag */ void set_monitor( int i ) { monitor = i; } protected void write_prompt() { string prompt; int tmp, i; string stuff; if( !( monitor & 2 ) || !creatorp(TO) ) efun::tell_object( TO, "> "); else { prompt = ""; stuff = TP->query_property("prompt"); for( i = 0; i < strlen(stuff); i++ ) { switch( stuff[i] ) { case 'h': case 'H': // Hp % tmp = ( ( query_hp() * 100 ) / query_max_hp() ); prompt += "Hp:"; switch( tmp ) { case 50..100 : prompt += "%^GREEN%^"; break; case 20..49 : prompt += "%^YELLOW%^"; break; default : prompt += "%^RED%^"; } prompt += query_hp() + "%^RESET%^"; break; case 'g': case 'G': // Gp tmp = ( query_gp() * 100 ) / query_max_gp(); prompt += "Gp:"; switch( tmp ) { case 50..100 : prompt += "%^GREEN%^"; break; case 20..49 : prompt += "%^YELLOW%^"; break; default : prompt += "%^RED%^"; } prompt += query_gp() + "%^RESET%^"; break; case 's': case 'S': // Sp tmp = ( query_sp() * 100 ) / query_max_sp(); prompt += "Sp:"; switch( tmp ) { case 50..100 : prompt += "%^GREEN%^"; break; case 20..49 : prompt += "%^YELLOW%^"; break; default : prompt += "%^RED%^"; } prompt += query_sp() + "%^RESET%^"; break; case 'x': case 'X': prompt += "Xp:" + query_xp(); break; default: if( sizeof(prompt) > 0 && prompt[sizeof(prompt)-1] != stuff[i] ) prompt += sprintf( "%c", stuff[i] ); } } prompt += "> "; efun::tell_object( TO, fix_string(prompt) ); } } /* write_prompt() */ /** * Called when the player wimpies out of a place. This does all the run * away stuff and things like that. * @return 1 if succeeded in running away, 0 if not */ int run_away() { int number; become_flummoxed(); number = ::run_away(); if( number ) tell_object( TO, ( number ? "Your feet run away with you!" : "You try to run away, but no matter how you scrabble, you can't " "find any way out.")+"\n"); return number; } /* run_away() */ /** * @ignore yes * This is needed by the driver. */ int query_wizard() { return creator; } /** * The amount of time on line. This is the total amount of time online * in seconds from when they first started playing. The return from * this method is *negative*, you will need to make it positive to use it * most likely. * @return the total log on time in seconds (the return value is *negative*) */ nomask int query_time_on() { return time_on - time(); } /** * This method determins if the player is mature or not yet. It is based * on the amount of time on. * @return 1 if the player is mature, 0 if they are not */ int query_mature() { return ( MATURITY * 60 * 60 + time_on < time() ); } /** * Can the player see? Checks to see if the player can see at the passed * in light level. * @return 1 if they can see, 0 if they cannot */ int check_dark(int light) { int i; if( race_ob ) if( catch( i = (int)race_ob->query_dark(light) ) ) race_ob = CONFIG_DEFAULT_RACE; else return i; return (int)CONFIG_DEFAULT_RACE->query_dark(light); } /* check_dark() */ /** * This method returns the players level. * @return their level */ int query_level() { if( _player_data->_level_time < ( time() - LEVEL_CACHE ) ) { _player_data->_level = STD_GUILD_OBJ->query_level(TO); _player_data->_level_time = time(); } return _player_data->_level; } /* query_level() */ /** * Restarts their heartbeat if it has been turned off for some reason. * @return always returns 1 */ int restart_heart_beat() { set_heart_beat(1); tell_object( TO, "Ok, heart beat restarted.\n"); return 1; } /* restart_heart_beat() */ /** @ignore yes */ nomask void set_snoopee( object ob ) { _player_data->_snoopee = ob; } /** * Returns the object snooping us. If someone is snooping the player it * returns the object doing the snooping. If no one is snooping the player * it returns 0. * @return the object snooping the player * @see efun::snoop() */ nomask object query_snoopee() { return _player_data->_snoopee; } /** @ignore yes */ void set_creator( int i ) { if( PO != master() && file_name(PO) != DOMAIN_H ) { write("Illegal attempt to set creator!\n"); log_file("ILLEGAL", this_player(1)->query_name()+" ("+ file_name(this_player(1))+") Illegal attempt to set_creator "+ "at "+ctime(time())+" from "+file_name(PO)+"\n"); return; } creator = i; set_home_dir("/w/"+query_name() ); save_me(); } /* set_creator() */ /** * Prevent the object from shadowing us? This checks to see if we should * prevent this object from shadowing the player. * @return 1 if we are prventing the shadow */ int query_prevent_shadow( object ob ) { if( function_exists("query_prevent_shadow", ob ) || function_exists("query_name", ob ) || function_exists("query_creator", ob ) || function_exists("dest_me", ob ) || function_exists("query_object_type", ob ) || function_exists("save_me", ob ) ) return 1; return 0; } /* query_prevent_shadow() */ /** * Returns the maxium number of deaths. This is the maxium * number of times they can die before they are totally dead. * @return the maxium number fo deaths */ int query_max_deaths() { return max_deaths; } /** * Sets the maximum number of deaths. This sets the maximum * number of times a player can die. * @param i the number of deaths to set it to * @see adjust_max_deaths() */ void set_max_deaths(int i) { max_deaths = i; } /** * Changes the current number of maxium deaths. This adjust * the number of times a player can die before they are totally dead. * @return the current total maximum deaths */ int adjust_max_deaths(int i) { return ( max_deaths += i ); } /** @ignore yes */ varargs int move( mixed dest, string msgin, string msgout ) { int i; object env; env= environment(); if( !i = living::move( dest, msgin, msgout ) ) me_moveing(env); return i; } /* move() */ /** @ignore yes */ nomask protected int do_refresh( int me ) { if( !me ) return notify_fail("Please read \"help refresh\" before doing " "this!\n"); if( query_property("guest") ) return notify_fail("Guest characters cannot refresh, sorry.\n"); if( creatorp(TO) ) return notify_fail("You cannot refresh a creator character, please " "ask an admin to demote you first.\n"); write("%^BOLD%^%^RED%^WARNING!%^RESET%^%^RESET%^\nPlease make sure you " "have read \"help refresh\" before doing this!\nAre you certain you " "wish to refresh yourself?\n%^YELLOW%^Please note you are using the " "character "+query_cap_name()+"!%^RESET%^\nAnswer \"YES\" or \"NO\"\n"); input_to("refresh2"); return 1; } /* do_refresh() */ /** @ignore yes */ protected int refresh2( string str ) { if( str != "YES" ) { write("Alright, not refreshing you.\n"); return 1; } write("Ok, refreshing you.\n"); say( query_cap_name()+" refreshes "+HIM+"self.\n"); move_with_look( CONFIG_NEWBIE_START_LOCATION ); Str = 11; Con = 11; Int = 11; Wis = 11; Dex = 11; inttmp = dextmp = wistmp = strtmp = contmp = 0; race_ob = CONFIG_DEFAULT_RACE; set_al(0); set_deity(0); languages = ({ "common" }); set_skills(0); add_skill_level("general.language.common.spoken", 100 ); add_skill_level("general.language.common.written", 100 ); spells = ([ ]); xp = 0; deaths = 0; max_deaths = 7; refresh_time = time_on - time(); refresh_time = -refresh_time; totaly_zap_bonus_cache(); known_commands = ({ "skills", "rearrange", "gp" }); reset_starts(); race_guild_commands(); totaly_zap_stat_cache(); drink_info = allocate( D_SIZEOF ); map_prop = ([ "determinate" : "", "player" : 1, "skills version" : "/handlers/change_skills"->query_version(), NEWS_RC : TP->query_property(NEWS_RC), ]); TO->remove_ghost(); reset_all(); set_family_name(0); _player_data->_titles = ([ ]); all_inventory()->dest_me(); // First refresh, then related files. REFRESH_H->player_refreshed(TO); BULK_DELETE_H->delete_related_files( query_name(), 0 ); START_PLAYER->start_player(); user_event( TO, "inform", query_name()+" refreshes "+HIM+"self", "refresh", TO ); write("Done.\n"); return 1; } /* refresh2() */ /** * This method returns the one letter object type which is used in the * finger command to display the type of object. * @return the one letter object type */ string query_object_type() { if( playtesterp( query_name() ) ) return "p"; return " "; } /* query_object_type() */ /** @ignore yes */ void set_my_ident( string str ) { if( base_name(PO) != LOGIN_OBJ ) { user_event( TO, "inform", "Invalid call to set_my_ident", "cheat"); return; } _player_data->_my_ident = str; } /* set_my_ident() */ /** * This method returns the ident of the player. The ident is * determined using the authentication protocol. * @return the ident of the player */ string query_my_ident() { if( PO != find_object("/secure/simul_efun") ) return 0; return _player_data->_my_ident; } /* query_my_ident() */ /** @ignore yes */ void event_enter( object thing, string mess, object from ) { events::event_enter( thing, mess, from ); living::event_enter( thing, mess, from ); } /* event_enter() */ /** @ignore yes */ void event_exit( object thing, string mess, object to ) { events::event_exit( thing, mess, to ); living::event_exit( thing, mess, to ); } /* event_exit() */ /** @ignore yes */ string *parse_command_id_list() { return living::parse_command_id_list() + family::parse_command_id_list(); } /* parse_command_id_list() */ /** @ignore yes */ string *parse_command_plural_id_list() { return living::parse_command_plural_id_list() + family::parse_command_plural_id_list(); } /* parse_command_plural_id_list() */ /** @ignore yes */ string *parse_command_adjectiv_id_list() { return living::parse_command_adjectiv_id_list() + family::parse_command_adjectiv_id_list(); } /* parse_command_adjectiv_id_list() */ /** * This method returns the time at which the player last logged on. * @return the last logged on time */ int query_last_log_on() { return last_log_on; } /** * This method returns the ip from which the player last logged on. * @return the ip address from which they last logged on */ string query_last_on_from() { return last_on_from; } /* This is the time since the last refresh... */ /** * This method returns the time at which the player last refreshed. * @return the last refresh time */ int query_refresh_time() { return refresh_time; } /** * This method returns the number of times the player has logged onto the mud. * @return the number of logins */ int query_no_logins() { return no_logins; } /** * This method returns if the object is a creator or not. * @return 1 if the object is a creator, 0 if not */ int query_creator() { return 0; } /** * This method returns the array of our friends. * @return our friends array */ string *query_friends() { return query_property("friends") || ({ }); } /** * This method will return 1 if the input name is a friend. * @param who the person to test for friendship * @return 1 if the specified person is our friend or not */ int query_friend( string who ) { return member_array( who, query_friends() ) != -1; } /* query_friend() */