/* /lib/npc.c * from the Dead Souls LPC Library * the standard non-player character object * created by Descartes of Borg 950323 * Version: @(#) npc.c 1.11@(#) * Last Modified: 96/12/21 */ #include <lib.h> #include <daemons.h> #include <position.h> #include <armor_types.h> #include <message_class.h> #include <vision.h> #include "include/npc.h" inherit LIB_CHAT; inherit LIB_COMMAND; inherit LIB_CONTAINER; inherit LIB_LIVING; inherit LIB_MESSAGES; inherit LIB_MOVE; inherit LIB_OBJECT; inherit LIB_CRAWL; inherit LIB_SAVE; private int CustomXP, ActionChance, CombatActionChance, AutoStand, Mount; private int MaximumHealth = 0; private mixed Encounter; private string *EnemyNames; private static int Level, Unique; private static mixed Die, Action, CombatAction; private static mapping Inventory; int eventExtraAction(){ return 1; } static void create() { AddSave( ({ "CarriedMass" }) ); SetSaveRecurse(1); chat::create(); command::create(); living::create(); messages::create(); object::create(); Setup(); SetPK(1); SetOpacity(0); EnemyNames = ({}); Encounter = 0; ActionChance = 0; Unique = 0; CustomXP = 0; Inventory = ([]); AutoStand = 1; } void CheckEncounter(){ string *enemies; if( !query_heart_beat() ) { eventCheckHealing(); set_heart_beat( GetHeartRate() ); } if( sizeof(enemies = GetEnemyNames()) ) { if( member_array((string)this_player()->GetKeyName(),enemies) != -1 ) { eventExecuteAttack(this_player()); return; } } if( Encounter && !query_invis(this_player(),this_object())) { int x = 0; if( functionp(Encounter) ) { x = (int)evaluate(Encounter, this_player()); } else if( arrayp(Encounter) ) { if( member_array(this_player()->GetKeyName(), Encounter) > -1 ) { x = 1; } else { x = 1; } } else if( (int)this_player()->GetStatLevel("charisma") < Encounter ) { x = 1; } if( x ) { SetAttack(this_player()); } } } static void init() { CheckEncounter(); } static void heart_beat() { int position; living::heart_beat(); if( !ContinueHeart() ) { set_heart_beat(0); return; } eventExtraAction(); position = GetPosition(); if( position == POSITION_LYING || position == POSITION_SITTING ) { if(AutoStand && !RACES_D->GetLimblessRace(this_object()->GetRace())) eventForce("stand up"); if(GetInCombat() && !RACES_D->GetLimblessRace(this_object()->GetRace()) ) eventForce("stand up"); } if( !GetInCombat() && ActionChance > random(100) ) { int x; if( functionp(Action) ) evaluate(Action); else if( pointerp(Action) && (x = sizeof(Action)) ) { mixed act; act = Action[random(x)]; if(functionp(act)) { evaluate(act); return; } if( act && act != "" && act[0] == '!' && act != "!" ) { act = act[1..]; eventForce(act); } else message("other_action", act, environment()); } } else if( GetInCombat() && CombatActionChance > random(100) ) { int x; if( functionp(CombatAction) ) { evaluate(CombatAction); } else if( pointerp(CombatAction) && (x = sizeof(CombatAction)) ) { mixed mact; string act; x--; mact = CombatAction[random(x)]; if( functionp(mact) ) { evaluate(mact); return; } if( !stringp(mact) ) mact = "emote looks confused."; else act = mact; if( act && act != "" ) { eventForce(act); } else message("other_action", act, environment()); } } } void eventDescribeEnvironment(int brief) { object env; mixed tmp; string *shorts; string desc, smell, sound, touch; int i, maxi; if(!(env = environment(this_object()))) { eventPrint("You are nowhere.", MSG_ROOMDESC); return; } switch( i = GetEffectiveVision() ) { case VISION_BLIND: eventPrint("You are blind and can see nothing."); break; case VISION_TOO_DARK: eventPrint("It is much too dark to see."); break; case VISION_DARK: eventPrint("It is too dark to see."); break; case VISION_TOO_BRIGHT: eventPrint("It is much too %^YELLOW%^bright%^RESET%^ to see."); break; case VISION_BRIGHT: eventPrint("It is too %^YELLOW%^bright%^RESET%^ to see."); break; } if( !brief ) { if( i == VISION_CLEAR ) { desc = (string)env->GetObviousExits() || ""; desc = capitalize((string)env->GetShort() || "") + " [" + desc + "]\n"; } else desc = ""; if( i == VISION_CLEAR || i == VISION_LIGHT || i == VISION_DIM ) desc += (string)env->GetLong(); if(functionp(tmp = (mixed)env->GetSmell("default"))) tmp = (string)(*tmp)("default"); smell = tmp; if(functionp(tmp = (mixed)env->GetListen("default"))) tmp = (string)(*tmp)("default"); sound = tmp; if( functionp(tmp = (mixed)env->GetTouch("default")) ) tmp = evaluate(tmp, "default"); touch = tmp; } else { if( i == VISION_CLEAR || i == VISION_LIGHT || i == VISION_DIM ) { desc = (string)env->GetShort(); if( (tmp = (string)env->GetObviousExits()) && tmp != "" ) desc += " [" + tmp + "]"; } else desc = ""; } if( desc ) eventPrint(desc, MSG_ROOMDESC); if( smell ) eventPrint("%^GREEN%^" + smell, MSG_ROOMDESC); if( sound ) eventPrint("%^CYAN%^" + sound, MSG_ROOMDESC); if( touch ) eventPrint("%^YELLOW%^" + touch, MSG_ROOMDESC); desc = ""; if( i == VISION_CLEAR ) { mapping lying = ([]); shorts = map(filter(all_inventory(env), function(object ob) { if( living(ob) ) return 0; if( (int)ob->GetInvis(this_object()) ) return 0; if( (int)ob->isFreshCorpse() ) return 0; return 1; }), (: (string)$1->GetShort() :)); foreach(string s in shorts) { if( s ) { lying[s]++; } } for(i=0, desc = 0, maxi = sizeof(shorts = keys(lying)); i<maxi; i++) { string key = shorts[i]; int val = lying[shorts[i]]; if( val < 2 ) { if( !desc ) desc = "%^MAGENTA%^" + capitalize(key) + "%^RESET%^MAGENTA%^"; else desc += key + "%^RESET%^MAGENTA%^"; } else { if( !desc ) desc = "%^MAGENTA%^" + capitalize(consolidate(val, key)) + "%^RESET%^MAGENTA%^"; else desc += consolidate(val, key) + "%^RESET%^MAGENTA%^"; } if( i == maxi - 1 ) { if( maxi > 1 || val >1 ) desc += " are here.%^RESET%^\n"; else desc += " is here.%^RESET%^\n"; } else if( i == maxi - 2 ) { if( maxi == 2 ) { desc += " and "; } else { desc += ", and "; } } else desc += ", "; } } i = GetEffectiveVision(); if( i == VISION_CLEAR || i == VISION_LIGHT || i == VISION_DIM ) { mapping lying = ([]), sitting = ([]), standing = ([]), flying = ([]); object *obs; string key; int val; obs = filter(all_inventory(env), function(object ob) { if( (int)ob->GetInvis(this_object()) ) return 0; if( living(ob) ) return 1; if( (int)ob->isFreshCorpse() ) return 1; }) - ({ this_object() }); maxi = sizeof(shorts = map(obs, (: (string)$1->GetHealthShort() :))); foreach(object liv in obs) { string s = (string)liv->GetHealthShort(); int pos = (int)liv->GetPosition(); if( !s ) continue; if( pos == POSITION_STANDING) standing[s]++; else if( pos == POSITION_LYING || (int)liv->isFreshCorpse() ) lying[s]++; else if( pos == POSITION_SITTING ) sitting[s]++; else if( pos == POSITION_FLYING ) flying[s]++; else lying[s]++; } if( !desc ) { tmp = ""; } else { tmp = desc; } desc = ""; foreach(key, val in lying) { if( val<2 ) desc += capitalize(key) + "%^RESET%^ is lying down."; else desc += capitalize(consolidate(val, key)) + "%^RESET%^ are lying down."; desc += "\n"; } foreach(key, val in sitting) { if( val<2 ) desc += capitalize(key) + "%^RESET%^ is sitting down."; else desc += capitalize(consolidate(val, key)) + "%^RESET%^ are sitting down."; desc += "\n"; } foreach(key, val in standing) { if( val<2 ) desc += capitalize(key) + "%^RESET%^ is standing here."; else desc += capitalize(consolidate(val, key)) + "%^RESET%^ are standing here."; desc += "\n"; } foreach(key, val in flying) { if( val<2 ) desc += capitalize(key) + "%^RESET%^ is hovering here."; else desc += capitalize(consolidate(val, key)) + "%^RESET%^ are hovering here."; desc += "\n"; } } if( tmp ) { desc = tmp + desc; } if( sizeof(desc) ) { eventPrint(desc + "\n", MSG_ROOMDESC); } } void receive_message(string cl, string msg) { catch_tell(msg); } void catch_tell(string msg) { } static int Destruct() { if( GetParty() ) PARTY_D->eventLeaveParty(this_object()); living::Destruct(); return object::Destruct(); } void eventReconnect() { } /* *************** /lib/npc.c command functions ************** */ static int cmdAll(string arg) { object env; string verb; verb = query_verb(); env = environment(); if( chat::chat_command(verb + " " + arg) == "" ) return 1; return command::cmdAll(arg); } /* *************** /lib/npc.c events *************** */ int eventCompleteMove(mixed dest) { mixed val; string file; int x; if( environment() ) return move::eventMove(dest); else x = move::eventMove(dest); if( !x ) return x; foreach(file, val in Inventory) { object ob; if( intp(val) ) { if( val < 0 ) { ob = unique(file, -val); if( ob ) ob->eventMove(this_object()); } else while(val--) if( ob = new(file) ) ob->eventMove(this_object()); } else if( stringp(val) ) { if( !(ob = new(file)) ) continue; if( ob->eventMove(this_object()) ) eventForce(val); } else if( functionp(val) ) { if( !(ob = new(file)) ) continue; if( ob->eventMove(this_object()) ) evaluate(val); } } return x; } int eventDestruct() { chat::eventDestruct(); object::eventDestruct(); } varargs int eventDie(mixed agent) { int x; if(this_object()->GetDead() || this_object()->GetDeathEvents()) return 0; if( (x = living::eventDie(agent)) != 1 ) return x; if( stringp(Die) ) { message("other_action", Die, environment(), ({ this_object() })); if( agent) message("my_action", "You kill " + GetName() + ".", agent); } else if( functionp(Die) && !evaluate(Die, agent) ) return 0; else { if(GetPosition() == POSITION_STANDING) message("other_action", "%^BOLD%^%^RED%^"+ GetName() + " drops dead.", environment(), ({ this_object() }) ); else if(GetPosition() == POSITION_FLYING) message("other_action", "%^BOLD%^%^RED%^"+ GetName() + " falls dead.", environment(), ({ this_object() }) ); else message("other_action", "%^BOLD%^%^RED%^"+ GetName() + " finally dies.", environment(), ({ this_object() }) ); if( agent ) message("my_action", "You kill " + GetName() + ".", agent); } set_heart_beat(0); call_out( (: Destruct :), 0); flush_messages(); return 1; } mixed eventTurn(object who) { if( !living::eventTurn(who) ) { return 0; } all_inventory()->eventDestruct(); call_out((: Destruct :), 0); return 1; } void eventEnemyDied(object ob) { living::eventEnemyDied(ob); EnemyNames -= ({ (string)ob->GetKeyName() }); } int eventMove(mixed dest) { int ret; ret = eventCompleteMove(dest); //if(environment(this_object())) eventMoveFollowers(environment(this_object())); return ret; } varargs int eventMoveLiving(mixed dest, string omsg, string imsg) { object *inv; object prev; string msgclass; if( prev = environment() ) { if( stringp(dest) ) { if(dest[0] != '/') { string *arr; arr = explode(file_name(prev), "/"); dest = "/"+implode(arr[0..sizeof(arr)-2], "/")+"/"+dest; } } if( !eventCompleteMove(dest) ) { eventPrint("You remain where you are."); return 0; } inv = filter(all_inventory(prev), (: (!GetInvis($1) && living($1) && ($1 != this_object())) :)); if( !omsg || omsg == "" ) omsg = GetMessage("telout"); else if(GetPosition() == POSITION_SITTING || GetPosition() == POSITION_LYING ){ omsg = this_object()->GetName()+" crawls "+omsg+"."; } else if(GetPosition() == POSITION_FLYING ){ omsg = this_object()->GetName()+" flies "+omsg+"."; } else omsg = GetMessage("leave", omsg); inv->eventPrint(omsg, MSG_ENV); } else if( !eventCompleteMove(dest) ) { eventPrint("You remain where you are."); return 0; } inv = filter(all_inventory(environment()), (: (!GetInvis($1) && living($1) && ($1 != this_object())) :)); if( (!imsg || imsg == "") && (!omsg || omsg == "") ) imsg = GetMessage(msgclass = "telin"); else if(GetPosition() == POSITION_SITTING || GetPosition() == POSITION_LYING ){ imsg = this_object()->GetName()+" crawls in"; } else if(GetPosition() == POSITION_FLYING ){ imsg = this_object()->GetName()+" flies in."; } else if( !imsg || imsg == "" ) imsg = GetMessage(msgclass = "come", imsg); else imsg = replace_string(imsg, "$N", GetName()); inv->eventPrint(imsg, MSG_ENV); this_object()->eventDescribeEnvironment(0); eventMoveFollowers(environment(this_player())); return 1; } varargs int eventPrint(string msg, mixed arg2, mixed arg3) { return 1; } int eventReceiveObject() { object ob; ob = previous_object(); if( !ob || !container::eventReceiveObject() ) return 0; AddCarriedMass((int)ob->GetMass()); return 1; } int eventReleaseObject() { object ob; ob = previous_object(); if( !ob || !container::eventReleaseObject() ) return 0; AddCarriedMass( -((int)ob->GetMass()) ); return 1; } varargs int eventShow(object who, string str) { if( !living::eventShow(who, str) ) return 0; eventPrint((string)this_player()->GetName() + " looks you over."); return 1; } /* *************** /lib/npc.c modal functions *************** */ int CanCarry(int amount) { return living::CanCarry(amount); } mixed CanGet(object who) { return GetName() + " is a living being!"; } int CanReceive(object ob) { return CanCarry((int)ob->GetMass()); } /* *************** /lib/npc.c lfuns *************** */ static int ContinueHeart() { object env; if( !(env = environment()) ) return 0; if( !sizeof(filter(all_inventory(env), (: living :))) ) return 0; return 1; } /* *************** /lib/npc.c data functions *************** */ mapping SetInventory(mapping mp ) { return (Inventory = mp); } mapping GetInventory() { return copy(Inventory); } varargs string SetRace(string race, mixed extra) { //if(arrayp(extra) && !sizeof(extra)) extra = ({ ({}), ({}), ({}), ({}), ({}) }); race = living::SetRace(race, extra); eventCompleteHeal(GetMaxHealthPoints()); return race; } string SetClass(string cls) { string *skills; int x, i; cls = living::SetClass(cls); x = Level; i = sizeof(skills = GetSkills()); while(i--) { int y; y = (GetSkillClass(skills[i]) || 5); SetSkill(skills[i], (3*x)/y, y); } return cls; } int SetLevel(int x) { string *tmp; int i; Level = x; i = sizeof(tmp = GetSkills()); while(i--) { int y; y = (GetSkillClass(tmp[i]) || 5); SetSkill(tmp[i], (3*x)/y, y); } i = sizeof(tmp = GetStats()); while(i--) { int y; y = (GetStatClass(tmp[i]) || 5); SetStat(tmp[i], ((5-y)*10) + (3*x)/y, y); } eventCompleteHeal(GetMaxHealthPoints()); return Level; } int GetLevel() { return Level; } int SetCustomXP(int i){ if(!i) i = 0; CustomXP = i; return CustomXP; } int GetCustomXP(){ return CustomXP; } int SetHealthPoints(int x) { if( x > GetMaxHealthPoints() ) SetStat("durability", (x-50)/10, GetStatClass("durability")); AddHealthPoints( x - GetHealthPoints() ); return GetHealthPoints(); } varargs int GetMaxHealthPoints(string limb){ if(MaximumHealth) return MaximumHealth; else return living::GetMaxHealthPoints(limb); } int SetMaxHealthPoints(int x) { if(x) MaximumHealth = x; else SetStat("durability", to_int((x-50)/10), GetStatClass("durability")); return GetMaxHealthPoints(); } int SetMagicPoints(int x) { if( x > GetMaxMagicPoints() ) SetStat("intelligence", (x-50)/10, GetStatClass("intelligence")); AddMagicPoints( x - GetMagicPoints() ); return GetMagicPoints(); } int SetMaxMagicPoints(int x) { SetStat("intelligence", (x-50)/10, GetStatClass("intelligence")); return GetMaxMagicPoints(); } float SetStaminaPoints(float x) { if( x > GetMaxStaminaPoints() ) SetStat("agility", to_int((x-50.0)/10.0), GetStatClass("agility")); AddStaminaPoints( x - GetStaminaPoints() ); return to_float(GetStaminaPoints()); } float SetMaxStaminaPoints(float x) { SetStat("agility", (x-50.0)/10.0, GetStatClass("agility")); return GetMaxStaminaPoints(); } varargs void SetCurrency(mixed val, int amount) { if( stringp(val) ) AddCurrency(val, amount); else if( mapp(val) ) { string *currs; int i; i = sizeof(currs = keys(val)); while(i--) AddCurrency(currs[i], val[currs[i]]); } else error("Bad argument 1 to SetCurrency()."); } mixed SetEncounter(mixed val) { return (Encounter = val); } mixed SetAggressive(mixed val){ if(sizeof(Encounter)) return Encounter; else Encounter = 100; } string *AddEncounter(string nom) { if( !stringp(nom) ) error("Bad argument 1 to AddEncounter()\n"); if( Encounter && !pointerp(Encounter) ) return 0; else Encounter += ({ convert_name(nom) }); return Encounter; } string *RemoveEncounter(string nom) { if( !stringp(nom) ) error("Bad argument 1 to RemoveEncounter()\n"); if( Encounter && !pointerp(Encounter) ) return 0; else Encounter -= ({ convert_name(nom) }); return Encounter; } mixed GetEncounter() { return Encounter; } mixed SetDie(mixed val) { return (Die = val); } mixed GetDie() { return Die; } string SetKeyName(string nom) { set_living_name(nom = object::SetKeyName(nom)); return nom; } string GetName() { return object::GetName(); } string GetCapName() { return object::GetCapName(); } string GetShort() { return object::GetShort(); } varargs string GetLong(string str) { mapping counts; string item, what; str = object::GetLong() + "\n"; what = "The "+GetGender()+" "+GetRace(); str += living::GetLong(what); foreach(item in map(all_inventory(), (: (string)$1->GetAffectLong(this_object()) :))) if( item ) str += item + "\n"; if(this_object()->GetAffectLong()) str += this_object()->GetAffectLong(); counts = ([]); foreach(item in map(all_inventory(),(: (string)$1->GetEquippedShort() :))) if( item ) counts[item]++; if( sizeof(counts) ) str += GetCapName() + " is carrying:\n"; foreach(item in keys(counts)) str += capitalize(consolidate(counts[item], item)) + "\n"; return str; } void SetAction(int chance, mixed val) { ActionChance = chance; if( stringp(val) ) val = ({ val }); else if( !functionp(val) && !pointerp(val) ) error("Bad argument 2 to SetAction()\n"); Action = val; } mixed GetAction() { return Action; } void SetCombatAction(int chance, mixed val) { CombatActionChance = chance; if( stringp(val) ) val = ({ val }); else if( !functionp(val) && !pointerp(val) ) error("Bad argument 2 to SetCombatAction()\n"); CombatAction = val; } mixed GetCombatAction() { return CombatAction; } int AddCarriedMass(int x) { return living::AddCarriedMass(x); } mixed *GetCommands() { return commands(); } int SetUnique(int x) { Unique = x; if( Unique ) UNIQUE_D->eventTouchObject(); return Unique; } int GetUnique() { return Unique; } string GetCommandFail() { return "What?"; } int AddEnemy(object ob) { string tmp; if( !living::AddEnemy(ob) ) return 0; if( member_array(tmp = (string)ob->GetKeyName(), EnemyNames) == -1 ) EnemyNames += ({ tmp }); return 1; } string *GetEnemyNames() { return EnemyNames; } int GetRadiantLight(int ambient) { return (object::GetRadiantLight(ambient) + container::GetRadiantLight(ambient)); } int *GetScreen() { return ({ 80, 24 }); } int SetMount(int x) { Mount = x; return Mount; } int GetMount(){ return Mount; } int GetAutoStand(){ return AutoStand; } int SetAutoStand(int i){ AutoStand = i; return AutoStand; }