/**************************************************************************/
// handler.cpp - Utility functions etc
/***************************************************************************
* 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 <assert.h>
#include "include.h" // dawn standard includes
#include "magic.h"
#include "msp.h"
#include "ictime.h"
#include "pload.h"
#include "lockers.h"
// command procedures needed
DECLARE_DO_FUN(do_return );
void set_char_magic_bits(char_data * ch);
void affect_copy_to_char( char_data *ch, AFFECT_DATA *paf );
char *flag_string( const struct flag_type *flag_table, int bits);
/**************************************************************************/
/*
* Local functions.
*/
void affect_modify args( ( char_data *ch, AFFECT_DATA *paf, bool fAdd ) );
void room_update( AREA_DATA *pArea );
void room_aff_update( ROOM_INDEX_DATA *room );
void bash_eq(char_data *ch, int chance)
{
OBJ_DATA *obj, *obj_next;
for(obj = ch->carrying; obj != NULL; obj = obj_next)
{
obj_next = obj->next_content;
if(obj->condition>0 && number_percent()<=chance+20 &&
!IS_SET(obj->extra_flags ,OBJEXTRA_NO_DEGRADE))
obj->condition-=1;
}
return;
}
/**************************************************************************/
// return the bit vector table relating to a given where location - Kal
const flag_type *affect_get_bitvector_table_for_where(int where)
{
switch(where)
{
case WHERE_AFFECTS: return affect_flags; // character
case WHERE_AFFECTS2: return affect2_flags; // character
case WHERE_IMMUNE: return imm_flags; // character
case WHERE_RESIST: return res_flags; // character
case WHERE_VULN: return vuln_flags; // character
case WHERE_OBJEXTRA: return objextra_flags; // object
case WHERE_OBJEXTRA2: return objextra2_flags; // object
case WHERE_WEAPON: return weapon_flags; // object
case WHERE_OBJECTSPELL:return objspell_flags; // object spell affect
case WHERE_MODIFIER: break;
case WHERE_SKILLS: break;
default:
bugf("affect_get_bitvector_table_for_where(): "
"Unsupported where value of %d.", where);
do_abort();
break;
}
return NULL;
}
/**************************************************************************/
/* returns number of people on an object */
int count_users(OBJ_DATA *obj)
{
char_data *fch;
int count = 0;
if(obj->in_room == NULL)
return 0;
for(fch = obj->in_room->people; fch; fch = fch->next_in_room){
if(fch->on == obj){
count++;
}
}
return count;
}
/**************************************************************************/
int num_enemies(char_data *ch)
{
char_data *fch;
int count=0;
for(fch = ch->in_room->people; fch != NULL; fch = fch->next_in_room)
if(fch->fighting==ch)
count+=1;
return count;
}
/**************************************************************************/
int weapontype (const char *name)
{
int type;
for(type = 0; weapon_table[type].name != NULL; type++)
{
if(LOWER(name[0]) == LOWER(weapon_table[type].name[0])
&& !str_prefix(name,weapon_table[type].name))
return weapon_table[type].type;
}
return WEAPON_EXOTIC;
}
/**************************************************************************/
char *get_weapontype(OBJ_DATA *obj)
{
if( obj->item_type == ITEM_WEAPON ){
return flag_string( weapon_class_types, obj->value[0] );
}
return NULL;
}
/**************************************************************************/
int item_lookup(const char *name)
{
int type;
for(type = 0; item_table[type].name != NULL; type++)
{
if(LOWER(name[0]) == LOWER(item_table[type].name[0])
&& !str_prefix(name,item_table[type].name))
return item_table[type].type;
}
return -1;
}
/**************************************************************************/
char *item_name(int item_type)
{
// return(flag_string(item_types, item_type));
int type;
for(type = 0; item_table[type].name != NULL; type++)
if(item_type == item_table[type].type)
return item_table[type].name;
return "none";
}
/**************************************************************************/
char *weapon_name( int weapon_type)
{
int type;
for(type = 0; weapon_table[type].name != NULL; type++)
if(weapon_type == weapon_table[type].type)
return weapon_table[type].name;
return "exotic";
}
/**************************************************************************/
/* for immunity, vulnerabiltiy, and resistant
the 'globals' (magic and weapons) may be overriden
three other cases -- wood, silver, and iron -- are checked in fight.c */
int check_immune(char_data *ch, int dam_type)
{
int immune, def;
int bit;
immune = -1;
def = IS_NORMAL;
if(dam_type == DAM_NONE)
return immune;
if(dam_type <= 3)
{
if(IS_SET(ch->imm_flags,IMM_WEAPON))
def = IS_IMMUNE;
else if (IS_SET(ch->res_flags,RES_WEAPON))
def = IS_RESISTANT;
else if (IS_SET(ch->vuln_flags,VULN_WEAPON))
def = IS_VULNERABLE;
}
else /* magical attack */
{
if(IS_SET(ch->imm_flags,IMM_MAGIC))
def = IS_IMMUNE;
else if (IS_SET(ch->res_flags,RES_MAGIC))
def = IS_RESISTANT;
else if (IS_SET(ch->vuln_flags,VULN_MAGIC))
def = IS_VULNERABLE;
}
/* set bits to check -- VULN etc. must ALL be the same or this will fail */
switch(dam_type)
{
case(DAM_BASH): bit = IMM_BASH; break;
case(DAM_PIERCE): bit = IMM_PIERCE; break;
case(DAM_SLASH): bit = IMM_SLASH; break;
case(DAM_FIRE): bit = IMM_FIRE; break;
case(DAM_COLD): bit = IMM_COLD; break;
case(DAM_LIGHTNING): bit = IMM_LIGHTNING;break;
case(DAM_ACID): bit = IMM_ACID; break;
case(DAM_POISON): bit = IMM_POISON; break;
case(DAM_NEGATIVE): bit = IMM_NEGATIVE; break;
case(DAM_HOLY): bit = IMM_HOLY; break;
case(DAM_ENERGY): bit = IMM_ENERGY; break;
case(DAM_MENTAL): bit = IMM_MENTAL; break;
case(DAM_DISEASE): bit = IMM_DISEASE; break;
case(DAM_DROWNING): bit = IMM_DROWNING; break;
case(DAM_LIGHT): bit = IMM_LIGHT; break;
case(DAM_CHARM): bit = IMM_CHARM; break;
case(DAM_SOUND): bit = IMM_SOUND; break;
case(DAM_ILLUSION): bit = IMM_ILLUSION; break;
default: return def;
}
if(IS_SET(ch->imm_flags,bit))
immune = IS_IMMUNE;
else if (IS_SET(ch->res_flags,bit) && immune != IS_IMMUNE)
immune = IS_RESISTANT;
else if (IS_SET(ch->vuln_flags,bit))
{
if(immune == IS_IMMUNE)
immune = IS_RESISTANT;
else if (immune == IS_RESISTANT)
immune = IS_NORMAL;
else
immune = IS_VULNERABLE;
}
if(immune == -1)
return def;
else
return immune;
}
/**************************************************************************/
bool is_clan(char_data *ch)
{
return(ch->clan>0);
}
/**************************************************************************/
bool is_same_clan(char_data *ch, char_data *victim)
{
return(ch->clan && ch->clan == victim->clan );
}
/**************************************************************************/
// for returning skill information
int get_skill(char_data *ch, int sn)
{
return(ch->get_skill(sn));
}
/**************************************************************************/
// for returning weapon information
int get_weapon_sn(char_data *ch)
{
OBJ_DATA *wield;
int sn;
wield = get_eq_char( ch, WEAR_WIELD );
if(wield == NULL || wield->item_type != ITEM_WEAPON)
sn = gsn_hand_to_hand;
else switch (wield->value[0])
{
default : sn = -1; break;
case(WEAPON_SPEAR): sn = gsn_spear; break;
case(WEAPON_SWORD): sn = gsn_sword; break;
case(WEAPON_DAGGER): sn = gsn_dagger; break;
case(WEAPON_STAFF): sn = gsn_staff; break;
case(WEAPON_MACE): sn = gsn_mace; break;
case(WEAPON_AXE): sn = gsn_axe; break;
case(WEAPON_FLAIL): sn = gsn_flail; break;
case(WEAPON_WHIP): sn = gsn_whip; break;
case(WEAPON_POLEARM): sn = gsn_polearm; break;
case(WEAPON_SICKLE): sn = gsn_sickle; break;
}
return sn;
}
/**************************************************************************/
int get_weapon_skill(char_data *ch, int sn)
{
int skill;
/* -1 is exotic */
if(IS_NPC(ch))
{
if(sn == -1)
skill = 3 * ch->level;
else if (sn == gsn_hand_to_hand)
skill = 40 + 2 * ch->level;
else
skill = 40 + 5 * ch->level / 2;
}
else
{
if(sn == -1)
skill = 3 * ch->level;
else
skill = ch->pcdata->learned[sn];
}
return URANGE(0,skill,100);
}
/**************************************************************************/
// used to de-screw characters
void reset_char(char_data *ch)
{
int loc,mod,stat;
OBJ_DATA *obj;
AFFECT_DATA *af;
int i;
int ac_amount;
if(IS_NPC(ch))
return;
// THIS FIRST SECTION OF CODE IS ONLY RUN ON NEWBIES OR CORRUPTED PFILES
if( ch->pcdata->perm_hit == 0
|| ch->pcdata->perm_mana == 0
|| ch->pcdata->perm_move == 0
|| (ch->pcdata->last_level == 0 && ch->played==0))
{ // do a FULL reset - START of full reset
for(loc = 0; loc < MAX_WEAR; loc++)
{ // start of wear location loop
obj = get_eq_char(ch,loc);
if(obj == NULL)
continue;
if( obj->wear_loc == WEAR_SHEATHED
|| obj->wear_loc == WEAR_CONCEALED )
continue;
for(af=OBJECT_AFFECTS(obj); af; af = af->next )
{
if(af->level > ch->level) continue;
mod = af->modifier;
switch(af->location)
{
case APPLY_SEX:
ch->sex -= mod;
if(ch->sex < 0 || ch->sex >2)
ch->sex = IS_NPC(ch) ? 0 : ch->pcdata->true_sex;
break;
case APPLY_MANA: ch->max_mana -= mod; break;
case APPLY_HIT: ch->max_hit -= mod; break;
case APPLY_MOVE: ch->max_move -= mod; break;
default: break; // do nothing for the rest of the conditions
}
}
} // end of wear location loop
// now reset the permanent stats
ch->pcdata->perm_hit = ch->max_hit;
ch->pcdata->perm_mana = ch->max_mana;
ch->pcdata->perm_move = ch->max_move;
ch->pcdata->last_level = ch->played/3600;
if(ch->pcdata->true_sex < 0 || ch->pcdata->true_sex > 2)
{
if(ch->sex > 0 && ch->sex < 3)
ch->pcdata->true_sex = ch->sex;
else
ch->pcdata->true_sex = 0;
}
} // do a FULL reset - END of full reset
// BELOW HERE IS RUN ON EVERY CHARACTER WHEN THEY CONNECT AND LEVEL
// should do invunerablities etc in here? or just above
// calculate their object restriction groupings
if(ch->pcdata){
int index;
ch->pcdata->objrestrict=0;
for(index=0; !IS_NULLSTR(classgroup_table[index].name); index++){
if(is_exact_name(class_table[ch->clss].name,classgroup_table[index].text_members)){
ch->pcdata->objrestrict|= (1<<classgroup_table[index].bitindex);
}
}
}
// update their body parts and form
ch->form = race_table[ch->race]->form;
ch->parts = race_table[ch->race]->parts;
// now restore the character to his/her true condition
for(stat = 0; stat < MAX_STATS; stat++)
{
ch->modifiers[stat]=0;
if(GAMESETTING(GAMESET_USE_ROLEMASTER_MODIFIERS)){
if(ch->perm_stats[stat] > 90){
ch->modifiers[stat] = (ch->perm_stats[stat]-90)*2+20;
}else if(ch->perm_stats[stat] > 70){
ch->modifiers[stat] = ch->perm_stats[stat]-70;
}else if(ch->perm_stats[stat] < 26){
ch->modifiers[stat] = -26 + ch->perm_stats[stat];
}
}else{
if(ch->perm_stats[stat] > 95){
ch->modifiers[stat] = (ch->perm_stats[stat]-95)*3+45;
}else if(ch->perm_stats[stat] > 85){
ch->modifiers[stat] = (ch->perm_stats[stat]-85)*2+25;
}else if(ch->perm_stats[stat] > 60){
ch->modifiers[stat] = ch->perm_stats[stat]-60;
}else if(ch->perm_stats[stat] < 26){
ch->modifiers[stat] = -26 + ch->perm_stats[stat];
}
}
ch->modifiers[stat] += race_table[ch->race]->stat_modifier[stat];
}
if(ch->pcdata->true_sex < 0 || ch->pcdata->true_sex > 2){
ch->pcdata->true_sex = 0; // reset true sex if it is stuffed
}
ch->sex = ch->pcdata->true_sex;
ch->max_hit = ch->pcdata->perm_hit;
ch->max_mana = ch->pcdata->perm_mana;
ch->max_move = ch->pcdata->perm_move;
for(i = 0; i < 4; i++)
{
if(HAS_CLASSFLAG(ch, CLASSFLAG_LEVEL_BASED_AC)){
ch->armor[i] = 100-4*ch->level;
}else{
ch->armor[i] = 100;
}
}
ch->hitroll = 0;
ch->damroll = 0;
ch->saving_throw = 0;
// setup their magic bits
set_char_magic_bits(ch);
// now start adding back the effects of objects (excluding paf->where==WHERE_OBJECTSPELL affects)
for(loc = 0; loc < MAX_WEAR; loc++)
{
obj = get_eq_char(ch,loc);
if(obj == NULL)
continue;
if( obj->wear_loc == WEAR_SHEATHED
|| obj->wear_loc == WEAR_CONCEALED )
continue;
for(i = 0; i < 4; i++)
{
ac_amount = apply_ac( obj, loc, i );
if(ch->level< obj->level)
ac_amount = ac_amount * ch->level/ obj->level;
ch->armor[i] -= ac_amount;
}
for(af = OBJECT_AFFECTS(obj); af; af = af->next)
{
if(af->level > ch->level ){
continue;
}
if(af->where!=WHERE_OBJECTSPELL){
mod = af->modifier;
switch(af->location)
{
case APPLY_ST: ch->modifiers[STAT_ST] += mod; break;
case APPLY_QU: ch->modifiers[STAT_QU] += mod; break;
case APPLY_PR: ch->modifiers[STAT_PR] += mod; break;
case APPLY_EM: ch->modifiers[STAT_EM] += mod; break;
case APPLY_IN: ch->modifiers[STAT_IN] += mod; break;
case APPLY_CO: ch->modifiers[STAT_CO] += mod; break;
case APPLY_AG: ch->modifiers[STAT_AG] += mod; break;
case APPLY_SD: ch->modifiers[STAT_SD] += mod; break;
case APPLY_ME: ch->modifiers[STAT_ME] += mod; break;
case APPLY_RE: ch->modifiers[STAT_RE] += mod; break;
case APPLY_SEX: ch->sex += mod; break;
case APPLY_MANA: ch->max_mana += mod; break;
case APPLY_HIT: ch->max_hit += mod; break;
case APPLY_MOVE: ch->max_move += mod; break;
case APPLY_AC:
for(i = 0; i < 4; i ++)
ch->armor[i] += mod;
break;
case APPLY_HITROLL: ch->hitroll += mod; break;
case APPLY_DAMROLL: ch->damroll += mod; break;
// saving throws
case APPLY_SAVES: ch->saving_throw += mod; break;
default: break; // do nothing for the rest of the conditions
}
}
}
// IC object restriction system - Kal
if(obj->pIndexData && ch->pcdata
&& HAS_CONFIG(ch,CONFIG_OBJRESTRICT)
&& ((ch->pcdata->objrestrict& obj->pIndexData->objrestrict)>0))
{
OBJRESTRICT_LIST_DATA *pr;
AFFECT_DATA aff;
int top_prority=-1;
for( pr = obj->pIndexData->restrict; pr; pr = pr->next )
{
if(IS_SET(ch->pcdata->objrestrict,(1<<pr->classgroup->bitindex))){
top_prority=UMAX(pr->priority, top_prority);
}
}
// IC object restriction system - Kal
for( pr = obj->pIndexData->restrict; pr; pr = pr->next )
{
if(top_prority!=pr->priority
|| !IS_SET(ch->pcdata->objrestrict,(1<<pr->classgroup->bitindex)))
{
continue;
}
aff.where = WHERE_RESTRICT;
aff.type = -1;
aff.level = 0;
aff.duration = -1;
aff.location = pr->affectprofile->wear_location;
aff.modifier = pr->affectprofile->wear_amount;
aff.bitvector = 0;
// do the act text
// act(pr->affectprofile->wear_message,ch,obj,NULL,TO_CHAR);
// add the affect
affect_modify( ch, &aff, true );
if(top_prority==-1) // -1 means only the first affectprofile is applied
break;
}
}
}
// now add back effects on the character
// now add back spell effects on the character
for(af = ch->affected; af; af = af->next){
mod = af->modifier;
switch(af->location)
{
case APPLY_ST: ch->modifiers[STAT_ST] += mod; break;
case APPLY_QU: ch->modifiers[STAT_QU] += mod; break;
case APPLY_PR: ch->modifiers[STAT_PR] += mod; break;
case APPLY_EM: ch->modifiers[STAT_EM] += mod; break;
case APPLY_IN: ch->modifiers[STAT_IN] += mod; break;
case APPLY_CO: ch->modifiers[STAT_CO] += mod; break;
case APPLY_AG: ch->modifiers[STAT_AG] += mod; break;
case APPLY_SD: ch->modifiers[STAT_SD] += mod; break;
case APPLY_ME: ch->modifiers[STAT_ME] += mod; break;
case APPLY_RE: ch->modifiers[STAT_RE] += mod; break;
case APPLY_SEX: ch->sex += mod; break;
case APPLY_MANA: ch->max_mana += mod; break;
case APPLY_HIT: ch->max_hit += mod; break;
case APPLY_MOVE: ch->max_move += mod; break;
case APPLY_AC:
for(i = 0; i < 4; i ++)
ch->armor[i] += mod;
break;
case APPLY_HITROLL: ch->hitroll += mod; break;
case APPLY_DAMROLL: ch->damroll += mod; break;
// saving throws
case APPLY_SAVES: ch->saving_throw += mod; break;
default: break; // do nothing for the rest of the conditions
}
}
ch->sex=URANGE(0, ch->sex, 3); // crash proof sex modifiers
}
/**************************************************************************/
// Retrieve a character's trusted level for permission checking.
// if someone is switched, their trust value is returned
int get_trust( char_data *ch )
{
if(!ch)
{
log_string("BUG: in get_trust - ch was NULL");
return 0;
}
if( TRUE_CH(ch)->trust)
return TRUE_CH(ch)->trust;
if( IS_NPC(TRUE_CH(ch)) && ch->level >= LEVEL_HERO )
return LEVEL_HERO - 1;
else
return TRUE_CH(ch)->level;
}
/**************************************************************************/
// Retrieve a character's carry capacity.
int can_carry_n( char_data *ch )
{
if( !IS_NPC(ch) && ch->level >= LEVEL_IMMORTAL )
return 10000;
if( IS_NPC(ch) && IS_SET(ch->act, ACT_PET) ){
return 50;
}
return(MAX_WEAR + 2 * (ch->modifiers[STAT_ST]+28) + ch->level/5 + 10)*10;
}
/**************************************************************************/
// Retrieve a character's carry capacity.
int can_carry_w( char_data *ch )
{
if( !IS_NPC(ch) && ch->level >= LEVEL_IMMORTAL )
return 10000000;
if( IS_NPC(ch) && IS_SET(ch->act, ACT_PET) && !IS_CONTROLLED(ch))
return 10;
return((ch->modifiers[STAT_ST]+28) * 10 + ch->level * 5)*10;
}
/**************************************************************************/
// See if a string is one of the names of an object.
bool is_name ( const char *str, const char *namelist )
{
char name[MSL*2], part[MSL*2];
const char *list, *string;
if(IS_NULLSTR(namelist)){
return false;
}
string = str;
// we need ALL parts of string to match part of namelist
for( ; ; ) // start parsing string
{
str = one_argument(str,part);
if(part[0] == '\0' )
return true;
// check to see if this is part of namelist
list = namelist;
for( ; ; ) // start parsing namelist
{
list = one_argument(list,name);
if(name[0] == '\0') // this name was not found
return false;
if(!str_prefix(string,name))
return true; // full pattern match
if(!str_prefix(part,name))
break;
}
}
}
/**************************************************************************/
// See if a string has one of the names infix
bool is_name_infix( const char *str, const char *namelist )
{
char name[MSL*2], part[MSL*2];
const char *list, *string;
if(IS_NULLSTR(namelist))
return false;
string = str;
// we need parts of string to match part of namelist
for( ; ; ) // start parsing string
{
str = one_argument(str,part);
if(part[0] == '\0' )
return true;
// check to see if this is part of namelist
list = namelist;
for( ; ; )
{
list = one_argument(list,name);
if(name[0] == '\0')
return false;
if(!str_infix(string,name))
return true;
if(!str_infix(part,name))
break;
}
}
}
/**************************************************************************/
// return true if name str is in the name list
bool is_exact_name( const char *str, const char *namelist )
{
char name[MSL*2];
if(IS_NULLSTR(namelist))
return false;
if(!str_cmp(str, namelist)){
return true;
}
for( ; ; )
{
namelist = one_argument( namelist, name );
if( name[0] == '\0' )
return false;
if( !str_cmp( str, name ) )
return true;
}
}
/**************************************************************************/
// move the affects from the olc template of an object to the actual
// object - used when enchanting etc.
void affects_from_template_to_obj(OBJ_DATA *obj)
{
if(obj->affected || obj->no_affects)
{
// previously got affects from template or has been disenchanted
return;
}
// okay, move all the old flags into new vectors if we have to
AFFECT_DATA *paf, *af_new;
for(paf = obj->pIndexData->affected; paf; paf = paf->next) // affects_from_template_to_obj
{
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;
}
}
/**************************************************************************/
// Reduces the wealth on a mob if it is above the highest it could
// possibly be rolled to.
// Notes:
// Used to reduce a mobs money when they are in roles that a lot of players
// give them money - e.g. bribe triggers for mobprogs, healers, money
// changers etc... (even though a lot of the code already does 'automated
// banking' on some of the above. - Kal May 1999
// This system prevents players from subduing or killing these mobs for
// their money
void limit_mobile_wealth(char_data *mob)
{
// can't be called on players
if(!IS_NPC(mob))
return;
// get their mobindex
MOB_INDEX_DATA *pMobIndex=mob->pIndexData;
// must have a valid index data
if(!pMobIndex)
return;
// get the maximum wealth value they could roll when the mob is
// created by create_mobile()
long max_wealth= 3 * pMobIndex->wealth/2;
long current_wealth= (mob->gold*100) + mob->silver;
// if their wealth is greater than the max possible
// reroll it using similar code as in create_mobile()
if(current_wealth>max_wealth){
// limit the mobs weatlth to its wealth value
long wealth = number_range(pMobIndex->wealth, 3 * pMobIndex->wealth/2);
mob->gold = number_range(wealth/200,wealth/100);
mob->silver = wealth - (mob->gold * 100);
}
}
/**************************************************************************/
extern char *target_name;
/**************************************************************************/
// Cast a spell onto a player (by an object)
// - should only be called from affect_modify()
static void affect_add_objectspell(char_data *ch, AFFECT_DATA *paf)
{
// the assumption made
assert(paf->where==WHERE_OBJECTSPELL);
int sn=paf->type;
if(sn<FIRST_SPELL || sn>LAST_SPELL){
bugf("affect_add_castspell(): sn==%d, which isn't a spell sn!", sn);
return;
}
// see if they already have a spell based on that
// spell function at a higher level
AFFECT_DATA *paf1;
SPELL_FUN * spell_fun= skill_table[sn].spell_fun;
if(!spell_fun || spell_fun==spell_null){
bugf("affect_add_castspell(): No spell function for sn==%d '%s'.",
sn, skill_table[sn].name);
REMOVE_BIT(paf->bitvector,OBJSPELL_ACTIVE);
return;
}
for( paf1= ch->affected; paf1; paf1= paf1->next ) {
if(paf1->type>=FIRST_SPELL && paf1->type<=LAST_SPELL){
if(skill_table[paf1->type].spell_fun == spell_fun
&& paf1!=paf && paf1->level>=paf->level)
{
// found an existing objectspell with same spell function
// at higher or equal level
REMOVE_BIT(paf->bitvector,OBJSPELL_ACTIVE);
return;
}
}
}
affect_parentspellfunc_strip( ch, sn); // remove all previous spells based on spell sn
AFFECT_DATA *stop=ch->affected; // backup where the affects are currently
// cast the new spell on the player
target_name = str_dup(ch->name);
(*skill_table[sn].spell_fun) ( sn, paf->level, ch, ch, TARGET_CHAR);
free_string(target_name);
target_name=NULL;
// affect is considered active
SET_BIT(paf->bitvector,OBJSPELL_ACTIVE);
// now find the affects from the spell just cast and patch the duration accordingly
if(paf->duration!=0){
AFFECT_DATA *paf_patch;
for(paf_patch=ch->affected; paf_patch && paf_patch!=stop; paf_patch=paf_patch->next){
if(paf_patch->type==sn){
paf_patch->duration=paf->duration;
}
}
// assert(paf_patch); // this shouldn't go false unless some spell is
// removing paf from ch->affected which shouldn't
// be the case since paf->where==WHERE_OBJECTSPELL
// unless an object has been equiped twice - booting up
}
}
/**************************************************************************/
// Remove a spell from a player, that was cast on it by an object
// - should only be called from affect_modify()
// - should only remove the spell if it is exactly matching
// and there are no other objects on the player which would
// cast the spell at the same level. If another object is there
// that will cast the object at a different level, remove then recast.
// - because affect_add_castspell() will only of put this spell on
// if it was the highest level at the time, we dont have to check
// for spells above us.
static void affect_remove_objectspell(char_data *ch, AFFECT_DATA *paf)
{
assert(paf->where==WHERE_OBJECTSPELL);
// if the affect isn't active, then we dont have any spells to remove
// that relate to this spell
if(!IS_SET(paf->bitvector,OBJSPELL_ACTIVE)){
return;
}
int sn=paf->type;
// make sure we are dealing with a valid spell
if(sn<FIRST_SPELL || sn>LAST_SPELL){
bugf("affect_remove_castspell(): sn==%d, which isn't a spell sn!", sn);
do_abort(); // abort here - how did they get it on in the first place
}
// find the affects of the particular sn and remove them accordingly
AFFECT_DATA *lpaf, *lpaf_next;
bool show_msg =true;
for(lpaf=ch->affected; lpaf; lpaf=lpaf_next){
lpaf_next=lpaf->next;
if(lpaf->where!=WHERE_OBJECTSPELL && lpaf->type==sn){
affect_remove(ch, lpaf);
if(show_msg && !IS_NULLSTR(skill_table[sn].msg_off))
{
ch->printf("%s\r\n",skill_table[sn].msg_off);
show_msg = false; // only display the fade out message once
}
}
}
// check if there are any other objectspells which is to replace
// the spell just removed, if there are find the highest level casting one
AFFECT_DATA *raf; // replacement affect
AFFECT_DATA *match=NULL; // matching affect
for(raf=ch->affected; raf; raf=raf->next){
if( raf!=paf
&& raf->where==WHERE_OBJECTSPELL
&& raf->type== paf->type)
{
if(match){
if(raf->level>match->level){
match=raf; // take the higher level affect
}
}else{
match=raf;
}
}
}
if(match){
affect_modify( ch, match, true); // cast the replacement
}
}
/**************************************************************************/
void affect_apply_modifier( char_data *ch, AFFECT_DATA *paf, bool fAdd )
{
int i;
int amount=(fAdd?paf->modifier:-paf->modifier);
switch( paf->location )
{
default:
bugf( "affect_apply_modifier(): unknown location %d.", paf->location );
do_abort(); // should never be in this situation
return;
case APPLY_NONE: break;
case APPLY_CLASS: break;
case APPLY_LEVEL: break;
case APPLY_AGE: break;
case APPLY_HEIGHT: break;
case APPLY_WEIGHT: break;
case APPLY_GOLD: break;
case APPLY_EXP: break;
case APPLY_ST: ch->modifiers[STAT_ST] += amount; break;
case APPLY_QU: ch->modifiers[STAT_QU] += amount; break;
case APPLY_PR: ch->modifiers[STAT_PR] += amount; break;
case APPLY_EM: ch->modifiers[STAT_EM] += amount; break;
case APPLY_IN: ch->modifiers[STAT_IN] += amount; break;
case APPLY_CO: ch->modifiers[STAT_CO] += amount; break;
case APPLY_AG: ch->modifiers[STAT_AG] += amount; break;
case APPLY_SD: ch->modifiers[STAT_SD] += amount; break;
case APPLY_ME: ch->modifiers[STAT_ME] += amount; break;
case APPLY_RE: ch->modifiers[STAT_RE] += amount; break;
case APPLY_SEX:
if(fAdd){
ch->sex+= amount;
}else{
// revert to default sex dont worry if they had
// multiple objects on them modifying their sex.
if(IS_NPC(ch)){
ch->sex = ch->pIndexData->sex;
if(ch->sex == 3){ // random sex
ch->sex = number_range(1,2);
}
}else{
ch->sex= ch->pcdata->true_sex;
}
}
ch->sex=URANGE(0, ch->sex, 3); // crash proof sex modifiers
break;
case APPLY_MANA:
ch->max_mana+= amount;
ch->mana+= amount;
break;
case APPLY_HIT:
ch->max_hit+= amount;
if( !fAdd
&& (GAMESETTING3(GAMESET3_ALWAYS_NO_NEGATIVE_HP_AT_AFFECTOFF) ||
(paf->type>=0
&& paf->type<MAX_SKILL
&& IS_SET(skill_table[paf->type].flags, SKFLAGS_NO_NEGATIVE_HP_AT_AFFECTOFF))))
{
if(ch->hit>0){
ch->hit+= amount;
ch->hit=UMAX(1,ch->hit); // no death from spell when it wears off
}else{
ch->hit+= amount;
}
}else{
ch->hit+= amount;
}
break;
case APPLY_MOVE:
ch->max_move+= amount;
ch->move+= amount;
break;
case APPLY_AC:
for(i = 0; i < 4; i ++){
ch->armor[i] += amount;
}
break;
case APPLY_HITROLL: ch->hitroll+= amount; break;
case APPLY_DAMROLL: ch->damroll+= amount; break;
case APPLY_SAVES: ch->saving_throw+= amount; break;
}
}
/**************************************************************************/
// Apply or remove an affect to a character.
void affect_modify( char_data *ch, AFFECT_DATA *paf, bool fAdd )
{
OBJ_DATA *wield= NULL;
if( fAdd )
{
switch(paf->where)
{
case WHERE_OBJECTSPELL:
affect_add_objectspell(ch,paf);
break;
// if the object is set to affect skills
case WHERE_SKILLS:
affect_to_skill( ch, paf->type, paf->modifier );
break;
case WHERE_AFFECTS:
SET_BIT(ch->affected_by, paf->bitvector);
break;
case WHERE_AFFECTS2:
SET_BIT(ch->affected_by2, paf->bitvector);
break;
case WHERE_IMMUNE:
SET_BIT(ch->imm_flags,paf->bitvector);
break;
case WHERE_RESIST:
SET_BIT(ch->res_flags,paf->bitvector);
break;
case WHERE_VULN:
SET_BIT(ch->vuln_flags,paf->bitvector);
break;
}
}else{ // removing the affect
switch(paf->where)
{
case WHERE_OBJECTSPELL:
affect_remove_objectspell(ch,paf);
break;
case WHERE_SKILLS:
affect_to_skill( ch, paf->type, -paf->modifier );
break;
case WHERE_AFFECTS:
REMOVE_BIT(ch->affected_by, paf->bitvector);
break;
case WHERE_AFFECTS2:
REMOVE_BIT(ch->affected_by2, paf->bitvector);
break;
case WHERE_IMMUNE:
REMOVE_BIT(ch->imm_flags,paf->bitvector);
break;
case WHERE_RESIST:
REMOVE_BIT(ch->res_flags,paf->bitvector);
break;
case WHERE_VULN:
REMOVE_BIT(ch->vuln_flags,paf->bitvector);
break;
}
}
// perform the modifiers of the affect
affect_apply_modifier(ch, paf, fAdd);
// Check for weapon wielding.
// Guard against recursion (for weapons with affects).
if( !IS_NPC(ch)
&& ( wield = get_eq_char( ch, WEAR_WIELD ) ) != NULL
&& get_obj_weight(wield) > (ch->modifiers[STAT_ST]+20)*10
&& ch->position != POS_DEAD )
{
static int depth;
if( depth == 0 )
{
depth++;
act( "You drop $p.", ch, wield, NULL, TO_CHAR );
act( "$n drops $p.", ch, wield, NULL, TO_ROOM );
obj_from_char( wield );
obj_to_room( wield, ch->in_room );
depth--;
}
}
update_pos(ch);
return;
}
/**************************************************************************/
// find an effect in an affect list
AFFECT_DATA *affect_find(AFFECT_DATA *paf, int sn)
{
AFFECT_DATA *paf_find;
for( paf_find = paf; paf_find != NULL; paf_find = paf_find->next )
{
if( paf_find->type == sn )
return paf_find;
}
return NULL;
}
/**************************************************************************/
// fix object affects when removing one
void affect_check(char_data *ch, int where, int vector)
{
AFFECT_DATA *paf;
OBJ_DATA *obj;
if(vector == 0 || flag_value(to_types, flag_string(to_types, where)) == NO_FLAG){
// flag_value(to_types, flag_string(to_types, where)) will return NO_FLAG
// if the where location relates to a table that isn't relevant to characters
return;
}
// loop thru a players current affects, reseting a bit if necessary
for(paf = ch->affected; paf; paf = paf->next){
if(paf->where == where && paf->bitvector == vector)
{
switch(where)
{
case WHERE_AFFECTS:
SET_BIT(ch->affected_by, paf->bitvector);
break;
case WHERE_AFFECTS2:
SET_BIT(ch->affected_by2, paf->bitvector);
break;
case WHERE_IMMUNE:
SET_BIT(ch->imm_flags,paf->bitvector);
break;
case WHERE_RESIST:
SET_BIT(ch->res_flags,paf->bitvector);
break;
case WHERE_VULN:
SET_BIT(ch->vuln_flags,paf->bitvector);
break;
}
return;
}
}
// loop thru what a player is wearing, reseting a bit if necessary
for(obj = ch->carrying; obj; obj = obj->next_content)
{
if(obj->wear_loc == WEAR_NONE)
continue;
for(paf = OBJECT_AFFECTS(obj); paf; paf = paf->next){
if(paf->where == where && paf->bitvector == vector){
switch(where)
{
case WHERE_AFFECTS:
SET_BIT(ch->affected_by, paf->bitvector);
break;
case WHERE_AFFECTS2:
SET_BIT(ch->affected_by2, paf->bitvector);
break;
case WHERE_IMMUNE:
SET_BIT(ch->imm_flags,paf->bitvector);
break;
case WHERE_RESIST:
SET_BIT(ch->res_flags,paf->bitvector);
break;
case WHERE_VULN:
SET_BIT(ch->vuln_flags,paf->bitvector);
break;
}
return;
}
}
}
}
/**************************************************************************/
// Give an affect to a char.
void affect_to_char( char_data *ch, AFFECT_DATA *paf )
{
AFFECT_DATA *paf_new;
// do debugging stuff
if(paf->type>=MAX_SKILL)
{
bug("affect_to_char(): Invalid affect!!!\r\n");
do_abort();
}
paf_new = new_affect();
*paf_new = *paf;
paf_new->next = ch->affected;
ch->affected = paf_new;
affect_modify( ch, paf_new, true );
return;
}
/**************************************************************************/
// Give an affect to a char, when paf->where==WHERE_OBJECTSPELL
// Give it if they are high enough level for the affect or
// if ignore level is set on the affect bitvector
void affect_copy_to_char( char_data *ch, AFFECT_DATA *paf, int objlevel )
{
AFFECT_DATA *paf_new;
// do debugging stuff
if(paf->type>=MAX_SKILL)
{
bugf("affect_copy_to_char(): Invalid affect - paf->type=%d (higher than MAX_SKILL)!!!", paf->type);
do_abort();
}
if(paf->where!=WHERE_OBJECTSPELL){
bug("affect_copy_to_char(): paf->where==WHERE_OBJECTSPELL!!!\r\n");
do_abort();
}
// level check
int castlevel=paf->level?paf->level:objlevel;
if(!IS_SET(OBJSPELL_IGNORE_LEVEL, paf->bitvector)
&& castlevel>ch->level)
{
return;
}
// store the affect on the player
paf_new = new_affect();
*paf_new = *paf;
// if it is a level 0 objectspell, patch up the cast level
if(paf_new->where==WHERE_OBJECTSPELL && paf_new->level==0){
paf_new->level=objlevel;
}
paf_new->next = ch->affected;
ch->affected = paf_new;
affect_modify( ch, paf_new, true ); // handles casting if necessary
return;
}
/**************************************************************************/
// loop thru all the affects on the object, finding which one it is
// - called from unequip_char()
// - paf is the affect on an object, not the copy
// - once the copy on the char has been found, it is removed, if there is
// multiple affects of the same type, the active one will be removed.
void affect_removecopy_from_char( char_data *ch, AFFECT_DATA *paf, int objlevel )
{
assert(paf->where==WHERE_OBJECTSPELL);
// level check
if(!IS_SET(OBJSPELL_IGNORE_LEVEL, paf->bitvector)
&& paf->level>ch->level)
{
return;
}
AFFECT_DATA *caf=NULL;
AFFECT_DATA *match=NULL;
// find our affect, favouring an OBJSPELL_ACTIVE if possible
for( caf= ch->affected; caf; caf=caf->next)
{
if((caf->type == paf->type) &&
(caf->where==WHERE_OBJECTSPELL))
{
// if it is a level 0 objectspell, patch up the cast level
if(paf->where==WHERE_OBJECTSPELL && paf->level==0){
if(caf->level!=objlevel){
continue;
}
}else{
if(caf->level!=paf->level){
continue;
}
}
match=caf;
if(IS_SET(caf->bitvector,OBJSPELL_ACTIVE)){
break; // found the active version
}
}
}
if(match){
affect_remove( ch, match);
}else{
bug("affect_removecopy_from_char(): couldn't find any affect copy.");
return;
}
}
/**************************************************************************/
// give an affect to an object
void affect_to_obj(OBJ_DATA *obj, AFFECT_DATA *paf)
{
AFFECT_DATA *paf_new;
affects_from_template_to_obj(obj);
paf_new = new_affect();
*paf_new = *paf;
paf_new->next = obj->affected;
obj->affected = paf_new;
// apply any affect vectors to the object's extra_flags
if(paf->bitvector){
switch(paf->where)
{
case WHERE_OBJEXTRA:
SET_BIT(obj->extra_flags,paf->bitvector);
break;
case WHERE_OBJEXTRA2:
SET_BIT(obj->extra2_flags,paf->bitvector);
break;
case WHERE_WEAPON:
if(obj->item_type == ITEM_WEAPON)
SET_BIT(obj->value[4],paf->bitvector);
break;
default:
break;
}
}
return;
}
/**************************************************************************/
// Remove an affect from a char.
void affect_remove( char_data *ch, AFFECT_DATA *paf )
{
int where;
int vector;
if( ch->affected == NULL )
{
bug("Affect_remove: no affects on ch!");
return;
}
affect_modify( ch, paf, false );
where = paf->where;
vector = paf->bitvector;
if( paf == ch->affected ){
ch->affected = paf->next;
}else{
AFFECT_DATA *prev;
for( prev = ch->affected; prev != NULL; prev = prev->next )
{
if( prev->next == paf )
{
prev->next = paf->next;
break;
}
}
if( prev == NULL )
{
bug("Affect_remove: cannot find paf.");
return;
}
}
free_affect(paf);
// fix up flying
if(!IS_AFFECTED(ch, AFF_FLYING)){
REMOVE_BIT(ch->dyn,DYN_NONMAGICAL_FLYING);
}
affect_check(ch,where,vector);
return;
}
/**************************************************************************/
void affect_remove_obj( OBJ_DATA *obj, AFFECT_DATA *paf)
{
int where, vector;
if( obj->affected == NULL )
{
bug("Affect_remove_object: no affect.");
return;
}
if(obj->carried_by != NULL && obj->wear_loc != -1)
affect_modify( obj->carried_by, paf, false );
where = paf->where;
vector = paf->bitvector;
/* remove flags from the object if needed */
if(paf->bitvector)
switch( paf->where)
{
case WHERE_OBJEXTRA:
REMOVE_BIT(obj->extra_flags,paf->bitvector);
break;
case WHERE_OBJEXTRA2:
REMOVE_BIT(obj->extra2_flags,paf->bitvector);
break;
case WHERE_WEAPON:
if(obj->item_type == ITEM_WEAPON)
REMOVE_BIT(obj->value[4],paf->bitvector);
break;
}
if( paf == obj->affected )
{
obj->affected = paf->next;
}
else
{
AFFECT_DATA *prev;
for( prev = obj->affected; prev != NULL; prev = prev->next )
{
if( prev->next == paf )
{
prev->next = paf->next;
break;
}
}
if( prev == NULL )
{
bug("Affect_remove_object: cannot find paf.");
return;
}
}
free_affect(paf);
if(obj->carried_by != NULL && obj->wear_loc != -1)
affect_check(obj->carried_by,where,vector);
return;
}
/**************************************************************************/
// Strip all affects of a given sn.
void affect_strip( char_data *ch, int sn )
{
AFFECT_DATA *paf;
AFFECT_DATA *paf_next;
for( paf = ch->affected; paf != NULL; paf = paf_next )
{
paf_next = paf->next;
if(paf->where==WHERE_OBJECTSPELL){
continue;
}
if( paf->type == sn ){
affect_remove( ch, paf );
}
}
return;
}
/**************************************************************************/
// Strip all affects from spells with the same spell function as that
// belonging to the given sn if it is a spell, otherwise just strip the
// affect
void affect_parentspellfunc_strip( char_data *ch, int sn )
{
AFFECT_DATA *paf;
AFFECT_DATA *paf_next;
if(sn<FIRST_SPELL || sn>LAST_SPELL){
affect_strip( ch, sn );
return;
}
SPELL_FUN * spell_fun= skill_table[sn].spell_fun;
if(!spell_fun || spell_fun==spell_null){
affect_strip( ch, sn );
return;
}
for( paf = ch->affected; paf != NULL; paf = paf_next )
{
paf_next = paf->next;
if(paf->type>=FIRST_SPELL && paf->type<=LAST_SPELL){
if(skill_table[paf->type].spell_fun == spell_fun){
if(paf->where==WHERE_OBJECTSPELL)
{
REMOVE_BIT(paf->bitvector,OBJSPELL_ACTIVE);
}else{
affect_remove( ch, paf );
}
}
}
}
return;
}
/**************************************************************************/
// Return the number of spells ch is affected by that are based on sn
int count_affected_by_base_spell( char_data *ch, int parentspell_sn )
{
AFFECT_DATA *paf;
AFFECT_DATA *paf_next;
int count = 0;
if(parentspell_sn <FIRST_SPELL || parentspell_sn >LAST_SPELL){
return 0;
}
SPELL_FUN * spell_fun= skill_table[parentspell_sn ].spell_fun;
if(!spell_fun || spell_fun==spell_null){
return 0;
}
for( paf = ch->affected; paf != NULL; paf = paf_next )
{
paf_next = paf->next;
if(paf->type>=FIRST_SPELL && paf->type<=LAST_SPELL){
if(!(paf->where==WHERE_OBJECTSPELL))
{
if(skill_table[paf->type].spell_fun == spell_fun){
count++;
}
}
}
}
return count;
}
/**************************************************************************/
// Return true if a char is affected by a spell.
bool is_affected( char_data *ch, int sn )
{
AFFECT_DATA *paf;
if( IS_NULLSTR(ch->name))
return false;
for( paf = ch->affected; paf != NULL; paf = paf->next ) {
if(paf->where==WHERE_OBJECTSPELL){
continue;
}
if( paf->type == sn )
return true;
}
return false;
}
/**************************************************************************/
// Kal - August 99
// notes: goes thru char, if they are flying, checking if they are flying
// due to magical causes (any spell that gives the AFF_FLY affect)
// If it is true it removes the DYN_NONMAGICAL_FLYING bit so we dont
// subtract movement points from them for flying.
void affect_fly_update(char_data *ch)
{
if( IS_AFFECTED(ch, AFF_FLYING) ){
SET_BIT(ch->dyn,DYN_NONMAGICAL_FLYING);
// loop thru finding any fly affects
AFFECT_DATA *paf;
for( paf= ch->affected; paf; paf = paf->next)
{
if( paf->bitvector== AFF_FLYING){
REMOVE_BIT(ch->dyn,DYN_NONMAGICAL_FLYING);
}
}
}else{
REMOVE_BIT(ch->dyn,DYN_NONMAGICAL_FLYING);
}
}
/**************************************************************************/
// Add or enhance an affect.
void affect_join( char_data *ch, AFFECT_DATA *paf )
{
AFFECT_DATA *paf_old;
bool found;
found = false;
for( paf_old = ch->affected; paf_old != NULL; paf_old = paf_old->next )
{
if(paf->where==WHERE_OBJECTSPELL){
continue;
}
if( paf_old->type == paf->type )
{
paf->level = (paf->level += paf_old->level) / 2;
paf->duration += paf_old->duration;
paf->modifier += paf_old->modifier;
affect_remove( ch, paf_old );
break;
}
}
affect_to_char( ch, paf );
return;
}
/**************************************************************************/
// returns true if the ch is actually in the list of people in a room
// used by nuke_pets() and extract_char() to detect the difference between:
// * When someones ch->in_room and/or ch->pet_in_room has been set to point
// to a room, but they arent actually in the room yet as they haven't
// gone thru nanny_read_motd() to get to the char_to_room() call.
// This time they wont be in the room.
// * When someone drops link, and reconnects to their linkdead char,
// then next time they login, they drop due to idling before a
// char_to_room() call. (This time they will be in the room, and
// ch->desc->connected_state will show them as something other than
// CON_PLAYING)
bool is_character_loaded_into_room(char_data *ch, ROOM_INDEX_DATA *room)
{
if(!room){
return false;
}
char_data *rch;
for( rch = room->people; rch; rch = rch->next_in_room )
{
if(rch==ch){
return true;
}
}
return false;
}
/**************************************************************************/
void make_corefile();
/**************************************************************************/
// Move a char out of a room.
void char_from_room( char_data *ch )
{
OBJ_DATA *obj;
if( ch->in_room == NULL )
{
bug("Char_from_room: NULL.");
return;
}
if( !IS_NPC(ch) )
{
--ch->in_room->area->nplayer;
}
ch->in_room->number_in_room--;
if(ch->in_room->number_in_room<0){
bugf("char_from_room(%s): ch->in_room->number_in_room<0!!!", ch->name);
ch->in_room->number_in_room=0;
}
// They arent actually in the room, just have the ch->room pointer
// set cause logging in then timed out
if((ch->desc && ch->desc->connected_state!=CON_PLAYING) || !ch->desc){
// if they arent in the game yet (haven't logged in, and this
// isn't a reconnect) or they are linkdead confirm
// they are in the room
if(!is_character_loaded_into_room(ch,ch->in_room)){
ch->in_room = NULL;
ch->next_in_room= NULL;
ch->on = NULL; // sanity check!
return;
};
}
if( ( obj = get_eq_char( ch, WEAR_LIGHT ) ) != NULL
&& obj->item_type == ITEM_LIGHT
&& obj->value[2] != 0
&& ch->in_room->light > 0 )
{
--ch->in_room->light;
}
if( ch == ch->in_room->people )
{
ch->in_room->people = ch->next_in_room;
}
else
{
char_data *prev;
for( prev = ch->in_room->people; prev; prev = prev->next_in_room )
{
if( prev->next_in_room == ch )
{
prev->next_in_room = ch->next_in_room;
break;
}
}
if( prev == NULL ){
bugf( "Char_from_room: ch not found!!! ch->name=%s, "
"ch->in_room->vnum=%d", ch->name, ch->in_room->vnum);
}
}
ch->in_room = NULL;
ch->next_in_room = NULL;
ch->on = NULL; // sanity check!
return;
}
/**************************************************************************/
// Move a char into a room.
void char_to_room( char_data *ch, ROOM_INDEX_DATA *pRoomIndex )
{
OBJ_DATA *obj;
if( pRoomIndex == NULL )
{
ROOM_INDEX_DATA *room;
bugf( "Char_to_room: pRoomIndex==NULL!, get_room_index()'s last failed call was for vnum %d.", DEBUG_LAST_NON_EXISTING_REQUESTED_ROOM_VNUM);
ch->printlnf("Bug in Char_to_room(): pRoomIndex==NULL!...`1`1"
"Last unfound requested room vnum was %d.`1"
"Please report this to the admin.",
DEBUG_LAST_NON_EXISTING_REQUESTED_ROOM_VNUM);
if((room = get_room_index(ROOM_VNUM_OOC)) != NULL){
ch->println("Taking you to the OOC rooms");
char_to_room(ch,room);
}
return;
}
ch->in_room = pRoomIndex;
ch->next_in_room = pRoomIndex->people;
pRoomIndex->people = ch;
if( !IS_NPC(ch) )
{
if(ch->in_room->area->empty)
{
ch->in_room->area->empty = false;
ch->in_room->area->age = 0;
}
ch->in_room->area->nplayer++;
}
ch->in_room->number_in_room++;
if( ( obj = get_eq_char( ch, WEAR_LIGHT ) ) != NULL
&& obj->item_type == ITEM_LIGHT
&& obj->value[2] != 0 )
++ch->in_room->light;
if(IS_AFFECTED(ch,AFF_PLAGUE))
{
AFFECT_DATA *af, plague;
char_data *vch;
for( af = ch->affected; af != NULL; af = af->next )
{
if(af->type == gsn_plague)
break;
}
if(af == NULL)
{
REMOVE_BIT(ch->affected_by,AFF_PLAGUE);
return;
}
if(af->level == 1)
return;
plague.where = WHERE_AFFECTS;
plague.type = gsn_plague;
plague.level = af->level - 1;
plague.duration = number_range(1,2 * plague.level);
plague.location = APPLY_ST;
plague.modifier = -5;
plague.bitvector = AFF_PLAGUE;
for( vch = ch->in_room->people; vch != NULL; vch = vch->next_in_room)
{
if(!saves_spell(plague.level - 2,vch,DAM_DISEASE)
&& !IS_IMMORTAL(vch) &&
!IS_AFFECTED(vch,AFF_PLAGUE) && number_bits(6) == 0)
{
vch->println("You feel hot and feverish.");
act("$n shivers and looks very ill.",vch,NULL,NULL,TO_ROOM);
affect_join(vch,&plague);
}
}
}
// MSP Check
if( !IS_NPC( ch )) {
if( CAN_HEAR_MSP( ch ) && !IS_NULLSTR(ch->in_room->msp_sound))
{
msp_to_room(MSPT_ROOM, ch->in_room->msp_sound,
0,
ch,
true,
false);
}
}
if( !IS_NPC( ch )) {
if( IS_SET( ch->in_room->affected_by, ROOMAFF_ALARM ))
{
if( !IS_NULLSTR( ch->in_room->alarm->name )
&& ch->in_room->alarm != ch )
{
ch->in_room->alarm->println("Your alarm has been triggered!");
REMOVE_BIT( ch->in_room->affected_by, ROOMAFF_ALARM );
ch->in_room->alarm = NULL;
}
}
}
return;
}
/**************************************************************************/
// Give an obj to a char.
void obj_to_char( OBJ_DATA *obj, char_data *ch )
{
obj->next_content = ch->carrying;
ch->carrying = obj;
obj->carried_by = ch;
obj->in_room = NULL;
obj->in_obj = NULL;
ch->carry_number += get_obj_number( obj );
ch->carry_weight += get_obj_weight( obj );
}
/**************************************************************************/
// Take an obj from its character.
void obj_from_char( OBJ_DATA *obj )
{
char_data *ch;
if( ( ch = obj->carried_by ) == NULL )
{
bug("Obj_from_char: null ch.");
return;
}
if( obj->wear_loc != WEAR_NONE )
{
unequip_char( ch, obj );
}
if( ch->carrying == obj )
{
ch->carrying = obj->next_content;
}
else
{
OBJ_DATA *prev;
for( prev = ch->carrying; prev != NULL; prev = prev->next_content )
{
if( prev->next_content == obj )
{
prev->next_content = obj->next_content;
break;
}
}
if( prev == NULL )
{
bug("Obj_from_char: obj not in list.");
}
}
obj->carried_by = NULL;
obj->next_content = NULL;
ch->carry_number -= get_obj_number( obj );
ch->carry_weight -= get_obj_weight( obj );
return;
}
/**************************************************************************/
// Find the ac value of an obj, including position effect.
int apply_ac( OBJ_DATA *obj, int /*iWear*/, int type )
{
if( obj->item_type != ITEM_ARMOR ){
return 0;
}
return obj->value[type];
}
/**************************************************************************/
// Find a piece of eq on a character.
OBJ_DATA *get_eq_char( char_data *ch, int iWear )
{
OBJ_DATA *obj;
if(ch == NULL)
return NULL;
for( obj = ch->carrying; obj != NULL; obj = obj->next_content )
{
if( obj->wear_loc == iWear )
return obj;
}
return NULL;
}
/**************************************************************************/
// Equip a char with an obj.
void equip_char( char_data *ch, OBJ_DATA *obj, int iWear )
{
AFFECT_DATA *paf;
int i, ac_amount;
if( get_eq_char( ch, iWear ) != NULL )
{
bug("Equip_char: already equipped");
bugf("(on mob %d, room=%d, object=%d, worn: %s).",
(IS_NPC(ch)?(ch->pIndexData->vnum) :0),
ch->in_room_vnum(), obj->pIndexData->vnum,
flag_string(wear_location_strings_types, iWear));
bugf("(%s, %s).", ch->short_descr, obj->short_descr);
return;
}
if( !IS_IMMORTAL( ch ) && !(IS_NPC(ch) &&
(IS_SET(ch->act,ACT_NOALIGN) || IS_SET(ch->act2,ACT2_NO_TENDENCY))
)
){
if( ( IS_OBJ_STAT(obj, OBJEXTRA_ANTI_EVIL) && IS_EVIL(ch) )
|| ( IS_OBJ_STAT(obj, OBJEXTRA_ANTI_GOOD) && IS_GOOD(ch) )
|| ( IS_OBJ_STAT(obj, OBJEXTRA_ANTI_NEUTRAL) && IS_NEUTRAL(ch) )
|| ( IS_OBJ2_STAT(obj, OBJEXTRA2_ANTI_CHAOS) && IS_TEND_CHAOTIC(ch) )
|| ( IS_OBJ2_STAT(obj, OBJEXTRA2_ANTI_LAW) && IS_TEND_LAWFUL(ch) )
|| ( IS_OBJ2_STAT(obj, OBJEXTRA2_ANTI_BALANCE) && IS_TEND_NEUTRAL(ch) ) )
{
act( "You are zapped by $p and drop it.", ch, obj, NULL, TO_CHAR );
act( "$n is zapped by $p and drops it.", ch, obj, NULL, TO_ROOM );
obj_from_char( obj );
obj_to_room( obj, ch->in_room );
return;
}
}
// apply the armour class
for(i = 0; i < 4; i++)
{
ac_amount = apply_ac( obj, iWear, i );
if(ch->level< obj->level)
ac_amount = ac_amount * ch->level/ obj->level;
ch->armor[i] -= ac_amount;
}
// record on the object where it is being worn
obj->wear_loc = iWear;
// Apply the affects of the object
// - from olc template if object doesn't have custom enchants
for(paf=OBJECT_AFFECTS(obj); paf; paf = paf->next )
{
if( paf->where==WHERE_OBJECTSPELL) {
affect_copy_to_char( ch, paf, obj->level);
}else{
if(paf->level<=ch->level){
affect_modify( ch, paf, true );
}
}
}
// check for restrictgroup based restrictions on object,
// if they exist apply them to the wearer
if(obj->pIndexData && ch->pcdata
&& HAS_CONFIG(ch,CONFIG_OBJRESTRICT)
&& ((ch->pcdata->objrestrict& obj->pIndexData->objrestrict)>0))
{
OBJRESTRICT_LIST_DATA *pr;
AFFECT_DATA aff;
int top_prority=-1;
for( pr = obj->pIndexData->restrict; pr; pr = pr->next )
{
if(IS_SET(ch->pcdata->objrestrict,(1<<pr->classgroup->bitindex))){
top_prority=UMAX(pr->priority, top_prority);
}
}
// IC object restriction system - Kal
for( pr = obj->pIndexData->restrict; pr; pr = pr->next )
{
if(top_prority!=pr->priority
|| !IS_SET(ch->pcdata->objrestrict,(1<<pr->classgroup->bitindex)))
{
continue;
}
aff.where = WHERE_RESTRICT;
aff.type = -1;
aff.level = 0;
aff.duration = -1;
aff.location = pr->affectprofile->wear_location;
aff.modifier = pr->affectprofile->wear_amount;
// can't have positive modifiers - invert them - done now in object loading
// if(aff.modifier>0){
// aff.modifier=0-aff.modifier;
// }
aff.bitvector = 0;
// do the act text
act(pr->affectprofile->wear_message,ch,obj,NULL,TO_CHAR);
// add the affect
affect_modify( ch, &aff, true );
if(top_prority==-1) // -1 means only the first affectprofile is applied
break;
}
}
// if the object is a light, change the room lighting
if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 && ch->in_room)
++ch->in_room->light;
return;
}
/**************************************************************************/
// Unequip a char with an obj.
void unequip_char( char_data *ch, OBJ_DATA *obj )
{
AFFECT_DATA *paf = NULL;
int i, ac_amount;
if( obj->wear_loc == WEAR_NONE )
{
bug("Unequip_char: already unequipped.");
return;
}
// remove the affects on the armour class
for(i = 0; i < 4; i++)
{
ac_amount = apply_ac( obj, obj->wear_loc, i );
if(ch->level< obj->level){
ac_amount = ac_amount * ch->level/ obj->level;
}
ch->armor[i] += ac_amount;
}
// record on the object that it is no longer worn
obj->wear_loc = WEAR_NONE;
// Remove the affects of the object
// - from olc template if object doesn't have custom enchants
for(paf=OBJECT_AFFECTS(obj); paf; paf = paf->next )
{
if( paf->where==WHERE_OBJECTSPELL)
{
affect_removecopy_from_char( ch, paf, obj->level );
}
else
{
if(paf->level<=ch->level){
affect_modify( ch, paf, false );
affect_check(ch,paf->where,paf->bitvector);
}
}
}
// check for restrictgroup based restrictions on object,
// if they exist remove them from the wearer
if(obj->pIndexData && ch->pcdata
&& HAS_CONFIG(ch,CONFIG_OBJRESTRICT)
&& ((ch->pcdata->objrestrict& obj->pIndexData->objrestrict)>0))
{
OBJRESTRICT_LIST_DATA *pr;
AFFECT_DATA aff;
// ch->println("equip_char(): group restriction on object match, searching...");
int top_prority=-1;
for( pr = obj->pIndexData->restrict; pr; pr = pr->next )
{
if(IS_SET(ch->pcdata->objrestrict,(1<<pr->classgroup->bitindex))){
top_prority=UMAX(pr->priority, top_prority);
}
}
for( pr = obj->pIndexData->restrict; pr; pr = pr->next )
{
if(top_prority!=pr->priority
|| !IS_SET(ch->pcdata->objrestrict,(1<<pr->classgroup->bitindex)))
{
continue;
}
// ch->println("unequip_char(): Found... removing.");
aff.where = WHERE_RESTRICT;
aff.type = -1;
aff.level = 0;
aff.duration = -1;
aff.location = pr->affectprofile->wear_location;
aff.modifier = pr->affectprofile->wear_amount;
aff.bitvector = 0;
// do the act text
act(pr->affectprofile->remove_message,ch,obj,NULL,TO_CHAR);
// remove the affect
affect_modify( ch, &aff, false);
if(top_prority==-1) // -1 means only the first affectprofile is applied
break;
}
}
// if the object is a light, change the room lighting
if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0
&& ch->in_room != NULL && ch->in_room->light > 0 ){
--ch->in_room->light;
}
return;
}
/**************************************************************************/
// Count occurrences of an obj in a list.
int count_obj_list( OBJ_INDEX_DATA *pObjIndex, OBJ_DATA *list )
{
OBJ_DATA *obj;
int nMatch;
nMatch = 0;
for( obj = list; obj != NULL; obj = obj->next_content )
{
if( obj->pIndexData == pObjIndex ){
nMatch++;
}
}
return nMatch;
}
/**************************************************************************/
// Move an obj out of a room.
void obj_from_room( OBJ_DATA *obj )
{
ROOM_INDEX_DATA *in_room;
char_data *ch;
if( ( in_room = obj->in_room ) == NULL )
{
bug("obj_from_room: NULL.");
return;
}
for(ch = in_room->people; ch != NULL; ch = ch->next_in_room)
if(ch->on == obj)
ch->on = NULL;
if( obj == in_room->contents )
{
in_room->contents = obj->next_content;
}
else
{
OBJ_DATA *prev;
for( prev = in_room->contents; prev; prev = prev->next_content )
{
if( prev->next_content == obj )
{
prev->next_content = obj->next_content;
break;
}
}
if( prev == NULL )
{
bug("Obj_from_room: obj not found.");
return;
}
}
obj->in_room = NULL;
obj->next_content = NULL;
return;
}
/**************************************************************************/
// Move an obj into a room.
void obj_to_room( OBJ_DATA *obj, ROOM_INDEX_DATA *pRoomIndex )
{
if(!pRoomIndex){
bug("obj_to_room(): NULL input pRoomIndex");
}else{
obj->next_content = pRoomIndex->contents;
pRoomIndex->contents = obj;
}
obj->in_room = pRoomIndex;
obj->carried_by = NULL;
obj->in_obj = NULL;
return;
}
/**************************************************************************/
// Move an object into an object.
void obj_to_obj( OBJ_DATA *obj, OBJ_DATA *obj_to )
{
obj->next_content = obj_to->contains;
obj_to->contains = obj;
obj->in_obj = obj_to;
obj->in_room = NULL;
obj->carried_by = NULL;
if(obj_to->pIndexData->vnum == OBJ_VNUM_PIT)
obj->cost = 0;
for( ; obj_to != NULL; obj_to = obj_to->in_obj )
{
if( obj_to->carried_by != NULL )
{
obj_to->carried_by->carry_number += get_obj_number( obj );
obj_to->carried_by->carry_weight += get_obj_weight( obj )
* WEIGHT_MULT(obj_to) / 100;
}
}
return;
}
/**************************************************************************/
// Move an object out of an object.
void obj_from_obj( OBJ_DATA *obj )
{
OBJ_DATA *obj_from;
if( ( obj_from = obj->in_obj ) == NULL )
{
bug("Obj_from_obj: null obj_from.");
return;
}
if( obj == obj_from->contains )
{
obj_from->contains = obj->next_content;
}
else
{
OBJ_DATA *prev;
for( prev = obj_from->contains; prev; prev = prev->next_content )
{
if( prev->next_content == obj )
{
prev->next_content = obj->next_content;
break;
}
}
if( prev == NULL )
{
bug("Obj_from_obj: obj not found.");
return;
}
}
obj->next_content = NULL;
obj->in_obj = NULL;
for( ; obj_from != NULL; obj_from = obj_from->in_obj )
{
if( obj_from->carried_by != NULL )
{
obj_from->carried_by->carry_number -= get_obj_number( obj );
obj_from->carried_by->carry_weight -= get_obj_weight( obj )
* WEIGHT_MULT(obj_from) / 100;
}
}
return;
}
/**************************************************************************/
// Extract an obj from the world.
void extract_obj( OBJ_DATA *obj )
{
OBJ_DATA *obj_content;
OBJ_DATA *obj_next;
if( obj->in_room != NULL )
obj_from_room( obj );
else if ( obj->carried_by != NULL )
obj_from_char( obj );
else if ( obj->in_obj != NULL )
obj_from_obj( obj );
for( obj_content = obj->contains; obj_content; obj_content = obj_next )
{
obj_next = obj_content->next_content;
extract_obj( obj_content );
}
if( object_list == obj )
{
object_list = obj->next;
}
else
{
OBJ_DATA *prev;
for( prev = object_list; prev != NULL; prev = prev->next )
{
if( prev->next == obj )
{
prev->next = obj->next;
break;
}
}
if( prev == NULL ){
if(obj->pIndexData){
bugf( "Extract_obj: obj %d not found.", obj->pIndexData->vnum );
}else{
bug("Extract_obj: obj ??? not found. (no pIndexData on it!)");
}
return;
}
}
if(obj->pIndexData)
obj->pIndexData->count--;
free_obj(obj);
return;
}
/**************************************************************************/
void duel_logout(char_data *ch);
/**************************************************************************/
void extract_char_from_char_list(char_data *ch)
{
if( ch == char_list ){
char_list = ch->next;
}else{
char_data *prev;
for( prev = char_list; prev != NULL; prev = prev->next )
{
if( prev->next == ch )
{
prev->next = ch->next;
break;
}
}
if( prev == NULL )
{
bug("extract_char_from_char_list(): char not found.");
return;
}
}
}
/**************************************************************************/
// Extract a char from the world - does NOT handle any pfile saving
void extract_char( char_data *ch, bool fPull )
{
char_data *wch;
OBJ_DATA *obj;
OBJ_DATA *obj_next;
ROOM_INDEX_DATA *location;
ROOM_INDEX_DATA *recount_in_room;
vn_int recall_vnum;
if( ch->in_room == NULL )
{
bug("Extract_char: ch->in_room == NULL.");
return;
}
// ploaded players can't be extracted using extract_char()
if(ch->pload && ch->pload->dont_save){
if(ch->pload->loaded_by){
ch->pload->loaded_by->printlnf( "extract_char(): character %s can't be extracted cause it was ploaded.", ch->name);
}
logf( "extract_char(): character %s can't be extracted cause it was ploaded.", ch->name);
return;
}
nuke_pets(ch);
ch->pet = NULL; // just in case
if( fPull )
{
die_follower( ch );
}
stop_fighting( ch, true );
for( obj = ch->carrying; obj != NULL; obj = obj_next )
{
obj_next = obj->next_content;
if(obj->item_type == ITEM_TOKEN &&
!IS_SET ( obj->value[0], TOKEN_DROPDEATH )) {
continue;
}
extract_obj( obj );
}
recount_in_room=ch->in_room;
char_from_room( ch );
ch->mpqueue_dequeue_all();
// Death room is set in the clan table now
if( !fPull ) // was a player that died - therefore not removed from the game
{
if(GAMESETTING5(GAMESET5_DEDICATED_PKILL_STYLE_MUD)){
recall_vnum =ROOM_PKILLPORT_DEATH_ROOM; // pkill port hack
}else{
recall_vnum = race_table[ch->race]->death_room;
}
if( ( location = get_room_index( recall_vnum ) ) == NULL )
{
ch->println("You feel totally disorientated.");
if( ( location = get_room_index( ROOM_VNUM_OOC ) ) == NULL)
{
ch->printf("BUG: Cant find the main ooc room (vnum = %d)\r\n"
"Please report this to an admin.\r\n", ROOM_VNUM_OOC);
return;
}
else
{
if(IS_SET(location->room_flags, ROOM_OOC))
{
ch->println("Taking you to the main OOC room since your normal death room doesn't exist.");
}
else
{
ch->printf("BUG: Taking you to the main ooc room (vnum = %d)\r\n"
"This room SHOULD be an OOC room - please report this bug to an admin.\r\n", ROOM_VNUM_OOC);
}
}
}
char_to_room(ch,location);
return;
}
// extracting characters cause they are quiting
if( IS_NPC(ch) ){
ch->pIndexData->count--;
}else{
// record the characters logout time
laston_logout(ch);
// 'logout' any duels
duel_logout(ch);
}
// in switched mobs
if( ch->desc != NULL && ch->desc->original != NULL )
{
do_return( ch, "" );
ch->desc = NULL;
}
// break off all those that reply, retell or mprog target memories
for( wch = char_list; wch; wch = wch->next )
{
if( wch->reply == ch )
wch->reply = NULL;
if( wch->retell == ch )
wch->retell = NULL;
if( wch->mprog_target == ch )
wch->mprog_target = NULL;
}
// take the mob/player out of the character list
// unless they are a ploaded player or pet of a ploaded player
if(!ch->pload){
extract_char_from_char_list(ch);
}
if(!IS_NPC(ch) )
{
if( ch == player_list )
{
player_list = ch->next_player;
}
else
{
char_data *prev;
for(prev = player_list; prev != NULL; prev = prev->next_player )
{
if(prev->next_player == ch)
{
prev->next_player = ch->next_player;
break;
}
}
}
// if they ploaded someone, extract the ploaded character
// should really automatically unload the loaded person
if(ch->ploaded){
assert(ch->ploaded->pload && ch->ploaded->pload->loaded_by==ch); // if we link to them, they should link to us
pload_extract(ch, ch->ploaded);
}
}
if( ch->desc != NULL ){
ch->desc->character = NULL;
}
free_char( ch );
// recount the number of people in the room they just logged out of
if(recount_in_room){
int rcount=0;
ch=recount_in_room->people;
while(ch){
rcount++;
ch=ch->next_in_room;
}
recount_in_room->number_in_room=rcount;
}
return;
}
/**************************************************************************/
/*
* Find a char in the room.
* major bits rewritten by Kalahn - March 98
*/
char_data *get_char_room( char_data *ch, char *argument )
{
char arg[MIL];
char_data *rch;
int number;
int count;
number = number_argument( argument, arg );
if( !str_cmp( arg, "self" ) )
return ch;
// support UID
int uid=get_uid(arg);
if(uid){
for( rch = ch->in_room->people; rch; rch = rch->next_in_room )
{
if(uid==rch->uid){
if(can_see( ch, rch )){
return rch;
}else{
return NULL;
}
}
}
return NULL;
}
// first do a name match and exact short descript match on mobs
count = 0;
for( rch = ch->in_room->people; rch != NULL; rch = rch->next_in_room )
{
if( !can_see( ch, rch ))
continue;
if(!( is_name( arg, rch->name )
|| (IS_NPC(rch) && rch!=ch && is_name( arg, strip_colour(rch->short_descr)) )
))
continue;
if( rch==ch && HAS_AUTOSELF(ch))
continue;
if( ++count == number )
return rch;
}
// now do a name and exact short descript match on everyone
count = 0;
for( rch = ch->in_room->people; rch != NULL; rch = rch->next_in_room )
{
if( !can_see( ch, rch ))
continue;
if(!(rch!=ch && is_name( arg, strip_colour(rch->short_descr))) )
continue;
if( rch==ch && HAS_AUTOSELF(ch))
continue;
if( ++count == number )
return rch;
}
return NULL;
}
/**************************************************************************/
// Find a pet in the room.
char_data *get_pet_room( char_data *ch, char *argument )
{
char arg[MIL];
char_data *rch;
int number;
int count;
number = number_argument( argument, arg );
count = 0;
if( !str_cmp( arg, "self" ) )
return ch;
for( rch = ch->in_room->people; rch != NULL; rch = rch->next_in_room )
{
if( !can_see( ch, rch ))
continue;
// pets only
if( !IS_NPC(rch) || !IS_SET(rch->act, ACT_PET) )
continue;
if(!( is_name( arg, rch->name )
|| (rch!=ch && is_name( arg, strip_colour(rch->short_descr)) )
))
continue;
/*
if( !can_see( ch, rch ) ||
(!is_name( arg, rch->name ) && (!is_name( arg, strip_colour(rch->short_descr)) ) ) )
continue;
*/
if(!IS_NPC(rch))
continue;
if( rch==ch && HAS_AUTOSELF(ch))
continue;
if( ++count == number )
return rch;
}
return NULL;
}
/**************************************************************************/
// Find a char in the world.
char_data *get_char_world( char_data *ch, char *argument )
{
char arg[MIL];
char_data *wch;
int number;
int count;
number = number_argument( argument, arg );
count = 0;
if( !str_cmp( arg, "self" ) )
return ch;
// support UID
int uid=get_uid(arg);
if(uid){
for( wch = char_list; wch ; wch = wch->next ){
if(uid==wch->uid){
if(wch->in_room && can_see( ch, wch )){
return wch;
}else{
return NULL;
}
}
}
return NULL;
}
// first check for an exact name match through
// the whole world -- for PCs only
for( wch = char_list; wch ; wch = wch->next ){
if( wch->in_room == NULL || !can_see( ch, wch )
|| str_cmp(arg, wch->name) || IS_NPC(wch))
continue;
if( wch==ch && HAS_AUTOSELF(ch))
continue;
if( ++count == number )
return wch;
}
if( ( wch = get_char_room( ch, argument ) ) != NULL )
return wch;
count = 0;
for( wch = char_list; wch != NULL ; wch = wch->next )
{
if(!wch->in_room)
continue;
if(!can_see(ch,wch))
continue;
if(!( is_name( arg, wch->name )
|| (wch!=ch && is_name( arg, strip_colour(wch->short_descr)) )
))
continue;
if( wch==ch && HAS_AUTOSELF(ch))
continue;
if( ++count == number )
return wch;
}
return NULL;
}
/**************************************************************************/
// Find a char in the ic world. (ie non ooc, or olconly areas)
char_data *get_char_icworld( char_data *ch, char *argument )
{
char arg[MIL];
char_data *wch;
int number;
int count;
number = number_argument( argument, arg );
count = 0;
if( !str_cmp( arg, "self" ) )
return ch;
// support UID
int uid=get_uid(arg);
if(uid){
for( wch = char_list; wch ; wch = wch->next ){
if(uid==wch->uid){
if(wch->in_room && can_see( ch, wch ) && IS_IC(wch)){
return wch;
}else{
return NULL;
}
}
}
return NULL;
}
/* first check for an exact name match through
* the whole world -- for PCs only */
for( wch = char_list; wch != NULL ; wch = wch->next )
{
if( wch->in_room == NULL || !can_see( ch, wch )
|| str_cmp(arg, wch->name) || IS_NPC(wch))
continue;
if(!IS_IC(wch))
continue;
if( wch==ch && HAS_AUTOSELF(ch))
continue;
if( ++count == number )
return wch;
}
if( ( wch = get_char_room( ch, argument ) ) != NULL )
return wch;
count = 0;
for( wch = char_list; wch != NULL ; wch = wch->next )
{
if(!wch->in_room)
continue;
if(!can_see(ch,wch))
continue;
if(!( is_name( arg, wch->name )
|| (wch!=ch && is_name( arg, strip_colour(wch->short_descr)) )
))
continue;
if( wch==ch && HAS_AUTOSELF(ch))
continue;
if(!IS_IC(wch))
continue;
if( ++count == number )
return wch;
}
return NULL;
}
/**************************************************************************/
/*
* Find a whovis char in the world.
* - called from do_tell, do_requestooc, do_dlook etc
* - will never return a mob
*/
char_data *get_whovis_player_world( char_data *ch, char *argument )
{
char arg[MIL];
char_data *wch;
int number;
int count;
number = number_argument( argument, arg );
if( !str_cmp( arg, "self" ) )
return ch;
// support UID
int uid=get_uid(arg);
if(uid){
for( wch = player_list; wch; wch = wch->next_player )
{
if(uid==wch->uid){
if(can_see_who(ch,wch)){
return wch;
}else{
return NULL;
}
}
}
return NULL;
}
// first check uid combined with for an exact name match whole world - player list
count = 0;
for( wch = player_list; wch; wch = wch->next_player )
{
// must be able to see the player on the who list
if(!can_see_who(ch,wch))
continue;
if(uid){ // searching by uid
if(uid==wch->uid){
return wch;
}
}else{
// must have an exactly matching name
if(str_cmp(arg, wch->name))
continue;
if(wch==ch && HAS_AUTOSELF(ch))
continue;
if( ++count == number )
return wch;
}
}
// next check for substring match on
// name in same room
count = 0;
for( wch = player_list; wch; wch = wch->next_player )
{
// must be in the same room
if( wch->in_room != ch->in_room)
continue;
// must be able to see the player on the who list
if(!can_see_who(ch,wch))
continue;
// must have a substring matching short or name
if(!is_name( arg, wch->name))
continue;
//autoself
if(wch==ch && HAS_AUTOSELF(ch))
continue;
if( ++count == number )
return wch;
}
// next check for substring match on
// player name anywhere in the game
count = 0;
for( wch = player_list; wch; wch = wch->next_player )
{
// must be able to see the player on the who list
if(!can_see_who(ch,wch))
continue;
// must have a substring matching short or name
if(!is_name( arg, wch->name))
continue;
//autoself
if(wch==ch && HAS_AUTOSELF(ch))
continue;
if( ++count == number )
return wch;
}
// next check for substring match on short
// description or name in same room
count = 0;
for( wch = player_list; wch; wch = wch->next_player )
{
// must be in the same room
if( wch->in_room != ch->in_room)
continue;
// must be able to see the player on the who list
if(!can_see_who(ch,wch))
continue;
// must have a substring matching short or name
if(!is_name( arg, strip_colour(wch->short_descr))
&& !is_name( arg, wch->name))
continue;
//autoself
if(wch==ch && HAS_AUTOSELF(ch))
continue;
if( ++count == number )
return wch;
}
// lastly check for substring match on short
// description or name anywhere in the game
count = 0;
for( wch = player_list; wch; wch = wch->next_player )
{
// must be able to see the player on the who list
if(!can_see_who(ch,wch))
continue;
// must have a substring matching short or name
if(!is_name( arg, strip_colour(wch->short_descr))
&& !is_name( arg, wch->name))
continue;
//autoself
if(wch==ch && HAS_AUTOSELF(ch))
continue;
if( ++count == number )
return wch;
}
// very very lastly check for a ploaded player in the same room as you
wch=pload_find_player_by_name(arg);
if(wch){
return wch;
}
return NULL;
}
/**************************************************************************/
/*
* Find some object with a given index data.
* Used by area-reset 'P' command.
*/
OBJ_DATA *get_obj_type( OBJ_INDEX_DATA *pObjIndex )
{
OBJ_DATA *obj;
for( obj = object_list; obj; obj = obj->next )
{
if( obj->pIndexData == pObjIndex )
return obj;
}
return NULL;
}
/**************************************************************************/
// will return a particular object in a chain, based on its pObjIndex
OBJ_DATA *get_obj_in_chain_recursive_by_index(OBJ_DATA *startobj, OBJ_INDEX_DATA *pObjIndex)
{
OBJ_DATA *result;
for( ; startobj; startobj= startobj->next_content )
{
if(startobj->pIndexData==pObjIndex){
return startobj;
}
if(startobj->contains){
// search deeper if necessary
result=get_obj_in_chain_recursive_by_index(startobj->contains, pObjIndex);
if(result){
return result;
}
}
}
return NULL;
}
/**************************************************************************/
/*
* Find some object with a given index data in the room.
* Used by area-reset 'P' command.
* will never return an object carried by a player
*/
OBJ_DATA *get_obj_of_type_in_room( OBJ_INDEX_DATA *pObjIndex, ROOM_INDEX_DATA *room )
{
OBJ_DATA *obj;
// first search all objects on the floor
obj=get_obj_in_chain_recursive_by_index(room->contents, pObjIndex);
if(obj){
return obj;
}
// now search thru all objects carried by mobs in the room
for(char_data *mob=room->people; mob; mob=mob->next_in_room){
obj=get_obj_in_chain_recursive_by_index(mob->carrying, pObjIndex);
if(obj){
return obj;
}
}
return NULL;
}
/**************************************************************************/
/*
* Find an obj in a list.
*/
OBJ_DATA *get_obj_list( char_data *ch, char *argument, OBJ_DATA *list )
{
char arg[MIL];
OBJ_DATA *obj;
int number;
int count;
// support UID
int uid=get_uid(argument);
if(uid){
for( obj = list; obj; obj = obj->next_content ){
if(uid==obj->uid
&& !IS_SET( obj->extra_flags, OBJEXTRA_NO_GET_ALL )
&& can_see_obj( ch, obj ) )
{
return obj;
}
}
return NULL;
}
number = number_argument( argument, arg );
count = 0;
for( obj = list; obj; obj = obj->next_content )
{
if( can_see_obj( ch, obj ) && is_name( arg, obj->name ) )
{
if( ++count == number ){
if( !IS_SET( obj->extra_flags, OBJEXTRA_NO_GET_ALL )
|| is_exact_name( arg, obj->name ) )
{
return obj;
}
}
}
}
return NULL;
}
/**************************************************************************/
// Find an obj in player's inventory, that looker can see
OBJ_DATA *get_obj_carry_for_looker( char_data *ch, char *argument, char_data *looker)
{
char arg[MIL];
OBJ_DATA *obj;
int number;
int count;
// support UID
int uid=get_uid(argument);
if(uid){
for( obj = ch->carrying; obj; obj = obj->next_content){
if(uid==obj->uid
&& obj->wear_loc == WEAR_NONE
&& can_see_obj( looker, obj ) )
{
return obj;
}
}
return NULL;
}
number = number_argument( argument, arg );
count = 0;
for( obj = ch->carrying; obj; obj = obj->next_content )
{
if( obj->wear_loc == WEAR_NONE
&& (can_see_obj( looker, obj ) )
&& is_name( arg, obj->name ) )
{
if( ++count == number )
return obj;
}
}
return NULL;
}
/**************************************************************************/
// Find an obj in player's inventory or an item they are holding
OBJ_DATA *get_obj_carry( char_data *ch, char *argument )
{
char arg[MIL];
OBJ_DATA *obj;
int number;
int count;
// support UID
int uid=get_uid(argument);
if(uid){
for( obj = ch->carrying; obj; obj = obj->next_content){
if(uid==obj->uid
&& (obj->wear_loc == WEAR_NONE
|| obj->wear_loc == WEAR_HOLD)
&& can_see_obj( ch, obj ) )
{
return obj;
}
}
return NULL;
}
number = number_argument( argument, arg );
count = 0;
for( obj = ch->carrying; obj; obj = obj->next_content )
{
if( (obj->wear_loc == WEAR_NONE
|| obj->wear_loc == WEAR_HOLD)
&& (can_see_obj( ch, obj ) )
&& is_name( arg, obj->name ) )
{
if( ++count == number )
return obj;
}
}
return NULL;
}
/**************************************************************************/
// Find a token in player's inventory
// Regular get_obj_carry doesn't work with
// tremove since victim can't see tokens
// supports vnum and description searching
OBJ_DATA *get_obj_token( char_data *ch, char *argument )
{
char arg[MIL];
OBJ_DATA *obj;
int number;
int count;
int tok_vnum=0;
bool use_vnum=false;
number = number_argument( argument, arg );
count = 0;
if(is_number(arg)){
tok_vnum=atoi(arg);
use_vnum=true;
}
for( obj = ch->carrying; obj; obj = obj->next_content ) {
if( obj->item_type == ITEM_TOKEN)
{
if((use_vnum && obj->pIndexData && obj->pIndexData->vnum==tok_vnum)
|| (!use_vnum && is_name( arg, obj->name)) )
{
if( ++count == number ){
return obj;
}
}
}
}
return NULL;
}
/**************************************************************************/
// Find an obj in player's equipment.
OBJ_DATA *get_obj_wear( char_data *ch, char *argument )
{
char arg[MIL];
OBJ_DATA *obj;
int number;
int count;
// support UID
int uid=get_uid(argument);
if(uid){
for( obj = ch->carrying; obj; obj = obj->next_content){
if(uid==obj->uid
&& obj->wear_loc != WEAR_NONE
&& can_see_obj( ch, obj ) )
{
return obj;
}
}
return NULL;
}
number = number_argument( argument, arg );
count = 0;
for( obj = ch->carrying; obj; obj = obj->next_content )
{
if( obj->wear_loc != WEAR_NONE
&& can_see_obj( ch, obj )
&& is_name( arg, obj->name ) )
{
if( ++count == number )
return obj;
}
}
return NULL;
}
/**************************************************************************/
// Find an obj in the room or in inventory.
// supports uid
OBJ_DATA *get_obj_here( char_data *ch, char *argument )
{
OBJ_DATA *obj;
bool free_required=false;
if( is_number( argument )){ // vnum support
OBJ_INDEX_DATA *pObj;
if(( pObj = get_obj_index( atoi( argument ))) == NULL ){
return NULL;
}
argument = str_dup( pObj->name );
free_required=true;
}
// locker support
if(!str_prefix("locker",argument)){
int locker_num=atoi(argument+6);
if(free_required){
free_string(argument);
}
return lockers->find_locker_object(ch, true, locker_num);
}
// get_obj_list() supports uid
obj = get_obj_list( ch, argument, ch->in_room->contents );
if(obj){
if(free_required){
free_string(argument);
}
return obj;
}
// get_obj_list() supports uid
obj = get_obj_carry( ch, argument );
if(obj){
if(free_required){
free_string(argument);
}
return obj;
}
// get_obj_list() supports uid
obj = get_obj_wear( ch, argument );
if(obj){
if(free_required){
free_string(argument);
}
return obj;
}
// locker abbreviated support for your own locker
if(!str_prefix("loc",argument)){
if(free_required){
free_string(argument);
}
return lockers->find_locker_object(ch, true, 0);
}
if(free_required){
free_string(argument);
}
return NULL;
}
/**************************************************************************/
// Find an obj in the world.
OBJ_DATA *get_obj_world( char_data *ch, char *argument )
{
char arg[MIL];
OBJ_DATA *obj;
int number;
int count;
// get_obj_here() supports uid
if( ( obj = get_obj_here( ch, argument ) ) != NULL )
return obj;
// support UID
int uid=get_uid(argument);
if(uid){
for( obj = object_list; obj; obj = obj->next ){
if(uid==obj->uid
&& can_see_obj( ch, obj ) )
{
return obj;
}
}
return NULL;
}
number = number_argument( argument, arg );
count = 0;
for( obj = object_list; obj; obj = obj->next )
{
if( can_see_obj( ch, obj ) && is_name( arg, obj->name ) )
{
if( ++count == number )
return obj;
}
}
return NULL;
}
/**************************************************************************/
// deduct cost from a character
void deduct_cost(char_data *ch, int cost)
{
int silver = 0, gold = 0;
silver = UMIN(ch->silver,cost);
if(silver<cost){
gold = ((cost - silver + 99) / 100);
silver = cost - 100 * gold;
}
ch->gold -= gold;
ch->silver -= silver;
if(ch->gold < 0){
bugf("deduct costs: gold %d < 0", (int)ch->gold);
ch->gold = 0;
}
if(ch->silver<0){
bugf("deduct costs: silver %d < 0", (int)ch->silver);
ch->silver = 0;
}
}
/**************************************************************************/
// Create a 'money' obj.
OBJ_DATA *create_money( int gold, int silver )
{
char buf[MSL];
OBJ_DATA *obj;
if( gold < 0 || silver < 0 || (gold == 0 && silver == 0) )
{
bug( "Create_money: zero or negative money.");
gold = UMAX(1,gold);
silver = UMAX(1,silver);
}
if(gold == 0 && silver == 1)
{
obj = create_object( get_obj_index( OBJ_VNUM_SILVER_ONE ));
}
else if (gold == 1 && silver == 0)
{
obj = create_object( get_obj_index( OBJ_VNUM_GOLD_ONE));
}
else if (silver == 0)
{
obj = create_object( get_obj_index( OBJ_VNUM_GOLD_SOME ));
sprintf( buf, obj->short_descr, gold );
free_string( obj->short_descr );
obj->short_descr = str_dup( buf );
obj->value[1] = gold;
obj->cost = gold;
obj->weight = gold/5;
}
else if (gold == 0)
{
obj = create_object( get_obj_index( OBJ_VNUM_SILVER_SOME ));
sprintf( buf, obj->short_descr, silver );
free_string( obj->short_descr );
obj->short_descr = str_dup( buf );
obj->value[0] = silver;
obj->cost = silver;
obj->weight = silver/20;
}
else
{
obj = create_object( get_obj_index( OBJ_VNUM_COINS ));
sprintf( buf, obj->short_descr, silver, gold );
free_string( obj->short_descr );
obj->short_descr = str_dup( buf );
obj->value[0] = silver;
obj->value[1] = gold;
obj->cost = 100 * gold + silver;
obj->weight = gold / 5 + silver / 20;
}
return obj;
}
/**************************************************************************/
/*
* Return # of objects which an object counts as.
* Thanks to Tony Chamberlain for the correct recursive code here.
*/
int get_obj_number( OBJ_DATA *obj )
{
int number;
if(obj->item_type == ITEM_HERB){
return 1;
}
if(obj->item_type == ITEM_CONTAINER || obj->item_type == ITEM_MONEY
|| obj->item_type == ITEM_GEM || obj->item_type == ITEM_JEWELRY
|| obj->item_type == ITEM_TOKEN ){
number = 0;
}else{
number = 10;
}
for( obj = obj->contains; obj != NULL; obj = obj->next_content )
number += get_obj_number( obj );
return number;
}
/**************************************************************************/
/*
* Return weight of an object, including weight of contents.
*/
int get_obj_weight( OBJ_DATA *obj )
{
int weight;
OBJ_DATA *tobj;
weight = obj->weight;
for( tobj = obj->contains; tobj != NULL; tobj = tobj->next_content )
weight += get_obj_weight( tobj ) * WEIGHT_MULT(obj) / 100;
return weight;
}
/**************************************************************************/
int get_true_weight(OBJ_DATA *obj)
{
int weight;
weight = obj->weight;
for( obj = obj->contains; obj != NULL; obj = obj->next_content )
weight += get_obj_weight( obj );
return weight;
}
/**************************************************************************/
// True if room is dark.
bool room_is_dark( ROOM_INDEX_DATA *pRoomIndex )
{
OBJ_DATA *obj;
if(!pRoomIndex)
return false;
if( IS_SET(pRoomIndex->affected_by, ROOMAFF_LEONIDS ))
return false;
if( IS_SET(pRoomIndex->affected_by, ROOMAFF_UTTERDARK ))
return true;
if( pRoomIndex->light > 0 )
return false;
for( obj = pRoomIndex->contents; obj; obj = obj->next_content ) {
if( obj->pIndexData->vnum == OBJ_VNUM_FIRE )
return false;
}
if( IS_SET(pRoomIndex->room_flags, ROOM_LIGHT ))
return false;
if( IS_SET(pRoomIndex->room_flags, ROOM_DARK) )
return true;
if( pRoomIndex->sector_type == SECT_INSIDE
|| pRoomIndex->sector_type == SECT_CITY )
return false;
if( weather_info[pRoomIndex->sector_type].sunlight == SUN_SET
|| weather_info[pRoomIndex->sector_type].sunlight == SUN_DARK )
return true;
return false;
}
/**************************************************************************/
// true if player is in room invite list, or their clan is.
bool player_on_rooms_invite_list(char_data *ch, ROOM_INDEX_DATA *room)
{
if(GAMESETTING4(GAMESET4_ROOM_INVITES_DISABLED)){
return false;
}
if(IS_NULLSTR(room->owner)){
return false;
}
if(IS_NULLSTR(room->invite_list)){
return false;
}
if(is_exact_name(ch->name, room->invite_list)){
return true;
}
if(ch->clan && is_exact_name(FORMATF("clan=%s", ch->clan->notename()), room->invite_list)){
return true;
}
return false;
}
/**************************************************************************/
bool is_room_owner(char_data *ch, ROOM_INDEX_DATA *room)
{
if(IS_NULLSTR(room->owner)){
return false;
}
if(is_exact_name(class_table[ch->clss].name,room->owner)){
return true;
}
if(is_exact_name("immortal",room->owner) && IS_IMMORTAL(ch)){
return true;
}
return is_exact_name(ch->name,room->owner);
}
/**************************************************************************/
// True if room is private excluding the owner question.
bool is_room_private_except_owner( ROOM_INDEX_DATA *pRoomIndex )
{
char_data *rch;
int count = 0;
for( rch = pRoomIndex->people; rch; rch = rch->next_in_room )
{
if( INVIS_LEVEL(rch)< LEVEL_IMMORTAL )
count++;
}
if( IS_SET(pRoomIndex->room_flags, ROOM_PRIVATE) && count >= 2 )
return true;
if( IS_SET(pRoomIndex->room_flags, ROOM_SOLITARY) && count >= 1 )
return true;
if( IS_SET(pRoomIndex->room_flags, ROOM_IMP_ONLY) )
return true;
return false;
}
/**************************************************************************/
// True if room is private.
bool room_is_private( ROOM_INDEX_DATA *pRoomIndex )
{
if(!IS_NULLSTR(pRoomIndex->owner)){
return true;
}
return is_room_private_except_owner( pRoomIndex );
}
/**************************************************************************/
// True if room is private to the character
bool is_room_private_to_char( ROOM_INDEX_DATA *pRoomIndex, char_data *ch )
{
if(IS_ADMIN(ch) && IS_SET(TRUE_CH(ch)->act, PLR_HOLYWALK)){
return false;
}
if(!IS_NULLSTR(pRoomIndex->owner)){
if(!is_room_owner(ch, pRoomIndex)
&& !player_on_rooms_invite_list(ch, pRoomIndex))
{
return true;
}
}
return is_room_private_except_owner( pRoomIndex );
}
/**************************************************************************/
// visibility on a room -- for entering and exits
bool can_see_room( char_data *ch, ROOM_INDEX_DATA *pRoomIndex )
{
if(IS_SET(pRoomIndex->room_flags, ROOM_IMP_ONLY)
&& get_trust(ch) < MAX_LEVEL)
return false;
if(IS_SET(pRoomIndex->room_flags, ROOM_GODS_ONLY)
&& !IS_IMMORTAL(ch))
return false;
if(IS_SET(pRoomIndex->room_flags, ROOM_HEROES_ONLY)
&& !IS_IMMORTAL(ch))
return false;
if(IS_SET(pRoomIndex->room_flags,ROOM_NEWBIES_ONLY)
&& ch->level > 5 && !IS_IMMORTAL(ch))
return false;
if(!IS_IMMORTAL(ch) && pRoomIndex->clan && ch->clan != pRoomIndex->clan)
return false;
// unswitched_mobs can't see ooc rooms
if(IS_UNSWITCHED_MOB(ch)
&& IS_SET((ch)->in_room->room_flags, ROOM_OOC))
return false;
// mortal players that arent building can't see olconly areas
if(!IS_NPC(ch) && !IS_IMMORTAL(ch) && !IS_SET(ch->comm,COMM_BUILDING)
&& IS_OLCAREA(pRoomIndex->area))
return false;
// mortal builders can't see ic areas
if(!IS_NPC(ch) && !IS_IMMORTAL(ch) && IS_SET(ch->comm,COMM_BUILDING)
&& !IS_SET((ch)->in_room->room_flags, ROOM_OOC)
&& !IS_SET((ch)->in_room->area->area_flags, AREA_OLCONLY)
)
return false;
return true;
}
/**************************************************************************/
// True if looker can see victim - (ch is doing the looking)
bool can_see( char_data *looker, char_data *victim )
{
if( looker == victim )
return true;
// support the inroom command
if(IS_SET(looker->dyn,DYN_IN_ROOM_ONLY) && looker->in_room!=victim->in_room)
{
return false;
}
// mortals can't see ploaded players
if(victim->pload && !IS_IMMORTAL(looker)){
return false;
}
// support for the 'mob seeall' command
if(IS_SET(looker->dyn,DYN_MOB_SEE_ALL)
&& INVIS_LEVEL(victim)<=LEVEL_HERO)
{
return true;
}
if( get_trust(looker) < INVIS_LEVEL(victim))
return false;
if(IS_IMMORTAL(victim)
&& get_trust(looker) < victim->incog_level
&& looker->in_room != victim->in_room)
return false;
if( !IS_NPC(looker) && IS_NPC(victim)
&& IS_SET(victim->act, ACT_IS_UNSEEN)
&& !HAS_HOLYLIGHT(looker))
return false;
if( IS_OOC(victim))
return true;
if( HAS_HOLYLIGHT(looker))
return true;
if( IS_SET( victim->affected_by2, AFF2_TREEFORM )
&& !IS_AFFECTED2(looker, AFF2_DETECT_TREEFORM ) )
return false;
if( IS_SET( victim->affected_by2, AFF2_VANISH )
&& !(get_skill(looker,gsn_vanish) > 0)
&& !IS_AFFECTED2(looker, AFF2_DETECT_VANISH))
return false;
if( IS_AFFECTED(looker, AFF_BLIND) )
return false;
if( room_is_dark( looker->in_room ) &&
(!IS_AFFECTED(looker, AFF_DARK_VISION) ||
!IS_AFFECTED(looker, AFF_INFRARED)) )
return false;
if( IS_AFFECTED(victim, AFF_INVISIBLE)
&& !IS_AFFECTED(looker, AFF_DETECT_INVIS ))
return false;
if(!(IS_NPC(looker) && IS_AFFECTED(looker, AFF_DETECT_HIDDEN)) )
{
// hidden - chance of not being seen
// The higher 'chance' is the less chance of the looker (looker)
// seeing the hiding mob/player (victim).
if(IS_AFFECTED(victim, AFF_HIDE)
&& !is_same_group( looker, victim)
&& victim->fighting == NULL)
{
int chance; // chance of not being seen
OBJ_DATA *obj;
chance = UMAX(90,get_skill(victim,gsn_hide));
chance += victim->level/5;
chance -= looker->level/3;
chance += (victim->level- looker->level)/3;
chance -= IS_NPC(looker)?0:looker->pcdata->tired*2;
chance -= looker->modifiers[STAT_AG];
chance -= looker->modifiers[STAT_QU];
chance += victim->modifiers[STAT_IN];
chance += victim->modifiers[STAT_PR];
chance -= get_skill(looker,gsn_hide)/10;
if(room_is_dark( victim->in_room ) &&
!IS_AFFECTED(looker, AFF_DARK_VISION)){
obj= get_eq_char( victim, WEAR_LIGHT );
if( obj && obj->item_type == ITEM_LIGHT
&& obj->value[2] != 0 ){
chance -= 50; // hider has light
// looker lookereck
obj= get_eq_char( looker, WEAR_LIGHT );
if( obj && obj->item_type == ITEM_LIGHT
&& obj->value[2] != 0 ){
chance += 15; // both have light
}else{
chance -= 50; // hider only has light
}
}else{
chance += 10; // hider doesn't have light
// looker lookereck
obj= get_eq_char( looker, WEAR_LIGHT );
if( obj && obj->item_type == ITEM_LIGHT
&& obj->value[2] != 0 ){
chance -= 50; // looker only has light
}else{
chance += 50; // neither have light
}
}
}
chance = UMAX(chance,1);
if(IS_AFFECTED(looker, AFF_DETECT_HIDDEN)){
chance/=4;
}else{
chance*=2;
}
// spamming look to see someone results in you having
// less chance of seeing them
if(looker->desc && looker->desc->repeat>3)
{
chance+=5;
chance*=8;
}else{
chance = URANGE(5,chance,99);
}
if(chance-10 >number_percent())
return false;
}
}
return true;
}
/**************************************************************************/
// True if looker can see victim on the wholist.
bool can_see_who( char_data *looker, char_data *victim )
{
if(!looker || !victim){
bug("can_see_who - null looker or victim");
make_corefile();
return false;
}
// mobs arent on who
if( IS_NPC(victim) ){
return false;
}
// can always see yourself on who
if( looker == victim ){
return true;
}
// support for the 'mob seeall' command
if(IS_SET(looker->dyn,DYN_MOB_SEE_ALL)){
return true;
}
// mortals see imms based sololy on the status of whovis
// whovis has no affect on characters less than LEVEL_IMMORTAL
if(!IS_IMMORTAL(looker) && victim->level>LEVEL_HERO){
if(IS_SET(victim->comm, COMM_WHOVIS))
return true;
else
return false;
}
// newbies can always see newbie support on who
if(IS_NEWBIE_SUPPORT(victim) && IS_NEWBIE(looker)){
return true;
}
#ifdef unix
// mortals can't see someone for 60 seconds after
// they logon and haven't talked on ooc
if(!IS_IMMORTAL(looker) && !IS_NPC(looker) && ((current_time - 60) < victim->logon) )
{
if(!IS_NPC(victim) && !victim->pcdata->did_ooc && looker!=victim)
return false;
}
#endif
// incog only works for imms in an IC sense
if(IS_IMMORTAL(victim) && get_trust(looker) < victim->incog_level
&& looker->in_room != victim->in_room){
return false;
}
// support the inroom command
if(IS_SET(looker->dyn,DYN_IN_ROOM_ONLY) && looker->in_room!=victim->in_room){
return false;
}
// trust check
if( get_trust(looker) < INVIS_LEVEL(victim)){
return false;
}
return true;
}
/**************************************************************************/
ROOM_INDEX_DATA *room_object_is_in(obj_data *obj)
{
if(obj->in_room){
return obj->in_room;
}else if(obj->carried_by){
return obj->carried_by->in_room;
}else if(obj->in_obj){
return room_object_is_in(obj->in_obj);
}
bugf("room_object_is_in(): couldn't find the room which object '%s' was in!\r\n",
obj->name);
return NULL;
}
/**************************************************************************/
// True if char can see obj.
bool can_see_obj( char_data *ch, obj_data *obj )
{
// support the inroom command
if(IS_SET(ch->dyn,DYN_IN_ROOM_ONLY) && ch->in_room!=room_object_is_in(obj)){
return false;
}
if( HAS_HOLYLIGHT(ch))
return true;
// support for the 'mob seeall' command
if(IS_SET(ch->dyn,DYN_MOB_SEE_ALL)){
return true;
}
if( obj->item_type == ITEM_TOKEN )
return false;
if( IS_SET( obj->extra2_flags, OBJEXTRA2_BURIED ))
return false;
if( IS_SET(obj->extra_flags,OBJEXTRA_VIS_DEATH))
return false;
if( IS_AFFECTED( ch, AFF_BLIND ) && obj->item_type != ITEM_POTION)
return false;
if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 )
return true;
if( IS_SET(obj->extra_flags, OBJEXTRA_INVIS)
&& !IS_AFFECTED(ch, AFF_DETECT_INVIS) )
return false;
if( IS_OBJ_STAT(obj,OBJEXTRA_GLOW))
return true;
if( room_is_dark( ch->in_room ) && !IS_AFFECTED(ch, AFF_DARK_VISION) )
return false;
return true;
}
/**************************************************************************/
// True if char can drop obj.
bool can_drop_obj( char_data *ch, OBJ_DATA *obj )
{
if( !IS_SET(obj->extra_flags, OBJEXTRA_NODROP) )
return true;
if( !IS_NPC(ch) && ch->level >= LEVEL_IMMORTAL )
return true;
return false;
}
/**************************************************************************/
// Return ascii name of an item type.
char *item_type_name( OBJ_DATA *obj )
{
return flag_string(item_types, obj->item_type);
}
/**************************************************************************/
// Return ascii name of an affect location.
char *affect_loc_name( int location )
{
return flag_string(apply_types, location);
}
/**************************************************************************/
char *full_affect_loc_name( AFFECT_DATA *paf)
{
static char result[MIL];
switch( paf->location )
{
case APPLY_NONE:
{
switch(paf->where)
{
case WHERE_WEAPON:
sprintf( result,"%s", weapon_bit_name( paf->bitvector ));
break;
case WHERE_VULN:
sprintf( result,"%s (vuln)", imm_bit_name( paf->bitvector ));
break;
case WHERE_RESIST:
sprintf( result,"%s (resist)", imm_bit_name( paf->bitvector ));
break;
case WHERE_IMMUNE:
sprintf( result,"%s (immune)", imm_bit_name( paf->bitvector ));
break;
case WHERE_OBJEXTRA:
sprintf( result,"%s (object)", extra_bit_name(paf->bitvector));
break;
case WHERE_OBJEXTRA2:
sprintf( result,"%s (object)", extra2_bit_name(paf->bitvector));
break;
case WHERE_AFFECTS:
default:
sprintf( result,"%s", affect_bit_name( paf->bitvector ));
break;
case WHERE_AFFECTS2:
sprintf( result,"%s", affect2_bit_name( paf->bitvector ));
break;
}
return result;
}
default:
return flag_string(apply_types, paf->location);
}
bugf( "Affect_location_name: unknown location %d.", paf->location );
return "(unknown)";
}
/**************************************************************************/
// return text flags of what the bits are for
char *affect_bit_name( int bits)
{
return flag_string(affect_flags, bits);
}
/**************************************************************************/
char *affect2_bit_name( int bits)
{
return flag_string(affect2_flags, bits);
}
/**************************************************************************/
char *extra_bit_name( int bits )
{
return flag_string(objextra_flags, bits);
}
/**************************************************************************/
char *extra2_bit_name( int bits)
{
return flag_string(objextra2_flags, bits);
}
/**************************************************************************/
char *act_bit_name( int bits)
{
if(IS_SET(bits,ACT_IS_NPC))
{
return flag_string(act_flags, bits);
}else{
return flag_string(plr_flags, bits);
}
}
/**************************************************************************/
char *act2_bit_name( int bits)
{
return flag_string(act2_flags, bits);
}
/**************************************************************************/
char *comm_bit_name(int bits)
{
return flag_string(comm_flags, bits);
}
/**************************************************************************/
char *imm_bit_name(int bits)
{
return flag_string(imm_flags, bits);
}
/**************************************************************************/
char *wear_bit_name(int bits)
{
return flag_string(wear_flags, bits);
}
/**************************************************************************/
char *form_bit_name(int bits)
{
return flag_string(form_flags, bits);
}
/**************************************************************************/
char *part_bit_name(int bits)
{
return flag_string(part_flags, bits);
}
/**************************************************************************/
char *weapon_bit_name(int bits)
{
return flag_string(weapon_flags, bits);
}
/**************************************************************************/
char *cont_bit_name( int cont_flags)
{
static char buf[512];
buf[0] = '\0';
if(cont_flags & CONT_CLOSEABLE ) strcat(buf, " closable");
if(cont_flags & CONT_PICKPROOF ) strcat(buf, " pickproof");
if(cont_flags & CONT_CLOSED ) strcat(buf, " closed");
if(cont_flags & CONT_LOCKED ) strcat(buf, " locked");
return(buf[0] != '\0' ) ? buf+1 : (char*)"none";
}
/**************************************************************************/
char *off_bit_name(int bits)
{
return flag_string(off_flags, bits);
}
/**************************************************************************/
char *room_flags_bit_name(int bits)
{
return flag_string(room_flags, bits);
}
/**************************************************************************/
/*
* Short description function - by Kalahn, displays the word YOU if
* ch==looker
*/
char * YOU_PERS( char_data *ch, char_data *looker)
{
if(!ch || !looker){
return "";
}
if(ch==looker){
return("you");
}
return(PERS(ch, looker));
}
/**************************************************************************/
/*
* Long description for mobs, short for players function
*/
char * LONGPERS( char_data *ch, char_data *looker)
{
static char buf[MSL];
char *descript="";
// use long of mobs, short of pcs
if(IS_NPC(ch)){
descript=ch->long_descr;
}else{
descript=ch->short_descr;
}
strcpy(buf, mxp_create_tag(looker, mxp_tag_for_mob(looker, ch), capitalize(descript)));
if(!IS_UNSWITCHED_MOB(looker))
{
if(IS_CONTROLLED(ch) && IS_SET(looker->act, PLR_HOLYNAME))
{ // add the little controling name
strcat( buf, FORMATF(" [%s]", TRUE_CH(ch)->name)) ;
}
}
if(!IS_UNSWITCHED_MOB(looker) &&
HAS_DESC(looker) && IS_SET(TRUE_CH(looker)->act, PLR_HOLYVNUM))
{
char buf2[MSL];
if(IS_NPC(ch) && ch->pIndexData)
{
if(ch->pIndexData->mprogs)
{
sprintf(buf2," `#`=H*%d,%d*`&", ch->pIndexData->vnum, ch->level);
}
else
{
sprintf(buf2," `#`=h[%d,%d]`&", ch->pIndexData->vnum, ch->level);
}
strcat(buf,buf2);
}
}
return(buf);
}
/**************************************************************************/
// char * char_position_text(char_data *ch )
// returns the correct position text for a characters position.
// eg " is <postion name> in <object_short>.",
// " is <postion name> on <object_short>.",
// " is <postion name> at <object_short>.",
// " is <postion name> here."
char * char_position_text( char_data *ch)
{
static char buf[MSL];
sh_int pos = ch->position;
if(ch->on != NULL)
{
if(IS_SET(ch->on->value[2],SLEEP_AT))
{
sprintf(buf," is %s at %s.", position_table[pos].name
,ch->on->short_descr);
}
else
{
if(IS_SET(ch->on->value[2],SLEEP_ON))
{
sprintf(buf," is %s on %s.", position_table[pos].name
,ch->on->short_descr);
}
else
{
sprintf(buf," is %s in %s.", position_table[pos].name
,ch->on->short_descr);
}
}
}
else
{
sprintf(buf," is %s here.", position_table[pos].name);
}
return(buf);
}
/**************************************************************************/
// returns the characters after a / from an area filename
char * area_fname (AREA_DATA *pArea)
{
static char buffer[128]; /* short filename */
char *slash; /* used for finding a slash */
if(!pArea)
{
bug("Area handed to area_fname was null");
return("");
}
strncpy(buffer, pArea->file_name, 128); /* copy the filename */
slash = strchr (buffer, '/'); /* find the slash (area/midgaard.are) */
if(slash) /* if there was one move one after it*/
slash++;
else
slash = buffer; /*otherwise set to the first word */
return(slash);
}
/**************************************************************************/
char *shortdate(time_t *tm) // kalahn - sept 97
{
static int i;
static char result[3][30];
// rotate buffers
++i= i%3;
result[i][0] = '\0';
if(!tm){
tm=¤t_time;
}
char *tbuf = ctime( tm );
tbuf[str_len(tbuf)-6] = '\0';
strcpy(result[i], tbuf);
return(result[i]);
}
/**************************************************************************/
char *shorttime(time_t *tm) // kalahn - nov 97
{
static int i;
static char result[3][30];
// rotate buffers
++i= i%3;
result[i][0] = '\0';
if(!tm){
tm=¤t_time;
}
char *tbuf = ctime( tm);
tbuf[str_len(tbuf)-6] = '\0';
strcpy(result[i], (char *)&tbuf[11]);
return(result[i]);
}
/**************************************************************************/
// Append a string to a file.
void append_string_to_file( const char *file, const char *str, bool newline )
{
FILE *fp;
if( str[0] == '\0' )
return;
if(fpAppend2FilReserve==NULL && runlevel==RUNLEVEL_SHUTING_DOWN)
return;
if(fpAppend2FilReserve){
fclose(fpAppend2FilReserve);
}
if( ( fp = fopen( file, "a" ) ) == NULL ){
bugf("append_string_to_file(): fopen '%s' for append - error %d (%s)",
file, errno, strerror( errno));
}else{
if(newline){
fprintf( fp, "%s\n", str );
}else{
fprintf( fp, "%s", str );
}
fclose( fp );
}
fpAppend2FilReserve = fopen( ANULL_FILE, "r" );
}
/**************************************************************************/
// Append a date and time coded string to a file.
void append_datetimestring_to_file( const char *file, const char *str )
{
char buf[MSL];
sprintf(buf, "%s: ", shortdate(NULL));
append_string_to_file( file, buf, false);
append_string_to_file( file, str, true);
}
/**************************************************************************/
// Append a time coded string to a file.
void append_timestring_to_file( const char *file, const char *str )
{
char buf[MSL];
sprintf(buf, "%s: ", shorttime(NULL));
append_string_to_file( file, buf, false);
append_string_to_file( file, str, true);
}
/**************************************************************************/
// appends to file short time code, room, players name
void append_logentry_to_file( char_data *ch, char *file, char *str )
{
char logbuf[MSL];
sprintf(logbuf, "[%5d] %s: %s",
ch->in_room ? ch->in_room->vnum : 0, TRUE_CH(ch)->name, str );
append_datetimestring_to_file(file, logbuf);
}
/**************************************************************************/
/*
* appends to a players log the string with a short time code on it
* and a [O] if they are in an ooc room
*/
void append_playerlog( char_data *ch, char *str )
{
char logfname[MSL];
char buf[MSL];
char buf_vnum[MSL];
char name[MIL];
// make lowercase and first word of the name
one_argument( TRUE_CH(ch)->name, name);
if(IS_IMMORTAL(ch)){
sprintf(logfname, "%s%s.log", IMMLOG_DIR, name);
}else{
sprintf(logfname, "%s%s.log", PLAYER_LOGS_DIR, name);
}
sprintf(buf_vnum, "<%5d>", (ch->in_room? ch->in_room->vnum:0));
if(IS_OOC(ch))
sprintf(buf,"%s [O] %s", buf_vnum, str);
else
sprintf(buf,"%s %s", buf_vnum, str);
append_datetimestring_to_file(logfname, buf);
}
/**************************************************************************/
/*
* appends to the string with a short time code on it
* and a [O] if they are in an ooc room
* [N] for newbie support person
*/
void append_newbie_support_log( char_data *ch, char *str )
{
char buf[MSL];
sprintf(buf,"%3s%3s %-13s:%s",
IS_OOC(ch)?"[O]":"",
IS_NEWBIE_SUPPORT(ch)?"[N]":"",
ch->name, str);
append_datetimestring_to_file(NEWBIE_SUPPORT_LOG_FILE, buf);
}
/**************************************************************************/
/*
* appends to file short date time code, room, players name
*/
void append_datetime_ch_to_file( char_data *ch, const char *file, const char *str )
{
char logbuf[MSL];
sprintf(logbuf, "%-13s<%2d>- %s", ch->name, ch->level, str );
append_datetimestring_to_file(file, logbuf);
}
/**************************************************************************/
void rand_to_char(char_data *ch, char *str1, char *str2, char *str3, char *str4, char *str5)
{
char *output="";
switch(dice(1,5))
{
case 1 : output=str1; break;
case 2 : output=str2; break;
case 3 : output=str3; break;
case 4 : output=str4; break;
case 5 : output=str5; break;
default:bug("BUG: rand_to_char: number out of range.");
do_abort(); // this should never be possible
break;
}
ch->print(output);
}
/**************************************************************************/
// Returns the visible length of a string after colour parsing
// written by Kalahn - march 98
// note: `1 and `} returns a -1 (new line codes)
int c_str_len(const char *s)
{
int len = 0; // visible string length (without colour codes)
for( ; *s; s++ )
{
if( *s == '`' ) // skip the colour code
{
s++; // move to the colour character
switch(*s)
{
default:
break;
case '=': // custom colour
s++; // skip the '=' moving to the colour char...
// we dont bother counting it since we assume
// it is always a colour
break;
case '-': // ~
case '`': // `
len++;
break;
case 'N': // N - game name
len+=str_len(game_settings->gamename);
break;
case '1': // newline returns a -1
case '}': // newline returns a -1
return -1;
break;
}
continue;
}
len++; // normal character count that length
}
return len;
}
/**************************************************************************/
// used to detect arguments sent by players to defraud systems
// like sending ``1 over ooc to make a new line and then putting
// new text to send to everyone.
bool check_defrauding_argument(char_data *ch, char *argument ){
if(IS_NULLSTR(argument)){
return false;
}
if(c_str_len(argument)==-1){
ch->println("Messages containing colour codes which create new lines will `RNOT`x be sent.");
return true;
}
return false;
}
/**************************************************************************/
// Returns true if str has a ` colour code in it
// written by Kalahn - march 98
bool has_colour(const char *s)
{
for( ; *s; s++ )
{
if( *s == '`' ) // find a colour code
return true;
}
return false;
}
/**************************************************************************/
// Returns true if str has a space character in it
// written by Kalahn - march 98
bool has_space(const char *s)
{
for( ; *s; s++ )
{
if( *s == ' ' ) // find a space
return true;
}
return false;
}
/**************************************************************************/
// Returns true if str has any whitespace character in it
// written by Kalahn - march 98
bool has_whitespace(const char *s)
{
for( ; *s; s++ )
{
if( is_space(*s)){ // find a whitespace
return true;
}
}
return false;
}
/**************************************************************************/
// Returns width amount of characters not counting colour codes for width
// Kalahn - Mar 98
char * str_width(const char *s, int width)
{
static int i;
static char buf[5][MSL];
++i%=5;
char *point;
int len = 0; // visible string length (without colour codes)
point=buf[i]; // start at the start of the input string s
point--;
for( ; *s && len<width; s++ )
{
*++point= *s; // copy the character
if( *s == '`' ) // if we have a colour code copy the next char
{
s++; // move to the colour character
*++point= *s; // copy the colour character
if(*s== '`'){ // count only the length of `` codes
len++;
}
if(*s== '='){ // custom colour code... move to custom code
s++; // move to the custom colour code character
*++point= *s; // copy the custom colour code character
}
continue;
}
len++; // normal character count that length
}
// if we need some blank spaces at the end of the string
while(len<width)
{
*++point= ' ';
len++;
}
*++point = '\0'; // terminate the string
return buf[i];
}
/**************************************************************************/
void center_to_char(char *argument, char_data *ch, int columns)
{
int spaces;
columns = (columns < 2) ? 80 : columns;
spaces=(columns-c_str_len(argument))/2;
ch->printf("%*c%s",spaces,' ',argument);
return;
}
/**************************************************************************/
void centerf_to_char (char_data *ch,int cols, char *fmt, ...)
{
char buf[MSL];
va_list args;
va_start(args, fmt);
vsnprintf(buf, MSL, fmt, args);
va_end(args);
center_to_char(buf, ch, cols);
}
/**************************************************************************/
void bugf (char * fmt, ...)
{
char buf[MSL*3];
va_list args;
va_start(args, fmt);
vsnprintf(buf, MSL*3, fmt, args);
va_end(args);
bug(buf);
}
/**************************************************************************/
char *mprog_type_to_name( int type );
/**************************************************************************/
// Kal - Feb 00
// Used to log a buggy prog, and disable it at the same time...
// till the next mpreset
void mpbuggy_prog(MPROG_LIST *trigger, char * fmt, ...)
{
char buf[HSL];
char *pBuf;
assert(trigger->prog->disabled==false); // shouldn't have been attempting to run
trigger->prog->disabled=true;
// add_buf(output,"{xMPBUG {G#mobprog{x, {Ymobvnum{x, {Binroom{x, {rline{x, {Ccall_level{x:\r\n");
sprintf(buf,"`S#############\r\n"
" Disabling mobprog `G%d`x,`Y%d`x,`B%d`x,`r%d`x,`C%d`x:\r\n"
" `Y",
callstack_pvnum[call_level-1],
callstack_mvnum[call_level-1],
callstack_rvnum[call_level-1],
callstack_line[call_level-1],
call_level-1);
// format and concate the error message text
pBuf=&buf[str_len(buf)];
va_list args;
va_start(args, fmt);
vsnprintf(pBuf, MSL, fmt, args);
va_end(args);
// put extra debugging info from the trigger
pBuf=&buf[str_len(buf)];
sprintf(pBuf, "\r\n`x Trigger type '%s' phrase '%s'\r\n",
mprog_type_to_name(trigger->trig_type),
trigger->trig_phrase);
strcat(pBuf," Call stack info:\r\n");
// display the call stack
char catbuf[MSL];
for(int i=0; i<call_level;i++)
{
if(!callstack_aborted[i] || (i==call_level-1)){
sprintf(catbuf, " MOBprog %d on mob %d (in room %d), line %d\r\n",
callstack_pvnum[i],
callstack_mvnum[i],
callstack_rvnum[i],
callstack_line[i]);
strcat(pBuf,catbuf);
}
}
strcat(pBuf,"`S#############\r\n\r\n");
append_timestring_to_file(MPBUG_FILE, buf);
trigger->prog->disabled_text=str_dup(buf); // record what is buggy with it
}
/**************************************************************************/
extern char *mq_mpbug_details;
extern bool mq_running_queued_command;
/**************************************************************************/
// mobprog bug log system
void mpbug(char * fmt, ...)
{
char buf[HSL];
char *pBuf;
// add_buf(output,"{xMPBUG {G#mobprog{x, {Ymobvnum{x, {Binroom{x, {rline{x, {Ccall_level{x:\r\n");
if(mq_running_queued_command && !IS_NULLSTR(mq_mpbug_details)){
strcpy(buf, mq_mpbug_details);
}else{
sprintf(buf,"`G%d`x,`Y%d`x,`B%d`x,`r%d`x,`C%d`x:",
callstack_pvnum[call_level-1],
callstack_mvnum[call_level-1],
callstack_rvnum[call_level-1],
callstack_line[call_level-1],
call_level-1);
}
pBuf=&buf[str_len(buf)];
va_list args;
va_start(args, fmt);
vsnprintf(pBuf, MSL, fmt, args);
va_end(args);
append_timestring_to_file(MPBUG_FILE, buf);
}
/**************************************************************************/
void logf(char * fmt, ...)
{
char buf[HSL];
va_list args;
va_start(args, fmt);
vsnprintf(buf, HSL-MIL, fmt, args);
va_end(args);
log_string(buf);
}
/**************************************************************************/
void broadcast(char_data *except, char * fmt, ...)
{
char buf[MSL];
char_data *wch;
va_list args;
va_start(args, fmt);
vsnprintf(buf, MSL, fmt, args);
va_end(args);
for( wch = player_list; wch; wch = wch->next_player )
{
if(wch!=except){
ACTIVE_CH(wch)->print(buf);
}
}
}
/**************************************************************************/
void pkill_broadcast(char * fmt, ...)
{
char buf[MSL], buf2[MSL];
char_data *wch;
va_list args;
va_start(args, fmt);
vsnprintf(buf, MSL, fmt, args);
va_end(args);
sprintf(buf2,"`RPKILL PORT BROADCAST:`W %s\r\n`x", buf);
for( wch = player_list; wch; wch = wch->next_player )
{
wch->print(buf2);
}
}
/**************************************************************************/
char * makef_titlebar(char *fmt, ...)
{
char buf [MSL];
char line[MSL];
char line2[MSL];
static char returnbuf[MSL];
int spaces;
// format all the text into buf
va_list args;
va_start(args, fmt);
vsnprintf(buf, MSL, fmt, args);
va_end(args);
if(c_str_len(buf)<1 || (c_str_len(buf)==1 && (buf[0]=='-' || buf[0]=='=') ))
{
return "`#`=t-====================================="
"======================================-`&\r\n";
}
if(c_str_len(buf)>78)
{
sprintf(returnbuf,"%s\r\n",buf);
return returnbuf;
}
spaces= (74-c_str_len(buf))/2;
for(int j=0;j<spaces; j++)
{
line[j]='=';
}
line[spaces]='\0';
if(spaces%2==0){
sprintf(returnbuf,"`#`=t-%s`# `=T%s `&%s-`&\r\n",line, buf, line);
}else{
strcpy(line2, line);
line2[spaces-1]='\0';
sprintf(returnbuf,"`#`=t-%s`# `=T%s `&%s-`&\r\n",line, buf, line2);
}
return returnbuf;
}
/**************************************************************************/
/* get the 'short' name of an area (e.g. MIDGAARD, MIRROR etc. */
/* assumes that the filename saved in the AREA_DATA struct is something like midgaard.are */
char * area_name (AREA_DATA *pArea)
{
static char buffer[64]; /* short filename */
char *period;
assert(pArea != NULL);
strncpy(buffer, pArea->file_name, 64); /* copy the filename */
period = strchr (buffer, '.'); /* find the period (midgaard.are) */
if(period) /* if there was one */
*period = '\0'; /* terminate the string there (midgaard) */
return buffer;
}
/**************************************************************************/
char *percent_colour_codebar(void){
return("`c0-3`x,`g4-11`x,`m12-18`x,`r19-26`x,`y27-33`x,`S34-41`x,"
"`w42-48`x,`W49-56`x,`Y57-63`x,`R64-71`x,`M72-78`x,"
"`G79-86`x,`C87-93`x,`B94+`x\r\n");
}
/**************************************************************************/
char *percent_colour_code(sh_int val){
static char col[5][MIL];
static sh_int i;
float tval;
sh_int newval;
// rotate buffers
++i= i%5;
tval=(float)val;
tval*=2;
tval/=15;
tval+=0.5;
newval=(sh_int)tval;
switch(newval)
{
case 0:
strcpy(col[i], "`c");
break;
case 1:
strcpy(col[i], "`g");
break;
case 2:
strcpy(col[i], "`m");
break;
case 3:
strcpy(col[i], "`r");
break;
case 4:
strcpy(col[i], "`y");
break;
case 5:
strcpy(col[i], "`S");
break;
case 6:
strcpy(col[i], "`w");
break;
case 7:
strcpy(col[i], "`W");
break;
case 8:
strcpy(col[i], "`Y");
break;
case 9:
strcpy(col[i], "`R");
break;
case 10:
strcpy(col[i], "`M");
break;
case 11:
strcpy(col[i], "`G");
break;
case 12:
strcpy(col[i], "`C");
break;
case 13:
case 14:
strcpy(col[i], "`B");
break;
}
return col[i];
}
/**************************************************************************/
// returns the underscored lowercase word
char * underscore_word(char *word)
{
static char rbuf[3][MIL];
static int index;
char * pStr;
++index%=3; // rotate return buffer
sprintf(rbuf[index], "%s", word);
pStr=&rbuf[index][0];
// convert spaces into _ characters
// and upper to lowercase
do{
if(*pStr==' '){
*pStr='_';
}else{
*pStr=tolower(*pStr);
}
}while (*(++pStr));
return rbuf[index];
}
/**************************************************************************/
char * lowercase(const char *str){
static int i;
static char buf[5][512];
int pos;
// rotate buffers
++i= i%5;
for( pos = 0; str[pos] != '\0'; pos++ )
buf[i][pos] = LOWER(str[pos]);
buf[i][pos] = '\0';
return buf[i];
}
/**************************************************************************/
char * uppercase(const char *str){
static int i;
static char buf[5][512];
int pos;
// rotate buffers
++i= i%5;
for( pos = 0; str[pos] != '\0'; pos++ )
buf[i][pos] = UPPER(str[pos]);
buf[i][pos] = '\0';
return buf[i];
}
/**************************************************************************/
void affect_to_room( ROOM_INDEX_DATA *room, AFFECT_DATA *paf )
{
AFFECT_DATA *paf_new;
paf_new = new_affect();
*paf_new = *paf;
paf_new->next = room->affected;
room->affected = paf_new;
if( paf->where == WHERE_AFFECTS )
SET_BIT(room->affected_by,paf->bitvector);
return;
}
/**************************************************************************/
void affect_remove_room( ROOM_INDEX_DATA *room, AFFECT_DATA *paf )
{
int where, vector;
if( room->affected == NULL )
{
bug("Affect_remove_room: no affect.");
return;
}
where = paf->where;
vector = paf->bitvector;
if(paf->bitvector)
switch( paf->where)
{
case WHERE_AFFECTS:
REMOVE_BIT(room->affected_by,paf->bitvector);
break;
}
if( paf == room->affected )
{
room->affected = paf->next;
}
else
{
AFFECT_DATA *prev;
for( prev = room->affected; prev != NULL; prev = prev->next )
{
if( prev->next == paf )
{
prev->next = paf->next;
break;
}
}
if( prev == NULL )
{
bug("Affect_remove_room: cannot find paf.");
return;
}
}
free_affect(paf);
return;
}
/**************************************************************************/
void affect_to_skill( char_data *ch, int sn, int amount )
{
if( IS_NPC( ch ) || sn < 0 ) return;
if( TRUE_CH(ch)->pcdata->learned[sn] > 0 )
TRUE_CH(ch)->pcdata->learned[sn] += amount;
return;
}
/**************************************************************************/
// counts the number of times a character is in the buffer
int count_char(const char *buffer, char character)
{
int result=0;
if(buffer==NULL){
return 0;
}
for(; *buffer; buffer++)
{
if(*buffer==character)
result++;
}
return result;
}
/**************************************************************************/
// used when the moblog flag is on
void process_moblog(char_data *ch, const char *txt)
{
if(!ch){
return;
}
if( !IS_VALID(ch)){
return;
}
if( !IS_NPC(ch) ){
return;
}
if(!IS_NULLSTR(txt)
&& IS_SET(ch->act,ACT_MOBLOG) )
{
char logbuf[MSL];
// prevent buffer overruns
char working[MSL];
strncpy(working, txt, MSL-30);
int len=UMIN(str_len(txt),MSL-40);
working[len]='\0';
// ditch the \r codes
strcpy(working, fix_string( working));
// trim off the tail \n code if there
if(working[str_len(working)-1]=='\n'){
working[str_len(working)-1]='\0';
}
sprintf(logbuf, "[%5d] '%s' in room %d '%s'",
(ch->pIndexData?ch->pIndexData->vnum:0),
ch->name,
(ch->in_room? ch->in_room->vnum:0),
working);
append_timestring_to_file( MOBLOG_LOGFILE, logbuf);
};
}
/**************************************************************************/
// reports a wiznet message about multilogging
// handles restoring of colour coding
// header is displayed before custom text
// default colour of custom text is RED
void multilog_alertf(char_data *ch, char * fmt, ...)
{
char *pbuf;
char buf[HSL];
if(!IS_NULLSTR(ch->remote_ip_copy)){
sprintf(buf,"`#`R***MULTILOG ALERT - `x%s`R:",
ch->remote_ip_copy);
}else{
strcpy(buf,"`#`R***MULTILOG ALERT: ");
}
pbuf=&buf[str_len(buf)];
va_list args;
va_start(args, fmt);
vsnprintf(pbuf, MSL, fmt, args);
va_end(args);
strcat(buf,"`&");
append_datetimestring_to_file(MULTILOG_FILE, buf);
wiznet(buf,NULL,NULL,WIZ_SITES,0,0);
}
/**************************************************************************/
char *to_affect_string( AFFECT_DATA *paf, int objects_level )
{
static char buf[MSL*2];
buf[0]='\0';
switch(paf->where)
{
case WHERE_AFFECTS:
sprintf( buf, "Adds %s affect.",
affect_bit_name(paf->bitvector));
break;
case WHERE_AFFECTS2:
sprintf( buf, "Adds %s affect2.",
affect2_bit_name(paf->bitvector));
break;
case WHERE_OBJEXTRA:
sprintf( buf, "Adds object extra flag(s) %s.",
extra_bit_name(paf->bitvector));
break;
case WHERE_OBJEXTRA2:
sprintf( buf, "Adds object extra2 flag(s) %s.",
extra2_bit_name(paf->bitvector));
break;
case WHERE_IMMUNE:
sprintf( buf, "Adds immunity to %s.",
imm_bit_name(paf->bitvector));
break;
case WHERE_RESIST:
sprintf( buf, "Adds resistance to %s.",
imm_bit_name(paf->bitvector));
break;
case WHERE_VULN:
sprintf( buf, "Adds vulnerability to %s.",
imm_bit_name(paf->bitvector));
break;
case WHERE_MODIFIER:
sprintf( buf, "Adds %s modifier of %d.",
flag_string(apply_types, paf->location), paf->modifier);
break;
case WHERE_WEAPON:
sprintf( buf, "Adds weapon flag(s) %s .", flag_string(weapon_flags, paf->modifier));
break;
case WHERE_SKILLS:
sprintf( buf, "Adds %d%% to %s.",
paf->modifier, skill_table[paf->type].name );
break;
case WHERE_OBJECTSPELL:
if(paf->level)
{
sprintf( buf, "Casts '%s' at level %3d for a duration of %3d.",
skill_table[paf->type].name, paf->level, paf->duration);
}
else
{
sprintf( buf, "Casts '%s' at objects level (%d) for a duration of %d.",
skill_table[paf->type].name, objects_level, paf->duration);
}
break;
default:
sprintf( buf, "Unknown bit %d: %d", paf->where,paf->bitvector);
break;
}
return buf;
}
/**************************************************************************/
// reports and logs bounds checking messages
void boundsbug(char * fmt, ...)
{
char buf[HSL];
char *pbuf;
strcpy(buf,"`#`R***BOUNDS BUG ALERT: ");
pbuf=&buf[str_len(buf)];
va_list args;
va_start(args, fmt);
vsnprintf(pbuf, MSL, fmt, args);
va_end(args);
append_datetimestring_to_file(BOUNDSBUG_FILE, buf);
bugf(buf);
do_abort();
}
/**************************************************************************/
char *preference_word(PREFERENCE_TYPE pt)
{
switch (pt){
case PREF_OFF: return "off";
case PREF_AUTOSENSE: return "autosense";
case PREF_ON: return "on";
default:
bugf("Unknown preference type %d", pt);
do_abort();
}
return "";
}
/**************************************************************************/
// Kal - Apr 01
char *FORMATF(const char *formatbuf, ...)
{
static int i;
static char buf[10][MSL*3];
++i%=10;
va_list args;
va_start(args, formatbuf);
vsnprintf(buf[i], MSL*3, formatbuf, args);
va_end(args);
return buf[i];
}
/**************************************************************************/
// Kal - Jun 01 - unique id system
int get_next_uid()
{
static int last=1000;
return ++last;
}
/**************************************************************************/
// get_uid() - unique id system, returns 0 if it isn't a UID text reference
int get_uid(const char *text)
{
const char *p=text;
// skip leading whitespace
while(is_space(*p)){
p++;
}
if(*p=='#' && is_number(p+1)){ // it is a UID
return atoi(p+1);
}
return 0;
}
/**************************************************************************/
// Kal - convert a 24 hour clock into 12 clock
char *convert24hourto12hour(int hour)
{
// multibuffered
static int i;
static char buf[5][512];
++i%=5;
buf[i][0] = '\0';
hour%=24; // avoid idiot results
if(hour>11){ // 12 -> 23
sprintf(buf[i], "%2dpm", ((hour-1)%12)+1);
}else{
sprintf(buf[i], "%2dam", ((hour-12)%12)+12);
}
return buf[i];
}
/**************************************************************************/
// Kal - Sept 02
void println_delayed_to_room(int seconds, room_index_data *room, char_data *all_but_this_person, const char *text)
{
char_data *p;
// check the room is sane
if(!room){
bugf("delayed_printf_to_room(), given a NULL room!");
return;
}
for( p=room->people; p; p=p->next_in_room ){
if(p==all_but_this_person){
continue;
}
p->println(seconds, text);
}
}
/**************************************************************************/
// return "" if ch doesn't have autodamage enabled, otherwise return
// the autodamage text required.
// Kal - Aug 03
char *autodamtext(char_data *ch, int damage_amount)
{
static char result[MIL];
if(GAMESETTING2(GAMESET2_NO_AUTODAMAGE_COMMAND)
|| !HAS_CONFIG2(ch, CONFIG2_AUTODAMAGE)){
return "";
}
sprintf(result, " [%d]", damage_amount);
return result;
}
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/