.-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-.
| |
| |
| |
| IMPROVED DETECT POISON |
| |
! !
: A Snippet written by :
: Valcados :
. for SMAUG MUDS .
. .
: :
: :
! !
| |
| |
| |
| |
| |
`-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-'
DESCRIPTION:
I asked myself: "what part of SMAUG most desperately needs a
facelift?" The answer was obvious: the spell, detect poison.
This snippet does just that.
IN-GAME MECHANICS:
Here's an exhaustive list of changes this does.
- Works on all items, not just edibles.
- Appropriate special message for casting detect poison on
the black poison powder object used for poisoning weaps.
- Detects if a drinkcon is empty
- Detects alcoholic beverage and responds humorously.
- Detects rotting food, warns of food poisoning
- Takes cooking code into account
- For pills/potions/salves, checks whether one of the spells
case is poison, sleep, curse, blindness, gas breath, or
chill touch. Responds accordingly.
- Checks if a weapon is poisoned.
- For weapons, armor, lights, staves, and wands, checks
to see if, when worn, the item has poison/curse/sleep as
an effect.
- Appropriate reply when cast on blood or quills
- When cast on NPC corpse, check if the mob was poisonous
while it lived.
- When cast on a container, count the number of poison
objects inside.
- Detects poison-themed traps.
KNOWN PROBLEMS/BUGS:
None at this time.
HOW TO INSTALL:
1. In magic.c, just above spell_detect_poison, add this:
DECLARE_SPEC_FUN( spec_poison );
2. Comment out, or delete, the original spell_detect_poison,
and replace it with this:
ch_ret spell_detect_poison( int sn, int level, CHAR_DATA *ch, void *vo )
{
OBJ_DATA *obj = (OBJ_DATA *) vo;
set_char_color( AT_MAGIC, ch );
/* Handle a certain special case */
if ( obj->pIndexData->vnum == OBJ_VNUM_BLACK_POWDER )
{
send_to_char( "You are OVERWHELMED by the amount of poison in this object!\n\r", ch );
set_char_color( AT_CYAN, ch );
send_to_char( "You convulse in disgust.\n\r", ch );
act( AT_MAGIC, "As $n casts detect poison on the black powder, $e recoils in horror!", ch, NULL, NULL, TO_ROOM );
return rNONE;
}
/* Do a switch based on item type */
switch( obj->item_type )
{
case ITEM_DRINK_CON:
if ( obj->value[1] <= 0 )
{
send_to_char( "You notice the container is empty.\n\r", ch );
break; /* Even though it's empty, it might be booby-trapped, so don't return yet */
}
/* For drink_cons, check for alcohol in addition */
if ( obj->value[2] >= LIQ_MAX )
{
bug( "Spell_detect_poison: bad liquid number %d on item %d",
obj->value[2], obj->pIndexData->vnum );
}
else if ( liq_table[obj->value[2]].liq_affect[COND_DRUNK] >= 10 )
send_to_char( "You sense some very strong alcohol.\n\r", ch );
else if ( liq_table[obj->value[2]].liq_affect[COND_DRUNK] >= 5 )
send_to_char( "You sense some strong alcohol.\n\r", ch );
else if ( liq_table[obj->value[2]].liq_affect[COND_DRUNK] >= 1 )
send_to_char( "You sense some alcohol.\n\r", ch );
case ITEM_FOOD: /* Includes case ITEM_DRINK_CON, for lack of break */
if ( obj->value[3] != 0 )
send_to_char( "You smell poisonous fumes.\n\r", ch );
else
{
/* If food is decaying, it is possible it can poison the player
despite not being flagged as poisonous... see do_eat */
if ( obj->item_type == ITEM_FOOD
&& obj->timer > 0
&& obj->value[1] > 0 )
{
switch( ( obj->timer * 10 ) / obj->value[1] )
{
case 0:
send_to_char( "You smell poisonous fumes.\n\r", ch );
break;
case 1:
send_to_char( "This item is thoroughly rotten.\n\r", ch );
send_to_char( "There's a high chance it'll give you food poisoning.\n\r", ch );
break;
case 2:
send_to_char( "This item is very rotten.\n\r", ch );
send_to_char( "There's a decent chance it'll give you food poisoning.\n\r", ch );
break;
case 3:
send_to_char( "This item is somewhat rotten.\n\r", ch );
send_to_char( "There's a chance it'll give you food poisoning.\n\r", ch );
break;
default:
send_to_char( "It looks very delicious.\n\r", ch );
break;
}
}
else
send_to_char( "It looks very delicious.\n\r", ch );
}
break;
case ITEM_COOK:
if ( obj->value[2] == 0 )
send_to_char( "It looks undercooked.\n\r", ch );
else if ( obj->value[3] != 0 )
send_to_char( "You smell poisonous fumes.\n\r", ch );
else if ( obj->value[2] > 2 )
send_to_char( "It looks burnt to a crisp... but edible.\n\r", ch );
else if ( obj->value[2] == 2 )
send_to_char( "It looks overcooked... but edible.\n\r", ch );
else
send_to_char( "It looks very delicious.\n\r", ch );
break;
case ITEM_PILL:
case ITEM_POTION:
case ITEM_SALVE:
/* For pills and potions and salves, examine the spells they cast */
/* We use a trick so that a single for-loop suffices for all 3, even
though salves have different objectvalue structure */
{
int psn = 0, sleep = 0, curse = 0, sn;
bool fPoison = FALSE, fSleep = FALSE, fCurse = FALSE;
int objvals[4], *objval;
SKILLTYPE *skill;
if ( obj->item_type == ITEM_SALVE )
objvals[0] = 4, objvals[1] = 5, objvals[2] = -1; /* objvalues with spell sn's */
else
objvals[0] = 1, objvals[1] = 2, objvals[2] = 3, objvals[3] = -1;
for ( objval = objvals; *objval != -1; objval++ )
{
sn = obj->value[*objval];
if ( sn == -1 )
continue;
if ( sn == gsn_poison )
{
psn += obj->value[0]; /* v0 = spell level */
fPoison = TRUE;
continue;
}
if ( sn == gsn_sleep )
{
sleep += obj->value[0];
fSleep = TRUE;
continue;
}
if ( sn == gsn_curse )
{
curse += obj->value[0];
fCurse = TRUE;
continue;
}
if ( sn == gsn_blindness ) /* Count this as a minor poison */
{
psn += obj->value[0] / 3;
fPoison = TRUE;
continue;
}
if ( sn == gsn_chill_touch ) /* Count this as a minor depressant */
{
sleep += obj->value[0] / 10;
fSleep = TRUE;
continue;
}
skill = get_skilltype(sn);
if ( !skill || !skill->spell_fun )
continue;
/* Count gas breath as a weak poison, even though it's not
the _eater_ who suffers.... ;) */
if ( skill->spell_fun == spell_gas_breath )
{
psn += obj->value[0] / 2;
fPoison = TRUE;
}
else if ( skill->spell_fun == spell_remove_curse )
curse = 0;
else if ( skill->spell_fun == spell_cure_poison )
psn = 0;
else if ( skill->spell_fun == spell_dispel_magic )
{
psn /= 10;
curse /= 10;
sleep /= 10;
}
} /* for loop */
if ( !fPoison && !fCurse && !fSleep )
{
if ( obj->item_type != ITEM_SALVE )
send_to_char( "It looks very delicious.\n\r", ch );
send_to_char( "It looks very delicious.\n\r", ch );
else
send_to_char( "It looks very healthy.\n\r", ch );
}
else
{
if ( fPoison && psn == 0 )
send_to_char( "This bizarre substance combines toxins and antidotes.\n\r", ch );
else if ( psn > 0 )
{
if ( psn >= 50 )
send_to_char( "This item is full of poison!\n\r", ch );
else if ( psn >= 30 )
send_to_char( "This item contains a lot of poison!\n\r", ch );
else if ( psn >= 10 )
send_to_char( "This item contains poison!\n\r", ch );
else
send_to_char( "This item contains trace amounts of poison!\n\r", ch );
}
if ( fCurse && curse == 0 )
send_to_char( "This bizarre substance combines spiritual toxins with spiritual antidotes.\n\r", ch );
else if ( curse > 0 )
{
if ( curse >= 50 )
send_to_char( "This item is full of spiritual poison!\n\r", ch );
else if ( curse >= 30 )
send_to_char( "This item contains a lot of spiritual poison!\n\r", ch );
else if ( curse >= 10 )
else if ( curse >= 10 )
send_to_char( "This item contains spiritual poison!\n\r", ch );
else
send_to_char( "This item contains trace amounts of spiritual poison!\n\r", ch );
}
if ( fSleep && sleep == 0 )
send_to_char( "This bizarre substance combines depressants with their antidotes.\n\r", ch );
else if ( sleep > 0 )
{
if ( sleep >= 50 )
send_to_char( "This item is full of depressants!\n\r", ch );
else if ( sleep >= 30 )
send_to_char( "This item contains a lot of depressants!\n\r", ch );
else if ( sleep >= 10 )
send_to_char( "This item contains depressants!\n\r", ch );
else
send_to_char( "This item contains trace amounts of depressants!\n\r", ch );
}
}
} /* ends case ITEM_PILL/POTION/SALVE */
break;
/* Report poisoned weapons */
case ITEM_WEAPON:
if ( xIS_SET( obj->extra_flags, ITEM_POISONED ) )
send_to_char( "This weapon is coated in a deadly poison.\n\r", ch );
case ITEM_ARMOR: /* Includes case ITEM_WEAPON for lack of a break*/
case ITEM_LIGHT:
case ITEM_STAFF:
case ITEM_WAND:
/* For equipment, examine affects */
{
AFFECT_DATA *paf;
int poisons = 0; /* This is a flag of individual bits */
SKILLTYPE *skill;
char * const equipment_poison_report[] = {
"no poison",
"deadly poisons",
"spiritual poisons",
"holistic poisons",
"depressants",
"poisonous depressants",
"accursed depressants",
"a truly vile mixture of toxins" };
for ( paf = obj->pIndexData->first_affect; paf; paf = paf->next )
{
if ( paf->location == APPLY_AFFECT
&& IS_SET( paf->modifier, 1 << AFF_POISON ) )
poisons |= 1;
if ( paf->location == APPLY_AFFECT
&& IS_SET( paf->modifier, 1 << AFF_CURSE ) )
poisons |= 2;
if ( paf->location == APPLY_AFFECT
&& IS_SET( paf->modifier, 1 << AFF_SLEEP ) )
poisons |= 4;
if ( ( paf->location == APPLY_WEARSPELL || paf->location == APPLY_REMOVESPELL )
&& IS_VALID_SN( paf->modifier )
&& ( skill = skill_table[paf->modifier] ) != NULL
&& skill->spell_fun )
{
if ( skill->spell_fun == spell_poison )
poisons |= 1;
else if ( skill->spell_fun == spell_curse )
poisons |= 2;
else if ( skill->spell_fun == spell_sleep )
poisons |= 4;
}
}
for ( paf = obj->first_affect; paf; paf = paf->next )
{
if ( paf->location == APPLY_AFFECT
&& IS_SET( paf->modifier, 1 << AFF_POISON ) )
poisons |= 1;
if ( paf->location == APPLY_AFFECT
&& IS_SET( paf->modifier, 1 << AFF_CURSE ) )
poisons |= 2;
if ( paf->location == APPLY_AFFECT
&& IS_SET( paf->modifier, 1 << AFF_SLEEP ) )
poisons |= 4;
if ( ( paf->location == APPLY_WEARSPELL || paf->location == APPLY_REMOVESPELL )
&& IS_VALID_SN( paf->modifier )
&& ( skill = skill_table[paf->modifier] ) != NULL
&& skill->spell_fun )
{
if ( skill->spell_fun == spell_poison )
poisons |= 1;
else if ( skill->spell_fun == spell_curse )
poisons |= 2;
else if ( skill->spell_fun == spell_sleep )
poisons |= 4;
}
}
if ( obj->item_type != ITEM_WEAPON || !xIS_SET( obj->extra_flags, ITEM_POISONED ) || poisons > 1 )
ch_printf( ch, "You manage to detect %s.\n\r", equipment_poison_report[poisons] );
} /* end case WEAPON/ARMOR */
break;
case ITEM_BLOOD:
if ( IS_VAMPIRE( ch ) )
send_to_char( "It looks extremely delicious.\n\r", ch );
else
send_to_char( "It is not in your nature to drink this.\n\r"
"But you sense no poison.\n\r", ch );
break;
/* For NPC corpses, check for poison attacks the mob had while alive,
or if the mob was innately affected by poison */
case ITEM_CORPSE_NPC:
{
MOB_INDEX_DATA *pMobIndex;
pMobIndex = get_mob_index((sh_int) abs(obj->cost));
if ( !pMobIndex )
{
send_to_char( "It looks very delicious.\n\r", ch );
break;
}
if ( pMobIndex->spec_fun == spec_poison
|| xIS_SET( pMobIndex->affected_by, AFF_POISON )
|| xIS_SET( pMobIndex->affected_by, AFF_VENOMSHIELD )
|| xIS_SET( pMobIndex->attacks, ATCK_POISON )
|| xIS_SET( pMobIndex->attacks, ATCK_NASTYPOISON )
|| xIS_SET( pMobIndex->attacks, ATCK_GASBREATH ) )
send_to_char( "You smell poisonous fumes.\n\r", ch );
else
send_to_char( "It looks very delicious.\n\r", ch );
}
break;
case ITEM_PEN:
if ( obj->value[1] >= 1 )
send_to_char( "You smell ink. It is probably not good to eat.\n\r", ch );
else
send_to_char( "It looks dry.\n\r", ch );
break;
case ITEM_CONTAINER:
/* Count the number of poisonous foods and drinks */
{
OBJ_DATA *inside;
int poison = 0;
for ( inside = obj->first_content; inside; inside = inside->next_content )
{
if ( inside->item_type != ITEM_FOOD && inside->item_type != ITEM_DRINK_CON && inside->item_type != ITEM_COOK )
continue;
if ( inside->value[3] != 0 )
poison += inside->count;
else if ( inside->item_type == ITEM_COOK && inside->value[2] == 0 )
poison += inside->count;
}
if ( !poison )
send_to_char( "You don't sense any poisoned food or drink in this container.\n\r", ch );
else
ch_printf( ch, "You sense the presence of %d poisonous substance%s.\n\r", poison, poison != 1 ? "s" : "" );
}
break;
default:
send_to_char( "It doesn't look poisoned.\n\r", ch );
break;
}
/* Check for poisonous booby-traps */
if ( obj->first_content )
{
OBJ_DATA *trap;
for ( trap = obj->first_content; trap; trap = trap->next_content )
{
if ( trap->item_type == ITEM_TRAP
&& ( trap->value[1] == TRAP_TYPE_POISON_GAS
|| trap->value[1] == TRAP_TYPE_POISON_DART
|| trap->value[1] == TRAP_TYPE_POISON_NEEDLE
|| trap->value[1] == TRAP_TYPE_POISON_DAGGER
|| trap->value[1] == TRAP_TYPE_POISON_ARROW ) )
{
send_to_char( "Your instincts alert you to the presence of a poisonous booby-trap!\n\r", ch );
break;
}
}
}
return rNONE;
}