/***************************************************************************
* 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;
}