/**************************************************************************/
// effects.cpp - effect spells
/***************************************************************************
* The Dawn of Time v1.69r (c)1997-2004 Michael Garratt *
* >> A number of people have contributed to the Dawn codebase, with the *
* majority of code written by Michael Garratt - www.dawnoftime.org *
* >> To use this source code, you must fully comply with all the licenses *
* in licenses.txt... In particular, you may not remove this copyright *
* notice. *
***************************************************************************
* >> Original Diku Mud copyright (c)1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, & Katja Nyboe. *
* >> Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* >> ROM 2.4 is copyright 1993-1995 Russ Taylor and has been brought to *
* you by the ROM consortium: Russ Taylor(rtaylor@pacinfo.com), *
* Gabrielle Taylor(gtaylor@pacinfo.com) & Brian Moore(rom@rom.efn.org) *
* >> Oblivion 1.2 is copyright 1996 Wes Wagner *
**************************************************************************/
#include "include.h" // dawn standard includes
/***************************************************************************/
void acid_effect(void *vo, int level, int dam, int target)
{
if (target == TARGET_ROOM) // nail objects on the floor
{
ROOM_INDEX_DATA *room = (ROOM_INDEX_DATA *) vo;
OBJ_DATA *obj, *obj_next;
for (obj = room->contents; obj != NULL; obj = obj_next)
{
obj_next = obj->next_content;
acid_effect(obj,level,dam,TARGET_OBJ);
}
return;
}
if (target == TARGET_CHAR) // do the effect on a victim
{
char_data *victim = (char_data *) vo;
OBJ_DATA *obj, *obj_next;
// let's toast some gear
for (obj = victim->carrying; obj != NULL; obj = obj_next)
{
obj_next = obj->next_content;
acid_effect(obj,level,dam,TARGET_OBJ);
}
return;
}
if (target == TARGET_OBJ) // toast an object
{
OBJ_DATA *obj = (OBJ_DATA *) vo;
OBJ_DATA *t_obj,*n_obj;
int chance;
char *msg;
if (IS_OBJ_STAT(obj,OBJEXTRA_BURN_PROOF)
|| IS_OBJ_STAT(obj,OBJEXTRA_NOPURGE)
|| number_range(0,4) == 0)
return;
chance = level / 4 + dam / 10;
if (chance > 25)
chance = (chance - 25) / 2 + 25;
if (chance > 50)
chance = (chance - 50) / 2 + 50;
if (IS_OBJ_STAT(obj,OBJEXTRA_BLESS))
chance -= 5;
chance -= obj->level * 2;
switch (obj->item_type)
{
default:
return;
case ITEM_CAULDRON:
case ITEM_CONTAINER:
case ITEM_FLASK:
case ITEM_MORTAR:
case ITEM_CORPSE_PC:
case ITEM_CORPSE_NPC:
msg = "$p fumes and dissolves.";
break;
case ITEM_ARMOR:
msg = "$p is pitted and etched.";
break;
case ITEM_CLOTHING:
msg = "$p is corroded into scrap.";
break;
case ITEM_STAFF:
case ITEM_WAND:
chance -= 10;
msg = "$p corrodes and breaks.";
break;
case ITEM_SCROLL:
chance += 10;
msg = "$p is burned into waste.";
break;
case ITEM_COMPONENT:
chance -= 10;
msg = "$p melts before your very eyes.";
break;
}
chance = URANGE(5,chance,95);
if (number_percent() > chance)
return;
if (obj->carried_by != NULL)
act(msg,obj->carried_by,obj,NULL,TO_ALL);
else if (obj->in_room != NULL && obj->in_room->people != NULL)
act(msg,obj->in_room->people,obj,NULL,TO_ALL);
if (obj->item_type == ITEM_ARMOR) /* etch it */
{
AFFECT_DATA *paf;
bool af_found = false;
int i;
affects_from_template_to_obj(obj); // make sure the object has local affects
for ( paf = obj->affected; paf; paf = paf->next)
{
if ( paf->location == APPLY_AC)
{
af_found = true;
paf->type = -1;
paf->modifier += 1;
paf->level = UMAX(paf->level,level);
// break; // Kal - do all ac modifiers? can have more than one?
}
}
if (!af_found) // needs a new affect
{
paf = new_affect();
paf->type = -1;
paf->level = level;
paf->duration = -1;
paf->location = APPLY_AC;
paf->modifier = 1;
paf->bitvector = 0;
paf->next = obj->affected;
obj->affected = paf;
}
if (obj->carried_by != NULL && obj->wear_loc != WEAR_NONE)
for (i = 0; i < 4; i++)
obj->carried_by->armor[i] += 1;
return;
}
// get rid of the object
if (obj->contains) // dump contents
{
for (t_obj = obj->contains; t_obj != NULL; t_obj = n_obj)
{
n_obj = t_obj->next_content;
obj_from_obj(t_obj);
if (obj->in_room != NULL)
obj_to_room(t_obj,obj->in_room);
else if (obj->carried_by != NULL)
obj_to_room(t_obj,obj->carried_by->in_room);
else
{
extract_obj(t_obj);
continue;
}
acid_effect(t_obj,level/2,dam/2,TARGET_OBJ);
}
}
extract_obj(obj);
return;
}
}
/***************************************************************************/
void cold_effect(void *vo, int level, int dam, int target)
{
if (target == TARGET_ROOM) // nail objects on the floor
{
ROOM_INDEX_DATA *room = (ROOM_INDEX_DATA *) vo;
OBJ_DATA *obj, *obj_next;
for (obj = room->contents; obj != NULL; obj = obj_next)
{
obj_next = obj->next_content;
cold_effect(obj,level,dam,TARGET_OBJ);
}
return;
}
if (target == TARGET_CHAR) // whack a character
{
char_data *victim = (char_data *) vo;
OBJ_DATA *obj, *obj_next;
// chill touch effect
if (!saves_spell(level/4 + dam / 20, victim, DAM_COLD))
{
AFFECT_DATA af;
act("$n turns blue and shivers.",victim,NULL,NULL,TO_ROOM);
act("A chill sinks deep into your bones.",victim,NULL,NULL,TO_CHAR);
af.where = WHERE_AFFECTS;
af.type = gsn_chill_touch;
af.level = level;
af.duration = 6;
af.location = APPLY_ST;
af.modifier = -1;
af.bitvector = 0;
affect_join( victim, &af );
}
// hunger! (warmth sucked out
if (!IS_NPC(victim))
gain_condition(victim,COND_HUNGER,dam/20);
// let's toast some gear
for (obj = victim->carrying; obj != NULL; obj = obj_next)
{
obj_next = obj->next_content;
cold_effect(obj,level,dam,TARGET_OBJ);
}
return;
}
if (target == TARGET_OBJ) // toast an object
{
OBJ_DATA *obj = (OBJ_DATA *) vo;
int chance;
char *msg;
if (IS_OBJ_STAT(obj,OBJEXTRA_BURN_PROOF)
|| IS_OBJ_STAT(obj,OBJEXTRA_NOPURGE)
|| number_range(0,4) == 0)
return;
chance = level / 4 + dam / 10;
if (chance > 25)
chance = (chance - 25) / 2 + 25;
if (chance > 50)
chance = (chance - 50) / 2 + 50;
if (IS_OBJ_STAT(obj,OBJEXTRA_BLESS))
chance -= 5;
chance -= obj->level * 2;
switch(obj->item_type)
{
default:
return;
case ITEM_POTION:
msg = "$p freezes and shatters!";
chance += 25;
break;
case ITEM_DRINK_CON:
msg = "$p freezes and shatters!";
chance += 5;
break;
}
chance = URANGE(5,chance,95);
if (number_percent() > chance)
return;
if (obj->carried_by != NULL)
act(msg,obj->carried_by,obj,NULL,TO_ALL);
else if (obj->in_room != NULL && obj->in_room->people != NULL)
act(msg,obj->in_room->people,obj,NULL,TO_ALL);
extract_obj(obj);
return;
}
}
/***************************************************************************/
void fire_effect(void *vo, int level, int dam, int target)
{
if (target == TARGET_ROOM) /* nail objects on the floor */
{
ROOM_INDEX_DATA *room = (ROOM_INDEX_DATA *) vo;
OBJ_DATA *obj, *obj_next;
for (obj = room->contents; obj != NULL; obj = obj_next)
{
obj_next = obj->next_content;
fire_effect(obj,level,dam,TARGET_OBJ);
}
return;
}
if (target == TARGET_CHAR) /* do the effect on a victim */
{
char_data *victim = (char_data *) vo;
OBJ_DATA *obj, *obj_next;
/* chance of blindness */
if (!IS_AFFECTED(victim,AFF_BLIND)
&& !saves_spell(level / 4 + dam / 20, victim,DAM_FIRE))
{
AFFECT_DATA af;
act("$n is blinded by smoke!",victim,NULL,NULL,TO_ROOM);
act("Your eyes tear up from smoke...you can't see a thing!",
victim,NULL,NULL,TO_CHAR);
af.where = WHERE_AFFECTS;
af.type = gsn_fire_breath;
af.level = level;
// af.duration = number_range(0,level/10);
af.duration = 1;
af.location = APPLY_HITROLL;
af.modifier = -4;
af.bitvector = AFF_BLIND;
affect_to_char(victim,&af);
}
/* getting thirsty */
if (!IS_NPC(victim))
gain_condition(victim,COND_THIRST,dam/20);
/* let's toast some gear! */
for (obj = victim->carrying; obj != NULL; obj = obj_next)
{
obj_next = obj->next_content;
fire_effect(obj,level,dam,TARGET_OBJ);
}
return;
}
if (target == TARGET_OBJ) /* toast an object */
{
OBJ_DATA *obj = (OBJ_DATA *) vo;
OBJ_DATA *t_obj,*n_obj;
int chance;
char *msg;
if (IS_OBJ_STAT(obj,OBJEXTRA_BURN_PROOF)
|| IS_OBJ_STAT(obj,OBJEXTRA_NOPURGE)
|| number_range(0,4) == 0)
return;
chance = level / 4 + dam / 10;
if (chance > 25)
chance = (chance - 25) / 2 + 25;
if (chance > 50)
chance = (chance - 50) / 2 + 50;
if (IS_OBJ_STAT(obj,OBJEXTRA_BLESS))
chance -= 5;
chance -= obj->level * 2;
switch ( obj->item_type )
{
default:
return;
case ITEM_CONTAINER:
msg = "$p ignites and burns!";
break;
case ITEM_POTION:
case ITEM_CAULDRON:
chance += 25;
msg = "$p bubbles and boils!";
break;
case ITEM_SCROLL:
chance += 50;
msg = "$p crackles and burns!";
break;
case ITEM_STAFF:
chance += 10;
msg = "$p smokes and chars!";
break;
case ITEM_WAND:
msg = "$p sparks and sputters!";
break;
case ITEM_FOOD:
msg = "$p blackens and crisps!";
break;
case ITEM_PILL:
msg = "$p melts and drips!";
break;
case ITEM_COMPONENT:
chance -= 10;
msg = "$p is consumed by fire.";
break;
}
chance = URANGE(5,chance,95);
if (number_percent() > chance)
return;
if (obj->carried_by != NULL)
act( msg, obj->carried_by, obj, NULL, TO_ALL );
else if (obj->in_room != NULL && obj->in_room->people != NULL)
act(msg,obj->in_room->people,obj,NULL,TO_ALL);
if (obj->contains)
{
/* dump the contents */
for (t_obj = obj->contains; t_obj != NULL; t_obj = n_obj)
{
n_obj = t_obj->next_content;
obj_from_obj(t_obj);
if (obj->in_room != NULL)
obj_to_room(t_obj,obj->in_room);
else if (obj->carried_by != NULL)
obj_to_room(t_obj,obj->carried_by->in_room);
else
{
extract_obj(t_obj);
continue;
}
fire_effect(t_obj,level/2,dam/2,TARGET_OBJ);
}
}
extract_obj( obj );
return;
}
}
/***************************************************************************/
void poison_effect(void *vo,int level, int dam, int target)
{
if (target == TARGET_ROOM) /* nail objects on the floor */
{
ROOM_INDEX_DATA *room = (ROOM_INDEX_DATA *) vo;
OBJ_DATA *obj, *obj_next;
for (obj = room->contents; obj != NULL; obj = obj_next)
{
obj_next = obj->next_content;
poison_effect(obj,level,dam,TARGET_OBJ);
}
return;
}
if (target == TARGET_CHAR) /* do the effect on a victim */
{
char_data *victim = (char_data *) vo;
OBJ_DATA *obj, *obj_next;
/* chance of poisoning */
if (!saves_spell(level / 4 + dam / 20,victim,DAM_POISON)
&& HAS_CLASSFLAG(victim, CLASSFLAG_POISON_IMMUNITY))
{
AFFECT_DATA af;
victim->println("You feel poison coursing through your veins.");
act("$n looks very ill.",victim,NULL,NULL,TO_ROOM);
af.where = WHERE_AFFECTS;
af.type = gsn_poison;
af.level = level;
af.duration = level / 2;
af.location = APPLY_ST;
af.modifier = -1;
af.bitvector = AFF_POISON;
affect_join( victim, &af );
}
/* equipment */
for (obj = victim->carrying; obj != NULL; obj = obj_next)
{
obj_next = obj->next_content;
poison_effect(obj,level,dam,TARGET_OBJ);
}
return;
}
if (target == TARGET_OBJ) /* do some poisoning */
{
OBJ_DATA *obj = (OBJ_DATA *) vo;
int chance;
if (IS_OBJ_STAT(obj,OBJEXTRA_BURN_PROOF)
|| IS_OBJ_STAT(obj,OBJEXTRA_BLESS)
|| number_range(0,4) == 0)
return;
chance = level / 4 + dam / 10;
if (chance > 25)
chance = (chance - 25) / 2 + 25;
if (chance > 50)
chance = (chance - 50) / 2 + 50;
chance -= obj->level * 2;
switch (obj->item_type)
{
default:
return;
case ITEM_FOOD:
break;
case ITEM_DRINK_CON:
if (obj->value[0] == obj->value[1])
return;
break;
}
chance = URANGE(5,chance,95);
if (number_percent() > chance)
return;
obj->value[3] = 1;
return;
}
}
/***************************************************************************/
void shock_effect(void *vo,int level, int dam, int target)
{
if (target == TARGET_ROOM)
{
ROOM_INDEX_DATA *room = (ROOM_INDEX_DATA *) vo;
OBJ_DATA *obj, *obj_next;
for (obj = room->contents; obj != NULL; obj = obj_next)
{
obj_next = obj->next_content;
shock_effect(obj,level,dam,TARGET_OBJ);
}
return;
}
if (target == TARGET_CHAR)
{
char_data *victim = (char_data *) vo;
OBJ_DATA *obj, *obj_next;
// daze and confused?
if (!saves_spell(level/4 + dam/20,victim,DAM_LIGHTNING))
{
victim->println("Your muscles stop responding.");
DAZE_STATE(victim,UMAX(12,level/4 + dam/20));
}
// toast some gear
for (obj = victim->carrying; obj != NULL; obj = obj_next)
{
obj_next = obj->next_content;
shock_effect(obj,level,dam,TARGET_OBJ);
}
return;
}
if (target == TARGET_OBJ)
{
OBJ_DATA *obj = (OBJ_DATA *) vo;
int chance;
char *msg;
if (IS_OBJ_STAT(obj,OBJEXTRA_BURN_PROOF)
|| IS_OBJ_STAT(obj,OBJEXTRA_NOPURGE)
|| number_range(0,4) == 0)
return;
chance = level / 4 + dam / 10;
if (chance > 25)
chance = (chance - 25) / 2 + 25;
if (chance > 50)
chance = (chance - 50) /2 + 50;
if (IS_OBJ_STAT(obj,OBJEXTRA_BLESS))
chance -= 5;
chance -= obj->level * 2;
switch(obj->item_type)
{
default:
return;
case ITEM_WAND:
case ITEM_STAFF:
chance += 10;
msg = "$p overloads and explodes!";
break;
case ITEM_JEWELRY:
chance -= 10;
msg = "$p is fused into a worthless lump.";
}
chance = URANGE(5,chance,95);
if (number_percent() > chance)
return;
if (obj->carried_by != NULL)
act(msg,obj->carried_by,obj,NULL,TO_ALL);
else if (obj->in_room != NULL && obj->in_room->people != NULL)
act(msg,obj->in_room->people,obj,NULL,TO_ALL);
extract_obj(obj);
return;
}
}
/***************************************************************************/
/***************************************************************************/
/***************************************************************************/
/***************************************************************************/
/***************************************************************************/