/***************************************************************************
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. *
* *
* Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* *
* In order to use any part of this Merc Diku Mud, you must comply with *
* both the original Diku license in 'license.doc' as well the Merc *
* license in 'license.txt'. In particular, you may not remove either of *
* these copyright notices. *
* *
* Much time and thought has gone into this software and you are *
* benefitting. We hope that you share your changes too. What goes *
* around, comes around. *
***************************************************************************/
/*
MurkMUD++ - A Windows compatible, C++ compatible Merc 2.2 Mud.
\author Jon A. Lambert
\date 01/02/2007
\version 1.5
\remarks
This source code copyright (C) 2005, 2006, 2007 by Jon A. Lambert
All rights reserved.
Use governed by the MurkMUD++ public license found in license.murk++
*/
#include "os.hpp"
#include "config.hpp"
#include "globals.hpp"
#include "io.hpp"
#include "utils.hpp"
#include "descriptor.hpp"
#include "pcdata.hpp"
#include "object.hpp"
#include "room.hpp"
#include "mobproto.hpp"
#include "objproto.hpp"
#include "affect.hpp"
#include "area.hpp"
#include "exit.hpp"
#include "database.hpp"
#include "world.hpp"
// temp globals
extern void multi_hit(Character *ch, Character *victim, int dt);
extern void mprog_act_trigger (const std::string & buf, Character * mob, Character * ch,
Object * obj, void *vo);
extern void mprog_entry_trigger (Character * mob);
extern void mprog_greet_trigger (Character * mob);
extern std::string get_title (int klass, int level, int sex);
sh_int movement_loss[SECT_MAX] = {
1, 2, 2, 3, 4, 6, 4, 1, 6, 10, 6
};
Character::Character() :
master(NULL), leader(NULL), fighting(NULL),
reply(NULL), spec_fun(NULL), pIndexData(NULL), desc(NULL),
pnote(NULL), in_room(NULL), was_in_room(NULL),
pcdata(NULL), sex(0), klass(0), race(0), level(0), trust(0), wizbit(false),
played(0), save_time(0), last_note(0), timer(0),
wait(0), hit(20), max_hit(20), mana(100), max_mana(100), move(100),
max_move(100), gold(0), exp(0), actflags(0), affected_by(0),
position(POS_STANDING), practice(21), carry_weight(0), carry_number(0),
saving_throw(0), alignment(0), hitroll(0), damroll(0), armor(100),
wimpy(0), deaf(0), mpact(NULL), mpactnum(0) {
logon = g_world->get_current_time();
}
/*
* Free a character.
*/
Character::~Character()
{
Object *obj;
Affect *paf;
ObjIter o, onext;
for (o = carrying.begin(); o != carrying.end(); o = onext) {
obj = *o;
onext = ++o;
obj->extract_obj ();
}
AffIter af, anext;
for (af = affected.begin(); af != affected.end(); af = anext) {
paf = *af;
anext = ++af;
affect_remove (paf);
}
if (pcdata != NULL) {
delete pcdata;
}
return;
}
int Character::mana_cost(int sn) {
if (is_npc())
return 0;
else
return std::max(skill_table[sn].min_mana, 100 /
(2 + level - skill_table[sn].skill_level[klass]));
}
/*
* Write to one char.
*/
void Character::send_to_char (const std::string & txt)
{
if (txt.empty() || desc == NULL)
return;
desc->showstr_head = (char*)std::malloc(txt.size()+1);
strncpy (desc->showstr_head, txt.c_str(), txt.size()+1);
desc->showstr_point = desc->showstr_head;
desc->show_string ("");
}
/*
* Append a string to a file.
*/
void Character::append_file (const char *file, const std::string & str)
{
if (is_npc () || str.empty())
return;
std::ofstream outfile;
outfile.open (file, std::ofstream::out | std::ofstream::app | std::ofstream::binary);
if (outfile.is_open()) {
outfile << "[" << std::setw(5) << (in_room ? in_room->vnum : 0) <<
"] " << name << ": " << str << std::endl;
outfile.close();
} else {
std::perror (file);
send_to_char ("Could not open the file!\r\n");
}
return;
}
/*
* True if char can see victim.
*/
bool Character::can_see (Character * victim)
{
if (this == victim)
return true;
if (!is_npc () && IS_SET (actflags, PLR_HOLYLIGHT))
return true;
if (is_affected (AFF_BLIND))
return false;
if (in_room->is_dark() && !is_affected (AFF_INFRARED))
return false;
if (victim->is_affected (AFF_INVISIBLE)
&& !is_affected (AFF_DETECT_INVIS))
return false;
if (victim->is_affected (AFF_HIDE)
&& !is_affected (AFF_DETECT_HIDDEN)
&& victim->fighting == NULL
&& (is_npc () ? !victim->is_npc () : victim->is_npc ()))
return false;
return true;
}
/*
* True if char can see obj.
*/
bool Character::can_see_obj (Object * obj)
{
if (!is_npc () && IS_SET (actflags, PLR_HOLYLIGHT))
return true;
if (obj->item_type == ITEM_POTION)
return true;
if (is_affected (AFF_BLIND))
return false;
if (obj->item_type == ITEM_LIGHT && obj->value[2] != 0)
return true;
if (in_room->is_dark() && !is_affected (AFF_INFRARED))
return false;
if (IS_SET (obj->extra_flags, ITEM_INVIS)
&& !is_affected (AFF_DETECT_INVIS))
return false;
return true;
}
/*
* True if char can drop obj.
*/
bool Character::can_drop_obj (Object * obj)
{
if (!IS_SET (obj->extra_flags, ITEM_NODROP))
return true;
if (!is_npc () && level >= LEVEL_IMMORTAL)
return true;
return false;
}
/*
* The primary output interface for formatted output.
*/
void Character::act (const std::string & format, const void *arg1, const void *arg2, int type)
{
static const char * he_she[] = { "it", "he", "she" };
static const char * him_her[] = { "it", "him", "her" };
static const char * his_her[] = { "its", "his", "her" };
Character *vch = (Character *) arg2;
Object *obj1 = (Object *) arg1;
Object *obj2 = (Object *) arg2;
/*
* Discard null and zero-length messages.
*/
if (format.empty())
return;
CharIter to = in_room->people.begin();
CharIter tend = in_room->people.end();
if (type == TO_VICT) {
if (vch == NULL) {
bug_printf ("Act: null vch with TO_VICT.");
return;
}
to = vch->in_room->people.begin();
tend = vch->in_room->people.end();
}
for (; to != tend; to++) {
if (((*to)->desc == NULL
&& ((*to)->is_npc () && !((*to)->pIndexData->progtypes & ACT_PROG)))
|| !(*to)->is_awake ())
continue;
if (type == TO_CHAR && *to != this)
continue;
if (type == TO_VICT && (*to != vch || *to == this))
continue;
if (type == TO_ROOM && *to == this)
continue;
if (type == TO_NOTVICT && (*to == this || *to == vch))
continue;
if (!is_npc() && (*to)->is_gagged(name))
continue;
std::string buf;
std::string::const_iterator str = format.begin();
while (str != format.end()) {
if (*str != '$') {
buf.append(1, *str);
str++;
continue;
}
++str;
if (arg2 == NULL && *str >= 'A' && *str <= 'Z') {
bug_printf ("Act: missing arg2 for code %d.", *str);
buf.append(" <@@@> ");
} else {
switch (*str) {
default:
bug_printf ("Act: bad code %d.", *str);
buf.append(" <@@@> ");
break;
/* Thx alex for 't' idea */
case 't':
buf.append((char *) arg1);
break;
case 'T':
buf.append((char *) arg2);
break;
case 'n':
buf.append(describe_to(*to));
break;
case 'N':
buf.append(vch->describe_to(*to));
break;
case 'e':
buf.append(he_she[URANGE (0, sex, 2)]);
break;
case 'E':
buf.append(he_she[URANGE (0, vch->sex, 2)]);
break;
case 'm':
buf.append(him_her[URANGE (0, sex, 2)]);
break;
case 'M':
buf.append(him_her[URANGE (0, vch->sex, 2)]);
break;
case 's':
buf.append(his_her[URANGE (0, sex, 2)]);
break;
case 'S':
buf.append(his_her[URANGE (0, vch->sex, 2)]);
break;
case 'p':
buf.append((*to)->can_see_obj(obj1)
? obj1->short_descr.c_str() : "something");
break;
case 'P':
buf.append((*to)->can_see_obj(obj2)
? obj2->short_descr.c_str() : "something");
break;
case 'd':
if (arg2 == NULL || ((char *) arg2)[0] == '\0') {
buf.append("door");
} else {
std::string fname;
one_argument ((char *) arg2, fname);
buf.append(fname);
}
break;
}
}
++str;
}
buf.append("\r\n");
buf[0] = toupper(buf[0]);
if ((*to)->desc)
(*to)->desc->write_to_buffer (buf);
if (MOBtrigger)
mprog_act_trigger (buf, *to, this, obj1, vch);
/* Added by Kahn */
}
MOBtrigger = true;
return;
}
bool Character::is_npc() {
return actflags & ACT_IS_NPC;
}
bool Character::is_awake() {
return position > POS_SLEEPING;
}
bool Character::is_good() {
return alignment >= 350;
}
bool Character::is_evil() {
return alignment <= -350;
}
bool Character::is_neutral() {
return !is_good() && !is_evil();
}
bool Character::is_affected(int flg) {
return affected_by & flg;
}
int Character::get_ac() {
if (is_awake())
return armor + dex_app[get_curr_dex()].defensive;
else
return armor;
}
int Character::get_hitroll() {
return hitroll + str_app[get_curr_str()].tohit;
}
int Character::get_damroll() {
return damroll + str_app[get_curr_str()].todam;
}
/*
* Retrieve character's current strength.
*/
int Character::get_curr_str() {
int max;
if (is_npc ())
return 13;
if (class_table[klass].attr_prime == APPLY_STR)
max = 25;
else
max = 22;
return URANGE (3, pcdata->get_curr("str"), max);
}
/*
* Retrieve character's current intelligence.
*/
int Character::get_curr_int() {
int max;
if (is_npc ())
return 13;
if (class_table[klass].attr_prime == APPLY_INT)
max = 25;
else
max = 22;
return URANGE (3, pcdata->get_curr("int"), max);
}
/*
* Retrieve character's current wisdom.
*/
int Character::get_curr_wis() {
int max;
if (is_npc ())
return 13;
if (class_table[klass].attr_prime == APPLY_WIS)
max = 25;
else
max = 22;
return URANGE (3, pcdata->get_curr("wis"), max);
}
/*
* Retrieve character's current dexterity.
*/
int Character::get_curr_dex() {
int max;
if (is_npc ())
return 13;
if (class_table[klass].attr_prime == APPLY_DEX)
max = 25;
else
max = 22;
return URANGE (3, pcdata->get_curr("dex"), max);
}
/*
* Retrieve character's current constitution.
*/
int Character::get_curr_con() {
int max;
if (is_npc ())
return 13;
if (class_table[klass].attr_prime == APPLY_CON)
max = 25;
else
max = 22;
return URANGE (3, pcdata->get_curr("con"), max);
}
/*
* Retrieve a character's age.
*/
int Character::get_age() {
return 17 + (played + (int) (g_world->get_current_time() - logon)) / 14400;
/* 12240 assumes 30 second hours, 24 hours a day, 20 day - Kahn */
}
/*
* Retrieve a character's carry capacity.
*/
int Character::can_carry_n() {
if (!is_npc () && level >= LEVEL_IMMORTAL)
return 1000;
if (is_npc () && IS_SET (actflags, ACT_PET))
return 0;
return MAX_WEAR + 2 * get_curr_dex() / 2;
}
/*
* Retrieve a character's carry capacity.
*/
int Character::can_carry_w() {
if (!is_npc () && level >= LEVEL_IMMORTAL)
return 1000000;
if (is_npc () && IS_SET (actflags, ACT_PET))
return 0;
return str_app[get_curr_str()].carry;
}
/*
* Retrieve a character's trusted level for permission checking.
*/
int Character::get_trust() {
Character *ch;
if (desc != NULL && desc->original != NULL)
ch = desc->original;
else
ch = this;
if (ch->trust != 0)
return ch->trust;
if (ch->is_npc () && ch->level >= LEVEL_HERO)
return LEVEL_HERO - 1;
else
return ch->level;
}
bool Character::is_immortal() {
return get_trust() >= LEVEL_IMMORTAL;
}
bool Character::is_hero() {
return get_trust() >= LEVEL_HERO;
}
int Character::is_outside() {
return !(in_room->room_flags & ROOM_INDOORS);
}
void Character::wait_state(int npulse) {
wait = std::max(wait, npulse);
}
/*
* Compute a saving throw.
* Negative apply's make saving throw better.
*/
bool Character::saves_spell (int lvl) {
int save;
save = 50 + (level - lvl - saving_throw) * 5;
save = URANGE (5, save, 95);
return number_percent () < save;
}
std::string Character::describe_to (Character* looker) {
if (looker->can_see(this)) {
if (is_npc())
return short_descr;
else
return name;
} else {
return "someone";
}
}
/*
* Find a piece of eq on a character.
*/
Object * Character::get_eq_char (int iWear)
{
for (ObjIter o = carrying.begin(); o != carrying.end(); o++) {
if ((*o)->wear_loc == iWear)
return *o;
}
return NULL;
}
/*
* Apply or remove an affect to a character.
*/
void Character::affect_modify (Affect * paf, bool fAdd)
{
Object *wield;
int mod = paf->modifier;
if (fAdd) {
SET_BIT (affected_by, paf->bitvector);
} else {
REMOVE_BIT (affected_by, paf->bitvector);
mod = 0 - mod;
}
if (is_npc ())
return;
switch (paf->location) {
default:
bug_printf ("Affect_modify: unknown location %d.", paf->location);
return;
case APPLY_NONE:
break;
case APPLY_STR:
pcdata->set_mod("str", pcdata->get_mod("str") + mod);
break;
case APPLY_DEX:
pcdata->set_mod("dex", pcdata->get_mod("dex") + mod);
break;
case APPLY_INT:
pcdata->set_mod("int", pcdata->get_mod("int") + mod);
break;
case APPLY_WIS:
pcdata->set_mod("wis", pcdata->get_mod("wis") + mod);
break;
case APPLY_CON:
pcdata->set_mod("con", pcdata->get_mod("con") + mod);
break;
case APPLY_SEX:
sex += mod;
break;
case APPLY_CLASS:
break;
case APPLY_LEVEL:
break;
case APPLY_AGE:
break;
case APPLY_HEIGHT:
break;
case APPLY_WEIGHT:
break;
case APPLY_MANA:
max_mana += mod;
break;
case APPLY_HIT:
max_hit += mod;
break;
case APPLY_MOVE:
max_move += mod;
break;
case APPLY_GOLD:
break;
case APPLY_EXP:
break;
case APPLY_AC:
armor += mod;
break;
case APPLY_HITROLL:
hitroll += mod;
break;
case APPLY_DAMROLL:
damroll += mod;
break;
case APPLY_SAVING_PARA:
saving_throw += mod;
break;
case APPLY_SAVING_ROD:
saving_throw += mod;
break;
case APPLY_SAVING_PETRI:
saving_throw += mod;
break;
case APPLY_SAVING_BREATH:
saving_throw += mod;
break;
case APPLY_SAVING_SPELL:
saving_throw += mod;
break;
}
/*
* Check for weapon wielding.
* Guard against recursion (for weapons with affects).
*/
if ((wield = get_eq_char (WEAR_WIELD)) != NULL
&& wield->get_obj_weight() > str_app[get_curr_str()].wield) {
static int depth;
if (depth == 0) {
depth++;
act ("You drop $p.", wield, NULL, TO_CHAR);
act ("$n drops $p.", wield, NULL, TO_ROOM);
wield->obj_from_char();
wield->obj_to_room (in_room);
depth--;
}
}
return;
}
/*
* Unequip a char with an obj.
*/
void Character::unequip_char (Object * obj)
{
if (obj->wear_loc == WEAR_NONE) {
bug_printf ("Unequip_char: already unequipped.");
return;
}
armor += obj->apply_ac (obj->wear_loc);
obj->wear_loc = -1;
AffIter paf;
for (paf = obj->pIndexData->affected.begin(); paf != obj->pIndexData->affected.end(); paf++)
affect_modify (*paf, false);
for (paf = obj->affected.begin(); paf != obj->affected.end(); paf++)
affect_modify (*paf, false);
if (obj->item_type == ITEM_LIGHT
&& obj->value[2] != 0 && in_room != NULL && in_room->light > 0)
--in_room->light;
if (obj->item_type == ITEM_DARKNESS
&& obj->value[2] != 0 && in_room != NULL && in_room->light < 1)
++in_room->light;
return;
}
/*
* Give an affect to a char.
*/
void Character::affect_to_char (Affect * paf)
{
Affect *paf_new = new Affect();
*paf_new = *paf;
affected.push_back(paf_new);
affect_modify (paf_new, true);
return;
}
/*
* Remove an affect from a char.
*/
void Character::affect_remove (Affect * paf)
{
if (affected.empty()) {
bug_printf ("Affect_remove: no affect.");
return;
}
affect_modify (paf, false);
affected.erase(find(affected.begin(), affected.end(), paf));
delete paf;
return;
}
/*
* Strip all affects of a given sn.
*/
void Character::affect_strip (int sn)
{
Affect *paf;
AffIter af, next;
for (af = affected.begin(); af != affected.end(); af = next) {
paf = *af;
next = ++af;
if (paf->type == sn)
affect_remove (paf);
}
return;
}
/*
* Return true if a char is affected by a spell.
*/
bool Character::has_affect (int sn)
{
AffIter af;
for (af = affected.begin(); af != affected.end(); af++) {
if ((*af)->type == sn)
return true;
}
return false;
}
/*
* Add or enhance an affect.
*/
void Character::affect_join (Affect * paf)
{
AffIter af;
for (af = affected.begin(); af != affected.end(); af++) {
if ((*af)->type == paf->type) {
paf->duration += (*af)->duration;
paf->modifier += (*af)->modifier;
affect_remove (*af);
break;
}
}
affect_to_char (paf);
return;
}
/*
* Find a char in the room.
*/
Character * Character::get_char_room (const std::string & argument)
{
std::string arg;
int number;
int count;
number = number_argument (argument, arg);
count = 0;
if (!str_cmp (arg, "self"))
return this;
CharIter rch;
for (rch = in_room->people.begin(); rch != in_room->people.end(); rch++) {
if (!can_see(*rch) || !is_name (arg, (*rch)->name))
continue;
if (++count == number)
return *rch;
}
return NULL;
}
/*
* Find a char in the world.
*/
Character * Character::get_char_world (const std::string & argument)
{
std::string arg;
Character *wch;
int number;
int count;
if ((wch = get_char_room (argument)) != NULL)
return wch;
number = number_argument (argument, arg);
count = 0;
CharIter c;
for (c = char_list.begin(); c != char_list.end(); c++) {
if (!can_see(*c) || !is_name (arg, (*c)->name))
continue;
if (++count == number)
return *c;
}
return NULL;
}
/*
* Find an obj in a list.
*/
Object * Character::get_obj_list (const std::string & argument, std::list<Object *> & list)
{
std::string arg;
int number;
int count;
number = number_argument (argument, arg);
count = 0;
ObjIter obj;
for (obj = list.begin(); obj != list.end(); obj++) {
if (can_see_obj(*obj) && is_name (arg, (*obj)->name)) {
if (++count == number)
return *obj;
}
}
return NULL;
}
/*
* Find an obj in player's inventory.
*/
Object * Character::get_obj_carry (const std::string & argument)
{
std::string arg;
int number;
int count;
number = number_argument (argument, arg);
count = 0;
ObjIter o;
for (o = carrying.begin(); o != carrying.end(); o++) {
if ((*o)->wear_loc == WEAR_NONE && can_see_obj(*o)
&& is_name (arg, (*o)->name)) {
if (++count == number)
return *o;
}
}
return NULL;
}
/*
* Find an obj in player's equipment.
*/
Object * Character::get_obj_wear (const std::string & argument)
{
std::string arg;
int number;
int count;
number = number_argument (argument, arg);
count = 0;
ObjIter o;
for (o = carrying.begin(); o != carrying.end(); o++) {
if ((*o)->wear_loc != WEAR_NONE && can_see_obj(*o)
&& is_name (arg, (*o)->name)) {
if (++count == number)
return *o;
}
}
return NULL;
}
/*
* Find an obj in the room or in inventory.
*/
Object * Character::get_obj_here (const std::string & argument)
{
Object *obj;
obj = get_obj_list (argument, in_room->contents);
if (obj != NULL)
return obj;
if ((obj = get_obj_carry (argument)) != NULL)
return obj;
if ((obj = get_obj_wear (argument)) != NULL)
return obj;
return NULL;
}
/*
* Find an obj in the world.
*/
Object * Character::get_obj_world (const std::string & argument)
{
std::string arg;
Object *obj;
if ((obj = get_obj_here (argument)) != NULL)
return obj;
int number = number_argument (argument, arg);
int count = 0;
for (ObjIter o = object_list.begin();
o != object_list.end(); o++) {
if (can_see_obj(*o) && is_name (arg, (*o)->name)) {
if (++count == number)
return *o;
}
}
return NULL;
}
/*
* Write the char.
*/
void Character::fwrite_char (std::ofstream & fp)
{
fp << "#" << (is_npc () ? "MOB" : "PLAYER") << "\n";
fp << "Name " << name << "~\n";
fp << "ShortDescr " << short_descr << "~\n";
fp << "LongDescr " << long_descr << "~\n";
fp << "Description " << description << "~\n";
fp << "Prompt " << prompt << "~\n";
fp << "Sex " << sex << "\n";
fp << "Class " << klass << "\n";
fp << "Race " << race << "\n";
fp << "Level " << level << "\n";
fp << "Trust " << trust << "\n";
fp << "Wizbit " << wizbit << "\n";
fp << "Played " << played + (int) (g_world->get_current_time() - logon) << "\n";
fp << "Note " << last_note << "\n";
fp << "Room " <<
((in_room == get_room_index (ROOM_VNUM_LIMBO)
&& was_in_room != NULL)
? was_in_room->vnum : in_room->vnum) << "\n";
fp << "HpManaMove " << hit << " " << max_hit << " " <<
mana << " " << max_mana << " " << move << " " <<
max_move << "\n";
fp << "Gold " << gold << "\n";
fp << "Exp " << exp << "\n";
fp << "Act " << actflags << "\n";
fp << "AffectedBy " << affected_by << "\n";
/* Bug fix from Alander */
if (position == POS_FIGHTING)
fp << "Position " << POS_STANDING<< "\n";
else
fp << "Position " << position << "\n";
fp << "Practice " << practice << "\n";
fp << "SavingThrow " << saving_throw << "\n";
fp << "Alignment " << alignment << "\n";
fp << "Hitroll " << hitroll << "\n";
fp << "Damroll " << damroll << "\n";
fp << "Armor " << armor << "\n";
fp << "Wimpy " << wimpy << "\n";
fp << "Deaf " << deaf << "\n";
if (is_npc ()) {
fp << "Vnum " << pIndexData->vnum << "\n";
} else {
fp << "Password " << pcdata->pwd << "~\n";
fp << "Bamfin " << pcdata->bamfin << "~\n";
fp << "Bamfout " << pcdata->bamfout << "~\n";
fp << "Title " << pcdata->title << "~\n";
fp << "AttrPerm " << pcdata->get_perm("str") << " " << pcdata->get_perm("int") <<
" " << pcdata->get_perm("wis") << " " << pcdata->get_perm("dex") << " " <<
pcdata->get_perm("con") << "\n";
fp << "AttrMod " << pcdata->get_mod("str") << " " << pcdata->get_mod("int") << " "
<< pcdata->get_mod("wis") << " " << pcdata->get_mod("dex") << " " <<
pcdata->get_mod("con") << "\n";
fp << "Condition " << pcdata->condition[0] << " " <<
pcdata->condition[1] << " " << pcdata->condition[2] << "\n";
fp << "Pagelen " << pcdata->pagelen << "\n";
for (int sn = 0; sn < MAX_SKILL; sn++) {
if (skill_table[sn].name != NULL && pcdata->learned[sn] > 0) {
fp << "Skill " << pcdata->learned[sn] << "'" <<
skill_table[sn].name << "'\n";
}
}
for (std::list<std::string>::iterator gag = pcdata->gag_list.begin();
gag != pcdata->gag_list.end(); gag++) {
fp << "Gag " << *gag << "~\n";
}
}
for (AffIter af = affected.begin(); af != affected.end(); af++) {
fp << "Affect " << (*af)->type << " " << (*af)->duration << " " <<
(*af)->modifier << " " << (*af)->location << " " <<
(*af)->bitvector << "\n";
}
fp << "End\n\n";
return;
}
/*
* Save a character and inventory.
* Would be cool to save NPC's too for quest purposes,
* some of the infrastructure is provided.
*/
void Character::save_char_obj ()
{
char strsave[MAX_INPUT_LENGTH];
std::ofstream fp;
if (is_npc () || level < 2)
return;
Character * ch = this;
if (desc != NULL && desc->original != NULL)
ch = desc->original;
ch->save_time = g_world->get_current_time();
/* player files parsed directories by Yaz 4th Realm */
snprintf (strsave, sizeof strsave, "%s%s", PLAYER_DIR, capitalize(ch->name).c_str());
fp.open (strsave, std::ofstream::out | std::ofstream::binary);
if (!fp.is_open()) {
bug_printf ("Save_char_obj: fopen");
std::perror (strsave);
} else {
ch->fwrite_char (fp);
std::list<Object*>::reverse_iterator o;
for (o = ch->carrying.rbegin(); o != ch->carrying.rend(); o++)
(*o)->fwrite_obj (ch, fp, 0);
fp << "#END\n";
}
fp.close();
return;
}
/*
* Read in a char.
*/
void Character::fread_char (std::ifstream & fp)
{
std::string word;
bool fMatch;
for (;;) {
word = fp.eof() ? std::string("End") : fread_word (fp);
fMatch = false;
switch (toupper (word[0])) {
case '*':
fMatch = true;
fread_to_eol (fp);
break;
case 'A':
KEY ("Act", actflags, fread_number (fp));
KEY ("AffectedBy", affected_by, fread_number (fp));
KEY ("Alignment", alignment, fread_number (fp));
KEY ("Armor", armor, fread_number (fp));
if (!str_cmp (word, "Affect")) {
Affect *paf;
paf = new Affect();
paf->type = fread_number (fp);
paf->duration = fread_number (fp);
paf->modifier = fread_number (fp);
paf->location = fread_number (fp);
paf->bitvector = fread_number (fp);
affected.push_back(paf);
fMatch = true;
break;
}
if (!str_cmp (word, "AttrMod")) {
pcdata->set_mod("str", fread_number (fp));
pcdata->set_mod("int", fread_number (fp));
pcdata->set_mod("wis", fread_number (fp));
pcdata->set_mod("dex", fread_number (fp));
pcdata->set_mod("con", fread_number (fp));
fMatch = true;
break;
}
if (!str_cmp (word, "AttrPerm")) {
pcdata->set_perm("str", fread_number (fp));
pcdata->set_perm("int", fread_number (fp));
pcdata->set_perm("wis", fread_number (fp));
pcdata->set_perm("dex", fread_number (fp));
pcdata->set_perm("con", fread_number (fp));
fMatch = true;
break;
}
break;
case 'B':
KEY ("Bamfin", pcdata->bamfin, fread_string (fp));
KEY ("Bamfout", pcdata->bamfout, fread_string (fp));
break;
case 'C':
KEY ("Class", klass, fread_number (fp));
if (!str_cmp (word, "Condition")) {
pcdata->condition[0] = fread_number (fp);
pcdata->condition[1] = fread_number (fp);
pcdata->condition[2] = fread_number (fp);
fMatch = true;
break;
}
break;
case 'D':
KEY ("Damroll", damroll, fread_number (fp));
KEY ("Deaf", deaf, fread_number (fp));
KEY ("Description", description, fread_string (fp));
break;
case 'E':
if (!str_cmp (word, "End"))
return;
KEY ("Exp", exp, fread_number (fp));
break;
case 'G':
KEY ("Gold", gold, fread_number (fp));
if (!str_cmp (word, "Gag")) {
pcdata->gag_list.push_back(fread_string (fp));
fMatch = true;
break;
}
break;
case 'H':
KEY ("Hitroll", hitroll, fread_number (fp));
if (!str_cmp (word, "HpManaMove")) {
hit = fread_number (fp);
max_hit = fread_number (fp);
mana = fread_number (fp);
max_mana = fread_number (fp);
move = fread_number (fp);
max_move = fread_number (fp);
fMatch = true;
break;
}
break;
case 'L':
KEY ("Level", level, fread_number (fp));
KEY ("LongDescr", long_descr, fread_string (fp));
break;
case 'N':
if (!str_cmp (word, "Name")) {
/*
* Name already set externally.
*/
fread_to_eol (fp);
fMatch = true;
break;
}
KEY ("Note", last_note, fread_number (fp));
break;
case 'P':
KEY ("Pagelen", pcdata->pagelen, fread_number (fp));
KEY ("Password", pcdata->pwd, fread_string (fp));
KEY ("Played", played, fread_number (fp));
KEY ("Position", position, fread_number (fp));
KEY ("Practice", practice, fread_number (fp));
KEY ("Prompt", prompt, fread_string (fp));
break;
case 'R':
KEY ("Race", race, fread_number (fp));
if (!str_cmp (word, "Room")) {
in_room = get_room_index (fread_number (fp));
if (in_room == NULL)
in_room = get_room_index (ROOM_VNUM_LIMBO);
fMatch = true;
break;
}
break;
case 'S':
KEY ("SavingThrow", saving_throw, fread_number (fp));
KEY ("Sex", sex, fread_number (fp));
KEY ("ShortDescr", short_descr, fread_string (fp));
if (!str_cmp (word, "Skill")) {
int sn;
int value;
value = fread_number (fp);
sn = skill_lookup (fread_word (fp));
if (sn < 0)
bug_printf ("Fread_char: unknown skill.");
else
pcdata->learned[sn] = value;
fMatch = true;
}
break;
case 'T':
KEY ("Trust", trust, fread_number (fp));
if (!str_cmp (word, "Title")) {
pcdata->title = fread_string (fp);
if (isalpha (pcdata->title[0]) || isdigit (pcdata->title[0])) {
pcdata->title = " " + pcdata->title;
}
fMatch = true;
break;
}
break;
case 'V':
if (!str_cmp (word, "Vnum")) {
pIndexData = get_mob_index (fread_number (fp));
fMatch = true;
break;
}
break;
case 'W':
KEY ("Wimpy", wimpy, fread_number (fp));
KEY ("Wizbit", wizbit, fread_number (fp));
break;
}
/* Make sure old chars have this field - Kahn */
if (!pcdata->pagelen)
pcdata->pagelen = 20;
if (prompt.empty())
prompt = "<%h %m %mv> ";
if (!fMatch) {
bug_printf ("Fread_char: no match.");
fread_to_eol (fp);
}
}
}
/*
* Equip a char with an obj.
*/
void Character::equip_char (Object * obj, int iWear)
{
if (get_eq_char (iWear) != NULL) {
bug_printf ("Equip_char: already equipped (%d).", iWear);
return;
}
if ((obj->is_obj_stat(ITEM_ANTI_EVIL) && is_evil ())
|| (obj->is_obj_stat(ITEM_ANTI_GOOD) && is_good ())
|| (obj->is_obj_stat(ITEM_ANTI_NEUTRAL) && is_neutral ())) {
/*
* Thanks to Morgenes for the bug fix here!
*/
act ("You are zapped by $p and drop it.", obj, NULL, TO_CHAR);
act ("$n is zapped by $p and drops it.", obj, NULL, TO_ROOM);
obj->obj_from_char();
obj->obj_to_room (in_room);
return;
}
armor -= obj->apply_ac (iWear);
obj->wear_loc = iWear;
AffIter af;
for (af = obj->pIndexData->affected.begin(); af != obj->pIndexData->affected.end(); af++)
affect_modify (*af, true);
for (af = obj->affected.begin(); af != obj->affected.end(); af++)
affect_modify (*af, true);
if (obj->item_type == ITEM_LIGHT
&& obj->value[2] != 0 && in_room != NULL)
++in_room->light;
if (obj->item_type == ITEM_DARKNESS
&& obj->value[2] != 0 && in_room != NULL)
--in_room->light;
return;
}
/*
* Move a char out of a room.
*/
void Character::char_from_room ()
{
Object *obj;
if (in_room == NULL) {
bug_printf ("Char_from_room: NULL.");
return;
}
if (!is_npc ())
--in_room->area->nplayer;
if ((obj = get_eq_char (WEAR_LIGHT)) != NULL
&& obj->item_type == ITEM_LIGHT
&& obj->value[2] != 0 && in_room->light > 0)
--in_room->light;
if ((obj = get_eq_char (WEAR_LIGHT)) != NULL
&& obj->item_type == ITEM_DARKNESS
&& obj->value[2] != 0 && in_room->light < 1)
++in_room->light;
deeprmnext = in_room->people.erase(
find(in_room->people.begin(), in_room->people.end(), this));
in_room = NULL;
return;
}
/*
* Move a char into a room.
*/
void Character::char_to_room (Room * pRoomIndex)
{
Object *obj;
if (pRoomIndex == NULL) {
bug_printf ("Char_to_room: NULL.");
return;
}
in_room = pRoomIndex;
pRoomIndex->people.push_back(this);
if (!is_npc ())
++in_room->area->nplayer;
if ((obj = get_eq_char (WEAR_LIGHT)) != NULL
&& obj->item_type == ITEM_LIGHT && obj->value[2] != 0)
++in_room->light;
if ((obj = get_eq_char (WEAR_LIGHT)) != NULL
&& obj->item_type == ITEM_DARKNESS && obj->value[2] != 0)
--in_room->light;
return;
}
void Character::set_title (const std::string & title)
{
if (is_npc ()) {
bug_printf ("Set_title: NPC.");
return;
}
if (isalpha (title[0]) || isdigit (title[0])) {
pcdata->title = " " + title;
} else {
pcdata->title = title;
}
return;
}
bool Character::is_switched ()
{
if (!is_npc () || desc == NULL)
return false;
return true;
}
bool Character::mp_commands ()
{ /* Can MOBProged mobs
use mpcommands? true if yes.
- Kahn */
if (is_switched())
return false;
if (is_npc ()
&& pIndexData->progtypes && !is_affected (AFF_CHARM))
return true;
return false;
}
/*
* Advancement stuff.
*/
void Character::advance_level ()
{
char buf[MAX_STRING_LENGTH];
int add_hp, add_mana, add_move, add_prac;
snprintf (buf, sizeof buf, "the %s", get_title(klass, level, sex).c_str());
set_title(buf);
add_hp = con_app[get_curr_con()].hitp +
number_range (class_table[klass].hp_min, class_table[klass].hp_max);
add_mana = class_table[klass].fMana ? number_range (2,
(2 * get_curr_int() + get_curr_wis()) / 8)
: 0;
add_move = number_range (5, (get_curr_con() + get_curr_dex()) / 4);
add_prac = wis_app[get_curr_wis()].practice;
add_hp = std::max (1, add_hp);
add_mana = std::max (0, add_mana);
add_move = std::max (10, add_move);
max_hit += add_hp;
max_mana += add_mana;
max_move += add_move;
practice += add_prac;
if (!is_npc ())
REMOVE_BIT (actflags, PLR_BOUGHT_PET);
snprintf (buf, sizeof buf,
"Your gain is: %d/%d hp, %d/%d m, %d/%d mv %d/%d prac.\r\n",
add_hp, max_hit, add_mana, max_mana, add_move, max_move, add_prac,
practice);
send_to_char (buf);
return;
}
void Character::gain_exp(int gain)
{
if (is_npc () || level >= LEVEL_HERO)
return;
exp = std::max (1000, exp + gain);
while (level < LEVEL_HERO && exp >= 1000 * (level + 1)) {
send_to_char ("You raise a level!! ");
level += 1;
advance_level();
}
return;
}
/*
* Regeneration stuff.
*/
int Character::hit_gain ()
{
int gain;
if (is_npc ()) {
gain = level * 3 / 2;
} else {
gain = std::min (5, level);
switch (position) {
case POS_SLEEPING:
gain += get_curr_con();
break;
case POS_RESTING:
gain += get_curr_con() / 2;
break;
}
if (pcdata->condition[COND_FULL] == 0)
gain /= 2;
if (pcdata->condition[COND_THIRST] == 0)
gain /= 2;
}
if (is_affected (AFF_POISON))
gain /= 4;
return std::min (gain, max_hit - hit);
}
int Character::mana_gain ()
{
int gain;
if (is_npc ()) {
gain = level;
} else {
gain = std::min (5, level / 2);
switch (position) {
case POS_SLEEPING:
gain += get_curr_int() * 2;
break;
case POS_RESTING:
gain += get_curr_int();
break;
}
if (pcdata->condition[COND_FULL] == 0)
gain /= 2;
if (pcdata->condition[COND_THIRST] == 0)
gain /= 2;
}
if (is_affected (AFF_POISON))
gain /= 4;
return std::min (gain, max_mana - mana);
}
int Character::move_gain ()
{
int gain;
if (is_npc ()) {
gain = level;
} else {
gain = std::max (15, 2 * level);
switch (position) {
case POS_SLEEPING:
gain += get_curr_dex();
break;
case POS_RESTING:
gain += get_curr_dex() / 2;
break;
}
if (pcdata->condition[COND_FULL] == 0)
gain /= 2;
if (pcdata->condition[COND_THIRST] == 0)
gain /= 2;
}
if (is_affected (AFF_POISON))
gain /= 4;
return std::min (gain, max_move - move);
}
void Character::gain_condition (int iCond, int value)
{
if (value == 0 || is_npc () || level >= LEVEL_HERO)
return;
int condition = pcdata->condition[iCond];
pcdata->condition[iCond] = URANGE (0, condition + value, 48);
if (pcdata->condition[iCond] == 0) {
switch (iCond) {
case COND_FULL:
send_to_char ("You are hungry.\r\n");
break;
case COND_THIRST:
send_to_char ("You are thirsty.\r\n");
break;
case COND_DRUNK:
if (condition != 0)
send_to_char ("You are sober.\r\n");
break;
}
}
return;
}
void Character::add_follower (Character * m)
{
if (master != NULL) {
bug_printf ("Add_follower: non-null master.");
return;
}
master = m;
leader = NULL;
if (m->can_see(this))
act ("$n now follows you.", NULL, m, TO_VICT);
act ("You now follow $N.", NULL, m, TO_CHAR);
return;
}
void Character::stop_follower()
{
if (master == NULL) {
bug_printf ("Stop_follower: null master.");
return;
}
if (is_affected (AFF_CHARM)) {
REMOVE_BIT (affected_by, AFF_CHARM);
affect_strip (skill_lookup("charm person"));
}
if (master->can_see(this))
act ("$n stops following you.", NULL, master, TO_VICT);
act ("You stop following $N.", NULL, master, TO_CHAR);
master = NULL;
leader = NULL;
return;
}
void Character::die_follower ()
{
if (master != NULL)
stop_follower();
leader = NULL;
for (CharIter c = char_list.begin(); c != char_list.end(); c++) {
if ((*c)->master == this)
(*c)->stop_follower();
if ((*c)->leader == this)
(*c)->leader = *c;
}
return;
}
/*
* Set position of a victim.
*/
void Character::update_pos ()
{
if (hit > 0) {
if (position <= POS_STUNNED)
position = POS_STANDING;
return;
}
if (is_npc () || hit <= -11) {
position = POS_DEAD;
return;
}
if (hit <= -6)
position = POS_MORTAL;
else if (hit <= -3)
position = POS_INCAP;
else
position = POS_STUNNED;
return;
}
/*
* Start fights.
*/
void Character::set_fighting (Character * victim)
{
if (fighting != NULL) {
bug_printf ("Set_fighting: already fighting");
return;
}
if (is_affected (AFF_SLEEP))
affect_strip (skill_lookup("sleep"));
fighting = victim;
position = POS_FIGHTING;
return;
}
/*
* Stop fights.
*/
void Character::stop_fighting (bool fBoth)
{
CharIter c;
for (c = char_list.begin(); c != char_list.end(); c++) {
if (*c == this || (fBoth && (*c)->fighting == this)) {
(*c)->fighting = NULL;
(*c)->position = POS_STANDING;
(*c)->update_pos();
}
}
return;
}
bool Character::check_blind ()
{
if (!is_npc () && IS_SET (actflags, PLR_HOLYLIGHT))
return true;
if (is_affected (AFF_BLIND)) {
send_to_char ("You can't see a thing!\r\n");
return false;
}
return true;
}
int Character::find_door (const std::string & arg)
{
Exit *pexit;
int door;
if (!str_cmp (arg, "n") || !str_cmp (arg, "north"))
door = 0;
else if (!str_cmp (arg, "e") || !str_cmp (arg, "east"))
door = 1;
else if (!str_cmp (arg, "s") || !str_cmp (arg, "south"))
door = 2;
else if (!str_cmp (arg, "w") || !str_cmp (arg, "west"))
door = 3;
else if (!str_cmp (arg, "u") || !str_cmp (arg, "up"))
door = 4;
else if (!str_cmp (arg, "d") || !str_cmp (arg, "down"))
door = 5;
else {
for (door = 0; door <= 5; door++) {
if ((pexit = in_room->exit[door]) != NULL
&& IS_SET (pexit->exit_info, EX_ISDOOR)
&& !pexit->name.empty() && is_name (arg, pexit->name))
return door;
}
act ("I see no $T here.", NULL, arg.c_str(), TO_CHAR);
return -1;
}
if ((pexit = in_room->exit[door]) == NULL) {
act ("I see no door $T here.", NULL, arg.c_str(), TO_CHAR);
return -1;
}
if (!IS_SET (pexit->exit_info, EX_ISDOOR)) {
send_to_char ("You can't do that.\r\n");
return -1;
}
return door;
}
bool Character::has_key (int key)
{
ObjIter o;
for (o = carrying.begin(); o != carrying.end(); o++) {
if ((*o)->pIndexData->vnum == key)
return true;
}
return false;
}
void Character::get_obj (Object * obj, Object * container)
{
if (!obj->can_wear(ITEM_TAKE)) {
send_to_char ("You can't take that.\r\n");
return;
}
if (carry_number + obj->get_obj_number() > can_carry_n()) {
act ("$d: you can't carry that many items.",
NULL, obj->name.c_str(), TO_CHAR);
return;
}
if (carry_weight + obj->get_obj_weight() > can_carry_w()) {
act ("$d: you can't carry that much weight.",
NULL, obj->name.c_str(), TO_CHAR);
return;
}
if (container != NULL) {
act ("You get $p from $P.", obj, container, TO_CHAR);
act ("$n gets $p from $P.", obj, container, TO_ROOM);
obj->obj_from_obj ();
} else {
act ("You get $p.", obj, container, TO_CHAR);
act ("$n gets $p.", obj, container, TO_ROOM);
obj->obj_from_room ();
}
if (obj->item_type == ITEM_MONEY) {
gold += obj->value[0];
obj->extract_obj ();
} else {
obj->obj_to_char (this);
}
return;
}
/*
* Flag a character as extractable
*/
void Character::extract_char (bool fPull)
{
if (is_npc())
SET_BIT(actflags, ACT_EXTRACT);
else
SET_BIT(actflags, PLR_EXTRACT);
extract_chars = true;
}
/*
* Extract a char from the world.
*/
void Character::extract_char_old (bool fPull)
{
Object *obj;
if (in_room == NULL) {
bug_printf ("Extract_char: NULL.");
return;
}
if (fPull)
die_follower();
stop_fighting (true);
ObjIter o, onext;
for (o = carrying.begin(); o != carrying.end(); o = onext) {
obj = *o;
onext = ++o;
obj->extract_obj();
}
char_from_room ();
if (!fPull) {
char_to_room(get_room_index (ROOM_VNUM_ALTAR));
return;
}
if (is_npc ())
--pIndexData->count;
if (desc != NULL && desc->original != NULL)
do_return ("");
CharIter c;
for (c = char_list.begin(); c != char_list.end(); c++) {
if ((*c)->reply == this)
(*c)->reply = NULL;
}
deepchnext = char_list.erase(find(char_list.begin(), char_list.end(), this));
if (desc)
desc->character = NULL;
delete this;
return;
}
void Character::stop_idling ()
{
if (desc == NULL || desc->connected != CON_PLAYING
|| was_in_room == NULL || in_room != get_room_index (ROOM_VNUM_LIMBO))
return;
timer = 0;
char_from_room();
char_to_room(was_in_room);
was_in_room = NULL;
act ("$n has returned from the void.", NULL, NULL, TO_ROOM);
return;
}
/*
* Remove an object.
*/
bool Character::remove_obj (int iWear, bool fReplace)
{
Object *obj;
if ((obj = get_eq_char (iWear)) == NULL)
return true;
if (!fReplace)
return false;
if (IS_SET (obj->extra_flags, ITEM_NOREMOVE)) {
act ("You can't remove $p.", obj, NULL, TO_CHAR);
return false;
}
unequip_char(obj);
act ("$n stops using $p.", obj, NULL, TO_ROOM);
act ("You stop using $p.", obj, NULL, TO_CHAR);
return true;
}
/*
* Wear one object.
* Optional replacement of existing objects.
* Big repetitive code, ick.
*/
void Character::wear_obj (Object * obj, bool fReplace)
{
char buf[MAX_STRING_LENGTH];
if (level < obj->level) {
snprintf (buf, sizeof buf, "You must be level %d to use this object.\r\n", obj->level);
send_to_char (buf);
act ("$n tries to use $p, but is too inexperienced.",
obj, NULL, TO_ROOM);
return;
}
if (obj->item_type == ITEM_LIGHT) {
if (!remove_obj (WEAR_LIGHT, fReplace))
return;
act ("$n lights $p and holds it.", obj, NULL, TO_ROOM);
act ("You light $p and hold it.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_LIGHT);
return;
}
if (obj->item_type == ITEM_DARKNESS) {
if (!remove_obj (WEAR_LIGHT, fReplace))
return;
act ("$n darkens $p and holds it.", obj, NULL, TO_ROOM);
act ("You darken $p and hold it.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_LIGHT);
return;
}
if (obj->can_wear(ITEM_WEAR_FINGER)) {
if (get_eq_char (WEAR_FINGER_L) != NULL
&& get_eq_char (WEAR_FINGER_R) != NULL
&& !remove_obj (WEAR_FINGER_L, fReplace)
&& !remove_obj (WEAR_FINGER_R, fReplace))
return;
if (get_eq_char (WEAR_FINGER_L) == NULL) {
act ("$n wears $p on $s left finger.", obj, NULL, TO_ROOM);
act ("You wear $p on your left finger.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_FINGER_L);
return;
}
if (get_eq_char (WEAR_FINGER_R) == NULL) {
act ("$n wears $p on $s right finger.", obj, NULL, TO_ROOM);
act ("You wear $p on your right finger.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_FINGER_R);
return;
}
bug_printf ("Wear_obj: no free finger.");
send_to_char ("You already wear two rings.\r\n");
return;
}
if (obj->can_wear(ITEM_WEAR_NECK)) {
if (get_eq_char (WEAR_NECK_1) != NULL
&& get_eq_char (WEAR_NECK_2) != NULL
&& !remove_obj (WEAR_NECK_1, fReplace)
&& !remove_obj (WEAR_NECK_2, fReplace))
return;
if (get_eq_char (WEAR_NECK_1) == NULL) {
act ("$n wears $p around $s neck.", obj, NULL, TO_ROOM);
act ("You wear $p around your neck.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_NECK_1);
return;
}
if (get_eq_char (WEAR_NECK_2) == NULL) {
act ("$n wears $p around $s neck.", obj, NULL, TO_ROOM);
act ("You wear $p around your neck.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_NECK_2);
return;
}
bug_printf ("Wear_obj: no free neck.");
send_to_char ("You already wear two neck items.\r\n");
return;
}
if (obj->can_wear(ITEM_WEAR_BODY)) {
if (!remove_obj (WEAR_BODY, fReplace))
return;
act ("$n wears $p on $s body.", obj, NULL, TO_ROOM);
act ("You wear $p on your body.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_BODY);
return;
}
if (obj->can_wear(ITEM_WEAR_HEAD)) {
if (!remove_obj (WEAR_HEAD, fReplace))
return;
act ("$n wears $p on $s head.", obj, NULL, TO_ROOM);
act ("You wear $p on your head.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_HEAD);
return;
}
if (obj->can_wear(ITEM_WEAR_LEGS)) {
if (!remove_obj (WEAR_LEGS, fReplace))
return;
act ("$n wears $p on $s legs.", obj, NULL, TO_ROOM);
act ("You wear $p on your legs.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_LEGS);
return;
}
if (obj->can_wear(ITEM_WEAR_FEET)) {
if (!remove_obj (WEAR_FEET, fReplace))
return;
act ("$n wears $p on $s feet.", obj, NULL, TO_ROOM);
act ("You wear $p on your feet.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_FEET);
return;
}
if (obj->can_wear(ITEM_WEAR_HANDS)) {
if (!remove_obj (WEAR_HANDS, fReplace))
return;
act ("$n wears $p on $s hands.", obj, NULL, TO_ROOM);
act ("You wear $p on your hands.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_HANDS);
return;
}
if (obj->can_wear(ITEM_WEAR_ARMS)) {
if (!remove_obj (WEAR_ARMS, fReplace))
return;
act ("$n wears $p on $s arms.", obj, NULL, TO_ROOM);
act ("You wear $p on your arms.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_ARMS);
return;
}
if (obj->can_wear(ITEM_WEAR_ABOUT)) {
if (!remove_obj (WEAR_ABOUT, fReplace))
return;
act ("$n wears $p about $s body.", obj, NULL, TO_ROOM);
act ("You wear $p about your body.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_ABOUT);
return;
}
if (obj->can_wear(ITEM_WEAR_WAIST)) {
if (!remove_obj (WEAR_WAIST, fReplace))
return;
act ("$n wears $p about $s waist.", obj, NULL, TO_ROOM);
act ("You wear $p about your waist.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_WAIST);
return;
}
if (obj->can_wear(ITEM_WEAR_WRIST)) {
if (get_eq_char (WEAR_WRIST_L) != NULL
&& get_eq_char (WEAR_WRIST_R) != NULL
&& !remove_obj (WEAR_WRIST_L, fReplace)
&& !remove_obj (WEAR_WRIST_R, fReplace))
return;
if (get_eq_char (WEAR_WRIST_L) == NULL) {
act ("$n wears $p around $s left wrist.", obj, NULL, TO_ROOM);
act ("You wear $p around your left wrist.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_WRIST_L);
return;
}
if (get_eq_char (WEAR_WRIST_R) == NULL) {
act ("$n wears $p around $s right wrist.", obj, NULL, TO_ROOM);
act ("You wear $p around your right wrist.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_WRIST_R);
return;
}
bug_printf ("Wear_obj: no free wrist.");
send_to_char ("You already wear two wrist items.\r\n");
return;
}
if (obj->can_wear( ITEM_WEAR_SHIELD)) {
if (!remove_obj (WEAR_SHIELD, fReplace))
return;
act ("$n wears $p as a shield.", obj, NULL, TO_ROOM);
act ("You wear $p as a shield.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_SHIELD);
return;
}
if (obj->can_wear(ITEM_WIELD)) {
if (!remove_obj (WEAR_WIELD, fReplace))
return;
if (obj->get_obj_weight() > str_app[get_curr_str()].wield) {
send_to_char ("It is too heavy for you to wield.\r\n");
return;
}
act ("$n wields $p.", obj, NULL, TO_ROOM);
act ("You wield $p.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_WIELD);
return;
}
if (obj->can_wear(ITEM_HOLD)) {
if (!remove_obj (WEAR_HOLD, fReplace))
return;
act ("$n holds $p in $s hands.", obj, NULL, TO_ROOM);
act ("You hold $p in your hands.", obj, NULL, TO_CHAR);
equip_char (obj, WEAR_HOLD);
return;
}
if (fReplace)
send_to_char ("You can't wear, wield, or hold that.\r\n");
return;
}
/*
* Show a list to a character.
* Can coalesce duplicated items.
*/
void Character::show_list_to_char (std::list<Object *> & list, bool fShort,
bool fShowNothing)
{
char buf[MAX_STRING_LENGTH];
int nShow;
int iShow;
bool fCombine;
if (desc == NULL)
return;
/*
* Alloc space for output lines.
*/
std::string * prgpstrShow = new std::string[list.size()];
int * prgnShow = new int[list.size()];
nShow = 0;
/*
* Format the list of objects.
*/
ObjIter obj;
for (obj = list.begin(); obj != list.end(); obj++) {
if ((*obj)->wear_loc == WEAR_NONE && can_see_obj(*obj)) {
std::string pstrShow = (*obj)->format_obj_to_char (this, fShort);
fCombine = false;
if (is_npc () || IS_SET (actflags, PLR_COMBINE)) {
/*
* Look for duplicates, case sensitive.
* Matches tend to be near end so run loop backwords.
*/
for (iShow = nShow - 1; iShow >= 0; iShow--) {
if (!strcmp (prgpstrShow[iShow].c_str(), pstrShow.c_str())) {
prgnShow[iShow]++;
fCombine = true;
break;
}
}
}
/*
* Couldn't combine, or didn't want to.
*/
if (!fCombine) {
prgpstrShow[nShow] = pstrShow;
prgnShow[nShow] = 1;
nShow++;
}
}
}
/*
* Output the formatted list.
*/
for (iShow = 0; iShow < nShow; iShow++) {
if (is_npc () || IS_SET (actflags, PLR_COMBINE)) {
if (prgnShow[iShow] != 1) {
snprintf (buf, sizeof buf, "(%2d) ", prgnShow[iShow]);
send_to_char (buf);
} else {
send_to_char (" ");
}
}
send_to_char (prgpstrShow[iShow]);
send_to_char ("\r\n");
}
if (fShowNothing && nShow == 0) {
if (is_npc () || IS_SET (actflags, PLR_COMBINE))
send_to_char (" ");
send_to_char ("Nothing.\r\n");
}
/*
* Clean up.
*/
delete [] prgnShow;
delete [] prgpstrShow;
return;
}
void Character::show_char_to_char_0 (Character * victim)
{
std::string buf;
if (victim->is_affected (AFF_INVISIBLE))
buf.append("(Invis) ");
if (victim->is_affected (AFF_HIDE))
buf.append("(Hide) ");
if (victim->is_affected (AFF_CHARM))
buf.append("(Charmed) ");
if (victim->is_affected (AFF_PASS_DOOR))
buf.append("(Translucent) ");
if (victim->is_affected (AFF_FAERIE_FIRE))
buf.append("(Pink Aura) ");
if (victim->is_evil () && is_affected (AFF_DETECT_EVIL))
buf.append("(Red Aura) ");
if (victim->is_affected (AFF_SANCTUARY))
buf.append("(White Aura) ");
if (!victim->is_npc () && IS_SET (victim->actflags, PLR_KILLER))
buf.append("(KILLER) ");
if (!victim->is_npc () && IS_SET (victim->actflags, PLR_THIEF))
buf.append("(THIEF) ");
if (victim->position == POS_STANDING && !victim->long_descr.empty()) {
buf.append(victim->long_descr);
send_to_char (buf);
return;
}
buf.append(victim->describe_to(this));
if (!victim->is_npc () && !IS_SET (actflags, PLR_BRIEF))
buf.append(victim->pcdata->title);
switch (victim->position) {
case POS_DEAD:
buf.append(" is DEAD!!");
break;
case POS_MORTAL:
buf.append(" is mortally wounded.");
break;
case POS_INCAP:
buf.append(" is incapacitated.");
break;
case POS_STUNNED:
buf.append(" is lying here stunned.");
break;
case POS_SLEEPING:
buf.append(" is sleeping here.");
break;
case POS_RESTING:
buf.append(" is resting here.");
break;
case POS_STANDING:
buf.append(" is here.");
break;
case POS_FIGHTING:
buf.append(" is here, fighting ");
if (victim->fighting == NULL)
buf.append("thin air??");
else if (victim->fighting == this)
buf.append("YOU!");
else if (victim->in_room == victim->fighting->in_room) {
buf.append(victim->fighting->describe_to(this));
buf.append(".");
} else
buf.append("somone who left??");
break;
}
buf.append("\r\n");
buf[0] = toupper(buf[0]);
send_to_char (buf);
return;
}
void Character::show_char_to_char_1 (Character * victim)
{
std::string buf;
Object *obj;
int iWear;
int percent;
bool found;
if (victim->can_see(this)) {
act ("$n looks at you.", NULL, victim, TO_VICT);
act ("$n looks at $N.", NULL, victim, TO_NOTVICT);
}
if (victim->description[0] != '\0') {
send_to_char (victim->description);
} else {
act ("You see nothing special about $M.", NULL, victim, TO_CHAR);
}
if (victim->max_hit > 0)
percent = (100 * victim->hit) / victim->max_hit;
else
percent = -1;
buf = victim->describe_to(this);
if (percent >= 100)
buf.append(" is in perfect health.\r\n");
else if (percent >= 90)
buf.append(" is slightly scratched.\r\n");
else if (percent >= 80)
buf.append(" has a few bruises.\r\n");
else if (percent >= 70)
buf.append(" has some cuts.\r\n");
else if (percent >= 60)
buf.append(" has several wounds.\r\n");
else if (percent >= 50)
buf.append(" has many nasty wounds.\r\n");
else if (percent >= 40)
buf.append(" is bleeding freely.\r\n");
else if (percent >= 30)
buf.append(" is covered in blood.\r\n");
else if (percent >= 20)
buf.append(" is leaking guts.\r\n");
else if (percent >= 10)
buf.append(" is almost dead.\r\n");
else
buf.append(" is DYING.\r\n");
buf[0] = toupper (buf[0]);
send_to_char (buf);
found = false;
for (iWear = 0; iWear < MAX_WEAR; iWear++) {
if ((obj = victim->get_eq_char (iWear)) != NULL && can_see_obj(obj)) {
if (!found) {
send_to_char ("\r\n");
act ("$N is using:", NULL, victim, TO_CHAR);
found = true;
}
send_to_char (where_name[iWear]);
send_to_char (obj->format_obj_to_char (this, true));
send_to_char ("\r\n");
}
}
if (victim != this && !is_npc ()
&& number_percent () < pcdata->learned[skill_lookup("peek")]) {
send_to_char ("\r\nYou peek at the inventory:\r\n");
show_list_to_char (victim->carrying, true, true);
}
return;
}
void Character::show_char_to_char (std::list<Character *> & list)
{
CharIter rch;
for (rch = list.begin(); rch != list.end(); rch++) {
if (*rch == this)
continue;
if (can_see(*rch)) {
show_char_to_char_0 (*rch);
} else if (in_room->is_dark()
&& (*rch)->is_affected (AFF_INFRARED)) {
send_to_char ("You see glowing red eyes watching YOU!\r\n");
}
}
return;
}
void Character::move_char (int door)
{
Character *fch;
Room *in_rm;
Room *to_room;
Exit *pexit;
if (door < 0 || door > 5) {
bug_printf ("Do_move: bad door %d.", door);
return;
}
in_rm = in_room;
if ((pexit = in_rm->exit[door]) == NULL
|| (to_room = pexit->to_room) == NULL) {
send_to_char ("Alas, you cannot go that way.\r\n");
return;
}
if (IS_SET (pexit->exit_info, EX_CLOSED)
&& !is_affected (AFF_PASS_DOOR)) {
act ("The $d is closed.", NULL, pexit->name.c_str(), TO_CHAR);
return;
}
if (is_affected (AFF_CHARM)
&& master != NULL && in_rm == master->in_room) {
send_to_char ("What? And leave your beloved master?\r\n");
return;
}
if (to_room->is_private()) {
send_to_char ("That room is private right now.\r\n");
return;
}
if (!is_npc ()) {
int iClass;
int mv;
for (iClass = 0; iClass < CLASS_MAX; iClass++) {
if (iClass != klass && to_room->vnum == class_table[iClass].guild) {
send_to_char ("You aren't allowed in there.\r\n");
return;
}
}
if (in_rm->sector_type == SECT_AIR || to_room->sector_type == SECT_AIR) {
if (!is_affected (AFF_FLYING)) {
send_to_char ("You can't fly.\r\n");
return;
}
}
if (in_rm->sector_type == SECT_WATER_NOSWIM
|| to_room->sector_type == SECT_WATER_NOSWIM) {
/*
* Look for a boat.
*/
bool found = false;
/*
* Suggestion for flying above water by Sludge
*/
if (is_affected (AFF_FLYING))
found = true;
for (ObjIter o = carrying.begin(); o != carrying.end(); o++) {
if ((*o)->item_type == ITEM_BOAT) {
found = true;
break;
}
}
if (!found) {
send_to_char ("You need a boat to go there.\r\n");
return;
}
}
mv = movement_loss[std::min ((int)SECT_MAX - 1, in_rm->sector_type)]
+ movement_loss[std::min ((int)SECT_MAX - 1, to_room->sector_type)];
if (move < mv) {
send_to_char ("You are too exhausted.\r\n");
return;
}
wait_state (1);
move -= mv;
}
if (!is_affected (AFF_SNEAK))
act ("$n leaves $T.", NULL, dir_name[door].c_str(), TO_ROOM);
char_from_room();
char_to_room(to_room);
if (!is_affected (AFF_SNEAK))
act ("$n has arrived.", NULL, NULL, TO_ROOM);
do_look ("auto");
CharIter rch, next;
for (rch = in_rm->people.begin(); rch != in_rm->people.end(); rch = next) {
fch = *rch;
next = ++rch;
if (fch->master == this && fch->position == POS_STANDING) {
fch->act ("You follow $N.", NULL, this, TO_CHAR);
fch->move_char (door);
}
}
if (this)
mprog_entry_trigger (this);
if (this)
mprog_greet_trigger (this);
return;
}
bool Character::check_social (const std::string & command, const std::string & argument)
{
std::string arg;
Character *victim;
char *sql = sqlite3_mprintf(
"SELECT name, char_no_arg, others_no_arg, char_found, others_found, vict_found, char_auto, others_auto FROM socials WHERE NAME LIKE '%q%%'",
command.c_str());
sqlite3_stmt *stmt = NULL;
if (sqlite3_prepare(g_db->database, sql, -1, &stmt, 0) != SQLITE_OK) {
bug_printf("Could not prepare statement: '%s' Error: %s", sql, sqlite3_errmsg(g_db->database));
sqlite3_free(sql);
return false;
}
if (sqlite3_step(stmt) != SQLITE_ROW) {
sqlite3_finalize(stmt);
sqlite3_free(sql);
return false;
}
if (!is_npc () && IS_SET (actflags, PLR_NO_EMOTE)) {
send_to_char ("You are anti-social!\r\n");
sqlite3_finalize(stmt);
sqlite3_free(sql);
return true;
}
switch (position) {
case POS_DEAD:
send_to_char ("Lie still; you are DEAD.\r\n");
sqlite3_finalize(stmt);
sqlite3_free(sql);
return true;
case POS_INCAP:
case POS_MORTAL:
send_to_char ("You are hurt far too bad for that.\r\n");
sqlite3_finalize(stmt);
sqlite3_free(sql);
return true;
case POS_STUNNED:
send_to_char ("You are too stunned to do that.\r\n");
sqlite3_finalize(stmt);
sqlite3_free(sql);
return true;
case POS_SLEEPING:
/*
* I just know this is the path to a 12" 'if' statement. :(
* But two players asked for it already! -- Furey
*/
if (!str_cmp ((const char*)sqlite3_column_text( stmt, 0 ), "snore"))
break;
send_to_char ("In your dreams, or what?\r\n");
sqlite3_finalize(stmt);
sqlite3_free(sql);
return true;
}
one_argument (argument, arg);
victim = NULL;
if (arg.empty()) {
act ((const char*)sqlite3_column_text( stmt, 2 ), NULL, victim, TO_ROOM);
act ((const char*)sqlite3_column_text( stmt, 1 ), NULL, victim, TO_CHAR);
} else if ((victim = get_char_room (arg)) == NULL) {
send_to_char ("They aren't here.\r\n");
} else if (victim == this) {
act ((const char*)sqlite3_column_text( stmt, 7 ), NULL, victim, TO_ROOM);
act ((const char*)sqlite3_column_text( stmt, 6 ), NULL, victim, TO_CHAR);
} else {
act ((const char*)sqlite3_column_text( stmt, 4 ), NULL, victim, TO_NOTVICT);
act ((const char*)sqlite3_column_text( stmt, 3 ), NULL, victim, TO_CHAR);
act ((const char*)sqlite3_column_text( stmt, 5 ), NULL, victim, TO_VICT);
if (!is_npc () && victim->is_npc ()
&& !victim->is_affected (AFF_CHARM)
&& victim->is_awake ()) {
switch (number_range (0, 15)) {
case 0:
multi_hit (victim, this, TYPE_UNDEFINED);
break;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
victim->act ((const char*)sqlite3_column_text( stmt, 4 ), NULL, this, TO_NOTVICT);
victim->act ((const char*)sqlite3_column_text( stmt, 3 ), NULL, this, TO_CHAR);
victim->act ((const char*)sqlite3_column_text( stmt, 5 ), NULL, this, TO_VICT);
break;
case 9:
case 10:
case 11:
case 12:
victim->act ("$n slaps $N.", NULL, this, TO_NOTVICT);
victim->act ("You slap $N.", NULL, this, TO_CHAR);
victim->act ("$n slaps you.", NULL, this, TO_VICT);
break;
}
}
}
sqlite3_finalize(stmt);
sqlite3_free(sql);
return true;
}
/*
* The main entry point for executing commands.
* Can be recursively called from 'at', 'order', 'force'.
*/
void Character::interpret (std::string argument)
{
std::string command;
int cmd;
bool found;
if (desc != NULL)
desc->incomm.erase();
/*
* Strip leading spaces.
*/
argument.erase(0, argument.find_first_not_of(" "));
if (argument.empty())
return;
/*
* No hiding.
*/
REMOVE_BIT (affected_by, AFF_HIDE);
/*
* Implement freeze command.
*/
if (!is_npc () && IS_SET (actflags, PLR_FREEZE)) {
send_to_char ("You're totally frozen!\r\n");
return;
}
/*
* Grab the command word.
* Special parsing so ' can be a command,
* also no spaces needed after punctuation.
*/
if (!isalpha (argument[0]) && !isdigit (argument[0])) {
command.assign(argument, 0, 1);
argument.erase(0, 1);
argument.erase(0, argument.find_first_not_of(" "));
} else {
argument = one_argument(argument, command);
}
/*
* Look for command in command table.
*/
found = false;
int trst = get_trust ();
for (cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++) {
if (command[0] == cmd_table[cmd].name[0]
&& !str_prefix (command, cmd_table[cmd].name)
&& (cmd_table[cmd].level <= trst || mp_commands())) {
found = true;
break;
}
}
if (!found) {
/*
* Look for command in socials table.
*/
if (!check_social (command, argument))
send_to_char ("Huh?\r\n");
return;
}
/*
* Character not in position for command?
*/
if (position < cmd_table[cmd].position) {
switch (position) {
case POS_DEAD:
send_to_char ("Lie still; you are DEAD.\r\n");
break;
case POS_MORTAL:
case POS_INCAP:
send_to_char ("You are hurt far too bad for that.\r\n");
break;
case POS_STUNNED:
send_to_char ("You are too stunned to do that.\r\n");
break;
case POS_SLEEPING:
send_to_char ("In your dreams, or what?\r\n");
break;
case POS_RESTING:
send_to_char ("Nah... You feel too relaxed...\r\n");
break;
case POS_FIGHTING:
send_to_char ("No way! You are still fighting!\r\n");
break;
}
return;
}
/*
* Dispatch the command.
*/
(this->*(cmd_table[cmd].do_fun)) (argument);
return;
}
bool Character::is_gagged(std::string & nm) {
if (is_npc ())
return false;
std::string gagname = capitalize(nm);
std::list<std::string>::iterator fnd;
fnd = find(pcdata->gag_list.begin(), pcdata->gag_list.end(), gagname);
if (fnd != pcdata->gag_list.end())
return true;
return false;
}