/****************************************************************************
* [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame | *
* -----------------------------------------------------------| \\._.// *
* SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version) | (0...0) *
* -----------------------------------------------------------| ).:.( *
* SMAUG (C) 1994, 1995, 1996 by Derek Snider | {o o} *
* -----------------------------------------------------------| / ' ' \ *
* SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus, |~'~.VxvxV.~'~*
* Scryn, Swordbearer, Rennard, Tricops, and Gorog. | *
* ------------------------------------------------------------------------ *
* 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 Staerfeldt, Tom Madsen, and Katja Nyboe. *
* ------------------------------------------------------------------------ *
* Regular update module *
****************************************************************************/
#include "stdafx.h"
#include "smaug.h"
#include "SysData.h"
#include "skill.h"
#include "mobiles.h"
#include "objects.h"
#include "rooms.h"
#include "area.h"
#include "auction.h"
#include "races.h"
#include "class.h"
#include "Exits.h"
#include "SmaugWizDoc.h"
#include "SmaugFiles.h"
#include "descriptor.h"
#include "character.h"
#include "Smaugx.h"
// Local functions.
int hit_gain (CCharacter *ch);
int mana_gain (CCharacter *ch);
int move_gain (CCharacter *ch);
void mobile_update ();
void weather_update ();
void char_update ();
void obj_update ();
void aggr_update ();
void room_act_update ();
void obj_act_update ();
void char_check ();
void drunk_randoms (CCharacter *ch);
void halucinations (CCharacter *ch);
void subtract_times (timeval *etime, timeval *stime);
// Global Variables
CCharacter *gch_prev;
CObjData *gobj_prev;
CCharacter *timechar;
char * corpse_descs [] = {
"The corpse of %s is in the last stages of decay.",
"The corpse of %s is crawling with vermin.",
"The corpse of %s fills the air with a foul stench.",
"The corpse of %s is buzzing with flies.",
"The corpse of %s lies here." };
extern int top_exit;
// Advancement stuff.
void advance_level (CCharacter *ch)
{
char buf [MAX_STRING_LENGTH];
int add_hp;
int add_mana;
int add_move;
int add_prac;
// save_char_obj (ch);
CClassData *pClass = ClassTable.GetClassData (ch->GetClass ());
sprintf (buf, "the %s", pClass->GetTitle (ch->GetLevel (),
ch->GetSex () == SEX_FEMALE ? 1 : 0));
set_title (ch, buf);
add_hp = con_app [ch->GetConstitution ()].hitp + number_range (
pClass->GetMinHp (), pClass->GetMaxHp ());
add_mana = pClass->HasManaGain ()
? number_range (2, (2*ch->GetIntelligence ()+ch->GetWisdom ())/8) : 0;
add_move = number_range (5, (ch->GetConstitution ()
+ ch->GetDexterity ()) / 4);
add_prac = wis_app [ch->GetWisdom ()].practice;
add_hp = UMAX (1, add_hp);
add_mana = UMAX (0, add_mana);
add_move = UMAX (10, add_move);
// bonus for deadlies
if (ch->IsPkiller ()) {
add_mana = (int) (add_mana + add_mana*.3);
add_move = (int) (add_move + add_move*.3);
++add_hp; // bitch at blod if you don't like this :)
sprintf (buf, "Gravoc's Pandect steels your sinews.\n\r");
}
ch->AddMaxHp (add_hp);
ch->AddMaxMana (add_mana);
ch->AddMaxMove (add_move);
ch->AddPractices (add_prac);
if (! ch->IsNpc ())
ch->ClrActBit (PLR_BOUGHT_PET);
if (ch->GetLevel () == LEVEL_AVATAR) {
sprintf (buf, "%s has just achieved Avatarhood!", ch->GetName ());
POSITION pos = DList.GetHeadPosition ();
while (pos) {
CDescriptor *d = DList.GetNext (pos);
if (d->IsDisconnecting ())
continue;
if (d->m_Connected == CON_PLAYING && d->m_pCharacter != ch) {
set_char_color (AT_IMMORT, d->m_pCharacter);
d->m_pCharacter->SendText (buf);
d->m_pCharacter->SendText ("\n\r");
}
}
set_char_color (AT_WHITE, ch);
do_help (ch, "M_ADVHERO_");
}
if (ch->GetLevel () < LEVEL_IMMORTAL) {
if (ch->IsVampire ())
sprintf (buf,
"Your gain is: %d/%d hp, %d/%d bp, %d/%d mv %d/%d prac.\n\r",
add_hp, ch->GetMaxHp (), 1, ch->GetLevel () + 10,
add_move, ch->GetMaxMove (),
add_prac, ch->GetPractices ());
else
sprintf (buf,
"Your gain is: %d/%d hp, %d/%d mana, %d/%d mv %d/%d prac.\n\r",
add_hp, ch->GetMaxHp (), add_mana, ch->GetMaxMana (),
add_move, ch->GetMaxMove (), add_prac, ch->GetPractices ());
set_char_color (AT_WHITE, ch);
ch->SendText (buf);
if (SysData.IsShowLevel () && ch->GetLevel () > 10) {
sprintf (buf, "Tales of heroic deeds spread as %s's name is on the tip "
"of every story tellers tongue.\n\r", ch->GetName ());
echo_to_all (AT_YELLOW, buf, ECHOTAR_ALL);
}
if (SysData.IsLevelMsg ()) {
supermob->SetTrust (LEVEL_SUPREME);
do_restore (supermob, NCCP ch->GetName ());
supermob->SetTrust (0);
}
}
}
void gain_exp (CCharacter *ch, int gain)
{
int modgain;
char buf [MAX_STRING_LENGTH];
if (ch->IsNpc () || ch->GetLevel () >= LEVEL_AVATAR)
return;
// Bonus for deadly lowbies
modgain = gain;
if (modgain > 0 && ch->IsPkiller () && ch->GetLevel () < 17) {
if (ch->GetLevel () <= 6) {
sprintf (buf, "The Favor of Gravoc fosters your learning.\n\r");
modgain *= 2;
}
if (ch->GetLevel () <= 10 && ch->GetLevel () >= 7) {
sprintf (buf, "The Hand of Gravoc hastens your learning.\n\r");
modgain = (int) (modgain * 1.75);
}
if (ch->GetLevel () <= 13 && ch->GetLevel () >= 11) {
sprintf (buf, "The Cunning of Gravoc succors your learning.\n\r");
modgain = (int) (modgain * 1.5);
}
if (ch->GetLevel () <= 16 && ch->GetLevel () >= 14) {
sprintf (buf, "The Patronage of Gravoc reinforces your learning.\n\r");
modgain = (int) (modgain * 1.25);
}
ch->SendText (buf);
}
// per-race experience multipliers
modgain = (int) (modgain *
(RaceTable.GetRaceData (ch->GetRace ())->GetExpMultiplier () / 100.0));
// Deadly exp loss floor is exp floor of level
if (ch->IsPkiller () && modgain < 0) {
if (ch->GetExp () + modgain < exp_level (ch, ch->GetLevel ())) {
modgain = exp_level (ch, ch->GetLevel ()) - ch->GetExp ();
sprintf (buf, "Gravoc's Pandect protects your insight.\n\r");
}
}
ch->SetExp (UMAX (0, ch->GetExp () + modgain));
if (!ch->IsAuthed () && ch->GetExp () >= exp_level (ch, ch->GetLevel ()+1)) {
ch->SendText ("You can not ascend to a higher level until you "
"are authorized.\n\r");
ch->SetExp (exp_level (ch, (ch->GetLevel ()+1)) - 1);
return;
}
while (ch->GetLevel () < LEVEL_AVATAR &&
ch->GetExp () >= exp_level (ch, ch->GetLevel ()+1)) {
set_char_color (AT_WHITE + AT_BLINK, ch);
ch->AddLevel (1);
ch->SendTextf ("You have now obtained experience level %d!\n\r",
ch->GetLevel ());
advance_level (ch);
}
}
// Regeneration stuff.
int hit_gain (CCharacter *ch)
{
int gain;
if (ch->IsNpc ())
gain = ch->GetLevel () * 3 / 2;
else {
gain = UMIN (5, ch->GetLevel ());
gain += RaceTable.GetHpRegen (ch->GetRace ());
switch (ch->GetPosition ()) {
case POS_DEAD: return 0;
case POS_MORTAL: return -1;
case POS_INCAP: return -1;
case POS_STUNNED: return 1;
case POS_SLEEPING:
gain += (int) (ch->GetConstitution () * 1.5);
break;
case POS_RESTING:
gain += ch->GetConstitution ();
break;
}
if (ch->IsVampire ()) {
if (ch->GetPcData ()->condition [COND_BLOODTHIRST] <= 1)
gain /= 2;
else if (ch->GetPcData ()->condition [COND_BLOODTHIRST]
>= (8 + ch->GetLevel ()))
gain *= 2;
if (ch->IsOutside ()) {
switch (weather_info.sunlight) {
case SUN_RISE:
case SUN_SET:
gain /= 2;
break;
case SUN_LIGHT:
gain /= 4;
break;
}
}
}
if (ch->GetPcData ()->condition [COND_FULL] == 0)
gain /= 2;
if (ch->GetPcData ()->condition [COND_THIRST] == 0)
gain /= 2;
}
if (ch->IsPoisoned ())
gain /= 4;
return UMIN (gain, ch->GetMaxHp () - ch->GetHp ());
}
int mana_gain (CCharacter *ch)
{
int gain;
if (ch->IsNpc ())
gain = ch->GetLevel ();
else {
if (ch->GetPosition () < POS_SLEEPING)
return 0;
gain = UMIN (5, ch->GetLevel () / 2);
gain += RaceTable.GetManaRegen (ch->GetRace ());
switch (ch->GetPosition ()) {
case POS_SLEEPING:
gain += ch->GetIntelligence () * 3;
break;
case POS_RESTING:
gain += (int) (ch->GetIntelligence () * 1.5);
break;
}
if (ch->GetPcData ()->condition [COND_FULL] == 0)
gain /= 2;
if (ch->GetPcData ()->condition [COND_THIRST] == 0)
gain /= 2;
}
if (ch->IsPoisoned ())
gain /= 4;
return UMIN (gain, ch->GetMaxMana () - ch->GetMana ());
}
int move_gain (CCharacter *ch)
{
int gain;
if (ch->IsNpc ()) {
gain = ch->GetLevel ();
} else {
gain = UMAX (15, 2 * ch->GetLevel ());
switch (ch->GetPosition ()) {
case POS_DEAD: return 0;
case POS_MORTAL: return -1;
case POS_INCAP: return -1;
case POS_STUNNED: return 1;
case POS_SLEEPING:
gain += ch->GetDexterity () * 2;
break;
case POS_RESTING:
gain += ch->GetDexterity ();
break;
}
if (ch->IsVampire ()) {
if (ch->GetPcData ()->condition[COND_BLOODTHIRST] <= 1)
gain /= 2;
else if (ch->GetPcData ()->condition [COND_BLOODTHIRST]
>= (8 + ch->GetLevel ()))
gain *= 2;
if (ch->IsOutside ()) {
switch (weather_info.sunlight) {
case SUN_RISE:
case SUN_SET:
gain /= 2;
break;
case SUN_LIGHT:
gain /= 4;
break;
}
}
}
if (ch->GetPcData ()->condition [COND_FULL] == 0)
gain /= 2;
if (ch->GetPcData ()->condition [COND_THIRST] == 0)
gain /= 2;
}
if (ch->IsPoisoned ())
gain /= 4;
return UMIN (gain, ch->GetMaxMove () - ch->GetMove ());
}
void gain_condition (CCharacter *ch, int iCond, int value)
{
ASSERT (ch);
int condition;
ch_ret retcode;
BOOL bIsVampire = ClassTable.IsVampire (ch->GetClass ());
if (value == 0 || ch->IsNpc ()
|| ch->GetLevel () > LEVEL_AVATAR || ! ch->IsAuthed ())
return;
condition = ch->GetPcData ()->condition [iCond];
if (iCond == COND_BLOODTHIRST)
ch->GetPcData ()->condition [iCond] =
URANGE (0, condition + value, 10 + ch->GetLevel ());
else
ch->GetPcData ()->condition [iCond] =
URANGE (0, condition + value, 48);
// Avatars don't get condition messages
if (ch->GetLevel () == LEVEL_AVATAR)
return;
if (ch->GetPcData ()->condition [iCond] == 0) {
switch (iCond) {
case COND_FULL:
if (! bIsVampire) {
set_char_color (AT_HUNGRY, ch);
ch->SendText ("You are STARVING!\n\r");
act (AT_HUNGRY, "$n is starved half to death!", ch, NULL,
NULL, TO_ROOM);
if (!ch->IsPkiller () || number_bits (1) == 0)
worsen_mental_state (ch, 1);
retcode = damage (ch, ch, 1, TYPE_UNDEFINED);
}
break;
case COND_THIRST:
if (! bIsVampire) {
set_char_color (AT_THIRSTY, ch);
ch->SendText ("You are DYING of THIRST!\n\r");
act (AT_THIRSTY, "$n is dying of thirst!", ch, NULL, NULL,
TO_ROOM);
worsen_mental_state (ch, ch->IsPkiller () ? 1: 2);
retcode = damage (ch, ch, 2, TYPE_UNDEFINED);
}
break;
case COND_BLOODTHIRST:
set_char_color (AT_BLOOD, ch);
ch->SendText ("You are starved to feast on blood!\n\r");
act (AT_BLOOD, "$n is suffering from lack of blood!", ch,
NULL, NULL, TO_ROOM);
worsen_mental_state (ch, 2);
retcode = damage (ch, ch, ch->GetMaxHp () / 20,
TYPE_UNDEFINED);
break;
case COND_DRUNK:
if (condition != 0) {
set_char_color (AT_SOBER, ch);
ch->SendText ("You are sober.\n\r");
}
retcode = rNONE;
break;
default:
bug ("Gain_condition: invalid condition type %d", iCond);
retcode = rNONE;
break;
}
}
if (retcode != rNONE)
return;
if (ch->GetPcData ()->condition [iCond] == 1) {
switch (iCond) {
case COND_FULL:
if (! bIsVampire) {
set_char_color (AT_HUNGRY, ch);
ch->SendText ("You are really hungry.\n\r");
act (AT_HUNGRY, "You can hear $n's stomach growling.", ch,
NULL, NULL, TO_ROOM);
if (number_bits (1) == 0)
worsen_mental_state (ch, 1);
}
break;
case COND_THIRST:
if (! bIsVampire) {
set_char_color (AT_THIRSTY, ch);
ch->SendText ("You are really thirsty.\n\r");
worsen_mental_state (ch, 1);
act (AT_THIRSTY, "$n looks a little parched.", ch, NULL,
NULL, TO_ROOM);
}
break;
case COND_BLOODTHIRST:
set_char_color (AT_BLOOD, ch);
ch->SendText ("You have a growing need to feast on blood!\n\r");
act (AT_BLOOD, "$n gets a strange look in $s eyes...", ch,
NULL, NULL, TO_ROOM);
worsen_mental_state (ch, 1);
break;
case COND_DRUNK:
if (condition != 0) {
set_char_color (AT_SOBER, ch);
ch->SendText (
"You are feeling a little less light headed.\n\r");
}
break;
}
}
if (ch->GetPcData ()->condition [iCond] == 2) {
switch (iCond) {
case COND_FULL:
if (! bIsVampire) {
set_char_color (AT_HUNGRY, ch);
ch->SendText ("You are hungry.\n\r");
}
break;
case COND_THIRST:
if (! bIsVampire) {
set_char_color (AT_THIRSTY, ch);
ch->SendText ("You are thirsty.\n\r");
}
break;
case COND_BLOODTHIRST:
set_char_color (AT_BLOOD, ch);
ch->SendText ("You feel an urgent need for blood.\n\r");
break;
}
}
if (ch->GetPcData ()->condition [iCond] == 3) {
switch (iCond) {
case COND_FULL:
if (! bIsVampire) {
set_char_color (AT_HUNGRY, ch);
ch->SendText ("You are a mite peckish.\n\r");
}
break;
case COND_THIRST:
if (! bIsVampire) {
set_char_color (AT_THIRSTY, ch);
ch->SendText ("You could use a sip of something refreshing.\n\r");
}
break;
case COND_BLOODTHIRST:
set_char_color (AT_BLOOD, ch);
ch->SendText ("You feel an aching in your fangs.\n\r");
break;
}
}
// Race alignment restrictions, h
CRaceData &Ra = *RaceTable.GetRaceData (ch->GetRace ());
if ((ch->GetAlignment () < Ra.GetMinAlign ())
|| (ch->GetAlignment () > Ra.GetMaxAlign ())) {
set_char_color (AT_BLOOD, ch);
ch->SendText ("Your actions have been incompatible with the ideals "
"of your race. This troubles you.");
}
// Nephandi alignment restrictions -h
if (! stricmp (ClassTable.GetName (ch->GetClass ()), "Nephandi")) {
if (ch->GetAlignment () > -250) {
set_char_color (AT_BLOOD, ch);
ch->SendText ("Damn you heathen! Go forth and do evil or suffer "
"the consequences!\n\r");
worsen_mental_state (ch, 35);
return;
}
}
// Paladins need some restrictions, this is where we crunch 'em -h
if (! stricmp (ClassTable.GetName (ch->GetClass ()), "Paladin")) {
if (ch->GetAlignment () < 250) {
set_char_color (AT_BLOOD, ch);
ch->SendText ("You are wracked with guilt and remorse for "
"your craven actions!\n\r");
act (AT_BLOOD, "$n prostrates $mself, seeking forgiveness "
"from $s Lord.", ch, NULL, NULL, TO_ROOM);
worsen_mental_state (ch, 15);
return;
}
if (ch->GetAlignment () < 500) {
set_char_color (AT_BLOOD, ch);
ch->SendText ("As you betray your faith, your mind begins to "
"betray you.\n\r");
act (AT_BLOOD, "$n shudders, judging $s actions unworthy of "
"a Paladin.", ch, NULL, NULL, TO_ROOM);
worsen_mental_state (ch, 6);
return;
}
}
}
// Mob autonomous action.
// This function takes 25% to 35% of ALL Mud cpu time.
void mobile_update ()
{
char buf[MAX_STRING_LENGTH];
CCharacter *ch;
CExitData *pexit;
int door;
ch_ret retcode;
retcode = rNONE;
try {
// Examine all mobs.
for (ch = last_char; ch; ch = gch_prev) {
set_cur_char (ch);
if (ch == first_char && ch->GetPrev ()) {
bug ("mobile_update: first_char->GetPrev () != NULL... fixed");
ch->SetPrev (NULL);
}
gch_prev = ch->GetPrev ();
if (gch_prev && gch_prev->GetNext () != ch) {
bug ("FATAL: Mobile_update: %s->GetPrev ()->GetNext ()"
" doesn't point to ch.", ch->GetName ());
bug ("Short-cutting here");
gch_prev = NULL;
ch->SetPrev (NULL);
sprintf (buf, "%s says, 'Prepare for the worst!'",
SysData.GetSupremeEntity ());
do_shout (ch, buf);
}
if (!ch->IsNpc ()) {
drunk_randoms (ch);
halucinations (ch);
continue;
}
if (!ch->GetInRoom () || ch->IsCharmed () || ch->IsParalysed ())
continue;
// Clean up 'animated corpses' that are not charmed' - Scryn
if (ch->GetMobIndex ()->vnum == MOB_VNUM_ANIMATED_CORPSE
&& !ch->IsCharmed ()) {
if (ch->GetInRoom ()->first_person)
act (AT_MAGIC, "$n returns to the dust from whence $e came.",
ch, NULL, NULL, TO_ROOM);
if (ch->IsNpc ()) // Guard against purging switched?
extract_char (ch, TRUE);
continue;
}
if (! ch->IsAction (ACT_RUNNING)
&& ! ch->IsAction (ACT_SENTINEL)
&& !ch->GetFightData () && ch->hunting) {
WAIT_STATE (ch, 2 * PULSE_VIOLENCE);
// Commented out temporarily to avoid spam - Scryn
//sprintf (buf, "%s hunting %s from %s.", ch->GetName (),
//ch->hunting->name,
//ch->GetInRoom ()->name);
//gpDoc->LogString (buf);
hunt_victim (ch);
continue;
}
// Examine call for special procedure
if (! ch->IsAction (ACT_RUNNING)
&& ch->GetSpecialMobFunction ()) {
if ((*ch->GetSpecialMobFunction ()) (ch))
continue;
if (char_died (ch))
continue;
}
// Check for mudprogram script on mob. If in a script,
// don't do any other mobprogs.
if (mprog_script_trigger (ch))
continue;
if (ch != cur_char) {
bug ("Mobile_update: ch != cur_char after spec_fun");
continue;
}
// That's all for sleeping / busy monster
if (ch->GetPosition () != POS_STANDING)
continue;
if (ch->IsAction (ACT_MOUNTED)) {
if (ch->IsAction (ACT_AGGRESSIVE))
do_emote (ch, "snarls and growls.");
continue;
}
if (ch->GetInRoom ()->IsSafe ()
&& ch->IsAction (ACT_AGGRESSIVE))
do_emote (ch, "glares around and snarls.");
// MOBprogram random trigger
if (ch->GetInRoom ()->GetArea ()->nplayer > 0) {
mprog_random_trigger (ch);
if (char_died (ch))
continue;
if (ch->GetPosition () < POS_STANDING)
continue;
}
// MOBprogram hour trigger: do something for an hour
mprog_hour_trigger (ch);
if (char_died (ch))
continue;
rprog_hour_trigger (ch);
if (char_died (ch))
continue;
if (ch->GetPosition () < POS_STANDING)
continue;
// Scavenge
if (ch->IsAction (ACT_SCAVENGER)
&& ! ch->GetInRoom ()->IsEmpty ()
&& number_bits (2) == 0) {
int max = 1;
CObjData *obj_best = NULL;
CObjData *obj = NULL;
POSITION pos = ch->GetInRoom ()->GetHeadContentPos ();
while (obj = ch->GetInRoom ()->GetNextContent (pos)) {
if (obj->CanWear (ITEM_TAKE) && obj->cost > max
&& ! obj->IsBuried ()) {
obj_best = obj;
max = obj->cost;
}
}
if (obj_best) {
obj_from_room (obj_best);
obj_to_char (obj_best, ch);
act (AT_ACTION, "$n gets $p.", ch, obj_best, NULL, TO_ROOM);
}
}
// Wander
if (! ch->IsAction (ACT_RUNNING)
&& ! ch->IsAction (ACT_SENTINEL)
&& ! ch->IsAction (ACT_PROTOTYPE)
&& (door = number_bits (5)) <= 9
&& (pexit = get_exit (ch->GetInRoom (), door)) != NULL
&& pexit->GetToRoom ()
&& ! pexit->IsClosed ()
&& ! pexit->GetToRoom ()->IsNoMob ()
&& ! pexit->GetToRoom ()->IsDeathRoom ()
&& (! ch->IsAction (ACT_STAY_AREA)
|| pexit->GetToRoom ()->GetArea () == ch->GetInRoom ()->GetArea ())) {
retcode = move_char (ch, pexit, 0);
// If ch changes position due to it's or someother mob's
// movement via MOBProgs, continue - Kahn
if (char_died (ch))
continue;
if (retcode != rNONE || ch->IsAction (ACT_SENTINEL)
|| ch->GetPosition () < POS_STANDING)
continue;
}
// Flee
if (ch->GetHp () < ch->GetMaxHp () / 2
&& (door = number_bits (4)) <= 9
&& (pexit = get_exit (ch->GetInRoom (),door)) != NULL
&& pexit->GetToRoom ()
&& ! pexit->IsClosed ()
&& ! pexit->GetToRoom ()->IsNoMob ()) {
BOOL found = FALSE;
CCharacter *rch = ch->GetInRoom ()->first_person;
for (; rch; rch = rch->GetNextInRoom ()) {
if (is_fearing (ch, rch)) {
switch (number_bits (2)) {
case 0:
sprintf (buf, "Get away from me, %s!",
rch->GetName ());
break;
case 1:
sprintf (buf, "Leave me be, %s!", rch->GetName ());
break;
case 2:
sprintf (buf, "%s is trying to kill me! Help!",
rch->GetName ());
break;
case 3:
sprintf (buf, "Someone save me from %s!",
rch->GetName ());
break;
}
do_yell (ch, buf);
found = TRUE;
break;
}
}
if (found)
retcode = move_char (ch, pexit, 0);
}
}
}
catch (CException* ex) {
CSwException sx;
char buf [128];
UINT id;
if (ex->GetErrorMessage (buf, sizeof (buf), &id))
sx.Printf ("CException %u:%s in mobile_update.", id, buf);
else sx.Printf ("Unknown CException in mobile_update.");
ex->Delete ();
throw sx;
}
}
// Update the weather.
void weather_update ()
{
char buf[MAX_STRING_LENGTH];
int diff;
short AT_TEMP = AT_PLAIN;
buf [0] = '\0';
switch (++time_info.hour) {
case 5:
weather_info.sunlight = SUN_LIGHT;
strcat (buf, "The day has begun.");
AT_TEMP = AT_YELLOW;
break;
case 6:
weather_info.sunlight = SUN_RISE;
strcat (buf, "The sun rises in the east.");
AT_TEMP = AT_ORANGE;
break;
case 12:
weather_info.sunlight = SUN_LIGHT;
strcat (buf, "It's noon.");
AT_TEMP = AT_YELLOW;
break;
case 19:
weather_info.sunlight = SUN_SET;
strcat (buf, "The sun slowly disappears in the west.");
AT_TEMP = AT_BLOOD;
break;
case 20:
weather_info.sunlight = SUN_DARK;
strcat (buf, "The night has begun.");
AT_TEMP = AT_DGREY;
break;
case 24:
time_info.hour = 0;
time_info.day++;
break;
}
if (time_info.day >= 30) {
time_info.day = 0;
time_info.month++;
}
if (time_info.month >= 17) {
time_info.month = 0;
time_info.year++;
}
if (buf [0] != '\0') {
POSITION pos = DList.GetHeadPosition ();
while (pos) {
CDescriptor *d = DList.GetNext (pos);
if (d->IsDisconnecting ())
continue;
if (d->m_Connected == CON_PLAYING
&& d->m_pCharacter->IsOutside ()
&& d->m_pCharacter->IsAwake ())
act (AT_TEMP, buf, d->m_pCharacter, 0, 0, TO_CHAR);
}
buf [0] = '\0';
}
// 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);
AT_TEMP = AT_GREY;
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)) {
strcat (buf, "The sky is getting cloudy.");
weather_info.sky = SKY_CLOUDY;
AT_TEMP = AT_GREY;
}
break;
case SKY_CLOUDY:
if (weather_info.mmhg < 970
|| (weather_info.mmhg < 990 && number_bits (2) == 0)) {
strcat (buf, "It starts to rain.");
weather_info.sky = SKY_RAINING;
AT_TEMP = AT_BLUE;
}
if (weather_info.mmhg > 1030 && number_bits (2) == 0) {
strcat (buf, "The clouds disappear.");
weather_info.sky = SKY_CLOUDLESS;
AT_TEMP = AT_WHITE;
}
break;
case SKY_RAINING:
if (weather_info.mmhg < 970 && number_bits (2) == 0) {
strcat (buf, "Lightning flashes in the sky.");
weather_info.sky = SKY_LIGHTNING;
AT_TEMP = AT_YELLOW;
}
if (weather_info.mmhg > 1030
|| (weather_info.mmhg > 1010 && number_bits (2) == 0)) {
strcat (buf, "The rain stopped.");
weather_info.sky = SKY_CLOUDY;
AT_TEMP = AT_WHITE;
}
break;
case SKY_LIGHTNING:
if (weather_info.mmhg > 1010
|| (weather_info.mmhg > 990 && number_bits (2) == 0)) {
strcat (buf, "The lightning has stopped.");
weather_info.sky = SKY_RAINING;
AT_TEMP = AT_GREY;
}
break;
}
if (buf [0] != '\0') {
POSITION pos = DList.GetHeadPosition ();
while (pos) {
CDescriptor *d = DList.GetNext (pos);
if (d->IsDisconnecting ())
continue;
if (d->m_Connected == CON_PLAYING
&& d->m_pCharacter->IsOutside ()
&& d->m_pCharacter->IsAwake ())
act (AT_TEMP, buf, d->m_pCharacter, 0, 0, TO_CHAR);
}
}
}
// Update all chars, including mobs.
// This function is performance sensitive.
void char_update ()
{
CCharacter *ch;
CCharacter *ch_save;
short save_count = 0;
ch_save = NULL;
for (ch = last_char; ch; ch = gch_prev) {
if (ch == first_char && ch->GetPrev ()) {
bug ("char_update: first_char->GetPrev () != NULL... fixed");
ch->SetPrev (NULL);
}
gch_prev = ch->GetPrev ();
set_cur_char (ch);
if (gch_prev && gch_prev->GetNext () != ch) {
bug ("char_update: ch->GetPrev ()->GetNext () != ch");
return;
}
// Do a room_prog rand check right off the bat
// if ch disappears (rprog might wax npc's), continue
if (!ch->IsNpc ())
rprog_random_trigger (ch);
if (char_died (ch))
continue;
if (ch->IsNpc ())
mprog_time_trigger (ch);
if (char_died (ch))
continue;
rprog_time_trigger (ch);
if (char_died (ch))
continue;
// See if player should be auto-saved.
if (!ch->IsNpc ()
&& (!ch->GetDesc () || ch->GetDesc ()->m_Connected == CON_PLAYING)
&& ch->GetLevel () >= 2
&& (CurrentTime - ch->GetSaveTime ()).GetTotalMinutes () >
(SysData.SaveFrequency))
then ch_save = ch;
else
ch_save = NULL;
if (ch->GetPosition () >= POS_STUNNED) {
if (ch->GetHp () < ch->GetMaxHp ())
ch->AddHp (hit_gain (ch));
if (ch->GetMana () < ch->GetMaxMana ())
ch->AddMana (mana_gain (ch));
if (ch->GetMove () < ch->GetMaxMove ())
ch->AddMove (move_gain (ch));
}
if (ch->GetPosition () == POS_STUNNED)
update_pos (ch);
if (!ch->IsNpc () && ch->GetLevel () < LEVEL_IMMORTAL) {
CObjData *obj;
if ((obj = get_eq_char (ch, WEAR_LIGHT)) != NULL
&& obj->item_type == ITEM_LIGHT && obj->value[2] > 0) {
if (--obj->value [2] == 0 && ch->GetInRoom ()) {
ch->GetInRoom ()->light -= obj->count;
act (AT_ACTION, "$p goes out.", ch, obj, NULL, TO_ROOM);
act (AT_ACTION, "$p goes out.", ch, obj, NULL, TO_CHAR);
if (obj->serial == cur_obj)
global_objcode = rOBJ_EXPIRED;
extract_obj (obj);
}
}
ch->AddTimer (1);
if (ch->GetTimer () >= 12) {
if (!ch->GetWasInRoom () && ch->GetInRoom ()) {
ch->SetWasInRoom (ch->GetInRoom ());
if (ch->GetFightData ())
stop_fighting (ch, TRUE);
act (AT_ACTION, "$n disappears into the void.",
ch, NULL, NULL, TO_ROOM);
ch->SendText ("You disappear into the void.\n\r");
if (SysData.IsSaveOnIdle ())
save_char_obj (ch);
ch->RemoveFromRoom ();
ch->SendToRoom (RoomTable.GetRoom (SysData.m_RoomLimbo));
}
}
if (ch->GetPcData ()->condition[COND_DRUNK] > 8)
worsen_mental_state (ch,
ch->GetPcData ()->condition [COND_DRUNK] / 8);
if (ch->GetPcData ()->condition [COND_FULL] > 1) {
switch (ch->GetPosition ()) {
case POS_SLEEPING: better_mental_state (ch, 4); break;
case POS_RESTING: better_mental_state (ch, 3); break;
case POS_SITTING:
case POS_MOUNTED: better_mental_state (ch, 2); break;
case POS_STANDING: better_mental_state (ch, 1); break;
case POS_FIGHTING:
case POS_EVASIVE:
case POS_DEFENSIVE:
case POS_AGGRESSIVE:
case POS_BERSERK:
if (number_bits (2) == 0)
better_mental_state (ch, 1);
break;
}
}
if (ch->GetPcData ()->condition [COND_THIRST] > 1) {
switch (ch->GetPosition ()) {
case POS_SLEEPING: better_mental_state (ch, 5); break;
case POS_RESTING: better_mental_state (ch, 3); break;
case POS_SITTING:
case POS_MOUNTED: better_mental_state (ch, 2); break;
case POS_STANDING: better_mental_state (ch, 1); break;
case POS_FIGHTING:
case POS_EVASIVE:
case POS_DEFENSIVE:
case POS_AGGRESSIVE:
case POS_BERSERK:
if (number_bits (2) == 0)
better_mental_state (ch, 1);
break;
}
}
gain_condition (ch, COND_DRUNK, -1);
gain_condition (ch, COND_FULL, -1 +
RaceTable.GetHungerMod (ch->GetRace ()));
if (ClassTable.IsVampire (ch->GetClass ()) && ch->GetLevel () >= 10) {
if (time_info.hour < 21 && time_info.hour > 5)
gain_condition (ch, COND_BLOODTHIRST, -1);
}
if (ch->GetInRoom ())
switch (ch->GetInRoom ()->sector_type) {
default:
gain_condition (ch, COND_THIRST, -1);
break;
case SECT_DESERT:
gain_condition (ch, COND_THIRST, -2);
break;
case SECT_UNDERWATER:
case SECT_OCEANFLOOR:
if (number_bits (1) == 0)
gain_condition (ch, COND_THIRST, -1);
break;
}
}
if (!char_died (ch)) {
// Careful with the damages here,
// MUST NOT refer to ch after damage taken,
// as it may be lethal damage (on NPC).
if (ch->IsPoisoned ()) {
act (AT_POISON, "$n shivers and suffers.", ch, NULL, NULL,
TO_ROOM);
act (AT_POISON, "You shiver and suffer.", ch, NULL, NULL,
TO_CHAR);
ch->SetMentalState (URANGE (20, ch->GetMentalState ()
+ (ch->IsPkiller () ? 3 : 4), 100));
damage (ch, ch, 6, gsn_poison);
}
else if (ch->GetPosition () == POS_INCAP)
damage (ch, ch, 1, TYPE_UNDEFINED);
else if (ch->GetPosition () == POS_MORTAL)
damage (ch, ch, 4, TYPE_UNDEFINED);
if (char_died (ch))
continue;
if (ch->GetMentalState () >= 30)
switch ((ch->GetMentalState () + 5) / 10) {
case 3:
ch->SendText ("You feel feverish.\n\r");
act (AT_ACTION, "$n looks kind of out of it.", ch, NULL,
NULL, TO_ROOM);
break;
case 4:
ch->SendText ("You do not feel well at all.\n\r");
act (AT_ACTION, "$n doesn't look too good.", ch, NULL,
NULL, TO_ROOM);
break;
case 5:
ch->SendText ("You need help!\n\r");
act (AT_ACTION, "$n looks like $e could use your help.",
ch, NULL, NULL, TO_ROOM);
break;
case 6:
ch->SendText ("Seekest thou a cleric.\n\r");
act (AT_ACTION, "Someone should fetch a healer for $n.",
ch, NULL, NULL, TO_ROOM);
break;
case 7:
ch->SendText ("You feel reality slipping away...\n\r");
act (AT_ACTION, "$n doesn't appear to be aware of what's"
" going on.", ch, NULL, NULL, TO_ROOM);
break;
case 8:
ch->SendText ("You begin to understand... "
"everything.\n\r");
act (AT_ACTION, "$n starts ranting like a madman!",
ch, NULL, NULL, TO_ROOM);
break;
case 9:
ch->SendText ("You are ONE with the universe.\n\r");
act (AT_ACTION, "$n is ranting on about 'the answer', "
"'ONE' and other mumbo-jumbo...",
ch, NULL, NULL, TO_ROOM);
break;
case 10:
ch->SendText ("You feel the end is near.\n\r");
act (AT_ACTION, "$n is muttering and ranting in "
"tongues...", ch, NULL, NULL, TO_ROOM);
break;
}
if (ch->GetMentalState () <= -30)
switch ((abs (ch->GetMentalState ())+5) / 10) {
case 10:
if (ch->GetPosition () > POS_SLEEPING) {
if ((ch->GetPosition () == POS_STANDING
|| ch->GetPosition () < POS_FIGHTING)
&& number_percent ()+10 <
abs (ch->GetMentalState ()))
then do_sleep (ch, "");
else
ch->SendText ("You're barely conscious.\n\r");
}
break;
case 9:
if (ch->GetPosition () > POS_SLEEPING) {
if ((ch->GetPosition () == POS_STANDING
|| ch->GetPosition () < POS_FIGHTING)
&& (number_percent ()+20) <
abs (ch->GetMentalState ()))
then do_sleep (ch, "");
else
ch->SendText (
"You can barely keep your eyes open.\n\r");
}
break;
case 8:
if (ch->GetPosition () > POS_SLEEPING) {
if (ch->GetPosition () < POS_SITTING
&& (number_percent ()+30) <
abs (ch->GetMentalState ()))
then do_sleep (ch, "");
else
ch->SendText ("You're extremely drowsy.\n\r");
}
break;
case 7:
if (ch->GetPosition () > POS_RESTING)
ch->SendText ("You feel very unmotivated.\n\r");
break;
case 6:
if (ch->GetPosition () > POS_RESTING)
ch->SendText ("You feel sedated.\n\r");
break;
case 5:
if (ch->GetPosition () > POS_RESTING)
ch->SendText ("You feel sleepy.\n\r");
break;
case 4:
if (ch->GetPosition () > POS_RESTING)
ch->SendText ("You feel tired.\n\r");
break;
case 3:
if (ch->GetPosition () > POS_RESTING)
ch->SendText ("You could use a rest.\n\r");
break;
}
if (ch->GetTimer () > 24)
do_quit (ch, "");
else
// save max of 10 per tick
if (ch == ch_save && SysData.IsAutoSave ()
&& ++save_count < 10)
save_char_obj (ch);
}
}
}
// Update all objs.
// This function is performance sensitive.
void obj_update ()
{
CObjData *obj;
short AT_TEMP;
CParseinfo Inf;
while (obj = Inf.ParseAreaLists (AllAreasList)) {
CCharacter *rch;
char *message;
set_cur_obj (obj);
if (obj->carried_by)
oprog_random_trigger (obj);
else if (obj->in_room && obj->in_room->GetArea ()->nplayer > 0)
oprog_random_trigger (obj);
if (obj_extracted (obj))
continue;
if (obj->item_type == ITEM_PIPE) {
if (IS_SET (obj->value [3], PIPE_LIT)) {
if (--obj->value [1] <= 0) {
obj->value [1] = 0;
REMOVE_BIT (obj->value [3], PIPE_LIT);
}
else if (IS_SET (obj->value [3], PIPE_HOT))
REMOVE_BIT (obj->value [3], PIPE_HOT);
else {
if (IS_SET (obj->value [3], PIPE_GOINGOUT)) {
REMOVE_BIT (obj->value [3], PIPE_LIT);
REMOVE_BIT (obj->value [3], PIPE_GOINGOUT);
}
else
SET_BIT (obj->value [3], PIPE_GOINGOUT);
}
if (!IS_SET (obj->value [3], PIPE_LIT))
SET_BIT (obj->value [3], PIPE_FULLOFASH);
}
else
REMOVE_BIT (obj->value [3], PIPE_HOT);
}
// Corpse decay
// (npc corpses decay at 8 times the rate of pc corpses) - Narn
if (obj->item_type == ITEM_CORPSE_PC
|| obj->item_type == ITEM_CORPSE_NPC) {
short timerfrac = UMAX (1, obj->timer - 1);
if (obj->item_type == ITEM_CORPSE_PC)
timerfrac = (int) (obj->timer / 8 + 1);
if (obj->timer > 0 && obj->value [2] > timerfrac) {
char buf [MAX_STRING_LENGTH];
char name [MAX_STRING_LENGTH];
char *bufptr;
bufptr = one_argument (obj->GetShortDescr (), name);
bufptr = one_argument (bufptr, name);
bufptr = one_argument (bufptr, name);
separate_obj (obj);
obj->value [2] = timerfrac;
sprintf (buf, corpse_descs [UMIN (timerfrac - 1, 4)],
capitalize (bufptr));
obj->SetDescription (buf);
}
}
// don't let inventory decay
if (obj->IsInventory ())
continue;
if ((obj->timer <= 0 || --obj->timer > 0))
continue;
// if we get this far, object's timer has expired.
AT_TEMP = AT_PLAIN;
switch (obj->item_type) {
default:
message = "$p mysteriously vanishes.";
AT_TEMP = AT_PLAIN;
break;
case ITEM_PORTAL:
message = "$p winks out of existence.";
remove_portal (obj);
obj->item_type = ITEM_TRASH; /* so extract_obj */
AT_TEMP = AT_MAGIC; /* doesn't remove_portal */
break;
case ITEM_FOUNTAIN:
message = "$p dries up.";
AT_TEMP = AT_BLUE;
break;
case ITEM_CORPSE_NPC:
message = "$p decays into dust and blows away.";
AT_TEMP = AT_OBJECT;
break;
case ITEM_CORPSE_PC:
message = "$p is sucked into a swirling vortex of colors...";
AT_TEMP = AT_MAGIC;
break;
case ITEM_FOOD:
message = "$p is devoured by a swarm of maggots.";
AT_TEMP = AT_HUNGRY;
break;
case ITEM_BLOOD:
message = "$p slowly seeps into the ground.";
AT_TEMP = AT_BLOOD;
break;
case ITEM_BLOODSTAIN:
message = "$p dries up into flakes and blows away.";
AT_TEMP = AT_BLOOD;
break;
case ITEM_SCRAPS:
message = "$p crumbles and decays into nothing.";
AT_TEMP = AT_OBJECT;
break;
case ITEM_FIRE:
if (obj->in_room)
--obj->in_room->light;
message = "$p burns out.";
AT_TEMP = AT_FIRE;
}
if (obj->carried_by) {
act (AT_TEMP, message, obj->carried_by, obj, NULL, TO_CHAR);
}
else if (obj->in_room
&& (rch = obj->in_room->first_person) != NULL
&& ! obj->IsBuried ()) {
act (AT_TEMP, message, rch, obj, NULL, TO_ROOM);
act (AT_TEMP, message, rch, obj, NULL, TO_CHAR);
}
if (obj->serial == cur_obj)
global_objcode = rOBJ_EXPIRED;
extract_obj (obj);
}
}
// Function to check important stuff happening to a player
// This function should take about 5% of mud cpu time
void char_check ()
{
CCharacter *ch, *ch_next;
CObjData *obj;
CExitData *pexit;
static int cnt = 0;
int door, retcode;
try {
cnt = (cnt+1) % 2;
for (ch = first_char; ch; ch = ch_next) {
set_cur_char (ch);
ch_next = ch->GetNext ();
will_fall (ch, 0);
if (char_died (ch))
continue;
if (ch->IsNpc ()) {
if (cnt != 0)
continue;
// running mobs -Thoric
if (ch->IsAction (ACT_RUNNING)) {
if (! ch->IsAction (ACT_SENTINEL)
&& !ch->GetFightData () && ch->hunting) {
WAIT_STATE (ch, 2 * PULSE_VIOLENCE);
hunt_victim (ch);
continue;
}
if (ch->GetSpecialMobFunction ()) {
if ((*ch->GetSpecialMobFunction ()) (ch))
continue;
if (char_died (ch))
continue;
}
if (! ch->IsAction (ACT_SENTINEL)
&& ! ch->IsAction (ACT_PROTOTYPE)
&& (door = number_bits (4)) <= 9
&& (pexit = get_exit (ch->GetInRoom (), door)) != NULL
&& pexit->GetToRoom ()
&& ! pexit->IsClosed ()
&& ! pexit->GetToRoom ()->IsNoMob ()
&& ! pexit->GetToRoom ()->IsDeathRoom ()
&& (! ch->IsAction (ACT_STAY_AREA)
|| pexit->GetToRoom ()->GetArea () == ch->GetInRoom ()->GetArea ())) {
retcode = move_char (ch, pexit, 0);
if (char_died (ch))
continue;
if (retcode != rNONE || ch->IsAction (ACT_SENTINEL)
|| ch->GetPosition () < POS_STANDING)
continue;
}
}
continue;
} else {
if (ch->mount
&& ch->GetInRoom () != ch->mount->GetInRoom ()) {
ch->mount->ClrActBit (ACT_MOUNTED);
ch->mount = NULL;
ch->SetPosition (POS_STANDING);
ch->SendText ("No longer upon your mount, you fall "
"to the ground...\n\rOUCH!\n\r");
}
if ((ch->GetInRoom ()
&& ch->GetInRoom ()->sector_type == SECT_UNDERWATER)
|| (ch->GetInRoom () &&
ch->GetInRoom ()->sector_type == SECT_OCEANFLOOR)) {
if (! ch->IsAffected (AFF_AQUA_BREATH)) {
if (ch->GetLevel () < LEVEL_IMMORTAL) {
int dam;
// Changed level of damage at Brittany's request. -- Narn
dam = number_range (ch->GetMaxHp () / 100, ch->GetMaxHp () / 50);
dam = UMAX (1, dam);
if (number_bits (3) == 0)
ch->SendText ("You cough and choke as you try to"
" breathe water!\n\r");
damage (ch, ch, dam, TYPE_UNDEFINED);
}
}
}
if (char_died (ch))
continue;
if (ch->GetInRoom ()
&& ((ch->GetInRoom ()->sector_type == SECT_WATER_NOSWIM)
|| (ch->GetInRoom ()->sector_type == SECT_WATER_SWIM))) {
if (! ch->IsFlying ()
&& ! ch->IsFloating ()
&& ! ch->IsAffected (AFF_AQUA_BREATH)
&& !ch->mount) {
POSITION pos = ch->GetHeadCarryPos ();
while (obj = ch->GetNextCarrying (pos))
if (obj->item_type == ITEM_BOAT)
break;
if (! obj) {
if (ch->GetLevel () < LEVEL_IMMORTAL) {
int mov;
int dam;
if (ch->GetMove () > 0) {
mov = number_range (ch->GetMaxMove () / 20,
ch->GetMaxMove () / 5);
mov = UMAX (1, mov);
ch->SetMove (
UMAX (0, ch->GetMove () - mov));
} else {
dam = number_range (ch->GetMaxHp () / 20,
ch->GetMaxHp () / 5);
dam = UMAX (1, dam);
if (number_bits (3) == 0)
ch->SendText ("Struggling with exhaustion, "
"you choke on a mouthful of water.\n\r");
damage (ch, ch, dam, TYPE_UNDEFINED);
}
}
}
}
}
// beat up on link dead players
if (!ch->GetDesc ()) {
CCharacter *wch, *wch_next;
for (wch=ch->GetInRoom ()->first_person; wch; wch=wch_next) {
wch_next = wch->GetNextInRoom ();
if (! wch->IsNpc () || wch->GetFightData ()
|| wch->IsCharmed () || ! wch->IsAwake ()
|| (wch->IsWimpy () && ch->IsAwake ())
|| !can_see (wch, ch))
continue;
if (is_hating (wch, ch)) {
found_prey (wch, ch);
continue;
}
if (! wch->IsAction (ACT_AGGRESSIVE)
|| wch->IsAction (ACT_MOUNTED)
|| wch->GetInRoom ()->IsSafe ())
continue;
global_retcode = multi_hit (wch, ch, TYPE_UNDEFINED);
}
}
}
}
}
catch (CException* ex) {
CSwException sx;
char buf [128];
UINT id;
if (ex->GetErrorMessage (buf, sizeof (buf), &id))
sx.Printf ("CException %u:%s in char_check.", id, buf);
else sx.Printf ("Unknown CException in char_check.");
ex->Delete ();
throw sx;
}
}
// Aggress.
//
// for each descriptor
// for each mob in room
// aggress on some random PC
//
// This function should take 5% to 10% of ALL mud cpu time.
// Unfortunately, checking on each PC move is too tricky,
// because we don't want the mob to just attack the first PC
// who leads the party into the room.
void aggr_update ()
{
CCharacter *wch;
CCharacter *ch;
CCharacter *ch_next;
CCharacter *vch;
CCharacter *vch_next;
CCharacter *victim;
CActProgData *apdtmp;
#ifdef UNDEFD
// GRUNT! To do
if (IS_NPC (wch) && wch->mpactnum > 0
&& wch->GetInRoom ()->area->nplayer > 0) {
CActProgData *tmp_act, *tmp2_act;
for (tmp_act = wch->mpact; tmp_act; tmp_act = tmp_act->next) {
oprog_wordlist_check (tmp_act->buf,wch, tmp_act->ch,
tmp_act->obj, tmp_act->vo, ACT_PROG);
delete tmp_act->buf;
}
for (tmp_act = wch->mpact; tmp_act; tmp_act = tmp2_act) {
tmp2_act = tmp_act->next;
delete tmp_act;
}
wch->mpactnum = 0;
wch->mpact = NULL;
}
#endif
// check mobprog act queue
while ((apdtmp = mob_act_list) != NULL) {
wch = (CCharacter*) mob_act_list->vo;
if (! char_died (wch) && wch->GetMobPactnum () > 0) {
CMobProgActList *tmp_act;
while ((tmp_act = wch->GetMobPact ())) {
if (tmp_act->obj && obj_extracted (tmp_act->obj))
tmp_act->obj = NULL;
if (tmp_act->ch && !char_died (tmp_act->ch))
mprog_wordlist_check (tmp_act->buf, wch, tmp_act->ch,
tmp_act->obj, tmp_act->vo, ACT_PROG);
wch->SetMobPact (tmp_act->GetNext ());
delete tmp_act;
}
wch->SetMobPactnum (0);
wch->SetMobPact (NULL);
}
mob_act_list = apdtmp->GetNext ();
delete apdtmp;
}
// Just check descriptors here for victims to aggressive mobs
// We can check for linkdead victims to mobile_update -Thoric
POSITION pos = DList.GetHeadPosition ();
while (pos) {
CDescriptor *d = DList.GetNext (pos);
if (d->IsDisconnecting () || d->m_Connected != CON_PLAYING
|| (wch=d->m_pCharacter) == NULL)
continue;
if (char_died (wch) || wch->IsNpc ()
|| wch->GetLevel () >= LEVEL_IMMORTAL || !wch->GetInRoom ())
continue;
for (ch = wch->GetInRoom ()->first_person; ch; ch = ch_next) {
int count;
ch_next = ch->GetNextInRoom ();
if (! ch->IsNpc () || ch->GetFightData () || ch->IsCharmed ()
|| ! ch->IsAwake () || (ch->IsWimpy () && wch->IsAwake ())
|| !can_see (ch, wch))
continue;
if (is_hating (ch, wch)) {
found_prey (ch, wch);
continue;
}
if (! ch->IsAction (ACT_AGGRESSIVE)
|| ch->IsAction (ACT_MOUNTED)
|| ch->GetInRoom ()->IsSafe ())
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->GetInRoom ()->first_person; vch; vch = vch_next) {
vch_next = vch->GetNextInRoom ();
if (! vch->IsNpc () && vch->GetLevel () < LEVEL_IMMORTAL
&& (! ch->IsWimpy () || ! vch->IsAwake ())
&& can_see (ch, vch)) {
if (number_range (0, count) == 0)
victim = vch;
count++;
}
}
if (!victim) {
bug ("Aggr_update: null victim.", count);
continue;
}
if (ch->IsNpc () && ch->CanBackStab ()) {
CObjData *obj;
if (!ch->mount
&& (obj = get_eq_char (ch, WEAR_WIELD)) != NULL
&& obj->value[3] == 11
&& !victim->GetFightData ()
&& victim->GetHp () >= victim->GetMaxHp ()) {
check_attacker (ch, victim);
WAIT_STATE (ch, SkillTable.GetBeats (gsn_backstab));
if (! victim->IsAwake ()
|| number_percent ()+5 < ch->GetLevel ()) {
global_retcode = multi_hit (ch, victim, gsn_backstab);
continue;
} else {
global_retcode = damage (ch, victim, 0, gsn_backstab);
continue;
}
}
}
global_retcode = multi_hit (ch, victim, TYPE_UNDEFINED);
}
}
}
// drunk randoms - Tricops
// (Made part of mobile_update -Thoric)
void drunk_randoms (CCharacter *ch)
{
CCharacter *rvch = NULL;
CCharacter *vch;
short drunk;
short position;
if (ch->IsNpc () || ch->GetPcData ()->condition[COND_DRUNK] <= 0)
return;
if (number_percent () < 30)
return;
drunk = ch->GetPcData ()->condition[COND_DRUNK];
position = ch->GetPosition ();
ch->SetPosition (POS_STANDING);
if (number_percent () < (2*drunk / 20))
check_social (ch, "burp", "");
else
if (number_percent () < (2*drunk / 20))
check_social (ch, "hiccup", "");
else
if (number_percent () < (2*drunk / 20))
check_social (ch, "drool", "");
else
if (number_percent () < (2*drunk / 20))
check_social (ch, "fart", "");
else
if (drunk > (10+ (ch->GetConstitution ()/5))
&& number_percent () < (2 * drunk / 18)) {
vch = ch->GetInRoom ()->first_person;
for (; vch; vch = vch->GetNextInRoom ())
if (number_percent () < 10)
rvch = vch;
check_social (ch, "puke", (rvch ? rvch->GetName () : ""));
}
ch->SetPosition (position);
}
void halucinations (CCharacter *ch)
{
if (ch->GetMentalState () >= 30
&& number_bits (5 - (ch->GetMentalState () >= 50)
- (ch->GetMentalState () >= 75)) == 0) {
char *t;
switch (number_range (1, UMIN (20, (ch->GetMentalState ()+5) / 5))) {
default:
case 1: t = "You feel very restless... you can't sit still.\n\r";
break;
case 2: t = "You're tingling all over.\n\r"; break;
case 3: t = "Your skin is crawling.\n\r"; break;
case 4: t = "You suddenly feel that something is terribly wrong.\n\r"; break;
case 5: t = "Those damn little fairies keep laughing at you!\n\r"; break;
case 6: t = "You can hear your mother crying...\n\r"; break;
case 7: t = "Have you been here before, or not? You're not sure...\n\r"; break;
case 8: t = "Painful childhood memories flash through your mind.\n\r"; break;
case 9: t = "You hear someone call your name in the distance...\n\r"; break;
case 10: t = "Your head is pulsating... you can't think straight.\n\r"; break;
case 11: t = "The ground... seems to be squirming...\n\r"; break;
case 12: t = "You're not quite sure what is real anymore.\n\r"; break;
case 13: t = "It's all a dream... or is it?\n\r"; break;
case 14: t = "They're coming to get you... coming to take you away...\n\r"; break;
case 15: t = "You begin to feel all powerful!\n\r"; break;
case 16: t = "You're light as air... the heavens are yours for the taking.\n\r"; break;
case 17: t = "Your whole life flashes by... and your future...\n\r"; break;
case 18: t = "You are everywhere and everything... you know all and are all!\n\r"; break;
case 19: t = "You feel immortal!\n\r"; break;
case 20: t = "Ahh... the power of a Supreme Entity... what to do...\n\r"; break;
}
ch->SendText (t);
}
}
void tele_update ()
{
CTeleportData *tele, *tele_next;
if (!first_teleport)
return;
for (tele = first_teleport; tele; tele = tele_next)
{
tele_next = tele->GetNext ();
if (--tele->timer <= 0)
{
if (tele->room->first_person)
{
if (tele->room->IsTeleShow ())
teleport (tele->room->first_person, tele->room->tele_vnum,
TELE_SHOWDESC | TELE_TRANSALL);
else
teleport (tele->room->first_person, tele->room->tele_vnum,
TELE_TRANSALL);
}
UNLINK (tele, first_teleport, last_teleport);
delete tele;
}
}
}
void auth_update ()
{
CCharacter *victim;
CString Msg, Log;
BOOL found_hit = FALSE; // was at least one found?
Log = "Pending authorizations:\n";
POSITION pos = DList.GetHeadPosition ();
while (pos) {
CDescriptor *d = DList.GetNext (pos);
if (d->IsDisconnecting ())
continue;
if ((victim = d->m_pCharacter) && victim->IsWaitingForAuth ()) {
found_hit = TRUE;
Msg.Format (" %s%s new %s %s\n", victim->GetName (),
victim->GetDesc ()->m_pHost,
RaceTable.GetName (victim->GetRace ()),
ClassTable.GetName (victim->GetClass ()));
Log += Msg;
}
}
if (found_hit) {
gpDoc->LogString (Log, LOG_PLAYER);
to_channel (Log, CHANNEL_MONITOR, "Monitor", 1);
}
}
// Handle all kinds of updates.
// Called once per pulse from game loop.
void update_handler ()
{
static int pulse_area;
static int pulse_mobile;
static int pulse_violence;
static int pulse_point;
static int pulse_second;
timeval stime;
timeval etime;
try {
if (timechar) {
set_char_color (AT_PLAIN, timechar);
timechar->SendText ("Starting update timer.\n\r");
gpDoc->gettimeofday (&stime, NULL);
}
if (--pulse_area <= 0) {
pulse_area = number_range (PULSE_AREA / 2, 3 * PULSE_AREA / 2);
area_update ();
}
if (--pulse_mobile <= 0) {
pulse_mobile = PULSE_MOBILE;
mobile_update ();
}
if (--pulse_violence <= 0) {
pulse_violence = PULSE_VIOLENCE;
violence_update ();
}
try {
if (--pulse_point <= 0) {
pulse_point = (int) (number_range ((int) (PULSE_TICK * 0.75),
(int) (PULSE_TICK * 1.25)));
auth_update (); // Gorog
weather_update ();
char_update ();
obj_update ();
clear_vrooms (); // remove virtual rooms
}
}
catch (CException* ex) {
CSwException sx;
char buf [128];
UINT id;
if (ex->GetErrorMessage (buf, sizeof (buf), &id))
sx.Printf ("CException %u:%s in Auth, weather...", id, buf);
else sx.Printf ("Unknown CException in Auth, weather...");
ex->Delete ();
throw sx;
}
if (--pulse_second <= 0) {
pulse_second = PULSE_PER_SECOND;
char_check ();
RebootCheck ();
}
try {
auction->Update (); // update any auction action
tele_update ();
aggr_update ();
obj_act_update ();
room_act_update ();
ExtractedObjList.RemoveAll (); // dispose of extracted objects
clean_char_queue (); // dispose of dead mobs/quitting chars
}
catch (CException* ex) {
CSwException sx;
char buf [128];
UINT id;
if (ex->GetErrorMessage (buf, sizeof (buf), &id))
sx.Printf ("CException %u:%s in Auction, aggr...", id, buf);
else sx.Printf ("Unknown CException in Auction, aggr...");
ex->Delete ();
throw sx;
}
if (timechar) {
gpDoc->gettimeofday (&etime, NULL);
set_char_color (AT_PLAIN, timechar);
timechar->SendText ("Update timing complete.\n\r");
subtract_times (&etime, &stime);
timechar->SendTextf ("Timing took %d.%06d seconds.\n\r",
etime.tv_sec, etime.tv_usec);
timechar = NULL;
}
}
catch (CException* ex) {
CSwException sx;
char buf [128];
UINT id;
if (ex->GetErrorMessage (buf, sizeof (buf), &id))
sx.Printf ("CException %u:%s in Update Handler.", id, buf);
else sx.Printf ("Unknown CException in Update Handler.");
ex->Delete ();
throw sx;
}
}
void remove_portal (CObjData *portal)
{
CRoomIndexData *fromRoom, *toRoom;
CCharacter *ch;
CExitData *pexit;
BOOL found;
if (!portal) {
bug ("remove_portal: portal is NULL");
return;
}
fromRoom = portal->in_room;
found = FALSE;
if (!fromRoom) {
bug ("remove_portal: portal->in_room is NULL");
return;
}
for (pexit = fromRoom->first_exit; pexit; pexit = pexit->GetNext ())
if (pexit->IsPortal ()) {
found = TRUE;
break;
}
if (!found) {
bug ("remove_portal: portal not found in room %d!", fromRoom->vnum);
return;
}
if (pexit->vdir != DIR_PORTAL)
bug ("remove_portal: exit in dir %d != DIR_PORTAL", pexit->vdir);
if ((toRoom = pexit->GetToRoom ()) == NULL)
bug ("remove_portal: toRoom is NULL");
extract_exit (fromRoom, pexit);
// send a message to toRoom
if (toRoom && (ch = toRoom->first_person))
act (AT_PLAIN, "A magical portal above winks from existence.",
ch, NULL, NULL, TO_ROOM);
}
class CBootData {
public:
char *msg;
BOOL bSent;
int time;
};
static CBootData Bd [] = {
{ "You feel the ground shake as the end comes near!", 0, 60 },
{ "Lightning crackles in the sky above!", 0, 120 },
{ "Crashes of thunder sound across the land!", 0, 180 },
{ "The sky has suddenly turned midnight black.", 0, 240 },
{ "You notice the life forms around you slowly dwindling away.", 0, 300 },
{ "The seas across the ($T) have turned frigid.", 0, 600 },
{ "The aura of magic that surrounds the ($T) seems slightly unstable.", 0, 900 },
{ "You sense a change in the magical forces surrounding you.", 0, 1800 },
};
#ifdef XXXX
static CBootData Bd [] = {
{ "You feel the ground shake as the end comes near!", 0, 10 },
{ "Lightning crackles in the sky above!", 0, 20 },
{ "Crashes of thunder sound across the land!", 0, 30 },
{ "The sky has suddenly turned midnight black.", 0, 40 },
{ "You notice the life forms around you slowly dwindling away.", 0, 50 },
{ "The seas across the ($T) have turned frigid.", 0, 60 },
{ "The aura of magic that surrounds the ($T) seems slightly unstable.", 0, 70 },
{ "You sense a change in the magical forces surrounding you.", 0, 80 },
};
#endif
void RebootCheck ()
{
if (gpDoc->IsShuttingDown ())
return;
CSwTime &Bt = gpDoc->m_RebootTime;
if (Bt <= CurrentTime) {
CCharacter *vch;
if (auction->IsActive ()) {
CString Sm;
Sm.Format ("Sale of %s has been stopped by mud.",
auction->GetItem ()->GetShortDescr ());
talk_auction (Sm);
obj_to_char (auction->GetItem (), auction->GetSeller ());
auction->Stop ();
}
// Save all the player files
for (vch = first_char; vch; vch = vch->GetNext ())
if (! vch->IsNpc ())
save_char_obj (vch);
ResetBootMessages ();
gpDoc->SetReboot ();
gpDoc->ShutSmaugDown ();
return;
}
if ((CurrentTime.GetTotalSeconds () % 1800) == 0) {
CString Pm;
Pm.Format ("%s: %d players", NCCP CurrentTime.GetString (),
DList.GetCount ());
append_to_file (FileTable.GetName (SM_USAGE_FILE), NCCP Pm);
}
CTime BootTime = Bt.GetCtime ();
CTime TestTime = CurrentTime.GetCtime ();
for (int i=0; i < DIM (Bd); ++i) {
if ((TestTime + Bd [i].time) > BootTime) {
if (! Bd [i].bSent) {
CString Msg = Bd [i].msg;
int pos = Msg.Find ("($T)");
if (pos > 0) {
Msg = Msg.Left (pos) + SysData.GetShortTitle () +
Msg.Mid (pos + 4);
}
echo_to_all (AT_YELLOW, Msg, ECHOTAR_ALL);
if (i < 6)
gpDoc->m_bDenyNewPlayers = TRUE;
Bd [i].bSent = TRUE;
}
break;
}
}
}
void ResetBootMessages ()
{
for (int i=0; i < DIM (Bd); ++i)
Bd [i].bSent = FALSE; // reset for next reboot
}
void subtract_times (struct timeval *etime, struct timeval *stime)
{
etime->tv_sec -= stime->tv_sec;
etime->tv_usec -= stime->tv_usec;
while (etime->tv_usec < 0)
{
etime->tv_usec += 1000000;
etime->tv_sec--;
}
return;
}