/*************************************************************************** * Mud20 1.0 by Todd H. Johnson (Kregor) a derivative of the Open Gaming * * License by Wizards of the Coast. All comments referring to D20, OGL, * * and SRD refer to the System Reference Document for the Open Gaming * * system. Any inclusion of these derivatives must include credit to the * * Mud20 system, the full and complete Open Gaming LIcense, and credit to * * the respective authors. See ../doc/srd.txt for more information. * * * * Emud 2.2 by Igor van den Hoven, Michiel Lange, and Martin Bethlehem. * * * * MrMud 1.4 by David Bills, Dug Michael and Martin Gallwey * * * * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * * * Original Diku Mud copyright (C) 1990 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik St{rfeld, Tom Madsen, and Katje Nyboe. * ***************************************************************************/ /*************************************************************************** * update.c: Functions for all update loops * ***************************************************************************/ #include "mud.h" /* Local functions. */ int hit_gain args ((CHAR_DATA * ch)); int spell_dice args ((CHAR_DATA * ch, int sn, int numdice, int sizedice )); int nonlethal_gain args ((CHAR_DATA * ch)); void mana_gain args ((CHAR_DATA * ch, int hour)); int move_gain args ((CHAR_DATA * ch)); void recovery_update args ((CHAR_DATA * ch)); void mobile_update args ((void)); void shop_update args ((void)); void weather_update args ((void)); void time_update args ((void)); void char_update args ((void)); void obj_update args ((void)); void aggr_update args ((void)); void bounty_update args ((void)); void auto_area_save args ((void)); void auto_char_save args ((void)); void mob_program_update args ((void)); void obj_program_update args ((void)); void room_program_update args ((void)); void purger_update args ((void)); void condition_update args ((void)); void asn_update args ((void)); void disease_update args ((CHAR_DATA * ch)); void check_exposure args ((CHAR_DATA * ch)); /* * Advancement for companions when character advances level - Kregor */ void advance_familiar(CHAR_DATA *ch, CHAR_DATA *mob) { int level; push_call("advance_familiar(%p,%p)",ch,mob); if (!ch || !mob) { pop_call(); return; } if ((level = multi_skill_level(ch,gsn_familiar)) <= 0) { pop_call(); return; } mob->max_hit = ch->max_hit / 2; mob->hit = get_max_hit(mob); mob->perm_int = level / 2 + 5; mob->nat_armor = level / 2; pop_call(); return; } void advance_companion(CHAR_DATA *ch, CHAR_DATA *mob) { int level, bonus; push_call("advance_companion(%p,%p)",ch,mob); if (!ch || !mob) { pop_call(); return; } if ((level = multi_skill_level(ch,gsn_companion)) <= 0) { pop_call(); return; } bonus = UMAX(0, (level - race_table[mob->race].hit_dice) / 3); mob->level = bonus * 2 + UMAX(1, race_table[mob->race].hit_dice); mob->nat_armor = bonus * 2; mob->perm_str = 10 + race_table[mob->race].race_mod[0] + bonus; mob->perm_dex = 10 + race_table[mob->race].race_mod[1] + bonus; mob->max_hit = dice(mob->level, race_type_table[race_type(mob)].hit_die); mob->hit = get_max_hit(mob); pop_call(); return; } void advance_warhorse(CHAR_DATA *ch, CHAR_DATA *mob) { int level; AFFECT_DATA af; push_call("advance_warhorse(%p,%p)",ch,mob); if (!ch || !mob) { pop_call(); return; } if ((level = multi_skill_level(ch,gsn_warhorse)) <= 0) { pop_call(); return; } mob->level = (level / 2) + race_table[mob->race].hit_dice; mob->nat_armor = ROUNDUP(level * 2 / 3); mob->perm_str = (level / 5) + 10 + race_table[mob->race].race_mod[0]; mob->perm_int = URANGE(6, level*2/3, 9) ; mob->max_hit = dice(mob->level, race_type_table[race_type(mob)].hit_die); mob->hit = get_max_hit(mob); while (mob->first_affect) { affect_from_char(mob, mob->first_affect); } if (level >= 10) { af.type = gsn_warhorse; af.duration = -1; af.bittype = AFFECT_TO_NONE; af.bitvector = AFF_NONE; af.location = APPLY_SPELL_RES; af.modifier = level + 11; af.level = level; affect_to_char( mob, mob, &af ); } if (level >= 6) { af.type = gsn_warhorse; af.duration = -1; af.level = level; af.bittype = AFFECT_TO_NONE; af.bitvector = AFF_NONE; af.location = APPLY_DR_EVIL; af.modifier = 5; affect_to_char( mob, mob, &af ); af.bitvector = AFF_NONE; af.location = APPLY_DR_COLD; af.modifier = 10; affect_to_char( mob, mob, &af ); af.bitvector = AFF_NONE; af.location = APPLY_DR_ACID; af.modifier = 10; affect_to_char( mob, mob, &af ); af.bitvector = AFF_NONE; af.location = APPLY_DR_ELECTRIC; af.modifier = 10; affect_to_char( mob, mob, &af ); } pop_call(); return; } /* * The dreaded level loss - Kregor * now using cached data for each level's gain to remove skills/hp/and points */ void lose_level (CHAR_DATA * ch, bool fSave) { int add_hp, add_mana, add_move, add_prac, sn, class_lvl; push_call("lose_level(%p,%p)",ch,fSave); if (ch->level <= starting_level(ch)) { wiz_printf("cannot lose racial hit dice levels"); pop_call(); return; } add_hp = ch->pcdata->hit_level[ch->level]; add_mana = get_max_mana(ch, ch->class); add_move = ch->pcdata->move_level[ch->level]; add_prac = ch->pcdata->pract_level[ch->level]; add_hp = UMAX (1, add_hp); add_mana = UMAX (1, add_mana); add_move = UMAX (1, add_move); ch->max_hit -= add_hp; ch->hit -= add_hp; ch->max_move -= add_move; ch->pcdata->practice = UMAX(0, ch->pcdata->pract_level[ch->level] - add_prac); if ((ch->level-1) % 3 == 0) { ch->pcdata->feat_pts = UMAX(0, ch->pcdata->feat_pts - 1); } if (ch->level % 4 == 0) { ch->pcdata->stat_pts = UMAX(0, ch->pcdata->stat_pts - 1); } if ((class_lvl = ch->mclass[ch->class]) == class_table[ch->class].first_bonus || (class_lvl > class_table[ch->class].first_bonus && (class_lvl - class_table[ch->class].first_bonus) % class_table[ch->class].bonus_lvl == 0 )) { ch->pcdata->bonus_feat[ch->class] = UMAX(0, ch->pcdata->bonus_feat[ch->class] - 1); } switch (ch->class) { case CLASS_FIGHTER: if (ch->mclass[ch->class] == 1) ch->pcdata->bonus_feat[ch->class] = UMAX(0, ch->pcdata->bonus_feat[ch->class] - 1); break; case CLASS_WIZARD: ch->pcdata->bonus_spells -= 2; break; } for (sn = 0 ; *skill_table[sn].name != '\0' ; sn++) { if (ch->pcdata->skill_level[ch->level][sn] > 0) ch->learned[sn] -= ch->pcdata->skill_level[ch->level][sn]; } ch->level--; /* change active class to previous class */ ch->class = ch->pcdata->class_level[ch->level]; /*make sure if ch not a druid or a rogue anymore, take away the class language*/ if (!class_level(ch, CLASS_DRUID) && IS_SET(ch->language, LANG_DRUIDIC)) REMOVE_BIT(ch->language, LANG_DRUIDIC); if (!class_level(ch, CLASS_ROGUE) && IS_SET(ch->language, LANG_THIEVESCANT)) REMOVE_BIT(ch->language, LANG_THIEVESCANT); restore_mana(ch); destroy_mana(ch); if (fSave) { sub_player (ch); add_player (ch); save_char_obj (ch, NORMAL_SAVE); save_char_obj (ch, BACKUP_SAVE); } ch_printf_color(ch, "Your loss is: %d/%d hp, %d/%d mv %d/%d skill points.\n\r", add_hp, add_move, add_prac ); vt100prompt(ch); pop_call(); return; } /* Adds monster levels to PC races with hit dice at char gen. */ void monster_levels (CHAR_DATA * ch, int levels) { int add_hp; int add_move; int add_prac; int cnt; push_call("monster_levels(%p,%p)",ch,levels); for (cnt = 0 ; cnt < levels ; cnt++) { ch->level++; ch->mclass[CLASS_MONSTER]++; add_hp = dice(1, race_type_table[race_table[ch->race].type].hit_die); add_move = number_range(1, race_type_table[race_table[ch->race].type].hit_die / 2); add_prac = race_type_table[race_table[ch->race].type].skill_pts + stat_bonus(FALSE, ch, APPLY_INT); add_hp = UMAX (1, add_hp); add_move = UMAX (1, add_move); ch->max_hit += add_hp; ch->hit += add_hp; ch->max_move += add_move; ch->pcdata->practice += add_prac; if ((ch->level-1) % 3 == 0) /* adding a feat every odd level - Kregor 12/1/2006 */ { ch->pcdata->feat_pts++; } if (ch->level % 4 == 0) /* adding a stat point every four levels - Kregor 12/1/2006 */ { ch->pcdata->stat_pts++; } } ch->pcdata->exp = exp_level(ch, ch->level-1) + 1; pop_call(); return; } /* Advancement stuff. */ void advance_level (CHAR_DATA * ch, bool fSave) { CHAR_DATA *mob; int class_lvl; int add_hp; int add_mana; int add_move; int add_prac; char colY[10]; push_call("advance_level(%p,%p)",ch,fSave); strcpy(colY, ansi_translate_text(ch, "{138}")); add_hp = dice(1, class_table[ch->class].hp_max); add_mana = get_max_mana(ch, ch->class) - ch->max_mana[ch->class]; add_move = number_range(1, class_table[ch->class].hp_max / 2); add_prac = class_table[ch->class].skill_pts + stat_bonus(FALSE, ch, APPLY_INT); if (ch->race == RACE_HUMAN) add_prac += 1; if (ch->race == RACE_HALFELF && ch->level % 2 == 0) add_prac += 1; add_hp = UMAX (1, add_hp); add_mana = UMAX (1, add_mana); add_move = UMAX (1, add_move); ch->max_mana[ch->class] = get_max_mana(ch, ch->class); ch->max_hit += add_hp; ch->hit += add_hp; ch->max_move += add_move; ch->mana[ch->class] += add_mana; ch->pcdata->practice += add_prac; ch->pcdata->hit_level[ch->level] = add_hp; ch->pcdata->move_level[ch->level] = add_move; ch->pcdata->class_level[ch->level] = ch->class; ch->pcdata->pract_level[ch->level] = add_prac; if (ch->lost_levels > 0) /* removing energy drained levels */ ch->lost_levels--; if ((ch->level - 1) % 2 == 0) /* adding a feat every odd level - Kregor 12/1/2006 */ { ch->pcdata->feat_pts++; ch_printf_color(ch, "%sYou gained a feat point, you now have %d.\n\r", colY, ch->pcdata->feat_pts ); } if (ch->level%4 == 0) /* adding a stat point every four levels - Kregor 12/1/2006 */ { ch->pcdata->stat_pts++; ch_printf_color(ch, "%sYou gained a stat point, you now have %d.\n\r", colY, ch->pcdata->stat_pts ); } if ((class_lvl = ch->mclass[ch->class]) == class_table[ch->class].first_bonus || (class_lvl > class_table[ch->class].first_bonus && (class_lvl - class_table[ch->class].first_bonus) % class_table[ch->class].bonus_lvl == 0 )) { ch->pcdata->bonus_feat[ch->class]++; ch_printf_color(ch, "%sYou gained a bonus feat.\n\r", colY); } if (ch->class == CLASS_FIGHTER && class_lvl == 1) ch->pcdata->bonus_feat[ch->class]++; switch (ch->class) { case CLASS_WIZARD: if (class_lvl == 1) ch_printf_color(ch, "%sYou may select a school of specialty. (Syntax: school)\n\r", colY); ch->pcdata->bonus_spells += 2; ch_printf_color(ch, "%sYou can learn two bonus spells. (Syntax: learn)\n\r", colY); break; case CLASS_RANGER: if (class_lvl == 1 || class_lvl % 5 == 0) ch_printf_color(ch, "%sYou may chose a new favored enemy. (Syntax: faveenemy)\n\r", colY); else if (class_lvl == 2) ch_printf_color(ch, "%sYou may select a fighting style, seek out a mentor.\n\r", colY); break; case CLASS_MONK: if (class_lvl == 1) ch_printf_color(ch, "%sYou may select a fighting style, seek out a mentor.\n\r", colY); break; } if (ch->class == CLASS_DRUID && !IS_SET(ch->language, LANG_DRUIDIC)) SET_BIT(ch->language, LANG_DRUIDIC); if (ch->class == CLASS_ROGUE && !IS_SET(ch->language, LANG_THIEVESCANT)) SET_BIT(ch->language, LANG_THIEVESCANT); if ((mob = get_familiar(ch)) != NULL) advance_familiar(ch, mob); if ((mob = get_companion(ch)) != NULL) advance_companion(ch, mob); if ((mob = get_warhorse(ch)) != NULL) advance_warhorse(ch, mob); if (fSave) { sub_player (ch); add_player (ch); save_char_obj (ch, NORMAL_SAVE); save_char_obj (ch, BACKUP_SAVE); } ch_printf_color(ch, "Your gain is: %d/%d hp, %d/%d m, %d/%d mv %d/%d skill points.\n\r", add_hp, get_max_hit(ch), add_mana, get_max_mana(ch,ch->class), add_move, get_max_move(ch), add_prac, ch->pcdata->practice ); char_reset(ch); vt100prompt(ch); pop_call(); return; } /* Return starting level for CH */ int starting_level( CHAR_DATA *ch ) { int level = 1; if (race_table[ch->race].hit_dice > 1) level = race_table[ch->race].hit_dice; return level; } void gain_exp (CHAR_DATA * ch, int gain) { int penalty; push_call("gain_exp(%p,%p)",ch,gain); if (IS_NPC (ch) || ch->level >= LEVEL_HERO - 1 || ch->in_room->area->low_r_vnum == ROOM_VNUM_ARENA) { pop_call(); return; } penalty = level_diff(ch) * 20; gain = gain * (100 - penalty) / 100; // add RP rating as a modifier to exp - Kregor if (ch->desc && ch->desc->account) gain = gain * (100 + ch->desc->account->rp_points) / 100; if (ch->pcdata->exp < exp_level(ch, ch->level + 1) -1) { ch->pcdata->exp = UMAX(0, ch->pcdata->exp + gain); while (ch->level < LEVEL_HERO && ch->pcdata->exp >= exp_level(ch, ch->level)) { if (NOT_AUTHED(ch) && ch->level >= 2) send_to_char ("You may not advance a level until you are authorized!\n\r", ch); else send_to_char ("You may advance a level! Go see a trainer! \n\r", ch); pop_call(); return; } } else { send_to_char ("You may not gain any more experience until you've leveled.\n\r", ch); } pop_call(); return; } /* * Regeneration stuff. * * hit_gain now checks each recovery period for recovery of * incapacitated character, per D20 rules, deviated to check * once per turn, rather than once per game hour, allowing * for real-time game scale - Kregor * * hit_gain much slower than other rates of recovery. * gains are all on accelerated scale to allow for bearable * real-time recovery period. */ int hit_gain (CHAR_DATA * ch) { int gain; push_call("hit_gain(%p)",ch); if (!IS_NPC(ch)) { pop_call(); return 0; } if (in_combat(ch)) { pop_call(); return 0; } if (is_affected(ch, gsn_festering_wounds)) { pop_call(); return 0; } gain = UMAX(1, get_max_hit(ch)/80 + stat_bonus(TRUE, ch, APPLY_CON)); if (IS_AFFECTED(ch, AFF_POISON)) { gain = 0; } switch (ch->position) { case POS_STUNNED: case POS_SLEEPING: case POS_RESTING: break; case POS_SITTING: case POS_KNEELING: case POS_CROUCHING: gain = UMAX(1, gain/2); break; case POS_STANDING: gain = UMAX(1, gain/4); break; default: gain = 0; break; } pop_call(); return UMIN(gain, get_max_hit(ch) - ch->hit); } int nonlethal_gain (CHAR_DATA * ch) { int gain; push_call("nonlethal_gain(%p)",ch); if (!IS_NPC(ch)) { pop_call(); return 0; } if (in_combat(ch)) { pop_call(); return 0; } if (is_affected(ch, gsn_festering_wounds)) { pop_call(); return 0; } gain = UMAX(1, ch->level + get_curr_con(ch)); switch (ch->position) { case POS_STUNNED: case POS_SLEEPING: case POS_RESTING: break; case POS_SITTING: case POS_KNEELING: case POS_CROUCHING: gain = UMAX(1, gain/2); break; case POS_STANDING: gain = UMAX(1, gain/4); break; default: gain = 0; break; } if (IS_AFFECTED(ch, AFF_POISON)) { gain = 0; } pop_call(); return UMIN(gain, ch->nonlethal); } void mana_gain(CHAR_DATA *ch, int hour) { int cnt; push_call("restore_mana(%p)",ch); for (cnt = 0 ; cnt < MAX_CLASS ; cnt++) { if (!class_level(ch, cnt) || class_table[cnt].mana_table == MANA_NONE) { ch->mana[cnt] = 0; continue; } if (hour == 1) { if (ch->mana[cnt] < get_max_mana(ch, cnt) / 3) ch->mana[cnt] = get_max_mana(ch, cnt) / 3; } if (hour == 2) { if (ch->mana[cnt] < get_max_mana(ch, cnt) * 2 / 3) ch->mana[cnt] = get_max_mana(ch, cnt) * 2 / 3; } if (hour >= 8) restore_mana(ch); } pop_call(); return; } void restore_mana(CHAR_DATA *ch) { int cnt; push_call("restore_mana(%p)",ch); for (cnt = 0 ; cnt < MAX_CLASS ; cnt++) { if (!class_level(ch, cnt) || class_table[cnt].mana_table == MANA_NONE) { ch->mana[cnt] = 0; continue; } ch->mana[cnt] = get_max_mana(ch, cnt); } pop_call(); return; } void destroy_mana(CHAR_DATA *ch) { int cnt; push_call("destroy_mana(%p)",ch); for (cnt = 0 ; cnt < MAX_CLASS ; cnt++) { ch->mana[cnt] = 0; } pop_call(); return; } int move_gain (CHAR_DATA * ch) { int gain, bonus; push_call("move_gain(%p)",ch); if (!IS_NPC(ch)) { pop_call(); return 0; } if (in_combat(ch)) { pop_call(); return 0; } bonus = (stat_bonus(TRUE, ch, APPLY_CON)); gain = UMAX(1, get_max_move(ch)/8 + bonus); switch (ch->position) { case POS_STUNNED: case POS_SLEEPING: case POS_RESTING: break; case POS_SITTING: case POS_KNEELING: case POS_CROUCHING: gain = UMAX(1, gain/2); break; case POS_STANDING: gain = UMAX(1, gain/4); break; default: gain = 0; break; } if (IS_AFFECTED(ch, AFF_POISON)) { gain /= 2; } pop_call(); return UMIN (gain, get_max_move(ch) - ch->move); } void gain_condition (CHAR_DATA * ch, int iCond, int value) { int old_condition; push_call("gain_condition(%p,%p,%p)",ch,iCond,value); if ( value == 0 || IS_NPC(ch) || IS_IMMORTAL(ch) || NEW_AUTH(ch)) { pop_call(); return; } if (!ch->in_room) { pop_call(); return; } old_condition = ch->pcdata->condition[iCond]; switch (iCond) { case COND_AIR: ch->pcdata->condition[iCond] = URANGE( -10, old_condition + value, max_air(ch)); break; case COND_THIRST: ch->pcdata->condition[iCond] = URANGE( -10, old_condition + value, max_thirst(ch) ); break; case COND_DRUNK: ch->pcdata->condition[iCond] = URANGE( 0, old_condition + value, max_drunk(ch) ); break; default: ch->pcdata->condition[iCond] = URANGE( -10, old_condition + value, max_hunger(ch) ); break; } if (IS_RACE(ch, RACE_VAMPIRE)) { if (iCond == COND_THIRST && ch->pcdata->condition[COND_THIRST] <= 3) { send_to_char("Your body is craving for blood.\n\r", ch); act( "$n looks pale and weak.", ch, NULL, NULL, TO_ROOM); damage( ch, ch, 1+ch->level/7, TYPE_NOFIGHT, NULL ); } pop_call(); return; } switch ( iCond ) { case COND_FULL: if (ch->pcdata->condition[iCond] <= 0) { if (ch->level > 1) { send_to_char_color( "{038}You are STARVING!{300}\n\r", ch ); act( "{038}$n is starved half to death!{300}", ch, NULL, NULL, TO_ROOM); if (con_roll(ch) < 10 - ch->pcdata->condition[iCond]) { ch->nonlethal += dice(1,6); } } else { send_to_char_color( "{038}You are REALLY hungry.{300}\n\r", ch ); } pop_call(); return; } else if (ch->pcdata->condition[iCond] < old_condition) { if (ch->pcdata->condition[iCond] <= 24) { send_to_char_color( "{038}You are REALLY hungry.{300}\n\r", ch ); act( "{038}You can hear $n's stomach growling.{300}", ch, NULL, NULL, TO_ROOM); } else if (ch->pcdata->condition[iCond] <= 32) { send_to_char_color( "{038}You are hungry.{300}\n\r", ch ); } else if (ch->pcdata->condition[iCond] <= 40) { send_to_char_color( "{038}You are a mite peckish.{300}\n\r", ch ); } } else if (old_condition < ch->pcdata->condition[iCond]) { if (ch->pcdata->condition[iCond] >= 48 && old_condition < ch->pcdata->condition[iCond]) { send_to_char_color( "{038}You are stuffed!{300}\n\r", ch ); } else if (ch->pcdata->condition[iCond] >= 44 && old_condition < ch->pcdata->condition[iCond]) { send_to_char_color( "{038}You are full.{300}\n\r", ch ); } } break; case COND_THIRST: if (ch->pcdata->condition[iCond] <= 0) { int dc = ch->pcdata->condition[iCond]; if (ch->in_room->sector_type == SECT_DESERT) dc *= 2; if( ch->level > 1 ) { send_to_char_color( "{168}You are DYING of THIRST!{300}\n\r", ch ); act( "{168}$n is dying of thirst!{300}", ch, NULL, NULL, TO_ROOM); if (con_roll(ch) < 10 - dc) { ch->nonlethal += dice(1,6); } } else { send_to_char_color( "{168}You are REALLY thirsty.{300}\n\r", ch ); act( "{168}$n is visibly parched.{300}", ch, NULL, NULL, TO_ROOM); } pop_call(); return; } else if (ch->pcdata->condition[iCond] < old_condition) { if (ch->pcdata->condition[iCond] <= get_curr_con(ch) / 2) { send_to_char_color( "{168}You are REALLY thirsty.{300}\n\r", ch ); act( "{168}$n is visibly parched.{300}", ch, NULL, NULL, TO_ROOM); } else if (ch->pcdata->condition[iCond] <= get_curr_con(ch)) { send_to_char_color( "{168}You are thirsty.{300}\n\r", ch ); } else if (ch->pcdata->condition[iCond] <= get_curr_con(ch)) { send_to_char_color( "{168}You could use a sip of something refreshing.{300}\n\r", ch ); } } else if (old_condition < ch->pcdata->condition[iCond]) { if (ch->pcdata->condition[iCond] >= 24 + get_curr_con(ch)) { send_to_char_color( "{168}You are no longer thirsty.{300}\n\r", ch ); } else if (ch->pcdata->condition[iCond] <= get_curr_con(ch) / 2 + 24) { send_to_char_color( "{168}You do not feel so thirsty.{300}\n\r", ch ); } } break; case COND_DRUNK: if (ch->pcdata->condition[iCond] <= 0) { if ( old_condition > 0 ) { send_to_char_color( "You are sober.\n\r", ch ); pop_call(); return; } } else if (ch->pcdata->condition[iCond] >= get_curr_con(ch) * 5) { if ( old_condition < ch->pcdata->condition[iCond] ) { send_to_char_color( "You are totally sloshed!\n\r", ch ); act( "$n looks totally sloshed.", ch, NULL, NULL, TO_ROOM); update_pos(ch,-1); } else { send_to_char_color( "You are feeling a little less sloshed.\n\r", ch ); } } else if (ch->pcdata->condition[iCond] >= get_curr_con(ch) * 4) { if ( old_condition < ch->pcdata->condition[iCond] ) { send_to_char_color( "You are getting hammered.\n\r", ch ); } else { send_to_char_color( "You feeling a little less plastered.\n\r", ch ); } } else if (ch->pcdata->condition[iCond] >= get_curr_con(ch) * 3) { if ( old_condition < ch->pcdata->condition[iCond] ) { send_to_char_color( "You are getting drunk.\n\r", ch ); } else { send_to_char_color( "You are feeling a little less hammered.\n\r", ch ); } } else if (ch->pcdata->condition[iCond] >= get_curr_con(ch) * 2) { if ( old_condition < ch->pcdata->condition[iCond] ) { send_to_char_color( "You are feeling light headed.\n\r", ch ); } else { send_to_char_color( "You are feeling a little less drunk.\n\r", ch ); } } else if (ch->pcdata->condition[iCond] >= get_curr_con(ch)) { if ( old_condition < ch->pcdata->condition[iCond] ) { send_to_char_color( "You are feeling a little tipsy.\n\r", ch ); } else { send_to_char_color( "You are feeling less light headed.\n\r", ch ); } } break; case COND_AIR: if (ch->pcdata->condition[iCond] <= 0) { if (ch->position < POS_STUNNED) { damage( ch, ch, 9, TYPE_NOFIGHT, NULL ); update_pos(ch,-1); } else if (!IS_AWAKE(ch)) { send_to_char_color( "{118}You cease struggling as your vision fades...\n\r", ch); damage( ch, ch, ch->hit + 1, TYPE_NOFIGHT, NULL ); } else if (!swim_check(ch, 0 - ch->pcdata->condition[iCond])) { act( "{118}You gasp and panic trying to get air in your lungs!", ch, NULL, NULL, TO_CHAR); act( "{148}$n gasps and panics trying to fill $s lungs!", ch, NULL, NULL, TO_ROOM); ch->nonlethal = UMIN(ch->nonlethal + get_max_hit(ch)/3, ch->hit); } else { act( "{148}You manage to hold your breath just a little bit longer...", ch, NULL, NULL, TO_CHAR); } } else if (ch->pcdata->condition[iCond] <= 5) { act( "{118}You can't hold your breath much longer!", ch, NULL, NULL, TO_CHAR); } break; } pop_call(); return; } /* * controls the addition/subraction of favor from * a PCs deity. Pass -1 in Domain to ignore domains - Kregor */ bool gain_favor( CHAR_DATA *ch, int Domain, int gain ) { int God; push_call("gain_favor(%p,%p,%p)",ch,Domain,gain); if (IS_NPC(ch)) { pop_call(); return FALSE; } if ((God = which_god(ch)) == GOD_NEUTRAL) { pop_call(); return FALSE; } // Pass -1 if favor awarded regardless of domain, otherwise, trap here. if (Domain != -1) { if (!god_table[God].domain[Domain]) { pop_call(); return FALSE; } } if (ch->pcdata->domain[Domain]) gain *= 2; ch->pcdata->god_favor = URANGE(-1000, ch->pcdata->god_favor + gain, 1000); if (gain < 0 && ch->pcdata->god_favor < 0) { send_to_char("{038}Your god is ignoring you.", ch); } pop_call(); return TRUE; } void obj_program_update (void) { push_call("obj_program_update()"); pop_call(); return; } void mob_program_update (void) { CHAR_DATA *ich; AREA_DATA *area; int vnum; push_call("mob_program_update()"); for (area = mud->f_area ; area ; area = area->next) { if (!area->nplayer) // no reason to trigger if no one is in area - Kregor continue; for (vnum = area->low_m_vnum ; vnum <= area->hi_m_vnum ; vnum++) { if (mob_index[vnum] == NULL || mob_index[vnum]->first_instance == NULL) { continue; } if (IS_SET(mob_index[vnum]->progtypes, RAND_PROG) || mob_index[vnum]->spec_fun) { for (ich = mob_index[vnum]->first_instance ; ich ; ich = mud->update_ich) { mud->update_ich = ich->next_instance; if (!MP_VALID_MOB(ich)) { continue; } /* if rand prog triggers, it overrides spec_fun, otherwise pass to spec_fun - Kregor */ if (IS_SET(mob_index[vnum]->progtypes, RAND_PROG) && ich->position > POS_SLEEPING && !in_combat(ich)) { if (mprog_percent_check(ich, NULL, NULL, NULL, RAND_PROG)) continue; } if (mob_index[vnum]->spec_fun) { (*ich->pIndexData->spec_fun) (ich); } } } } } pop_call(); return; } void room_program_update (void) { AREA_DATA *area; ROOM_INDEX_DATA *room; int vnum; push_call("mob_program_update()"); for (area = mud->f_area ; area ; area = area->next) { if (!area->nplayer) // rand progs do not trigger if no one is in area - Kregor continue; for (vnum = area->low_m_vnum ; vnum <= area->hi_m_vnum ; vnum++) { if ((room = room_index[vnum]) == NULL || !room->nplayer) // rand progs do not trigger without someone in the room either - Kregor { continue; } if (IS_SET(room->progtypes, RAND_PROG)) { rset_supermob(room); rprog_percent_check(supermob, NULL, NULL, NULL, RAND_PROG); release_supermob(); } } } pop_call(); return; } void auto_char_save (void) { PLAYER_GAME *npl; CHAR_DATA *oldest; CLAN_DATA *oldest_clan, *clan; int oldest_time, oldest_clan_time, total_clans; static int save_delay; static int clan_delay; push_call("auto_char_save()"); if (save_delay >= 0) { save_delay--; } else { save_delay = 600 / PULSE_CHARSAVE / UMAX(1, mud->total_plr); oldest = NULL; oldest_time = mud->current_time; for (npl = mud->f_player ; npl ; npl = npl->next) { if (npl->ch->pcdata->last_saved < oldest_time) { oldest_time = npl->ch->pcdata->last_saved; oldest = npl->ch; } } if (oldest) { save_char_obj(oldest, NORMAL_SAVE); } for (npl = mud->f_player ; npl ; npl = npl->next) { if (npl->ch->timer > 30 && (!npl->ch->pcdata->switched || npl->ch->timer > 60) && (!IS_IMMORTAL(npl->ch) || !is_desc_valid(npl->ch))) { char_from_room(npl->ch); char_to_room(npl->ch, npl->ch->pcdata->was_in_room, TRUE); do_quit(npl->ch, NULL); break; } } } if (clan_delay >= 0) { clan_delay--; } else { for (total_clans = 0, clan = mud->f_clan ; clan ; clan = clan->next) { total_clans++; } clan_delay = 3600 / PULSE_CHARSAVE / UMAX(1, total_clans); oldest_clan_time = mud->current_time; oldest_clan = NULL; for (clan = mud->f_clan ; clan ; clan = clan->next) { if (clan->last_saved < oldest_clan_time) { oldest_clan_time = clan->last_saved; oldest_clan = clan; } } if (oldest_clan) { save_clan(oldest_clan); oldest_clan->last_saved = mud->current_time; } } pop_call(); return; } void auto_area_save (void) { push_call("auto_area_save(void)"); do_savearea(NULL, "forreal"); pop_call(); return; } void shop_update (void) { OBJ_DATA *obj; CHAR_DATA *ich; AREA_DATA *area; int vnum; push_call("shop_update()"); for (area = mud->f_area ; area ; area = area->next) { for (vnum = area->low_m_vnum ; vnum <= area->hi_m_vnum ; vnum++) { if (mob_index[vnum] == NULL || mob_index[vnum]->pShop == NULL) { continue; } for (ich = mob_index[vnum]->first_instance ; ich ; ich = ich->next_instance) { for (obj = ich->last_carrying ; obj ; obj = obj->prev_content) { if (obj->reset == NULL) { if (number_bits(3) == 0) { act ("$n sells $p to a shopper.", ich, obj, NULL, TO_ROOM); junk_obj(obj); } } else { break; } } } } } pop_call(); return; } void mobile_update (void) { CHAR_DATA *ich; CHAR_DATA *rch; AREA_DATA *area; EXIT_DATA *pExit; OBJ_DATA *obj, *obj_best; int vnum, door, max; push_call("mobile_update()"); for (area = mud->f_area ; area ; area = area->next) { for (vnum = area->low_m_vnum ; vnum <= area->hi_m_vnum ; vnum++) { if (mob_index[vnum] == NULL || mob_index[vnum]->first_instance == NULL) { continue; } for (ich = mob_index[vnum]->first_instance ; ich ; ich = mud->update_ich) { mud->update_ich = ich->next_instance; if (ich->in_room && ich != supermob && ich->in_room->vnum == ROOM_VNUM_JUNK) { junk_mob(ich); continue; } // purge mprog loaded mobs if area is clear - Kregor if (ich->npcdata->mloaded && !ich->in_room->area->nplayer) { junk_mob(ich); continue; } // put hunting code here for now, may be better place - Kregor if (hunt_victim(ich)) continue; // added to make sentinel mobiles return to their reset room, to // foil exploit of shoving/baiting away from their station - Kregor if (IS_ACT(ich, ACT_SENTINEL) && !ich->rewalkto && !ich->walkto) { if (!ich->reset) continue; if (ich->master) continue; if (in_combat(ich)) continue; if (IS_AFFECTED(ich, AFF_DOMINATE)) continue; if (IS_AWAKE(ich) && ich->position != ich->pIndexData->position) { switch (ich->pIndexData->position) { default: break; case POS_SITTING: do_sit(ich, ""); break; case POS_RESTING: do_rest(ich, ""); break; case POS_KNEELING: do_kneel(ich, ""); break; case POS_CROUCHING: do_crouch(ich, ""); break; case POS_STANDING: do_stand(ich, ""); break; } continue; } if (ich->in_room->vnum == ich->reset->arg3) continue; if (IS_ACT(ich, ACT_WILL_DIE)) continue; if ((door = findpath_room(ich, ich->reset->arg3, 400)) < 0 || !is_valid_exit(ich, ich->in_room, door)) { act( "$n leaves.", ich, NULL, NULL, TO_ROOM); char_from_room(ich); char_to_room(ich, ich->reset->arg3, TRUE); act( "$n arrives.", ich, NULL, NULL, TO_ROOM); continue; } move_char( ich, door, TRUE, NULL ); if (ich->reset->arg3 == ich->in_room->vnum) { mprog_arrival_trigger(ich); } } if (ich->rewalkto != 0 && ich->walkto == 0) { ich->walkto = ich->rewalkto; ich->rewalkto = 0; continue; } if (ich->walkto != 0) { if (in_combat(ich)) continue; if ((door = findpath_room(ich, ich->walkto, 400)) == -2) { ich->walkto = 0; continue; } else if (!MP_VALID_MOB(ich)) { ich->walkto = 0; continue; } else if (ich->position < POS_STANDING) { do_stand(ich, ""); continue; } else if (door == -1) { if (CAN_TALK(ich)) do_say(ich, "Where was I going again?"); ich->walkto = 0; continue; } if( IS_SET( ich->in_room->exit[door]->exit_info, EX_CLOSED ) ) { if (!is_handy(ich)) { ich->walkto = 0; continue; } do_open( ich, (char *) dir_name[door] ); if ( IS_SET( ich->in_room->exit[door]->exit_info, EX_CLOSED ) ) ich->walkto = 0; continue; } else if (!is_valid_exit(ich, ich->in_room, door)) { if (CAN_TALK(ich)) do_say(ich, "Where was I going again?"); ich->walkto = 0; continue; } else { move_char( ich, door, TRUE, NULL ); if (ich->walkto == ich->in_room->vnum) { mprog_arrival_trigger(ich); ich->walkto = 0; } continue; } } // } // // for (ich = mob_index[vnum]->first_instance ; ich ; ich = mud->update_ich) // { // mud->update_ich = ich->next_instance; if (ich->in_room && ich->in_room->area->nplayer == 0 && number_bits(6)) { continue; } if (!IS_SET(mob_index[vnum]->act, ACT_SENTINEL)) { if (!MP_VALID_MOB(ich)) continue; if (ich->position != POS_STANDING) continue; if (ich->walkto != 0) continue; if (IS_SET(ich->act, ACT_SENTINEL)) continue; if (!ich->npcdata->hate_fear && number_bits(4) != 0) continue; door = number_door(); if ((pExit = ich->in_room->exit[door]) == NULL) continue; if (room_index[pExit->to_room] == NULL) continue; if (IS_SET(room_index[pExit->to_room]->room_flags, ROOM_NO_MOB)) continue; if (IS_SET(ich->act, ACT_NOWANDER) && ich->reset && room_index[pExit->to_room]->sector_type != room_index[ich->reset->arg3]->sector_type) continue; if (IS_SET(ich->act, ACT_STAY_AREA) && room_index[pExit->to_room]->area->low_r_vnum != mob_index[ich->pIndexData->vnum]->area->low_r_vnum) continue; if (!is_valid_exit(ich, ich->in_room, door)) continue; if (room_index[pExit->to_room]->area == ich->in_room->area) { move_char( ich, door, TRUE, NULL ); continue; } } if (IS_SET(mob_index[vnum]->act, ACT_SCAVENGER)) { if (!MP_VALID_MOB(ich)) continue; if (ich->position != POS_STANDING) continue; if (ich->in_room->first_content == NULL) continue; if (ich->carry_number >= 10) continue; if (number_bits(4)) continue; max = 1; obj_best = NULL; for (obj = ich->in_room->first_content ; obj ; obj = obj->next_content) { if (IS_SET(obj->wear_flags, CAN_WEAR_TAKE) && obj->cost > max) { obj_best = obj; max = obj->cost; } } if (obj_best) { char objName[MAX_INPUT_LENGTH]; sprintf (objName, "i%u", obj_best->pIndexData->vnum); do_get (ich, objName); do_wear(ich, objName); } } // if no one in area with NPC, go no further with this loop - Kregor if (ich->in_room && ich->in_room->area->nplayer == 0) { continue; } if (!ich->npcdata->hate_fear) continue; if (!MP_VALID_MOB(ich)) continue; if (ich->position < POS_STANDING) continue; if (!IS_SET(ich->act, ACT_WIMPY)) { if (!in_combat(ich)) { for (rch = ich->in_room->first_person ; rch ; rch = rch->next_in_room) { if (IS_NPC(rch)) continue; if (ich->npcdata->hate_fear == rch->pcdata->pvnum) { found_hating(ich, rch); break; } } /* added for pet assisting of master */ if (IS_SET(ich->act, ACT_NOASSIST)) continue; if (!ich->master || !in_same_room(ich, ich->master)) continue; if (!in_combat(ich->master) || !who_fighting(ich->master)) continue; fight(ich, who_fighting(ich->master)); break; } else if (ich->master) { if (!in_same_room(ich, ich->master) || !in_combat(ich->master)) //pet will not fight without master! { char_from_combat(ich); break; } /* end adding of pet assist */ } continue; } else { if (ich->hit > get_max_hit(ich) / 2) continue; if (in_combat(ich)) { do_withdraw(ich, NULL); continue; } door = number_door(); if ((pExit = get_exit(ich->in_room->vnum, door)) == NULL) continue; if (IS_SET(pExit->exit_info, EX_CLOSED)) continue; if (IS_SET(room_index[pExit->to_room]->room_flags, ROOM_NO_MOB)) continue; if (IS_SET(ich->act, ACT_NOWANDER) && ich->reset && room_index[pExit->to_room]->sector_type != room_index[ich->reset->arg3]->sector_type) continue; if (IS_SET(ich->act, ACT_STAY_AREA) && room_index[pExit->to_room]->area->low_r_vnum != mob_index[ich->pIndexData->vnum]->area->low_r_vnum) continue; for (rch = ich->in_room->first_person ; rch ; rch = rch->next_in_room) { if (IS_NPC(rch)) continue; if (ich->npcdata->hate_fear == rch->pcdata->pvnum) { char buf[MAX_INPUT_LENGTH]; if (CAN_TALK(ich)) { switch (number_bits (2)) { case 0: sprintf (buf, "Help! %s is trying to kill me!", capitalize(adjective(rch))); break; case 1: sprintf (buf, "Help! I'm being attacked by %s!", adjective(rch)); break; case 2: sprintf (buf, "Stay away from me muderer! Help!!"); break; case 3: sprintf (buf, "Someone help me! %s is attacking me!", adjective(rch)); break; } do_shout (ich, buf); } else { switch (number_bits(2)) { case 0: act( "$n looks around frantically as $e searches for an escape!", ich, NULL, NULL, TO_ROOM); break; case 1: act( "$n darts around mindlessly in an attempt to get away!", ich, NULL, NULL, TO_ROOM); break; case 2: act( "$n lets out a whimper and tries to flee!", ich, NULL, NULL, TO_ROOM); break; case 3: act( "$n howls in fear as $e attempts to flee!", ich, NULL, NULL, TO_ROOM); break; } } break; } } if (rch) { move_char( ich, door, TRUE, NULL ); } } } } } pop_call(); return; } /* Update the weather - Scandum 22-06-2003 */ void weather_area_update( AREA_DATA *area ) { char buf[MAX_STRING_LENGTH]; PLAYER_GAME *gch; int temp, season, daily; buf[0] = '\0'; /* Calculate temperature in degrees Celcius */ season = area->weather_info->temp_summer - area->weather_info->temp_winter; daily = area->weather_info->temp_daily; temp = area->weather_info->temp_winter; temp += season * ( 8 - abs(mud->time_info->month - 6)) / 6; temp += daily * (12 - abs(mud->time_info->hour - 12)) / 12; /* Calculate the wind speed based on current wind speed 0 - 10 */ if (area->weather_info->wind_speed > area->weather_info->wind_scale + 1) { area->weather_info->wind_speed += number_range(0, 3) - 2; } else if (area->weather_info->wind_speed < area->weather_info->wind_scale - 1) { area->weather_info->wind_speed += number_range(0, 3) - 1; } else { area->weather_info->wind_speed += number_range(0, 2) - 1; } area->weather_info->wind_speed = URANGE(-10, area->weather_info->wind_speed, 20); /* Calculate the wind direction, based on current wind direction */ area->weather_info->wind_dir = abs(area->weather_info->wind_dir + number_range(0, 2) - 1) % 8; /* Calculate the weather, based on current weather */ if (area->weather_info->change < area->weather_info->wet_scale - 1) { area->weather_info->change += number_range(0, 3) - 1; } else if (area->weather_info->change > area->weather_info->wet_scale + 1) { area->weather_info->change += number_range(0, 3) - 2; } else { area->weather_info->change += number_range(0, 4) - 2; } area->weather_info->change = URANGE(-100, area->weather_info->change, 110); /* Modify temperature based on weather */ temp += 5 - URANGE(0, area->weather_info->change, 10); area->weather_info->temperature = temp; switch (area->weather_info->sky) { default: bug ("Weather_update: bad sky %d.", area->weather_info->sky); area->weather_info->sky = SKY_CLOUDLESS; break; case SKY_CLOUDLESS: if (area->weather_info->change > 3) { switch (number_bits(2)) { case 0: cat_sprintf(buf, "Threatening clouds gather on the %s horizon, blocking the sky from view.\n\r", wind_dir_name[area->weather_info->wind_dir]); break; case 1: cat_sprintf(buf, "Low clouds form on the %s horizon and slowly drift towards you.\n\r", wind_dir_name[area->weather_info->wind_dir]); break; case 2: cat_sprintf(buf, "Clouds sweep quickly across the %s horizon, darkening the skies above.\n\r", wind_dir_name[area->weather_info->wind_dir]); break; case 3: cat_sprintf(buf, "Bright white clouds drift lazily towards you from the %s horizon.\n\r", wind_dir_name[area->weather_info->wind_dir]); break; } area->weather_info->sky = SKY_CLOUDY; } break; case SKY_CLOUDY: if (area->weather_info->change > 6) { if (area->weather_info->temperature < 0) { switch (number_bits(2)) { case 0: strcat(buf, "Snowflakes flutter down from the leaden skies above.\n\r"); break; case 1: strcat(buf, "Lacy flakes of snow silently fall to the ground from the clouds above.\n\r"); break; case 2: strcat(buf, "The clouds above give way to softly falling snow.\n\r"); break; case 3: strcat(buf, "Snowflakes whisper down from the skies in a soft white dance.\n\r"); break; } } else if (area->weather_info->temperature < 4) { switch (number_bits(2)) { case 0: strcat(buf, "Snow begins to meld with icy rain as sleet falls to the ground.\n\r"); break; case 1: strcat(buf, "Sharp sleet stings as it falls from the clouds above.\n\r"); break; case 2: strcat(buf, "Icy sleet rains down from the heavens.\n\r"); break; case 3: strcat(buf, "Sheets of stinging sleet fall from the dark clouds.\n\r"); break; } } else { switch (number_bits(2)) { case 0: strcat(buf, "Dark clouds break open, pouring rain down upon the lands.\n\r"); break; case 1: strcat(buf, "Silvery drops of rain fall from angry looking clouds above.\n\r"); break; case 2: strcat(buf, "Soft ribbons of rain slip unheeded from the dark skies overhead.\n\r"); break; case 3: strcat(buf, "Fat raindrops merrily pepper the lands from the clouds looming above.\n\r"); break; } } area->weather_info->change += 2; area->weather_info->sky = SKY_RAINING; } else if (area->weather_info->change < 4) { switch (number_bits(2)) { case 0: strcat(buf, "The clouds recede, revealing a crystal clear sky.\n\r"); break; case 1: strcat(buf, "Dark clouds dip below the horizon as the skies clear.\n\r"); break; case 2: strcat(buf, "The clouds swiftly move across the skies as it grows clear.\n\r"); break; case 3: strcat(buf, "Clouds sigh softly as they disperse, leaving the skies clear.\n\r"); break; } area->weather_info->change -= 2; area->weather_info->sky = SKY_CLOUDLESS; } break; case SKY_RAINING: if (area->weather_info->change < 7) { if (area->weather_info->temperature < 0) { switch (number_bits(2)) { case 0: strcat(buf, "A few last snowflakes swirl over the ground as the snow stops.\n\r"); break; case 1: strcat(buf, "The world is left in silence as the snows stop falling.\n\r"); break; case 2: strcat(buf, "The lands are blanketed in white as the last snowflakes fall.\n\r"); break; case 3: strcat(buf, "With a final whisper, the snow stops falling.\n\r"); break; } } else if (area->weather_info->temperature < 4) { switch (number_bits(2)) { case 0: strcat(buf, "The final slivers of sleet fall from the dark clouds as it stops.\n\r"); break; case 1: strcat(buf, "The miserable sleet falling from the clouds above stops.\n\r"); break; case 2: strcat(buf, "The slippery razor-sharp sleet ends its assault on the lands.\n\r"); break; case 3: strcat(buf, "The stinging sleet suddenly ceases as quickly as it began.\n\r"); break; } } else { switch (number_bits(2)) { case 0: strcat(buf, "The air smells fresh as the rain stops falling.\n\r"); break; case 1: strcat(buf, "The clouds above dry their tears as the rain ceases.\n\r"); break; case 2: strcat(buf, "The greyness above seems to ease as the rains halt.\n\r"); break; case 3: strcat(buf, "A sweet scent fills the air as the rain suddenly stops.\n\r"); break; } } area->weather_info->change -= 1; area->weather_info->sky = SKY_CLOUDY; } else if (area->weather_info->wind_speed > 6) { if (area->weather_info->temperature < 0) { switch (number_bits(2)) { case 0: strcat(buf, "A ferocious blizzard sweeps through the lands, burying them in white.\n\r"); break; case 1: strcat(buf, "Heavy snows swirl in the air as the blizzard rages.\n\r"); break; case 2: strcat(buf, "The snows fall heavily upon the ground as a blizzard takes hold.\n\r"); break; case 3: strcat(buf, "Snowdrifts form quickly as the blizzard increases its fierceness.\n\r"); break; } } else if (area->weather_info->temperature < 4) { switch (number_bits(2)) { case 0: strcat(buf, "The clouds darken and thicken as they fill with hail.\n\r"); break; case 1: strcat(buf, "Huge chunks of hail fall to the grounds with a sizzle.\n\r"); break; case 2: strcat(buf, "Hail bombards the ground as creatures run for cover.\n\r"); break; case 3: strcat(buf, "A shower of hail falls from the sky to pelts the lands below.\n\r"); break; } } else { switch (number_bits(2)) { case 0: strcat(buf, "The lands glow blue-white as lightning cracks across the sky.\n\r"); break; case 1: strcat(buf, "Electricity fills the air as forks of lightning dance over the sky.\n\r"); break; case 2: strcat(buf, "The horizon shines brightly as sheet lightning flashes in a quick stutter.\n\r"); break; case 3: strcat(buf, "The winds gather speed as lightning paints the skies with broad strokes.\n\r"); break; } } area->weather_info->wind_speed += 2; area->weather_info->sky = SKY_LIGHTNING; } break; case SKY_LIGHTNING: if (area->weather_info->change < 7 || area->weather_info->wind_speed < 7) { if (area->weather_info->temperature < 0) { switch (number_bits(2)) { case 0: strcat(buf, "The thick, swirling snow peters out as the blizzard ends.\n\r"); break; case 1: strcat(buf, "The ferocity of the blizzard gives way as the snows diminish.\n\r"); break; case 2: strcat(buf, "With a last gasp, the blizzard dwindles away to nothing.\n\r"); break; case 3: strcat(buf, "With a final stroke of white, the blizzard dies out.\n\r"); break; } } else if (area->weather_info->temperature < 4) { switch (number_bits(2)) { case 0: strcat(buf, "The cannonade of hail comes to a sudden halt.\n\r"); break; case 1: strcat(buf, "The burning hail ends its furious salvo of the lands.\n\r"); break; case 2: strcat(buf, "The icy hot hail diminishes, retreating to the clouds above.\n\r"); break; case 3: strcat(buf, "As swiftly as it started, the hailstorm recedes.\n\r"); break; } } else { switch (number_bits(2)) { case 0: strcat(buf, "With a few last flashes, the lightning moves down the horizon.\n\r"); break; case 1: strcat(buf, "The skies turn ice blue and brilliant white with a final flash of lightning.\n\r"); break; case 2: strcat(buf, "The heavens calm as the lightning storm ends.\n\r"); break; case 3: strcat(buf, "The air is alive with electricity as the last fork of lightning appears.\n\r"); break; } } area->weather_info->wind_speed -= 2; area->weather_info->sky = SKY_RAINING; } break; } return; //disable echoes for now! - Kregor if (area->nplayer <= 0 || buf[0] == '\0') { return; } for (gch = mud->f_player ; gch ; gch = gch->next) { if (gch->ch->in_room->area != area) { continue; } if (!IS_OUTSIDE(gch->ch) || NO_WEATHER_SECT(gch->ch->in_room->sector_type)) { continue; } if (!IS_AWAKE(gch->ch)) { continue; } send_to_char(justify(buf, get_page_width(gch->ch)), gch->ch); } } void weather_update (void) { AREA_DATA *area; push_call("weather_update()"); for (area = mud->f_area ; area ; area = area->next) { weather_area_update(area); } pop_call(); return; } void strip_greater (char *str) { char *pt; push_call("strip_greater(%p)",str); for (pt = str; *pt != '\0'; pt++) { if (*pt == '<') { *pt = '('; } else if (*pt == '>') { *pt = ')'; } } pop_call(); return; } /* Make a html web page - Chaos 3/28/96 */ void save_html_who (void) { FILE *fp; char buf[MAX_STRING_LENGTH], buf_race[20]; char buf2[MAX_STRING_LENGTH]; char t1[MAX_STRING_LENGTH]; int leng; CHAR_DATA *fch; DESCRIPTOR_DATA *d; int nMatch, nTotal, cnt; CHAR_DATA *wch; char class[MAX_INPUT_LENGTH]; PLAYER_GAME *fpl; char *pt; push_call("save_html_who()"); pop_call(); return; close_reserve(); fp = my_fopen ("../public_html/who.html", "w",TRUE); if (fp == NULL) { open_reserve(); pop_call(); return; } fprintf (fp, "<!DOCTYPE html PUBLIC \"-//IETF//DTD// HTML 2.0//EN\">\n"); fprintf (fp, "<BODY BACKGROUND=\"bumps1.jpg\" text=#000000 alink=#80FF30 vlink=#90FF30 link=#FFFF30 >\n"); fprintf (fp, "<HTML><HEAD><TITLE>E-Mud Who List</TITLE></HEAD>\n"); fprintf (fp, "<BODY><FONT SIZE=+2><CENTER>\n"); fprintf (fp, "Shining Sands WHO Page<p>\n"); /* Set default arguments. */ fch = NULL; /* Now show matching chars. */ nMatch = 0; nTotal = 0; buf[0] = '\0'; leng = 0; for (fpl = mud->f_player ; fpl ; fpl = fpl->next) { wch = fpl->ch; d = NULL; if (is_desc_valid (wch)) d = wch->desc; /* Check for match against restrictions. * Don't use trust as that exposes trusted mortals. * Chaos set to see all chars, invis or not. */ if (IS_ACT(wch, PLR_WIZINVIS) || IS_ACT(wch, PLR_WIZCLOAK)) { continue; } nTotal++; nMatch++; //string the multiclasses together. for (class[0] = '\0', cnt = 0 ; cnt < MAX_CLASS ; cnt++) { if (!class_level(wch, cnt)) continue; cat_sprintf(class, "%3s %-2d ", class_table[cnt].who_name, class_level(wch, cnt)); } strcpy (buf_race, str_resize(race_table[wch->race].race_name, t1, -7)); /* * Format it up. */ sprintf (buf2, "%s%s", wch->name, IS_NPC(wch) ? "the monster" : wch->pcdata->title); buf2[71] = '\0'; strip_greater (buf2); while (strlen (buf2) < 71) { str_cat_max (buf2, " ", MAX_STRING_LENGTH); } leng = str_apd_max (buf, buf2, leng, MAX_STRING_LENGTH); if (wch->pcdata->switched || wch->desc != NULL) { strcpy (buf2, wch->in_room->area->name); buf2[8] = '\0'; while (strlen (buf2) < 8) { str_cat_max (buf2, " ", MAX_STRING_LENGTH); } } else if (wch->desc == NULL) { strcpy (buf2, "LinkLost"); } else { strcpy (buf2, "Unknown "); /* Stealth Mode */ } leng = str_apd_max (buf, " [", leng, MAX_STRING_LENGTH); leng = str_apd_max (buf, buf2, leng, MAX_STRING_LENGTH); leng = str_apd_max (buf, "] ", leng, MAX_STRING_LENGTH); if (wch->pcdata->html_address != NULL && *wch->pcdata->html_address != '\0') { strcpy (buf2, wch->pcdata->html_address); for (pt = buf2; *pt != '\0'; pt++) { if (*pt == '*') { *pt = '~'; } } leng = str_apd_max (buf, " Home Page: <a href=\"http://", leng, MAX_STRING_LENGTH); leng = str_apd_max (buf, buf2, leng, MAX_STRING_LENGTH); leng = str_apd_max (buf, "\">", leng, MAX_STRING_LENGTH); leng = str_apd_max (buf, buf2, leng, MAX_STRING_LENGTH); leng = str_apd_max (buf, "</a>", leng, MAX_STRING_LENGTH); } sprintf(buf2, "<BR>  %s - %s", buf_race, class); leng = str_apd_max (buf, buf2, leng, MAX_STRING_LENGTH); leng = str_apd_max (buf, "<p>\n", leng, MAX_STRING_LENGTH); } sprintf (buf2, "Players: %d</CENTER></font><p><font size=-1>\n", nTotal); fprintf (fp, "%s", buf2); fprintf (fp, "%s", buf); fprintf (fp, "</font></BODY></HTML>\n"); if(fp) /* this prevents the possibility to my_fclose(NULL) - Manwe, 15-10-2000 */ { my_fclose (fp); } open_reserve(); pop_call(); return; } void vengeance_storm( ROOM_TIMER_DATA *rtd ) { ROOM_INDEX_DATA *room; CHAR_DATA *ch; CHAR_DATA *vch; push_call("vengeance_storm(%p,%p)",rtd); if ((room = room_index[rtd->vnum]) == NULL) { pop_call(); return; } if ((ch = get_caster_room(rtd)) == NULL) { send_to_room("{108}The storm suddenly dissipates!\n\r", room); del_room_timer(rtd->vnum, rtd->type); pop_call(); return; } if ((vch = room->first_person) == NULL) { pop_call(); return; } switch (rtd->duration) { default: break; case 8: act("{128}Caustic acid rain falls on the area!", vch, NULL, NULL, TO_ALL); spell_damage(ch, vch, dice(1,6), gsn_acid_rain, rtd->level); break; case 6: act("{178}Lightning cracks wildly!", vch, NULL, NULL, TO_ALL); spell_damage(ch, vch, dice(1,10), gsn_call_lightning, rtd->level); break; case 4: act("{168}Hail beats down upon you!", vch, NULL, NULL, TO_ALL); spell_damage(ch, vch, dice(5,6), gsn_ice_storm, rtd->level); break; } pop_call(); return; } void lightning_storm( ROOM_TIMER_DATA *rtd ) { ROOM_INDEX_DATA *room; CHAR_DATA *ch; CHAR_DATA *vch, *vch_next; int cnt, rdm, dam, sizedice, numdice; push_call("vengeance_storm(%p,%p)",rtd); if ((room = room_index[rtd->vnum]) == NULL) { del_room_timer(rtd->vnum, rtd->type); pop_call(); return; } if ((ch = get_caster_room(rtd)) == NULL || ch->in_room != room) { send_to_room("{108}The storm suddenly dissipates!\n\r", room); del_room_timer(rtd->vnum, rtd->type); pop_call(); return; } if ( ch->in_room->area->weather_info->sky >= SKY_RAINING ) sizedice = 10; else sizedice = 6; if (rtd->type == gsn_call_lightning) numdice = 3; else numdice = 5; dam = spell_dice(ch, rtd->type, numdice, sizedice); do_mpareaecho(ch, "{178}Lightning crashes wildly in the area!"); for (cnt = 0, vch = room->first_person ; vch ; vch = vch_next) { vch_next = vch->next; if (!can_mass_cast(ch, vch, rtd->type)) continue; if (is_same_group(ch, vch)) continue; if (!who_fighting(vch)) continue; if (who_fighting(vch) != ch && !is_same_group(who_fighting(vch), ch)) continue; cnt++; } rdm = number_range(1,cnt); for (cnt = 0, vch = room->first_person ; vch ; vch = vch_next) { vch_next = vch->next; if (!can_mass_cast(ch, vch, rtd->type)) continue; if (is_same_group(ch, vch)) continue; if (!who_fighting(vch)) continue; if (who_fighting(vch) != ch && !is_same_group(who_fighting(vch), ch)) continue; cnt++; if (cnt == rdm) { spell_damage(ch, vch, dam, rtd->type, rtd->level); break; } } pop_call(); return; } /* * triggered every 6 seconds per D20 SRD * character updates switch to turn-based when * in combat - Kregor */ void round_update( void ) { CHAR_DATA *ch; OBJ_DATA *obj; push_call("round_update()"); /* * Let's base affect durations on the melee round, * instead of by the hour - Kregor 4/15/07 */ for (ch = mud->f_char ; ch ; ch = mud->update_wch) { mud->update_wch = ch->next; if (ch->hit > get_max_hit(ch)) ch->hit = get_max_hit(ch); /* updates in combat are in turn time */ if (in_combat(ch)) continue; ch->action = 0; if (IS_STAGGERED(ch)) ch->action = ACTION_MOVE; ch->attack = 0; damage_update(ch); affect_update(ch); obj_char_update(ch); pray_update(ch); } ROOM_TIMER_DATA *rtd, *rtd_next; for (rtd = mud->f_room_timer ; rtd ; rtd = rtd_next) { rtd_next = rtd->next; if (--rtd->duration <= 0) { if (skill_table[rtd->type].msg_off) { rset_supermob(room_index[rtd->vnum]); act( skill_table[rtd->type].msg_off, supermob, NULL, NULL, TO_ROOM); release_supermob(); } del_room_timer(rtd->vnum, rtd->type); } else { if (rtd->type == gsn_storm_of_vengeance) { vengeance_storm(rtd); } if (rtd->type == gsn_call_lightning_storm) { lightning_storm(rtd); } } } for (obj = mud->f_obj ; obj ; obj = mud->update_obj) { AFFECT_DATA *paf, *paf_next; mud->update_obj = obj->next; if (obj->carried_by) // controlled by obj_char_update continue; for (paf = obj->first_affect ; paf ; paf = paf_next) { paf_next = paf->next; /* obj carried are updated by obj_char_update */ if (paf->duration >= 0 && --paf->duration <= 0) { if (obj->in_room && obj->in_room->first_person) { act( skill_table[paf->type].msg_obj_off, obj->in_room->first_person, obj, NULL, TO_ALL); } affect_from_obj(obj, paf); } } } pop_call(); return; } void time_update (void) { char buf[MAX_INPUT_LENGTH]; CHAR_DATA *ch; OBJ_DATA *obj; SHOP_DATA *pShop; AREA_DATA *area; PLAYER_GAME *gpl; ROOM_INDEX_DATA *room; int vnum; push_call("time_update()"); mud->time_info->hour++; if (mud->time_info->hour >= 24) { mud->time_info->hour = 0; mud->time_info->day++; } if (mud->time_info->day >= 30) { mud->time_info->day = 0; mud->time_info->month++; } if (mud->time_info->month >= 12) { mud->time_info->month = 0; mud->time_info->year++; } switch (mud->time_info->hour) { case 5: mud->sunlight = SUN_RISE; break; case 6: mud->sunlight = SUN_LIGHT; break; case 11: mud->sunlight = SUN_NOON; break; case 15: mud->sunlight = SUN_LIGHT; break; case 19: mud->sunlight = SUN_SET; break; case 20: mud->sunlight = SUN_DARK; break; } for (area = mud->f_area ; area ; area = area->next) { if (area->nplayer == 0) { continue; } buf[0] = '\0'; switch (mud->time_info->hour) { case 5: sprintf(buf, "{168}The day has begun.{300}\n\r"); break; case 6: sprintf(buf, "{138}The sun rises in the east.{300}\n\r"); break; case 19: sprintf(buf, "{018}The sun slowly disappears in the west.{300}\n\r"); break; case 20: sprintf(buf, "{048}The night has begun.{300}\n\r"); break; } if (buf[0] == '\0') { continue; } for (gpl = mud->f_player ; gpl ; gpl = gpl->next) { if (gpl->ch->in_room->area == area && IS_OUTSIDE(gpl->ch) && !NO_WEATHER_SECT(gpl->ch->in_room->sector_type) && IS_AWAKE(gpl->ch)) { send_to_char_color(buf, gpl->ch); } } } for (ch = mud->f_char ; ch ; ch = mud->update_wch) { mud->update_wch = ch->next; if (!MP_VALID_MOB(ch)) { if (ch->desc && CH(ch->desc)->pcdata->vt100 == 1) { vt100prompt (ch); } continue; } if (IS_NPC(ch) && (pShop = ch->pIndexData->pShop) != NULL) { if (pShop->open_hour != 0 || pShop->close_hour != 23) { if (mud->time_info->hour == pShop->open_hour - 1) act( "$n prepares to open $s shop.", ch, NULL, NULL, TO_ROOM); else if (mud->time_info->hour == pShop->open_hour) act( "$n opens $s shop.", ch, NULL, NULL, TO_ROOM); else if (mud->time_info->hour == pShop->close_hour - 1) act( "$n prepares to close $s shop.", ch, NULL, NULL, TO_ROOM); else if (mud->time_info->hour == pShop->close_hour) act( "$n closes $s shop.", ch, NULL, NULL, TO_ROOM); } } if (IS_SET(ch->pIndexData->progtypes, TIME_PROG)) { if (!MP_VALID_MOB(ch) || in_combat(ch)) { continue; } mprog_time_check(ch, NULL, NULL, NULL, TIME_PROG); } if (IS_SET(ch->pIndexData->progtypes, DAY_PROG) && mud->time_info->hour == 0) { if (!MP_VALID_MOB(ch) || in_combat(ch)) { continue; } mprog_time_check(ch, NULL, NULL, NULL, DAY_PROG); } if (IS_SET(ch->pIndexData->progtypes, MONTH_PROG) && mud->time_info->hour == 0 && mud->time_info->day == 0) { if (!MP_VALID_MOB(ch) || in_combat(ch)) { continue; } mprog_time_check(ch, NULL, NULL, NULL, MONTH_PROG); } } for (obj = mud->f_obj ; obj ; obj = mud->update_obj) { if (IS_SET(obj->pIndexData->progtypes, TIME_PROG)) { oprog_time_check(NULL, NULL, obj, NULL, TIME_PROG); } if (IS_SET(obj->pIndexData->progtypes, DAY_PROG) && mud->time_info->hour == 0) { oprog_time_check(NULL, NULL, obj, NULL, DAY_PROG); } if (IS_SET(obj->pIndexData->progtypes, MONTH_PROG) && mud->time_info->hour == 0 && mud->time_info->day == 0) { oprog_time_check(NULL, NULL, obj, NULL, MONTH_PROG); } } /* room progs only trigger if there is a char to see them */ for (vnum = 0 ; vnum < MAX_VNUM ; vnum++) { if ((room = room_index[vnum]) == NULL) continue; if (!room->first_person) continue; if (IS_SET(room->progtypes, TIME_PROG)) { rprog_time_check(NULL, room->first_person, NULL, NULL, TIME_PROG); } if (IS_SET(room->progtypes, DAY_PROG)) { rprog_time_check(NULL, room->first_person, NULL, NULL, DAY_PROG); } if (IS_SET(room->progtypes, MONTH_PROG)) { rprog_time_check(NULL, room->first_person, NULL, NULL, MONTH_PROG); } } pop_call(); return; } /* * hourly character updates */ void condition_update (void) { CHAR_DATA *ch; push_call("condition_update()"); for (ch = mud->f_char ; ch ; ch = mud->update_wch) { mud->update_wch = ch->next; // keep hunger and thirst out of newbie area - Kregor if (in_area(ch, ROOM_VNUM_SCHOOL)) { continue; } if (!IS_NPC(ch)) { // Delay Poison staves drunkenness, but delays recovery also. if (!is_affected(ch, gsn_delay_poison)) { gain_condition (ch, COND_DRUNK, -8); } if (must_eat(ch)) { if (ch->in_room && (ch->in_room->vnum != ROOM_VNUM_LIMBO && ch->in_room->area->low_r_vnum != ROOM_VNUM_ABYSS)) { gain_condition (ch, COND_FULL, -1); gain_condition (ch, COND_THIRST, -1); } } } disease_update(ch); check_exposure(ch); gain_favor(ch, -1, -1); } pop_call(); return; } /* * Per minute character updates */ void char_update (void) { CHAR_DATA *ch, *attacker; push_call("char_update()"); for (ch = mud->f_char ; ch ; ch = mud->update_wch) { mud->update_wch = ch->next; if (!IS_NPC(ch)) { if (ch->gold < 0) { ch->gold = 0; } if (ch->level < LEVEL_IMMORTAL) { if (ch->level < ch->in_room->area->low_hard_range || ch->level > ch->in_room->area->hi_hard_range) { char_to_room(ch, ROOM_VNUM_TEMPLE, TRUE); ch->pcdata->death_room = ch->pcdata->recall = ROOM_VNUM_TEMPLE; } } // if (IS_SET(ch->act, PLR_KILLER) && (ch->pcdata->played - ch->pcdata->killer_played) > 60 * 60 * 24) // { // REMOVE_BIT (ch->act, PLR_KILLER); // } // // if (IS_SET(ch->act, PLR_OUTCAST) && (ch->pcdata->played - ch->pcdata->outcast_played) > 60 * 60 * 24) // { // REMOVE_BIT (ch->act, PLR_OUTCAST); // } // if (ch->pcdata->just_died_ctr > 0) { if (--ch->pcdata->just_died_ctr == 0) { send_to_char ("The Gods are no longer protecting you.\n\r", ch); break; } } if (++ch->timer > 20) { if (ch->in_room && (ch->in_room->vnum != ROOM_VNUM_LIMBO && ch->in_room->area->low_r_vnum != ROOM_VNUM_ABYSS)) { if (!ch->pcdata->switched && !IS_IMMORTAL(ch)) { ch->pcdata->was_in_room = ch->in_room->vnum; if (in_combat(ch)) { withdraw_combat(ch); } act( "$n wanders off distractedly.", ch, NULL, NULL, TO_CAN_SEE); act( "You wander off distractedly.", ch, NULL, NULL, TO_CHAR); save_char_obj(ch, NORMAL_SAVE); char_from_room(ch); char_to_room(ch, ROOM_VNUM_LIMBO, TRUE); } } } ch->pcdata->idle++; } if (ch->in_room && (ch->in_room->vnum != ROOM_VNUM_LIMBO && ch->in_room->area->low_r_vnum != ROOM_VNUM_ABYSS)) { if (ch->position > POS_INCAP) { recovery_update(ch); if (ch->move < get_max_move(ch)) { ch->move += move_gain(ch); } } if (ch->position == POS_INCAP) { if (ch->hit < 0) { if ((attacker = get_char_pvnum(ch->critical_hit_by)) == NULL) { attacker = ch; } if (get_apply(ch, APPLY_REGENERATION) > 0) { damage(attacker, ch, 0, TYPE_NOFIGHT, NULL); } else if (learned(ch, gsn_diehard) || fort_save(ch, NULL, 10 - ch->hit, -1)) { act( "{018}You become more stable...", ch, NULL, NULL, TO_CHAR); act( "{018}$n stirs in $s comatose state.", ch, NULL, NULL, TO_ROOM); ch->hit++; } else { act( "{018}You slip a little further towards oblivion...", ch, NULL, NULL, TO_CHAR); act( "{018}$n is slipping away slowly...", ch, NULL, NULL, TO_ROOM); damage(attacker, ch, 1, TYPE_NOFIGHT, NULL); } } } } if (IS_NPC(ch)) { if (ch->npcdata->pvnum_last_hit != 0) { if (ch->hit == get_max_hit(ch)) { ch->npcdata->pvnum_last_hit = 0; ch->npcdata->hate_fear = 0; } } if (!in_combat(ch)) { if (IS_SET(ch->pIndexData->affected_by, AFF_INVISIBLE) && !IS_SET(ch->affected_by, AFF_INVISIBLE)) { act( "$n {108}fades out of view.", ch, NULL, NULL, TO_ROOM); SET_BIT(ch->affected_by, AFF_INVISIBLE ); } if (IS_SET(ch->pIndexData->affected_by, AFF_HIDE) && !IS_SET(ch->affected_by, AFF_HIDE)) { act( "$n {108}slips quietly into the shadows.", ch, NULL, NULL, TO_ROOM); SET_BIT(ch->affected_by, AFF_HIDE); } } } update_pos(ch,-1); if (ch->position == POS_DEAD) { raw_kill(ch, -1); continue; } if (ch->poison) poison_update(ch); if (!MP_VALID_MOB(ch)) { if (ch->desc && CH(ch->desc)->pcdata->vt100 == 1) { vt100prompt (ch); } continue; } if (ch->timer > 0) { ch->timer--; if (ch->timer == 0) { mprog_delay_trigger(ch, ch->npcdata->delay_index); } } } static int lastHour; mud->usage->players[mud->time.tm_hour][mud->time.tm_wday] = mud->total_plr; if (lastHour != mud->time.tm_hour) { save_usage(); save_hiscores(); bounty_update(); save_timeinfo(); /* Check for Clan Rent, Purger, Backup - Scandum 03-09-2002 */ if (mud->time.tm_hour == 0) { log_printf("Backing up player files."); system("cd ..;./backup&"); } if (mud->time.tm_wday == 0 && mud->time.tm_hour == 0) { if (IS_SET(mud->flags, MUD_CLANRENT)) { if (mud->f_clan == NULL) { log_printf("There are no clans."); } else { do_forcerent(NULL, NULL); } REMOVE_BIT(mud->flags, MUD_CLANRENT); start_purger(); } } else if (mud->time.tm_hour != 0) { SET_BIT(mud->flags, MUD_CLANRENT); } } lastHour = mud->time.tm_hour; /* Minutely update on the WHO HTML Page - Chaos 3/26/96 */ if (IS_SET(mud->flags, MUD_EMUD_REALGAME)) { save_html_who(); } pop_call(); return; } /* * poison continuous damage, brand new treatment - Kregor * Mechanic - Must make a new save each minute or take * secondary damage. Poison purges from system after two * successful saves. */ void poison_update( CHAR_DATA *ch ) { POISON_DATA *pd, *pd_next; CHAR_DATA *attacker; AFFECT_DATA af; int location; push_call("poison_update(%p)",ch); if (!valid_victim(ch)) { pop_call(); return; } if (!valid_victim(ch)) { pop_call(); return; } if (!ch->poison) { pop_call(); return; } for (pd = ch->poison ; pd ; pd = pd_next) { pd_next = pd->next; if ((attacker = get_char_pvnum(pd->poisoner)) == NULL) { attacker = ch; } /* delay poison keeps poison from affecting, but also from going away */ if (is_affected(ch, gsn_delay_poison)) { continue; } if (!fort_save(ch, NULL, pd->dc ? pd->dc : poison_table[pd->type].dc, gsn_poison_attack)) { if ((location = poison_table[pd->type].sec_loc) > APPLY_NONE) { act( "{128}You don't feel so well...", ch, NULL, NULL, TO_CHAR); act( "{128}$n doesn't look so well.", ch, NULL, NULL, TO_ROOM); if (pd->type == POISON_SLEEP_POISON) { if (IS_AWAKE(ch) && !is_immune(ch, SDESC_SLEEP)) { act("You feel woozy and collapse.", ch, NULL, NULL, TO_CHAR); act("$n collapses into a deep sleep.", ch, NULL, NULL, TO_ROOM); ch->position = POS_SLEEPING; } } else if (location == APPLY_HIT) { int roll = dice(poison_table[pd->type].sec_nodice, poison_table[pd->type].sec_sizedice); damage(attacker, ch, roll, gsn_poison_attack, NULL); } else { af.type = gsn_ability_damage; af.duration = -1; af.location = location; af.modifier = 0 - dice(poison_table[pd->type].sec_nodice, poison_table[pd->type].sec_sizedice); af.bittype = AFFECT_TO_NONE; af.bitvector = AFF_NONE; af.level = pd->dc ? pd->dc : poison_table[pd->type].dc; affect_join( attacker, ch, &af ); } } } else // Should come to a 2 save total { if (pd->constant_duration == -1) { pd->constant_duration = 1; } else { pd->constant_duration--; } if (pd->constant_duration == 0) { ch->poison = ch->poison->next; FREEMEM(pd); } } update_pos(ch,-1); } pop_call(); return; } /* * Disease continuous damage - Kregor * Mechanic - each update ticks up incubation, once * incubation reaches the incubation time of disease, * damage is dealt unless save. Each update thereafter * check save again. Tally consecutive saves. If meets * the number needed to overcome the disease, it purges. * * Still need to run check across other char_in_room to * see if they catch from infected char. */ void disease_update( CHAR_DATA *ch ) { DISEASE_DATA *dis, *dis_next; CHAR_DATA *vch, *vch_next; AFFECT_DATA af; int location = APPLY_NONE; push_call("disease_update(%p)",ch); if (!valid_victim(ch)) { pop_call(); return; } if (!ch->first_disease) { pop_call(); return; } for (dis = ch->first_disease ; dis ; dis = dis_next) { dis_next = dis->next; if (dis->incubation < disease_table[dis->type].incubation) { dis->incubation++; continue; } if (!fort_save(ch, NULL, dis->dc ? dis->dc : disease_table[dis->type].dc, gsn_disease_attack)) { act( disease_table[dis->type].echo_char, ch, NULL, NULL, TO_CHAR); act( disease_table[dis->type].echo_room, ch, NULL, NULL, TO_ROOM); af.type = gsn_ability_damage; af.duration = -1; af.location = location; af.modifier = 0 - dice(1, disease_table[dis->type].damdice); af.bittype = AFFECT_TO_NONE; af.bitvector = AFF_NONE; af.level = dis->dc ? dis->dc : disease_table[dis->type].dc; affect_join( ch, ch, &af ); if (dis->type == DIS_BLINDING_SICKNESS && af.modifier < -2) { if (!fort_save(ch, NULL, dis->dc ? dis->dc : disease_table[dis->type].dc, gsn_disease_attack)) { SET_AFFECT(ch, AFF_BLIND); act("{108}You can no longer see anything!", ch, NULL, NULL, TO_CHAR); } } dis->saves = 0; // failed save cancels save tally } else // disease cured after consecutive saves. { dis->saves++; if (dis->saves >= disease_table[dis->type].saves_cure) { disease_from_char(ch, dis); } } update_pos(ch,-1); } //And now the nasty part, infecting others if (ch->in_room) { for (vch = ch->in_room->first_person ; vch ; vch = vch_next) { vch_next = vch->next; if (!fort_save(vch, NULL, dis->dc ? dis->dc : disease_table[dis->type].dc, gsn_disease_attack)) { infect_char(NULL, vch, dis->type); } } } pop_call(); return; } /* * Use in round_update when PC is is praying position - Kregor */ void pray_update ( CHAR_DATA *ch ) { OBJ_DATA *altar = NULL; int God, Dom; push_call("pray_update(%p)",ch); if (IS_NPC(ch)) { pop_call(); return; } if ((altar = ch->furniture) == NULL || !IS_SET(altar->value[2], FURN_KNEEL_AT) || ch->position != POS_KNEELING) { pop_call(); return; } // If deity matches, award favor and go no further if ((God = altar->value[5]) > GOD_NEUTRAL && God == which_god(ch)) { gain_favor(ch, -1, 4); pop_call(); return; } // if deity opposes CH deity, award no favor if (god_table[God].faith_enemy[which_god(ch)]) { pop_call(); return; } // If at least one aspect of deity matches CH deity, award half favor. for (Dom = 0 ; Dom < MAX_DOMAIN ; Dom++) { if (god_table[God].domain[Dom] && god_table[which_god(ch)].domain[Dom]) { gain_favor(ch, -1, 2); pop_call(); return; } } gain_favor(ch, -1, 1); pop_call(); return; } /* Update all objs per minute */ void obj_update (void) { OBJ_DATA *obj; CHAR_DATA *carrier; char *message; push_call("obj_update()"); for (obj = mud->f_obj ; obj ; obj = mud->update_obj) { mud->update_obj = obj->next; if (obj->in_room && obj->in_room->vnum == ROOM_VNUM_JUNK) { junk_obj(obj); continue; } /* Look for enhanced objects */ if (obj->item_type != obj->pIndexData->item_type) { obj->item_type = obj->pIndexData->item_type; obj->value[0] = obj->pIndexData->value[0]; obj->value[1] = obj->pIndexData->value[1]; obj->value[2] = obj->pIndexData->value[2]; obj->value[3] = obj->pIndexData->value[3]; log_printf("obj_update: bad item type: %d", obj->pIndexData->vnum); } switch (obj->item_type) { case ITEM_CONTAINER: case ITEM_QUIVER: case ITEM_SHEATH: case ITEM_SPELLPOUCH: if (obj->carried_by == NULL && IS_SET(obj->value[1], CONT_CLOSEABLE) && !IS_SET(obj->value[1], CONT_CLOSED)) { SET_BIT(obj->value[1], CONT_CLOSED); if (obj->value[2] > 0) { SET_BIT(obj->value[1], CONT_LOCKED); } } break; case ITEM_LIGHT: if (obj->value[0] != obj->pIndexData->value[0]) { obj->value[0] = obj->pIndexData->value[0]; } if (obj->value[3] && !IS_BURNING(obj)) break; if (IS_WORN(obj) && (carrier = obj->carried_by) != NULL && obj->carried_by->desc && obj->carried_by->desc->connected >= CON_PLAYING) { if (obj->value[2] > 0) { if (--obj->value[2] == 0) { act ("$p goes out.", obj->carried_by, obj, NULL, TO_ALL); if (!obj->value[1]) junk_obj(obj); if (carrier->in_room) carrier->in_room->light = calc_room_light(carrier, NULL, carrier->in_room, TRUE); continue; } if (obj->value[2] == 1) { act ("$p flickers.", obj->carried_by, obj, NULL, TO_CHAR); } } } break; case ITEM_TREASURE: // should reset uses per day at midnight - Kregor if (!IS_SET(obj->value[0], TFLAG_SPELL_RECAST)) { continue; } if (obj->value[2] >= obj->value[1]) { continue; } if (mud->time_info->hour != 0) { continue; } obj->value[2] = obj->value[1]; break; case ITEM_FIRE: if (obj->in_room) { if (obj->value[2] > 0) { if (--obj->value[2] == 0) { send_to_room(format("%s goes out.\n\r", obj->short_descr), obj->in_room); obj->in_room->light = calc_room_light(NULL, obj, obj->in_room, FALSE); junk_obj(obj); continue; } if (obj->value[2] == 1) { send_to_room(format("%s flickers.\n\r", obj->short_descr), obj->in_room); } } } break; case ITEM_TOTEM: totem_cast_spell(obj); break; } if (!IS_SET(obj->extra_flags, ITEM_NOT_VALID) && !IS_SET(obj->pIndexData->extra_flags, ITEM_NOT_VALID)) { if (obj->timer == 0 && obj->sac_timer == 0) { continue; } if (obj->in_room == NULL) { if (obj->timer == 0 || (obj->timer > 0 && --obj->timer > 0)) { continue; } } if (obj->in_room != NULL) { if (IS_SET(obj->in_room->room_flags, ROOM_CLANSTOREROOM)) { if (obj->timer == 0 || (obj->timer > 0 && --obj->timer > 0)) { continue; } } else { if ((obj->timer == 0 || (obj->timer > 0 && --obj->timer > 0)) && (obj->sac_timer == 0 || (obj->sac_timer > 0 && --obj->sac_timer > 0))) { continue; } } } } switch (obj->item_type) { default: message = "$p vanishes."; break; case ITEM_FOUNTAIN: message = "$p dries up."; break; case ITEM_CORPSE_NPC: case ITEM_CORPSE_PC: message = "$p decays into dust."; break; case ITEM_FOOD: message = "$p decomposes."; break; case ITEM_TRAP: message = ""; break; } /* traps do not echo when they purge */ if (!IS_OBJ_TYPE(obj, ITEM_TRAP)) { if (obj->carried_by != NULL) { act (message, obj->carried_by, obj, NULL, TO_CHAR); } else if (obj->in_room != NULL && obj->in_room->first_person != NULL) { act (message, obj->in_room->first_person, obj, NULL, TO_ROOM); act (message, obj->in_room->first_person, obj, NULL, TO_CHAR); } } while (obj->first_content) { if (obj->carried_by) { obj_to_char(obj->first_content, obj->carried_by); } else if (obj->in_room) { obj->first_content->sac_timer = OBJ_SAC_TIME; obj_to_room(obj->first_content, obj->in_room->vnum); } else { junk_obj(obj->first_content); } } junk_obj(obj); } pop_call(); return; } void aggr_update (void) { CHAR_DATA *ich; AREA_DATA *area; int vnum; push_call("aggr_update()"); for (area = mud->f_area ; area ; area = area->next) { for (vnum = area->low_m_vnum ; vnum <= area->hi_m_vnum ; vnum++) { if (mob_index[vnum] == NULL) { continue; } if (!IS_SET(mob_index[vnum]->progtypes, DELAY_PROG)) { continue; } for (ich = mob_index[vnum]->first_instance ; ich ; ich = mud->update_ich) { mud->update_ich = ich->next_instance; if (ich->wait > 0 && !ich->desc) { ich->wait--; if (ich->wait == 0 && MP_VALID_MOB(ich)) { mprog_delay_trigger(ich, ich->npcdata->delay_index); } } } } } // moved to mprog greet function // for (npl = mud->f_player ; npl ; npl = next_npl) // { // next_npl = npl->next; // // if (npl->ch->level >= LEVEL_IMMORTAL) // { // continue; // } // // if (npl->ch->desc == NULL && npl->ch->wait > 0) // { // npl->ch->wait = UMAX(0, npl->ch->wait - PULSE_AGGRESSIVE); // } // // for (ch = npl->ch->in_room->first_person ; ch ; ch = mud->update_rch) // { // mud->update_rch = ch->next_in_room; // // if (!IS_NPC(ch) || !IS_SET(ch->act, ACT_AGGRESSIVE)) // { // continue; // } // // if (!MP_VALID_MOB(ch)) // { // continue; // } // // if (!IS_AWAKE(ch) || in_combat(ch) || ch->hit < get_max_hit(ch)/2) // { // continue; // } // // if (IS_SET(ch->act, ACT_WIMPY) && IS_AWAKE(npl->ch)) // { // continue; // } // // if (!can_see(ch, npl->ch) || number_bits(1) == 0) // { // continue; // } // fight(ch, npl->ch); // } // } pop_call(); return; } void purger_update (void) { JUNK_DATA *junk; push_call("purger_update(void)"); while (mud->f_junk) { junk = mud->f_junk; if (junk->mob) { extract_char(junk->mob); } else { extract_obj(junk->obj); } UNLINK(junk, mud->f_junk, mud->l_junk, next, prev); FREEMEM(junk); } pop_call(); return; } /* Called once per pulse from game loop. Update routines spread out over 10 pulses for psuedo-randomness and fluidity - Kregor */ void update_handler (void) { static sh_int pulse_areasave = 9 + PULSE_AREASAVE; static sh_int pulse_shops = 9 + PULSE_SHOPS; static sh_int pulse_area = 8 + PULSE_TICK; static sh_int pulse_weather = 7 + PULSE_TICK; static sh_int pulse_time = 9 + PULSE_TIME; static sh_int pulse_obj = 8 + PULSE_TICK; static sh_int pulse_tick = 7 + PULSE_TICK; static sh_int pulse_charsave = 6 + PULSE_CHARSAVE; static sh_int pulse_mobprog = 4 + PULSE_PROGRAM; static sh_int pulse_objprog = 3 + PULSE_PROGRAM; static sh_int pulse_violence = 2 + PULSE_VIOLENCE; static sh_int pulse_mobile = 1 + PULSE_MOBILE; static sh_int pulse_aggressive = 0 + PULSE_AGGRESSIVE; push_call("update_handler()"); if (--pulse_areasave <= 0) { pulse_areasave = PULSE_AREASAVE; start_timer(TIMER_AREA_SAVE); auto_area_save(); close_timer(TIMER_AREA_SAVE); } if (--pulse_shops <= 0) { pulse_shops = PULSE_SHOPS; start_timer (TIMER_SHOP_UPD); shop_update (); close_timer (TIMER_SHOP_UPD); } if (--pulse_tick <= 0) { pulse_tick = PULSE_TICK; start_timer (TIMER_CHAR_UPD); char_update(); auth_update(); close_timer(TIMER_CHAR_UPD); } if (--pulse_area <= 0) { pulse_area = PULSE_AREA; start_timer (TIMER_AREA_UPD); area_update (); close_timer (TIMER_AREA_UPD); } if (--pulse_weather <= 0) { pulse_weather = PULSE_TICK; start_timer (TIMER_WEATHER_UPD); weather_update(); close_timer (TIMER_WEATHER_UPD); } if (--pulse_time <= 0) { pulse_time = PULSE_TIME; start_timer (TIMER_TIME_UPD); time_update(); condition_update(); close_timer (TIMER_TIME_UPD); } if (--pulse_obj <= 0) { pulse_obj = PULSE_TICK; start_timer (TIMER_OBJ_UPD); obj_update (); close_timer (TIMER_OBJ_UPD); } if (--pulse_charsave <= 0) { pulse_charsave = PULSE_CHARSAVE; start_timer( TIMER_CHAR_SAVE ); auto_char_save(); close_timer( TIMER_CHAR_SAVE ); } if (--pulse_violence <= 0) { pulse_violence = PULSE_VIOLENCE; start_timer (TIMER_VIOL_UPD); round_update (); close_timer (TIMER_VIOL_UPD); } if (--pulse_mobprog <= 0) { pulse_mobprog = PULSE_PROGRAM; start_timer(TIMER_MOB_PROG); mob_program_update(); close_timer(TIMER_MOB_PROG); } if (--pulse_objprog <= 0) { pulse_objprog = PULSE_PROGRAM; start_timer(TIMER_OBJ_PROG); room_program_update(); close_timer(TIMER_OBJ_PROG); } if (--pulse_mobile <= 0) { pulse_mobile = PULSE_MOBILE; start_timer (TIMER_MOB_UPD); mobile_update(); combat_update(); close_timer (TIMER_MOB_UPD); } if (--pulse_aggressive <= 0) { pulse_aggressive = PULSE_AGGRESSIVE; start_timer (TIMER_AGGR_UPD); update_casting(); update_skill_timer(); aggr_update(); asn_update(); close_timer (TIMER_AGGR_UPD); } start_timer (TIMER_PURGE); purger_update(); if (IS_SET(mud->flags, MUD_PURGER)) { update_purger(); } close_timer (TIMER_PURGE); pop_call(); return; } /* * return exp to level for char - Kregor */ int exp_level (CHAR_DATA * ch, int level) { int num, lvl_adj; push_call("exp_level(%p,%p)",ch,level); if (level == 0) { pop_call(); return (0); } lvl_adj = race_table[ch->race].lvl_adj; level += lvl_adj; // dnd formula: level * (level + 1) * 500 = 1000xp * next level num = level * level * 1000; // equivalent to 1000xp * (level + previous level) pop_call(); return ((int) num); } /* * counts down the value of bounty til expiration */ void bounty_update( void ) { BOUNTY_DATA *bounty, *bounty_next; push_call("bounty_update()"); for (bounty = mud->f_bounty ; bounty ; bounty = bounty_next) { bounty_next = bounty->next; if (bounty->expires < mud->current_time) { remove_bounty( bounty ); save_bounties(); } } pop_call(); return; } /* * applies continuous damage per round - Kregor */ bool continuous_damage( CHAR_DATA *ch ) { AFFECT_DATA *paf; CHAR_DATA *attacker; OBJ_DATA *obj; int dam, dt; bool dmg = FALSE; push_call("continuous_damage(%p)",ch); for (paf = ch->first_affect ; paf ; paf = paf->next) { if (!valid_victim(ch)) break; if ((attacker = get_char_world_even_blinded(ch, paf->caster)) == NULL) attacker = ch; dt = paf->type; if (paf->type == gsn_acid_arrow) { dam = dice(2,4); dt = gsn_acid_hit; } else if (paf->type == gsn_insect_plague) { dam = dice(1,6); dt = gsn_insect_plague; } else if (paf->type == gsn_heat_metal || paf->type == gsn_chill_metal) { switch (paf->duration) { default: dam = 0; break; case 2: case 6: dam = dice(1,4); break; case 3: case 4: case 5: dam = dice(2,4); break; } } else if (paf->type == gsn_poison) { if (fort_save(ch, attacker, paf->level, paf->type)) { act("You feel poison leaving your system.", ch, NULL, NULL, TO_CHAR); act("$n seems to shake off $s poison.", ch, NULL, NULL, TO_ROOM); affect_strip(ch, gsn_poison); } else { act("{128}You shiver and suffer.", ch, NULL, NULL, TO_CHAR); act("{128}$n shivers and suffers.", ch, NULL, NULL, TO_ROOM); stat_damage(attacker, ch, APPLY_CON_DAMAGE, gsn_poison); } continue; } else { continue; } dam = damage_modify(attacker, ch, dt, dam, NULL); dam_message(attacker, ch, dam, dt, NULL); damage(attacker, ch, dam, TYPE_NOFIGHT, NULL); if (dam > 0) dmg = TRUE; else continue; if (dt == gsn_heat_metal || dt == gsn_chill_metal) { if (IS_AWAKE(ch)) { if ((obj = get_wield(ch, FALSE)) != NULL && obj->material && material_table[obj->material].parent == MATERIAL_TYPE_METAL) { if (!fort_save(ch, attacker == ch ? NULL : attacker, 2 * dam, dt)) { act("{118}You cannot bear to hold onto $p as it sears you!", ch, obj, NULL, TO_CHAR); act("{118}$n cries out in pain as $e drops $p!", ch, obj, NULL, TO_CHAR); unequip_char(ch, obj, TRUE); } } } } } pop_call(); return dmg; } /* * Per hour environmental effects - Kregor */ void check_exposure( CHAR_DATA *ch ) { int dam = 0; push_call("check_exposure(%p)",ch); if (!valid_victim(ch)) { pop_call(); return; } if (is_affected(ch, gsn_time_stop)) { pop_call(); return; } if (ch->in_room) { pop_call(); return; } // keep exposure effects from happening to newbies - Kregor if (ch->in_room->area->low_r_vnum == ROOM_VNUM_SCHOOL) { pop_call(); return; } if (!IS_NPC(ch)) { switch (ch->in_room->sector_type) { case SECT_DESERT: if (IS_OUTSIDE(ch) && (mud->sunlight == SUN_LIGHT || mud->sunlight == SUN_NOON)) { if (!is_immune(ch, SDESC_FIRE) && !IS_SET(race_table[get_race(ch)].sectors, 1LL << SECT_DESERT) && !has_domain(ch, DOMAIN_WEATHER)) { act("The hot desert sun beats down upon you!", ch, NULL, NULL, TO_CHAR); dam = damage_modify(ch, ch, gsn_heatstroke, dice(1,4), NULL); if (ch->nonlethal < ch->hit) ch->nonlethal += dam; else ch->hit -= dam; update_pos(ch,-1); } } break; case SECT_TUNDRA: if (IS_OUTSIDE(ch)) { if (!is_immune(ch, SDESC_COLD) && !IS_SET(race_table[get_race(ch)].sectors, 1LL << SECT_TUNDRA) && !has_domain(ch, DOMAIN_WEATHER)) { act("The frozen air rattles you to your bones!", ch, NULL, NULL, TO_CHAR); dam = damage_modify(ch, ch, gsn_frostbite, dice(1,6), NULL); if (ch->nonlethal < ch->hit) ch->nonlethal += dam; else ch->hit -= dam; update_pos(ch,-1); } } break; default: if (ch->in_room->area->weather_info->temperature < 0) { if (!is_immune(ch, SDESC_COLD) && !IS_SET(race_table[get_race(ch)].sectors, 1LL << SECT_TUNDRA) && !has_domain(ch, DOMAIN_WEATHER)) { act("The frozen air rattles you to your bones!", ch, NULL, NULL, TO_CHAR); dam = damage_modify(ch, ch, gsn_frostbite, dice(1,6), NULL); if (ch->nonlethal < ch->hit) ch->nonlethal += dam; else ch->hit -= dam; update_pos(ch,-1); } } if (ch->in_room->area->weather_info->temperature >= 40) { if (!is_immune(ch, SDESC_FIRE) && !IS_SET(race_table[get_race(ch)].sectors, 1LL << SECT_DESERT) && !has_domain(ch, DOMAIN_WEATHER)) { act("The hot desert sun beats down upon you!", ch, NULL, NULL, TO_CHAR); dam = damage_modify(ch, ch, gsn_heatstroke, dice(1,4), NULL); if (ch->nonlethal < ch->hit) ch->nonlethal += dam; else ch->hit -= dam; update_pos(ch,-1); } } break; } } pop_call(); return; } /* * Per round damage updates including suffocation and drowning - Kregor */ void damage_update( CHAR_DATA *ch ) { CHAR_DATA *victim; OBJ_DATA *obj; int dam; bool dmg; push_call("damage_update(%p)",ch); if (!valid_victim(ch)) { pop_call(); return; } if (is_affected(ch, gsn_time_stop)) { pop_call(); return; } if (is_affected(ch, gsn_nightmare)) { if (number_percent() < 5 && (obj = get_eq_char(ch, WEAR_FLOAT)) != NULL) { act( "$n screams and flings $p away!", ch, obj, NULL, TO_ROOM); switch (number_range(1,3)) { case 1: act( "The evil leer of a demon appears within the glaring light of $p!", ch, obj, NULL, TO_CHAR); break; case 2: act( "A soul rending shriek is torn from $p!", ch, obj, NULL, TO_CHAR); break; case 3: act( "Images of your painful demise emanate from $p!", ch, obj, NULL, TO_CHAR); break; } act( "You scream and fling $p away!", ch, obj, NULL, TO_CHAR); unequip_char(ch, obj, TRUE); } else if (number_percent() < 5 && (obj = get_eq_char(ch, WEAR_HOLD)) != NULL) { act( "$n screams and flings $p away!", ch, obj, NULL, TO_ROOM); switch (number_range(1,3)) { case 1: act( "$p starts to slowly drain away your dwindling vitality!", ch, obj, NULL, TO_CHAR); break; case 2: act( "A whisper hisses from $p 'You shall do my bidding weakling!", ch, obj, NULL, TO_CHAR); break; case 3: act( "A stabbing pain lances through you from $p!", ch, obj, NULL, TO_CHAR); break; } act( "You scream and fling $p away!", ch, obj, NULL, TO_CHAR); unequip_char(ch, obj, TRUE); } else if ( number_percent() < 5 && (obj = get_eq_char(ch, WEAR_WIELD )) != NULL) { act( "$n screams and flings $p away!", ch, obj, NULL, TO_ROOM); switch (number_range(1,3)) { case 1: act( "The hilt of $p glows red starting to smolder with heat!", ch, obj, NULL, TO_CHAR); break; case 2: act( "Like a viper, $p turns on you and tries to strike!", ch, obj, NULL, TO_CHAR); break; case 3: act( "$p takes on a life of its own, struggling within your grasp!", ch, obj, NULL, TO_CHAR); break; } act( "You scream and fling $p away!", ch, obj, NULL, TO_CHAR); unequip_char(ch, obj, TRUE); } } if (IS_AFFECTED(ch, AFF2_BLEEDING)) { if (get_apply(ch, APPLY_REGENERATION) > 0) // regenerating creatures do not bleed to death { AFFECT_STRIP(ch, AFF2_BLEEDING); } else { AFFECT_DATA *paf; ch_printf_color(ch, "%sYour life is bleeding out of nasty wounds!\n\r", get_color_string(ch, COLOR_YOU_ARE_HIT, VT102_BOLD)); act( "$n's life is bleeding out of nasty wounds!", ch, NULL, NULL, TO_ROOM); for (dam = 0, paf = ch->first_affect ; paf ; paf = paf->next) { if (paf->bitvector == AFF2_BLEEDING) { dam += paf->modifier; } } if (dam < 1) { dam = 1; } if ((victim = get_char_pvnum(ch->critical_hit_by)) == NULL) { victim = ch; } damage(victim, ch, dam, TYPE_NOFIGHT, NULL); } } if (!valid_victim(ch)) { pop_call(); return; } dmg = continuous_damage(ch); if (!IS_NPC(ch) && ch->in_room) { switch (ch->in_room->sector_type) { case SECT_ASTRAL: if (!CAN_ASTRAL_WALK(ch)) { if (in_combat(ch)) { withdraw_combat(ch); } char_from_room (ch); if (room_index[ch->pcdata->last_real_room]->sector_type == SECT_ASTRAL) { char_to_room (ch, ROOM_VNUM_TEMPLE, TRUE); } else { char_to_room (ch, ch->pcdata->last_real_room, TRUE); } } break; case SECT_UNDER_WATER: if (!CAN_BREATH_WATER(ch) && must_breathe(ch)) { gain_condition(ch, COND_AIR, -1); } break; case SECT_LAVA: if (!CAN_FIREWALK(ch)) { if ((dam = damage_modify(ch, ch, gsn_fire_hit, dice(2,6), NULL)) > 0) { send_to_char_color("{118}That lava is REALLY hot!\n\r", ch); damage( ch, ch, dam, TYPE_NOFIGHT, NULL ); } } break; case SECT_LAKE: case SECT_OCEAN: if (!IS_FLYING(ch)) { if (!CAN_BREATH_WATER(ch) && must_breathe(ch)) { if (!CAN_SWIM(ch)) { OBJ_DATA *obj; for (obj = ch->first_carrying ; obj ; obj = obj->next_content) { if (obj->item_type == ITEM_BOAT) { break; } } if (obj == NULL) { swim_check(ch, 0); } } } } break; default: if (must_breathe(ch)) { if (IS_SET(ch->in_room->room_flags, ROOM_NO_AIR) || (IS_AFFECTED(ch, AFF2_DROWNING) && !CAN_BREATH_WATER(ch))) { if (IS_AFFECTED(ch, AFF2_DROWNING)) { ch->pcdata->condition[COND_AIR] = -10; } gain_condition(ch, COND_AIR, -1); } else if (ch->pcdata->condition[COND_AIR] < get_curr_con(ch)*2) { send_to_char("You take a deep breath of fresh air.\n\r", ch); ch->pcdata->condition[COND_AIR] = get_curr_con(ch)*2; } } break; } } if (!valid_victim(ch)) { pop_call(); return; } if (IS_NPC(ch)) { if (ch->npcdata->sac_timer > 0) { ch->npcdata->sac_timer--; if (ch->npcdata->sac_timer == 0) { if (is_string(ch->npcdata->sac_string)) act( ch->npcdata->sac_string, ch, NULL, NULL, TO_ROOM); if (ch->desc && ch->desc->original) do_return(ch, NULL); junk_mob(ch); pop_call(); return; } } if (must_breathe(ch)) { if (IS_AWAKE(ch) && (IS_AFFECTED(ch, AFF2_DROWNING) && !CAN_BREATH_WATER(ch))) { act("{118}$n collapses for lack of air!", ch, NULL, NULL, TO_ROOM); ch->hit = -1; update_pos(ch, -1); } } } int regen; /* regeneration prevents blood loss, dying */ if ((regen = UMIN(get_apply(ch, APPLY_REGENERATION), get_max_hit(ch) - ch->hit)) > 0) { ch_printf_color(ch, "You regenerate %d hitpoint%s.\n\r", regen, regen > 1 ? "s" : ""); ch->hit += regen; update_pos(ch,-1); pop_call(); return; } else if ((regen = get_apply(ch, APPLY_FAST_HEALING)) > 0 && ch->position > POS_MORTAL) { if (ch->hit < get_max_hit(ch)) { regen = UMIN(regen, get_max_hit(ch) - ch->hit); ch_printf_color(ch, "You regain %d hitpoint%s.", regen, regen > 1 ? "s" : ""); ch->hit += regen; } } /* variant rule uses FORT save to stabilize rather than 10% chance. */ if (ch->position == POS_MORTAL) { int dc = 15 - ch->hit; if (!dmg && (learned(ch, gsn_diehard) || fort_save(ch, NULL, dc, -1))) { ch->position = POS_INCAP; act( "$n has stabilized.", ch, NULL, NULL, TO_ROOM); act( "You have stabilized.", ch, NULL, NULL, TO_CHAR); } else { if ((victim = get_char_pvnum(ch->critical_hit_by)) == NULL) { victim = ch; } act( "{018}$n is bleeding to death!", ch, NULL, NULL, TO_ROOM); act( "{018}You are bleeding to death!", ch, NULL, NULL, TO_CHAR); damage( victim, ch, 1, TYPE_NOFIGHT, NULL ); } } update_pos(ch,-1); if (ch->position == POS_DEAD) { raw_kill(ch, -1); } pop_call(); return; } bool check_entangle( CHAR_DATA *ch, ROOM_TIMER_DATA *rtd ) { CHAR_DATA *caster; push_call("check_entangle(%p,%p)",ch,rtd); if (!valid_victim(ch)) { pop_call(); return FALSE; } if ((caster = get_caster_room(rtd)) == NULL) { pop_call(); return FALSE; } if (caster != ch || can_mass_cast(caster, ch, rtd->type)) { if (!IS_ENTANGLED(ch)) { if (rtd->type == gsn_entangle) act( "{128}Plants and vines whip around and snag at you!", ch, NULL, NULL, TO_CHAR); if (rtd->type == gsn_black_tentacles) act( "{108}Black rubbery tentacles flail around you!", ch, NULL, NULL, TO_CHAR); if (!refl_save(ch, NULL, rtd->level, rtd->type)) { AFFECT_DATA af; if (rtd->type == gsn_entangle) act( "{128}Plants and vines whip around and snag $n.", ch, NULL, NULL, TO_ROOM); if (rtd->type == gsn_black_tentacles) act( "{108}Rubbery black tentacles wrap around $n.", ch, NULL, NULL, TO_ROOM); af.type = rtd->type; af.duration = rtd->duration; af.modifier = 0; af.location = APPLY_NONE; af.bittype = AFFECT_TO_CHAR; af.bitvector = AFF2_ENTANGLED; af.level = rtd->level; af.caster = STRALLOC(rtd->caster); affect_join( caster, ch, &af ); } } if (IS_ENTANGLED(ch) && rtd->type == gsn_black_tentacles) { damage(caster, ch, dice(1,6)+4, rtd->type, NULL); } pop_call(); return TRUE; } pop_call(); return FALSE; } bool check_cloud_kill( CHAR_DATA *ch, ROOM_TIMER_DATA *rtd ) { CHAR_DATA *caster; AFFECT_DATA af; int save, dam; push_call("check_cloud_kill(%p,%p)",ch,rtd); if (!valid_victim(ch)) { pop_call(); return FALSE; } if ((caster = get_caster_room(rtd)) == NULL) { pop_call(); return FALSE; } if (caster != ch || can_mass_cast(caster, ch, rtd->type)) { if ((save = save_resist(caster, ch, rtd->type, rtd->level)) == TRUE) { pop_call(); return FALSE; } act("{128}You choke and gag in the noxious clouds.", ch, NULL, NULL, TO_CHAR); act("{128}$n chokes and gags in the noxious clouds.", ch, NULL, NULL, TO_ROOM); if (ch->level < 4 || (ch->level < 6 && !save)) { damage(caster, ch, ch->hit + 11, gsn_cloudkill, NULL); pop_call(); return TRUE; } dam = dice(1,4); if (ch->level > 5 && save) dam /= 2; af.type = gsn_cloudkill; af.duration = -1; af.modifier = 0 - dam; af.location = APPLY_CON_DAMAGE; af.bittype = AFFECT_TO_NONE; af.bitvector = AFF_NONE; af.level = rtd->level; affect_join( caster, ch, &af ); pop_call(); return TRUE; } pop_call(); return FALSE; } bool check_creeping_doom( CHAR_DATA *ch, ROOM_TIMER_DATA *rtd ) { CHAR_DATA *caster; int dam; push_call("check_creeping_doom(%p,%p)",ch,rtd); if (!valid_victim(ch)) { pop_call(); return FALSE; } if ((caster = get_caster_room(rtd)) == NULL) { affect_room_strip(room_index[rtd->vnum], gsn_creeping_doom); pop_call(); return FALSE; } if (caster->in_room->vnum != rtd->vnum || !IS_AWAKE(caster)) { act("{138}The creeping doom disperses without your command.", caster, NULL, NULL, TO_CHAR); affect_room_strip(room_index[rtd->vnum], gsn_creeping_doom); pop_call(); return FALSE; } if (caster != ch || can_mass_cast(caster, ch, rtd->type)) { if (IS_FLYING(ch)) { pop_call(); return FALSE; } if (!CAN_CRITICAL(ch)) { pop_call(); return FALSE; } dam = dice(1,6) + rtd->level; act("{038}Tiny vermin crawl and slither over your body!", ch, NULL, NULL, TO_CHAR); damage(caster, ch, dam, gsn_creeping_doom, NULL); rtd->modifier -= dam; if (rtd->modifier <= 0) { affect_room_strip(room_index[rtd->vnum], gsn_creeping_doom); } } pop_call(); return TRUE; } /* * Called at end of affect_update to check * damage done by room affects - Kregor */ void room_timer_check( CHAR_DATA *ch ) { ROOM_TIMER_DATA *rtd, *rtd_next; push_call("room_timer_check(%p)",ch); for (rtd = mud->f_room_timer ; rtd ; rtd = rtd_next) { rtd_next = rtd->next; if (rtd->vnum != ch->in_room->vnum) continue; if (IS_SET(rtd->bitvector, ROOM_ENTANGLE)) { check_entangle(ch, rtd); } if (rtd->type == gsn_creeping_doom) { check_creeping_doom(ch, rtd); continue; } if (rtd->type == gsn_cloudkill) { check_cloud_kill(ch, rtd); continue; } if (rtd->type == gsn_sanctify || rtd->type == gsn_faithful_hound) { CHAR_DATA *caster; if ((caster = get_caster_room(rtd)) == NULL || caster->in_room->vnum != rtd->vnum) affect_room_strip(room_index[rtd->vnum], rtd->type); } } pop_call(); return; } /* * Update char affects per round * Extensively updated and revised * to support camping and inns - Kregor */ void affect_update( CHAR_DATA *ch ) { AFFECT_DATA *paf, *paf_next; sh_int RecRate; CHAR_DATA *caster; CHAR_DATA *attacker; int aff_level, sn; push_call("affect_update()"); if (!ch->in_room) { pop_call(); return; } // check PC for assist of master if dominated - Kregor if (!IS_NPC(ch) && IS_AFFECTED(ch, AFF_DOMINATE)) { if ((attacker = who_fighting(ch->master)) != NULL) { if (is_affected(ch, gsn_dominate_person)) { sn = gsn_dominate_person; } if (is_affected(ch, gsn_dominate_monster)) { sn = gsn_dominate_monster; } aff_level = get_affect_level(ch, sn); // PC will only attack master's foes if fails a WILL save if (in_combat(ch) && in_same_room(attacker, ch) && IS_NPC(attacker) && !will_save(ch, ch->master, aff_level, sn)) { fight(ch, attacker); } } } if (IS_AFFECTED(ch, AFF2_CAMPING) || IS_SET(ch->in_room->room_flags, ROOM_INN)) { switch(ch->position) { case POS_RESTING: case POS_SLEEPING: RecRate = 10; break; default: RecRate = 1; break; } } else { RecRate = 1; } // time stop prevents ticking of effects - Kregor if (is_affected(ch, gsn_time_stop)) { if ((paf = get_affect_sn(ch, gsn_time_stop)) != NULL) { if ((paf->duration -= RecRate) <= 0) { if (paf->next == NULL || paf->type != paf->next->type) { if (is_string(skill_table[paf->type].msg_off)) { act( skill_table[paf->type].msg_off, ch, NULL, NULL, TO_CHAR); } if (is_string(skill_table[paf->type].msg_off_room)) { act( skill_table[paf->type].msg_off_room, ch, NULL, NULL, TO_ROOM); } } affect_from_char(ch, paf); } } pop_call(); return; } // creatures w/stench force checks every round - Kregor check_stench(ch); if (ch->distracted > 0) { ch->distracted--; if (ch->distracted == 0 && IS_NPC(ch) && ch->position < ch->pIndexData->position) { do_stand(ch, ""); } } if (!IS_AFFECTED(ch, AFF2_FEAR) && ch->fear_level > 0) { ch->fear_level--; if (!ch->fear_level) { act( "You regain your composure from your fear.", ch, NULL, NULL, TO_CHAR); act( "$n regains $s composure from $s fear.", ch, NULL, NULL, TO_ROOM); } } for (paf = ch->first_affect ; paf ; paf = paf_next) { paf_next = paf->next; if (paf->duration < 0) continue; if (paf->type == gsn_circle_against_good || paf->type == gsn_circle_against_evil || paf->type == gsn_circle_against_chaos || paf->type == gsn_circle_against_law || paf->type == gsn_undeath_ward) { if ((caster = get_char_world_even_blinded(ch, paf->caster)) != NULL && caster != ch && !in_same_room(ch, caster)) { act("You've left your protective circle, losing your ward.", ch, NULL, NULL, TO_CHAR); affect_from_char(ch, paf); continue; } } if (paf->type == gsn_mass_invis) { if ((caster = get_char_world_even_blinded(ch, paf->caster)) != NULL && caster != ch && !in_same_room(ch, caster)) { act("You've left your invisibility sphere.", ch, NULL, NULL, TO_CHAR); affect_from_char(ch, paf); continue; } } if (paf->type == gsn_insect_plague) { if ((caster = get_char_world_even_blinded(ch, paf->caster)) == NULL || !in_same_room(ch, caster)) { act("{038}The swarm around you scatters without its master to command it.", ch, NULL, NULL, TO_CHAR); affect_from_char(ch, paf); continue; } } if (paf->type == gsn_entangle || paf->type == gsn_black_tentacles) { if (IS_SET(paf->bitvector, AFF2_ENTANGLED) && !IS_SET(ch->in_room->room_flags, ROOM_ENTANGLE)) { affect_from_char(ch, paf); continue; } } if ((paf->duration -= RecRate) <= 0) { if (paf->type > 0 && (paf->next == NULL || paf->type != paf->next->type)) { if (is_string(skill_table[paf->type].msg_off)) { act( skill_table[paf->type].msg_off, ch, NULL, NULL, TO_CHAR); } if (is_string(skill_table[paf->type].msg_off_room)) { act( skill_table[paf->type].msg_off_room, ch, NULL, NULL, TO_ROOM); } } if (paf->bitvector == AFF_SLEEP) { if (IS_NPC(ch)) { ch->position = ch->pIndexData->position; } else if (ch->position == POS_SLEEPING) { ch->position = POS_RESTING; } } if (IS_SET(skill_table[paf->type].spell_desc, SDESC_POLYMORPH)) { revert_morph(ch, TRUE); continue; } if (IS_SET(paf->bitvector, AFF_DOMINATE)) { stop_follower(ch); } affect_from_char(ch, paf); if (paf->type == gsn_defensive_stance && !learned(ch, gsn_tireless_defense)) { int cost = get_max_move(ch) / 2; move_loss(ch, NULL, cost); } } else { if (paf->type == gsn_elemental_swarm && paf->duration % 10 == 0 && paf->modifier > 0) { (*skill_table[paf->type].spell_fun) (paf->type, paf->level, ch, NULL, TAR_IGNORE); paf->modifier--; if (paf->modifier <= 0) affect_from_char(ch, paf); } if (paf->type == gsn_improved_invis && !IS_AFFECTED(ch, AFF_INVISIBLE)) { act("{068}$n's image flickers away once more!", ch, NULL, NULL, TO_ROOM); SET_BIT(ch->affected_by, AFF_INVISIBLE); } if (paf->type == gsn_irresistible_dance && !in_combat(ch)) { irresistible_dance(ch); } } } if (IS_FLYING(ch) && !CAN_FLY(ch)) { REMOVE_BIT(ch->affected_by, AFF_FLYING); } room_timer_check(ch); pop_call(); return; } /* * Update affects on objs carried by char */ void obj_char_update( CHAR_DATA *ch ) { OBJ_DATA *obj, *obj_next; AFFECT_DATA *paf, *paf_next; int paf_type = 0; push_call("obj_char_update()"); if (is_affected(ch, gsn_time_stop)) { pop_call(); return; } for (obj = ch->first_carrying ; obj ; obj = obj_next) { obj_next = obj->next_content; for (paf = obj->first_affect ; paf ; paf = paf_next) { paf_next = paf->next; if (paf->duration >= 0 && --paf->duration <= 0) { if (paf_type && paf_type == paf->type && is_string(skill_table[paf->type].msg_obj_off)) { act( skill_table[paf->type].msg_obj_off, ch, obj, NULL, TO_CHAR); if (ch->in_room) act( skill_table[paf->type].msg_obj_off, ch, obj, NULL, TO_ROOM); } affect_from_obj(obj, paf); paf_type = paf->type; } } if (ch->desc && IS_SET(obj->pIndexData->progtypes, RAND_PROG)) { set_supermob(obj); oprog_percent_check(supermob, NULL, obj, NULL, RAND_PROG); release_supermob(); } } pop_call(); return; } /* * Variant recovery system for SRD works with * spell and endurance points. Gain 1/3 of mana * and move with 1 game hour rest, 2/3 on the * second hour. Must rest a full 8 game hours * to get full point recovery. Hitpoints and * nonlethal damage only gain with full 8 hours, * per standard recovery rates. * Camping or inn rest speeds recovery time 10x - Kregor */ void recovery_update ( CHAR_DATA *ch ) { int gain, cnt, hpgain, mvgain, dc, sn; AFFECT_DATA *paf, *paf_next; push_call("recovery_update(%p)",ch); if (IS_NPC(ch) || in_camp(ch) || IS_SET(ch->in_room->room_flags, ROOM_INN)) { gain = 10; } else { gain = 1; } mvgain = UMAX(1, stat_bonus(TRUE, ch, APPLY_CON)); if (learned(ch, gsn_endurance)) mvgain *= 2; if (IS_NPC(ch) || (ch->pcdata->condition[COND_FULL] > 0 && ch->pcdata->condition[COND_THIRST] > 0)) { if (ch->position != POS_FIGHTING) { if (ch->position <= POS_RESTING) { mvgain *= 2; } ch->move = UMIN(mvgain * gain + ch->move, get_max_move(ch)); } } switch (ch->position) { case POS_STUNNED: case POS_SLEEPING: case POS_RESTING: break; default: pop_call(); return; } if (!IS_NPC(ch) && (ch->pcdata->condition[COND_FULL] <= 0 || ch->pcdata->condition[COND_THIRST] <= 0)) { send_to_char("You cannot gain benefit from your rest until you eat and drink.", ch); pop_call(); return; } ch->rest += gain; if (gain == 10 && ch->rest % 10 != 0) ch->rest -= ch->rest % 10; // Here's where all the good healing is done! if (ch->rest >= 80) { send_to_char("You've rested fully.\n\r", ch); if (!IS_AFFECTED(ch, AFF2_LONGTERM_CARE)) { hpgain = ch->level + stat_bonus(FALSE, ch, APPLY_CON); if (ch->hit < get_max_hit(ch)) { ch->hit = UMIN(ch->hit + hpgain, get_max_hit(ch)); } if (ch->nonlethal > 0) { ch->nonlethal = UMAX(0, ch->nonlethal - (hpgain * 2)); } } else { ch->hit = get_max_hit(ch); ch->nonlethal = 0; REMOVE_AFFECT(ch, AFF2_LONGTERM_CARE); } restore_mana(ch); for (cnt = 0 ; *skill_table[cnt].name != '\0' ; cnt++) ch->uses[cnt] = 0; ch->move = get_max_move(ch); ch->rest = 0; if (!IS_NPC(ch)) ch->pcdata->condition[COND_DRUNK] = 0; // auto recovery of level drain and stat damage (but not stat drain) for (paf = ch->first_affect ; paf ; paf = paf_next) { paf_next = paf->next; // if ((sn = paf->type) != gsn_energy_drain) if ((sn = paf->type) != gsn_cha_damage) if ((sn = paf->type) != gsn_con_damage) if ((sn = paf->type) != gsn_dex_damage) if ((sn = paf->type) != gsn_int_damage) if ((sn = paf->type) != gsn_str_damage) if ((sn = paf->type) != gsn_wis_damage) continue; dc = paf->level; if (fort_save(ch, NULL, dc, sn)) { if (paf->modifier++ >= 0) { act( "You recover fully from $t.", ch, skill_table[sn].noun_damage, NULL, TO_CHAR); affect_from_char(ch, paf); } else { act( "You recover partially from $t.", ch, skill_table[sn].noun_damage, NULL, TO_CHAR); } } } if (!IS_NPC(ch) && IS_SET(ch->in_room->room_flags, ROOM_INN)) { ch->pcdata->condition[COND_FULL] = max_hunger(ch); ch->pcdata->condition[COND_THIRST] = max_thirst(ch); } else if (!IS_NPC(ch) && in_camp(ch)) { if (survival_check(ch, NULL, survival_roll(ch), 10)) { ch->pcdata->condition[COND_FULL] = max_hunger(ch); ch->pcdata->condition[COND_THIRST] = max_thirst(ch); } } } else if (ch->rest == 10) { mana_gain(ch, 1); if (ch->move < get_max_move(ch) / 3) ch->move = get_max_move(ch) / 3; } else if (ch->rest == 20) { mana_gain(ch, 2); if (ch->move < get_max_move(ch) * 2 / 3) ch->move += get_max_move(ch) * 2 / 3; } pop_call(); return; } /* * Countdown an assassination attempt - Kregor * victim gets a sense motive roll each round to detect the attempt */ void asn_update(void) { CHAR_DATA *ch; CHAR_DATA *victim; push_call("asn_update()"); for (ch = mud->f_char ; ch ; ch = mud->update_wch) { mud->update_wch = ch->next; if (ch->asn_count > 0) { ch->asn_count--; if ((victim = ch->assassinate) == NULL) { pop_call(); return; } if (ch->asn_count > 0 && victim->in_room != ch->in_room) { act( "Your target must have slipped out of sight.", ch, NULL, NULL, TO_CHAR); ch->assassinate = NULL; ch->asn_count = 0; pop_call(); return; } if (in_combat(ch)) { act( "You lost your opportunity to assassinate $N.", ch, NULL, victim, TO_CHAR); ch->assassinate = NULL; ch->asn_count = 0; pop_call(); return; } if (ch->asn_count <= 0) { act( "You lost your opportunity to assassinate $N.", ch, NULL, victim, TO_CHAR); ch->assassinate = NULL; pop_call(); return; } if (ch->asn_count % 12 == 0) { if (ch->asn_count <= 36) { act( "You have determined $N to be opportune for a killing blow.", ch, NULL, victim, TO_CHAR); pop_call(); return; } else { if (IS_AWAKE(victim) && can_see(victim, ch) && sense_motive_check(victim, ch, sense_motive_roll(victim), bluff_roll(ch))) { act( "$N {118}eyes you suspiciously.", ch, NULL, victim, TO_CHAR); act( "{138}$n eyes you like a stalker sizing up $s kill.", ch, NULL, victim, TO_VICT); ch->assassinate = NULL; ch->asn_count = 0; pop_call(); return; } else { act( "You continue to study $N for an opportunity...", ch, NULL, victim, TO_CHAR); } } } } } pop_call(); return; } /* * free the time delay of a skill */ void free_skill( CHAR_DATA *ch ) { push_call("free_skill(%p)",ch); ch->concentrating = FALSE; ch->skill_timer = 0; ch->timer_fun = NULL; STRFREE(ch->cmd_argument); pop_call(); return; } /* * Controls when characters complete a timed skill. */ void update_skill_timer(void) { CHAR_DATA *ch; CHAR_DATA *rch, *rch_next; int roll; push_call("update_skill_timer()"); for (ch = mud->f_char ; ch ; ch = mud->update_wch) { mud->update_wch = ch->next; if (ch->concentrating) { if (!valid_victim(ch)) { free_skill(ch); } if (ch->timer_fun == NULL) { free_skill(ch); } if (in_combat(ch) && !is_active(ch)) continue; ch->skill_timer--; /* NPC opponents might take opportunity if they perceive they can. */ if (ch->skill_timer == 6) { for (rch = ch->in_room->first_person ; rch ; rch = rch_next) { rch_next = rch->next_in_room; if (!IS_NPC(rch)) continue; if (wis_roll(rch) < 10) continue; if (who_fighting(rch) && who_fighting(rch) == ch) { attack_of_opportunity(rch, ch); } } } if (ch->skill_timer <= 0) { if (IS_ENTANGLED(ch) || drunk_level(ch) >= DRUNK_TIPSY || ch->grappling || ch->grappled_by || is_affected(ch, gsn_insect_plague)) { roll = concentration_roll(ch); int DC; if (ch->grappled_by) DC = 10 + combat_maneuver_bonus(ch->grappled_by); else DC = 15; if ((ch->timer_fun == do_pick || ch->timer_fun == do_disable) && learned(ch, gsn_skill_mastery)) roll += 4; if (!concentration_check(ch, NULL, roll, DC)) { send_to_char_color("{138}You lost your concentration!\n\r", ch); free_skill(ch); turn_complete(ch); pop_call(); return; } } command(ch, ch->timer_fun, ch->cmd_argument); free_skill(ch); turn_complete(ch); continue; } else if (ch->skill_timer % 8 == 0) { if (in_combat(ch) && is_active(ch)) { turn_end(ch->in_battle->turn_list, 200 - ch->initiative); continue; } } } } pop_call(); return; }