dawn/notes/
dawn/src/
dawn/src/docs/
/**************************************************************************/
// 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;
    }
}

/***************************************************************************/
/***************************************************************************/
/***************************************************************************/
/***************************************************************************/
/***************************************************************************/