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