/***************************************************************************
* 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++
*/
/*-----------------------------------------------------------------------*/
/* OS DEPENDENT INCLUDES AND DEFINITIONS */
/*-----------------------------------------------------------------------*/
#include "os.hpp"
/*-----------------------------------------------------------------------*/
/* MurkMUD++ BEGINS HERE */
/*-----------------------------------------------------------------------*/
#include "config.hpp"
#include "utils.hpp"
#include "io.hpp"
#include "database.hpp"
#include "symbols.hpp"
#include "world.hpp"
/*
* Globals.
*/
World *g_world = NULL;
Database *g_db = NULL;
Area *area_last = NULL;
std::list<Ban *> ban_list;
std::list<Character *> char_list;
std::list<Descriptor *> descriptor_list; /* All open descriptors */
std::list<Note *> note_list;
std::list<Object *> object_list;
std::list<Shop *> shop_list;
// These iterators used on loops where the next iterator can be invalidated
// because of a nested method that erases an object in the list.
CharIter deepchnext, deeprmnext;
ObjIter deepobnext;
DescIter deepdenext;
//bool character_invalidated = false; // This is set in Mprogs if we
std::map<int, MobPrototype *> mob_table;
std::map<int, ObjectPrototype *> obj_table;
std::map<int, Room *> room_table;
struct kill_data kill_table[MAX_LEVEL];
/*
* Global variables.
*/
bool merc_down; /* Shutdown */
bool wizlock; /* Game is wizlocked */
std::string str_boot_time;
std::string help_greeting;
short g_port;
SOCKET g_listen;
bool extract_chars;
/*
* The kludgy global is for spells who want more stuff from command line.
*/
std::string target_name;
std::ifstream * fpArea;
std::string strArea;
/*
* Array of containers read for proper re-nesting of objects.
*/
Object *rgObjNest[MAX_NEST];
bool MOBtrigger;
std::string dir_name[] = {
"north", "east", "south", "west", "up", "down"
};
sh_int rev_dir[] = {
2, 3, 0, 1, 5, 4
};
std::string where_name[] = {
"<used as light> ",
"<worn on finger> ",
"<worn on finger> ",
"<worn around neck> ",
"<worn around neck> ",
"<worn on body> ",
"<worn on head> ",
"<worn on legs> ",
"<worn on feet> ",
"<worn on hands> ",
"<worn on arms> ",
"<worn as shield> ",
"<worn about body> ",
"<worn about waist> ",
"<worn around wrist> ",
"<worn around wrist> ",
"<wielded> ",
"<held> "
};
/*
* Class table.
*/
struct class_type class_table[] = {
{ "Mag", APPLY_INT, OBJ_VNUM_SCHOOL_DAGGER,
3018, 95, 18, 10, 6, 8, true},
{ "Cle", APPLY_WIS, OBJ_VNUM_SCHOOL_MACE,
3003, 95, 18, 12, 7, 10, true},
{ "Thi", APPLY_DEX, OBJ_VNUM_SCHOOL_DAGGER,
3028, 85, 18, 8, 8, 13, false},
{ "War", APPLY_STR, OBJ_VNUM_SCHOOL_SWORD,
3022, 85, 18, 6, 11, 15, false}
};
/*
* Attribute bonus structures.
*/
struct str_app_type str_app[26] = {
{-5, -4, 0, 0}, {-5, -4, 3, 1}, {-3, -2, 3, 2}, {-3, -1, 10, 3},
{-2, -1, 25, 4}, {-2, -1, 55, 5}, {-1, 0, 80, 6}, {-1, 0, 90, 7},
{0, 0, 100, 8}, {0, 0, 100, 9}, {0, 0, 115, 10}, {0, 0, 115, 11},
{0, 0, 140, 12}, {0, 0, 140, 13}, {0, 1, 170, 14}, {1, 1, 170, 15}, /* 15 */
{1, 2, 195, 16}, {2, 3, 220, 22}, {2, 4, 250, 25}, {3, 5, 400, 30},
{3, 6, 500, 35}, {4, 7, 600, 40}, {5, 7, 700, 45}, {6, 8, 800, 50},
{8, 10, 900, 55}, {10, 12, 999, 60} /* 25 */
};
struct int_app_type int_app[26] = {
{3}, {5}, {7}, {8}, {9}, {10}, /* 5 */
{11}, {12}, {13}, {15}, {17}, /* 10 */
{19}, {22}, {25}, {28}, {31}, /* 15 */
{34}, {37}, {40}, {44}, {49}, /* 20 */
{55}, {60}, {70}, {85}, {99} /* 25 */
};
struct wis_app_type wis_app[26] = {
{0}, {0}, {0}, {0}, {0}, {1}, /* 5 */
{1}, {1}, {1}, {2}, {2}, /* 10 */
{2}, {2}, {2}, {2}, {3}, /* 15 */
{3}, {4}, {4}, {5}, {5}, /* 20 */
{6}, {7}, {7}, {7}, {8} /* 25 */
};
struct dex_app_type dex_app[26] = {
{60}, {50}, {50}, {40}, {30}, {20}, /* 5 */
{10}, {0}, {0}, {0}, {0}, /* 10 */
{0}, {0}, {0}, {0}, {-10}, /* 15 */
{-15}, {-20}, {-30}, {-40}, {-50}, /* 20 */
{-65}, {-75}, {-90}, {-105}, {-120} /* 25 */
};
struct con_app_type con_app[26] = {
{-4, 20}, {-3, 25}, {-2, 30}, {-2, 35}, {-1, 40}, {-1, 45}, /* 5 */
{-1, 50}, {0, 55}, {0, 60}, {0, 65}, {0, 70}, /* 10 */
{0, 75}, {0, 80}, {0, 85}, {0, 88}, {1, 90}, /* 15 */
{2, 95}, {2, 97}, {3, 99}, {3, 99}, {4, 99}, /* 20 */
{4, 99}, {5, 99}, {6, 99}, {7, 99}, {8, 99} /* 25 */
};
/*
* Liquid properties.
*/
struct liq_type liq_table[LIQ_MAX] = {
{"water", "clear", {0, 1, 10}}, /* 0 */
{"beer", "amber", {3, 2, 5}},
{"wine", "rose", {5, 2, 5}},
{"ale", "brown", {2, 2, 5}},
{"dark ale", "dark", {1, 2, 5}},
{"whisky", "golden", {6, 1, 4}}, /* 5 */
{"lemonade", "pink", {0, 1, 8}},
{"firebreather", "boiling", {10, 0, 0}},
{"local specialty", "everclear", {3, 3, 3}},
{"slime mold juice", "green", {0, 4, -8}},
{"milk", "white", {0, 3, 6}}, /* 10 */
{"tea", "tan", {0, 1, 6}},
{"coffee", "black", {0, 1, 6}},
{"blood", "red", {0, 2, -1}},
{"salt water", "clear", {0, 1, -2}},
{"cola", "cherry", {0, 1, 5}} /* 15 */
};
/* prototypes */
void multi_hit(Character *ch, Character *victim, int dt);
void damage(Character *ch, Character *victim, int dam, int dt);
bool dragon(Character *ch, const char *spell_name);
void mprog_act_trigger(const std::string & buf, Character *mob, Character *ch, Object *obj, void *vo);
void mprog_bribe_trigger(Character *mob, Character *ch, int amount);
void mprog_death_trigger(Character *mob);
void mprog_entry_trigger(Character *mob);
void mprog_fight_trigger(Character *mob, Character *ch);
void mprog_give_trigger(Character *mob, Character *ch, Object *obj);
void mprog_greet_trigger(Character *mob);
void mprog_hitprcnt_trigger(Character *mob, Character *ch);
void mprog_random_trigger(Character *mob);
void mprog_speech_trigger(char *txt, Character *mob);
bool spec_breath_any(Character *ch);
bool spec_breath_acid(Character *ch);
bool spec_breath_fire(Character *ch);
bool spec_breath_frost(Character *ch);
bool spec_breath_gas(Character *ch);
bool spec_breath_lightning(Character *ch);
bool spec_cast_adept(Character *ch);
bool spec_cast_cleric(Character *ch);
bool spec_cast_judge(Character *ch);
bool spec_cast_mage(Character *ch);
bool spec_cast_undead(Character *ch);
bool spec_executioner(Character *ch);
bool spec_fido(Character *ch);
bool spec_guard(Character *ch);
bool spec_janitor(Character *ch);
bool spec_mayor(Character *ch);
bool spec_poison(Character *ch);
bool spec_thief(Character *ch);
/* Structs */
/*
* MOBprogram block
*/
class MobProgramActList {
public:
MobProgramActList *next;
std::string buf;
Character *ch;
Object *obj;
void *vo;
MobProgramActList() :
next(NULL), ch(NULL), obj(NULL), vo(NULL) {
}
};
class MobProgram {
public:
MobProgram *next;
int type;
std::string arglist;
std::string comlist;
MobProgram() :
next(NULL), type(0) {
}
};
#include "shop.hpp"
#include "note.hpp"
#include "affect.hpp"
#include "ban.hpp"
#include "exit.hpp"
#include "reset.hpp"
#include "area.hpp"
#include "room.hpp"
#include "objproto.hpp"
#include "object.hpp"
#include "mobproto.hpp"
#include "descriptor.hpp"
#include "pcdata.hpp"
#include "extra.hpp"
struct cmd_type cmd_table[] = {
/*
* Common movement commands.
*/
{"north", &Character::do_north, POS_STANDING, 0},
{"east", &Character::do_east, POS_STANDING, 0},
{"south", &Character::do_south, POS_STANDING, 0},
{"west", &Character::do_west, POS_STANDING, 0},
{"up", &Character::do_up, POS_STANDING, 0},
{"down", &Character::do_down, POS_STANDING, 0},
/*
* Common other commands.
* Placed here so one and two letter abbreviations work.
*/
{"buy", &Character::do_buy, POS_RESTING, 0},
{"cast", &Character::do_cast, POS_FIGHTING, 0},
{"exits", &Character::do_exits, POS_RESTING, 0},
{"get", &Character::do_get, POS_RESTING, 0},
{"inventory", &Character::do_inventory, POS_DEAD, 0},
{"kill", &Character::do_kill, POS_FIGHTING, 0},
{"look", &Character::do_look, POS_RESTING, 0},
{"order", &Character::do_order, POS_RESTING, 0},
{"rest", &Character::do_rest, POS_RESTING, 0},
{"sleep", &Character::do_sleep, POS_SLEEPING, 0},
{"stand", &Character::do_stand, POS_SLEEPING, 0},
{"tell", &Character::do_tell, POS_RESTING, 0},
{"wield", &Character::do_wear, POS_RESTING, 0},
{"wizhelp", &Character::do_wizhelp, POS_DEAD, L_HER},
/*
* Informational commands.
*/
{"areas", &Character::do_areas, POS_DEAD, 0},
{"bug", &Character::do_bug, POS_DEAD, 0},
{"commands", &Character::do_commands, POS_DEAD, 0},
{"compare", &Character::do_compare, POS_RESTING, 0},
{"consider", &Character::do_consider, POS_RESTING, 0},
{"credits", &Character::do_credits, POS_DEAD, 0},
{"equipment", &Character::do_equipment, POS_DEAD, 0},
{"examine", &Character::do_examine, POS_RESTING, 0},
{"help", &Character::do_help, POS_DEAD, 0},
{"idea", &Character::do_idea, POS_DEAD, 0},
{"report", &Character::do_report, POS_DEAD, 0},
{"pagelength", &Character::do_pagelen, POS_DEAD, 0},
{"score", &Character::do_score, POS_DEAD, 0},
{"slist", &Character::do_slist, POS_DEAD, 0},
{"socials", &Character::do_socials, POS_DEAD, 0},
{"time", &Character::do_time, POS_DEAD, 0},
{"typo", &Character::do_typo, POS_DEAD, 0},
{"weather", &Character::do_weather, POS_RESTING, 0},
{"who", &Character::do_who, POS_DEAD, 0},
{"wizlist", &Character::do_wizlist, POS_DEAD, 0},
/*
* Configuration commands.
*/
{"auto", &Character::do_auto, POS_DEAD, 0},
{"autoexit", &Character::do_autoexit, POS_DEAD, 0},
{"autoloot", &Character::do_autoloot, POS_DEAD, 0},
{"autosac", &Character::do_autosac, POS_DEAD, 0},
{"blank", &Character::do_blank, POS_DEAD, 0},
{"brief", &Character::do_brief, POS_DEAD, 0},
{"channels", &Character::do_channels, POS_DEAD, 0},
{"combine", &Character::do_combine, POS_DEAD, 0},
{"config", &Character::do_config, POS_DEAD, 0},
{"description", &Character::do_description, POS_DEAD, 0},
{"password", &Character::do_password, POS_DEAD, 0},
{"prompt", &Character::do_prompt, POS_DEAD, 0},
{"title", &Character::do_title, POS_DEAD, 0},
{"wimpy", &Character::do_wimpy, POS_DEAD, 0},
/*
* Communication commands.
*/
{"answer", &Character::do_answer, POS_SLEEPING, 0},
{"auction", &Character::do_auction, POS_SLEEPING, 0},
{"chat", &Character::do_chat, POS_SLEEPING, 0},
{".", &Character::do_chat, POS_SLEEPING, 0},
{"emote", &Character::do_emote, POS_RESTING, 0},
{",", &Character::do_emote, POS_RESTING, 0},
{"gtell", &Character::do_gtell, POS_DEAD, 0},
{";", &Character::do_gtell, POS_DEAD, 0},
{"music", &Character::do_music, POS_SLEEPING, 0},
{"note", &Character::do_note, POS_SLEEPING, 0},
{"question", &Character::do_question, POS_SLEEPING, 0},
{"reply", &Character::do_reply, POS_RESTING, 0},
{"say", &Character::do_say, POS_RESTING, 0},
{"'", &Character::do_say, POS_RESTING, 0},
{"shout", &Character::do_shout, POS_RESTING, 3},
{"yell", &Character::do_yell, POS_RESTING, 0},
{"gag", &Character::do_gag, POS_DEAD, 0},
{"ignore", &Character::do_gag, POS_DEAD, 0},
/*
* Object manipulation commands.
*/
{"brandish", &Character::do_brandish, POS_RESTING, 0},
{"close", &Character::do_close, POS_RESTING, 0},
{"drink", &Character::do_drink, POS_RESTING, 0},
{"drop", &Character::do_drop, POS_RESTING, 0},
{"eat", &Character::do_eat, POS_RESTING, 0},
{"fill", &Character::do_fill, POS_RESTING, 0},
{"give", &Character::do_give, POS_RESTING, 0},
{"hold", &Character::do_wear, POS_RESTING, 0},
{"list", &Character::do_list, POS_RESTING, 0},
{"lock", &Character::do_lock, POS_RESTING, 0},
{"open", &Character::do_open, POS_RESTING, 0},
{"pick", &Character::do_pick, POS_RESTING, 0},
{"put", &Character::do_put, POS_RESTING, 0},
{"quaff", &Character::do_quaff, POS_RESTING, 0},
{"recite", &Character::do_recite, POS_RESTING, 0},
{"remove", &Character::do_remove, POS_RESTING, 0},
{"sell", &Character::do_sell, POS_RESTING, 0},
{"take", &Character::do_get, POS_RESTING, 0},
{"sacrifice", &Character::do_sacrifice, POS_RESTING, 0},
{"unlock", &Character::do_unlock, POS_RESTING, 0},
{"value", &Character::do_value, POS_RESTING, 0},
{"wear", &Character::do_wear, POS_RESTING, 0},
{"zap", &Character::do_zap, POS_RESTING, 0},
/*
* Combat commands.
*/
{"backstab", &Character::do_backstab, POS_STANDING, 0},
{"bs", &Character::do_backstab, POS_STANDING, 0},
{"disarm", &Character::do_disarm, POS_FIGHTING, 0},
{"flee", &Character::do_flee, POS_FIGHTING, 0},
{"kick", &Character::do_kick, POS_FIGHTING, 0},
{"murde", &Character::do_murde, POS_FIGHTING, 5},
{"murder", &Character::do_murder, POS_FIGHTING, 5},
{"rescue", &Character::do_rescue, POS_FIGHTING, 0},
/*
* Miscellaneous commands.
*/
{"follow", &Character::do_follow, POS_RESTING, 0},
{"group", &Character::do_group, POS_SLEEPING, 0},
{"hide", &Character::do_hide, POS_RESTING, 0},
{"practice", &Character::do_practice, POS_SLEEPING, 0},
{"qui", &Character::do_qui, POS_DEAD, 0},
{"quit", &Character::do_quit, POS_DEAD, 0},
{"recall", &Character::do_recall, POS_FIGHTING, 0},
{"/", &Character::do_recall, POS_FIGHTING, 0},
{"rent", &Character::do_rent, POS_DEAD, 0},
{"save", &Character::do_save, POS_DEAD, 0},
{"sleep", &Character::do_sleep, POS_SLEEPING, 0},
{"sneak", &Character::do_sneak, POS_STANDING, 0},
{"spells", &Character::do_spells, POS_SLEEPING, 0},
{"split", &Character::do_split, POS_RESTING, 0},
{"steal", &Character::do_steal, POS_STANDING, 0},
{"train", &Character::do_train, POS_RESTING, 0},
{"visible", &Character::do_visible, POS_SLEEPING, 0},
{"wake", &Character::do_wake, POS_SLEEPING, 0},
{"where", &Character::do_where, POS_RESTING, 0},
/*
* Immortal commands.
*/
{"advance", &Character::do_advance, POS_DEAD, L_GOD},
{"trust", &Character::do_trust, POS_DEAD, L_GOD},
{"allow", &Character::do_allow, POS_DEAD, L_SUP},
{"ban", &Character::do_ban, POS_DEAD, L_SUP},
{"deny", &Character::do_deny, POS_DEAD, L_SUP},
{"disconnect", &Character::do_disconnect, POS_DEAD, L_SUP},
{"freeze", &Character::do_freeze, POS_DEAD, L_SUP},
{"hotboo", &Character::do_hotboo, POS_DEAD, L_SUP},
{"hotboot", &Character::do_hotboot, POS_DEAD, L_SUP},
{"shutdow", &Character::do_shutdow, POS_DEAD, L_SUP},
{"shutdown", &Character::do_shutdown, POS_DEAD, L_SUP},
{"users", &Character::do_users, POS_DEAD, L_SUP},
{"wizify", &Character::do_wizify, POS_DEAD, L_SUP},
{"wizlock", &Character::do_wizlock, POS_DEAD, L_SUP},
{"force", &Character::do_force, POS_DEAD, L_DEI},
{"mload", &Character::do_mload, POS_DEAD, L_DEI},
{"mset", &Character::do_mset, POS_DEAD, L_DEI},
{"noemote", &Character::do_noemote, POS_DEAD, L_DEI},
{"notell", &Character::do_notell, POS_DEAD, L_DEI},
{"oload", &Character::do_oload, POS_DEAD, L_DEI},
{"oset", &Character::do_oset, POS_DEAD, L_DEI},
{"owhere", &Character::do_owhere, POS_DEAD, L_DEI},
{"pardon", &Character::do_pardon, POS_DEAD, L_DEI},
{"peace", &Character::do_peace, POS_DEAD, L_DEI},
{"purge", &Character::do_purge, POS_DEAD, L_DEI},
{"restore", &Character::do_restore, POS_DEAD, L_DEI},
{"rset", &Character::do_rset, POS_DEAD, L_DEI},
{"silence", &Character::do_silence, POS_DEAD, L_DEI},
{"sla", &Character::do_sla, POS_DEAD, L_DEI},
{"slay", &Character::do_slay, POS_DEAD, L_DEI},
{"sset", &Character::do_sset, POS_DEAD, L_DEI},
{"transfer", &Character::do_transfer, POS_DEAD, L_DEI},
{"mpstat", &Character::do_mpstat, POS_DEAD, L_DEI},
{"at", &Character::do_at, POS_DEAD, L_ANG},
{"bamfin", &Character::do_bamfin, POS_DEAD, L_ANG},
{"bamfout", &Character::do_bamfout, POS_DEAD, L_ANG},
{"echo", &Character::do_echo, POS_DEAD, L_ANG},
{"goto", &Character::do_goto, POS_DEAD, L_ANG},
{"holylight", &Character::do_holylight, POS_DEAD, L_ANG},
{"memory", &Character::do_memory, POS_DEAD, L_ANG},
{"mfind", &Character::do_mfind, POS_DEAD, L_ANG},
{"mstat", &Character::do_mstat, POS_DEAD, L_ANG},
{"mwhere", &Character::do_mwhere, POS_DEAD, L_ANG},
{"ofind", &Character::do_ofind, POS_DEAD, L_ANG},
{"ostat", &Character::do_ostat, POS_DEAD, L_ANG},
{"recho", &Character::do_recho, POS_DEAD, L_ANG},
{"return", &Character::do_return, POS_DEAD, L_ANG},
{"rstat", &Character::do_rstat, POS_DEAD, L_ANG},
{"slookup", &Character::do_slookup, POS_DEAD, L_ANG},
{"switch", &Character::do_switch, POS_DEAD, L_ANG},
{"immtalk", &Character::do_immtalk, POS_DEAD, L_ANG},
{":", &Character::do_immtalk, POS_DEAD, L_ANG},
/*
* MOBprogram commands.
*/
{"mpasound", &Character::do_mpasound, POS_DEAD, 41},
{"mpjunk", &Character::do_mpjunk, POS_DEAD, 41},
{"mpecho", &Character::do_mpecho, POS_DEAD, 41},
{"mpechoat", &Character::do_mpechoat, POS_DEAD, 41},
{"mpechoaround", &Character::do_mpechoaround, POS_DEAD, 41},
{"mpkill", &Character::do_mpkill, POS_DEAD, 41},
{"mpmload", &Character::do_mpmload, POS_DEAD, 41},
{"mpoload", &Character::do_mpoload, POS_DEAD, 41},
{"mppurge", &Character::do_mppurge, POS_DEAD, 41},
{"mpgoto", &Character::do_mpgoto, POS_DEAD, 41},
{"mpat", &Character::do_mpat, POS_DEAD, 41},
{"mptransfer", &Character::do_mptransfer, POS_DEAD, 41},
{"mpforce", &Character::do_mpforce, POS_DEAD, 41},
/*
* End of list.
*/
{"", 0, POS_DEAD, 0}
};
/*
* The skill and spell table.
* Slot numbers must never be changed as they appear in #OBJECTS sections.
* Skills include spells as a particular case.
*/
struct skill_type skill_table[MAX_SKILL] = {
/*
* Magic spells.
*/
{ "reserved", {99, 99, 99, 99},
NULL, TAR_IGNORE, POS_STANDING,
0, 0, "", ""},
{ "acid blast", {20, 37, 37, 37},
&Character::spell_acid_blast, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
20, 12, "acid blast", "!Acid Blast!"},
{ "armor", {5, 1, 37, 37},
&Character::spell_armor, TAR_CHAR_DEFENSIVE, POS_STANDING,
5, 12, "", "You feel less protected."},
{ "bless", {37, 5, 37, 37},
&Character::spell_bless, TAR_CHAR_DEFENSIVE, POS_STANDING,
5, 12, "", "You feel less righteous."},
{ "blindness", {8, 5, 37, 37},
&Character::spell_blindness, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
5, 12, "", "You can see again."},
{ "burning hands", {5, 37, 37, 37},
&Character::spell_burning_hands, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
15, 12, "burning hands", "!Burning Hands!"},
{ "call lightning", {37, 12, 37, 37},
&Character::spell_call_lightning, TAR_IGNORE, POS_FIGHTING,
15, 12, "lightning bolt", "!Call Lightning!"},
{ "cause critical", {37, 9, 37, 37},
&Character::spell_cause_critical, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
20, 12, "spell", "!Cause Critical!"},
{ "cause light", {37, 1, 37, 37},
&Character::spell_cause_light, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
15, 12, "spell", "!Cause Light!"},
{ "cause serious", {37, 5, 37, 37},
&Character::spell_cause_serious, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
17, 12, "spell", "!Cause Serious!"},
{ "change sex", {37, 37, 37, 37},
&Character::spell_change_sex, TAR_CHAR_DEFENSIVE, POS_FIGHTING,
15, 12, "", "Your body feels familiar again."},
{ "charm person", {14, 37, 37, 37},
&Character::spell_charm_person, TAR_CHAR_OFFENSIVE, POS_STANDING,
5, 12, "", "You feel more self-confident."},
{ "chill touch", {3, 37, 37, 37},
&Character::spell_chill_touch, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
15, 12, "chilling touch", "You feel less cold."},
{ "colour spray", {11, 37, 37, 37},
&Character::spell_colour_spray, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
15, 12, "colour spray", "!Colour Spray!"},
{ "continual light", {4, 2, 37, 37},
&Character::spell_continual_light, TAR_IGNORE, POS_STANDING,
7, 12, "", "!Continual Light!"},
{ "control weather", {10, 13, 37, 37},
&Character::spell_control_weather, TAR_IGNORE, POS_STANDING,
25, 12, "", "!Control Weather!"},
{ "create food", {37, 3, 37, 37},
&Character::spell_create_food, TAR_IGNORE, POS_STANDING,
5, 12, "", "!Create Food!"},
{ "create spring", {10, 37, 37, 37},
&Character::spell_create_spring, TAR_IGNORE, POS_STANDING,
20, 12, "", "!Create Spring!"},
{ "create water", {37, 2, 37, 37},
&Character::spell_create_water, TAR_OBJ_INV, POS_STANDING,
5, 12, "", "!Create Water!"},
{ "cure blindness", {37, 4, 37, 37},
&Character::spell_cure_blindness, TAR_CHAR_DEFENSIVE, POS_FIGHTING,
5, 12, "", "!Cure Blindness!"},
{ "cure critical", {37, 9, 37, 37},
&Character::spell_cure_critical, TAR_CHAR_DEFENSIVE, POS_FIGHTING,
20, 12, "", "!Cure Critical!"},
{ "cure light", {37, 1, 37, 37},
&Character::spell_cure_light, TAR_CHAR_DEFENSIVE, POS_FIGHTING,
10, 12, "", "!Cure Light!"},
{ "cure poison", {37, 9, 37, 37},
&Character::spell_cure_poison, TAR_CHAR_DEFENSIVE, POS_STANDING,
5, 12, "", "!Cure Poison!"},
{ "cure serious", {37, 5, 37, 37},
&Character::spell_cure_serious, TAR_CHAR_DEFENSIVE, POS_FIGHTING,
15, 12, "", "!Cure Serious!"},
{ "curse", {12, 12, 37, 37},
&Character::spell_curse, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
20, 12, "curse", "The curse wears off."},
{ "detect evil", {37, 4, 37, 37},
&Character::spell_detect_evil, TAR_CHAR_SELF, POS_STANDING,
5, 12, "", "The red in your vision disappears."},
{ "detect hidden", {37, 7, 37, 37},
&Character::spell_detect_hidden, TAR_CHAR_SELF, POS_STANDING,
5, 12, "", "You feel less aware of your suroundings."},
{ "detect invis", {2, 5, 37, 37},
&Character::spell_detect_invis, TAR_CHAR_SELF, POS_STANDING,
5, 12, "", "You no longer see invisible objects."},
{ "detect magic", {2, 3, 37, 37},
&Character::spell_detect_magic, TAR_CHAR_SELF, POS_STANDING,
5, 12, "", "The detect magic wears off."},
{ "detect poison", {37, 5, 37, 37},
&Character::spell_detect_poison, TAR_OBJ_INV, POS_STANDING,
5, 12, "", "!Detect Poison!"},
{ "dispel evil", {37, 10, 37, 37},
&Character::spell_dispel_evil, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
15, 12, "dispel evil", "!Dispel Evil!"},
{ "dispel magic", {26, 31, 37, 37},
&Character::spell_dispel_magic, TAR_CHAR_OFFENSIVE, POS_STANDING,
15, 12, "", "!Dispel Magic!"},
{ "earthquake", {37, 7, 37, 37},
&Character::spell_earthquake, TAR_IGNORE, POS_FIGHTING,
15, 12, "earthquake", "!Earthquake!"},
{ "enchant weapon", {12, 37, 37, 37},
&Character::spell_enchant_weapon, TAR_OBJ_INV, POS_STANDING,
100, 24, "", "!Enchant Weapon!"},
{ "energy drain", {13, 37, 37, 37},
&Character::spell_energy_drain, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
35, 12, "energy drain", "!Energy Drain!"},
{ "faerie fire", {4, 2, 37, 37},
&Character::spell_faerie_fire, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
5, 12, "faerie fire", "The pink aura around you fades away."},
{ "faerie fog", {10, 14, 37, 37},
&Character::spell_faerie_fog, TAR_IGNORE, POS_STANDING,
12, 12, "faerie fog", "!Faerie Fog!"},
{ "fireball", {15, 37, 37, 37},
&Character::spell_fireball, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
15, 12, "fireball", "!Fireball!"},
{ "flamestrike", {37, 13, 37, 37},
&Character::spell_flamestrike, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
20, 12, "flamestrike", "!Flamestrike!"},
{ "fly", {7, 12, 37, 37},
&Character::spell_fly, TAR_CHAR_DEFENSIVE, POS_STANDING,
10, 18, "", "You slowly float to the ground."},
{ "gate", {37, 37, 37, 37},
&Character::spell_gate, TAR_CHAR_DEFENSIVE, POS_FIGHTING,
50, 12, "", "!Gate!"},
{ "giant strength", {7, 37, 37, 37},
&Character::spell_giant_strength, TAR_CHAR_DEFENSIVE, POS_STANDING,
20, 12, "", "You feel weaker."},
{ "harm", {37, 15, 37, 37},
&Character::spell_harm, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
35, 12, "harm spell", "!Harm!"},
{ "heal", {37, 14, 37, 37},
&Character::spell_heal, TAR_CHAR_DEFENSIVE, POS_FIGHTING,
50, 12, "", "!Heal!"},
{ "identify", {10, 10, 37, 37},
&Character::spell_identify, TAR_OBJ_INV, POS_STANDING,
12, 24, "", "!Identify!"},
{ "infravision", {6, 9, 37, 37},
&Character::spell_infravision, TAR_CHAR_DEFENSIVE, POS_STANDING,
5, 18, "", "You no longer see in the dark."},
{ "invis", {4, 37, 37, 37},
&Character::spell_invis, TAR_CHAR_DEFENSIVE, POS_STANDING,
5, 12, "", "You are no longer invisible."},
{ "know alignment", {8, 5, 37, 37},
&Character::spell_know_alignment, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
9, 12, "", "!Know Alignment!"},
{ "lightning bolt", {9, 37, 37, 37},
&Character::spell_lightning_bolt, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
15, 12, "lightning bolt", "!Lightning Bolt!"},
{ "locate object", {6, 10, 37, 37},
&Character::spell_locate_object, TAR_IGNORE, POS_STANDING,
20, 18, "", "!Locate Object!"},
{ "magic missile", {1, 37, 37, 37},
&Character::spell_magic_missile, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
15, 12, "magic missile", "!Magic Missile!"},
{ "mass invis", {15, 17, 37, 37},
&Character::spell_mass_invis, TAR_IGNORE, POS_STANDING,
20, 24, "", "!Mass Invis!"},
{ "pass door", {18, 37, 37, 37},
&Character::spell_pass_door, TAR_CHAR_SELF, POS_STANDING,
20, 12, "", "You feel solid again."},
{ "poison", {37, 8, 37, 37},
&Character::spell_poison, TAR_CHAR_OFFENSIVE, POS_STANDING,
10, 12, "poison", "You feel less sick."},
{ "protection", {37, 6, 37, 37},
&Character::spell_protection, TAR_CHAR_SELF, POS_STANDING,
5, 12, "", "You feel less protected."},
{ "refresh", {5, 3, 37, 37},
&Character::spell_refresh, TAR_CHAR_DEFENSIVE, POS_STANDING,
12, 18, "refresh", "!Refresh!"},
{ "remove curse", {37, 12, 37, 37},
&Character::spell_remove_curse, TAR_CHAR_DEFENSIVE, POS_STANDING,
5, 12, "", "!Remove Curse!"},
{ "sanctuary", {37, 13, 37, 37},
&Character::spell_sanctuary, TAR_CHAR_DEFENSIVE, POS_STANDING,
75, 12, "", "The white aura around your body fades."},
{ "shield", {13, 37, 37, 37},
&Character::spell_shield, TAR_CHAR_DEFENSIVE, POS_STANDING,
12, 18, "", "Your force shield shimmers then fades away."},
{ "shocking grasp", {7, 37, 37, 37},
&Character::spell_shocking_grasp, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
15, 12, "shocking grasp", "!Shocking Grasp!"},
{ "sleep", {14, 37, 37, 37},
&Character::spell_sleep, TAR_CHAR_OFFENSIVE, POS_STANDING,
15, 12, "", "You feel less tired."},
{ "stone skin", {17, 37, 37, 37},
&Character::spell_stone_skin, TAR_CHAR_SELF, POS_STANDING,
12, 18, "", "Your skin feels soft again."},
{ "summon", {37, 8, 37, 37},
&Character::spell_summon, TAR_IGNORE, POS_STANDING,
50, 12, "", "!Summon!"},
{ "teleport", {8, 37, 37, 37},
&Character::spell_teleport, TAR_CHAR_SELF, POS_FIGHTING,
35, 12, "", "!Teleport!"},
{ "ventriloquate", {1, 37, 37, 37},
&Character::spell_ventriloquate, TAR_IGNORE, POS_STANDING,
5, 12, "", "!Ventriloquate!"},
{ "weaken", {7, 37, 37, 37},
&Character::spell_weaken, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
20, 12, "spell", "You feel stronger."},
{ "word of recall", {37, 37, 37, 37},
&Character::spell_word_of_recall, TAR_CHAR_SELF, POS_RESTING,
5, 12, "", "!Word of Recall!"},
/*
* Dragon breath
*/
{ "acid breath", {33, 37, 37, 37},
&Character::spell_acid_breath, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
0, 4, "blast of acid", "!Acid Breath!"},
{ "fire breath", {34, 37, 37, 37},
&Character::spell_fire_breath, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
0, 4, "blast of flame", "!Fire Breath!"},
{ "frost breath", {31, 37, 37, 37},
&Character::spell_frost_breath, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
0, 4, "blast of frost", "!Frost Breath!"},
{ "gas breath", {35, 37, 37, 37},
&Character::spell_gas_breath, TAR_IGNORE, POS_FIGHTING,
0, 4, "blast of gas", "!Gas Breath!"},
{ "lightning breath", {32, 37, 37, 37},
&Character::spell_lightning_breath, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
0, 4, "blast of lightning", "!Lightning Breath!"},
/*
* Fighter and thief skills.
*/
{ "backstab", {37, 37, 1, 37},
&Character::spell_null, TAR_IGNORE, POS_STANDING,
0, 24, "backstab", "!Backstab!"},
{ "disarm", {37, 37, 10, 37},
&Character::spell_null, TAR_IGNORE, POS_FIGHTING,
0, 24, "", "!Disarm!"},
{ "dodge", {37, 37, 1, 37},
&Character::spell_null, TAR_IGNORE, POS_FIGHTING,
0, 0, "", "!Dodge!"},
{ "enhanced damage", {37, 37, 37, 1},
&Character::spell_null, TAR_IGNORE, POS_FIGHTING,
0, 0, "", "!Enhanced Damage!"},
{ "hide", {37, 37, 1, 37},
&Character::spell_null, TAR_IGNORE, POS_RESTING,
0, 12, "", "!Hide!"},
{ "kick", {37, 37, 37, 1},
&Character::spell_null, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
0, 8, "kick", "!Kick!"},
{ "parry", {37, 37, 37, 1},
&Character::spell_null, TAR_IGNORE, POS_FIGHTING,
0, 0, "", "!Parry!"},
{ "peek", {37, 37, 1, 37},
&Character::spell_null, TAR_IGNORE, POS_STANDING,
0, 0, "", "!Peek!"},
{ "pick lock", {37, 37, 1, 37},
&Character::spell_null, TAR_IGNORE, POS_STANDING,
0, 12, "", "!Pick!"},
{ "rescue", {37, 37, 37, 1},
&Character::spell_null, TAR_IGNORE, POS_FIGHTING,
0, 12, "", "!Rescue!"},
{ "second attack", {37, 37, 1, 1},
&Character::spell_null, TAR_IGNORE, POS_FIGHTING,
0, 0, "", "!Second Attack!"},
{ "sneak", {37, 37, 1, 37},
&Character::spell_null, TAR_IGNORE, POS_STANDING,
0, 12, "", NULL},
{ "steal", {37, 37, 1, 37},
&Character::spell_null, TAR_IGNORE, POS_STANDING,
0, 24, "", "!Steal!"},
{ "third attack", {37, 37, 37, 1},
&Character::spell_null, TAR_IGNORE, POS_FIGHTING,
0, 0, "", "!Third Attack!"},
/*
* Spells for mega1.are from Glop/Erkenbrand.
*/
{ "general purpose", {37, 37, 37, 37},
&Character::spell_general_purpose, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
0, 12, "general purpose ammo", "!General Purpose Ammo!"},
{ "high explosive", {37, 37, 37, 37},
&Character::spell_high_explosive, TAR_CHAR_OFFENSIVE, POS_FIGHTING,
0, 12, "high explosive ammo", "!High Explosive Ammo!"}
};
///////////////////
// start of code //
///////////////////
/*
* Lowest level output function.
* Write a block of text to the file descriptor.
* If this gives errors on very long blocks (like 'ofind all'),
* try lowering the max block size.
*/
bool write_to_descriptor (SOCKET desc, const char *txt, int length)
{
int iStart;
int nWrite;
int nBlock;
if (length <= 0)
length = strlen (txt);
for (iStart = 0; iStart < length; iStart += nWrite) {
nBlock = std::min (length - iStart, 4096);
if ((nWrite = send (desc, txt + iStart, nBlock, 0)) == SOCKET_ERROR) {
std::perror ("Write_to_descriptor");
return false;
}
}
return true;
}
/*
* Return ascii name of an affect location.
*/
std::string affect_loc_name (int location)
{
switch (location) {
case APPLY_NONE:
return "none";
case APPLY_STR:
return "strength";
case APPLY_DEX:
return "dexterity";
case APPLY_INT:
return "intelligence";
case APPLY_WIS:
return "wisdom";
case APPLY_CON:
return "constitution";
case APPLY_SEX:
return "sex";
case APPLY_CLASS:
return "class";
case APPLY_LEVEL:
return "level";
case APPLY_AGE:
return "age";
case APPLY_MANA:
return "mana";
case APPLY_HIT:
return "hp";
case APPLY_MOVE:
return "moves";
case APPLY_GOLD:
return "gold";
case APPLY_EXP:
return "experience";
case APPLY_AC:
return "armor class";
case APPLY_HITROLL:
return "hit roll";
case APPLY_DAMROLL:
return "damage roll";
case APPLY_SAVING_PARA:
return "save vs paralysis";
case APPLY_SAVING_ROD:
return "save vs rod";
case APPLY_SAVING_PETRI:
return "save vs petrification";
case APPLY_SAVING_BREATH:
return "save vs breath";
case APPLY_SAVING_SPELL:
return "save vs spell";
}
bug_printf ("Affect_location_name: unknown location %d.", location);
return "(unknown)";
}
/*
* Return ascii name of an affect bit vector.
*/
std::string affect_bit_name (int vector)
{
std::string buf;
if (vector & AFF_BLIND)
buf.append(" blind");
if (vector & AFF_INVISIBLE)
buf.append(" invisible");
if (vector & AFF_DETECT_EVIL)
buf.append(" detect_evil");
if (vector & AFF_DETECT_INVIS)
buf.append(" detect_invis");
if (vector & AFF_DETECT_MAGIC)
buf.append(" detect_magic");
if (vector & AFF_DETECT_HIDDEN)
buf.append(" detect_hidden");
if (vector & AFF_SANCTUARY)
buf.append(" sanctuary");
if (vector & AFF_FAERIE_FIRE)
buf.append(" faerie_fire");
if (vector & AFF_INFRARED)
buf.append(" infrared");
if (vector & AFF_CURSE)
buf.append(" curse");
if (vector & AFF_POISON)
buf.append(" poison");
if (vector & AFF_PROTECT)
buf.append(" protect");
if (vector & AFF_SLEEP)
buf.append(" sleep");
if (vector & AFF_SNEAK)
buf.append(" sneak");
if (vector & AFF_HIDE)
buf.append(" hide");
if (vector & AFF_CHARM)
buf.append(" charm");
if (vector & AFF_FLYING)
buf.append(" flying");
if (vector & AFF_PASS_DOOR)
buf.append(" pass_door");
if (buf.empty())
buf.append("none");
else
buf.erase(0,1);
return buf;
}
/*
* Return ascii name of extra flags vector.
*/
std::string extra_bit_name (int extra_flags)
{
std::string buf;
if (extra_flags & ITEM_GLOW)
buf.append(" glow");
if (extra_flags & ITEM_HUM)
buf.append(" hum");
if (extra_flags & ITEM_DARK)
buf.append(" dark");
if (extra_flags & ITEM_LOCK)
buf.append(" lock");
if (extra_flags & ITEM_EVIL)
buf.append(" evil");
if (extra_flags & ITEM_INVIS)
buf.append(" invis");
if (extra_flags & ITEM_MAGIC)
buf.append(" magic");
if (extra_flags & ITEM_NODROP)
buf.append(" nodrop");
if (extra_flags & ITEM_BLESS)
buf.append(" bless");
if (extra_flags & ITEM_ANTI_GOOD)
buf.append(" anti-good");
if (extra_flags & ITEM_ANTI_EVIL)
buf.append(" anti-evil");
if (extra_flags & ITEM_ANTI_NEUTRAL)
buf.append(" anti-neutral");
if (extra_flags & ITEM_NOREMOVE)
buf.append(" noremove");
if (extra_flags & ITEM_INVENTORY)
buf.append(" inventory");
if (buf.empty())
buf.append("none");
else
buf.erase(0,1);
return buf;
}
/*
* Lookup a skill by name.
*/
int skill_lookup (const std::string & name)
{
int sn;
for (sn = 0; sn < MAX_SKILL; sn++) {
if (skill_table[sn].name == NULL)
break;
if (tolower(name[0]) == tolower(skill_table[sn].name[0])
&& !str_prefix (name, skill_table[sn].name))
return sn;
}
return -1;
}
/*
* Translates mob virtual number to its mob index struct.
* Hash table lookup.
*/
MobPrototype *get_mob_index (int vnum)
{
std::map<int,MobPrototype*>::iterator pMobIndex;
pMobIndex = mob_table.find(vnum);
if (pMobIndex != mob_table.end())
return (*pMobIndex).second;
if (g_db->fBootDb) {
fatal_printf ("Get_mob_index: bad vnum %d.", vnum);
}
return NULL;
}
/*
* Translates mob virtual number to its obj index struct.
* Hash table lookup.
*/
ObjectPrototype *get_obj_index (int vnum)
{
std::map<int,ObjectPrototype*>::iterator pObjIndex;
pObjIndex = obj_table.find(vnum);
if (pObjIndex != obj_table.end())
return (*pObjIndex).second;
if (g_db->fBootDb) {
fatal_printf ("Get_obj_index: bad vnum %d.", vnum);
}
return NULL;
}
/*
* Translates mob virtual number to its room index struct.
* Hash table lookup.
*/
Room *get_room_index (int vnum)
{
std::map<int,Room*>::iterator pRoomIndex;
pRoomIndex = room_table.find(vnum);
if (pRoomIndex != room_table.end())
return (*pRoomIndex).second;
if (g_db->fBootDb) {
fatal_printf ("Get_room_index: bad vnum %d.", vnum);
}
return NULL;
}
std::string get_title (int klass, int level, int sex)
{
sqlite3_stmt *stmt = NULL;
char *sql = sqlite3_mprintf(
"SELECT title FROM titles WHERE class = %d AND level = %d and sex = %d",
klass, level, sex == SEX_FEMALE ? 1 : 0);
std::string str("");
Database* db = Database::instance();
if (sqlite3_prepare(db->database, sql, -1, &stmt, 0) != SQLITE_OK) {
bug_printf("Could not prepare statement: %s", sqlite3_errmsg(db->database));
sqlite3_free(sql);
return str;
}
if (sqlite3_step(stmt) == SQLITE_ROW) {
str.assign((const char*)sqlite3_column_text( stmt, 0 ));
}
sqlite3_finalize(stmt);
sqlite3_free(sql);
return str;
}
/*
* Get an extra description from a list.
*/
std::string get_extra_descr (const std::string & name, std::list<ExtraDescription *> & ed)
{
std::list<ExtraDescription *>::iterator e;
for (e = ed.begin(); e != ed.end(); e++) {
if (is_name (name, (*e)->keyword))
return (*e)->description;
}
return "";
}
/*
* Given a name, return the appropriate spec fun.
*/
SPEC_FUN *spec_lookup (const std::string & name)
{
if (!str_cmp (name, "spec_breath_any"))
return spec_breath_any;
if (!str_cmp (name, "spec_breath_acid"))
return spec_breath_acid;
if (!str_cmp (name, "spec_breath_fire"))
return spec_breath_fire;
if (!str_cmp (name, "spec_breath_frost"))
return spec_breath_frost;
if (!str_cmp (name, "spec_breath_gas"))
return spec_breath_gas;
if (!str_cmp (name, "spec_breath_lightning"))
return spec_breath_lightning;
if (!str_cmp (name, "spec_cast_adept"))
return spec_cast_adept;
if (!str_cmp (name, "spec_cast_cleric"))
return spec_cast_cleric;
if (!str_cmp (name, "spec_cast_judge"))
return spec_cast_judge;
if (!str_cmp (name, "spec_cast_mage"))
return spec_cast_mage;
if (!str_cmp (name, "spec_cast_undead"))
return spec_cast_undead;
if (!str_cmp (name, "spec_executioner"))
return spec_executioner;
if (!str_cmp (name, "spec_fido"))
return spec_fido;
if (!str_cmp (name, "spec_guard"))
return spec_guard;
if (!str_cmp (name, "spec_janitor"))
return spec_janitor;
if (!str_cmp (name, "spec_mayor"))
return spec_mayor;
if (!str_cmp (name, "spec_poison"))
return spec_poison;
if (!str_cmp (name, "spec_thief"))
return spec_thief;
return 0;
}
/*
* MOBprogram code block
*/
/* the functions */
/* This routine transfers between alpha and numeric forms of the
* mob_prog bitvector types. This allows the use of the words in the
* mob/script files.
*/
int mprog_name_to_type (const std::string & name)
{
if (!str_cmp (name, "in_file_prog"))
return IN_FILE_PROG;
if (!str_cmp (name, "act_prog"))
return ACT_PROG;
if (!str_cmp (name, "speech_prog"))
return SPEECH_PROG;
if (!str_cmp (name, "rand_prog"))
return RAND_PROG;
if (!str_cmp (name, "fight_prog"))
return FIGHT_PROG;
if (!str_cmp (name, "hitprcnt_prog"))
return HITPRCNT_PROG;
if (!str_cmp (name, "death_prog"))
return DEATH_PROG;
if (!str_cmp (name, "entry_prog"))
return ENTRY_PROG;
if (!str_cmp (name, "greet_prog"))
return GREET_PROG;
if (!str_cmp (name, "all_greet_prog"))
return ALL_GREET_PROG;
if (!str_cmp (name, "give_prog"))
return GIVE_PROG;
if (!str_cmp (name, "bribe_prog"))
return BRIBE_PROG;
return (ERROR_PROG);
}
/* This routine reads in scripts of MOBprograms from a file */
MobProgram *mprog_file_read (const std::string & f, MobProgram *mprg, MobPrototype *pMobIndex)
{
MobProgram *mprg2;
std::ifstream progfile;
bool done = false;
char MOBProgfile[MAX_INPUT_LENGTH];
snprintf (MOBProgfile, sizeof MOBProgfile, "%s%s", MOB_DIR, f.c_str());
progfile.open (MOBProgfile, std::ifstream::in | std::ifstream::binary);
if (!progfile.is_open()) {
fatal_printf ("Mob:%d couldnt open mobprog file", pMobIndex->vnum);
}
mprg2 = mprg;
switch (fread_letter (progfile)) {
case '>':
break;
case '|':
fatal_printf ("empty mobprog file.");
break;
default:
fatal_printf ("in mobprog file syntax error.");
break;
}
while (!done) {
mprg2->type = mprog_name_to_type (fread_word (progfile));
switch (mprg2->type) {
case ERROR_PROG:
fatal_printf ("mobprog file type error");
break;
case IN_FILE_PROG:
fatal_printf ("mprog file contains a call to file.");
break;
default:
pMobIndex->progtypes = pMobIndex->progtypes | mprg2->type;
mprg2->arglist = fread_string (progfile);
mprg2->comlist = fread_string (progfile);
switch (fread_letter (progfile)) {
case '>':
mprg2->next = new MobProgram();
mprg2 = mprg2->next;
mprg2->next = NULL;
break;
case '|':
done = true;
break;
default:
fatal_printf ("in mobprog file syntax error.");
break;
}
break;
}
}
progfile.close();
return mprg2;
}
/* This procedure is responsible for reading any in_file MOBprograms.
*/
void mprog_read_programs (std::ifstream & fp, MobPrototype *pMobIndex)
{
MobProgram *mprg;
bool done = false;
if ((fread_letter (fp)) != '>') {
fatal_printf ("Load_mobiles: vnum %d MOBPROG char", pMobIndex->vnum);
}
pMobIndex->mobprogs = new MobProgram();
mprg = pMobIndex->mobprogs;
while (!done) {
mprg->type = mprog_name_to_type (fread_word (fp));
switch (mprg->type) {
case ERROR_PROG:
fatal_printf ("Load_mobiles: vnum %d MOBPROG type.", pMobIndex->vnum);
break;
case IN_FILE_PROG:
mprg = mprog_file_read (fread_string (fp), mprg, pMobIndex);
fread_to_eol (fp);
switch (fread_letter (fp)) {
case '>':
mprg->next = new MobProgram();
mprg = mprg->next;
mprg->next = NULL;
break;
case '|':
mprg->next = NULL;
fread_to_eol (fp);
done = true;
break;
default:
fatal_printf ("Load_mobiles: vnum %d bad MOBPROG.", pMobIndex->vnum);
break;
}
break;
default:
pMobIndex->progtypes = pMobIndex->progtypes | mprg->type;
mprg->arglist = fread_string (fp);
fread_to_eol (fp);
mprg->comlist = fread_string (fp);
fread_to_eol (fp);
switch (fread_letter (fp)) {
case '>':
mprg->next = new MobProgram();
mprg = mprg->next;
mprg->next = NULL;
break;
case '|':
mprg->next = NULL;
fread_to_eol (fp);
done = true;
break;
default:
fatal_printf ("Load_mobiles: vnum %d bad MOBPROG.", pMobIndex->vnum);
break;
}
break;
}
}
}
/*
* Create a 'money' obj.
*/
Object *create_money (int amount)
{
char buf[MAX_STRING_LENGTH];
Object *obj;
if (amount <= 0) {
bug_printf ("Create_money: zero or negative money %d.", amount);
amount = 1;
}
if (amount == 1) {
obj = get_obj_index(OBJ_VNUM_MONEY_ONE)->create_object(0);
} else {
obj = get_obj_index(OBJ_VNUM_MONEY_SOME)->create_object(0);
snprintf (buf, sizeof buf, obj->short_descr.c_str(), amount);
obj->short_descr = buf;
obj->value[0] = amount;
}
return obj;
}
/*
* It is very important that this be an equivalence relation:
* (1) A ~ A
* (2) if A ~ B then B ~ A
* (3) if A ~ B and B ~ C, then A ~ C
*/
bool is_same_group (Character * ach, Character * bch)
{
if (ach->leader != NULL)
ach = ach->leader;
if (bch->leader != NULL)
bch = bch->leader;
return ach == bch;
}
bool Note::is_note_to (Character * ch)
{
if (!str_cmp (ch->name, sender))
return true;
if (is_name ("all", to_list))
return true;
if (ch->is_hero() && is_name ("immortal", to_list))
return true;
if (is_name (ch->name, to_list))
return true;
return false;
}
void note_attach (Character * ch)
{
Note *pnote;
if (ch->pnote != NULL)
return;
pnote = new Note();
pnote->sender = ch->name;
ch->pnote = pnote;
return;
}
void note_remove (Character * ch, Note * pnote)
{
std::string to_new, to_one, to_list;
/*
* Build a new to_list.
* Strip out this recipient.
*/
to_list = pnote->to_list;
while (!to_list.empty()) {
to_list = one_argument (to_list, to_one);
if (!to_list.empty() && str_cmp (ch->name, to_one)) {
to_new.append(" ");
to_new.append(to_one);
}
}
/*
* Just a simple recipient removal?
*/
if (str_cmp (ch->name, pnote->sender) && !to_new.empty()) {
pnote->to_list = to_new.substr(1);
return;
}
/*
* Remove note from linked list.
*/
note_list.erase(std::find(note_list.begin(),note_list.end(),pnote));
delete pnote;
/*
* Rewrite entire list.
*/
std::ofstream notefile;
notefile.open (NOTE_FILE, std::ofstream::out | std::ofstream::binary);
if (!notefile.is_open()) {
std::perror (NOTE_FILE);
} else {
for (std::list<Note*>::iterator p = note_list.begin();
p != note_list.end(); p++) {
notefile << "Sender " << (*p)->sender << "~\n";
notefile << "Date " << (*p)->date << "~\n";
notefile << "Stamp " << (*p)->date_stamp << "\n";
notefile << "To " << (*p)->to_list << "~\n";
notefile << "Subject " << (*p)->subject << "~\n";
notefile << "Text\n" << (*p)->text << "~\n\n";
}
notefile.close();
}
return;
}
/* This routine handles the variables for command expansion.
* If you want to add any go right ahead, it should be fairly
* clear how it is done and they are quite easy to do, so you
* can be as creative as you want. The only catch is to check
* that your variables exist before you use them. At the moment,
* using $t when the secondary target refers to an object
* i.e. >prog_act drops~<nl>if ispc($t)<nl>sigh<nl>endif<nl>~<nl>
* probably makes the mud crash (vice versa as well) The cure
* would be to change act() so that vo becomes vict & v_obj.
* but this would require a lot of small changes all over the code.
*/
void mprog_translate (char ch, std::string & t, Character * mob, Character * actor,
Object * obj, void *vo, Character * rndm)
{
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 *vict = (Character *) vo;
Object *v_obj = (Object *) vo;
t.erase();
switch (ch) {
case 'i':
one_argument (mob->name, t);
break;
case 'I':
t = mob->short_descr;
break;
case 'n':
if (actor && mob->can_see(actor)) {
one_argument (actor->name, t);
if (!actor->is_npc ())
t[0] = toupper(t[0]);
}
break;
case 'N':
if (actor) {
if (mob->can_see(actor)) {
if (actor->is_npc ())
t = actor->short_descr;
else
t = actor->name + " " + actor->pcdata->title;
} else {
t = "someone";
}
}
break;
case 't':
if (vict && mob->can_see(vict)) {
one_argument (vict->name, t);
if (!vict->is_npc ())
t[0] = toupper(t[0]);
}
break;
case 'T':
if (vict) {
if (mob->can_see(vict)) {
if (vict->is_npc ())
t = vict->short_descr;
else
t = vict->name + " " + vict->pcdata->title;
} else {
t = "someone";
}
}
break;
case 'r':
if (rndm && mob->can_see(rndm)) {
one_argument (rndm->name, t);
if (!rndm->is_npc ())
t[0] = toupper(t[0]);
}
break;
case 'R':
if (rndm) {
if (mob->can_see(rndm)) {
if (rndm->is_npc ())
t = rndm->short_descr;
else
t = rndm->name + " " + rndm->pcdata->title;
} else {
t = "someone";
}
}
break;
case 'e':
if (actor) {
if (mob->can_see(actor))
t = he_she[actor->sex];
else
t = "someone";
}
break;
case 'm':
if (actor) {
if (mob->can_see(actor))
t = him_her[actor->sex];
else
t = "someone";
}
break;
case 's':
if (actor) {
if (mob->can_see(actor))
t = his_her[actor->sex];
else
t = "someone's";
}
break;
case 'E':
if (vict) {
if (mob->can_see(vict))
t = he_she[vict->sex];
else
t = "someone";
}
break;
case 'M':
if (vict) {
if (mob->can_see(vict))
t = him_her[vict->sex];
else
t = "someone";
}
break;
case 'S':
if (vict) {
if (mob->can_see(vict))
t = his_her[vict->sex];
else
t = "someone's";
}
break;
case 'j':
t = he_she[mob->sex];
break;
case 'k':
t = him_her[mob->sex];
break;
case 'l':
t = his_her[mob->sex];
break;
case 'J':
if (rndm) {
if (mob->can_see(rndm))
t = he_she[rndm->sex];
else
t = "someone";
}
break;
case 'K':
if (rndm) {
if (mob->can_see(rndm))
t = him_her[rndm->sex];
else
t = "someone";
}
break;
case 'L':
if (rndm) {
if (mob->can_see(rndm))
t = his_her[rndm->sex];
else
t = "someone's";
}
break;
case 'o':
if (obj) {
if (mob->can_see_obj(obj))
one_argument (obj->name, t);
else
t = "something";
}
break;
case 'O':
if (obj) {
if (mob->can_see_obj(obj))
t = obj->short_descr;
else
t = "something";
}
break;
case 'p':
if (v_obj) {
if (mob->can_see_obj(v_obj))
one_argument (v_obj->name, t);
else
t = "something";
}
break;
case 'P':
if (v_obj) {
if (mob->can_see_obj(v_obj))
t = v_obj->short_descr;
else
t = "something";
}
break;
case 'a':
if (obj)
switch (obj->name[0]) {
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
t = "an";
break;
default:
t = "a";
}
break;
case 'A':
if (v_obj)
switch (v_obj->name[0]) {
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
t = "an";
break;
default:
t = "a";
}
break;
case '$':
t = "$";
break;
default:
bug_printf ("Mob: %d bad $var", mob->pIndexData->vnum);
break;
}
return;
}
/* This procedure simply copies the cmnd to a buffer while expanding
* any variables by calling the translate procedure. The observant
* code scrutinizer will notice that this is taken from act()
*/
void mprog_process_cmnd (const std::string & cmnd, Character * mob,
Character * actor, Object * obj, void *vo, Character * rndm)
{
std::string buf;
std::string tmp;
std::string::const_iterator str;
str = cmnd.begin();
while (str != cmnd.end()) {
if (*str != '$') {
buf.append(1, *str);
str++;
continue;
}
str++;
mprog_translate (*str, tmp, mob, actor, obj, vo, rndm);
buf.append(tmp);
str++;
}
mob->interpret (buf);
return;
}
/* Used to get sequential lines of a multi line string (separated by "\r\n")
* Thus its like one_argument(), but a trifle different. It is destructive
* to the multi line string argument, and thus clist must not be shared.
*/
std::string mprog_next_command (std::string & clist, std::string & cmd)
{
std::string::iterator pointer = clist.begin();
while (*pointer != '\n' && *pointer != '\r' && pointer != clist.end())
pointer++;
cmd.assign(clist.begin(), pointer);
while ((*pointer == '\n' || *pointer == '\r') && pointer != clist.end())
pointer++;
return std::string(pointer, clist.end());
}
/* These two functions do the basic evaluation of ifcheck operators.
* It is important to note that the string operations are not what
* you probably expect. Equality is exact and division is substring.
* remember that lhs has been stripped of leading space, but can
* still have trailing spaces so be careful when editing since:
* "guard" and "guard " are not equal.
*/
bool mprog_seval (const std::string & lhs, const std::string & opr, const std::string & rhs)
{
if (!str_cmp (opr, "=="))
return (bool) (!str_cmp (lhs, rhs));
if (!str_cmp (opr, "!="))
return (bool) (str_cmp (lhs, rhs));
if (!str_cmp (opr, "/"))
return (bool) (!str_infix (rhs, lhs));
if (!str_cmp (opr, "!/"))
return (bool) (str_infix (rhs, lhs));
bug_printf ("Improper MOBprog operator");
return 0;
}
bool mprog_veval (int lhs, const std::string & opr, int rhs)
{
if (!str_cmp (opr, "=="))
return (lhs == rhs);
if (!str_cmp (opr, "!="))
return (lhs != rhs);
if (!str_cmp (opr, ">"))
return (lhs > rhs);
if (!str_cmp (opr, "<"))
return (lhs < rhs);
if (!str_cmp (opr, ">="))
return (lhs <= rhs);
if (!str_cmp (opr, ">="))
return (lhs >= rhs);
if (!str_cmp (opr, "&"))
return (lhs & rhs);
if (!str_cmp (opr, "|"))
return (lhs | rhs);
bug_printf ("Improper MOBprog operator\r\n");
return 0;
}
/* This function performs the evaluation of the if checks. It is
* here that you can add any ifchecks which you so desire. Hopefully
* it is clear from what follows how one would go about adding your
* own. The syntax for an if check is: ifchck ( arg ) [opr val]
* where the parenthesis are required and the opr and val fields are
* optional but if one is there then both must be. The spaces are all
* optional. The evaluation of the opr expressions is farmed out
* to reduce the redundancy of the mammoth if statement list.
* If there are errors, then return -1 otherwise return boolean 1,0
*/
int mprog_do_ifchck (const std::string & ifchck, Character * mob,
Character * actor, Object * obj, void *vo, Character * rndm)
{
char buf[MAX_INPUT_LENGTH];
char arg[MAX_INPUT_LENGTH];
char opr[MAX_INPUT_LENGTH];
char val[MAX_INPUT_LENGTH];
Character *vict = (Character *) vo;
Object *v_obj = (Object *) vo;
char *bufpt = buf;
char *argpt = arg;
char *oprpt = opr;
char *valpt = val;
std::string::const_iterator point = ifchck.begin();
int lhsvl;
int rhsvl;
if (ifchck.empty()) {
bug_printf ("Mob: %d null ifchck", mob->pIndexData->vnum);
return -1;
}
/* skip leading spaces */
while (*point == ' ')
point++;
/* get whatever comes before the left paren.. ignore spaces */
while (*point != '(')
if (point == ifchck.end()) {
bug_printf ("Mob: %d ifchck syntax error", mob->pIndexData->vnum);
return -1;
} else if (*point == ' ')
point++;
else
*bufpt++ = *point++;
*bufpt = '\0';
point++;
/* get whatever is in between the parens.. ignore spaces */
while (*point != ')')
if (point == ifchck.end()) {
bug_printf ("Mob: %d ifchck syntax error", mob->pIndexData->vnum);
return -1;
} else if (*point == ' ')
point++;
else
*argpt++ = *point++;
*argpt = '\0';
point++;
/* check to see if there is an operator */
while (*point == ' ')
point++;
if (point == ifchck.end()) {
*opr = '\0';
*val = '\0';
} else { /* there should be an operator and value, so get them */
while ((*point != ' ') && (!isalnum (*point)))
if (point == ifchck.end()) {
bug_printf ("Mob: %d ifchck operator without value", mob->pIndexData->vnum);
return -1;
} else
*oprpt++ = *point++;
*oprpt = '\0';
/* finished with operator, skip spaces and then get the value */
while (*point == ' ')
point++;
for (;;) {
if ((*point != ' ') && (point == ifchck.end()))
break;
else
*valpt++ = *point++;
}
*valpt = '\0';
}
// bufpt = buf;
// argpt = arg;
// oprpt = opr;
// valpt = val;
/* Ok... now buf contains the ifchck, arg contains the inside of the
* parentheses, opr contains an operator if one is present, and val
* has the value if an operator was present.
* So.. basically use if statements and run over all known ifchecks
* Once inside, use the argument and expand the lhs. Then if need be
* send the lhs,opr,rhs off to be evaluated.
*/
if (!str_cmp (buf, "rand")) {
return (number_percent () <= std::atoi (arg));
}
if (!str_cmp (buf, "ispc")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
return 0;
case 'n':
if (actor)
return (!actor->is_npc ());
else
return -1;
case 't':
if (vict)
return (!vict->is_npc ());
else
return -1;
case 'r':
if (rndm)
return (!rndm->is_npc ());
else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'ispc'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "isnpc")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
return 1;
case 'n':
if (actor)
return actor->is_npc ();
else
return -1;
case 't':
if (vict)
return vict->is_npc ();
else
return -1;
case 'r':
if (rndm)
return rndm->is_npc ();
else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'isnpc'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "isgood")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
return mob->is_good ();
case 'n':
if (actor)
return actor->is_good ();
else
return -1;
case 't':
if (vict)
return vict->is_good ();
else
return -1;
case 'r':
if (rndm)
return rndm->is_good ();
else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'isgood'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "isfight")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
return (mob->fighting) ? 1 : 0;
case 'n':
if (actor)
return (actor->fighting) ? 1 : 0;
else
return -1;
case 't':
if (vict)
return (vict->fighting) ? 1 : 0;
else
return -1;
case 'r':
if (rndm)
return (rndm->fighting) ? 1 : 0;
else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'isfight'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "isimmort")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
return (mob->get_trust () > LEVEL_IMMORTAL);
case 'n':
if (actor)
return (actor->get_trust () > LEVEL_IMMORTAL);
else
return -1;
case 't':
if (vict)
return (vict->get_trust () > LEVEL_IMMORTAL);
else
return -1;
case 'r':
if (rndm)
return (rndm->get_trust () > LEVEL_IMMORTAL);
else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'isimmort'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "ischarmed")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
return mob->is_affected (AFF_CHARM);
case 'n':
if (actor)
return actor->is_affected (AFF_CHARM);
else
return -1;
case 't':
if (vict)
return vict->is_affected (AFF_CHARM);
else
return -1;
case 'r':
if (rndm)
return rndm->is_affected (AFF_CHARM);
else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'ischarmed'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "isfollow")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
return (mob->master != NULL && mob->master->in_room == mob->in_room);
case 'n':
if (actor)
return (actor->master != NULL
&& actor->master->in_room == actor->in_room);
else
return -1;
case 't':
if (vict)
return (vict->master != NULL
&& vict->master->in_room == vict->in_room);
else
return -1;
case 'r':
if (rndm)
return (rndm->master != NULL
&& rndm->master->in_room == rndm->in_room);
else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'isfollow'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "isaffected")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
return (mob->affected_by & std::atoi (arg));
case 'n':
if (actor)
return (actor->affected_by & std::atoi (arg));
else
return -1;
case 't':
if (vict)
return (vict->affected_by & std::atoi (arg));
else
return -1;
case 'r':
if (rndm)
return (rndm->affected_by & std::atoi (arg));
else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'isaffected'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "hitprcnt")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
lhsvl = mob->hit / mob->max_hit;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
case 'n':
if (actor) {
lhsvl = actor->hit / actor->max_hit;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 't':
if (vict) {
lhsvl = vict->hit / vict->max_hit;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 'r':
if (rndm) {
lhsvl = rndm->hit / rndm->max_hit;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'hitprcnt'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "inroom")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
lhsvl = mob->in_room->vnum;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
case 'n':
if (actor) {
lhsvl = actor->in_room->vnum;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 't':
if (vict) {
lhsvl = vict->in_room->vnum;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 'r':
if (rndm) {
lhsvl = rndm->in_room->vnum;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'inroom'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "sex")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
lhsvl = mob->sex;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
case 'n':
if (actor) {
lhsvl = actor->sex;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 't':
if (vict) {
lhsvl = vict->sex;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 'r':
if (rndm) {
lhsvl = rndm->sex;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'sex'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "position")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
lhsvl = mob->position;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
case 'n':
if (actor) {
lhsvl = actor->position;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 't':
if (vict) {
lhsvl = vict->position;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 'r':
if (rndm) {
lhsvl = rndm->position;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'position'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "level")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
lhsvl = mob->get_trust ();
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
case 'n':
if (actor) {
lhsvl = actor->get_trust ();
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 't':
if (vict) {
lhsvl = vict->get_trust ();
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 'r':
if (rndm) {
lhsvl = rndm->get_trust ();
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'level'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "class")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
lhsvl = mob->klass;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
case 'n':
if (actor) {
lhsvl = actor->klass;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 't':
if (vict) {
lhsvl = vict->klass;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 'r':
if (rndm) {
lhsvl = rndm->klass;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'class'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "goldamt")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
lhsvl = mob->gold;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
case 'n':
if (actor) {
lhsvl = actor->gold;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 't':
if (vict) {
lhsvl = vict->gold;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 'r':
if (rndm) {
lhsvl = rndm->gold;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'goldamt'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "objtype")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'o':
if (obj) {
lhsvl = obj->item_type;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 'p':
if (v_obj) {
lhsvl = v_obj->item_type;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'objtype'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "objval0")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'o':
if (obj) {
lhsvl = obj->value[0];
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 'p':
if (v_obj) {
lhsvl = v_obj->value[0];
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'objval0'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "objval1")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'o':
if (obj) {
lhsvl = obj->value[1];
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 'p':
if (v_obj) {
lhsvl = v_obj->value[1];
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'objval1'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "objval2")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'o':
if (obj) {
lhsvl = obj->value[2];
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 'p':
if (v_obj) {
lhsvl = v_obj->value[2];
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'objval2'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "objval3")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'o':
if (obj) {
lhsvl = obj->value[3];
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 'p':
if (v_obj) {
lhsvl = v_obj->value[3];
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'objval3'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "number")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
lhsvl = mob->gold;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
case 'n':
if (actor) {
if (actor->is_npc ()) {
lhsvl = actor->pIndexData->vnum;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
}
} else
return -1;
case 't':
if (vict) {
if (actor->is_npc ()) {
lhsvl = vict->pIndexData->vnum;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
}
} else
return -1;
case 'r':
if (rndm) {
if (actor->is_npc ()) {
lhsvl = rndm->pIndexData->vnum;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
}
} else
return -1;
case 'o':
if (obj) {
lhsvl = obj->pIndexData->vnum;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
case 'p':
if (v_obj) {
lhsvl = v_obj->pIndexData->vnum;
rhsvl = std::atoi (val);
return mprog_veval (lhsvl, opr, rhsvl);
} else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'number'", mob->pIndexData->vnum);
return -1;
}
}
if (!str_cmp (buf, "name")) {
switch (arg[1]) { /* arg should be "$*" so just get the letter */
case 'i':
return mprog_seval (mob->name, opr, val);
case 'n':
if (actor)
return mprog_seval (actor->name, opr, val);
else
return -1;
case 't':
if (vict)
return mprog_seval (vict->name, opr, val);
else
return -1;
case 'r':
if (rndm)
return mprog_seval (rndm->name, opr, val);
else
return -1;
case 'o':
if (obj)
return mprog_seval (obj->name, opr, val);
else
return -1;
case 'p':
if (v_obj)
return mprog_seval (v_obj->name, opr, val);
else
return -1;
default:
bug_printf ("Mob: %d bad argument to 'name'", mob->pIndexData->vnum);
return -1;
}
}
/* Ok... all the ifchcks are done, so if we didnt find ours then something
* odd happened. So report the bug and abort the MOBprogram (return error)
*/
bug_printf ("Mob: %d unknown ifchck", mob->pIndexData->vnum);
return -1;
}
/* Quite a long and arduous function, this guy handles the control
* flow part of MOBprograms. Basicially once the driver sees an
* 'if' attention shifts to here. While many syntax errors are
* caught, some will still get through due to the handling of break
* and errors in the same fashion. The desire to break out of the
* recursion without catastrophe in the event of a mis-parse was
* believed to be high. Thus, if an error is found, it is bugged and
* the parser acts as though a break were issued and just bails out
* at that point. I havent tested all the possibilites, so I'm speaking
* in theory, but it is 'guaranteed' to work on syntactically correct
* MOBprograms, so if the mud crashes here, check the mob carefully!
*/
std::string mprog_process_if (const std::string & ifchck, std::string & com_list,
Character * mob, Character * actor, Object * obj, void *vo, Character * rndm)
{
std::string buf;
std::string morebuf;
std::string cmnd;
bool loopdone = false;
bool flag = false;
int legal;
/* check for trueness of the ifcheck */
if ((legal = mprog_do_ifchck (ifchck, mob, actor, obj, vo, rndm))) {
if (legal == 1)
flag = true;
else
return "";
}
while (loopdone == false) { /*scan over any existing or statements */
com_list = mprog_next_command (com_list, cmnd);
cmnd.erase(0, cmnd.find_first_not_of(" "));
if (cmnd.empty()) {
bug_printf ("Mob: %d no commands after IF/OR", mob->pIndexData->vnum);
return "";
}
morebuf = one_argument (cmnd, buf);
if (!str_cmp (buf, "or")) {
if ((legal = mprog_do_ifchck (morebuf, mob, actor, obj, vo, rndm))) {
if (legal == 1)
flag = true;
else
return "";
}
} else
loopdone = true;
}
if (flag)
for (;;) { /*ifcheck was true, do commands but ignore else to endif */
if (!str_cmp (buf, "if")) {
com_list =
mprog_process_if (morebuf, com_list, mob, actor, obj, vo, rndm);
cmnd.erase(0, cmnd.find_first_not_of(" "));
if (com_list.empty())
return "";
com_list = mprog_next_command (com_list, cmnd);
morebuf = one_argument (cmnd, buf);
continue;
}
if (!str_cmp (buf, "break"))
return "";
if (!str_cmp (buf, "endif"))
return com_list;
if (!str_cmp (buf, "else")) {
while (str_cmp (buf, "endif")) {
com_list = mprog_next_command (com_list, cmnd);
cmnd.erase(0, cmnd.find_first_not_of(" "));
if (cmnd.empty()) {
bug_printf ("Mob: %d missing endif after else", mob->pIndexData->vnum);
return "";
}
morebuf = one_argument (cmnd, buf);
}
return com_list;
}
mprog_process_cmnd (cmnd, mob, actor, obj, vo, rndm);
com_list = mprog_next_command (com_list, cmnd);
cmnd.erase(0, cmnd.find_first_not_of(" "));
if (cmnd.empty()) {
bug_printf ("Mob: %d missing else or endif", mob->pIndexData->vnum);
return "";
}
morebuf = one_argument (cmnd, buf);
} else { /*false ifcheck, find else and do existing commands or quit at endif */
while ((str_cmp (buf, "else")) && (str_cmp (buf, "endif"))) {
com_list = mprog_next_command (com_list, cmnd);
cmnd.erase(0, cmnd.find_first_not_of(" "));
if (cmnd.empty()) {
bug_printf ("Mob: %d missing an else or endif", mob->pIndexData->vnum);
return "";
}
morebuf = one_argument (cmnd, buf);
}
/* found either an else or an endif.. act accordingly */
if (!str_cmp (buf, "endif"))
return com_list;
com_list = mprog_next_command (com_list, cmnd);
cmnd.erase(0, cmnd.find_first_not_of(" "));
if (cmnd.empty()) {
bug_printf ("Mob: %d missing endif", mob->pIndexData->vnum);
return "";
}
morebuf = one_argument (cmnd, buf);
for (;;) { /*process the post-else commands until an endif is found. */
if (!str_cmp (buf, "if")) {
com_list = mprog_process_if (morebuf, com_list, mob, actor,
obj, vo, rndm);
cmnd.erase(0, cmnd.find_first_not_of(" "));
if (com_list.empty())
return "";
com_list = mprog_next_command (com_list, cmnd);
morebuf = one_argument (cmnd, buf);
continue;
}
if (!str_cmp (buf, "else")) {
bug_printf ("Mob: %d found else in an else section", mob->pIndexData->vnum);
return "";
}
if (!str_cmp (buf, "break"))
return "";
if (!str_cmp (buf, "endif"))
return com_list;
mprog_process_cmnd (cmnd, mob, actor, obj, vo, rndm);
com_list = mprog_next_command (com_list, cmnd);
cmnd.erase(0, cmnd.find_first_not_of(" "));
if (cmnd.empty()) {
bug_printf ("Mob:%d missing endif in else section", mob->pIndexData->vnum);
return "";
}
morebuf = one_argument (cmnd, buf);
}
}
}
/* The main focus of the MOBprograms. This routine is called
* whenever a trigger is successful. It is responsible for parsing
* the command list and figuring out what to do. However, like all
* complex procedures, everything is farmed out to the other guys.
*/
void mprog_driver (const std::string & com_list, Character * mob,
Character * actor, Object * obj, void *vo)
{
std::string tmpcmndlst;
std::string buf;
std::string morebuf;
std::string command_list;
std::string cmnd;
Character *rndm = NULL;
int count = 0;
if (mob->is_affected (AFF_CHARM))
return;
/* get a random visable mortal player who is in the room with the mob */
CharIter vch;
for (vch = mob->in_room->people.begin(); vch != mob->in_room->people.end(); vch++)
if (!(*vch)->is_npc ()
&& (*vch)->level < LEVEL_IMMORTAL && mob->can_see(*vch)) {
if (number_range (0, count) == 0)
rndm = *vch;
count++;
}
tmpcmndlst = com_list;
command_list = tmpcmndlst;
command_list = mprog_next_command (command_list, cmnd);
while (!cmnd.empty()) {
morebuf = one_argument (cmnd, buf);
if (!str_cmp (buf, "if"))
command_list = mprog_process_if (morebuf, command_list, mob,
actor, obj, vo, rndm);
else
mprog_process_cmnd (cmnd, mob, actor, obj, vo, rndm);
command_list = mprog_next_command (command_list, cmnd);
}
return;
}
/* The next two routines are the basic trigger types. Either trigger
* on a certain percent, or trigger on a keyword or word phrase.
* To see how this works, look at the various trigger routines..
*/
void mprog_wordlist_check (const std::string & arg, Character * mob,
Character * actor, Object * obj, void *vo, int type)
{
std::string list;
std::string dupl;
std::string word;
MobProgram *mprg;
std::string::size_type start;
std::string::size_type end;
unsigned int i;
for (mprg = mob->pIndexData->mobprogs; mprg != NULL; mprg = mprg->next)
if (mprg->type & type) {
list = mprg->arglist;
for (i = 0; i < list.size(); i++)
list[i] = tolower (list[i]);
dupl = arg;
for (i = 0; i < dupl.size(); i++)
dupl[i] = tolower (dupl[i]);
if (list.substr(0,2) == "p ") {
list = list.substr(2);
while ((start = dupl.find(list)) != std::string::npos)
if ((start == 0 || dupl[start - 1] == ' ')
&& (dupl[end = start + list.size()] == ' '
|| dupl[end] == '\n' || dupl[end] == '\r' || dupl[end] == '\0')) {
mprog_driver (mprg->comlist, mob, actor, obj, vo);
break;
} else
dupl = dupl.substr(start + 1);
} else {
list = one_argument (list, word);
for (; !word.empty(); list = one_argument (list, word))
while ((start = dupl.find(word)) != std::string::npos)
if ((start == 0 || dupl[start - 1] == ' ')
&& (dupl[end = start + word.size()] == ' '
|| dupl[end] == '\n' || dupl[end] == '\r' || dupl[end] == '\0')) {
mprog_driver (mprg->comlist, mob, actor, obj, vo);
break;
} else
dupl = dupl.substr(start + 1);
}
}
return;
}
/*
* Mob autonomous action.
* This function takes 25% to 35% of ALL Merc cpu time.
* -- Furey
*/
void mobile_update (void)
{
Character *ch;
Exit *pexit;
int door;
try {
/* Examine all mobs. */
CharIter c;
for (c = char_list.begin(); c != char_list.end(); c = deepchnext) {
ch = *c;
deepchnext = ++c;
if (!ch->is_npc () || ch->in_room == NULL || ch->is_affected (AFF_CHARM))
continue;
/* Examine call for special procedure */
if (ch->spec_fun != 0) {
if ((*ch->spec_fun) (ch))
continue;
}
/* That's all for sleeping / busy monster */
if (ch->position < POS_STANDING)
continue;
/* MOBprogram random trigger */
if (ch->in_room->area->nplayer > 0) {
mprog_random_trigger (ch);
/* If ch dies or changes
position due to it's random
trigger continue - Kahn */
if (ch->position < POS_STANDING)
continue;
}
/* Scavenge */
if (IS_SET (ch->actflags, ACT_SCAVENGER)
&& !ch->in_room->contents.empty() && number_percent() <= 25) {
Object *obj_best;
int max;
max = 1;
obj_best = 0;
ObjIter obj;
for (obj = ch->in_room->contents.begin(); obj != ch->in_room->contents.end(); obj++) {
if ((*obj)->can_wear(ITEM_TAKE) && (*obj)->cost > max) {
obj_best = *obj;
max = (*obj)->cost;
}
}
if (obj_best) {
obj_best->obj_from_room ();
obj_best->obj_to_char (ch);
ch->act ("$n gets $p.", obj_best, NULL, TO_ROOM);
}
}
/* Wander */
if (!IS_SET (ch->actflags, ACT_SENTINEL)
&& (door = number_range (0, 31)) <= 5
&& (pexit = ch->in_room->exit[door]) != NULL
&& pexit->to_room != NULL && !IS_SET (pexit->exit_info, EX_CLOSED)
&& !IS_SET (pexit->to_room->room_flags, ROOM_NO_MOB)
&& (!IS_SET (ch->actflags, ACT_STAY_AREA)
|| pexit->to_room->area == ch->in_room->area)) {
ch->move_char (door);
/* If ch changes position due
to it's or someother mob's
movement via MOBProgs,
continue - Kahn */
if (ch->position < POS_STANDING)
continue;
}
/* Flee */
if (ch->hit < (ch->max_hit / 2)
&& (door = number_range (0, 7)) <= 5
&& (pexit = ch->in_room->exit[door]) != NULL
&& pexit->to_room != NULL && !IS_SET (pexit->exit_info, EX_CLOSED)
&& !IS_SET (pexit->to_room->room_flags, ROOM_NO_MOB)) {
bool found;
found = false;
CharIter rch;
for (rch = pexit->to_room->people.begin(); rch != pexit->to_room->people.end(); rch++) {
if (!(*rch)->is_npc ()) {
found = true;
break;
}
}
if (!found)
ch->move_char (door);
}
}
} catch (...) {
fatal_printf("mobile_update() exception");
}
return;
}
/*
* Update the weather.
*/
void weather_update (void)
{
std::string buf = g_world->weather_update();
if (!buf.empty()) {
for (DescIter d = descriptor_list.begin();
d != descriptor_list.end(); d++) {
if ((*d)->connected == CON_PLAYING && (*d)->character->is_outside()
&& (*d)->character->is_awake ())
(*d)->character->send_to_char (buf);
}
}
return;
}
/*
* Update all chars, including mobs.
* This function is performance sensitive.
*/
void char_update (void)
{
Character *ch;
Character *ch_save;
Character *ch_quit;
time_t save_time;
try {
save_time = g_world->get_current_time();
ch_save = NULL;
ch_quit = NULL;
CharIter c;
for (c = char_list.begin(); c != char_list.end(); c = deepchnext) {
Affect *paf;
ch = *c;
deepchnext = ++c;
/*
* Find dude with oldest save time.
*/
if (!ch->is_npc ()
&& (ch->desc == NULL || ch->desc->connected == CON_PLAYING)
&& ch->level >= 2 && ch->save_time < save_time) {
ch_save = ch;
save_time = ch->save_time;
}
if (ch->position >= POS_STUNNED) {
if (ch->hit < ch->max_hit)
ch->hit += ch->hit_gain();
if (ch->mana < ch->max_mana)
ch->mana += ch->mana_gain ();
if (ch->move < ch->max_move)
ch->move += ch->move_gain();
}
if (ch->position == POS_STUNNED)
ch->update_pos();
if (!ch->is_npc () && ch->level < LEVEL_IMMORTAL) {
Object *obj;
if ((obj = ch->get_eq_char (WEAR_LIGHT)) != NULL
&& obj->item_type == ITEM_LIGHT && obj->value[2] > 0) {
if (--obj->value[2] == 0 && ch->in_room != NULL) {
--ch->in_room->light;
ch->act ("$p goes out.", obj, NULL, TO_ROOM);
ch->act ("$p goes out.", obj, NULL, TO_CHAR);
obj->extract_obj ();
}
}
if ((obj = ch->get_eq_char (WEAR_LIGHT)) != NULL
&& obj->item_type == ITEM_DARKNESS && obj->value[2] > 0) {
if (--obj->value[2] == 0 && ch->in_room != NULL) {
++ch->in_room->light;
ch->act ("$p disappears.", obj, NULL, TO_ROOM);
ch->act ("$p disappears.", obj, NULL, TO_CHAR);
obj->extract_obj ();
}
}
if (++ch->timer >= 12) {
if (ch->was_in_room == NULL && ch->in_room != NULL) {
ch->was_in_room = ch->in_room;
if (ch->fighting != NULL)
ch->stop_fighting (true);
ch->act ("$n disappears into the void.", NULL, NULL, TO_ROOM);
ch->send_to_char ("You disappear into the void.\r\n");
ch->save_char_obj();
ch->char_from_room();
ch->char_to_room(get_room_index (ROOM_VNUM_LIMBO));
}
}
if (ch->timer > 30)
ch_quit = ch;
ch->gain_condition (COND_DRUNK, -1);
ch->gain_condition (COND_FULL, -1);
ch->gain_condition (COND_THIRST, -1);
}
AffIter af, next;
for (af = ch->affected.begin(); af != ch->affected.end(); af = next) {
paf = *af;
next = ++af;
if (paf->duration > 0)
paf->duration--;
else if (paf->duration < 0);
else {
if (next == ch->affected.end()
|| (*next)->type != paf->type || (*next)->duration > 0) {
if (paf->type > 0 && skill_table[paf->type].msg_off[0] != '\0') {
ch->send_to_char (skill_table[paf->type].msg_off);
ch->send_to_char ("\r\n");
}
}
ch->affect_remove (paf);
}
}
/*
* Careful with the damages here,
* MUST NOT refer to ch after damage taken,
* as it may be lethal damage (on NPC).
*/
if (ch->is_affected (AFF_POISON)) {
ch->act ("$n shivers and suffers.", NULL, NULL, TO_ROOM);
ch->send_to_char ("You shiver and suffer.\r\n");
damage (ch, ch, 2, skill_lookup("poison"));
} else if (ch->position == POS_INCAP) {
damage (ch, ch, 1, TYPE_UNDEFINED);
} else if (ch->position == POS_MORTAL) {
damage (ch, ch, 2, TYPE_UNDEFINED);
}
}
/*
* Autosave and autoquit.
* Check that these chars still exist.
*/
if (ch_save != NULL || ch_quit != NULL) {
CharIter cnext;
for (c = char_list.begin(); c != char_list.end(); c = cnext) {
ch = *c;
cnext = ++c;
if (ch == ch_save)
ch->save_char_obj();
if (ch == ch_quit)
ch->do_quit ("");
}
}
} catch (...) {
fatal_printf("char_update() exception");
}
return;
}
/*
* Update all objs.
* This function is performance sensitive.
*/
void obj_update (void)
{
Object *obj;
ObjIter o;
try {
for (o = object_list.begin(); o != object_list.end(); o = deepobnext) {
const char *message;
obj = *o;
deepobnext = ++o;
if (obj->timer <= 0 || --obj->timer > 0)
continue;
switch (obj->item_type) {
default:
message = "$p vanishes.";
break;
case ITEM_FOUNTAIN:
message = "$p dries up.";
break;
case ITEM_CORPSE_NPC:
message = "$p decays into dust.";
break;
case ITEM_CORPSE_PC:
message = "$p decays into dust.";
break;
case ITEM_FOOD:
message = "$p decomposes.";
break;
}
if (obj->carried_by != NULL) {
obj->carried_by->act (message, obj, NULL, TO_CHAR);
} else if (obj->in_room != NULL && !obj->in_room->people.empty()) {
Character *rch = obj->in_room->people.front();
rch->act (message, obj, NULL, TO_ROOM);
rch->act (message, obj, NULL, TO_CHAR);
}
obj->extract_obj ();
}
} catch (...) {
fatal_printf("obj_update() exception");
}
return;
}
/*
* Aggress.
*
* for each mortal PC
* for each mob in room
* aggress on some random PC
*
* This function takes 25% to 35% of ALL Merc cpu time.
* Unfortunately, checking on each PC move is too tricky,
* because we don't the mob to just attack the first PC
* who leads the party into the room.
*
* -- Furey
*/
void aggr_update (void)
{
Character *wch;
Character *ch;
Character *victim;
try {
CharIter c;
for (c = char_list.begin(); c != char_list.end(); c = deepchnext) {
wch = *c;
deepchnext = ++c;
/* MOBProgram ACT_PROG trigger */
if (wch->is_npc () && wch->mpactnum > 0 && wch->in_room->area->nplayer > 0) {
MobProgramActList *tmp_act, *tmp2_act;
for (tmp_act = wch->mpact; tmp_act != NULL; tmp_act = tmp_act->next) {
mprog_wordlist_check (tmp_act->buf, wch, tmp_act->ch,
tmp_act->obj, tmp_act->vo, ACT_PROG);
}
for (tmp_act = wch->mpact; tmp_act != NULL; tmp_act = tmp2_act) {
tmp2_act = tmp_act->next;
delete tmp_act;
}
wch->mpactnum = 0;
wch->mpact = NULL;
}
if (wch->is_npc ()
|| wch->level >= LEVEL_IMMORTAL || wch->in_room == NULL)
continue;
Room* rlist = wch->in_room;
CharIter rch, rcnext;
for (rch = rlist->people.begin(); rch != rlist->people.end(); rch = deeprmnext) {
ch = *rch;
deeprmnext = ++rch;
int count;
if (!ch->is_npc ()
|| !IS_SET (ch->actflags, ACT_AGGRESSIVE)
|| ch->fighting != NULL || ch->is_affected (AFF_CHARM)
|| !ch->is_awake ()
|| (IS_SET (ch->actflags, ACT_WIMPY) && wch->is_awake ())
|| !ch->can_see(wch))
continue;
/*
* Ok we have a 'wch' player character and a 'ch' npc aggressor.
* Now make the aggressor fight a RANDOM pc victim in the room,
* giving each 'vch' an equal chance of selection.
*/
count = 0;
victim = NULL;
for (CharIter vc = rlist->people.begin(); vc != rlist->people.end(); vc++) {
if (!(*vc)->is_npc () && (*vc)->level < LEVEL_IMMORTAL
&& (!IS_SET (ch->actflags, ACT_WIMPY) || !(*vc)->is_awake ())
&& ch->can_see(*vc)) {
if (number_range (0, count) == 0)
victim = *vc;
count++;
}
}
if (victim == NULL) {
bug_printf ("Aggr_update: null victim.", count);
continue;
}
multi_hit (ch, victim, TYPE_UNDEFINED);
}
}
} catch (...) {
fatal_printf("aggr_update() exception");
}
return;
}
/*
* Control the fights going on.
* Called periodically by update_handler.
*/
void violence_update (void)
{
Character *ch;
Character *victim;
Character *rch;
try {
CharIter c;
for (c = char_list.begin(); c != char_list.end(); c = deepchnext) {
ch = *c;
deepchnext = ++c;
victim = ch->fighting;
if (victim == NULL || ch->in_room == NULL)
continue;
if (ch->is_awake () && ch->in_room == victim->in_room)
multi_hit (ch, victim, TYPE_UNDEFINED);
else
ch->stop_fighting (false);
victim = ch->fighting;
if (victim == NULL)
continue;
mprog_hitprcnt_trigger (ch, victim);
mprog_fight_trigger (ch, victim);
/*
* Fun for the whole family!
*/
if (ch == NULL || ch->in_room == NULL)
continue;
victim = ch->fighting;
if (victim == NULL)
continue;
Room * rlist = ch->in_room;
CharIter rc, rnext;
for (rc = rlist->people.begin(); rc != rlist->people.end(); rc = deeprmnext) {
rch = *rc;
deeprmnext = ++rc;
if (rch->fighting == NULL && rch->is_awake ()) {
/*
* PC's auto-assist others in their group.
*/
if (!ch->is_npc () || ch->is_affected (AFF_CHARM)) {
if ((!rch->is_npc () || rch->is_affected (AFF_CHARM))
&& is_same_group (ch, rch))
multi_hit (rch, victim, TYPE_UNDEFINED);
continue;
}
/*
* NPC's assist NPC's of same type or 12.5% chance regardless.
*/
if (rch->is_npc () && !rch->is_affected (AFF_CHARM)) {
if (rch->pIndexData == ch->pIndexData || number_range (0, 7) == 0) {
Character *target;
int number;
target = NULL;
number = 0;
CharIter vch;
for (vch = rlist->people.begin(); vch != rlist->people.end(); vch++) {
if (rch->can_see(*vch)
&& is_same_group (*vch, victim)
&& number_range (0, number) == 0) {
target = *vch;
number++;
}
}
if (target != NULL) {
if ((((target->level - rch->level <= 4)
&& (target->level - rch->level >= -4))
&& !(rch->is_good () && target->is_good ()))
|| (rch->is_evil () || target->is_evil ()))
multi_hit (rch, target, TYPE_UNDEFINED);
}
}
}
}
}
}
} catch (...) {
fatal_printf("violence_update() exception");
}
return;
}
void extract_dead_characters()
{
Character *curr;
CharIter c, next;
for (c = char_list.begin(); c != char_list.end(); c = next) {
curr = *c;
next = ++c;
if (curr->is_npc()) {
if (IS_SET(curr->actflags, ACT_EXTRACT)) {
REMOVE_BIT(curr->actflags, ACT_EXTRACT);
curr->extract_char_old(true);
}
} else {
if (IS_SET(curr->actflags, PLR_EXTRACT)) {
REMOVE_BIT(curr->actflags, PLR_EXTRACT);
curr->extract_char_old(false);
}
}
}
}
/*
* Handle all kinds of updates.
* Called once per pulse from game loop.
* Random times to defeat tick-timing clients and players.
*/
void update_handler (void)
{
static int pulse_area;
static int pulse_mobile;
static int pulse_violence;
static int pulse_point;
try {
if (--pulse_area <= 0) {
pulse_area = number_range (PULSE_AREA / 2, 3 * PULSE_AREA / 2);
g_world->area_update ();
}
if (--pulse_violence <= 0) {
pulse_violence = PULSE_VIOLENCE;
violence_update ();
}
if (--pulse_mobile <= 0) {
pulse_mobile = PULSE_MOBILE;
mobile_update ();
}
if (--pulse_point <= 0) {
pulse_point = number_range (PULSE_TICK / 2, 3 * PULSE_TICK / 2);
weather_update ();
char_update ();
obj_update ();
}
aggr_update ();
if (extract_chars) {
extract_dead_characters();
extract_chars = false;
}
} catch (...) {
fatal_printf("update_handler() exception");
}
return;
}
/*
* Shopping commands.
*/
Character *find_keeper (Character * ch)
{
char buf[MAX_STRING_LENGTH];
Shop *pShop;
pShop = NULL;
CharIter keeper;
for (keeper = ch->in_room->people.begin(); keeper != ch->in_room->people.end(); keeper++) {
if ((*keeper)->is_npc () && (pShop = (*keeper)->pIndexData->pShop) != NULL)
break;
}
if (pShop == NULL) {
ch->send_to_char ("You can't do that here.\r\n");
return NULL;
}
/*
* Undesirables.
*/
if (!ch->is_npc () && IS_SET (ch->actflags, PLR_KILLER)) {
(*keeper)->do_say ("Killers are not welcome!");
snprintf (buf, sizeof buf, "%s the KILLER is over here!\r\n", ch->name.c_str());
(*keeper)->do_shout (buf);
return NULL;
}
if (!ch->is_npc () && IS_SET (ch->actflags, PLR_THIEF)) {
(*keeper)->do_say ("Thieves are not welcome!");
snprintf (buf, sizeof buf, "%s the THIEF is over here!\r\n", ch->name.c_str());
(*keeper)->do_shout (buf);
return NULL;
}
/*
* Shop hours.
*/
if (g_world->hour() < pShop->open_hour) {
(*keeper)->do_say ("Sorry, come back later.");
return NULL;
}
if (g_world->hour() > pShop->close_hour) {
(*keeper)->do_say ("Sorry, come back tomorrow.");
return NULL;
}
/*
* Invisible or hidden people.
*/
if (!(*keeper)->can_see(ch)) {
(*keeper)->do_say ("I don't trade with folks I can't see.");
return NULL;
}
return *keeper;
}
int get_cost (Character * keeper, Object * obj, bool fBuy)
{
Shop *pShop;
int cost;
if (obj == NULL || (pShop = keeper->pIndexData->pShop) == NULL)
return 0;
if (fBuy) {
cost = obj->cost * pShop->profit_buy / 100;
} else {
int itype;
cost = 0;
for (itype = 0; itype < MAX_TRADE; itype++) {
if (obj->item_type == pShop->buy_type[itype]) {
cost = obj->cost * pShop->profit_sell / 100;
break;
}
}
ObjIter o;
for (o = keeper->carrying.begin(); o != keeper->carrying.end(); o++) {
if (obj->pIndexData == (*o)->pIndexData)
cost /= 2;
}
}
if (obj->item_type == ITEM_STAFF || obj->item_type == ITEM_WAND)
cost = cost * obj->value[2] / obj->value[1];
return cost;
}
/*
* Generic channel function.
*/
void talk_channel (Character * ch, const std::string & argument, int channel,
const char *verb)
{
char buf[MAX_STRING_LENGTH];
int position;
if (argument.empty()) {
snprintf (buf, sizeof buf, "%s what?\r\n", verb);
buf[0] = toupper (buf[0]);
return;
}
if (!ch->is_npc () && IS_SET (ch->actflags, PLR_SILENCE)) {
snprintf (buf, sizeof buf, "You can't %s.\r\n", verb);
ch->send_to_char (buf);
return;
}
REMOVE_BIT (ch->deaf, channel);
switch (channel) {
default:
snprintf (buf, sizeof buf, "You %s '%s'.\r\n", verb, argument.c_str());
ch->send_to_char (buf);
snprintf (buf, sizeof buf, "$n %ss '$t'.", verb);
break;
case CHANNEL_IMMTALK:
snprintf (buf, sizeof buf, "$n: $t.");
position = ch->position;
ch->position = POS_STANDING;
ch->act (buf, argument.c_str(), NULL, TO_CHAR);
ch->position = position;
break;
}
for (DescIter d = descriptor_list.begin();
d != descriptor_list.end(); d++) {
Character *och;
Character *vch;
och = (*d)->original ? (*d)->original : (*d)->character;
vch = (*d)->character;
if ((*d)->connected == CON_PLAYING
&& vch != ch && !IS_SET (och->deaf, channel)) {
if (channel == CHANNEL_IMMTALK && !och->is_hero())
continue;
if (channel == CHANNEL_YELL && vch->in_room->area != ch->in_room->area)
continue;
position = vch->position;
if (channel != CHANNEL_SHOUT && channel != CHANNEL_YELL)
vch->position = POS_STANDING;
ch->act (buf, argument.c_str(), vch, TO_VICT);
vch->position = position;
}
}
return;
}
Room *find_location (Character * ch, const std::string & arg)
{
Character *victim;
Object *obj;
if (is_number (arg))
return get_room_index (std::atoi (arg.c_str()));
if ((victim = ch->get_char_world (arg)) != NULL)
return victim->in_room;
if ((obj = ch->get_obj_world (arg)) != NULL)
return obj->in_room;
return NULL;
}
bool is_safe (Character * ch, Character * victim)
{
if (ch->is_npc () || victim->is_npc ())
return false;
if (ch->get_age() < 21) {
ch->send_to_char ("You aren't old enough.\r\n");
return true;
}
if (IS_SET (victim->actflags, PLR_KILLER))
return false;
if (ch->level >= victim->level) {
ch->send_to_char ("You may not attack a lower level player.\r\n");
return true;
}
return false;
}
/*
* See if an attack justifies a KILLER flag.
*/
void check_killer (Character * ch, Character * victim)
{
/*
* Follow charm thread to responsible character.
* Attacking someone's charmed char is hostile!
*/
while (victim->is_affected (AFF_CHARM) && victim->master != NULL)
victim = victim->master;
/*
* NPC's are fair game.
* So are killers and thieves.
*/
if (victim->is_npc ()
|| IS_SET (victim->actflags, PLR_KILLER)
|| IS_SET (victim->actflags, PLR_THIEF))
return;
/*
* Charm-o-rama.
*/
if (IS_SET (ch->affected_by, AFF_CHARM)) {
if (ch->master == NULL) {
bug_printf ("Check_killer: %s bad AFF_CHARM",
ch->is_npc () ? ch->short_descr.c_str() : ch->name.c_str());
ch->affect_strip (skill_lookup("charm person"));
REMOVE_BIT (ch->affected_by, AFF_CHARM);
return;
}
ch->master->send_to_char ("*** You are now a KILLER!! ***\r\n");
SET_BIT (ch->master->actflags, PLR_KILLER);
ch->stop_follower();
return;
}
/*
* NPC's are cool of course (as long as not charmed).
* Hitting yourself is cool too (bleeding).
* So is being immortal (Alander's idea).
* And current killers stay as they are.
*/
if (ch->is_npc ()
|| ch == victim
|| ch->level >= LEVEL_IMMORTAL || IS_SET (ch->actflags, PLR_KILLER))
return;
ch->send_to_char ("*** You are now a KILLER!! ***\r\n");
SET_BIT (ch->actflags, PLR_KILLER);
ch->save_char_obj();
return;
}
/*
* Check for parry.
*/
bool check_parry (Character * ch, Character * victim)
{
int chance;
if (!victim->is_awake ())
return false;
if (victim->is_npc ()) {
/* Tuan was here. :) */
chance = std::min (60, 2 * victim->level);
} else {
if (victim->get_eq_char (WEAR_WIELD) == NULL)
return false;
chance = victim->pcdata->learned[skill_lookup("parry")] / 2;
}
if (number_percent () >= chance + victim->level - ch->level)
return false;
ch->act ("You parry $n's attack.", NULL, victim, TO_VICT);
ch->act ("$N parries your attack.", NULL, victim, TO_CHAR);
return true;
}
/*
* Check for dodge.
*/
bool check_dodge (Character * ch, Character * victim)
{
int chance;
if (!victim->is_awake ())
return false;
if (victim->is_npc ())
/* Tuan was here. :) */
chance = std::min (60, 2 * victim->level);
else
chance = victim->pcdata->learned[skill_lookup("dodge")] / 2;
if (number_percent () >= chance + victim->level - ch->level)
return false;
ch->act ("You dodge $n's attack.", NULL, victim, TO_VICT);
ch->act ("$N dodges your attack.", NULL, victim, TO_CHAR);
return true;
}
/*
* Make a corpse out of a character.
*/
void make_corpse (Character * ch)
{
char buf[MAX_STRING_LENGTH];
Object *corpse;
Object *obj;
std::string name;
if (ch->is_npc ()) {
name = ch->short_descr;
corpse = get_obj_index(OBJ_VNUM_CORPSE_NPC)->create_object(0);
corpse->timer = number_range (2, 4);
if (ch->gold > 0) {
create_money(ch->gold)->obj_to_obj(corpse);
ch->gold = 0;
}
} else {
name = ch->name;
corpse = get_obj_index (OBJ_VNUM_CORPSE_PC)->create_object(0);
corpse->timer = number_range (25, 40);
}
snprintf (buf, sizeof buf, corpse->short_descr.c_str(), name.c_str());
corpse->short_descr = buf;
snprintf (buf, sizeof buf, corpse->description.c_str(), name.c_str());
corpse->description = buf;
ObjIter o, onext;
for (o = ch->carrying.begin(); o != ch->carrying.end(); o = onext) {
obj = *o;
onext = ++o;
obj->obj_from_char();
if (IS_SET (obj->extra_flags, ITEM_INVENTORY))
obj->extract_obj ();
else
obj->obj_to_obj(corpse);
}
corpse->obj_to_room (ch->in_room);
return;
}
/*
* Improved Death_cry contributed by Diavolo.
*/
void death_cry (Character * ch)
{
Room *was_in_room;
const char *msg;
int door;
int vnum;
vnum = 0;
switch (number_range (0, 15)) {
default:
msg = "You hear $n's death cry.";
break;
case 0:
msg = "$n hits the ground ... DEAD.";
break;
case 1:
msg = "$n splatters blood on your armor.";
break;
case 2:
msg = "You smell $n's sphincter releasing in death.";
vnum = OBJ_VNUM_FINAL_TURD;
break;
case 3:
msg = "$n's severed head plops on the ground.";
vnum = OBJ_VNUM_SEVERED_HEAD;
break;
case 4:
msg = "$n's heart is torn from $s chest.";
vnum = OBJ_VNUM_TORN_HEART;
break;
case 5:
msg = "$n's arm is sliced from $s dead body.";
vnum = OBJ_VNUM_SLICED_ARM;
break;
case 6:
msg = "$n's leg is sliced from $s dead body.";
vnum = OBJ_VNUM_SLICED_LEG;
break;
}
ch->act (msg, NULL, NULL, TO_ROOM);
if (vnum != 0) {
char buf[MAX_STRING_LENGTH];
Object *obj;
std::string name;
name = ch->is_npc () ? ch->short_descr : ch->name;
obj = get_obj_index(vnum)->create_object(0);
obj->timer = number_range (4, 7);
snprintf (buf, sizeof buf, obj->short_descr.c_str(), name.c_str());
obj->short_descr = buf;
snprintf (buf, sizeof buf, obj->description.c_str(), name.c_str());
obj->description = buf;
obj->obj_to_room (ch->in_room);
}
if (ch->is_npc ())
msg = "You hear something's death cry.";
else
msg = "You hear someone's death cry.";
was_in_room = ch->in_room;
for (door = 0; door <= 5; door++) {
Exit *pexit;
if ((pexit = was_in_room->exit[door]) != NULL
&& pexit->to_room != NULL && pexit->to_room != was_in_room) {
ch->in_room = pexit->to_room;
ch->act (msg, NULL, NULL, TO_ROOM);
}
}
ch->in_room = was_in_room;
return;
}
void raw_kill (Character * victim)
{
victim->stop_fighting(true);
mprog_death_trigger (victim);
make_corpse (victim);
if (victim->is_npc ()) {
victim->pIndexData->killed++;
kill_table[URANGE (0, victim->level, MAX_LEVEL - 1)].killed++;
victim->extract_char (true);
return;
}
victim->extract_char (false);
while (victim->affected.begin() != victim->affected.end())
victim->affect_remove (*victim->affected.begin());
victim->affected_by = 0;
victim->armor = 100;
victim->position = POS_RESTING;
victim->hit = std::max (1, victim->hit);
victim->mana = std::max (1, victim->mana);
victim->move = std::max (1, victim->move);
victim->save_char_obj();
return;
}
/*
* Compute xp for a kill.
* Also adjust alignment of killer.
* Edit this function to change xp computations.
*/
int xp_compute (Character * gch, Character * victim)
{
int align;
int xp;
int extra;
int level;
int number;
xp = 300 - URANGE (-3, gch->level - victim->level, 6) * 50;
align = gch->alignment - victim->alignment;
if (align > 500) {
gch->alignment = std::min (gch->alignment + (align - 500) / 4, 1000);
xp = 5 * xp / 4;
} else if (align < -500) {
gch->alignment = std::max (gch->alignment + (align + 500) / 4, -1000);
} else {
gch->alignment -= gch->alignment / 4;
xp = 3 * xp / 4;
}
/*
* Adjust for popularity of target:
* -1/8 for each target over 'par' (down to -100%)
* +1/8 for each target under 'par' ( up to + 25%)
*/
level = URANGE (0, victim->level, MAX_LEVEL - 1);
number = std::max (1, kill_table[level].number);
extra = victim->pIndexData->killed - kill_table[level].killed / number;
xp -= xp * URANGE (-2, extra, 8) / 8;
xp = number_range (xp * 3 / 4, xp * 5 / 4);
xp = std::max (0, xp);
return xp;
}
void group_gain (Character * ch, Character * victim)
{
char buf[MAX_STRING_LENGTH];
Character *lch;
int xp;
int members;
/*
* Monsters don't get kill xp's or alignment changes.
* P-killing doesn't help either.
* Dying of mortal wounds or poison doesn't give xp to anyone!
*/
if (ch->is_npc () || !victim->is_npc () || victim == ch)
return;
members = 0;
CharIter gch;
for (gch = ch->in_room->people.begin(); gch != ch->in_room->people.end(); gch++) {
if (is_same_group (*gch, ch))
members++;
}
if (members == 0) {
bug_printf ("Group_gain: members.", members);
members = 1;
}
lch = (ch->leader != NULL) ? ch->leader : ch;
for (gch = ch->in_room->people.begin(); gch != ch->in_room->people.end(); gch++) {
Object *obj;
if (!is_same_group (*gch, ch))
continue;
if ((*gch)->level - lch->level >= 6) {
(*gch)->send_to_char ("You are too high for this group.\r\n");
continue;
}
if ((*gch)->level - lch->level <= -6) {
(*gch)->send_to_char ("You are too low for this group.\r\n");
continue;
}
xp = xp_compute (*gch, victim) / members;
snprintf (buf, sizeof buf, "You receive %d experience points.\r\n", xp);
(*gch)->send_to_char (buf);
(*gch)->gain_exp(xp);
ObjIter o, onext;
for (o = ch->carrying.begin(); o != ch->carrying.end(); o = onext) {
obj = *o;
onext = ++o;
if (obj->wear_loc == WEAR_NONE)
continue;
if ((obj->is_obj_stat(ITEM_ANTI_EVIL) && ch->is_evil ())
|| (obj->is_obj_stat(ITEM_ANTI_GOOD) && ch->is_good ())
|| (obj->is_obj_stat(ITEM_ANTI_NEUTRAL) && ch->is_neutral ())) {
ch->act ("You are zapped by $p.", obj, NULL, TO_CHAR);
ch->act ("$n is zapped by $p.", obj, NULL, TO_ROOM);
obj->obj_from_char();
obj->obj_to_room(ch->in_room);
}
}
}
return;
}
void dam_message (Character * ch, Character * victim, int dam, int dt)
{
static const char * attack_table[] = {
"hit",
"slice", "stab", "slash", "whip", "claw",
"blast", "pound", "crush", "grep", "bite",
"pierce", "suction"
};
char buf1[256], buf2[256], buf3[256];
const char *vs;
const char *vp;
std::string attack;
char punct;
if (dam == 0) {
vs = "miss";
vp = "misses";
} else if (dam <= 4) {
vs = "scratch";
vp = "scratches";
} else if (dam <= 8) {
vs = "graze";
vp = "grazes";
} else if (dam <= 12) {
vs = "hit";
vp = "hits";
} else if (dam <= 16) {
vs = "injure";
vp = "injures";
} else if (dam <= 20) {
vs = "wound";
vp = "wounds";
} else if (dam <= 24) {
vs = "maul";
vp = "mauls";
} else if (dam <= 28) {
vs = "decimate";
vp = "decimates";
} else if (dam <= 32) {
vs = "devastate";
vp = "devastates";
} else if (dam <= 36) {
vs = "maim";
vp = "maims";
} else if (dam <= 40) {
vs = "MUTILATE";
vp = "MUTILATES";
} else if (dam <= 44) {
vs = "DISEMBOWEL";
vp = "DISEMBOWELS";
} else if (dam <= 48) {
vs = "EVISCERATE";
vp = "EVISCERATES";
} else if (dam <= 52) {
vs = "MASSACRE";
vp = "MASSACRES";
} else if (dam <= 100) {
vs = "*** DEMOLISH ***";
vp = "*** DEMOLISHES ***";
} else {
vs = "*** ANNIHILATE ***";
vp = "*** ANNIHILATES ***";
}
punct = (dam <= 24) ? '.' : '!';
if (dt == TYPE_HIT) {
snprintf (buf1, sizeof buf1, "$n %s $N%c", vp, punct);
snprintf (buf2, sizeof buf2, "You %s $N%c", vs, punct);
snprintf (buf3, sizeof buf3, "$n %s you%c", vp, punct);
} else {
if (dt >= 0 && dt < MAX_SKILL)
attack = skill_table[dt].noun_damage;
else if (dt >= TYPE_HIT
&& dt < (int) (TYPE_HIT + sizeof (attack_table) / sizeof (attack_table[0])))
attack = attack_table[dt - TYPE_HIT];
else {
bug_printf ("Dam_message: bad dt %d.", dt);
attack = attack_table[0];
}
snprintf (buf1, sizeof buf1, "$n's %s %s $N%c", attack.c_str(), vp, punct);
snprintf (buf2, sizeof buf2, "Your %s %s $N%c", attack.c_str(), vp, punct);
snprintf (buf3, sizeof buf3, "$n's %s %s you%c", attack.c_str(), vp, punct);
}
ch->act (buf1, NULL, victim, TO_NOTVICT);
ch->act (buf2, NULL, victim, TO_CHAR);
ch->act (buf3, NULL, victim, TO_VICT);
return;
}
/*
* Disarm a creature.
* Caller must check for successful attack.
*/
void disarm (Character * ch, Character * victim)
{
Object *obj;
if ((obj = victim->get_eq_char (WEAR_WIELD)) == NULL)
return;
if (ch->get_eq_char (WEAR_WIELD) == NULL && number_percent() <= 50)
return;
ch->act ("$n DISARMS you!", NULL, victim, TO_VICT);
ch->act ("You disarm $N!", NULL, victim, TO_CHAR);
ch->act ("$n DISARMS $N!", NULL, victim, TO_NOTVICT);
obj->obj_from_char();
if (victim->is_npc ())
obj->obj_to_char (victim);
else
obj->obj_to_room (victim->in_room);
return;
}
/*
* Trip a creature.
* Caller must check for successful attack.
*/
void trip (Character * ch, Character * victim)
{
if (victim->wait == 0) {
ch->act ("$n trips you and you go down!", NULL, victim, TO_VICT);
ch->act ("You trip $N and $N goes down!", NULL, victim, TO_CHAR);
ch->act ("$n trips $N and $N goes down!", NULL, victim, TO_NOTVICT);
ch->wait_state (2 * PULSE_VIOLENCE);
victim->wait_state (2 * PULSE_VIOLENCE);
victim->position = POS_RESTING;
}
return;
}
/*
* Inflict damage from a hit.
*/
void damage (Character * ch, Character * victim, int dam, int dt)
{
if (victim->position == POS_DEAD)
return;
/*
* Stop up any residual loopholes.
*/
if (dam > 1000) {
bug_printf ("Damage: %d: more than 1000 points!", dam);
dam = 1000;
}
if (victim != ch) {
/*
* Certain attacks are forbidden.
* Most other attacks are returned.
*/
if (is_safe (ch, victim))
return;
check_killer (ch, victim);
if (victim->position > POS_STUNNED) {
if (victim->fighting == NULL)
victim->set_fighting(ch);
victim->position = POS_FIGHTING;
}
if (victim->position > POS_STUNNED) {
if (ch->fighting == NULL)
ch->set_fighting(victim);
/*
* If victim is charmed, ch might attack victim's master.
*/
if (ch->is_npc ()
&& victim->is_npc ()
&& victim->is_affected (AFF_CHARM)
&& victim->master != NULL
&& victim->master->in_room == ch->in_room && number_range(0, 7) == 0) {
ch->stop_fighting(false);
multi_hit (ch, victim->master, TYPE_UNDEFINED);
return;
}
}
/*
* More charm stuff.
*/
if (victim->master == ch)
victim->stop_follower();
/*
* Inviso attacks ... not.
*/
if (ch->is_affected (AFF_INVISIBLE)) {
ch->affect_strip (skill_lookup("invis"));
ch->affect_strip (skill_lookup("mass invis"));
REMOVE_BIT (ch->affected_by, AFF_INVISIBLE);
ch->act ("$n fades into existence.", NULL, NULL, TO_ROOM);
}
/*
* Damage modifiers.
*/
if (victim->is_affected (AFF_SANCTUARY))
dam /= 2;
if (victim->is_affected (AFF_PROTECT) && ch->is_evil ())
dam -= dam / 4;
if (dam < 0)
dam = 0;
/*
* Check for disarm, trip, parry, and dodge.
*/
if (dt >= TYPE_HIT) {
if (ch->is_npc () && number_percent () < ch->level / 2)
disarm (ch, victim);
if (ch->is_npc () && number_percent () < ch->level / 2)
trip (ch, victim);
if (check_parry (ch, victim))
return;
if (check_dodge (ch, victim))
return;
}
dam_message (ch, victim, dam, dt);
}
/*
* Hurt the victim.
* Inform the victim of his new state.
*/
victim->hit -= dam;
if (!victim->is_npc ()
&& victim->level >= LEVEL_IMMORTAL && victim->hit < 1)
victim->hit = 1;
victim->update_pos();
switch (victim->position) {
case POS_MORTAL:
victim->act ("$n is mortally wounded, and will die soon, if not aided.",
NULL, NULL, TO_ROOM);
victim->send_to_char
("You are mortally wounded, and will die soon, if not aided.\r\n");
break;
case POS_INCAP:
victim->act ("$n is incapacitated and will slowly die, if not aided.",
NULL, NULL, TO_ROOM);
victim->send_to_char
("You are incapacitated and will slowly die, if not aided.\r\n");
break;
case POS_STUNNED:
victim->act ("$n is stunned, but will probably recover.",
NULL, NULL, TO_ROOM);
victim->send_to_char ("You are stunned, but will probably recover.\r\n");
break;
case POS_DEAD:
victim->act ("$n is DEAD!!", 0, 0, TO_ROOM);
victim->send_to_char ("You have been KILLED!!\r\n\r\n");
break;
default:
if (dam > victim->max_hit / 4)
victim->send_to_char ("That really did HURT!\r\n");
if (victim->hit < victim->max_hit / 4)
victim->send_to_char ("You sure are BLEEDING!\r\n");
break;
}
/*
* Sleep spells and extremely wounded folks.
*/
if (!victim->is_awake ())
victim->stop_fighting(false);
/*
* Payoff for killing things.
*/
if (victim->position == POS_DEAD) {
group_gain (ch, victim);
if (!victim->is_npc ()) {
log_printf ("%s killed by %s at %d", victim->name.c_str(),
(ch->is_npc () ? ch->short_descr.c_str() : ch->name.c_str()), victim->in_room->vnum);
/*
* Dying penalty:
* 1/2 way back to previous level.
*/
if (victim->exp > 1000 * victim->level)
victim->gain_exp((1000 * victim->level - victim->exp) / 2);
}
raw_kill (victim);
if (!ch->is_npc () && victim->is_npc ()) {
if (IS_SET (ch->actflags, PLR_AUTOLOOT))
ch->do_get ("all corpse");
else
ch->do_look ("in corpse");
if (IS_SET (ch->actflags, PLR_AUTOSAC))
ch->do_sacrifice ("corpse");
}
return;
}
if (victim == ch)
return;
/*
* Take care of link dead people.
*/
if (!victim->is_npc () && victim->desc == NULL) {
if (number_range (0, victim->wait) == 0) {
victim->do_recall ("");
return;
}
}
/*
* Wimp out?
*/
if (victim->is_npc () && dam > 0) {
if ((IS_SET (victim->actflags, ACT_WIMPY) && number_percent() <= 50
&& victim->hit < victim->max_hit / 2)
|| (victim->is_affected (AFF_CHARM) && victim->master != NULL
&& victim->master->in_room != victim->in_room))
victim->do_flee ("");
}
if (!victim->is_npc ()
&& victim->hit > 0 && victim->hit <= victim->wimpy && victim->wait == 0)
victim->do_flee ("");
return;
}
/*
* Hit one guy once.
*/
void one_hit (Character * ch, Character * victim, int dt)
{
Object *wield;
int victim_ac;
int thac0;
int thac0_00;
int thac0_32;
int dam;
/*
* Can't beat a dead char!
* Guard against weird room-leavings.
*/
if (victim->position == POS_DEAD || ch->in_room != victim->in_room)
return;
/*
* Figure out the type of damage message.
*/
wield = ch->get_eq_char (WEAR_WIELD);
if (dt == TYPE_UNDEFINED) {
dt = TYPE_HIT;
if (wield != NULL && wield->item_type == ITEM_WEAPON)
dt += wield->value[3];
}
/*
* Calculate to-hit-armor-class-0 versus armor.
*/
if (ch->is_npc ()) {
thac0_00 = 20;
thac0_32 = 0;
} else {
thac0_00 = class_table[ch->klass].thac0_00;
thac0_32 = class_table[ch->klass].thac0_32;
}
thac0 = interpolate (ch->level, thac0_00, thac0_32) - ch->get_hitroll();
victim_ac = std::max (-15, victim->get_ac() / 10);
if (!ch->can_see(victim))
victim_ac -= 4;
/*
* The moment of excitement!
*/
int diceroll = number_range(0, 19);
if (diceroll == 0 || (diceroll != 19 && diceroll < thac0 - victim_ac)) {
/* Miss. */
damage (ch, victim, 0, dt);
return;
}
/*
* Hit.
* Calc damage.
*/
if (ch->is_npc ()) {
dam = number_range (ch->level / 2, ch->level * 3 / 2);
if (wield != NULL)
dam += dam / 2;
} else {
if (wield != NULL)
dam = number_range (wield->value[1], wield->value[2]);
else
dam = number_range (1, 4);
}
/*
* Bonuses.
*/
dam += ch->get_damroll();
int enh = skill_lookup("enhanced damage");
if (!ch->is_npc () && ch->pcdata->learned[enh] > 0)
dam += dam * ch->pcdata->learned[enh] / 150;
if (!victim->is_awake ())
dam *= 2;
if (dt == skill_lookup("backstab"))
dam *= 2 + ch->level / 8;
if (dam <= 0)
dam = 1;
damage (ch, victim, dam, dt);
return;
}
/*
* Do one group of attacks.
*/
void multi_hit (Character * ch, Character * victim, int dt)
{
int chance;
one_hit (ch, victim, dt);
if (ch->fighting != victim || dt == skill_lookup("backstab"))
return;
chance =
ch->is_npc () ? ch->level : ch->pcdata->learned[skill_lookup("second attack")] / 2;
if (number_percent () < chance) {
one_hit (ch, victim, dt);
if (ch->fighting != victim)
return;
}
chance =
ch->is_npc () ? ch->level : ch->pcdata->learned[skill_lookup("third attack")] / 4;
if (number_percent () < chance) {
one_hit (ch, victim, dt);
if (ch->fighting != victim)
return;
}
chance = ch->is_npc () ? ch->level / 2 : 0;
if (number_percent () < chance)
one_hit (ch, victim, dt);
return;
}
/*
* Utter mystical words for an sn.
*/
void say_spell (Character * ch, int sn)
{
std::string mwords, buf, buf2;
const char *pName;
int iSyl;
int length;
struct syl_type {
const char *old;
const char *newsyl;
};
static const struct syl_type syl_table[] = {
{" ", " "},
{"ar", "abra"},
{"au", "kada"},
{"bless", "fido"},
{"blind", "nose"},
{"bur", "mosa"},
{"cu", "judi"},
{"de", "oculo"},
{"en", "unso"},
{"light", "dies"},
{"lo", "hi"},
{"mor", "zak"},
{"move", "sido"},
{"ness", "lacri"},
{"ning", "illa"},
{"per", "duda"},
{"ra", "gru"},
{"re", "candus"},
{"son", "sabru"},
{"tect", "infra"},
{"tri", "cula"},
{"ven", "nofo"},
{"a", "a"}, {"b", "b"}, {"c", "q"}, {"d", "e"},
{"e", "z"}, {"f", "y"}, {"g", "o"}, {"h", "p"},
{"i", "u"}, {"j", "y"}, {"k", "t"}, {"l", "r"},
{"m", "w"}, {"n", "i"}, {"o", "a"}, {"p", "s"},
{"q", "d"}, {"r", "f"}, {"s", "g"}, {"t", "h"},
{"u", "j"}, {"v", "z"}, {"w", "x"}, {"x", "n"},
{"y", "l"}, {"z", "k"},
{"", ""}
};
for (pName = skill_table[sn].name; *pName != '\0'; pName += length) {
for (iSyl = 0; (length = strlen (syl_table[iSyl].old)) != 0; iSyl++) {
if (!str_prefix (syl_table[iSyl].old, pName)) {
mwords.append(syl_table[iSyl].newsyl);
break;
}
}
if (length == 0)
length = 1;
}
buf = "$n utters the words, '";
buf.append(skill_table[sn].name);
buf.append("'.");
buf2 = "$n utters the words, '";
buf2.append(mwords);
buf2.append("'.");
CharIter rch;
for (rch = ch->in_room->people.begin(); rch != ch->in_room->people.end(); rch++) {
if (*rch != ch)
ch->act ( (ch->klass == (*rch)->klass ? buf : buf2).c_str(), NULL, *rch, TO_VICT);
}
return;
}
/*
* Cast spells at targets using a magical object.
*/
void obj_cast_spell (int sn, int level, Character * ch, Character * victim,
Object * obj)
{
void *vo;
if (sn <= 0)
return;
if (sn >= MAX_SKILL || skill_table[sn].spell_fun == 0) {
bug_printf ("Obj_cast_spell: bad sn %d.", sn);
return;
}
switch (skill_table[sn].target) {
default:
bug_printf ("Obj_cast_spell: bad target for sn %d.", sn);
return;
case TAR_IGNORE:
vo = NULL;
break;
case TAR_CHAR_OFFENSIVE:
if (victim == NULL)
victim = ch->fighting;
if (victim == NULL || !victim->is_npc ()) {
ch->send_to_char ("You can't do that.\r\n");
return;
}
vo = (void *) victim;
break;
case TAR_CHAR_DEFENSIVE:
if (victim == NULL)
victim = ch;
vo = (void *) victim;
break;
case TAR_CHAR_SELF:
vo = (void *) ch;
break;
case TAR_OBJ_INV:
if (obj == NULL) {
ch->send_to_char ("You can't do that.\r\n");
return;
}
vo = (void *) obj;
break;
}
target_name = "";
(ch->*(skill_table[sn].spell_fun)) (sn, level, vo);
if (skill_table[sn].target == TAR_CHAR_OFFENSIVE && victim->master != ch) {
Character *vch;
CharIter rch, next;
for (rch = ch->in_room->people.begin(); rch != ch->in_room->people.end(); rch = next) {
vch = *rch;
next = ++rch;
if (victim == vch && victim->fighting == NULL) {
multi_hit (victim, ch, TYPE_UNDEFINED);
break;
}
}
}
return;
}
/* Snarf a MOBprogram section from the area file.
*/
void load_mobprogs (std::ifstream & fp)
{
char letter;
MobPrototype *iMob;
int value;
MobProgram *original;
MobProgram *working;
for (;;)
switch (letter = fread_letter (fp)) {
default:
fatal_printf ("Load_mobprogs: bad command '%c'.", letter);
break;
case 'S':
case 's':
fread_to_eol (fp);
return;
case '*':
fread_to_eol (fp);
break;
case 'M':
case 'm':
value = fread_number (fp);
if ((iMob = get_mob_index (value)) == NULL) {
fatal_printf ("Load_mobprogs: vnum %d doesnt exist", value);
}
if ((original = iMob->mobprogs) != NULL)
for ( ; original->next != NULL; original = original->next) ;
working = new MobProgram();
if (original)
original->next = working;
else
iMob->mobprogs = working;
working = mprog_file_read (fread_word (fp), working, iMob);
working->next = NULL;
fread_to_eol (fp);
break;
}
}
void mprog_percent_check (Character * mob, Character * actor, Object * obj,
void *vo, int type)
{
MobProgram *mprg;
for (mprg = mob->pIndexData->mobprogs; mprg != NULL; mprg = mprg->next)
if ((mprg->type & type)
&& (number_percent () < std::atoi (mprg->arglist.c_str()))) {
mprog_driver (mprg->comlist, mob, actor, obj, vo);
if (type != GREET_PROG && type != ALL_GREET_PROG)
break;
}
return;
}
/* The triggers.. These are really basic, and since most appear only
* once in the code (hmm. i think they all do) it would be more efficient
* to substitute the code in and make the mprog_xxx_check routines global.
* However, they are all here in one nice place at the moment to make it
* easier to see what they look like. If you do substitute them back in,
* make sure you remember to modify the variable names to the ones in the
* trigger calls.
*/
void mprog_act_trigger (const std::string & buf, Character * mob, Character * ch,
Object * obj, void *vo)
{
if (mob == NULL || ch == NULL)
return;
MobProgramActList *tmp_act;
if (mob->is_npc ()
&& (mob->pIndexData->progtypes & ACT_PROG)) {
tmp_act = new MobProgramActList();
if (mob->mpactnum > 0)
tmp_act->next = mob->mpact->next;
else
tmp_act->next = NULL;
mob->mpact = tmp_act;
mob->mpact->buf = buf;
mob->mpact->ch = ch;
mob->mpact->obj = obj;
mob->mpact->vo = vo;
mob->mpactnum++;
}
return;
}
void mprog_bribe_trigger (Character * mob, Character * ch, int amount)
{
if (mob == NULL || ch == NULL)
return;
char buf[MAX_STRING_LENGTH];
MobProgram *mprg;
Object *obj;
if (mob->is_npc ()
&& (mob->pIndexData->progtypes & BRIBE_PROG)) {
obj = get_obj_index (OBJ_VNUM_MONEY_SOME)->create_object(0);
snprintf (buf, sizeof buf, obj->short_descr.c_str(), amount);
obj->short_descr = buf;
obj->value[0] = amount;
obj->obj_to_char (mob);
mob->gold -= amount;
for (mprg = mob->pIndexData->mobprogs; mprg != NULL; mprg = mprg->next)
if ((mprg->type & BRIBE_PROG)
&& (amount >= std::atoi (mprg->arglist.c_str()))) {
mprog_driver (mprg->comlist, mob, ch, obj, NULL);
break;
}
}
return;
}
void mprog_death_trigger (Character * mob)
{
if (mob == NULL)
return;
if (mob->is_npc ()
&& (mob->pIndexData->progtypes & DEATH_PROG)) {
mprog_percent_check (mob, NULL, NULL, NULL, DEATH_PROG);
}
death_cry (mob);
return;
}
void mprog_entry_trigger (Character * mob)
{
if (mob == NULL)
return;
if (mob->is_npc ()
&& (mob->pIndexData->progtypes & ENTRY_PROG))
mprog_percent_check (mob, NULL, NULL, NULL, ENTRY_PROG);
return;
}
void mprog_fight_trigger (Character * mob, Character * ch)
{
if (mob == NULL || ch == NULL)
return;
if (mob->is_npc ()
&& (mob->pIndexData->progtypes & FIGHT_PROG))
mprog_percent_check (mob, ch, NULL, NULL, FIGHT_PROG);
return;
}
void mprog_give_trigger (Character * mob, Character * ch, Object * obj)
{
if (mob == NULL || ch == NULL || obj == NULL)
return;
std::string buf;
MobProgram *mprg;
if (mob->is_npc ()
&& (mob->pIndexData->progtypes & GIVE_PROG))
for (mprg = mob->pIndexData->mobprogs; mprg != NULL; mprg = mprg->next) {
one_argument (mprg->arglist, buf);
if ((mprg->type & GIVE_PROG)
&& ((!str_cmp (obj->name, mprg->arglist))
|| (!str_cmp ("all", buf)))) {
mprog_driver (mprg->comlist, mob, ch, obj, NULL);
break;
}
}
return;
}
void mprog_greet_trigger (Character * mob)
{
if (mob == NULL)
return;
Room* rm = mob->in_room;
Character* vmob;
CharIter v;
for (v = rm->people.begin(); v != rm->people.end(); v++) {
vmob = *v;
if (vmob->is_npc ()
&& (vmob->fighting == NULL)
&& vmob->is_awake ()) {
if (mob != vmob && vmob->can_see(mob)
&& (vmob->pIndexData->progtypes & GREET_PROG)) {
mprog_percent_check (vmob, mob, NULL, NULL, GREET_PROG);
} else if (vmob->pIndexData->progtypes & ALL_GREET_PROG) {
mprog_percent_check (vmob, mob, NULL, NULL, ALL_GREET_PROG);
}
}
}
return;
}
void mprog_hitprcnt_trigger (Character * mob, Character * ch)
{
if (mob == NULL || ch == NULL)
return;
MobProgram *mprg;
if (mob->is_npc ()
&& (mob->pIndexData->progtypes & HITPRCNT_PROG))
for (mprg = mob->pIndexData->mobprogs; mprg != NULL; mprg = mprg->next)
if ((mprg->type & HITPRCNT_PROG)
&& ((100 * mob->hit / mob->max_hit) < std::atoi (mprg->arglist.c_str()))) {
mprog_driver (mprg->comlist, mob, ch, NULL, NULL);
break;
}
return;
}
void mprog_random_trigger (Character * mob)
{
if (mob == NULL)
return;
if (mob->pIndexData->progtypes & RAND_PROG)
mprog_percent_check (mob, NULL, NULL, NULL, RAND_PROG);
return;
}
void mprog_speech_trigger (const std::string & txt, Character * mob)
{
if (mob == NULL)
return;
Room* rm = mob->in_room;
Character* vmob;
CharIter v;
for (v = rm->people.begin(); v != rm->people.end(); v++) {
vmob = *v;
if (vmob->is_npc () && (vmob->pIndexData->progtypes & SPEECH_PROG))
mprog_wordlist_check (txt, vmob, mob, NULL, NULL, SPEECH_PROG);
}
return;
}
/*
* Core procedure for dragons.
*/
bool dragon (Character * ch, const char *spell_name)
{
Character *victim = NULL;
int sn;
if (ch->position != POS_FIGHTING)
return false;
CharIter rch;
for (rch = ch->in_room->people.begin(); rch != ch->in_room->people.end(); rch++) {
if ((*rch)->fighting == ch && number_percent() <= 25) {
victim = *rch;
break;
}
}
if (victim == NULL)
return false;
if ((sn = skill_lookup (spell_name)) < 0)
return false;
(ch->*(skill_table[sn].spell_fun)) (sn, ch->level, victim);
return true;
}
/*
* Special procedures for mobiles.
*/
bool spec_breath_any (Character * ch)
{
if (ch->position != POS_FIGHTING)
return false;
switch (number_range(0, 7)) {
case 0:
return spec_breath_fire (ch);
case 1:
case 2:
return spec_breath_lightning (ch);
case 3:
return spec_breath_gas (ch);
case 4:
return spec_breath_acid (ch);
case 5:
case 6:
case 7:
return spec_breath_frost (ch);
}
return false;
}
bool spec_breath_acid (Character * ch)
{
return dragon (ch, "acid breath");
}
bool spec_breath_fire (Character * ch)
{
return dragon (ch, "fire breath");
}
bool spec_breath_frost (Character * ch)
{
return dragon (ch, "frost breath");
}
bool spec_breath_gas (Character * ch)
{
int sn;
if (ch->position != POS_FIGHTING)
return false;
if ((sn = skill_lookup ("gas breath")) < 0)
return false;
(ch->*(skill_table[sn].spell_fun)) (sn, ch->level, NULL);
return true;
}
bool spec_breath_lightning (Character * ch)
{
return dragon (ch, "lightning breath");
}
bool spec_cast_adept (Character * ch)
{
Character *victim = NULL;
if (!ch->is_awake ())
return false;
CharIter rch;
for (rch = ch->in_room->people.begin(); rch != ch->in_room->people.end(); rch++) {
if (*rch != ch && ch->can_see(*rch) && number_percent() <= 50) {
victim = *rch;
break;
}
}
if (victim == NULL)
return false;
switch (number_range(0, 7)) {
case 0:
ch->act ("$n utters the word 'tehctah'.", NULL, NULL, TO_ROOM);
ch->spell_armor (skill_lookup ("armor"), ch->level, victim);
return true;
case 1:
ch->act ("$n utters the word 'nhak'.", NULL, NULL, TO_ROOM);
ch->spell_bless (skill_lookup ("bless"), ch->level, victim);
return true;
case 2:
ch->act ("$n utters the word 'yeruf'.", NULL, NULL, TO_ROOM);
ch->spell_cure_blindness (skill_lookup ("cure blindness"),
ch->level, victim);
return true;
case 3:
ch->act ("$n utters the word 'garf'.", NULL, NULL, TO_ROOM);
ch->spell_cure_light (skill_lookup ("cure light"), ch->level, victim);
return true;
case 4:
ch->act ("$n utters the words 'rozar'.", NULL, NULL, TO_ROOM);
ch->spell_cure_poison (skill_lookup ("cure poison"), ch->level, victim);
return true;
case 5:
ch->act ("$n utters the words 'nadroj'.", NULL, NULL, TO_ROOM);
ch->spell_refresh (skill_lookup ("refresh"), ch->level, victim);
return true;
}
return false;
}
bool spec_cast_cleric (Character * ch)
{
Character *victim = NULL;
const char *spell;
int sn;
if (ch->position != POS_FIGHTING)
return false;
CharIter rch;
for (rch = ch->in_room->people.begin(); rch != ch->in_room->people.end(); rch++) {
if ((*rch)->fighting == ch && number_percent() <= 50) {
victim = *rch;
break;
}
}
if (victim == NULL)
return false;
for (;;) {
int min_level;
switch (number_range(0, 15)) {
case 0:
min_level = 0;
spell = "blindness";
break;
case 1:
min_level = 3;
spell = "cause serious";
break;
case 2:
min_level = 7;
spell = "earthquake";
break;
case 3:
min_level = 9;
spell = "cause critical";
break;
case 4:
min_level = 10;
spell = "dispel evil";
break;
case 5:
min_level = 12;
spell = "curse";
break;
case 6:
min_level = 12;
spell = "change sex";
break;
case 7:
min_level = 13;
spell = "flamestrike";
break;
case 8:
case 9:
case 10:
min_level = 15;
spell = "harm";
break;
default:
min_level = 16;
spell = "dispel magic";
break;
}
if (ch->level >= min_level)
break;
}
if ((sn = skill_lookup (spell)) < 0)
return false;
(ch->*(skill_table[sn].spell_fun)) (sn, ch->level, victim);
return true;
}
bool spec_cast_judge (Character * ch)
{
Character *victim = NULL;
const char *spell;
int sn;
if (ch->position != POS_FIGHTING)
return false;
CharIter rch;
for (rch = ch->in_room->people.begin(); rch != ch->in_room->people.end(); rch++) {
if ((*rch)->fighting == ch && number_percent() <= 50) {
victim = *rch;
break;
}
}
if (victim == NULL)
return false;
spell = "high explosive";
if ((sn = skill_lookup (spell)) < 0)
return false;
(ch->*(skill_table[sn].spell_fun)) (sn, ch->level, victim);
return true;
}
bool spec_cast_mage (Character * ch)
{
Character *victim = NULL;
const char *spell;
int sn;
if (ch->position != POS_FIGHTING)
return false;
CharIter rch;
for (rch = ch->in_room->people.begin(); rch != ch->in_room->people.end(); rch++) {
if ((*rch)->fighting == ch && number_percent() <= 50) {
victim = *rch;
break;
}
}
if (victim == NULL)
return false;
for (;;) {
int min_level;
switch (number_range(0, 15)) {
case 0:
min_level = 0;
spell = "blindness";
break;
case 1:
min_level = 3;
spell = "chill touch";
break;
case 2:
min_level = 7;
spell = "weaken";
break;
case 3:
min_level = 8;
spell = "teleport";
break;
case 4:
min_level = 11;
spell = "colour spray";
break;
case 5:
min_level = 12;
spell = "change sex";
break;
case 6:
min_level = 13;
spell = "energy drain";
break;
case 7:
case 8:
case 9:
min_level = 15;
spell = "fireball";
break;
default:
min_level = 20;
spell = "acid blast";
break;
}
if (ch->level >= min_level)
break;
}
if ((sn = skill_lookup (spell)) < 0)
return false;
(ch->*(skill_table[sn].spell_fun)) (sn, ch->level, victim);
return true;
}
bool spec_cast_undead (Character * ch)
{
Character *victim = NULL;
const char *spell;
int sn;
if (ch->position != POS_FIGHTING)
return false;
CharIter rch;
for (rch = ch->in_room->people.begin(); rch != ch->in_room->people.end(); rch++) {
if ((*rch)->fighting == ch && number_percent() <= 50) {
victim = *rch;
break;
}
}
if (victim == NULL)
return false;
for (;;) {
int min_level;
switch (number_range(0, 15)) {
case 0:
min_level = 0;
spell = "curse";
break;
case 1:
min_level = 3;
spell = "weaken";
break;
case 2:
min_level = 6;
spell = "chill touch";
break;
case 3:
min_level = 9;
spell = "blindness";
break;
case 4:
min_level = 12;
spell = "poison";
break;
case 5:
min_level = 15;
spell = "energy drain";
break;
case 6:
min_level = 18;
spell = "harm";
break;
case 7:
min_level = 21;
spell = "teleport";
break;
default:
min_level = 24;
spell = "gate";
break;
}
if (ch->level >= min_level)
break;
}
if ((sn = skill_lookup (spell)) < 0)
return false;
(ch->*(skill_table[sn].spell_fun)) (sn, ch->level, victim);
return true;
}
bool spec_executioner (Character * ch)
{
if (!ch->is_awake () || ch->fighting != NULL)
return false;
Character *victim = NULL;
const char *crime = "";
CharIter rch;
for (rch = ch->in_room->people.begin(); rch != ch->in_room->people.end(); rch++) {
if (!(*rch)->is_npc () && IS_SET ((*rch)->actflags, PLR_KILLER)) {
victim = *rch;
crime = "KILLER";
break;
}
if (!(*rch)->is_npc () && IS_SET ((*rch)->actflags, PLR_THIEF)) {
victim = *rch;
crime = "THIEF";
break;
}
}
if (victim == NULL)
return false;
char buf[MAX_STRING_LENGTH];
snprintf (buf, sizeof buf, "%s is a %s! PROTECT THE INNOCENT! MORE BLOOOOD!!!",
victim->name.c_str(), crime);
ch->do_shout (buf);
multi_hit (ch, victim, TYPE_UNDEFINED);
get_mob_index(MOB_VNUM_CITYGUARD)->create_mobile()->char_to_room(ch->in_room);
get_mob_index(MOB_VNUM_CITYGUARD)->create_mobile()->char_to_room(ch->in_room);
return true;
}
bool spec_fido (Character * ch)
{
if (!ch->is_awake ())
return false;
Object *corpse;
Object *obj;
ObjIter c, cnext;
for (c = ch->in_room->contents.begin(); c != ch->in_room->contents.end(); c = cnext) {
corpse = *c;
cnext = ++c;
if (corpse->item_type != ITEM_CORPSE_NPC)
continue;
ch->act ("$n savagely devours a corpse.", NULL, NULL, TO_ROOM);
ObjIter o, onext;
for (o = corpse->contains.begin(); o != corpse->contains.end(); o = onext) {
obj = *o;
onext = ++o;
obj->obj_from_obj ();
obj->obj_to_room (ch->in_room);
}
corpse->extract_obj ();
return true;
}
return false;
}
bool spec_guard (Character * ch)
{
if (!ch->is_awake () || ch->fighting != NULL)
return false;
Character *victim = NULL;
int max_evil = 300;
Character* ech = NULL;
const char* crime = "";
CharIter rch;
for (rch = ch->in_room->people.begin(); rch != ch->in_room->people.end(); rch++) {
if (!(*rch)->is_npc () && IS_SET ((*rch)->actflags, PLR_KILLER)) {
victim = *rch;
crime = "KILLER";
break;
}
if (!(*rch)->is_npc () && IS_SET ((*rch)->actflags, PLR_THIEF)) {
victim = *rch;
crime = "THIEF";
break;
}
if ((*rch)->fighting != NULL
&& (*rch)->fighting != ch && (*rch)->alignment < max_evil) {
max_evil = (*rch)->alignment;
ech = *rch;
}
}
if (victim != NULL) {
char buf[MAX_STRING_LENGTH];
snprintf (buf, sizeof buf, "%s is a %s! PROTECT THE INNOCENT!! BANZAI!!",
victim->name.c_str(), crime);
ch->do_shout (buf);
multi_hit (ch, victim, TYPE_UNDEFINED);
return true;
}
if (ech != NULL) {
ch->act ("$n screams 'PROTECT THE INNOCENT!! BANZAI!!",
NULL, NULL, TO_ROOM);
multi_hit (ch, ech, TYPE_UNDEFINED);
return true;
}
return false;
}
bool spec_janitor (Character * ch)
{
Object *trash;
if (!ch->is_awake ())
return false;
ObjIter o, onext;
for (o = ch->in_room->contents.begin(); o != ch->in_room->contents.end(); o = onext) {
trash = *o;
onext = ++o;
if (!IS_SET (trash->wear_flags, ITEM_TAKE))
continue;
if (trash->item_type == ITEM_DRINK_CON
|| trash->item_type == ITEM_TRASH || trash->cost < 10) {
ch->act ("$n picks up some trash.", NULL, NULL, TO_ROOM);
trash->obj_from_room ();
trash->obj_to_char (ch);
return true;
}
}
return false;
}
bool spec_mayor (Character * ch)
{
static const char open_path[] =
"W3a3003b33000c111d0d111Oe333333Oe22c222112212111a1S.";
static const char close_path[] =
"W3a3003b33000c111d0d111CE333333CE22c222112212111a1S.";
static const char *path;
static int pos;
static bool move;
if (!move) {
if (g_world->hour() == 6) {
path = open_path;
move = true;
pos = 0;
}
if (g_world->hour() == 20) {
path = close_path;
move = true;
pos = 0;
}
}
if (ch->fighting != NULL)
return spec_cast_cleric (ch);
if (!move || ch->position < POS_SLEEPING)
return false;
switch (path[pos]) {
case '0':
case '1':
case '2':
case '3':
ch->move_char (path[pos] - '0');
break;
case 'W':
ch->position = POS_STANDING;
ch->act ("$n awakens and groans loudly.", NULL, NULL, TO_ROOM);
break;
case 'S':
ch->position = POS_SLEEPING;
ch->act ("$n lies down and falls asleep.", NULL, NULL, TO_ROOM);
break;
case 'a':
ch->act ("$n says 'Hello Honey!'", NULL, NULL, TO_ROOM);
break;
case 'b':
ch->act ("$n says 'What a view! I must do something about that dump!'",
NULL, NULL, TO_ROOM);
break;
case 'c':
ch->act ("$n says 'Vandals! Youngsters have no respect for anything!'",
NULL, NULL, TO_ROOM);
break;
case 'd':
ch->act ("$n says 'Good day, citizens!'", NULL, NULL, TO_ROOM);
break;
case 'e':
ch->act ("$n says 'I hereby declare the city of Midgaard open!'",
NULL, NULL, TO_ROOM);
break;
case 'E':
ch->act ("$n says 'I hereby declare the city of Midgaard closed!'",
NULL, NULL, TO_ROOM);
break;
case 'O':
ch->do_unlock ("gate");
ch->do_open ("gate");
break;
case 'C':
ch->do_close ("gate");
ch->do_lock ("gate");
break;
case '.':
move = false;
break;
}
pos++;
return false;
}
bool spec_poison (Character * ch)
{
Character *victim;
if (ch->position != POS_FIGHTING
|| (victim = ch->fighting) == NULL || number_percent () > 2 * ch->level)
return false;
ch->act ("You bite $N!", NULL, victim, TO_CHAR);
ch->act ("$n bites $N!", NULL, victim, TO_NOTVICT);
ch->act ("$n bites you!", NULL, victim, TO_VICT);
ch->spell_poison (skill_lookup("poison"), ch->level, victim);
return true;
}
bool spec_thief (Character * ch)
{
if (ch->position != POS_STANDING)
return false;
CharIter rch;
for (rch = ch->in_room->people.begin(); rch != ch->in_room->people.end(); rch++) {
Character* victim = *rch;
if (victim->is_npc() || victim->level >= LEVEL_IMMORTAL ||
number_percent() <= 75 || !ch->can_see(victim)) /* Thx Glop */
continue;
if (victim->is_awake () && number_range (0, ch->level) == 0) {
ch->act ("You discover $n's hands in your wallet!",
NULL, victim, TO_VICT);
ch->act ("$N discovers $n's hands in $S wallet!",
NULL, victim, TO_NOTVICT);
return true;
} else {
int gold = victim->gold * number_range (1, 20) / 100;
ch->gold += 7 * gold / 8;
victim->gold -= gold;
return true;
}
}
return false;
}
/* This routine transfers between alpha and numeric forms of the
* mob_prog bitvector types. It allows the words to show up in mpstat to
* make it just a hair bit easier to see what a mob should be doing.
*/
const char *mprog_type_to_name (int type)
{
switch (type) {
case IN_FILE_PROG:
return "in_file_prog";
case ACT_PROG:
return "act_prog";
case SPEECH_PROG:
return "speech_prog";
case RAND_PROG:
return "rand_prog";
case FIGHT_PROG:
return "fight_prog";
case HITPRCNT_PROG:
return "hitprcnt_prog";
case DEATH_PROG:
return "death_prog";
case ENTRY_PROG:
return "entry_prog";
case GREET_PROG:
return "greet_prog";
case ALL_GREET_PROG:
return "all_greet_prog";
case GIVE_PROG:
return "give_prog";
case BRIBE_PROG:
return "bribe_prog";
default:
return "ERROR_PROG";
}
}
/* A trivial rehack of do_mstat. This doesnt show all the data, but just
* enough to identify the mob and give its basic condition. It does however,
* show the MOBprograms which are set.
*/
void Character::do_mpstat (std::string argument)
{
char buf[MAX_STRING_LENGTH];
std::string arg;
MobProgram *mprg;
Character *victim;
one_argument (argument, arg);
if (arg.empty()) {
send_to_char ("MobProg stat whom?\r\n");
return;
}
if ((victim = get_char_world (arg)) == NULL) {
send_to_char ("They aren't here.\r\n");
return;
}
if (!victim->is_npc ()) {
send_to_char ("Only Mobiles can have Programs!\r\n");
return;
}
if (!(victim->pIndexData->progtypes)) {
send_to_char ("That Mobile has no Programs set.\r\n");
return;
}
snprintf (buf, sizeof buf, "Name: %s. Vnum: %d.\r\n",
victim->name.c_str(), victim->pIndexData->vnum);
send_to_char (buf);
snprintf (buf, sizeof buf, "Short description: %s.\r\nLong description: %s",
victim->short_descr.c_str(),
!victim->long_descr.empty() ? victim->long_descr.c_str() : "(none).\r\n");
send_to_char (buf);
snprintf (buf, sizeof buf, "Hp: %d/%d. Mana: %d/%d. Move: %d/%d. \r\n",
victim->hit, victim->max_hit,
victim->mana, victim->max_mana, victim->move, victim->max_move);
send_to_char (buf);
snprintf (buf, sizeof buf,
"Lv: %d. Class: %d. Align: %d. AC: %d. Gold: %d. Exp: %d.\r\n",
victim->level, victim->klass, victim->alignment,
victim->get_ac(), victim->gold, victim->exp);
send_to_char (buf);
for (mprg = victim->pIndexData->mobprogs; mprg != NULL; mprg = mprg->next) {
snprintf (buf, sizeof buf, ">%s %s\r\n%s\r\n",
mprog_type_to_name (mprg->type), mprg->arglist.c_str(), mprg->comlist.c_str());
send_to_char (buf);
}
return;
}
/* prints the argument to all the rooms aroud the mobile */
void Character::do_mpasound (std::string argument)
{
Room *was_in_rm;
int door;
if (!is_npc ()) {
send_to_char ("Huh?\r\n");
return;
}
if (argument.empty()) {
bug_printf ("Mpasound - No argument from vnum %d.", pIndexData->vnum);
return;
}
was_in_rm = in_room;
for (door = 0; door <= 5; door++) {
Exit *pexit;
if ((pexit = was_in_rm->exit[door]) != NULL
&& pexit->to_room != NULL && pexit->to_room != was_in_rm) {
in_room = pexit->to_room;
MOBtrigger = false;
act (argument, NULL, NULL, TO_ROOM);
}
}
in_room = was_in_rm;
return;
}
/* lets the mobile kill any player or mobile without murder*/
void Character::do_mpkill (std::string argument)
{
std::string arg;
Character *victim;
if (!is_npc ()) {
send_to_char ("Huh?\r\n");
return;
}
one_argument (argument, arg);
if (arg.empty()) {
bug_printf ("MpKill - No argument from vnum %d.", pIndexData->vnum);
return;
}
if ((victim = get_char_room (arg)) == NULL) {
bug_printf ("MpKill - Victim not in room from vnum %d.", pIndexData->vnum);
return;
}
if (victim == this) {
bug_printf ("MpKill - Victim is self from vnum %d.", pIndexData->vnum);
return;
}
if (is_affected (AFF_CHARM) && master == victim) {
bug_printf ("MpKill - Charmed mob attacking master from vnum %d.",
pIndexData->vnum);
return;
}
if (position == POS_FIGHTING) {
bug_printf ("MpKill - Already fighting from vnum %d", pIndexData->vnum);
return;
}
multi_hit (this, victim, TYPE_UNDEFINED);
return;
}
/* lets the mobile destroy an object in its inventory
it can also destroy a worn object and it can destroy
items using all.xxxxx or just plain all of them */
void Character::do_mpjunk (std::string argument)
{
std::string arg;
Object *obj;
if (!is_npc ()) {
send_to_char ("Huh?\r\n");
return;
}
one_argument (argument, arg);
if (arg.empty()) {
bug_printf ("Mpjunk - No argument from vnum %d.", pIndexData->vnum);
return;
}
if (str_cmp (arg, "all") && str_prefix ("all.", arg)) {
if ((obj = get_obj_wear (arg)) != NULL) {
unequip_char(obj);
obj->extract_obj ();
return;
}
if ((obj = get_obj_carry (arg)) == NULL)
return;
obj->extract_obj ();
} else {
ObjIter o, onext;
for (o = carrying.begin(); o != carrying.end(); o = onext) {
obj = *o;
onext = ++o;
if (arg[3] == '\0' || is_name (&arg[4], obj->name)) {
if (obj->wear_loc != WEAR_NONE)
unequip_char(obj);
obj->extract_obj ();
}
}
}
return;
}
/* prints the message to everyone in the room other than the mob and victim */
void Character::do_mpechoaround (std::string argument)
{
std::string arg;
Character *victim;
if (!is_npc ()) {
send_to_char ("Huh?\r\n");
return;
}
argument = one_argument (argument, arg);
if (arg.empty()) {
bug_printf ("Mpechoaround - No argument from vnum %d.", pIndexData->vnum);
return;
}
if (!(victim = get_char_room (arg))) {
bug_printf ("Mpechoaround - Victim does not exist from vnum %d.",
pIndexData->vnum);
return;
}
act (argument, NULL, victim, TO_NOTVICT);
return;
}
/* prints the message to only the victim */
void Character::do_mpechoat (std::string argument)
{
std::string arg;
Character *victim;
if (!is_npc ()) {
send_to_char ("Huh?\r\n");
return;
}
argument = one_argument (argument, arg);
if (arg.empty() || argument.empty()) {
bug_printf ("Mpechoat - No argument from vnum %d.", pIndexData->vnum);
return;
}
if (!(victim = get_char_room (arg))) {
bug_printf ("Mpechoat - Victim does not exist from vnum %d.",
pIndexData->vnum);
return;
}
act (argument, NULL, victim, TO_VICT);
return;
}
/* prints the message to the room at large */
void Character::do_mpecho (std::string argument)
{
if (!is_npc ()) {
send_to_char ("Huh?\r\n");
return;
}
if (argument.empty()) {
bug_printf ("Mpecho - Called w/o argument from vnum %d.", pIndexData->vnum);
return;
}
act (argument, NULL, NULL, TO_ROOM);
return;
}
/* lets the mobile load an item or mobile. All items
are loaded into inventory. you can specify a level with
the load object portion as well. */
void Character::do_mpmload (std::string argument)
{
std::string arg;
MobPrototype *pMobIndex;
Character *victim;
if (!is_npc ()) {
send_to_char ("Huh?\r\n");
return;
}
one_argument (argument, arg);
if (arg.empty() || !is_number (arg)) {
bug_printf ("Mpmload - Bad vnum as arg from vnum %d.", pIndexData->vnum);
return;
}
if ((pMobIndex = get_mob_index (std::atoi (arg.c_str()))) == NULL) {
bug_printf ("Mpmload - Bad mob vnum from vnum %d.", pIndexData->vnum);
return;
}
victim = pMobIndex->create_mobile ();
victim->char_to_room(in_room);
return;
}
void Character::do_mpoload (std::string argument)
{
std::string arg1, arg2;
ObjectPrototype *pObjIndex;
Object *obj;
int lvl;
if (!is_npc ()) {
send_to_char ("Huh?\r\n");
return;
}
argument = one_argument (argument, arg1);
argument = one_argument (argument, arg2);
if (arg1.empty() || !is_number (arg1)) {
bug_printf ("Mpoload - Bad syntax from vnum %d.", pIndexData->vnum);
return;
}
if (arg2.empty()) {
lvl = get_trust ();
} else {
/*
* New feature from Alander.
*/
if (!is_number (arg2)) {
bug_printf ("Mpoload - Bad syntax from vnum %d.", pIndexData->vnum);
return;
}
lvl = std::atoi (arg2.c_str());
if (lvl < 0 || lvl > get_trust ()) {
bug_printf ("Mpoload - Bad level from vnum %d.", pIndexData->vnum);
return;
}
}
if ((pObjIndex = get_obj_index (std::atoi (arg1.c_str()))) == NULL) {
bug_printf ("Mpoload - Bad vnum arg from vnum %d.", pIndexData->vnum);
return;
}
obj = pObjIndex->create_object (lvl);
if (obj->can_wear(ITEM_TAKE)) {
obj->obj_to_char (this);
} else {
obj->obj_to_room (in_room);
}
return;
}
/* lets the mobile purge all objects and other npcs in the room,
or purge a specified object or mob in the room. It can purge
itself, but this had best be the last command in the MOBprogram
otherwise ugly stuff will happen */
void Character::do_mppurge (std::string argument)
{
std::string arg;
Character *victim;
Object *obj;
if (!is_npc ()) {
send_to_char ("Huh?\r\n");
return;
}
one_argument (argument, arg);
if (arg.empty()) {
/* 'purge' */
CharIter rch, rnext;
for (rch = in_room->people.begin(); rch != in_room->people.end(); rch = rnext) {
victim = *rch;
rnext = ++rch;
if (victim->is_npc () && victim != this)
victim->extract_char (true);
}
ObjIter o, onext;
for (o = in_room->contents.begin(); o != in_room->contents.end(); o = onext) {
obj = *o;
onext = ++o;
obj->extract_obj ();
}
return;
}
if ((victim = get_char_room (arg)) == NULL) {
if ((obj = get_obj_here (arg))) {
obj->extract_obj ();
} else {
bug_printf ("Mppurge - Bad argument from vnum %d.", pIndexData->vnum);
}
return;
}
if (!victim->is_npc ()) {
bug_printf ("Mppurge - Purging a PC from vnum %d.", pIndexData->vnum);
return;
}
victim->extract_char (true);
return;
}
/* lets the mobile goto any location it wishes that is not private */
void Character::do_mpgoto (std::string argument)
{
std::string arg;
Room *location;
if (!is_npc ()) {
send_to_char ("Huh?\r\n");
return;
}
one_argument (argument, arg);
if (arg.empty()) {
bug_printf ("Mpgoto - No argument from vnum %d.", pIndexData->vnum);
return;
}
if ((location = find_location (this, arg)) == NULL) {
bug_printf ("Mpgoto - No such location from vnum %d.", pIndexData->vnum);
return;
}
if (fighting != NULL)
stop_fighting (true);
char_from_room();
char_to_room(location);
return;
}
/* lets the mobile do a command at another location. Very useful */
void Character::do_mpat (std::string argument)
{
std::string arg;
Room *location;
Room *original;
if (!is_npc ()) {
send_to_char ("Huh?\r\n");
return;
}
argument = one_argument (argument, arg);
if (arg.empty() || argument.empty()) {
bug_printf ("Mpat - Bad argument from vnum %d.", pIndexData->vnum);
return;
}
if ((location = find_location (this, arg)) == NULL) {
bug_printf ("Mpat - No such location from vnum %d.", pIndexData->vnum);
return;
}
original = in_room;
char_from_room();
char_to_room(location);
interpret (argument);
/*
* See if 'this' still exists before continuing!
* Handles 'at XXXX quit' case.
*/
for (CharIter c = char_list.begin(); c != char_list.end(); c++) {
if (*c == this) {
char_from_room();
char_to_room(original);
break;
}
}
return;
}
/* lets the mobile transfer people. the all argument transfers
everyone in the current room to the specified location */
void Character::do_mptransfer (std::string argument)
{
std::string arg1, arg2;
Room *location;
Character *victim;
if (!is_npc ()) {
send_to_char ("Huh?\r\n");
return;
}
argument = one_argument (argument, arg1);
argument = one_argument (argument, arg2);
if (arg1.empty()) {
bug_printf ("Mptransfer - Bad syntax from vnum %d.", pIndexData->vnum);
return;
}
if (!str_cmp (arg1, "all")) {
for (DescIter d = descriptor_list.begin(); d != descriptor_list.end(); d++) {
if ((*d)->connected == CON_PLAYING
&& (*d)->character != this
&& (*d)->character->in_room != NULL && can_see((*d)->character)) {
char buf[MAX_STRING_LENGTH];
snprintf (buf, sizeof buf, "%s %s", (*d)->character->name.c_str(), arg2.c_str());
do_transfer (buf);
}
}
return;
}
/*
* Thanks to Grodyn for the optional location parameter.
*/
if (arg2.empty()) {
location = in_room;
} else {
if ((location = find_location (this, arg2)) == NULL) {
bug_printf ("Mptransfer - No such location from vnum %d.",
pIndexData->vnum);
return;
}
if (location->is_private()) {
bug_printf ("Mptransfer - Private room from vnum %d.", pIndexData->vnum);
return;
}
}
if ((victim = get_char_world (arg1)) == NULL) {
bug_printf ("Mptransfer - No such person from vnum %d.", pIndexData->vnum);
return;
}
if (victim->in_room == NULL) {
bug_printf ("Mptransfer - Victim in Limbo from vnum %d.", pIndexData->vnum);
return;
}
if (victim->fighting != NULL)
victim->stop_fighting (true);
victim->char_from_room();
victim->char_to_room(location);
return;
}
/* lets the mobile force someone to do something. must be mortal level
and the all argument only affects those in the room with the mobile */
void Character::do_mpforce (std::string argument)
{
std::string arg;
if (!is_npc ()) {
send_to_char ("Huh?\r\n");
return;
}
argument = one_argument (argument, arg);
if (arg.empty() || argument.empty()) {
bug_printf ("Mpforce - Bad syntax from vnum %d.", pIndexData->vnum);
return;
}
if (!str_cmp (arg, "all")) {
Character *vch;
CharIter c, next;
for (c = char_list.begin(); c != char_list.end(); c = next) {
vch = *c;
next = ++c;
if (vch->in_room == in_room && vch->get_trust () < get_trust ()
&& can_see(vch)) {
vch->interpret (argument);
}
}
} else {
Character *victim;
if ((victim = get_char_room (arg)) == NULL) {
bug_printf ("Mpforce - No such victim from vnum %d.", pIndexData->vnum);
return;
}
if (victim == this) {
bug_printf ("Mpforce - Forcing oneself from vnum %d.", pIndexData->vnum);
return;
}
victim->interpret (argument);
}
return;
}
void new_descriptor (void)
{
Descriptor *dnew;
struct sockaddr_in sock;
SOCKET desc;
#ifndef WIN32
socklen_t size;
#else
int size;
unsigned long flags = 1;
#endif
size = sizeof (sock);
getsockname (g_listen, (struct sockaddr *) &sock, &size);
if ((desc = accept (g_listen, (struct sockaddr *) &sock, &size)) == INVALID_SOCKET) {
std::perror ("New_descriptor: accept");
return;
}
#if !defined(FNDELAY)
#define FNDELAY O_NDELAY
#endif
#ifdef WIN32
if (ioctlsocket (desc, FIONBIO, &flags)) {
#else
if (fcntl (desc, F_SETFL, FNDELAY) == -1) {
#endif
std::perror ("New_descriptor: fcntl: FNDELAY");
return;
}
/*
* Cons a new descriptor.
*/
dnew = new Descriptor(desc);
dnew->assign_hostname();
/*
* Swiftest: I added the following to ban sites. I don't
* endorse banning of sites, but Copper has few descriptors now
* and some people from certain sites keep abusing access by
* using automated 'autodialers' and leaving connections hanging.
*
* Furey: added suffix check by request of Nickel of HiddenWorlds.
*/
for (std::list<Ban*>::iterator pban = ban_list.begin();
pban != ban_list.end(); pban++) {
if (str_suffix ((*pban)->name, dnew->host)) {
write_to_descriptor (desc,
"Your site has been banned from this Mud.\r\n", 0);
closesocket (desc);
delete dnew;
return;
}
}
descriptor_list.push_back(dnew);
/*
* Send the greeting.
*/
dnew->write_to_buffer (help_greeting);
return;
}
void game_loop (void)
{
static struct timeval null_time;
struct timeval last_time;
#ifndef WIN32
signal (SIGPIPE, SIG_IGN);
#endif
gettimeofday (&last_time, NULL);
g_world->set_current_time(last_time.tv_sec);
/* Main loop */
while (!merc_down) {
fd_set in_set;
fd_set out_set;
fd_set exc_set;
#ifdef WIN32
fd_set dummy_set;
#endif
SOCKET maxdesc;
/*
* Poll all active descriptors.
*/
FD_ZERO (&in_set);
FD_ZERO (&out_set);
FD_ZERO (&exc_set);
FD_SET (g_listen, &in_set);
#ifdef WIN32
FD_ZERO (&dummy_set);
FD_SET (g_listen, &dummy_set);
#endif
maxdesc = g_listen;
DescIter d;
for (d = descriptor_list.begin(); d != descriptor_list.end(); d++) {
maxdesc = std::max (maxdesc, (*d)->descriptor);
FD_SET ((*d)->descriptor, &in_set);
FD_SET ((*d)->descriptor, &out_set);
FD_SET ((*d)->descriptor, &exc_set);
}
if (select (maxdesc + 1, &in_set, &out_set, &exc_set, &null_time) == SOCKET_ERROR) {
fatal_printf ("Game_loop: select: poll");
}
/*
* New connection?
*/
if (FD_ISSET (g_listen, &in_set))
new_descriptor ();
/*
* Kick out the freaky folks.
*/
for (d = descriptor_list.begin(); d != descriptor_list.end(); d = deepdenext) {
Descriptor* d_this = *d;
deepdenext = ++d;
if (FD_ISSET (d_this->descriptor, &exc_set)) {
FD_CLR (d_this->descriptor, &in_set);
FD_CLR (d_this->descriptor, &out_set);
if (d_this->character)
d_this->character->save_char_obj();
d_this->outbuf.erase();
d_this->close_socket();
}
}
/*
* Process input.
*/
for (d = descriptor_list.begin(); d != descriptor_list.end(); d = deepdenext) {
Descriptor* d_this = *d;
deepdenext = ++d;
d_this->fcommand = false;
if (FD_ISSET (d_this->descriptor, &in_set)) {
if (d_this->character != NULL)
d_this->character->timer = 0;
if (!d_this->read_from_descriptor()) {
FD_CLR (d_this->descriptor, &out_set);
if (d_this->character != NULL)
d_this->character->save_char_obj();
d_this->outbuf.erase();
d_this->close_socket();
continue;
}
}
if (d_this->character != NULL && d_this->character->wait > 0) {
--d_this->character->wait;
continue;
}
d_this->read_from_buffer();
if (!d_this->incomm.empty()) {
d_this->fcommand = true;
if (d_this->character)
d_this->character->stop_idling();
if (d_this->connected == CON_PLAYING)
if (d_this->showstr_point)
d_this->show_string (d_this->incomm);
else
d_this->character->interpret (d_this->incomm);
else
d_this->nanny (d_this->incomm);
}
}
/*
* Autonomous game motion.
*/
update_handler ();
/*
* Output.
*/
for (d = descriptor_list.begin(); d != descriptor_list.end(); d = deepdenext) {
Descriptor* d_this = *d;
deepdenext = ++d;
if ((d_this->fcommand || !d_this->outbuf.empty())
&& FD_ISSET (d_this->descriptor, &out_set)) {
if (!d_this->process_output(true)) {
if (d_this->character != NULL)
d_this->character->save_char_obj();
d_this->outbuf.erase();
d_this->close_socket();
}
}
}
/*
* Synchronize to a clock.
* Sleep( last_time + 1/PULSE_PER_SECOND - now ).
* Careful here of signed versus unsigned arithmetic.
*/
{
struct timeval now_time;
long secDelta;
long usecDelta;
gettimeofday (&now_time, NULL);
usecDelta = ((int) last_time.tv_usec) - ((int) now_time.tv_usec)
+ 1000000 / PULSE_PER_SECOND;
secDelta = ((int) last_time.tv_sec) - ((int) now_time.tv_sec);
while (usecDelta < 0) {
usecDelta += 1000000;
secDelta -= 1;
}
while (usecDelta >= 1000000) {
usecDelta -= 1000000;
secDelta += 1;
}
if (secDelta > 0 || (secDelta == 0 && usecDelta > 0)) {
struct timeval stall_time;
stall_time.tv_usec = usecDelta;
stall_time.tv_sec = secDelta;
#ifdef WIN32 /* windows select demands a valid fd_set */
if (select (0, NULL, NULL, &dummy_set, &stall_time) == SOCKET_ERROR) {
#else
if (select (0, NULL, NULL, NULL, &stall_time) == SOCKET_ERROR) {
#endif
fatal_printf ("Game_loop: select: stall");
}
}
}
gettimeofday (&last_time, NULL);
g_world->set_current_time(last_time.tv_sec);
}
return;
}
/*
* Parse a name for acceptability.
*/
bool check_parse_name (const std::string & name)
{
/*
* Reserved words.
*/
if (is_name (name, "all auto immortal self someone"))
return false;
/*
* Length restrictions.
*/
if (name.size() < 3 || name.size() > 12)
return false;
/*
* Alphanumerics only.
* Lock out IllIll twits.
*/
std::string::const_iterator pc;
bool fIll = true;
for (pc = name.begin(); pc != name.end(); pc++) {
if (!isalpha (*pc))
return false;
if (tolower (*pc) != 'i' && tolower (*pc) != 'l')
fIll = false;
}
if (fIll)
return false;
/*
* Prevent players from naming themselves after mobs.
*/
std::map<int,MobPrototype*>::iterator pmob;
for (pmob = mob_table.begin(); pmob != mob_table.end(); pmob++) {
if (is_name (name, (*pmob).second->name))
return false;
}
return true;
}
int init_server_socket (void)
{
SOCKET fd;
if ((fd = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
fatal_printf("Init_socket: socket");
}
int x = 1;
if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (char *) &x, sizeof (x)) == SOCKET_ERROR) {
closesocket (fd);
fatal_printf ("Init_socket: SO_REUSEADDR");
}
#ifdef SO_DONTLINGER
struct linger ld;
ld.l_onoff = 1;
ld.l_linger = 1000;
if (setsockopt (fd, SOL_SOCKET, SO_DONTLINGER, (char *) &ld, sizeof (ld)) == SOCKET_ERROR) {
closesocket (fd);
fatal_printf ("Init_socket: SO_DONTLINGER");
}
#endif
static struct sockaddr_in sa_zero;
struct sockaddr_in sa;
sa = sa_zero;
sa.sin_family = AF_INET;
sa.sin_port = htons (g_port);
if (bind (fd, (struct sockaddr *) &sa, sizeof (sa)) < 0) {
closesocket (fd);
fatal_printf ("Init_socket: bind");
}
if (listen (fd, 3) < 0) {
closesocket (fd);
fatal_printf ("Init_socket: listen");
}
return fd;
}
#if defined(WIN32) && !defined(__DMC__)
void hotboot(void) {
WSAPROTOCOL_INFO proto_info;
int count_users = 0;
std::FILE* fp;
// Open events created by parent server
void * file_event = OpenEvent(SYNCHRONIZE, FALSE, "file_created");
if (file_event == NULL) {
win_errprint("Error opening event, file_created");
return;
}
void * shutdown_event = OpenEvent(EVENT_MODIFY_STATE, FALSE, "ok_to_shutdown");
if (shutdown_event == NULL) {
win_errprint("Error opening event, ok_to_shutdown");
CloseHandle(file_event);
return;
}
// Wait for parent server to build copyover file
if (WaitForSingleObject(file_event, INFINITE) == WAIT_FAILED) {
win_errprint("Error waiting on file_event");
CloseHandle(file_event);
CloseHandle(shutdown_event);
return;
}
if ((fp = std::fopen ("hotboot.$$$", "r+b")) == NULL) {
win_errprint("Error opening hotboot file");
CloseHandle(file_event);
CloseHandle(shutdown_event);
return;
}
fread(&count_users, sizeof(int),1,fp); // how many users?
// read in info about listening socket and build it
fread(&proto_info,sizeof(WSAPROTOCOL_INFO),1,fp);
g_listen = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
&proto_info, 0, 0);
if (g_listen == INVALID_SOCKET) {
bug_printf ("Error opening listening socket : %d.", WSAGetLastError());
}
// read in info about users and build sockets for them
for (int i = 0; i < count_users; i++) {
char chname[25];
fread(&proto_info,sizeof(WSAPROTOCOL_INFO),1,fp);
fread(chname,sizeof(chname),1,fp);
SOCKET sock = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
&proto_info, 0, 0);
if (sock == INVALID_SOCKET) {
bug_printf ("Error opening user socket : %d.", WSAGetLastError());
continue;
}
if (!write_to_descriptor (sock, "Returning from hotboot.\r\n", 0)) {
bug_printf ("Error writing user socket");
closesocket(sock);
continue;
}
Descriptor * d = new Descriptor(sock);
d->assign_hostname();
descriptor_list.push_back(d);
if (!d->load_char_obj (chname)) {
write_to_descriptor (sock, "Your character vanished during hotboot. Bye.\r\n", 0);
bug_printf ("Error loading character %s after hotboot", chname);
d->close_socket();
continue;
}
d->connected = CON_PLAYING;
char_list.push_back(d->character);
write_to_descriptor (sock, "Hotboot recovery complete.\r\n", 0);
if (!d->character->in_room)
d->character->in_room = get_room_index (ROOM_VNUM_TEMPLE);
d->character->char_to_room (d->character->in_room);
d->character->do_look("");
d->character->act ("$n materializes!", NULL, NULL, TO_ROOM);
}
fclose(fp);
// if (remove("hotboot.$$$")) {
// win_errprint("Error removing hotboot file");
// }
// tell parent its ok to go away
if (SetEvent(shutdown_event) == NULL) {
win_errprint("Error setting ok_to_shutdown event");
}
// cleanup event handles
CloseHandle(file_event);
CloseHandle(shutdown_event);
}
#endif
int main (int argc, char **argv)
{
g_world = World::instance();
g_db = Database::instance();
str_boot_time = g_world->get_time_text();
g_db->initialize("murk.db");
// Get the port number.
g_port = 1234;
if (argc > 1) {
if (!is_number (argv[1])) {
fatal_printf ("Usage: %s [port #]");
} else if ((g_port = std::atoi (argv[1])) <= 1024) {
fatal_printf("Port number must be above 1024.");
}
}
WIN32STARTUP
// Run the game.
#if defined(WIN32) && !defined(__DMC__)
if (argc < 3) g_listen = init_server_socket();
g_db->boot();
if (argc > 2) hotboot();
#else
g_listen = init_server_socket();
g_db->boot();
#endif
g_world->area_update();
log_printf ("Merc is ready to rock on port %d.", g_port);
game_loop ();
// Normal exit
closesocket (g_listen);
log_printf ("Normal termination of game.");
WIN32CLEANUP
g_db->shutdown();
return 0;
}