/* Do not remove the headers from this file! see /USAGE for more info. */ //### lots of functions in here need to be autodoc'd // /std/player.c Written after login.c 1-12-94 // Rust@ZorkMUD // with mods by alot of us: // including: Nevyn@ZorkMUD (GUE) // Peregrin @ZorkMUD (GUE) // Beek ... Deathblade ... The rest of the Zorkmud staff ... // #include <security.h> #include <setbit.h> #include <playerflags.h> #include <commands.h> #include <move.h> #include <hooks.h> #include <size.h> #include <clean_up.h> // Files we need to inherit -- inherit ADVERSARY; inherit M_ACCESS; inherit M_SMARTMOVE; inherit M_FOLLOW; inherit M_ACTIONS; #ifndef EVERYTHING_SAVES private inherit M_SAVE; // don't want people calling load_from_string() // externally #endif inherit __DIR__ "body/quests"; inherit __DIR__ "body/cmd"; inherit __DIR__ "body/help"; inherit __DIR__ "body/wizfuncs"; inherit __DIR__ "body/money"; inherit __DIR__ "body/start"; inherit __DIR__ "body/time"; inherit __DIR__ "body/naming"; #ifndef SAY_HISTORY_IN_ROOMS inherit __DIR__ "body/history"; #endif #ifdef USE_STATUS_LINE inherit __DIR__ "body/status_line"; #endif #ifdef USE_SKILLS inherit __DIR__ "body/skills"; #endif #ifdef USE_TITLES inherit __DIR__ "body/title"; #endif #ifdef USE_WIZ_POSITION inherit __DIR__ "body/wiz_position"; #endif #ifdef USE_GUILDS inherit __DIR__ "body/guilds"; #endif #ifdef USE_STATS inherit M_BODY_STATS; #endif void heal_all(); varargs void stop_fight(object); void reinstate_effects(); // Global variables -- private string plan; private nosave object link; private mixed saved_items; // interfaces for other objects to manipulate our global variables //:FUNCTION is_body //Is this a body object? int is_body() { return 1; } //:FUNCTION query_link //Return our link object nomask object query_link() { return link; } void mudlib_setup(mixed args...) { adversary::mudlib_setup(args...); m_follow::mudlib_setup(args...); } #ifdef EVERYONE_HAS_A_PLAN //:FUNCTION query_plan //Returns our plan nomask string query_plan() { return plan; } //:FUNCTION set_plan //Sets our plan nomask void set_plan(string new_plan) { if ( this_body() != this_object() ) error("illegal attempt to set plan\n"); plan = new_plan; save_me(); } #endif /* EVERYONE_HAS_A_PLAN */ protected void update_for_new_body(mapping tmp) { /* nothing for now; can be overloaded for races that need it */ } //:FUNCTION update_containers // Update containers on loading - fix relationships private nomask void update_container(object ob) { foreach(object inv in all_inventory(ob)) { if(inv->is_container()) update_container(inv); ob->receive_object(inv); } } // Added to complete initialisation of stuff loaded private nomask void init_stuff() { foreach(object inv in all_inventory()) { #ifdef WIELD_SINGLE if(inv->test_flag(F_WIELDED)) do_wield(inv); #else if( inv->query_wielding() && sizeof(inv->query_wielding()) ) this_object()->wield(inv, inv->query_wielding()[0]); #endif if(inv->test_flag(F_WORN)) inv->do_wear(); } update_container(this_object()); reinstate_effects(); } /* initialize various internal things */ //### needs a new name private nomask void init_cmd_hook() { link = previous_object(); naming_init_ids(); #ifdef USE_MASS set_mass(100); #endif #ifdef USE_SIZE set_size(VERY_LARGE); #endif set_max_capacity(VERY_LARGE); if (saved_items) { string e; if (e = catch(load_from_string(saved_items, 1))) { mapping tmp = restore_variable(saved_items); if (tmp["#base_name#"] != base_name(this_object())) { update_for_new_body(tmp); tmp["#base_name#"] = base_name(this_object()); load_from_string(save_variable(tmp), 1); } else error("Rethrown: " + e); } saved_items = 0; } init_stuff(); } nomask void su_enter_game(object where) { init_cmd_hook(); move(where); } void enter_game(int state) { switch (state) { case 1: /* FALLTHROUGH */ case 0: /* existing user */ init_cmd_hook(); CHANNEL_D->deliver_emote("announce", query_name(), sprintf("enters %s.", mud_name())); /* move the body. make sure this comes before the simple_action */ if ( !move_to_start() ) { write("Uh-oh, you have no environment.\n"); } else { /* we don't want other people to get the extra newlines */ write("\n"); if(is_visible()) simple_action("$N $venter "+mud_name()+"."); write("\n"); } /* FALLTHROUGH */ case 2: do_game_command("look"); update_health(); } } //:FUNCTION save_me //Saves us :-) void save_me() { object shell_ob = link && link->query_shell_ob(); string userid = query_userid(); /* save the shell information */ if ( shell_ob ) shell_ob->save_me(); //### This check is bogus. What should it be? // This check also doesn't work for su's -- John // if (previous_object()==this_object()) saved_items = save_to_string(1); // 1 meaning it is recursive. unguarded( 1, (: save_object , USER_PATH(userid) :) ); saved_items = 0; } //:FUNCTION remove //Handle mailboxes and the last login daemon, as well as the normal stuff void remove() { object ob; if ( !clonep() ) { ::remove(); return; } #ifdef PLAYERS_START_WHERE_THEY_QUIT if (environment() && !wizardp(link)) set_start_location(file_name(environment())); #endif save_me(); LAST_LOGIN_D->register_last(query_userid()); SNOOP_D->bye(); ::remove(); } //### This should be protected. //:FUNCTION quit //Quit the game. void quit() { if ( !clonep() ) { ::remove(); return; } if (is_visible()) simple_action("$N $vhave left "+mud_name()+"."); CHANNEL_D->deliver_emote("announce", query_name(), sprintf("has left %s.", mud_name())); remove(); } void do_receive(string msg, int msg_type) { if ( link ) link->do_receive(msg, msg_type); } //:FUNCTION net_dead //This function is called when we lose our link void net_dead() { //### add security here? if ( is_visible() ) simple_action("$N $vhave gone link-dead."); CHANNEL_D->deliver_emote("announce", query_name(), sprintf("has gone link-dead.", mud_name())); SNOOP_D->bye(this_object()); } //:FUNCTION reconnect //This function is called when we get our link back void reconnect(object new_link) { //### add security here? link = new_link; if(is_visible()) simple_action("$N $vhave reconnected."); CHANNEL_D->deliver_emote("announce", query_name(), sprintf("has reconnected.", mud_name())); } protected void die() { if ( wizardp(link) ) { if(is_visible()) simple_action("If $n $vwere mortal, $n would now no longer be mortal."); stop_fight(); call_out((: reincarnate :), 1); heal_all(); return; } stop_fight(); if(is_visible()) simple_action("$N $vhave kicked the bucket, and $vare now pushing up the daisies."); receive_private_msg("\n\n **** You have died ****\n\n" "A pity, really. Way too many people dying these days for me to just patch\n" "everyone up. Oh well, you'll live.\n",0,0); rack_up_a_death(); drop_corpse(); #ifdef DEATH_MESSAGES { // another option is to choose the message here based on player level // or something, instead of just letting action() pick at random. // something like: // action(({ this_object()}), // (MESSAGES_D->get_messages("player_death"))[query_level()/5])[1]; string msg = action(({this_object()}), MESSAGES_D->get_messages("player-death"))[1]; tell( bodies() - ({ this_body() }), msg ); } #endif } int clean_up() { return NEVER_AGAIN; } //:FUNCTION id //id(s) returns 1 if we respond to the name 's' int id(string arg) { if(!is_visible() && arg == lower_case(query_invis_name())) return 1; return ::id(arg); } string stat_me() { string result = short() + "\n" + "Userid: " + query_userid() + "\n" + ::stat_me(); if ( link ) result += link->stat_me(); return result; } void create(string userid) { if ( !clonep() ) return; if ( base_name(previous_object()) != USER_OB ) error("security violation: illegal attempt to change name\n"); messages = ([]); adversary::create(); /* ** Make some of the flags non-persistent (they won't be saved). */ configure_set(PLAYER_NP_FLAGS, 1); set_long( (: our_description :) ); set_name(userid); unguarded(1, (: restore_object, USER_PATH(userid), 1 :)); // up to the player set_attack_speed(0); } //### temp hack. be both user and body nomask object query_body() { return this_object(); } /* verb interaction */ mixed indirect_give_obj_to_liv(object ob, object liv) { if( previous_object() == liv && ob->is_in( liv )) return "You already have that."; return 1; } string inventory_header() { return query_name() + " is carrying:\n"; } int ob_state() { return -1; } void force_look(int force_long_desc) { environment(this_object())->do_looking(force_long_desc, this_object()); } // Called when our environment destructs. If we don't move, we get dested too. void move_or_destruct(object suggested_dest) { mixed err; object dested_env = environment(); mixed destination; if ( !query_link() ) return; // Might want to add another failsafe room on the end of this list // that doesn't inherit room.c and is guaranteed to load/accept people. foreach (destination in ({ suggested_dest, VOID_ROOM, this_body()->query_start_location(), START, WIZARD_START })) { err = catch { if (stringp(destination)) destination = load_object(destination); if (destination != dested_env) { err = move(destination); if (stringp(err)) throw(err); } else throw("Being destructed.\n"); }; if (destination && !err) { receive_private_msg(dested_env->short() + " being destructed: You have been moved to " + destination->short() + ".\n"); return; } else { if (destination) receive_private_msg("Cannot move to " + destination->short() + ": " + err); } } receive_private_msg("Uh oh..., couldn't move you anywhere. Goodbye.\n"); (this_object()->query_link())->remove(); } string living_query_name() { return ::query_name(); } string query_name() { return naming::adjust_name(::query_name()); } /* ** These are overrides from our ancestor (MONSTER) */ string short() { return query_name(); } string a_short() { return query_name(); } string the_short() { return query_name(); } string in_room_desc() { return base_in_room_desc() + query_idle_string(); } /* end of naming overrides */ int allow(string what) { if ( this_body() == this_object() ) { return 1; } return 0; }