/****************************************************************************
* [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. *
* ------------------------------------------------------------------------ *
* "Special procedure" module *
****************************************************************************/
#include "stdafx.h"
#include "smaug.h"
#include "area.h"
#include "skill.h"
#include "mobiles.h"
#include "objects.h"
#include "rooms.h"
#include "descriptor.h"
#include "character.h"
/*
* The following special functions are available for mobiles.
*/
DECLARE_SPEC_FUN ( spec_breath_any );
DECLARE_SPEC_FUN ( spec_breath_acid );
DECLARE_SPEC_FUN ( spec_breath_fire );
DECLARE_SPEC_FUN ( spec_breath_frost );
DECLARE_SPEC_FUN ( spec_breath_gas );
DECLARE_SPEC_FUN ( spec_breath_lightning );
DECLARE_SPEC_FUN ( spec_cast_adept );
DECLARE_SPEC_FUN ( spec_cast_cleric );
DECLARE_SPEC_FUN ( spec_cast_mage );
DECLARE_SPEC_FUN ( spec_cast_undead );
DECLARE_SPEC_FUN ( spec_executioner );
DECLARE_SPEC_FUN ( spec_fido );
DECLARE_SPEC_FUN ( spec_guard );
DECLARE_SPEC_FUN ( spec_janitor );
DECLARE_SPEC_FUN ( spec_mayor );
DECLARE_SPEC_FUN ( spec_poison );
DECLARE_SPEC_FUN ( spec_thief );
/*
* Given a name, return the appropriate spec fun.
*/
SPEC_FUN *spec_lookup (const char *name)
{
if (!str_cmp (name, "spec_breath_any" )) return spec_breath_any;
if (!str_cmp (name, "spec_breath_acid" )) return spec_breath_acid;
if (!str_cmp (name, "spec_breath_fire" )) return spec_breath_fire;
if (!str_cmp (name, "spec_breath_frost" )) return spec_breath_frost;
if (!str_cmp (name, "spec_breath_gas" )) return spec_breath_gas;
if (!str_cmp (name, "spec_breath_lightning" )) return
spec_breath_lightning;
if (!str_cmp (name, "spec_cast_adept" )) return spec_cast_adept;
if (!str_cmp (name, "spec_cast_cleric" )) return spec_cast_cleric;
if (!str_cmp (name, "spec_cast_mage" )) return spec_cast_mage;
if (!str_cmp (name, "spec_cast_undead" )) return spec_cast_undead;
if (!str_cmp (name, "spec_executioner" )) return spec_executioner;
if (!str_cmp (name, "spec_fido" )) return spec_fido;
if (!str_cmp (name, "spec_guard" )) return spec_guard;
if (!str_cmp (name, "spec_janitor" )) return spec_janitor;
if (!str_cmp (name, "spec_mayor" )) return spec_mayor;
if (!str_cmp (name, "spec_poison" )) return spec_poison;
if (!str_cmp (name, "spec_thief" )) return spec_thief;
return 0;
}
/*
* Given a pointer, return the appropriate spec fun text.
*/
char *lookup_spec (SPEC_FUN *special)
{
if (special == spec_breath_any ) return "spec_breath_any";
if (special == spec_breath_acid) return "spec_breath_acid";
if (special == spec_breath_fire) return "spec_breath_fire";
if (special == spec_breath_frost ) return "spec_breath_frost";
if (special == spec_breath_gas ) return "spec_breath_gas";
if (special == spec_breath_lightning) return "spec_breath_lightning";
if (special == spec_cast_adept ) return "spec_cast_adept";
if (special == spec_cast_cleric) return "spec_cast_cleric";
if (special == spec_cast_mage ) return "spec_cast_mage";
if (special == spec_cast_undead) return "spec_cast_undead";
if (special == spec_executioner) return "spec_executioner";
if (special == spec_fido ) return "spec_fido";
if (special == spec_guard ) return "spec_guard";
if (special == spec_janitor) return "spec_janitor";
if (special == spec_mayor ) return "spec_mayor";
if (special == spec_poison ) return "spec_poison";
if (special == spec_thief ) return "spec_thief";
return "";
}
/* if a spell casting mob is hating someone... try and summon them */
void summon_if_hating (CCharacter *ch)
{
CCharacter *victim;
char buf[MAX_STRING_LENGTH];
char name[MAX_INPUT_LENGTH];
BOOL found = FALSE;
if (ch->GetFightData () || ch->fearing
|| !ch->hating || ch->GetInRoom ()->IsSafe ())
return;
/* if player is close enough to hunt... don't summon */
if (ch->hunting)
return;
one_argument (ch->hating->name, name);
/* make sure the char exists - works even if player quits */
for (victim = first_char;
victim;
victim = victim->GetNext ())
{
if (!str_cmp (ch->hating->name, victim->GetName ()))
{
found = TRUE;
break;
}
}
if (!found)
return;
if (ch->GetInRoom () == victim->GetInRoom ())
return;
if (!victim->IsNpc ())
sprintf (buf, "summon 0.%s", name);
else
sprintf (buf, "summon %s", name);
do_cast (ch, buf);
return;
}
/*
* Core procedure for dragons.
*/
BOOL dragon (CCharacter *ch, char *spell_name)
{
CCharacter *victim;
CCharacter *v_next;
int sn;
if (! ch->IsFightPosition ())
return FALSE;
for (victim = ch->GetInRoom ()->first_person; victim; victim = v_next) {
v_next = victim->GetNextInRoom ();
if (victim->GetFightWho () == ch && number_bits (2) == 0)
break;
}
if (! victim)
return FALSE;
if ((sn = SkillTable.Lookup (spell_name)) < 0)
return FALSE;
(*SkillTable.GetSpellFunction (sn)) (sn, ch->GetLevel (), ch, victim);
return TRUE;
}
/*
* Special procedures for mobiles.
*/
BOOL spec_breath_any (CCharacter *ch)
{
if (! ch->IsFightPosition ())
return FALSE;
switch (number_bits (3))
{
case 0: return spec_breath_fire (ch);
case 1:
case 2: return spec_breath_lightning (ch);
case 3: return spec_breath_gas (ch);
case 4: return spec_breath_acid (ch);
case 5:
case 6:
case 7: return spec_breath_frost (ch);
}
return FALSE;
}
BOOL spec_breath_acid (CCharacter *ch)
{
return dragon (ch, "acid breath");
}
BOOL spec_breath_fire (CCharacter *ch)
{
return dragon (ch, "fire breath");
}
BOOL spec_breath_frost (CCharacter *ch)
{
return dragon (ch, "frost breath");
}
BOOL spec_breath_gas (CCharacter *ch)
{
int sn;
if (! ch->IsFightPosition ())
return FALSE;
if ((sn = SkillTable.Lookup ("gas breath")) < 0)
return FALSE;
(*SkillTable.GetSpellFunction (sn)) (sn, ch->GetLevel (), ch, NULL);
return TRUE;
}
BOOL spec_breath_lightning (CCharacter *ch)
{
return dragon (ch, "lightning breath");
}
BOOL spec_cast_adept (CCharacter *ch)
{
CCharacter *victim;
CCharacter *v_next;
if (!ch->IsAwake ())
return FALSE;
for (victim = ch->GetInRoom ()->first_person; victim; victim = v_next)
{
v_next = victim->GetNextInRoom ();
if (victim != ch && can_see (ch, victim) && number_bits (1) == 0)
break;
}
if (!victim)
return FALSE;
switch (number_bits (3))
{
case 0:
act (AT_MAGIC, "$n utters the word 'ciroht'.", ch, NULL, NULL, TO_ROOM);
spell_smaug (SkillTable.Lookup ("armor"), ch->GetLevel (), ch, victim);
return TRUE;
case 1:
act (AT_MAGIC, "$n utters the word 'sunimod'.", ch, NULL, NULL, TO_ROOM);
spell_smaug (SkillTable.Lookup ("bless"), ch->GetLevel (), ch, victim);
return TRUE;
case 2:
act (AT_MAGIC, "$n utters the word 'suah'.", ch, NULL, NULL, TO_ROOM);
spell_cure_blindness (SkillTable.Lookup ("cure blindness"),
ch->GetLevel (), ch, victim);
return TRUE;
case 3:
act (AT_MAGIC, "$n utters the word 'nran'.", ch, NULL, NULL, TO_ROOM);
spell_smaug (SkillTable.Lookup ("cure light"),
ch->GetLevel (), ch, victim);
return TRUE;
case 4:
act (AT_MAGIC, "$n utters the word 'nyrcs'.", ch, NULL, NULL, TO_ROOM);
spell_cure_poison (SkillTable.Lookup ("cure poison"),
ch->GetLevel (), ch, victim);
return TRUE;
case 5:
act (AT_MAGIC, "$n utters the word 'gartla'.", ch, NULL, NULL, TO_ROOM);
spell_smaug (SkillTable.Lookup ("refresh"), ch->GetLevel (), ch, victim);
return TRUE;
}
return FALSE;
}
BOOL spec_cast_cleric (CCharacter *ch)
{
CCharacter *victim;
CCharacter *v_next;
char *spell;
int sn;
summon_if_hating (ch);
if (! ch->IsFightPosition ())
return FALSE;
for (victim = ch->GetInRoom ()->first_person; victim; victim = v_next)
{
v_next = victim->GetNextInRoom ();
if (victim->GetFightWho () == ch && number_bits (2) == 0)
break;
}
if (!victim || victim == ch)
return FALSE;
for (;;)
{
int min_level;
switch (number_bits (4))
{
case 0: min_level = 0; spell = "blindness"; break;
case 1: min_level = 3; spell = "cause serious"; break;
case 2: min_level = 7; spell = "earthquake"; break;
case 3: min_level = 9; spell = "cause critical"; break;
case 4: min_level = 10; spell = "dispel evil"; break;
case 5: min_level = 12; spell = "curse"; break;
case 7: min_level = 13; spell = "flamestrike"; break;
case 8:
case 9:
case 10: min_level = 15; spell = "harm"; break;
default: min_level = 16; spell = "dispel magic"; break;
}
if (ch->GetLevel () >= min_level)
break;
}
if ((sn = SkillTable.Lookup (spell)) < 0)
return FALSE;
(*SkillTable.GetSpellFunction (sn)) (sn, ch->GetLevel (), ch, victim);
return TRUE;
}
BOOL spec_cast_mage (CCharacter *ch)
{
CCharacter *victim;
CCharacter *v_next;
char *spell;
int sn;
summon_if_hating (ch);
if (! ch->IsFightPosition ())
return FALSE;
for (victim = ch->GetInRoom ()->first_person; victim; victim = v_next)
{
v_next = victim->GetNextInRoom ();
if (victim->GetFightWho () == ch && number_bits (2) == 0)
break;
}
if (!victim || victim == ch)
return FALSE;
for (;;)
{
int min_level;
switch (number_bits (4))
{
case 0: min_level = 0; spell = "blindness"; break;
case 1: min_level = 3; spell = "chill touch"; break;
case 2: min_level = 7; spell = "weaken"; break;
/* Commented out by Narn, Nov 28/95 at the request of Selic
case 3: min_level = 8; spell = "teleport"; break; */
case 4: min_level = 11; spell = "colour spray"; break;
case 6: min_level = 13; spell = "energy drain"; break;
case 7:
case 8:
case 9: min_level = 15; spell = "fireball"; break;
default: min_level = 20; spell = "acid blast"; break;
}
if (ch->GetLevel () >= min_level)
break;
}
if ((sn = SkillTable.Lookup (spell)) < 0)
return FALSE;
(*SkillTable.GetSpellFunction (sn)) (sn, ch->GetLevel (), ch, victim);
return TRUE;
}
BOOL spec_cast_undead (CCharacter *ch)
{
CCharacter *victim;
CCharacter *v_next;
char *spell;
int sn;
summon_if_hating (ch);
if (! ch->IsFightPosition ())
return FALSE;
for (victim = ch->GetInRoom ()->first_person; victim; victim = v_next)
{
v_next = victim->GetNextInRoom ();
if (victim->GetFightWho () == ch && number_bits (2) == 0)
break;
}
if (!victim || victim == ch)
return FALSE;
for (;;)
{
int min_level;
switch (number_bits (4))
{
case 0: min_level = 0; spell = "curse"; break;
case 1: min_level = 3; spell = "weaken"; break;
case 2: min_level = 6; spell = "chill touch"; break;
case 3: min_level = 9; spell = "blindness"; break;
case 4: min_level = 12; spell = "poison"; break;
case 5: min_level = 15; spell = "energy drain"; break;
case 6: min_level = 18; spell = "harm"; break;
/* Commented out by Narn, Nov 28/95
case 7: min_level = 21; spell = "teleport"; break; */
default: min_level = 35; spell = "gate"; break;
}
if (ch->GetLevel () >= min_level)
break;
}
if ((sn = SkillTable.Lookup (spell)) < 0)
return FALSE;
(*SkillTable.GetSpellFunction (sn)) (sn, ch->GetLevel (), ch, victim);
return TRUE;
}
BOOL spec_executioner (CCharacter *ch)
{
char buf[MAX_STRING_LENGTH];
CMobIndexData *cityguard;
CCharacter *victim;
CCharacter *v_next;
char *crime;
if (!ch->IsAwake () || ch->GetFightData ())
return FALSE;
crime = "";
for (victim = ch->GetInRoom ()->first_person; victim; victim = v_next)
{
v_next = victim->GetNextInRoom ();
if (!victim->IsNpc () && victim->IsKiller ())
{ crime = "KILLER"; break; }
if (!victim->IsNpc () && victim->IsThief ())
{ crime = "THIEF"; break; }
}
if (!victim)
return FALSE;
if (ch->GetInRoom ()->IsSafe ())
{
sprintf (buf, "%s is a %s! As well as a COWARD!",
victim->GetName (), crime);
do_yell (ch, buf);
return TRUE;
}
sprintf (buf, "%s is a %s! PROTECT THE INNOCENT! MORE BLOOOOD!!!",
victim->GetName (), crime);
do_shout (ch, buf);
multi_hit (ch, victim, TYPE_UNDEFINED);
if (char_died (ch))
return TRUE;
/* Added log in case of missing cityguard -- Tri */
cityguard = MobTable.GetMob (MOB_VNUM_CITYGUARD);
if (!cityguard)
{
sprintf (buf, "Missing Cityguard - Vnum:[%d]", MOB_VNUM_CITYGUARD);
bug (buf, 0);
return TRUE;
}
create_mobile (cityguard)->SendToRoom (ch->GetInRoom ());
create_mobile (cityguard)->SendToRoom (ch->GetInRoom ());
return TRUE;
}
BOOL spec_fido (CCharacter *ch)
{
CObjData *corpse;
CObjData *obj;
if (! ch->IsAwake ())
return FALSE;
POSITION pos = ch->GetInRoom ()->GetHeadContentPos ();
while (corpse = ch->GetInRoom ()->GetNextContent (pos)) {
if (corpse->item_type != ITEM_CORPSE_NPC)
continue;
act (AT_ACTION, "$n savagely devours a corpse.", ch, NULL, NULL, TO_ROOM);
POSITION Cpos = corpse->GetHeadContentPos ();
while (obj = corpse->GetNextContent (Cpos)) {
obj_from_obj (obj);
obj_to_room (obj, ch->GetInRoom ());
}
extract_obj (corpse);
return TRUE;
}
return FALSE;
}
BOOL spec_guard (CCharacter *ch)
{
char buf[MAX_STRING_LENGTH];
CCharacter *victim;
CCharacter *v_next;
CCharacter *ech;
char *crime;
int max_evil;
if (!ch->IsAwake () || ch->GetFightData ())
return FALSE;
max_evil = 300;
ech = NULL;
crime = "";
for (victim = ch->GetInRoom ()->first_person; victim; victim = v_next)
{
v_next = victim->GetNextInRoom ();
if (!victim->IsNpc () && victim->IsKiller ())
{ crime = "KILLER"; break; }
if (!victim->IsNpc () && victim->IsThief ())
{ crime = "THIEF"; break; }
if (victim->GetFightData () && victim->GetFightWho () != ch
&& victim->GetAlignment () < max_evil)
{
max_evil = victim->GetAlignment ();
ech = victim;
}
}
if (victim && ch->GetInRoom ()->IsSafe ())
{
sprintf (buf, "%s is a %s! As well as a COWARD!",
victim->GetName (), crime);
do_yell (ch, buf);
return TRUE;
}
if (victim)
{
sprintf (buf, "%s is a %s! PROTECT THE INNOCENT!! BANZAI!!",
victim->GetName (), crime);
do_shout (ch, buf);
multi_hit (ch, victim, TYPE_UNDEFINED);
return TRUE;
}
if (ech)
{
act (AT_YELL, "$n screams 'PROTECT THE INNOCENT!! BANZAI!!",
ch, NULL, NULL, TO_ROOM);
multi_hit (ch, ech, TYPE_UNDEFINED);
return TRUE;
}
return FALSE;
}
BOOL spec_janitor (CCharacter *ch)
{
CObjData *trash;
if (! ch->IsAwake ())
return FALSE;
POSITION pos = ch->GetInRoom ()->GetHeadContentPos ();
while (trash = ch->GetInRoom ()->GetNextContent (pos)) {
if (! IS_SET (trash->wear_flags, ITEM_TAKE) || trash->IsBuried ())
continue;
if (trash->item_type == ITEM_DRINK_CON
|| trash->item_type == ITEM_TRASH
|| trash->cost < 10
|| (trash->pIndexData->vnum == OBJ_VNUM_SHOPPING_BAG
&& trash->IsEmpty ())) {
act (AT_ACTION, "$n picks up some trash.", ch, NULL, NULL, TO_ROOM);
obj_from_room (trash);
obj_to_char (trash, ch);
return TRUE;
}
}
return FALSE;
}
BOOL spec_mayor (CCharacter *ch)
{
static const char open_path[] =
"W3a3003b33000c111d0d111Oe333333Oe22c222112212111a1S.";
static const char close_path[] =
"W3a3003b33000c111d0d111CE333333CE22c222112212111a1S.";
static const char *path;
static int pos;
static BOOL move;
if (!move)
{
if (time_info.hour == 6)
{
path = open_path;
move = TRUE;
pos = 0;
}
if (time_info.hour == 20)
{
path = close_path;
move = TRUE;
pos = 0;
}
}
if (ch->GetFightData ())
return spec_cast_cleric (ch);
if (!move || ch->GetPosition () < POS_SLEEPING)
return FALSE;
switch (path[pos])
{
case '0':
case '1':
case '2':
case '3':
move_char (ch, get_exit (ch->GetInRoom (), path[pos] - '0'), 0);
break;
case 'W':
ch->SetPosition (POS_STANDING);
act (AT_ACTION, "$n awakens and groans loudly.", ch, NULL, NULL, TO_ROOM);
break;
case 'S':
ch->SetPosition (POS_SLEEPING);
act (AT_ACTION, "$n lies down and falls asleep.", ch, NULL, NULL, TO_ROOM);
break;
case 'a':
act (AT_SAY, "$n says 'Hello Honey!'", ch, NULL, NULL, TO_ROOM);
break;
case 'b':
act (AT_SAY, "$n says 'What a view! I must do something about that dump!'",
ch, NULL, NULL, TO_ROOM);
break;
case 'c':
act (AT_SAY, "$n says 'Vandals! Youngsters have no respect for anything!'",
ch, NULL, NULL, TO_ROOM);
break;
case 'd':
act (AT_SAY, "$n says 'Good day, citizens!'", ch, NULL, NULL, TO_ROOM);
break;
case 'e':
act (AT_SAY, "$n says 'I hereby declare the town of Darkhaven open!'",
ch, NULL, NULL, TO_ROOM);
break;
case 'E':
act (AT_SAY, "$n says 'I hereby declare the town of Darkhaven closed!'",
ch, NULL, NULL, TO_ROOM);
break;
case 'O':
do_unlock (ch, "gate");
do_open (ch, "gate");
break;
case 'C':
do_close (ch, "gate");
do_lock (ch, "gate");
break;
case '.' :
move = FALSE;
break;
}
pos++;
return FALSE;
}
BOOL spec_poison (CCharacter *ch)
{
CCharacter *victim;
if (! ch->IsFightPosition ()
|| (victim = ch->GetFightWho ()) == NULL
|| number_percent () > 2 * ch->GetLevel ())
return FALSE;
act (AT_HIT, "You bite $N!", ch, NULL, victim, TO_CHAR );
act (AT_ACTION, "$n bites $N!", ch, NULL, victim, TO_NOTVICT);
act (AT_POISON, "$n bites you!", ch, NULL, victim, TO_VICT );
spell_poison (gsn_poison, ch->GetLevel (), ch, victim);
return TRUE;
}
BOOL spec_thief (CCharacter *ch)
{
CCharacter *victim;
CCharacter *v_next;
int gold, maxgold;
if (ch->GetPosition () != POS_STANDING)
return FALSE;
for (victim = ch->GetInRoom ()->first_person; victim; victim = v_next)
{
v_next = victim->GetNextInRoom ();
if (victim->IsNpc ()
|| victim->GetLevel () >= LEVEL_IMMORTAL
|| number_bits (2) != 0
|| !can_see (ch, victim)) /* Thx Glop */
continue;
if (victim->IsAwake () && number_range (0, ch->GetLevel ()) == 0)
{
act (AT_ACTION, "You discover $n's hands in your sack of gold!",
ch, NULL, victim, TO_VICT);
act (AT_ACTION, "$N discovers $n's hands in $S sack of gold!",
ch, NULL, victim, TO_NOTVICT);
return TRUE;
}
else
{
maxgold = ch->GetLevel () * ch->GetLevel () * 1000;
gold = victim->GetGold ()
* number_range (1, URANGE (2, ch->GetLevel ()/4, 10)) / 100;
ch->AddGold (9 * gold / 10);
victim->AddGold (-gold);
if (ch->GetGold () > maxgold)
{
ch->GetInRoom ()->GetArea ()->BoostEconomy (ch->GetGold () - maxgold/2);
ch->SetGold (maxgold/2);
}
return TRUE;
}
}
return FALSE;
}