/*$Id: gr_magic.c,v 1.14 2005/03/17 02:41:09 tyrion Exp $*/
/*****************************************************************************
* Interface for group spell casting. *
* -- Altrag *
*****************************************************************************/
#if defined( macintosh )
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "merc.h"
/*
* Local functions
*/
void add_gspell args ( ( CHAR_DATA *ch, int sn, int level, void *vo ) );
int gslot_lookup args ( ( int sn ) );
bool is_same_gspell args ( ( CHAR_DATA *ch, CHAR_DATA *victim ) );
/*
* NonLocal functions manually declared
*/
void say_spell(CHAR_DATA * ch, int sn);
int slot_lookup(int slot);
bool is_same_gspell( CHAR_DATA *ch, CHAR_DATA *victim )
{
if ( !is_same_group( ch, victim ) )
return FALSE;
if ( !ch->gspell || !victim->gspell )
return FALSE;
if ( ch->gspell->sn != victim->gspell->sn )
return FALSE;
if ( ch->gspell->victim != victim->gspell->victim )
return FALSE;
return TRUE;
}
void set_gspell( CHAR_DATA *ch, GSPELL_DATA *gsp )
{
GSPELL_DATA *gsp_new;
gsp_new = alloc_mem( sizeof(*gsp_new) );
*gsp_new = *gsp;
ch->gspell = gsp_new;
}
void end_gspell( CHAR_DATA *ch )
{
if ( !ch->gspell )
{
bug( "end_gspell: no gspell", 0 );
return;
}
free_mem( ch->gspell, sizeof(*ch->gspell) );
ch->gspell = NULL;
}
/*
* Implementation stuff
*/
void check_gcast( CHAR_DATA *ch )
{
CHAR_DATA *gch;
int looper;
int casters[MAX_CLASS];
int sn;
int level = 0;
int total = 0;
if ( !ch->gspell || ch->gspell->timer <= 0 ) {
return;
}
sn = ch->gspell->sn;
for ( looper = 0; looper < MAX_CLASS; looper++ )
casters[looper] = 0;
for ( gch = ch->in_room->people; gch; gch = gch->next_in_room )
{
if ( is_same_gspell( ch, gch ) )
{
if (gch->class != gch->multied)
casters[gch->multied]++;
casters[gch->class]++;
total++;
level = (level / 2) + gch->gspell->level / 2;
}
}
for ( looper = 0; looper < MAX_CLASS; looper++ )
if ( casters[looper] < gskill_table[gslot_lookup(sn)].casters[looper] )
{
send_to_char(AT_BLUE, "You begin casting a group spell.\n\r", ch );
say_spell(ch, slot_lookup(sn));
return;
}
sn = slot_lookup(sn);
say_spell(ch, slot_lookup(sn));
act(AT_BLUE, "$n unleashes a group spell!", ch, NULL, NULL, TO_ROOM );
(* skill_table[sn].spell_fun) ( sn, level, ch->leader ? ch->leader : ch,
ch->gspell->victim );
for ( gch = ch->in_room->people; gch; gch = gch->next_in_room )
{
if ( is_same_gspell( ch, gch ) && ch != gch )
end_gspell( gch );
}
switch (skill_table[sn].target)
{
case TAR_GROUP_OFFENSIVE:
{
CHAR_DATA *victim = (CHAR_DATA *)ch->gspell->victim;
rprog_cast_sn_trigger( ch->in_room, ch, sn, victim );
if ( !victim->fighting )
{
multi_hit( victim, ch->leader ? ch->leader : ch, TYPE_UNDEFINED );
}
/* Ok, now we're starting a fight, do a multi_hit on either the
caster, or the caster's leader, and make all non-fighting
group members do a multi_hit() on the victim
--Manaux
*/
for (gch = ch->in_room->people; gch; gch = gch->next_in_room)
{
if (gch->deleted)
continue;
if ((gch->leader == (ch->leader ? ch->leader : ch ))
&& (!gch->fighting))
{
multi_hit(gch, victim, TYPE_UNDEFINED);
}
}
}
break;
case TAR_GROUP_DEFENSIVE:
rprog_cast_sn_trigger( ch->in_room, ch, sn, ch->gspell->victim );
break;
case TAR_GROUP_ALL:
rprog_cast_sn_trigger( ch->in_room, ch, sn, ch );
break;
case TAR_GROUP_IGNORE:
rprog_cast_sn_trigger( ch->in_room, ch, sn,
(ch->gspell->victim ? ch->gspell->victim : ch) );
break;
case TAR_GROUP_OBJ:
if ( ch->gspell->victim )
{
oprog_cast_sn_trigger( ch->gspell->victim, ch, sn, ch->gspell->victim );
rprog_cast_sn_trigger( ch->in_room, ch, sn, ch->gspell->victim );
}
break;
}
end_gspell( ch );
return;
}
int gslot_lookup( int sn )
{
int count;
for ( count = 0; count < MAX_GSPELL; count++ )
if ( gskill_table[count].slot == sn )
return count;
bug( "Gslot_lookup: sn not found #%d", sn );
return 0;
}
void add_gspell( CHAR_DATA *ch, int sn, int level, void *vo )
{
CHAR_DATA *gch;
GSPELL_DATA gsp;
for ( gch = ch->in_room->people; gch; gch = gch->next_in_room )
{
if ( gch == ch || !gch->gspell || gch->gspell->timer <= 0 )
continue;
if ( is_same_group( ch, gch ) && gch->gspell->sn == sn )
{
switch ( skill_table[sn].target )
{
case TAR_GROUP_DEFENSIVE:
case TAR_GROUP_OFFENSIVE:
case TAR_GROUP_OBJ:
case TAR_GROUP_IGNORE:
if ( gch->gspell->victim != vo )
continue;
break;
}
break;
}
}
sn = skill_table[sn].slot;
gsp.sn = sn;
gsp.victim = vo;
gsp.level = level;
if ( !gch || !gch->gspell )
gsp.timer = gskill_table[gslot_lookup(sn)].wait;
else
gsp.timer = gch->gspell->timer;
set_gspell( ch, &gsp );
check_gcast( ch );
return;
}
void group_cast( int sn, int level, CHAR_DATA *ch, char *argument )
{
CHAR_DATA *victim = NULL;
CHAR_DATA * rch = NULL;
OBJ_DATA *obj = NULL;
int found = FALSE;
int mana;
if ( IS_NPC(ch) )
return;
if ( ch->gspell && ch->gspell->timer > 0 )
{
send_to_char(AT_BLUE,"You already have a group spell in progress.\n\r",ch);
return;
}
/* check to make sure that ch is part of a group.
--Manaux
*/
if ( (!ch->leader) || (ch->leader == ch) )
for (rch = ch->in_room->people; rch; rch = rch->next_in_room )
{
if (rch->deleted || rch == ch)
continue;
if (rch->leader == ch )
{
found = TRUE;
break;
}
}
else
found = TRUE;
if (!found)
{
send_to_char(AT_BLUE, "You don't have a group here!\n\r", ch);
return;
}
mana = MANA_COST(ch,sn);
if (( ch->class == 9 )||( ch->class == 11))
mana /= 4;
if ((( ch->class == 9 )||( ch->class == 11 )) && ( ch->bp < mana))
{
send_to_char(AT_RED, "You don't have enough blood to cast that!\n\r", ch);
return;
}
else if ((ch->class != 9 && ch->class != 11) && (ch->mana < mana))
{
send_to_char(AT_BLUE, "You don't have enough mana!\n\r", ch);
return;
}
WAIT_STATE(ch, skill_table[sn].beats);
if ( number_percent( ) > ( ch->pcdata->learned[sn] / 10 ) )
{
send_to_char(AT_BLUE, "You lost your concentration.\n\r",ch);
if (( ch->class != 9 )&&(ch->class != 11))
ch->mana -= mana / 2;
else
ch->bp -= mana / 2;
if( ch->pcdata->learned[sn] <= 750 )
update_skpell( ch, sn, 0 );
return;
}
if (( ch->class != 9 )&&( ch->class != 11))
ch->mana -= mana;
else
ch->bp -= mana;
switch ( skill_table[sn].target )
{
default:
bug( "group_cast: non-group target on sn #%d.", sn );
return;
case TAR_GROUP_DEFENSIVE:
if ( argument[0] == '\0' )
{
victim = ch;
break;
}
if ( !( victim = get_char_room( ch, argument ) ) )
{
send_to_char( AT_BLUE, "They aren't here.\n\r", ch );
return;
}
add_gspell( ch, sn, level, (void *) victim );
break;
case TAR_GROUP_OFFENSIVE:
if ( argument[0] == '\0' )
{
if ( ch->fighting )
victim = ch->fighting;
else
{
send_to_char(AT_BLUE, "Cast the spell on whom?\n\r",ch);
return;
}
}
else
{
if ( !( victim = get_char_room( ch, argument ) ) )
{
send_to_char( AT_BLUE, "They aren't here.\n\r", ch );
return;
}
}
if ( IS_SET( ch->in_room->room_flags, ROOM_NO_OFFENSIVE ) )
{
send_to_char( AT_BLUE, "You failed.\n\r", ch );
return;
}
if(!is_pkillable(ch, victim ) ) {
send_to_char(AT_WHITE, "You failed.\n\r", ch);
return;
}
if (!IS_NPC(victim))
ch->pkill_timer = 0;
if ( IS_AFFECTED(victim, AFF_PEACE) )
{
send_to_char(AT_WHITE, "A wave of peace overcomes you.\n\r", ch);
return;
}
if ( IS_AFFECTED2( ch, AFF_SHADOW_PLANE) )
{
send_to_char(AT_WHITE, "You must exit the shadow realm.\n\r", ch);
return;
}
if ( IS_AFFECTED4( ch, AFF_BURROW) )
{
send_to_char(AT_WHITE, "You must wait until the earth heals you!\n\r", ch);
return;
}
if ( IS_AFFECTED2( victim, AFF_SHADOW_PLANE) )
{
send_to_char(AT_WHITE, "You can not attack someone who is in the shadow realm.\n\r", ch);
return;
}
if ( IS_AFFECTED4( victim, AFF_BURROW) )
{
send_to_char(AT_WHITE, "You can not attack someone who is burrowed.\n\r", ch);
return;
}
if ( IS_AFFECTED( ch, AFF_PEACE) )
{
affect_strip( ch, skill_lookup("aura of peace") );
REMOVE_BIT( ch->affected_by, AFF_PEACE );
}
if (is_safe(ch, victim ) )
{
send_to_char( AT_BLUE, "You failed.\n\r",ch);
return;
}
add_gspell( ch, sn, level, (void *) victim );
break;
case TAR_GROUP_ALL:
add_gspell( ch, sn, level, NULL );
break;
case TAR_GROUP_OBJ:
if ( !( obj = get_obj_list( ch, argument, ch->in_room->contents ) ) )
{
send_to_char( AT_WHITE, "You don't see that.\n\r", ch );
return;
}
add_gspell( ch, sn, level, (void *) obj );
break;
case TAR_GROUP_IGNORE:
add_gspell( ch, sn, level, (void *) argument );
break;
}
return;
}
int gspell_flamesphere( int sn, int level, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *victim = (CHAR_DATA *) vo;
int dam;
dam = dice(1000, level/5);
if ( saves_spell( level, victim ) )
dam /= 2;
damage( ch, victim, dam, sn );
return SKPELL_NO_DAMAGE;
}
/* Smite evil is very similar to wrath of god, except that the target
* must be evil, and, the damage isn't doubled against pc's
* (otherwise, pk would be very very short) As is, against a evil
* with an average amount of shields, it'll do around 4k damage with champs
* -- Manaux (note, smite good is almost identical)
*/
int gspell_smite_evil( int sn, int level, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *victim = (CHAR_DATA *) vo;
int dam;
if (!IS_EVIL( victim ) || IS_EVIL(ch) )
{
act(AT_WHITE, "$n's burst of holy energy backfires.", ch, 0, 0, TO_ROOM);
act(AT_WHITE, "Your burst of holy energy backfires.", ch, 0, 0, TO_CHAR);
damage(ch, ch, number_range(500, 2000), sn);
update_pos(ch);
return SKPELL_NO_DAMAGE;
}
dam = number_fuzzy( number_fuzzy(level) * number_fuzzy(level ) );
damage( ch, victim, dam, sn );
return SKPELL_NO_DAMAGE;
}
int gspell_smite_good( int sn, int level, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *victim = (CHAR_DATA *) vo;
int dam;
if ((!IS_GOOD( victim )) || (IS_GOOD(ch)) )
{
act(AT_RED, "$n's burst of unholy energy backfires.", ch, 0, 0, TO_ROOM);
act(AT_RED, "Your burst of unholy energy backfires.", ch, 0, 0, TO_CHAR);
damage(ch, ch, number_range(500, 2000), sn);
update_pos(ch);
return SKPELL_NO_DAMAGE;
}
dam = number_fuzzy( number_fuzzy(level) * number_fuzzy(level ) );
damage( ch, victim, dam, sn );
return SKPELL_NO_DAMAGE;
}
int gspell_deadly_poison( int sn, int level, CHAR_DATA * ch, void * vo)
{
CHAR_DATA * victim = (CHAR_DATA * ) vo;
AFFECT_DATA af;
int dur = level / 20 + 1;
af.type = sn;
af.level = level;
af.duration = dur;
af.location = APPLY_NONE;
af.modifier = 0;
af.bitvector = AFF_DEADLY_POISON;
affect_to_char3(victim, &af);
act(AT_GREEN, "$n's body is ravaged by a deadly poison!", ch, NULL, victim, TO_ROOM);
return SKPELL_NO_DAMAGE;
}
int gspell_volcanic_blast(int sn, int level, CHAR_DATA * ch, void * vo)
{
CHAR_DATA *victim;
int count;
int chance;
int chance2;
for (victim = ch->in_room->people; victim; victim=victim->next_in_room )
{
if (victim->deleted || is_same_group(victim, ch ))
continue;
count = 1;
chance = 300;
if (!IS_NPC(victim ) )
{
while ((chance > 20) && (victim->hit > 0))
{
chance2 = number_range(15,25);
act(AT_RED, "The Volcanic Ash ERRUPTS under $n.\n\r" , victim, NULL,
NULL,TO_ROOM);
damage(ch, victim, ch->level*chance2 * 2, sn);
count = count + 1;
chance -= number_range(1,200);
}
}
else if (IS_NPC(victim ) )
{
while ((chance > 15) && (victim->hit > 0))
{
chance2 = number_range(15,25);
act(AT_RED, "The Volcanic Ash ERRUPTS under&w $n.\n\r",victim, NULL, NULL,TO_ROOM);
damage(ch, victim, ch->level*chance2* 3, sn);
count = count + 1;
chance -= number_range(1,200);
}
}
}
return SKPELL_NO_DAMAGE;
}
int gspell_mass_shield( int sn, int level, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *vch;
for ( vch = ch->in_room->people; vch; vch = vch->next_in_room )
{
if ( !is_same_group( ch, vch ) )
continue;
switch ( number_range( 1, 10 ) )
{
case 1:
spell_fireshield( skill_lookup( "fireshield" ), level, vch, vch );
break;
case 2:
spell_shockshield( skill_lookup( "shockshield" ), level, vch, vch );
break;
case 3:
spell_iceshield( skill_lookup( "iceshield" ), level, vch, vch );
break;
case 4:
spell_chaosfield( skill_lookup( "chaos field" ), level, vch, vch );
break;
case 5:
spell_inertial( skill_lookup( "vibrate" ), level, vch, vch );
break;
case 6:
spell_sanctuary( skill_lookup( "sanctuary" ), level, vch, vch );
break;
case 7:
spell_golden_armor( skill_lookup( "golden armor" ), level, vch, vch );
break;
case 8:
spell_ghost_shield( skill_lookup( "ghost shield" ), level, vch, vch );
break;
case 9:
spell_mist( skill_lookup( "mist" ), level, vch, vch );
break;
case 10:
spell_shadow_image( skill_lookup( "shadow image" ), level, vch, vch );
break;
}
}
return SKPELL_NO_DAMAGE;
}
int gspell_timequake( int sn, int level, CHAR_DATA *ch, void *vo )
{
do_timequake( (CHAR_DATA *) vo, "" );
return SKPELL_NO_DAMAGE;
}
int gspell_restoration( int sn, int level, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *rch;
for (rch = ch->in_room->people; rch != NULL; rch = rch->next_in_room)
{
if (rch->deleted)
continue;
affect_strip(rch,skill_lookup("poison"));
affect_strip(rch,skill_lookup("blindness"));
affect_strip(rch,skill_lookup("curse"));
affect_strip(rch,skill_lookup("sleep"));
rch->hit = rch->max_hit;
rch->move = rch->max_move;
rch->poison_level = 0;
update_pos( rch);
act(AT_BLUE,"$n has restored you.",ch,NULL, rch,TO_VICT);
}
send_to_char(AT_BLUE,"You have restored yourself..\n\r",ch);
return SKPELL_NO_DAMAGE;
}