/***************************************************************************
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. *
* *
* Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* *
* In order to use any part of this Envy Diku Mud, you must comply with *
* the original Diku license in 'license.doc', the Merc license in *
* 'license.txt', as well as the Envy license in 'license.nvy'. *
* In particular, you may not remove either of these copyright notices. *
* *
* Much time and thought has gone into this software and you are *
* benefitting. We hope that you share your changes too. What goes *
* around, comes around. *
* *
* ROM 2.4 is copyright 1993-1998 Russ Taylor *
* ROM has been brought to you by the ROM consortium *
* Russ Taylor (rtaylor@hypercube.org) *
* Gabrielle Taylor (gtaylor@hypercube.org) *
* Brian Moore (zump@rom.org) *
* By using this code, you have agreed to follow the terms of the *
* ROM license, in the file Rom24/doc/rom.license *
* *
* Code Adapted and Improved by Abandoned Realms Mud *
* and Aabahran: The Forsaken Lands Mud by Virigoth *
* *
* Continued Production of this code is available at www.flcodebase.com *
***************************************************************************/
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "merc.h"
#include "magic.h"
#include "recycle.h"
#include "interp.h"
extern void cast_new( CHAR_DATA *ch, char *argument, bool iscommune, bool fIgnorePos );
void do_tune( CHAR_DATA *ch, char *argument)
{
char arg[MIL], arg2[MIL];
OBJ_DATA *obj;
AFFECT_DATA *paf;
int chance, loc_var, result, fail = 25, bonus = 0, stat_chg, added, level = ch->level, sn = skill_lookup("tune");
bool stop = FALSE, old_found = FALSE;
argument = one_argument( argument, arg);
argument = one_argument( argument, arg2);
if (ch->fighting != NULL)
{
send_to_char("Shouldn't you be concentrating on the battle instead?\n\r",ch);
return;
}
if (arg[0] == '\0' || (chance = get_skill(ch,sn)) == 0)
{
send_to_char("You put your lips together and whistle a tune.\n\r",ch);
return;
}
if (ch->mana < 100)
{
send_to_char("You feel too weak to forge right now.\n\r", ch);
return;
}
if ((obj = get_obj_list(ch,arg,ch->carrying)) == NULL || CAN_WEAR(obj,ITEM_WEAR_TATTOO))
{
send_to_char("You don't have that instrument.\n\r",ch);
return;
}
if (obj->wear_loc != -1)
{
send_to_char("You must carry the instrument you wish to tune.\n\r",ch);
return;
}
if (obj->item_type != ITEM_INSTRUMENT)
{
send_to_char("How do you expect to tune that?\n\r",ch);
return;
}
WAIT_STATE(ch, skill_table[skill_lookup("tune")].beats);
if (!IS_IMMORTAL(ch))
ch->mana -= 100;
if (IS_IMMORTAL(ch) && arg2[0] != '\0')
{
if (!is_number(arg2))
{
send_to_char("Must be numeric.\n\r",ch);
return;
}
stat_chg = atoi(arg2);
if( (stat_chg <= 0) || (stat_chg > 5) )
{
send_to_char("Must be numeric 1 to 5.\n\r",ch);
return;
}
}
else //checks what stat it'll change
stat_chg = number_range(1, 5);
if (!obj->enchanted)
{
for ( paf = obj->pIndexData->affected; paf != NULL; paf = paf->next )
{
if ( paf->location == APPLY_HIT || paf->location == APPLY_MANA )
{
bonus = paf->modifier;
if (bonus > 24)
stop = TRUE;
fail += 2 * (bonus * bonus)/25;
}
else if (paf->location == APPLY_DAMROLL || paf->location == APPLY_HITROLL)
{
bonus = paf->modifier;
if (bonus > 4)
stop = TRUE;
fail += 3 * (bonus * bonus);
}
else if (paf->location > APPLY_STR && paf->location < APPLY_CON )
{
bonus = paf->modifier;
if (bonus > 4)
stop = TRUE;
fail += 2 * (bonus * bonus);
}
else
fail += 25;
}
}
for ( paf = obj->affected; paf != NULL; paf = paf->next )
{
if ( paf->location == APPLY_HIT || paf->location == APPLY_MANA )
{
bonus = paf->modifier;
if (bonus > 24)
stop = TRUE;
fail += 2 * (bonus * bonus)/25;
}
else if (paf->location == APPLY_DAMROLL || paf->location == APPLY_HITROLL)
{
bonus = paf->modifier;
if (bonus > 4)
stop = TRUE;
fail += 3 * (bonus * bonus);
}
else if (paf->location > APPLY_STR && paf->location < APPLY_CON )
{
bonus = paf->modifier;
if (bonus > 4)
stop = TRUE;
fail += 2 * (bonus * bonus);
}
else
fail += 25;
}
fail -= level;
if (obj->level > level)
fail += obj->level - level;
if (IS_OBJ_STAT(obj,ITEM_BLESS))
fail -= 15;
if (IS_OBJ_STAT(obj,ITEM_GLOW))
fail += 5;
fail = URANGE(5,fail,95);
result = number_percent();
if (result < (fail / 5) || chance < fail/4)
{
act("$p is ruined from excessive tuning!",ch,obj,NULL,TO_ALL);
extract_obj(obj);
return;
}
if (result < (fail / 2) || chance < fail/2 || stop)
{
AFFECT_DATA *paf_next;
act("$p goes completely out of tune.",ch,obj,NULL,TO_ALL);
obj->enchanted = TRUE;
for (paf = obj->affected; paf != NULL; paf = paf_next)
{
paf_next = paf->next;
free_affect(paf);
}
obj->affected = NULL;
REMOVE_BIT(obj->extra_flags,ITEM_GLOW);
REMOVE_BIT(obj->extra_flags,ITEM_HUM);
REMOVE_BIT(obj->extra_flags,ITEM_MAGIC);
return;
}
if ( result <= fail || result > chance)
{
send_to_char("Nothing seemed to happen.\n\r",ch);
return;
}
act("$p is more finely tuned.",ch,obj,NULL,TO_ALL);
SET_BIT(obj->extra_flags, ITEM_MAGIC);
if (obj->level < LEVEL_HERO - 1)
obj->level = UMIN(LEVEL_HERO - 1,obj->level + 1);
if (!obj->enchanted)
{
AFFECT_DATA *af_new;
obj->enchanted = TRUE;
for (paf = obj->pIndexData->affected; paf != NULL; paf = paf->next)
{
af_new = new_affect();
af_new->next = obj->affected;
obj->affected = af_new;
af_new->where = paf->where;
af_new->type = UMAX(0,paf->type);
af_new->level = paf->level;
af_new->duration = paf->duration;
af_new->location = paf->location;
af_new->modifier = paf->modifier;
af_new->bitvector = paf->bitvector;
}
}
//*************Blah blah blah
switch (stat_chg)
{
// 1 = int, 2 = wis, 3 = dex, 4 = hp, 5 = mana
case (1):
loc_var = APPLY_INT;
added = 1;
break;
case (2):
loc_var = APPLY_WIS;
added = 1;
break;
case (3):
loc_var = APPLY_DEX;
added = 1;
break;
case (4):
loc_var = APPLY_HIT;
added = 5;
break;
case (5):
loc_var = APPLY_MANA;
added = 5;
break;
default:
send_to_char("Something is admist, notify an IMP!\n\r",ch);
return;
}
for ( paf = obj->affected; paf != NULL; paf = paf->next )
{
if (paf->location == loc_var)
{
paf->modifier += added;
old_found = TRUE;
}
}
//adding stats in
if (!old_found)
{
paf = new_affect();
paf->where = TO_OBJECT;
paf->type = sn;
paf->level = level;
paf->duration = -1;
paf->location = loc_var;
paf->modifier = added;
paf->bitvector = 0;
paf->next = obj->affected;
obj->affected = paf;
}
check_improve(ch,sn,TRUE,1);
}
void do_ventriloquate( CHAR_DATA *ch, char *argument)
{
char buf1[MSL], buf2[MSL], speaker[MIL];
CHAR_DATA *vch;
int chance, sn = skill_lookup("ventriloquate");
argument = one_argument( argument, speaker );
if ( (chance = get_skill(ch,sn)) == 0)
{
send_to_char("You don't know how to throw your voice.\n\r",ch);
return;
}
if (ch->mana < 5)
{
send_to_char("Rest up first.\n\r", ch);
return;
}
if (speaker[0] == '\0' || argument[0] == '\0')
{
send_to_char("Syntax: ventriloquate <speaker> <message>\n\r",ch);
return;
}
ch->mana -= 5;
WAIT_STATE(ch, skill_table[sn].beats);
if (chance < number_percent())
{
send_to_char("You failed.\n\r",ch);
return;
}
sprintf( buf1, "%s says '`#%s``'\n\r", speaker, argument );
sprintf( buf2, "Someone makes %s say '`#%s``'\n\r", speaker, argument );
buf1[0] = UPPER(buf1[0]);
for ( vch = ch->in_room->people; vch != NULL; vch = vch->next_in_room )
if (!is_exact_name( speaker, vch->name) && IS_AWAKE(vch))
send_to_char( saves_spell(ch->level,vch,DAM_OTHER,skill_table[sn].spell_type) ? buf2 : buf1, vch );
}
void do_feed( CHAR_DATA *ch, char *argument)
{
CHAR_DATA *victim;
char arg[MIL], arg2[MIL];
OBJ_DATA *obj;
argument = one_argument( argument, arg);
argument = one_argument( argument, arg2);
if (arg[0] == '\0' || arg2[0] == '\0')
{
send_to_char("Feed who what?\n\r",ch);
return;
}
if ((victim = get_char_room(ch, NULL, arg)) == NULL)
{
send_to_char("They aren't here.\n\r",ch);
return;
}
if ((obj = get_obj_list(ch,arg2,ch->carrying)) == NULL )
{
send_to_char("You don't have that item.\n\r",ch);
return;
}
if (obj->wear_loc != -1)
{
send_to_char("You must carry the item you wish to feed.\n\r",ch);
return;
}
if (obj->item_type != ITEM_FOOD
&& obj->item_type != ITEM_DRINK_CON)
{
send_to_char("You can't feed someone with that.\n\r",ch);
return;
}
if (victim->master != ch && !is_same_group(ch,victim))
{
act("$N isn't following you.",ch,NULL,victim,TO_CHAR);
return;
}
if (!IS_AWAKE(victim))
{
act("$N is sleeping at the moment.",ch,NULL,victim,TO_CHAR);
return;
}
if (!can_see_obj( victim, obj ) )
{
sendf(ch, "%s can't see it.\n\r", PERS(victim,ch));
return;
}
if ( obj->value[3] != 0 ){
send_to_char("You cannot feed someone with dangerous goods.\n\r", ch);
return;
}
/* if (!IS_NPC(victim))
{
act("$N can do that $Mself.",ch,NULL,victim,TO_CHAR);
return;
} */
if (IS_NPC(victim) && victim->pIndexData->vnum == MOB_VNUM_PHOENIX && victim->practice > 20)
{
act("$N isn't hungry right now.",ch,NULL,victim,TO_CHAR);
return;
}
WAIT_STATE2(ch, PULSE_VIOLENCE / 2);
act("$n feeds $N with $p.",ch,obj,victim,TO_NOTVICT);
act("$n feeds you with $p.",ch,obj,victim,TO_VICT);
act("You feed $N with $p.",ch,obj,victim,TO_CHAR);
/* obj_to_ch unlike obj_to_char omits can_Carry checks so the item will always get moved */
obj_from_char(obj);
obj_to_ch(obj,victim);
if (obj->item_type == ITEM_DRINK_CON){
do_drink(victim, obj->name);
/* obj_to_ch omits can_Carry checks so the item will always get moved */
obj_from_char(obj);
obj_to_ch(obj,ch);
}
else if (obj->item_type == ITEM_FOOD)
do_eat(victim,obj->name);
}
void do_ignite( CHAR_DATA *ch, char *argument)
{
CHAR_DATA *victim;
int value;
if (!IS_NPC(ch))
{
send_to_char("Huh?\n\r",ch);
return;
}
if (ch->pIndexData->vnum != MOB_VNUM_PHOENIX)
return;
if ((victim = ch->master) == NULL)
return;
if (victim->level < ch->level/2)
{
act("$n tries to ignite but is too inexperienced.",ch,NULL,NULL,TO_ROOM);
return;
}
if (ch->hit < ch->max_hit/2)
{
act("$n tries to ignite but looks too hurt.",ch,NULL,NULL,TO_ROOM);
return;
}
act("$n ignites into a ball of energy, and combines with $N!",ch,NULL,victim,TO_NOTVICT);
act("$n ignites into a ball of energy, and combines with you!",ch,NULL,victim,TO_VICT);
value = ((ch->level*ch->hit)/4)/victim->level;
victim->hit = UMIN(victim->max_hit, victim->hit + value);
update_pos(victim);
char_from_room(ch);
extract_char(ch,TRUE);
}
/* checks for dreamprobe link between the two chars */
bool dprobe_check(CHAR_DATA* ch, CHAR_DATA* victim){
AFFECT_DATA* dprobe;
if (ch == NULL || victim == NULL)
return FALSE;
else if (ch->in_room == victim->in_room)
return FALSE;
else if ( (dprobe = affect_find(ch->affected, gsn_dreamprobe)) != NULL
&& dprobe->has_string
&& !IS_NULLSTR(dprobe->string)
&& !str_cmp(dprobe->string, victim->name))
return TRUE;
else
return FALSE;
}
/* psi dreamwalk ability */
void do_dreamwalk( CHAR_DATA *ch, char *argument){
CHAR_DATA* victim;
AFFECT_DATA* paf;
char buf[MIL];
const int cost = 15;
int sn = 0;
const int gsn_omen = skill_lookup("omen");
const int gsn_deathmare = skill_lookup("deathmare");
const int gsn_dreamprobe = skill_lookup("dreamprobe");
const int gsn_mindmelt = skill_lookup("mindmelt");
if (get_skill(ch, gsn_telepathy) < 2){
send_to_char("Huh?\n\r", ch);
return;
}
else if (IS_NULLSTR(argument)){
send_to_char("Dreamwalk in who's dream?\n\r", ch);
return;
}
/* VICTIM CHOICE */
if ( (victim = get_char_world(ch, argument)) != NULL
&& (ch->in_room == victim->in_room || dprobe_check(ch, victim))
){
AFFECT_DATA af;
if (is_safe(ch, victim)){
return;
}
else if (IS_AWAKE(victim)){
send_to_char("They must be sleeping first.\n\r", ch);
return;
}
else if (IS_NPC(victim) || IS_UNDEAD(victim) || IS_DEMON(victim)){
send_to_char("Try as you might you do not sense any dreams.\n\r", ch);
return;
}
else if (ch->in_room != victim->in_room
&& victim
&& !dprobe_check(ch, victim)){
send_to_char("They aren't here.\n\r", ch );
return;
}
affect_strip(ch, gen_dreamwalk );
act("You tune your senses into $N's dream.", ch, NULL, victim, TO_CHAR);
act("$n touches $N's head and chants softly.", ch, NULL, victim, TO_ROOM);
af.type = gen_dreamwalk;
af.level = 0; //used to keep track of the messages
af.duration = 4;
af.where = TO_AFFECTS;
af.bitvector = 0;
af.location = APPLY_NONE;
af.modifier = 0;//sn of the spell to cast
paf = affect_to_char(ch, &af);
string_to_affect(paf, victim->name );
return;
}
/* SPELL CHOICE */
sn = find_spell(ch,argument);
if (sn != gsn_omen
&& sn != gsn_deathmare
&& sn != gsn_mindmelt
&& sn != gsn_dreamprobe){
send_to_char("You can only dreamwalk omen, deathmare, dreamprobe and mind melt.\n\r", ch);
return;
}
else if ( (paf = affect_find(ch->affected, gen_dreamwalk)) == NULL
|| !paf->has_string
|| IS_NULLSTR(paf->string)){
send_to_char("You must enter someone's dream first.\n\r", ch);
return;
}
else if (ch->mana < cost){
send_to_char("You cannot concentrate sufficiently.\n\r", ch);
return;
}
WAIT_STATE(ch, 2 * PULSE_VIOLENCE );
if (number_percent() < get_skill(ch, gsn_telepathy)){
ch->mana -= cost;
check_improve(ch, gsn_telepathy, TRUE, 1);
}
else{
ch->mana -= cost / 2;
check_improve(ch, gsn_telepathy, FALSE, 1);
send_to_char("You failed to enter their dreams.\n\r", ch );
return;
}
/* compose the spell target */
sprintf(buf, "\'%s\' %s", skill_table[sn].name, paf->string);
/* cast the spell */
a_yell( ch, victim );
cast_new( ch, buf, FALSE, TRUE );
}
/* wrap around telepathic tell */
void do_telepathy( CHAR_DATA* ch, char* argument ){
bool fTele = IS_TELEPATH( ch );
if (get_skill( ch, gsn_telepathy) < 2){
send_to_char("Huh?\n\r", ch );
return;
}
if (!fTele)
SET_BIT(ch->comm, COMM_TELEPATH);
do_tell( ch, argument );
if (!fTele)
REMOVE_BIT(ch->comm, COMM_TELEPATH);
}
//shows tracks in the room
void show_tracks( CHAR_DATA* ch, ROOM_INDEX_DATA* in_room ){
AFFECT_DATA* paf;
char* age_str;
long age;
if (ch->class != gcn_ranger)
return;
else if ( (paf = affect_find(ch->affected, gsn_tracker)) == NULL)
return;
else if (paf->modifier < 1 || paf->modifier >= MAX_ANATOMY)
return;
age = (mud_data.current_time - in_room->tracks[paf->modifier]) / 30;
switch( age ){
case -1:
case 0: age_str = "new"; break;
case 1: age_str = "fresh"; break;
case 2: age_str = "recent"; break;
case 3:
case 4: age_str = "old"; break;
default:
return;
}
sendf(ch, "You spot some %s spur.\n\r", age_str );
}
/* new ranger skills */
void do_track( CHAR_DATA* ch, char* argument ){
AFFECT_DATA af, *paf;
const int lag = skill_table[gsn_tracker].beats;
const int cost = skill_table[gsn_tracker].min_mana;
int skill = get_skill(ch, gsn_tracker );
int anat;
if (skill < 2){
send_to_char("Huh?\n\r", ch);
return;
}
else if (IS_NULLSTR(argument)){
send_to_char("Track footprints of which type?\n\r", ch);
return;
}
else if ( (anat = anatomy_lookup( argument )) < 1){
int i;
send_to_char("You may track:\n\r", ch );
for (i = 1; i < MAX_ANATOMY && !IS_NULLSTR(anatomy_table[i].name); i++){
sendf(ch, "%s\n\r", anatomy_table[i].name );
}
return;
}
else if (ch->mana < cost){
send_to_char("You cannot remember the spur.\n\r", ch);
return;
}
ch->mana -= cost;
WAIT_STATE(ch, lag );
if (number_percent() > skill){
send_to_char("You failed to recall the shape of the footprint.\n\r", ch);
check_improve(ch, gsn_tracker, FALSE, 5);
return;
}
check_improve(ch, gsn_tracker, TRUE, 1);
if ( (paf = affect_find( ch->affected, gsn_tracker )) != NULL){
paf->modifier = anat;
paf->duration = number_fuzzy( ch->level / 6 );
}
else{
af.type = gsn_tracker;
af.level = ch->level;
af.duration = number_fuzzy( ch->level / 6 );
af.where = TO_AFFECTS;
af.bitvector= 0;
af.location = APPLY_NONE;
af.modifier = anat;
affect_to_char(ch, &af);
}
sendf( ch, "You begin to look for %s spur.\n\r", anatomy_table[anat].name );
}
/* causes any ranger pets to return to him */
void do_pack_call( CHAR_DATA* ch, char* argument ){
CHAR_DATA* pet;
AFFECT_DATA af;
const int gsn_pack_call = skill_lookup("pack call");
int skill = get_skill(ch, gsn_pack_call );
const int lag = skill_table[gsn_pack_call].beats;
const int cost = skill_table[gsn_pack_call].min_mana;
bool fSet = FALSE;
if (skill < 2){
send_to_char("Huh?\n\r", ch);
return;
}
else if (is_affected(ch, gsn_pack_call)){
send_to_char("You're not ready to call on your pack yet.\n\r", ch);
return;
}
else if (ch->mana < cost){
send_to_char("You can't manage to concentrate.\n\r", ch);
return;
}
else
ch->mana -= cost;
WAIT_STATE2(ch, lag );
if (number_percent() < 4 * skill / 5){
send_to_char("You failed to call on your pack.\n\r", ch);
check_improve(ch, gsn_pack_call, FALSE, 5);
return;
}
else
check_improve(ch, gsn_pack_call, TRUE, 5);
send_to_char("You call on your pack.\n\r", ch);
act("$n calls on $s pack.", ch, NULL, NULL, TO_ROOM);
//we generate paths for each creature found.
for (pet = char_list; pet; pet = pet->next){
if (!IS_NPC(pet) || !IS_AFFECTED(pet, AFF_CHARM) || pet->master == NULL)
continue;
else if (pet->pIndexData->vnum != MOB_VNUM_RAVEN
&& pet->pIndexData->vnum != MOB_VNUM_LEOPARD
&& pet->pIndexData->vnum != MOB_VNUM_WYVERN
&& pet->pIndexData->vnum != MOB_VNUM_DISPLACER
&& pet->pIndexData->vnum != MOB_VNUM_MAMMOTH)
continue;
else if (pet->in_room == ch->in_room)
continue;
if (set_path( pet, pet->in_room, ch->in_room, 256, NULL) < 1){
sendf(ch, "%s could not find its way here.\n\r", PERS2(pet));
}
else
fSet = TRUE;
}
if (fSet){
af.type = gsn_pack_call;
af.level = ch->level;
af.duration = 12;
af.where = TO_AFFECTS;
af.bitvector = 0;
af.location = APPLY_NONE;
af.modifier = 0;
affect_to_char(ch, &af);
}
}
/* faster reload in combat at 3/4 damage */
void do_rapid_fire( CHAR_DATA* ch, char* argument ){
AFFECT_DATA af;
int skill = get_skill(ch, gsn_rapid_fire );
const int lag = skill_table[gsn_rapid_fire].beats;
const int cost = skill_table[gsn_rapid_fire].min_mana;
if (skill < 2){
send_to_char("Huh?\n\r", ch);
return;
}
else if (is_affected(ch, gsn_rapid_fire)){
send_to_char("You're already prepared to quicky fire in combat.\n\r", ch);
return;
}
else if (ch->mana < cost){
send_to_char("You can't manage to concentrate.\n\r", ch);
return;
}
else
ch->mana -= cost;
WAIT_STATE2(ch, lag );
af.type = gsn_rapid_fire;
af.duration = number_fuzzy(ch->level / 10 );
af.level = ch->level;
af.where = TO_AFFECTS;
af.bitvector = 0;
af.modifier = -1;
af.location = APPLY_DEX;
affect_to_char( ch, &af);
send_to_char("You will now forsake aim for speed in ranged combat.\n\r", ch);
}