/* -*- LPC -*- */ /* * $Locker: $ * $Id: health.c,v 1.41 2003/05/13 20:21:07 ceres Exp $ */ #include <drinks.h> #include <living.h> #undef POINTS_LOG #ifdef USE_SURRENDER #define SURRENDER_SHADOW "/std/shadows/misc/surrender_shadow" #endif varargs int adjust_xp( int number, int shared ); int hp, max_hp, gp, max_gp, xp, wimpy, *drink_info; #ifdef USE_SURENDER int surrender; #endif nosave int max; nosave int* runaway_callout_handles; nosave string which; nosave mapping counts; mapping verbs; nosave int callingdeath; void create() { max_hp = 1; max_gp = 1; #ifdef USE_SURRENDER surrender = -1; #endif drink_info = allocate( D_SIZEOF ); counts = ([ ]); verbs = ([ ]); callingdeath = 0; runaway_callout_handles = ({ }); } /* create() */ int query_hp() { return hp; } varargs int set_hp( int number, object attacker ) { if ( number > max_hp ) number = max_hp; hp = number; if ( ( hp < 0 ) && ( find_call_out( "do_death" ) == -1 ) ) call_out( "do_death", 0, attacker ); return hp; } /* set_hp() */ protected int check_wimpy() { int hp; hp = this_object()->query_hp(); if ( hp < 1 ) return 0; if( 100 * hp < this_object()->query_wimpy() * this_object()->query_max_hp()) { if(find_call_out("run_away") == -1) { // Add the runaway callout handle to our other ones runaway_callout_handles += ({ call_out("run_away", 0) }); } return 1; } return 0; } #ifdef USE_SURRENDER protected int check_surrender() { int hp; object *attackers, *all, shad; hp = this_object()->query_hp(); if (hp < 1) return 0; if (hp * 100 < this_object()->query_surrender() * this_object()->query_max_hp()) { attackers = filter(this_object()->query_attacker_list(), (: $1 && !$1->query_property("dead") && environment($1) == environment($2) :), this_object()); all = attackers + ({ this_object() }); all->event_surrender(this_object(), attackers); shad = clone_object(SURRENDER_SHADOW); if (shad) { shad->setup_shadow(this_object(), attackers); } return 1; } return 0; } #endif /* * Resets the flag that prevents the creation of a new do_death * callout */ void reset_callingdeath() { callingdeath = 0; } /** * Returns the current value of the callingdeath variable. */ int query_callingdeath() { return callingdeath; } /* query_callingdeath() */ varargs int adjust_hp(int number, object attacker, object weapon, string attack) { int i; hp += number; if ( hp > max_hp ) hp = max_hp; if (hp <= 0 && !callingdeath) { /* Set the flag so that I won't create another do_death * callout, and then create the callout. I'm expecting * do_death to reset this flag. Not that it should need * to, but we want to be clean. */ callingdeath = time(); call_out("do_death", 0, attacker, weapon, attack); // Get rid of any call_outs to run away for( i=0; i<sizeof(runaway_callout_handles); i++ ) { remove_call_out( runaway_callout_handles[i] ); } runaway_callout_handles = ({ }); } if (hp > 0 && number < 0 && attacker && attacker != this_object()) { #ifdef USE_SURRENDER if(this_object()->query_surrender() >= this_object()->query_wimpy()) { if (!check_surrender()) check_wimpy(); } else { if (!check_wimpy()) check_surrender(); } #else check_wimpy(); #endif } return hp; } /* undefinedp() doesn't seem to work on reference arguments. Have to work around. */ varargs string health_string(int flag, int ref health_level) { int level; string ret; if ( this_object()->query_property( "dead" ) ) { ret = "appears to be dead"; level = 0; } else if ( hp < max_hp / 10 ) { ret = "is in very bad shape"; level = 1; } else if ( hp < max_hp / 5 ) { ret = "is in bad shape"; level = 2; } else if ( hp < max_hp / 2 ) { ret = "is not in good shape"; level = 3; } else if ( hp < ( max_hp - 200 ) ) { ret = "is slightly hurt"; level = 4; } else { ret = "is in good shape"; level = 5; } if (!undefinedp(flag)) health_level = level; return ret; } /* health_string() */ int query_max_hp() { return max_hp; } int set_max_hp( int number ) { int old_hp; old_hp = hp; if ( max_hp == hp ) hp = number; else if ( max_hp ) hp = ( hp * number ) / max_hp; else hp = number; max_hp = number; if ( hp > max_hp ) hp = max_hp; if ( ( hp < 0 ) && ( old_hp > 0 ) ) hp = max_hp; return max_hp; } /* set_max_hp() */ int query_gp() { return gp; } int query_specific_gp( string gp_type ) { int now; if ( !gp_type ) return 0; max = (int)this_object()->query_skill_bonus( gp_type +".points" ) + 50; if ( max >= max_gp ) max = max_gp; now = gp + max - max_gp; which = gp_type; if ( find_call_out( "clear_gp_info" ) == -1 ) call_out( "clear_gp_info", 1 ); return now; } /* query_specific_gp() */ void clear_gp_info() { which = 0; } int set_gp( int number ) { gp = number; if ( gp > max_gp ) gp = max_gp; return gp; } /* set_gp() */ int adjust_gp( int number ) { int tmp, diff; string guild_ob, current_verb; mixed *bits; if ( gp + number < 0 ) return -1; gp += number; if ( gp > max_gp ) gp = max_gp; tmp = number; /* Give people automatic gp advances. * which is setup by query_specific_gp. * max is their maximum gp. * * Also, give people XP for using guild commands. */ if((number < 0) && stringp(which)) { if (number > ( GP_ADVANCE_MOD * max ) / MIN_TIMES) number = (GP_ADVANCE_MOD * max) / MIN_TIMES; counts[ which ] -= number; if (counts[ which ] > GP_ADVANCE_MOD * max) { counts[ which ] = 0; // they get a chance of an advance not a guarantee. // the use of exp(skill/somevalue) causes a steep growth // curve at higher values thus preventing massive levels // in .points skills. number = this_object()->query_skill(which +".points"); if(!random(to_int(exp(number/150.0))) && this_object()->add_skill_level( which +".points", 1, this_object())) { tell_object( this_object(), "%^YELLOW%^You find yourself " "more able to concentrate on this task than you " "thought.%^RESET%^\n" ); } #ifdef POINTS_LOG log_file( "POINTS", time() +" "+ (string)this_object()->query_name() +" gets "+ which +" ("+ max +")\n" ); #endif } // Give them Xp if they used a guild command. number = tmp; guild_ob = this_object()->query_guild_ob(); /* * If it's one of their guild commands give them 10x the GP. * if it's not then give then 4x the GP. Give 6x for cast since * */ if(guild_ob && member_array(query_verb(), guild_ob->query_commands()) != -1) number *= -10; else number *= -4; // These use all available GPs so need to give less XP. // These use all available GPs so need to give less XP. if(query_verb() == "pray" || query_verb() == "berserk" || query_verb() == "spellcheck" || query_verb() == "octograve") number /= 2; // If it's a repeat command and repeated too quickly then // or if it's just another command done very quickly (eg. a;b;a;b) // we reduce the Xp they get. current_verb = query_verb(); if(current_verb == "cast" || current_verb == "perform") { current_verb = this_object()->query_current_cmd(); } if(verbs[current_verb]) { bits = verbs[current_verb]; diff = time() - bits[0]; // see when they last did this command. If it was more than 5 minutes // ago then reduce their count of repeats. if(diff > 300) bits[1] /= 2; if(bits[1] < 1) bits[1] = 1; if(bits[1] > 1 && bits[2] == environment()) bits[1] += 1; // they're repeating the command so reduce their xp somewhat. if(bits[1] < 10) number /= (random(bits[1]) + 1); else number = 0; verbs[current_verb] = ({ time(), bits[1]+1, environment() }); } else { verbs[current_verb] = ({ time(), 1, environment() }); } if(number) adjust_xp(number, 1); #ifdef GUILD_XP log_file("GUILD_XP", "%s Gave %s %d points for %s [%s, %d]\n", ctime(time()), this_object()->query_name(), number, current_verb, verbs[current_verb][0], verbs[current_verb][1]); #endif } return gp; } int query_max_gp() { return max_gp; } int set_max_gp( int number ) { if ( max_gp ) gp = ( gp * number ) / max_gp; else gp = number; max_gp = number; if ( gp > max_gp ) gp = max_gp; return max_gp; } /* set_max_gp() */ int query_xp() { return xp; } varargs int adjust_xp( int number, int shared ) { if(number > 0 && (xp > 10000000 || xp + number > 10000000)) return xp; if(number > 10000 && previous_object() && explode(file_name(previous_object()), "/")[0] == "w") log_file("/d/admin/log/CHEAT", ctime(time()) + " " + file_name(previous_object()) + " gave " + number + " Xp for " + this_object()->query_name() + "\n"); xp += number; return xp; } /* adjust_xp() */ int query_wimpy() { return wimpy < 30 ? wimpy : 30; } int set_wimpy( int number ) { if ( ( number < 0 ) || ( number > 30 ) ) return -1; return wimpy = number; } /* set_wimpy() */ #ifdef USE_SURRENDER int query_surrender() { if (surrender == -1) { if (wimpy > 94) return 100; else return wimpy + 5; } else return surrender; } int set_surrender( int number ) { if ( ( number < 0 ) || ( number > 100 ) ) return -1; return surrender = number; } /* set_surrender() */ #endif int *query_drink_info() { return drink_info; } int query_volume( int type ) { if ( type >= D_SIZEOF ) return 0; return drink_info[ type ]; } /* query_volume() */ int adjust_volume( int type, int amount ) { if ( type >= D_SIZEOF ) return 0; return drink_info[ type ] += amount; } /* adjust_volume() */ void update_volumes() { int i, delta; delta = (int)this_object()->query_con(); for ( i = 0; i < sizeof( drink_info ); i++ ) { if ( drink_info[ i ] > delta ) { drink_info[ i ] -= delta; // drinking cools you down if you're hot, eating warms you up // if you're cold. if(this_object()->query_personal_temp() > 0 && i == D_DRINK) this_object()->adjust_personal_temp(-(delta/3)); else if(this_object()->query_personal_temp() < 0 && i == D_FOOD) this_object()->adjust_personal_temp(delta/3); } else if ( drink_info[ i ] < -delta ) drink_info[ i ] += delta; else drink_info[ i ] = 0; } } /* update_volumes() */ mapping query_counts() { return counts; }