/*
* $Id: update.c,v 1.97 1998/12/22 19:03:03 fjoe Exp $
*/
/***************************************************************************
* ANATOLIA 2.1 is copyright 1996-1997 Serdar BULUT *
* ANATOLIA has been brought to you by ANATOLIA consortium *
* Serdar BULUT {Chronos} bulut@rorqual.cc.metu.edu.tr *
* Ibrahim Canpunar {Mandrake} canpunar@rorqual.cc.metu.edu.tr *
* Murat BICER {KIO} mbicer@rorqual.cc.metu.edu.tr *
* D.Baris ACAR {Powerman} dbacar@rorqual.cc.metu.edu.tr *
* By using this code, you have agreed to follow the terms of the *
* ANATOLIA license, in the file Anatolia/anatolia.licence *
***************************************************************************/
/***************************************************************************
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. *
* *
* Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* *
* In order to use any part of this Merc Diku Mud, you must comply with *
* both the original Diku license in 'license.doc' as well the Merc *
* license in 'license.txt'. In particular, you may not remove either of *
* these copyright notices. *
* *
* Much time and thought has gone into this software and you are *
* benefitting. We hope that you share your changes too. What goes *
* around, comes around. *
***************************************************************************/
/***************************************************************************
* ROM 2.4 is copyright 1993-1995 Russ Taylor *
* ROM has been brought to you by the ROM consortium *
* Russ Taylor (rtaylor@pacinfo.com) *
* Gabrielle Taylor (gtaylor@pacinfo.com) *
* Brian Moore (rom@rom.efn.org) *
* By using this code, you have agreed to follow the terms of the *
* ROM license, in the file Rom24/doc/rom.license *
***************************************************************************/
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "merc.h"
#include "update.h"
#include "mob_prog.h"
#include "obj_prog.h"
#include "fight.h"
#include "resource.h"
/* command procedures needed */
DECLARE_DO_FUN(do_human );
DECLARE_DO_FUN(do_murder );
DECLARE_DO_FUN(do_rescue );
DECLARE_DO_FUN(do_quit );
DECLARE_DO_FUN(do_quit_count );
DECLARE_DO_FUN(do_spellbane );
DECLARE_DO_FUN(do_stand );
DECLARE_DO_FUN(do_track );
DECLARE_DO_FUN(do_yell );
void back_home (CHAR_DATA *ch);
/*
* Local functions.
*/
int hit_gain (CHAR_DATA *ch);
int mana_gain (CHAR_DATA *ch);
int move_gain (CHAR_DATA *ch);
void mobile_update (void);
void weather_update (void);
void char_update (void);
void obj_update (void);
void aggr_update (void);
int potion_cure_level (OBJ_DATA *potion);
int potion_arm_level (OBJ_DATA *potion);
bool potion_cure_blind (OBJ_DATA *potion);
bool potion_cure_poison (OBJ_DATA *potion);
bool potion_cure_disease (OBJ_DATA *potion);
/* below done by chronos */
void quest_update args((void));
void auction_update args((void));
void light_update args((void));
void room_update args((void));
void room_affect_update args((void));
void check_reboot args((void));
void track_update args((void));
/* used for saving */
int save_number = 0;
int rebooter = 0;
/*
* Advancement stuff.
*/
void advance_level(CHAR_DATA *ch)
{
int add_hp;
int add_mana;
int add_move;
int add_prac;
CLASS_DATA *cl;
if (IS_NPC(ch)) {
bug("Advance_level: a mob to advance!", 0);
return;
}
if ((cl = class_lookup(ch->class)) == NULL) {
log_printf("advance_level", "%s: unknown class %d",
ch->name, ch->class);
return;
}
ch->pcdata->last_level = (ch->played +
(int) (current_time - ch->logon)) / 3600;
/* Momo set_title(ch, title_lookup(ch));*/
if (ch->pcdata->plevels > 0) {
ch->pcdata->plevels--;
return;
}
add_hp = (con_app[get_curr_stat(ch,STAT_CON)].hitp +
number_range(1,5)) - 3;
add_hp = (add_hp * cl->hp_rate) / 100;
add_mana = number_range(get_curr_stat(ch,STAT_INT)/2,
(2*get_curr_stat(ch,STAT_INT) +
get_curr_stat(ch,STAT_WIS)/5));
add_mana = (add_mana * cl->mana_rate) / 100;
add_move = number_range(1, (get_curr_stat(ch,STAT_CON) +
get_curr_stat(ch,STAT_DEX)) / 6);
add_prac = wis_app[get_curr_stat(ch,STAT_WIS)].practice;
add_hp = UMAX(3, add_hp);
add_mana = UMAX(3, add_mana);
add_move = UMAX(6, add_move);
if (ch->sex == SEX_FEMALE) {
add_hp -= 1;
add_mana += 2;
}
ch->max_hit += add_hp;
ch->max_mana += add_mana;
ch->max_move += add_move;
ch->practice += add_prac;
ch->train += ch->level % 5 ? 0 : 1;
ch->pcdata->perm_hit += add_hp;
ch->pcdata->perm_mana += add_mana;
ch->pcdata->perm_move += add_move;
char_printf(ch, "Your gain is {C%d{x hp, {C%d{x mana, {C%d{x mv {C%d{x prac.\n",
add_hp, add_mana, add_move, add_prac);
}
void gain_exp(CHAR_DATA *ch, int gain)
{
if (IS_NPC(ch) || ch->level >= LEVEL_HERO)
return;
if (IS_SET(ch->plr_flags, PLR_NOEXP) && gain > 0) {
char_puts("You can't gain exp without your spirit.\n", ch);
return;
}
ch->exp += gain;
ch->exp_tl += gain;
while (ch->level < LEVEL_HERO && exp_to_level(ch) <= 0) {
char_nputs(MSG_YOU_RAISE_A_LEVEL, ch);
ch->level += 1;
ch->exp_tl = 0;
info (NULL,0,"{G[{RCONGRATS!{G]{P %s{W has gained a level!\r\n",ch->name);
if ((ch->class == CLASS_SAMURAI) && (ch->level == 10))
ch->wimpy = 0;
if (ch->level == 91)
log_printf("%s made level 91.", ch->name);
wiznet_printf(ch, NULL, WIZ_LEVELS, 0, 0,
"$N has attained level %d!",(int)ch->level);
advance_level(ch);
save_char_obj(ch, FALSE);
}
}
/*
* Regeneration stuff.
*/
int hit_gain(CHAR_DATA *ch)
{
int gain;
int number;
CLASS_DATA *cl;
if (ch->in_room == NULL || (cl = class_lookup(ch->class)) == NULL)
return 0;
if (IS_NPC(ch)) {
gain = 5 + ch->level;
if (IS_AFFECTED(ch, AFF_REGENERATION))
gain *= 2;
switch(ch->position) {
default: gain /= 2; break;
case POS_SLEEPING: gain = 3 * gain/2; break;
case POS_RESTING: break;
case POS_FIGHTING: gain /= 3; break;
}
}
else {
gain = UMAX(3, 2 * get_curr_stat(ch,STAT_CON) + (7 * ch->level) / 4);
gain = (gain * cl->hp_rate) / 100;
number = number_percent();
if (number < get_skill(ch, gsn_fast_healing)) {
gain += number * gain / 100;
if (ch->hit < ch->max_hit)
check_improve(ch, gsn_fast_healing, TRUE, 8);
}
if (number < get_skill(ch, gsn_trance)) {
gain += number * gain / 150;
if (ch->mana < ch->max_mana)
check_improve(ch, gsn_trance, TRUE, 8);
}
switch (ch->position) {
default: gain /= 4; break;
case POS_SLEEPING: break;
case POS_RESTING: gain /= 2; break;
case POS_FIGHTING: gain /= 6; break;
}
if (ch->pcdata->condition[COND_HUNGER] < 0)
gain = 0;
if (ch->pcdata->condition[COND_THIRST] < 0)
gain = 0;
}
gain = gain * ch->in_room->heal_rate / 100;
if (ch->on != NULL && ch->on->pIndexData->item_type == ITEM_FURNITURE)
gain = gain * ch->on->value[3] / 100;
if (IS_AFFECTED(ch, AFF_POISON))
gain /= 4;
if (IS_AFFECTED(ch, AFF_PLAGUE))
gain /= 8;
if (IS_AFFECTED(ch, AFF_HASTE))
gain /= 2;
if (IS_AFFECTED(ch, AFF_SLOW))
gain /= 2;
if (get_curr_stat(ch, STAT_CON) > 20)
gain = (gain * 14) / 10;
if (IS_HARA_KIRI(ch))
gain *= 3;
return UMIN(gain, ch->max_hit - ch->hit);
}
int mana_gain(CHAR_DATA *ch)
{
int gain;
int number;
CLASS_DATA *cl;
if (ch->in_room == NULL || (cl = class_lookup(ch->class)) == NULL)
return 0;
if (IS_NPC(ch))
{
gain = 5 + ch->level;
switch (ch->position)
{
default: gain /= 2; break;
case POS_SLEEPING: gain = 3 * gain/2; break;
case POS_RESTING: break;
case POS_FIGHTING: gain /= 3; break;
}
}
else
{
gain = get_curr_stat(ch,STAT_WIS)
+ (2 * get_curr_stat(ch,STAT_INT)) + ch->level;
gain = (gain * cl->mana_rate) / 100;
number = number_percent();
if (number < get_skill(ch,gsn_meditation))
{
gain += number * gain / 100;
if (ch->mana < ch->max_mana)
check_improve(ch,gsn_meditation,TRUE,8);
}
if (number < get_skill(ch,gsn_trance))
{
gain += number * gain / 100;
if (ch->mana < ch->max_mana)
check_improve(ch,gsn_trance,TRUE,8);
}
if (!IS_SET(cl->flags, CLASS_MAGIC))
gain /= 2;
switch (ch->position)
{
default: gain /= 4; break;
case POS_SLEEPING: break;
case POS_RESTING: gain /= 2; break;
case POS_FIGHTING: gain /= 6; break;
}
if (ch->pcdata->condition[COND_HUNGER] < 0)
gain = 0;
if (ch->pcdata->condition[COND_THIRST] < 0)
gain = 0;
}
gain = gain * ch->in_room->mana_rate / 100;
if (ch->on != NULL && ch->on->pIndexData->item_type == ITEM_FURNITURE)
gain = gain * ch->on->value[4] / 100;
if (IS_AFFECTED(ch, AFF_POISON))
gain /= 4;
if (IS_AFFECTED(ch, AFF_PLAGUE))
gain /= 8;
if (IS_AFFECTED(ch,AFF_HASTE))
gain /= 2 ;
if (IS_AFFECTED(ch,AFF_SLOW))
gain /= 2 ;
if (get_curr_stat(ch,STAT_INT) > 20)
gain = (gain * 13) / 10;
if (get_curr_stat(ch,STAT_WIS) > 20)
gain = (gain * 11) / 10;
if (IS_HARA_KIRI(ch))
gain *= 3;
return UMIN(gain, ch->max_mana - ch->mana);
}
int move_gain(CHAR_DATA *ch)
{
int gain;
if (ch->in_room == NULL)
return 0;
if (IS_NPC(ch))
{
gain = ch->level;
}
else
{
gain = UMAX(15, 2 * ch->level);
switch (ch->position)
{
case POS_SLEEPING: gain += 2 * (get_curr_stat(ch,STAT_DEX)); break;
case POS_RESTING: gain += get_curr_stat(ch,STAT_DEX); break;
}
if (ch->pcdata->condition[COND_HUNGER] < 0)
gain = 3;
if (ch->pcdata->condition[COND_THIRST] < 0)
gain = 3;
}
gain = gain * ch->in_room->heal_rate/100;
if (ch->on != NULL && ch->on->pIndexData->item_type == ITEM_FURNITURE)
gain = gain * ch->on->value[3] / 100;
if (IS_AFFECTED(ch, AFF_POISON))
gain /= 4;
if (IS_AFFECTED(ch, AFF_PLAGUE))
gain /= 8;
if (IS_AFFECTED(ch,AFF_HASTE) || IS_AFFECTED(ch,AFF_SLOW))
gain /=2 ;
if (get_curr_stat(ch,STAT_DEX) > 20)
gain *= (14 /10);
if (IS_HARA_KIRI(ch))
gain *= 3;
return UMIN(gain, ch->max_move - ch->move);
}
void gain_condition(CHAR_DATA *ch, int iCond, int value)
{
int condition;
int damage_hunger;
int fdone;
CHAR_DATA *vch,*vch_next;
if (value == 0 || IS_NPC(ch) || ch->level >= LEVEL_IMMORTAL)
return;
if (HAS_SKILL(ch, gsn_vampire)
&& (iCond == COND_THIRST ||
iCond == COND_FULL ||
iCond == COND_HUNGER))
return;
condition = ch->pcdata->condition[iCond];
ch->pcdata->condition[iCond] = URANGE(-6, condition + value, 96);
if (iCond == COND_FULL && (ch->pcdata->condition[COND_FULL] < 0))
ch->pcdata->condition[COND_FULL] = 0;
if ((iCond == COND_DRUNK) && (condition < 1))
ch->pcdata->condition[COND_DRUNK] = 0;
if (ch->pcdata->condition[iCond] < 1
&& ch->pcdata->condition[iCond] > -6) {
switch (iCond) {
case COND_HUNGER:
char_puts("You are hungry.\n", ch);
break;
case COND_THIRST:
char_puts("You are thirsty.\n", ch);
break;
case COND_DRUNK:
if (condition != 0)
char_puts("You are sober.\n", ch);
break;
case COND_BLOODLUST:
if (condition != 0)
char_puts("You are hungry for blood.\n",
ch);
break;
case COND_DESIRE:
if (condition != 0)
char_puts("You have missed your home.\n",
ch);
break;
}
}
if (ch->pcdata->condition[iCond] == -6 && ch->level >= PK_MIN_LEVEL) {
switch (iCond) {
case COND_HUNGER:
char_puts("You are starving!\n", ch);
act("$n is starving!", ch, NULL, NULL, TO_ROOM);
damage_hunger = ch->max_hit * number_range(2, 4) / 100;
if (!damage_hunger)
damage_hunger = 1;
damage(ch, ch, damage_hunger, TYPE_HUNGER, DAM_HUNGER,
TRUE);
if (ch->position == POS_SLEEPING)
return;
break;
case COND_THIRST:
char_puts("You are dying of thrist!\n", ch);
act("$n is dying of thirst!", ch, NULL, NULL, TO_ROOM);
damage_hunger = ch->max_hit * number_range(2, 4) / 100;
if (!damage_hunger)
damage_hunger = 1;
damage(ch, ch, damage_hunger, TYPE_HUNGER, DAM_THIRST,
TRUE);
if (ch->position == POS_SLEEPING)
return;
break;
case COND_BLOODLUST:
fdone = 0;
char_puts("You are suffering from thrist of blood!\n",
ch);
act("$n is suffering from thirst of blood!",
ch, NULL, NULL, TO_ROOM);
if (ch->in_room && ch->in_room->people
&& ch->fighting == NULL) {
if (!IS_AWAKE(ch)) do_stand(ch, str_empty);
for (vch = ch->in_room->people;
vch != NULL && ch->fighting == NULL;
vch = vch_next) {
vch_next = vch->next_in_room;
if (ch != vch && can_see(ch, vch)
&& !is_safe_nomessage(ch, vch)) {
do_yell(ch,
"BLOOD! I NEED BLOOD!");
do_murder(ch, vch->name);
fdone = 1;
}
}
}
if (fdone)
break;
damage_hunger = ch->max_hit * number_range(2, 4) / 100;
if (!damage_hunger)
damage_hunger = 1;
damage(ch, ch, damage_hunger, TYPE_HUNGER, DAM_THIRST,
TRUE);
if (ch->position == POS_SLEEPING)
return;
break;
case COND_DESIRE:
char_puts("You want to go your home!\n", ch);
act("$n desires for $s home!", ch, NULL, NULL, TO_ROOM);
if (ch->position >= POS_STANDING)
move_char(ch, number_door(), FALSE);
break;
}
}
}
/*
* Mob autonomous action.
* This function takes 25% to 35% of ALL Merc cpu time.
* -- Furey
*/
void mobile_update(void)
{
CHAR_DATA *ch, *ch_next;
EXIT_DATA *pexit;
int door;
OBJ_DATA *obj;
/* Examine all mobs. */
for (ch = char_list; ch != NULL; ch = ch_next) {
bool bust_prompt = FALSE;
flag_t act;
ch_next = ch->next;
if (ch->in_room == NULL)
continue;
if (ch->position == POS_FIGHTING)
SET_FIGHT_TIME(ch);
/* permanent spellbane */
if (!IS_NPC(ch)) {
if (ch->level < LEVEL_IMMORTAL
&& get_skill(ch, gsn_spellbane)
&& !is_affected(ch, gsn_spellbane))
do_spellbane(ch, str_empty);
/* update ghost state */
if (ch->last_death_time != -1
&& current_time - ch->last_death_time >=
GHOST_DELAY_TIME
&& IS_SET(ch->plr_flags, PLR_GHOST)) {
char_puts("You return to your normal form.\n",
ch);
REMOVE_BIT(ch->plr_flags, PLR_GHOST);
}
}
/* update pumped state */
if (ch->last_fight_time != -1
&& current_time - ch->last_fight_time >= FIGHT_DELAY_TIME
&& IS_PUMPED(ch)) {
REMOVE_BIT((ch)->plr_flags, PLR_PUMPED);
if (!IS_NPC(ch) && ch->desc != NULL
&& ch->desc->pString == NULL
&& (ch->last_death_time == -1 ||
ch->last_death_time < ch->last_fight_time))
char_nputs(MSG_YOU_SETTLE_DOWN, ch);
}
if (IS_AFFECTED(ch, AFF_REGENERATION) && ch->in_room != NULL) {
ch->hit = UMIN(ch->hit + ch->level / 10, ch->max_hit);
if (ch->race == 18 /* troll */)
ch->hit = UMIN(ch->hit + ch->level / 10,
ch->max_hit);
if (ch->hit != ch->max_hit)
bust_prompt = TRUE;
}
if (IS_AFFECTED(ch, AFF_CORRUPTION) && ch->in_room != NULL) {
ch->hit -= ch->level / 10;
if (ch->hit < 1) {
ch->position = POS_DEAD;
handle_death(ch, ch);
continue;
}
bust_prompt = TRUE;
}
if (!IS_NPC(ch) && ch->level < LEVEL_IMMORTAL && bust_prompt
&& ch->desc != NULL && ch->desc->pString == NULL
&& ch->desc->showstr_point == NULL)
char_puts(str_empty, ch);
/*
* that's all for PCs and charmed mobiles
*/
if (!IS_NPC(ch)
|| IS_AFFECTED(ch, AFF_CHARM))
continue;
act = ch->pIndexData->act;
if (IS_SET(act, ACT_HUNTER) && ch->hunting)
hunt_victim(ch);
if (ch->in_room->area->empty
&& !IS_SET(act, ACT_UPDATE_ALWAYS))
continue;
/* Examine call for special procedure */
if (ch->spec_fun != 0) {
if ((*ch->spec_fun) (ch))
continue;
}
if (ch->pIndexData->pShop != NULL /* give him some gold */
|| (ch->gold * 100 + ch->silver) < ch->pIndexData->wealth) {
ch->gold += ch->pIndexData->wealth * number_range(1,20)/5000000;
ch->silver += ch->pIndexData->wealth * number_range(1,20)/50000;
}
/* check triggers (only if mobile still in default position) */
if (ch->position == ch->pIndexData->default_pos) {
if (HAS_TRIGGER(ch, TRIG_DELAY)
&& ch->mprog_delay > 0) {
if (--ch->mprog_delay <= 0) {
mp_percent_trigger(ch, NULL, NULL,
NULL, TRIG_DELAY);
continue;
}
}
if (HAS_TRIGGER(ch, TRIG_RANDOM)) {
if(mp_percent_trigger(ch, NULL, NULL,
NULL, TRIG_RANDOM))
continue;
}
}
/* potion using and stuff for intelligent mobs */
if (ch->pIndexData->pShop == NULL
&& (ch->position == POS_STANDING ||
ch->position == POS_RESTING ||
ch->position == POS_FIGHTING)
&& get_curr_stat(ch, STAT_INT) > 15
&& (ch->hit < ch->max_hit * 90 / 100 ||
IS_AFFECTED(ch, AFF_BLIND) ||
IS_AFFECTED(ch, AFF_POISON) ||
IS_AFFECTED(ch, AFF_PLAGUE) ||
ch->fighting != NULL)) {
for (obj = ch->carrying; obj; obj = obj->next_content) {
if (obj->pIndexData->item_type != ITEM_POTION)
continue;
if (ch->hit < ch->max_hit * 90 / 100) {
int cl = potion_cure_level(obj);
if (cl > 0) {
if (ch->hit < ch->max_hit*0.5
&& cl > 3) {
quaff_obj(ch, obj);
continue;
}
if (ch->hit < ch->max_hit*0.7) {
quaff_obj(ch, obj);
continue;
}
}
}
if (IS_AFFECTED(ch, AFF_POISON)
&& potion_cure_poison(obj)) {
quaff_obj(ch, obj);
continue;
}
if (IS_AFFECTED(ch, AFF_PLAGUE)
&& potion_cure_disease(obj)) {
quaff_obj(ch, obj);
continue;
}
if (IS_AFFECTED(ch, AFF_BLIND)
&& potion_cure_blind(obj)) {
quaff_obj(ch, obj);
continue;
}
if (ch->fighting) {
int al = potion_arm_level(obj);
if (ch->level - ch->fighting->level < 7
&& al > 3) {
quaff_obj(ch, obj);
continue;
}
if (ch->level - ch->fighting->level < 8
&& al > 2) {
quaff_obj(ch, obj);
continue;
}
if (ch->level - ch->fighting->level < 9
&& al > 1) {
quaff_obj(ch, obj);
continue;
}
if (ch->level - ch->fighting->level < 10
&& al > 0) {
quaff_obj(ch, obj);
continue;
}
}
}
}
/* That's all for sleeping / busy monster, and empty zones */
if (ch->position != POS_STANDING)
continue;
/* Scavenge */
if (IS_SET(act, ACT_SCAVENGER)
&& ch->in_room->contents != NULL
&& number_bits(6) == 0) {
OBJ_DATA *obj;
OBJ_DATA *obj_best = NULL;
int max = 1;
for (obj = ch->in_room->contents; obj;
obj = obj->next_content) {
if (CAN_WEAR(obj, ITEM_TAKE)
&& can_loot(ch, obj)
&& obj->cost > max) {
obj_best = obj;
max = obj->cost;
}
}
if (obj_best)
get_obj(ch, obj_best, NULL);
}
/* Wander */
if (!IS_SET(act, ACT_SENTINEL)
&& number_bits(3) == 0
&& (door = number_bits(5)) <= 5
&& !RIDDEN(ch)
&& (pexit = ch->in_room->exit[door]) != NULL
&& pexit->u1.to_room != NULL
&& !IS_SET(pexit->exit_info, EX_CLOSED)
&& !IS_SET(pexit->u1.to_room->room_flags, ROOM_NOMOB)
&& (!IS_SET(act, ACT_STAY_AREA) ||
pexit->u1.to_room->area == ch->in_room->area)
&& (!IS_SET(act, ACT_AGGRESSIVE) ||
!IS_SET(pexit->u1.to_room->room_flags,
ROOM_PEACE | ROOM_SAFE))
&& (!IS_SET(act, ACT_OUTDOORS) ||
!IS_SET(pexit->u1.to_room->room_flags, ROOM_INDOORS))
&& (!IS_SET(act, ACT_INDOORS) ||
IS_SET(pexit->u1.to_room->room_flags, ROOM_INDOORS)))
move_char(ch, door, FALSE);
}
}
int potion_cure_level(OBJ_DATA *potion)
{
int cl;
int i;
cl = 0;
for (i=1;i<5;i++)
{
if (sn_lookup("cure critical") == potion->value[i])
cl += 3;
if (sn_lookup("cure light") == potion->value[i])
cl += 1;
if (sn_lookup("cure serious") == potion->value[i])
cl += 2;
if (sn_lookup("heal") == potion->value[i])
cl += 4;
}
return(cl);
}
int potion_arm_level(OBJ_DATA *potion)
{
int al;
int i;
al = 0;
for (i=1;i<5;i++)
{
if (sn_lookup("armor") == potion->value[i])
al += 1;
if (sn_lookup("shield") == potion->value[i])
al += 1;
if (sn_lookup("stone skin") == potion->value[i])
al += 2;
if (sn_lookup("sanctuary") == potion->value[i])
al += 4;
if (sn_lookup("protection") == potion->value[i])
al += 3;
}
return(al);
}
bool potion_cure_blind(OBJ_DATA *potion)
{
int i;
for (i=0;i<5;i++)
{
if (sn_lookup("cure blindness") == potion->value[i])
return(TRUE);
}
return(FALSE);
}
bool potion_cure_poison(OBJ_DATA *potion)
{
int i;
for (i=0;i<5;i++)
{
if (sn_lookup("cure poison") == potion->value[i])
return(TRUE);
}
return(FALSE);
}
bool potion_cure_disease(OBJ_DATA *potion)
{
int i;
for (i=0;i<5;i++)
{
if (sn_lookup("cure disease") == potion->value[i])
return(TRUE);
}
return(FALSE);
}
/*
* Update the weather.
*/
void weather_update(void)
{
CHAR_DATA *ch;
int diff;
const char *msg_daytime = NULL;
const char *msg_weather = NULL;
switch (++time_info.hour) {
case 5:
weather_info.sunlight = SUN_LIGHT;
msg_daytime = "The day has begun.";
break;
case 6:
weather_info.sunlight = SUN_RISE;
msg_daytime = "The sun rises in the east.";
break;
case 19:
weather_info.sunlight = SUN_SET;
msg_daytime = "The sun slowly disappears in the west.";
break;
case 20:
weather_info.sunlight = SUN_DARK;
msg_daytime = "The night has begun.";
break;
case 24:
time_info.hour = 0;
time_info.day++;
break;
}
if (time_info.day >= 35) {
time_info.day = 0;
time_info.month++;
}
if (time_info.month >= 17) {
time_info.month = 0;
time_info.year++;
}
/*
* Weather change.
*/
if (time_info.month >= 9 && time_info.month <= 16)
diff = weather_info.mmhg > 985 ? -2 : 2;
else
diff = weather_info.mmhg > 1015 ? -2 : 2;
weather_info.change += diff * dice(1, 4) + dice(2, 6) - dice(2, 6);
weather_info.change = UMAX(weather_info.change, -12);
weather_info.change = UMIN(weather_info.change, 12);
weather_info.mmhg += weather_info.change;
weather_info.mmhg = UMAX(weather_info.mmhg, 960);
weather_info.mmhg = UMIN(weather_info.mmhg, 1040);
switch (weather_info.sky) {
default:
bug("Weather_update: bad sky %d.", weather_info.sky);
weather_info.sky = SKY_CLOUDLESS;
break;
case SKY_CLOUDLESS:
if (weather_info.mmhg < 990
|| (weather_info.mmhg < 1010 && number_bits(2) == 0)) {
msg_weather = "The sky is getting cloudy.";
weather_info.sky = SKY_CLOUDY;
}
break;
case SKY_CLOUDY:
if (weather_info.mmhg < 970
|| (weather_info.mmhg < 990 && number_bits(2) == 0)) {
msg_weather = "It starts to rain.";
weather_info.sky = SKY_RAINING;
}
else if (weather_info.mmhg > 1030 && number_bits(2) == 0) {
msg_weather = "The clouds disappear.";
weather_info.sky = SKY_CLOUDLESS;
}
break;
case SKY_RAINING:
if (weather_info.mmhg > 1030
|| (weather_info.mmhg > 1010 && number_bits(2) == 0)) {
msg_weather = "The rain stopped.";
weather_info.sky = SKY_CLOUDY;
}
else if (weather_info.mmhg < 970 && number_bits(2) == 0) {
msg_weather = "Lightning flashes in the sky.";
weather_info.sky = SKY_LIGHTNING;
}
break;
case SKY_LIGHTNING:
if (weather_info.mmhg > 1010
|| (weather_info.mmhg > 990 && number_bits(2) == 0)) {
msg_weather = "The lightning has stopped.";
weather_info.sky = SKY_RAINING;
}
break;
}
if (!msg_daytime && !msg_weather)
return;
for (ch = char_list; ch; ch = ch->next) {
if (IS_NPC(ch) && !HAS_TRIGGER(ch, TRIG_ACT))
continue;
if (!IS_OUTSIDE(ch) || !IS_AWAKE(ch))
continue;
act(msg_daytime, ch, NULL, NULL, TO_CHAR);
act(msg_weather, ch, NULL, NULL, TO_CHAR);
}
}
/*
* Update all chars, including mobs.
*/
void char_update(void)
{
CHAR_DATA *ch;
CHAR_DATA *ch_next;
CHAR_DATA *ch_quit;
static time_t last_save_time = -1;
ch_quit = NULL;
/* update save counter */
save_number++;
if (save_number > 29)
save_number = 0;
for (ch = char_list; ch != NULL; ch = ch_next) {
AFFECT_DATA *paf;
AFFECT_DATA *paf_next;
int chance;
RACE_DATA *r = race_lookup(ch->race);
ch_next = ch->next;
if (!r)
continue;
/* reset path find */
if (!IS_NPC(ch) && (chance = get_skill(ch, gsn_path_find))) {
if (number_percent() < chance) {
ch->endur += chance / 2;
check_improve(ch, gsn_path_find, TRUE, 8);
}
else
check_improve(ch, gsn_path_find, FALSE, 16);
}
if (!ch->fighting) {
flag_t skip = AFF_FLYING;
affect_check(ch, TO_AFFECTS, -1);
/* Remove caltrops effect after fight off */
if (is_affected(ch, gsn_caltrops))
affect_strip(ch, gsn_caltrops);
if (!MOUNTED(ch)) {
if (!IS_AFFECTED(ch, AFF_HIDE)
&& (r->aff & AFF_HIDE))
char_puts("You step back into the shadows.\n", ch);
if (!IS_AFFECTED(ch, AFF_SNEAK)
&& (r->aff & AFF_SNEAK))
char_puts("You move silently again.\n", ch);
}
else
skip |= AFF_HIDE | AFF_FADE | AFF_INVIS |
AFF_IMP_INVIS | AFF_SNEAK |
AFF_CAMOUFLAGE;
SET_BIT(ch->affected_by, r->aff & ~skip);
}
/* Remove vampire effect when morning. */
if (is_affected(ch, gsn_vampire)
&& (weather_info.sunlight == SUN_LIGHT ||
weather_info.sunlight == SUN_RISE))
do_human(ch, str_empty);
if (!IS_NPC(ch) && is_affected(ch, gsn_thumbling)) {
if (dice(5, 6) > get_curr_stat(ch, STAT_DEX)) {
act("You failed to reach the true source of tennis ball power.", ch, NULL, NULL, TO_CHAR);
act("$n falls to the ground flat on $s face.", ch, NULL, NULL, TO_ROOM);
affect_strip(ch, gsn_thumbling);
}
}
if (ch->timer > 20 && !IS_NPC(ch))
ch_quit = ch;
if (ch->position >= POS_STUNNED) {
int old_hit = ch->hit;
int old_mana = ch->mana;
int old_move = ch->move;
/* check to see if we need to go home */
if (IS_NPC(ch) && ch->zone != NULL
&& ch->in_room != NULL
&& ch->zone != ch->in_room->area && ch->desc == NULL
&& ch->fighting == NULL
/* && ch->progtypes==0 */
&& !IS_AFFECTED(ch,AFF_CHARM) && ch->last_fought == NULL
&& !RIDDEN(ch)) {
if (ch->in_mind != NULL
&& ch->pIndexData->vnum > 100)
back_home(ch);
else {
act_nprintf(ch, NULL, NULL, TO_ROOM,
POS_RESTING, MSG_N_WANDERS_ON_HOME);
extract_char(ch, TRUE);
}
continue;
}
if (ch->hit < ch->max_hit)
ch->hit += hit_gain(ch);
else
ch->hit = ch->max_hit;
if (ch->mana < ch->max_mana)
ch->mana += mana_gain(ch);
else
ch->mana = ch->max_mana;
if (ch->move < ch->max_move)
ch->move += move_gain(ch);
else
ch->move = ch->max_move;
if (!IS_NPC(ch) && ch->level < LEVEL_IMMORTAL
&& ch->desc != NULL && ch->desc->pString == NULL
&& ch->desc->showstr_point == NULL
&& (old_hit != ch->hit || old_mana != ch->mana ||
old_move != ch->move))
char_puts(str_empty, ch);
}
if (ch->position == POS_STUNNED)
update_pos(ch);
if (!IS_NPC(ch) && ch->level < LEVEL_IMMORTAL
&& ch->in_room != NULL) {
OBJ_DATA *obj;
if ((obj = get_eq_char(ch, WEAR_LIGHT))
&& obj->pIndexData->item_type == ITEM_LIGHT
&& obj->value[2] > 0) {
if (--obj->value[2] == 0) {
if (ch->in_room->light > 0)
--ch->in_room->light;
act("$p goes out.",
ch, obj, NULL, TO_ROOM);
act("$p flickers and goes out.",
ch, obj, NULL, TO_CHAR);
extract_obj(obj);
}
else if (obj->value[2] <= 5)
act("$p flickers.",
ch, obj, NULL, TO_CHAR);
}
if (++ch->timer >= 12) {
if (ch->was_in_room == NULL) {
ch->was_in_room = ch->in_room;
if (ch->fighting != NULL)
stop_fighting(ch, TRUE);
act("$n disappears into the void.",
ch, NULL, NULL, TO_ROOM);
char_puts("You disappear "
"into the void.\n", ch);
if (ch->level > 1)
save_char_obj(ch, FALSE);
char_from_room(ch);
char_to_room(ch, get_room_index(ROOM_VNUM_LIMBO));
}
}
if (!ch->was_in_room) {
gain_condition(ch, COND_DRUNK, -1);
if (HAS_SKILL(ch, gsn_vampire))
gain_condition(ch, COND_BLOODLUST, -1);
gain_condition(ch, COND_FULL,
ch->size > SIZE_MEDIUM ? -4 : -2);
if (ch->in_room->sector_type == SECT_DESERT)
gain_condition(ch, COND_THIRST, -3);
else
gain_condition(ch, COND_THIRST, -1);
gain_condition(ch, COND_HUNGER,
ch->size > SIZE_MEDIUM ? -2 : -1);
}
}
for (paf = ch->affected; paf != NULL; paf = paf_next) {
paf_next = paf->next;
if (paf->duration > 0) {
paf->duration--;
if (number_range(0, 4) == 0 && paf->level > 0)
paf->level--;
/* spell strength fades with time */
}
else if (paf->duration == 0) {
SKILL_DATA *sk;
if ((paf_next == NULL ||
paf_next->type != paf->type ||
paf_next->duration > 0)
&& paf->type > 0
&& (sk = skill_lookup(paf->type))
&& !IS_NULLSTR(sk->msg_off))
char_printf(ch, "%s\n", sk->msg_off);
affect_remove(ch, paf);
}
}
/*
* Careful with the damages here,
* MUST NOT refer to ch after damage taken,
* as it may be lethal damage (on NPC).
*/
if (is_affected(ch, gsn_witch_curse)) {
AFFECT_DATA *af, witch;
if (ch->in_room == NULL)
continue;
act("The witch curse makes $n feel $s life slipping away.",
ch, NULL, NULL, TO_ROOM);
char_puts("The witch curse makes you feeling your life slipping away.\n", ch);
for (af = ch->affected; af!= NULL; af = af->next)
if (af->type == gsn_witch_curse)
break;
if (af == NULL)
continue;
if (af->level == 1)
continue;
witch.where = af->where;
witch.type = af->type;
witch.level = af->level;
witch.duration = af->duration;
witch.location = af->location;
witch.modifier = af->modifier * 2;
witch.bitvector = 0;
affect_remove(ch, af);
affect_to_char(ch ,&witch);
ch->hit = UMIN(ch->hit, ch->max_hit);
if (ch->hit < 1) {
ch->position = POS_DEAD;
handle_death(ch, ch);
continue;
}
}
if (IS_AFFECTED(ch, AFF_PLAGUE) && ch != NULL) {
AFFECT_DATA *af, plague;
CHAR_DATA *vch;
int dam;
if (ch->in_room == NULL)
continue;
act("$n writhes in agony as plague sores erupt from $s skin.",
ch, NULL, NULL, TO_ROOM);
char_puts("You writhe in agony from the plague.\n", ch);
for (af = ch->affected; af != NULL; af = af->next)
if (af->type == gsn_plague)
break;
if (af == NULL) {
REMOVE_BIT(ch->affected_by, AFF_PLAGUE);
continue;
}
if (af->level == 1)
continue;
plague.where = TO_AFFECTS;
plague.type = gsn_plague;
plague.level = af->level - 1;
plague.duration = number_range(1,2 * plague.level);
plague.location = APPLY_STR;
plague.modifier = -5;
plague.bitvector = AFF_PLAGUE;
for (vch = ch->in_room->people; vch != NULL;
vch = vch->next_in_room) {
if (!saves_spell(plague.level + 2,
vch, DAM_DISEASE)
&& !IS_IMMORTAL(vch)
&& !IS_AFFECTED(vch, AFF_PLAGUE)
&& number_bits(2) == 0) {
char_puts("You feel hot and feverish.\n", vch);
act_nprintf(vch, NULL, NULL, TO_ROOM,
POS_RESTING ,MSG_N_SHIVERS_ILL);
affect_join(vch, &plague);
}
}
dam = UMIN(ch->level, af->level/5 + 1);
ch->mana -= dam;
ch->move -= dam;
damage(ch, ch, dam, gsn_plague, DAM_DISEASE,FALSE);
if (number_range(1, 100) < 70)
damage(ch, ch, UMAX(ch->max_hit/20, 50),
gsn_plague, DAM_DISEASE, TRUE);
}
else if (IS_AFFECTED(ch, AFF_POISON) && ch != NULL
&& !IS_AFFECTED(ch, AFF_SLOW)) {
AFFECT_DATA *poison;
poison = affect_find(ch->affected, gsn_poison);
if (poison != NULL) {
act("$n shivers and suffers.",
ch, NULL, NULL, TO_ROOM);
char_puts("You shiver and suffer.\n", ch);
damage(ch, ch, poison->level/10 + 1, gsn_poison,
DAM_POISON, TRUE);
}
}
else if (ch->position == POS_INCAP
&& number_range(0, 1) == 0)
damage(ch, ch, 1, TYPE_UNDEFINED, DAM_NONE, FALSE);
else if (ch->position == POS_MORTAL)
damage(ch, ch, 1, TYPE_UNDEFINED, DAM_NONE, FALSE);
} /* global for */
/*
* Autosave and autoquit.
* Check that these chars still exist.
*/
if (last_save_time == -1 || current_time - last_save_time > 300) {
last_save_time = current_time;
for (ch = char_list; ch != NULL; ch = ch_next) {
ch_next = ch->next;
if (!IS_NPC(ch))
save_char_obj(ch, FALSE);
if (ch == ch_quit || ch->timer > 20)
do_quit(ch, str_empty);
}
}
return;
}
void water_float_update(void)
{
OBJ_DATA *obj_next;
OBJ_DATA *obj;
CHAR_DATA *ch;
for (obj = object_list; obj != NULL; obj = obj_next) {
obj_next = obj->next;
if (!obj->in_room || !IS_WATER(obj->in_room))
continue;
obj->water_float = obj->water_float > 0 ?
obj->water_float - 1 : -1;
if (obj->pIndexData->item_type == ITEM_DRINK_CON) {
obj->value[1] = URANGE(1, obj->value[1]+8,
obj->value[0]);
if ((ch = obj->in_room->people))
act("$p makes bubbles on the water.", ch, obj,
NULL, TO_ALL);
obj->water_float = obj->value[0]-obj->value[1];
obj->value[2] = 0;
}
if (obj->water_float == 0) {
if((ch = obj->in_room->people))
act("$p sinks down the water.", ch, obj, NULL,
TO_ALL);
extract_obj(obj);
}
}
}
void update_obj_affects(OBJ_DATA *obj)
{
AFFECT_DATA *paf, *paf_next;
/* go through affects and decrement */
for (paf = obj->affected; paf != NULL; paf = paf_next) {
paf_next = paf->next;
if (paf->duration > 0) {
paf->duration--;
/* spell strength fades with time */
if (number_range(0,4) == 0 && paf->level > 0)
paf->level--;
}
else if (paf->duration == 0) {
SKILL_DATA *sk;
if ((paf_next == NULL || paf_next->type != paf->type ||
paf_next->duration > 0)
&& paf->type > 0
&& (sk = skill_lookup(paf->type))
&& !IS_NULLSTR(sk->msg_obj)) {
if (obj->carried_by != NULL)
act(sk->msg_obj, obj->carried_by,
obj, NULL, TO_CHAR);
if (obj->in_room != NULL
&& obj->in_room->people)
act(sk->msg_obj, obj->in_room->people,
obj, NULL, TO_ALL);
}
affect_remove_obj(obj, paf);
}
}
}
bool update_ice_obj(OBJ_DATA *obj)
{
if (obj->carried_by != NULL
&& obj->carried_by->in_room->sector_type == SECT_DESERT
&& number_percent() < 40) {
act("The extreme heat melts $p.", obj->carried_by, obj, NULL,
TO_CHAR);
extract_obj(obj);
return TRUE;
}
if (obj->in_room != NULL
&& obj->in_room->sector_type == SECT_DESERT
&& number_percent() < 50) {
act("The extreme heat melts $p.", obj->in_room->people, obj,
NULL, TO_ALL);
extract_obj(obj);
return TRUE;
}
return FALSE;
}
bool update_glass_obj(OBJ_DATA *obj)
{
if (obj->carried_by
&& obj->carried_by->in_room->sector_type == SECT_DESERT
&& !IS_NPC(obj->carried_by)
&& number_percent() < 20) {
act("$p evaporates.", obj->carried_by, obj, NULL, TO_CHAR);
extract_obj(obj);
return TRUE;
}
if (obj->in_room
&& obj->in_room->sector_type == SECT_DESERT
&& number_percent() < 30) {
act("$p evaporates by the extream heat.", obj->in_room->people,
obj, NULL, TO_ROOM);
act("$p evaporates by the extream heat.", obj->in_room->people,
obj, NULL, TO_CHAR);
extract_obj(obj);
return TRUE;
}
return FALSE;
}
void update_pit(OBJ_DATA *obj)
{
OBJ_DATA *t_obj, *next_obj;
static int pit_count = 1;
/* more or less an hour */
pit_count = ++pit_count % 120;
if (obj_is_pit(obj) && !pit_count)
for (t_obj = obj->contains; t_obj != NULL; t_obj = next_obj) {
next_obj = t_obj->next_content;
obj_from_obj(t_obj);
extract_obj(t_obj);
}
}
void update_one_obj(OBJ_DATA *obj)
{
OBJ_DATA *pit, *t_obj;
CHAR_DATA *rch;
char *message;
update_obj_affects(obj);
/* find the uppest obj container */
for(t_obj = obj; t_obj->in_obj; t_obj = t_obj->in_obj)
;
if ((t_obj->in_room != NULL &&
t_obj->in_room->area->nplayer > 0)
|| (t_obj->carried_by &&
t_obj->carried_by->in_room &&
t_obj->carried_by->in_room->area->nplayer > 0))
oprog_call(OPROG_AREA, obj, NULL, NULL);
if (check_material(obj, "ice")
&& update_ice_obj(obj))
return;
if (check_material(obj, "glass")
&& obj->pIndexData->item_type == ITEM_POTION
&& update_glass_obj(obj))
return;
if (obj->condition > -1 && (obj->timer <= 0 || --obj->timer > 0))
return;
switch (obj->pIndexData->item_type) {
default:
message = "$p crumbles into dust.";
break;
case ITEM_FOUNTAIN:
message = "$p dries up.";
break;
case ITEM_CORPSE_NPC:
message = "$p decays into dust.";
break;
case ITEM_CORPSE_PC:
message = "$p decays into dust.";
break;
case ITEM_FOOD:
message = "$p decomposes.";
break;
case ITEM_POTION:
message = "$p has evaporated from disuse.";
break;
case ITEM_PORTAL:
message = "$p fades out of existence.";
break;
case ITEM_CONTAINER:
if (CAN_WEAR(obj, ITEM_WEAR_FLOAT))
if (obj->contains)
message = "$p flickers and vanishes, spilling "
"its contents on the floor.";
else
message = "$p flickers and vanishes.";
else
message = "$p crumbles into dust.";
break;
}
if (obj->carried_by != NULL)
if (IS_NPC(obj->carried_by)
&& obj->carried_by->pIndexData->pShop != NULL)
obj->carried_by->silver += obj->cost/5;
else {
act(message, obj->carried_by, obj, NULL, TO_CHAR);
if (obj->wear_loc == WEAR_FLOAT)
act(message, obj->carried_by, obj, NULL,
TO_ROOM);
}
if (obj->in_room && (rch = obj->in_room->people)
&& !(obj->in_obj && obj_is_pit(obj->in_obj)
&& !CAN_WEAR(obj->in_obj, ITEM_TAKE)))
act(message, rch, obj, NULL, TO_ALL);
if ((obj->pIndexData->item_type == ITEM_CORPSE_PC
|| obj->wear_loc == WEAR_FLOAT)
&& obj->contains) {
/* save the contents */
OBJ_DATA *t_obj, *next_obj;
for (t_obj = obj->contains; t_obj != NULL; t_obj = next_obj) {
next_obj = t_obj->next_content;
obj_from_obj(t_obj);
/* in another object */
if (obj->in_obj)
obj_to_obj(t_obj, obj->in_obj);
/* carried */
else if (obj->carried_by)
if (obj->wear_loc == WEAR_FLOAT
&& obj->carried_by->in_room)
obj_to_room(t_obj,
obj->carried_by->in_room);
else
obj_to_char(t_obj, obj->carried_by);
/* to the pit */
else {
for (pit = get_room_index(obj->altar)->contents;
pit && pit->pIndexData->vnum != obj->pit;
pit = pit->next);
if (pit)
obj_to_obj(t_obj, pit);
else if(obj->in_room)
obj_to_room(t_obj, obj->in_room);
}
}
}
extract_obj(obj);
}
OBJ_DATA *last_updated_obj;
void obj_update_list(OBJ_DATA *obj)
{
int i;
OBJ_DATA *obj_next;
/* some diagnostics */
obj_next = obj;
for (i = 0; obj && obj->extracted; obj = obj->next, i++)
;
if (i)
log_printf("obj_update_list: skipped %d extracted objs, "
"object_list == %p, obj == %p, "
"last_updated_obj == %p, "
"last_updated_obj->next == %p",
i, object_list, obj_next,
last_updated_obj, last_updated_obj->next);
for (; obj; obj = obj_next) {
obj_next = obj->next;
if (obj->extracted) {
obj_update_list(last_updated_obj ?
last_updated_obj->next : object_list);
return;
}
update_one_obj(obj);
if (!obj->extracted)
last_updated_obj = obj;
}
}
/*
* Update all objs.
* This function is performance sensitive.
*/
void obj_update(void)
{
last_updated_obj = NULL;
obj_update_list(object_list);
}
/*
* Aggress.
*
* for each mortal PC
* for each mob in room
* aggress on some random PC
*
* This function takes 25% to 35% of ALL Merc cpu time.
* Unfortunately, checking on each PC move is too tricky,
* because we don't the mob to just attack the first PC
* who leads the party into the room.
*
* -- Furey
*/
void aggr_update(void)
{
CHAR_DATA *wch, *wch_next;
CHAR_DATA *ch, *ch_next;
CHAR_DATA *vch, *vch_next;
CHAR_DATA *victim;
for (wch = char_list; wch != NULL; wch = wch_next) {
wch_next = wch->next;
if (IS_AWAKE(wch)
&& IS_AFFECTED(wch, AFF_BLOODTHIRST)
&& wch->fighting == NULL) {
for (vch = wch->in_room->people;
vch != NULL && wch->fighting == NULL;
vch = vch_next) {
vch_next = vch->next_in_room;
if (wch != vch && can_see(wch,vch)
&& !is_safe_nomessage(wch,vch)) {
act_puts("{RMORE BLOOD! MORE BLOOD! MORE BLOOD!!!{x", wch,NULL,NULL,TO_CHAR,POS_RESTING);
do_murder(wch,vch->name);
}
}
}
if (IS_NPC(wch)
|| wch->level >= LEVEL_IMMORTAL
|| wch->in_room == NULL
|| wch->in_room->area->empty)
continue;
for (ch = wch->in_room->people; ch != NULL; ch = ch_next) {
int count;
flag_t act;
ch_next = ch->next_in_room;
if (!IS_NPC(ch)
|| (!IS_SET(act = ch->pIndexData->act,
ACT_AGGRESSIVE) &&
ch->last_fought == NULL)
|| IS_SET(ch->in_room->room_flags,
ROOM_PEACE | ROOM_SAFE)
|| IS_AFFECTED(ch, AFF_CALM)
|| ch->fighting != NULL
|| RIDDEN(ch)
|| IS_AFFECTED(ch, AFF_CHARM)
|| !IS_AWAKE(ch)
|| (IS_SET(act, ACT_WIMPY) && IS_AWAKE(wch))
|| !can_see(ch, wch)
|| number_bits(1) == 0
|| is_safe_nomessage(ch,wch))
continue;
/* Mad mob attacks! */
if (ch->last_fought == wch
&& !IS_AFFECTED(ch, AFF_SCREAM)) {
doprintf(do_yell, ch, "%s! Now you die!",
PERS(wch,ch));
wch = check_guard(wch, ch);
multi_hit(ch, wch, TYPE_UNDEFINED);
continue;
}
if (ch->last_fought != NULL)
continue;
/*
* Ok we have a 'wch' player character and a 'ch' npc
* aggressor. Now make the aggressor fight a RANDOM
* pc victim in the room, giving each 'vch' an equal
* chance of selection.
*/
count = 0;
victim = NULL;
for (vch = wch->in_room->people;
vch != NULL; vch = vch_next) {
vch_next = vch->next_in_room;
if (!IS_NPC(vch)
&& vch->level < LEVEL_IMMORTAL
&& ch->level >= vch->level - 5
&& (!IS_SET(act, ACT_WIMPY)
|| !IS_AWAKE(vch))
&& can_see(ch, vch)
/* do not attack vampires */
&& !HAS_SKILL(vch, gsn_vampire)
/* good vs good :( */
&& !(IS_GOOD(ch) && IS_GOOD(vch))) {
if (number_range(0, count) == 0)
victim = vch;
count++;
}
}
if (victim == NULL)
continue;
if (!is_safe_nomessage(ch, victim)) {
int dt = TYPE_UNDEFINED;
int bs_chance;
victim = check_guard(victim, ch);
if (get_dam_type(ch,
get_eq_char(ch, WEAR_WIELD),
&dt) == DAM_PIERCE
&& (bs_chance = get_skill(ch, gsn_backstab))
&& backstab_ok(NULL, victim))
backstab(ch, victim, bs_chance);
else
multi_hit(ch, victim, TYPE_UNDEFINED);
}
}
}
}
/*
* Handle all kinds of updates.
* Called once per pulse from game loop.
* Random times to defeat tick-timing clients and players.
*/
void update_handler(void)
{
static int pulse_area;
static int pulse_mobile;
static int pulse_violence;
static int pulse_point;
static int pulse_music;
static int pulse_water_float;
static int pulse_raffect;
static int pulse_track;
if (--pulse_area <= 0) {
wiznet("AREA & ROOM TICK!", NULL, NULL, WIZ_TICKS, 0, 0);
pulse_area = PULSE_AREA;
area_update();
room_update();
}
if (--pulse_music <= 0) {
pulse_music = PULSE_MUSIC;
/* song_update(); */
}
if (--pulse_mobile <= 0) {
pulse_mobile = PULSE_MOBILE;
mobile_update();
light_update();
}
if (--pulse_violence <= 0) {
pulse_violence = PULSE_VIOLENCE;
violence_update();
}
if (--pulse_water_float <= 0) {
pulse_water_float = PULSE_WATER_FLOAT;
water_float_update();
}
if (--pulse_raffect <= 0) {
pulse_raffect = PULSE_RAFFECT;
room_affect_update();
}
if (--pulse_track <= 0) {
pulse_track = PULSE_TRACK;
track_update();
}
if (--pulse_point <= 0) {
CHAR_DATA *ch;
wiznet("CHAR TICK!",NULL,NULL,WIZ_TICKS,0,0);
pulse_point = PULSE_TICK;
weather_update();
char_update();
quest_update();
obj_update();
check_reboot();
/* room counting */
for (ch = char_list; ch != NULL; ch = ch->next)
if (!IS_NPC(ch) && ch->in_room != NULL)
ch->in_room->area->count++;
}
aggr_update();
auction_update();
tail_chain();
return;
}
void light_update(void)
{
CHAR_DATA *ch;
int dam_light;
DESCRIPTOR_DATA *d;
for (d = descriptor_list; d != NULL; d = d->next) {
if (d->connected != CON_PLAYING)
continue;
ch = (d->original != NULL) ? d->original : d->character;
if (IS_IMMORTAL(ch))
continue;
/* also checks vampireness */
if ((dam_light = isn_dark_safe(ch)) == 0)
continue;
if (dam_light != 2
&& number_percent() < get_skill(ch, gsn_light_resistance)) {
check_improve(ch, gsn_light_resistance, TRUE, 32);
continue;
}
if (dam_light == 1)
char_puts("The light in the room disturbs you.\n", ch);
else
char_puts("Sun light disturbs you.\n",ch);
dam_light = 1 + (ch->max_hit * 4)/ 100;
damage(ch, ch, dam_light, TYPE_HUNGER, DAM_LIGHT_V, TRUE);
if (ch->position == POS_STUNNED)
update_pos(ch);
if (number_percent() < 10)
gain_condition(ch, COND_DRUNK, -1);
}
}
void room_update(void)
{
ROOM_INDEX_DATA *room;
ROOM_INDEX_DATA *room_next;
for (room = top_affected_room; room; room = room_next) {
AFFECT_DATA *paf;
AFFECT_DATA *paf_next;
room_next = room->aff_next;
for (paf = room->affected; paf != NULL; paf = paf_next) {
paf_next = paf->next;
if (paf->duration > 0) {
paf->duration--;
/*
* spell strength shouldn't fade with time
* because of checks in safe_rspell with af->level
if (number_range(0,4) == 0 && paf->level > 0)
paf->level--;
*/
}
else if (paf->duration == 0)
affect_remove_room(room, paf);
}
}
}
void room_affect_update(void)
{
ROOM_INDEX_DATA *room;
ROOM_INDEX_DATA *room_next;
for (room = top_affected_room; room ; room = room_next)
{
room_next = room->aff_next;
while (IS_ROOM_AFFECTED(room, RAFF_PLAGUE) && room->people != NULL)
{
AFFECT_DATA *af, plague;
CHAR_DATA *vch;
for (af = room->affected; af != NULL; af = af->next)
{
if (af->type == gsn_black_death)
break;
}
if (af == NULL)
{
REMOVE_BIT(room->affected_by,RAFF_PLAGUE);
break;
}
if (af->level == 1)
af->level = 2;
plague.where = TO_AFFECTS;
plague.type = gsn_plague;
plague.level = af->level - 1;
plague.duration = number_range(1,((plague.level/2)+1));
plague.location = APPLY_NONE;
plague.modifier = -5;
plague.bitvector = AFF_PLAGUE;
for (vch = room->people; vch != NULL; vch = vch->next_in_room)
{
if (!saves_spell(plague.level ,vch,DAM_DISEASE)
&& !IS_IMMORTAL(vch)
&& !is_safe_rspell(af->level,vch)
&& !IS_AFFECTED(vch,AFF_PLAGUE) && number_bits(3) == 0)
{
char_puts("You feel hot and feverish.\n",vch);
act("$n shivers and looks very ill.",vch,NULL,NULL,TO_ROOM);
affect_join(vch,&plague);
}
}
break;
}
while (IS_ROOM_AFFECTED(room, RAFF_POISON) && room->people != NULL)
{
AFFECT_DATA *af, paf;
CHAR_DATA *vch;
for (af = room->affected; af != NULL; af = af->next)
{
if (af->type == gsn_deadly_venom)
break;
}
if (af == NULL)
{
REMOVE_BIT(room->affected_by,RAFF_POISON);
break;
}
if (af->level == 1)
af->level = 2;
paf.where = TO_AFFECTS;
paf.type = gsn_poison;
paf.level = af->level - 1;
paf.duration = number_range(1,((paf.level/5)+1));
paf.location = APPLY_NONE;
paf.modifier = -5;
paf.bitvector = AFF_POISON;
for (vch = room->people; vch != NULL; vch = vch->next_in_room)
{
if (!saves_spell(paf.level ,vch,DAM_POISON)
&& !IS_IMMORTAL(vch)
&& !is_safe_rspell(af->level,vch)
&& !IS_AFFECTED(vch,AFF_POISON) && number_bits(3) == 0)
{
char_puts("You feel very sick.\n",vch);
act("$n looks very ill.",vch,NULL,NULL,TO_ROOM);
affect_join(vch,&paf);
}
}
break;
}
while (IS_ROOM_AFFECTED(room, RAFF_SLOW) && room->people != NULL)
{
AFFECT_DATA *af, paf;
CHAR_DATA *vch;
for (af = room->affected; af != NULL; af = af->next)
{
if (af->type == gsn_lethargic_mist)
break;
}
if (af == NULL)
{
REMOVE_BIT(room->affected_by,RAFF_SLOW);
break;
}
if (af->level == 1)
af->level = 2;
paf.where = TO_AFFECTS;
paf.type = gsn_slow;
paf.level = af->level - 1;
paf.duration = number_range(1,((paf.level/5)+1));
paf.location = APPLY_NONE;
paf.modifier = -5;
paf.bitvector = AFF_SLOW;
for (vch = room->people; vch != NULL; vch = vch->next_in_room)
{
if (!saves_spell(paf.level ,vch,DAM_OTHER)
&& !IS_IMMORTAL(vch)
&& !is_safe_rspell(af->level,vch)
&& !IS_AFFECTED(vch,AFF_SLOW) && number_bits(3) == 0)
{
char_puts("You start to move less quickly.\n",vch);
act("$n is moving less quickly.",vch,NULL,NULL,TO_ROOM);
affect_join(vch,&paf);
}
}
break;
}
while (IS_ROOM_AFFECTED(room, RAFF_SLEEP) && room->people != NULL)
{
AFFECT_DATA *af, paf;
CHAR_DATA *vch;
for (af = room->affected; af != NULL; af = af->next)
{
if (af->type == gsn_mysterious_dream)
break;
}
if (af == NULL)
{
REMOVE_BIT(room->affected_by,RAFF_SLEEP);
break;
}
if (af->level == 1)
af->level = 2;
paf.where = TO_AFFECTS;
paf.type = gsn_sleep;
paf.level = af->level - 1;
paf.duration = number_range(1,((paf.level/10)+1));
paf.location = APPLY_NONE;
paf.modifier = -5;
paf.bitvector = AFF_SLEEP;
for (vch = room->people; vch != NULL; vch = vch->next_in_room)
{
if (!saves_spell(paf.level - 4,vch,DAM_CHARM)
&& !IS_IMMORTAL(vch)
&& !is_safe_rspell(af->level,vch)
&& !(IS_NPC(vch) && IS_SET(vch->pIndexData->act, ACT_UNDEAD))
&& !IS_AFFECTED(vch,AFF_SLEEP) && number_bits(3) == 0)
{
if (IS_AWAKE(vch))
{
char_puts("You feel very sleepy.......zzzzzz.\n",vch);
act("$n goes to sleep.",vch,NULL,NULL,TO_ROOM);
vch->position = POS_SLEEPING;
}
affect_join(vch,&paf);
}
}
break;
}
while (IS_ROOM_AFFECTED(room, RAFF_ESPIRIT) && room->people != NULL)
{
AFFECT_DATA *af, paf;
CHAR_DATA *vch;
for (af = room->affected; af != NULL; af = af->next)
{
if (af->type == gsn_evil_spirit)
break;
}
if (af == NULL)
{
REMOVE_BIT(room->affected_by,RAFF_ESPIRIT);
break;
}
if (af->level == 1)
af->level = 2;
paf.where = TO_AFFECTS;
paf.type = gsn_evil_spirit;
paf.level = af->level;
paf.duration = number_range(1,(paf.level/30));
paf.location = APPLY_NONE;
paf.modifier = 0;
paf.bitvector = 0;
for (vch = room->people; vch != NULL; vch = vch->next_in_room)
{
if (!saves_spell(paf.level + 2,vch,DAM_MENTAL)
&& !IS_IMMORTAL(vch)
&& !is_safe_rspell(af->level,vch)
&& !is_affected(vch,gsn_evil_spirit) && number_bits(3) == 0)
{
char_puts("You feel worse than ever.\n",vch);
act("$n looks more evil.",vch,NULL,NULL,TO_ROOM);
affect_join(vch,&paf);
}
}
break;
}
/* new ones here
while (IS_ROOM_AFFECTED(room, RAFF_) && room->people != NULL)
{
AFFECT_DATA *af, paf;
CHAR_DATA *vch;
for (af = room->affected; af != NULL; af = af->next)
{
if (af->type == gsn_)
break;
}
if (af == NULL)
{
REMOVE_BIT(room->affected_by,RAFF_);
break;
}
if (af->level == 1)
af->level = 2;
paf.where = TO_AFFECTS;
paf.type = gsn_;
paf.level = af->level - 1;
paf.duration = number_range(1,((paf.level/5)+1));
paf.location = APPLY_NONE;
paf.modifier = -5;
paf.bitvector = AFF_;
for (vch = room->people; vch != NULL; vch = vch->next_in_room)
{
if (!saves_spell(paf.level + 2,vch,DAM_)
&& !IS_IMMORTAL(vch)
&& !is_safe_rspell(af->level,vch)
&& !IS_AFFECTED(vch,AFF_) && number_bits(3) == 0)
{
char_puts("You feel hot and feverish.\n",vch);
act("$n shivers and looks very ill.",vch,NULL,NULL,TO_ROOM);
affect_join(vch,&paf);
}
}
break;
}
*/
}
return;
}
void check_reboot(void)
{
DESCRIPTOR_DATA *d;
switch(reboot_counter) {
case -1:
break;
case 0:
reboot_muddy();
return;
case 1:
case 2:
case 3:
case 4:
case 5:
case 10:
case 15:
for (d = descriptor_list; d != NULL; d = d->next)
if (d->character != NULL)
if (rebooter || !IS_IMMORTAL(d->character))
char_printf(d->character,
"{*{W*****{R REBOOT IN {W%d{R MIN. {W*****{x\n",
reboot_counter);
else
char_printf(d->character,
"{*{W*****{R AUTOMAGIC REBOOT IN {W%d{R MIN. {W*****{x\n",
reboot_counter);
/* FALLTHRU */
default:
reboot_counter--;
break;
}
}
void track_update(void)
{
CHAR_DATA *ch, *ch_next;
for (ch = char_list; ch != NULL; ch = ch_next) {
CHAR_DATA *vch, *vch_next;
ch_next = ch->next;
if (!IS_NPC(ch)
|| IS_AFFECTED(ch, AFF_CALM | AFF_CHARM)
|| ch->fighting
|| !ch->in_room
|| !IS_AWAKE(ch)
|| IS_SET(ch->pIndexData->act, ACT_NOTRACK)
|| RIDDEN(ch)
|| IS_AFFECTED(ch, AFF_SCREAM))
continue;
if (ch->last_fought != NULL
&& ch->in_room != ch->last_fought->in_room) {
do_track(ch, ch->last_fought->name);
continue;
}
if (ch->in_mind == NULL)
continue;
for (vch = ch->in_room->people; vch; vch = vch_next) {
vch_next = vch->next_in_room;
if (IS_IMMORTAL(vch)
|| !can_see(ch,vch)
|| is_safe_nomessage(ch,vch)
|| !is_name(vch->name,ch->in_mind))
continue;
doprintf(do_yell, ch,
"So we meet again, %s", vch->name);
do_murder(ch, vch->name);
}
}
}