Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/std/npc/chat.c b/std/npc/chat.c
new file mode 100644
index 0000000..7f4f89b
--- /dev/null
+++ b/std/npc/chat.c
@@ -0,0 +1,66 @@
+// MorgenGrauen MUDlib
+//
+// npc/chat.c -- Labernde NPCs
+//
+// $Id: chat.c 6801 2008-03-21 23:34:46Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <properties.h>
+#define NEED_PROTOTYPES
+#include <living/combat.h>
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+#define ME this_object()
+
+/*
+ * Some simple chat variables
+ */
+
+/*
+ * heart_beat is called so the monster may chat.
+ */
+void SetChats(int chance, mixed strs) {
+ if (!pointerp(strs))
+ return;
+ SetProp(P_CHAT_CHANCE,chance);
+ SetProp(P_CHATS,strs);
+}
+
+void SetAttackChats(int chance, mixed strs) {
+ if (!pointerp(strs))
+ return;
+ SetProp(P_ACHAT_CHANCE,chance);
+ SetProp(P_ACHATS,strs);
+}
+
+void DoAttackChat() {
+ string* c;
+ if (!ME || !environment(ME))
+ return;
+ if (QueryProp(P_DISABLE_ATTACK)>0)return ;
+ if (random(100) < QueryProp(P_ACHAT_CHANCE))
+ if ((c = QueryProp(P_ACHATS)) && sizeof(c))
+ tell_room(environment(ME),
+ process_string(c[random(sizeof(c))]));
+}
+
+void DoChat() {
+ string *c;
+ if (!ME || !environment(ME))
+ return;
+ if (random(100) < QueryProp(P_CHAT_CHANCE))
+ if ((c = QueryProp(P_CHATS)) && sizeof(c))
+ tell_room(environment(ME),
+ process_string(c[random(sizeof(c))]));
+}
+
+protected void heart_beat()
+{
+ if( InFight() ) DoAttackChat();
+ else DoChat();
+}
diff --git a/std/npc/combat.c b/std/npc/combat.c
new file mode 100644
index 0000000..747714b
--- /dev/null
+++ b/std/npc/combat.c
@@ -0,0 +1,407 @@
+// MorgenGrauen MUDlib
+//
+// npc/combat.c -- NPC-spezifische Kampffunktionen
+//
+// $Id: combat.c 9488 2016-02-19 21:24:04Z Arathorn $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "std/living/combat";
+
+#include <combat.h>
+#include <language.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+
+#define NEED_PROTOTYPES 1
+#include <living/life.h>
+#undef NEED_PROTOTYPES
+
+#define HB_CHECK 7
+#define ME this_object()
+#define STATMASTER "/p/service/rochus/guildstat/master"
+
+nosave int heartbeat, beatcount;
+
+private void catch_up_hbs();
+
+protected void create() {
+ ::create();
+ beatcount=1;
+ heartbeat=1;
+}
+
+protected void create_super() {
+ set_next_reset(-1);
+}
+
+// aktuelles Lebewesen, fuer das dieser NPC gerade taetig ist. Default:
+// Spieler, bei dem er als Helfer-NPC registriert ist.
+public object QueryUser()
+{
+ mixed helperdata = QueryProp(P_HELPER_NPC);
+ if (pointerp(helperdata) && objectp(helperdata[0]))
+ return helperdata[0];
+ return 0;
+}
+
+// ggf. Feinde expiren. Soll das Problem verringern, dass Spieler nach Tagen
+// erst die Meldung kriegen, dass der NPC sie nicht mehr jagt, wenn der HB
+// reaktivert wird.
+void reset() {
+ // ggf. die abgeschalteten HBs nachholen.
+ if (!heartbeat)
+ catch_up_hbs();
+ // ggf. P_ENEMY_DAMAGE zuruecksetzen
+ ResetEnemyDamage();
+}
+
+static void _set_max_hp(int i) {
+ Set(P_MAX_HP,i);
+ SetProp(P_HP,i);
+}
+
+static void _set_max_sp(int i) {
+ Set(P_MAX_SP,i);
+ SetProp(P_SP,i);
+}
+
+
+// Check-Funktion fuer die P_NO_ATTACK-QueryMethod
+static mixed _check_immortality()
+{
+ int t;
+
+ if ( !(t = Query("time_to_mortality")) || time() > t ){
+ // Zeit ist abgelaufen - wieder angreifbar machen
+ Set( P_NO_ATTACK, 0, F_QUERY_METHOD );
+ heartbeat = 1;
+ beatcount = 1;
+ set_heart_beat(1);
+
+ return 0;
+ }
+
+ // der NPC ist noch unangreifbar
+ return break_string( capitalize(name( WER, 1 )) + " versteckt sich hinter "
+ "einem Fehler im Raum-Zeit-Gefuege und entgeht so "
+ "voruebergehend allen Angriffen.", 78 );
+}
+
+
+// wenn der HeartBeat buggt, wird diese Funktion vom Master aufgerufen
+public void make_immortal()
+{
+ // fuer 5 Minuten unangreifbar machen
+ Set( P_NO_ATTACK, #'_check_immortality, F_QUERY_METHOD );
+
+ Set( "time_to_mortality", time() + 300, F_VALUE );
+
+ // damit die Spieler keinen Vorteil durch den Bug haben, heilen
+ heal_self(10000);
+
+ // da nun der Heartbeat abgeschaltet ist und normalerweise erst
+ // reaktiviert wird, sobald jemand nach 5min P_NO_ATTACK abfragt, muss man
+ // aber auf Viecher achten, die immer nen Heartbeat haben wollen. In dem
+ // fall per call_out selber die Prop abfragen.
+ if (QueryProp(P_HB))
+ call_out(#'QueryProp, 301, P_NO_ATTACK);
+}
+
+
+// Damit NPCs gegeneinander weiterkaempfen, auch wenn kein Spieler
+// in der Naehe ist:
+static int _query_hb()
+{
+ // TODO: return InFight() || Query(P_HB, F_VALUE), sobald InFight()
+ // geaendert.
+ return (InFight() || Query(P_HB,F_VALUE)) ? 1 : 0;
+}
+
+
+#define SPELL_TOTALRATE 0
+#define SPELL_DAMAGE 1
+#define SPELL_TEXT_FOR_ENEMY 2
+#define SPELL_TEXT_FOR_OTHERS 3
+#define SPELL_DAMTYPE 4
+#define SPELL_FUNC 5
+#define SPELL_ARG 6
+
+varargs int AddSpell(int rate, int damage, string TextForEnemy,
+ string TextForOthers, string|string* dam_type, string func,
+ int|mapping spellarg) {
+ mixed *spells;
+ int total_rates;
+
+ if (rate<0 || damage<=0 || !stringp(TextForEnemy) ||
+ !stringp(TextForOthers))
+ return 0;
+
+ if (stringp(dam_type))
+ dam_type = ({dam_type});
+ else if (!pointerp(dam_type))
+ dam_type = ({DT_MAGIC});
+
+ // Tatsaechlich ist es immer ein nicht-physischer Angriff, wenn spellarg ein
+ // int ist, weil wenn spellarg==0 ist der Default nicht-physischer Angriff
+ // und bei spellarg!=0 auch. Nur mit einem mapping kann man einen phys.
+ // Angriff erzeugen.
+ if (intp(spellarg))
+ spellarg = ([SP_PHYSICAL_ATTACK: 0]);
+
+ // Falls vorhanden, alte Syntax auf die von replace_personal() anpassen,
+ // die im heart_beat() beim Ausgeben der Meldung verwendet wird.
+ if ( strstr(TextForOthers, "@", 0) != -1 )
+ {
+ // Zeichen nach @WER & Co in runde Klammern einschliessen, damit es als
+ // Sub-Pattern im Ausgabestring wiederholt werden kann. Ansonsten wuerde
+ // es mit ersetzt.
+ TextForOthers = regreplace(TextForOthers, "@WER([^1-9])", "@WER1\\1", 1);
+ TextForOthers = regreplace(TextForOthers, "@WESSEN([^1-9])",
+ "@WESSEN1\\1", 1);
+ TextForOthers = regreplace(TextForOthers, "@WEM([^1-9])", "@WEM1\\1", 1);
+ TextForOthers = regreplace(TextForOthers, "@WEN([^1-9])", "@WEN1\\1", 1);
+ }
+ total_rates=Query("npc:total_rates")+rate;
+ spells=Query(P_SPELLS);
+ if (!pointerp(spells))
+ spells=({});
+ spells+=({({total_rates, damage, TextForEnemy, TextForOthers,
+ dam_type, func, spellarg})});
+ Set(P_SPELLS,spells);
+ Set("npc:total_rates",total_rates);
+ return 1;
+}
+
+int AutoAttack(object ob) {
+ mixed m;
+
+ if (!query_once_interactive(ob))
+ return 0;
+ if (mappingp(m=QueryProp(P_AGGRESSIVE))) {
+ mixed *ind,x,z;
+ float f;
+ int i,n;
+
+ ind=m_indices(m)-({0});n=0;f=0.0;
+ for (i=sizeof(ind)-1;i>=0;i--) {
+ x=ind[i];
+ if ((z=m[x][ob->QueryProp(x)]) || (z=m[x][0])) {
+ f=f+(float)z;
+ n++;
+ }
+ }
+ if (n)
+ m=f/((float)n);
+ else
+ m=m[0];
+ }
+ if (((int)(100*(m+ob->QueryProp(P_AGGRESSIVE))))<=random(100))
+ return 0;
+ if (IS_LEARNER(ob)
+ && (ob->QueryProp(P_INVIS)
+ || ob->QueryProp(P_WANTS_TO_LEARN)))
+ return 0;
+ return 1;
+}
+
+void SpellAttack(object enemy) {
+}
+
+#if 0
+TJ(string s) {
+ object o;
+ if (o=find_player("jof"))
+ tell_object(o,sprintf("%O: %s\n",this_object(),s));
+}
+#else
+#define TJ(x)
+#endif
+
+protected void heart_beat() {
+ int r,i;
+ mixed env,*spells, sinfo;
+ object enemy;
+
+ if ( --beatcount < 0 )
+ beatcount = 0;
+
+ if (!beatcount && !Query(P_HB)) {
+ if (!environment()) {
+ set_heart_beat(0);
+ heartbeat = 0;
+ if( clonep(this_object()) ) remove();
+ return;
+ }
+ if (!QueryProp(P_POISON)) {
+ // Spieler anwesend?
+ env = filter(all_inventory(environment()), #'query_once_interactive);
+ if (!sizeof(env)) {
+ // Nein, HBs abschalten.
+ set_heart_beat(0);
+ heartbeat=0;
+ TJ("OFF\n");
+ beatcount=HB_CHECK;
+ Set("npc:beat_off_num",absolute_hb_count());
+ return;
+ }
+ }
+ }
+ ::heart_beat();
+ if (!ME)
+ return;
+ enemy=SelectEnemy();
+ if (QueryProp(P_AGGRESSIVE)
+ && (!enemy || environment()!=environment(enemy))
+ && !beatcount) {
+ beatcount=HB_CHECK;
+ env=filter(all_inventory(environment()),#'AutoAttack);
+ if (!sizeof(env))
+ return;
+ i=random(sizeof(env));
+ Kill(env[i]);
+ }
+ else if (!beatcount)
+ beatcount=HB_CHECK;
+ if (!objectp(enemy) ||QueryProp(P_DISABLE_ATTACK)>0)
+ return;
+ SpellAttack(enemy);
+
+ if (!pointerp(spells=Query(P_SPELLS))
+ || !sizeof(spells)
+ || !objectp(enemy=SelectEnemy())
+ || environment(enemy)!=environment()
+ || (QueryProp(P_DISABLE_ATTACK)>0)
+ || random(100)>Query(P_SPELLRATE))
+ return;
+ r=random(Query("npc:total_rates"));
+ for (i=sizeof(spells)-1;(i>0 && spells[i-1][SPELL_TOTALRATE]>r);i--)
+ ;
+ string akt_spell_mess = spells[i][SPELL_TEXT_FOR_ENEMY];
+ if ( sizeof(akt_spell_mess) )
+ tell_object(enemy, break_string(akt_spell_mess, 78));
+ akt_spell_mess = spells[i][SPELL_TEXT_FOR_OTHERS];
+ // Nur, wenn ueberhaupt eine Meldung gesetzt wurde, muss diese verarbeitet
+ // werden.
+ if (stringp(akt_spell_mess) && sizeof(akt_spell_mess))
+ {
+ akt_spell_mess = replace_personal(akt_spell_mess, ({enemy}), 1);
+ say(break_string(akt_spell_mess, 78),({enemy, this_object()}));
+ }
+ sinfo = deep_copy(spells[i][SPELL_ARG]);
+ if(!mappingp(sinfo))
+ sinfo=([ SI_MAGIC_TYPE :({ MT_ANGRIFF }) ]);
+ else if(!sinfo[SI_MAGIC_TYPE])
+ sinfo[ SI_MAGIC_TYPE]=({ MT_ANGRIFF });
+ if(!sinfo[SP_PHYSICAL_ATTACK] &&
+ (enemy->SpellDefend(this_object(),sinfo) >
+ random(MAX_ABILITY+QueryProp(P_LEVEL)*50))){
+ tell_object(enemy,"Du wehrst den Spruch ab.\n");
+ say(enemy->Name(WER,1)+" wehrt den Spruch ab.\n",
+ ({ enemy, this_object()}));
+ return ;
+ }
+ int damage = random(spells[i][SPELL_DAMAGE])+1;
+ enemy->Defend(damage, spells[i][SPELL_DAMTYPE],
+ spells[i][SPELL_ARG],
+ this_object());
+
+ // Falls der Gegner (oder wir) im Defend stirbt, hier abbrechen
+ if ( !objectp(ME) || !objectp(enemy)
+ || enemy->QueryProp(P_GHOST) ) return;
+
+ if (spells[i][SPELL_FUNC] && stringp(spells[i][SPELL_FUNC]))
+ catch(call_other(this_object(),
+ spells[i][SPELL_FUNC],
+ enemy,
+ damage,
+ spells[i][SPELL_DAMTYPE]);publish);
+}
+
+// Heartbeats nachholen.
+private void catch_up_hbs() {
+ // gibt es HBs zum nachholen?
+ int beat_off_num = Query("npc:beat_off_num");
+ if (!beat_off_num)
+ return; // nein.
+ // wieviele HBs nachholen?
+ beat_off_num = absolute_hb_count() - beat_off_num;
+
+ if (beat_off_num>0) {
+ // Nicht ausgefuehrtes HEILEN nachholen
+ int rlock=QueryProp(P_NO_REGENERATION);
+ int hp=QueryProp(P_HP);
+ int sp=QueryProp(P_SP);
+ int alc=QueryProp(P_ALCOHOL);
+ if (!(rlock & NO_REG_HP)) {
+ hp+=beat_off_num/HEAL_DELAY+alc/ALCOHOL_DELAY;
+ SetProp(P_HP,hp);
+ }
+ if (!(rlock & NO_REG_SP)) {
+ sp+=beat_off_num/HEAL_DELAY+alc/ALCOHOL_DELAY;
+ SetProp(P_SP,sp);
+ }
+ alc-=beat_off_num/ALCOHOL_DELAY;
+ if ( alc < 0 )
+ alc = 0;
+ SetProp(P_ALCOHOL,alc);
+ int da = QueryProp(P_DISABLE_ATTACK);
+ // Paralysen abbauen
+ if ( da > 0 ) {
+ da -= beat_off_num;
+ if ( da < 0 )
+ da = 0;
+ SetProp( P_DISABLE_ATTACK, da );
+ }
+ // Hunttimes aktualisieren, Feinde expiren
+ update_hunt_times(beat_off_num);
+ if (!heartbeat)
+ // HBs immer noch abgeschaltet, naechstes Mal HBs seit jetzt nachholen.
+ Set("npc:beat_off_num",absolute_hb_count());
+ else
+ // HB laeuft wieder, nix mehr nachholen, bis zur naechsten Abschaltung.
+ Set("npc:beat_off_num",0);
+ }
+}
+
+void init() {
+
+ // ggf. Heartbeats nachholen und wieder einschalten.
+ if (!heartbeat) {
+ set_heart_beat(1);
+ heartbeat=1;
+ catch_up_hbs();
+ }
+
+ if (AutoAttack(this_player()))
+ Kill(this_player());
+}
+
+private nosave closure mod_att_stat;
+
+int Defend(int dam, mixed dam_type, mixed spell, object enemy) {
+ if (objectp(enemy=(enemy||this_player()))
+ && query_once_interactive(enemy)
+ && !IS_LEARNER(enemy)) {
+ if (!objectp(get_type_info(mod_att_stat,2))) {
+ object ma;
+ if (!objectp(ma=find_object(STATMASTER)))
+ return ::Defend(dam,dam_type,spell,enemy);
+ // Keine Statistik wenn Master nicht geladen ist.
+ mod_att_stat=symbol_function("ModifyAttackStat",ma);
+ }
+ funcall(mod_att_stat,
+ enemy->QueryProp(P_GUILD),
+ enemy->QueryProp(P_GUILD_LEVEL),
+ dam,
+ dam_type,
+ spell);
+ }
+
+ return ::Defend(dam,dam_type,spell,enemy);
+}
diff --git a/std/npc/comm.c b/std/npc/comm.c
new file mode 100644
index 0000000..892d8fa
--- /dev/null
+++ b/std/npc/comm.c
@@ -0,0 +1,57 @@
+// MorgenGrauen MUDlib
+//
+// npc/comm.c -- Basiskommunikation fuer NPCs
+//
+// $Id: comm.c 9358 2015-10-22 18:35:04Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/comm";
+
+#include <language.h>
+#include <living/comm.h>
+#define NEED_PROTOTYPES
+#include <thing/description.h>
+
+
+void create() {
+ add_action( "sage", "sag", 1 );
+ add_action( "echo", "echo" );
+ add_action( "emote", "emote" );
+}
+
+int echo( string str ) {
+ say( str + "\n" );
+ return 1;
+}
+
+int sage( string str ) {
+ say( break_string(str, 78, capitalize(name(WER,2))+" sagt: "));
+ return 1;
+}
+
+int emote( string str ) {
+ say( capitalize(name(WER,2))+" "+str+"\n" );
+ return 1;
+}
+
+// zum ueberschreiben - DEPRECATED! USE ReceiveMsg()!
+public void catch_msg(mixed *arr, object obj) {}
+public void catch_tell(string str) {}
+
+// by default, the msg is delivered to catch_tell() for compatibility reasons
+// and otherwise ignored.
+public varargs int ReceiveMsg(string msg, int msg_typ, string msg_action,
+ string msg_prefix, object origin)
+{
+ // compatibility...
+ if (msg_typ & MSG_DONT_WRAP)
+ catch_tell(sprintf("%s%s", msg_prefix||"", msg));
+ else
+ catch_tell(sprintf("%s%s\n", msg_prefix||"", msg));
+ return MSG_DELIVERED;
+}
+
diff --git a/std/npc/info.c b/std/npc/info.c
new file mode 100644
index 0000000..d66ee26
--- /dev/null
+++ b/std/npc/info.c
@@ -0,0 +1,345 @@
+// MorgenGrauen MUDlib
+//
+// npc/info.c -- Behandeln von Fragen an den NPC
+//
+// $Id: info.c 9522 2016-03-01 19:20:10Z Arathorn $
+
+/* Letzte Aenderungen von Wim 8.1.99
+ *
+ * AddInfo( schluessel, antwort [, indent [, [silent [, casebased] ] ] )
+ * Wenn ein Spieler dieses Monster nach "schluessel" fragt, so gib die
+ * Programmierte Antwort aus.
+ * Erweiterung von Wim: ist silent gesetzt, so erfolgt eine "persoenliche"
+ * Antwort. d.h. umstehende Personen bekommen bei silent==1 keinen, bei
+ * stringp(silent), den String ausgegeben, dabei kann er auch die Schluessel-
+ * Worte @WER @WESSEN @WEM @WEN enthalten.
+ * - Ergaenzt um @CAP_WER... fuer zwangs-capitalisierte Namen an Satzanfaengen.
+ * ist bei fragenden NPCs und PCs mit Tarnkappe wichtig! (Silvana)
+ * - Auch in der Antwort des fragenden wird nun ersetzt (Vanion)
+ * Enthaelt casedbased einen Funktionsnamen oder verweist auf eine closure, so
+ * wird die Beantwortung der Frage von dem return-Wert abhaengig gemacht.
+ * Bei 0 wird die Frage normal beantwortet, bei 1 erfolgt die Ausgabe des
+ * unter DEFAULT_NOINFO gespeicherten Textes.
+ * Wird ein String zurueckgegeben, so wird er unter Beachtung von ident an
+ * Stelle der urspruenglichen Information ausgegeben.
+ *
+ * RemoveInfo( schluessel )
+ * Das Monster antwortet nicht mehr auf diesen Schluessel.
+ *
+ * SetProp( P_DEFAULT_INFO, antwort [, indent ] )
+ * Setze die Antwort, die das Monster auf unverstaendliche Fragen geben
+ * soll. (Diese Funktion ist obsolet! Benutze stattdessen
+ * AddInfo( "\ndefault info", antwort [, indent ] );
+ *
+ * GetInfo( [schluessel] )
+ * Wenn Schluessel gesetzt ist, so wird die dazugehoerige Info,
+ * ansonsten werden alle Infos zurueckgegeben.
+ *
+ * Die Antworten sollten wie emote - kommandos aussehen.
+ * Der optionale Indent wird zum Umbrechen von langen Infos benutzt.
+ * (Typischerweise sollte indent="sagt: " sein.)
+ *
+ * In den Infos darf mit process_string gearbeitet werden. Das Ergebnis von
+ * process_string wird dann mit umgebrochen!
+ *
+ *---------------------------------------------------------------------------
+ */
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/description.h>
+#include <thing/properties.h>
+#include <npc.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+#include <config.h>
+#include <exploration.h>
+
+// TODO: langfristig waer hier private schoen.
+nosave mapping infos;
+
+protected void create()
+{
+ // Initialisierung nur wenn noetig, damit beim virtuellen Erben von
+ // /std/npc in npc1 und npc2 dann in npc3 beim npc1::create();
+ // npc2:create(); im zweiten create() die Infos nicht
+ // ueberschrieben/geloescht werden.
+ if (!mappingp(infos)) {
+ infos = ([
+ DEFAULT_INFO:"schaut Dich fragend an.\n";0;
+ "schaut @WEN fragend an.\n";0,
+ DEFAULT_NOINFO:"moechte Dir nicht antworten.\n";0;
+ "verweigert @WEM die Antwort.\n";1
+ ]);
+ }
+}
+
+
+void init() {
+ add_action( "frage", "frag", 1 );
+}
+
+
+static void smart_npc_log(string str)
+{
+ string creat, creat_det;
+
+ if (!stringp(creat=QueryProp(P_LOG_INFO))) {
+ creat = MASTER->creator_file(this_object());
+ if (creat == ROOTID)
+ creat = "ROOT";
+ else if( creat==BACKBONEID )
+ creat="STD";
+ creat_det="report/"+explode(creat, ".")[<1]+"_INFO.rep";
+ creat="report/"+explode(creat, ".")[<1]+".rep";
+ }
+ log_file(creat,
+ sprintf("INFO von %s [%s] (%s):\n%s\n",
+ getuid(this_interactive()),
+ explode(object_name(this_object()),"#")[0],
+ strftime("%d. %b %Y"),
+ str));
+ if (stringp(creat_det) && sizeof(creat_det))
+ log_file(creat_det,
+ sprintf("INFO von %s [%s] (%s):\n%s\n",
+ getuid(this_interactive()),
+ explode(object_name(this_object()),"#")[0],
+ strftime("%d. %b %Y"),
+ str));
+}
+
+public int frage(string str) {
+ string myname, text;
+
+ str=(extern_call()?this_player()->_unparsed_args():str);
+ if( !str || sscanf( str, "%s nach %s", myname, text ) != 2 ) {
+ _notify_fail( "WEN willst Du nach WAS fragen?\n" );
+ return 0;
+ }
+
+ if( !id( lower_case(myname) )
+ || QueryProp(P_INVIS) ) {
+ _notify_fail( "So jemanden findest Du hier nicht.\n" );
+ return 0;
+ }
+ say( capitalize(this_player()->name(WER))+" fragt " +
+ name(WEN,2)+" nach "+capitalize(text)+".\n",
+ this_player() );
+
+ text = lower_case(text);
+ GiveEP(EP_INFO, text);
+
+ return do_frage( text );
+}
+
+static string infoDefaultReplace(string pstring, object pl)
+{
+ pstring=" "+pstring;
+ if (strstr(pstring,"@WER",0) >-1 )
+ pstring= regreplace(pstring,"@WER",pl->name(WER,1),1);
+ if (strstr(pstring,"@WESSEN",0) >-1 )
+ pstring= regreplace(pstring,"@WESSEN",pl->name(WESSEN,1),1);
+ if (strstr(pstring,"@WEM",0) >-1 )
+ pstring= regreplace(pstring,"@WEM",pl->name(WEM,1),1);
+ if (strstr(pstring,"@WEN",0) >-1 )
+ pstring= regreplace(pstring,"@WEN",pl->name(WEN,1),1);
+ if (strstr(pstring,"@CAP_WER",0) >-1 )
+ pstring= regreplace(pstring,"@CAP_WER",pl->Name(WER,1),1);
+ if (strstr(pstring,"@CAP_WESSEN",0) >-1 )
+ pstring= regreplace(pstring,"@CAP_WESSEN",pl->Name(WESSEN,1),1);
+ if (strstr(pstring,"@CAP_WEM",0) >-1 )
+ pstring= regreplace(pstring,"@CAP_WEM",pl->Name(WEM,1),1);
+ if (strstr(pstring,"@CAP_WEN",0) >-1 )
+ pstring= regreplace(pstring,"@CAP_WEN",pl->Name(WEN,1),1);
+
+ return pstring[1..];
+}
+
+static mixed *GetInfoArr(string str)
+{
+ return ({ infos[str, 0], infos[str, 1], infos[str,2], infos[str, 3] });
+}
+
+public int do_frage(string text)
+{
+ string indent,answer;
+ mixed silent, preinfo, noanswer;
+ mixed *info;
+
+ if (stringp(preinfo = QueryProp(P_PRE_INFO)))
+ {
+ // Die message action wird auf "frage" fixiert, damit dies immer das
+ // gleiche ist, egal, mit welchem Verb man in diese Funktion kommt.
+ this_player()->ReceiveMsg(preinfo, MT_LISTEN, "frage",
+ Name(WER,2)+" ",this_object());
+ send_room(environment(this_object()),
+ "ist nicht gewillt, "+this_player()->Name(WEM,2)
+ +" zu antworten.",
+ MT_LISTEN, "frage",
+ Name(WER,2)+" ",
+ ({this_player()}) );
+ return 1;
+ }
+ else
+ {
+ if (intp(preinfo) && preinfo > 0)
+ return 1;
+ }
+
+ info=GetInfoArr(text);
+ if (!info[0])
+ {
+ if( this_interactive() && QueryProp(P_LOG_INFO) )
+ smart_npc_log(text);
+ text = DEFAULT_INFO;
+ info=GetInfoArr(text);
+ }
+
+ if (closurep(info[0]) ) {
+ answer=funcall(info[0]);
+ if( !answer || answer=="") return 1;
+ } else {
+ answer=process_string(info[0]);
+ }
+
+ if (closurep(info[3]) )
+ {
+ noanswer=funcall(info[3]);
+ if ( intp(noanswer) && noanswer > 0)
+ {
+ text = DEFAULT_NOINFO;
+ info = GetInfoArr(text);
+ if (closurep(info[0]) ) {
+ answer=funcall(info[0]);
+ if( !answer || answer=="") return 1;
+ } else {
+ answer=process_string(info[0]);
+ }
+ }
+ else if ( stringp(noanswer) )
+ answer = noanswer;
+ }
+
+ silent=info[2];
+
+ // Replacements gehen auch in der Antwort des NPC. Das gibt den Antworten
+ // eine persoenliche Note, und so teuer is das auch nicht :)
+ answer = infoDefaultReplace(answer, this_player());
+
+ if( indent=info[1] )
+ {
+ if (stringp(silent) || (intp(silent) && silent > 0) )
+ { // Persoenliche Antwort mit indent
+ this_player()->ReceiveMsg(answer, MT_LISTEN|MSG_BS_LEAVE_LFS,
+ "frage",
+ Name(WER,2)+" "+indent,
+ this_object());
+ if (stringp(silent))
+ {
+ silent=infoDefaultReplace(silent, this_player());
+ send_room(environment(), silent, MT_LISTEN, "frage",
+ Name(WER,2)+" ", ({this_player()}));
+ }
+ }
+ else // "normale Antwort" mit Indent
+ {
+ send_room(environment(), answer, MT_LISTEN|MSG_BS_LEAVE_LFS,
+ "frage",
+ Name(WER,2)+" "+indent, ({this_object()}));
+ }
+ }
+ else
+ {
+ if (stringp(silent) || (intp(silent) && silent > 0) )
+ { // Persoenliche Antwort ohne indent
+ this_player()->ReceiveMsg(answer, MT_LISTEN|MSG_DONT_WRAP,
+ "frage",
+ Name(WER,2)+" ",
+ this_object());
+ if (stringp(silent))
+ {
+ silent=infoDefaultReplace(silent, this_player());
+ send_room(environment(), silent, MT_LISTEN, "frage",
+ Name(WER,2)+" ", ({this_player()}) );
+ }
+ }
+ else // "normale Antwort" ohne Indent
+ send_room(environment(), answer, MT_LISTEN|MSG_DONT_WRAP,
+ "frage", Name(WER,2)+" ");
+ }
+ return 1;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * Setzen von Infos
+ *---------------------------------------------------------------------------
+ */
+
+public varargs void AddInfo(mixed key, mixed info, string indent,
+ mixed silent, mixed casebased ) {
+
+ if (stringp(casebased))
+ casebased=symbol_function(casebased,this_object());
+
+ if( pointerp( key ) ) {
+ int i;
+ for ( i=sizeof( key )-1; i>=0; i-- )
+ infos += ([ key[i]: info; indent; silent; casebased ]);
+ }
+ else
+ infos += ([ key: info; indent; silent; casebased ]);
+}
+
+public varargs void AddSpecialInfo(mixed keys, string functionname,
+ string indent, mixed silent, mixed casebased )
+{
+ int i;
+ closure cl;
+
+ if(!(cl=symbol_function(functionname,this_object()))) return;
+ return AddInfo(keys,cl,indent,silent,casebased);
+}
+
+
+public void RemoveInfo( string key )
+{
+ m_delete(infos,key);
+}
+
+static varargs void _set_default_info( mixed info )
+{
+ if (pointerp(info))
+ apply(#'AddInfo/*'*/,DEFAULT_INFO,info);
+ else
+ AddInfo(DEFAULT_INFO,info);
+}
+
+public varargs mixed GetInfo(string str)
+{
+ if (!str) return deep_copy(infos);
+ return infos[str];
+}
+
+
+static mapping _query_npc_info()
+{
+ return deep_copy(infos);
+}
+
+
+static mapping _set_npc_info( mapping map_ldfied )
+{
+ if ( !mappingp(map_ldfied) )
+ return 0;
+
+ return infos = map_ldfied;
+}
+
diff --git a/std/npc/items.c b/std/npc/items.c
new file mode 100644
index 0000000..f0260cf
--- /dev/null
+++ b/std/npc/items.c
@@ -0,0 +1,240 @@
+// MorgenGrauen MUDlib
+//
+// /std/npc/items.c -- Item-Verwaltung fuer NPCs
+//
+// $Id: items.c 8146 2012-10-30 18:18:14Z Zesstra $
+//
+// (c) by Padreic (Padreic@mg.mud.de)
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <living/combat.h>
+#undef NEED_PROTOTYPES
+#include <npc.h>
+#include <properties.h>
+#include <combat.h>
+#include <config.h>
+#include <moving.h>
+#include <sys_debug.h>
+
+protected void create()
+{
+ // dieses flag ist nur bis zum ersten move gesetzt. Nimmt danach also auch
+ Set(NPC_NEEDS_ITEM_INIT, 1); // keinen Speicher mehr in Anspruch.
+ // Set(P_ITEMS, ({})); // keine initialisierung spart Speicher
+}
+
+protected void create_super() {
+ set_next_reset(-1);
+}
+
+protected object* maybe_own_stuff() {
+ object* obs = all_inventory(environment());
+ object haufen = present("\nhaufen "+name(WEM),environment());
+ if( objectp(haufen) ) obs += all_inventory(haufen);
+ object leiche = present("\nleiche "+name(WEM),environment());
+ if( objectp(leiche) ) obs += all_inventory(leiche);
+ return obs;
+}
+
+// clont alle Objekte die geclont werden muessen...
+protected void _clone_items()
+{
+ int i, j, mode, refresh;
+ object ob, *inv1;
+ mixed props, *items;
+ string file, *inv2;
+
+ if (!pointerp(items=QueryProp(P_ITEMS))) return;
+ if (environment()) { // Liste mit filenamen (inv2) erstellen.
+ inv1 = maybe_own_stuff();
+ inv2 = map(inv1, #'load_name);
+ }
+ else inv2=({}); // inv1 wird gar nicht mehr benoetigt
+
+ for (i=sizeof(items)-1; i>=0; i--) {
+ ob = items[i][RITEM_OBJECT];
+ file = items[i][RITEM_FILE];
+ mode = items[i][RITEM_REFRESH];
+ refresh = mode & 0x0000ffff;
+ props = items[i][RITEM_PROPS];
+ if (props && intp(props)) { // unique Gegenstand?
+ ob=find_object(file);
+ if (ob) {
+ // kann ob aufgenommen werden? Wenn das Objekt in inv1 drin ist, ist es
+ // im Env oder in einer Leiche oder einem Haufen.
+ if (member(inv1,ob) > -1) {
+ if (environment()) {
+ if (environment(ob) == environment())
+ tell_room(environment(),
+ capitalize(name(WER))+" hebt "+ob->name(WEN)+" auf.\n");
+ else
+ tell_room(environment(),break_string(
+ capitalize(name(WER))+" nimmt "+ob->name(WEN)+ " aus "
+ +environment(ob)->name(WEM) + ".",78));
+ }
+ ob->remove(); // zerstoeren und neuladen.
+ if (ob) destruct(ob);
+ if (catch(ob=load_object(file);publish))
+ raise_error(sprintf(
+ "_clone_items(): %O does not exist or is not loadable\n", file));
+ ob->move(this_object(), M_NOCHECK);
+ items[i][RITEM_OBJECT]=ob;
+ }
+ else switch( refresh ) {
+ case REFRESH_DESTRUCT:
+ if (environment(ob)) {
+ ob=0; // nicht zuecken, tragen, initialisieren...
+ break;
+ }
+ case REFRESH_ALWAYS:
+ case REFRESH_REMOVE:
+ ob->remove();
+ if (ob) destruct(ob);
+ if (catch(ob=load_object(file);publish))
+ raise_error(sprintf("_clone_items(): "
+ "%O does not exist or is not loadable\n", file));
+ ob->move(this_object(), M_NOCHECK);
+ items[i][RITEM_OBJECT]=ob;
+ break;
+ case REFRESH_NONE:
+ default: ob=0; // nicht zuecken, tragen, initialisieren...
+ }
+ }
+ else {
+ if (catch(ob=load_object(file);publish)) raise_error(sprintf(
+ "_clone_items(): %O does not exist or is not loadable\n", file));
+ if (objectp(ob)) ob->move(this_object(), M_NOCHECK);
+ items[i][RITEM_OBJECT]=ob;
+ }
+ } // if (props ...) // also nicht unique
+ else {
+ switch( refresh ) {
+ case REFRESH_DESTRUCT: // erfuellt auch REFRESH_REMOVE Bedingung...
+ if (ob) {
+ ob=0; // nicht zuecken, tragen, initialisieren...
+ break;
+ }
+ case REFRESH_REMOVE:
+ if (ob) {
+ if(present(ob, this_object())) {
+ ob=0; // nicht zuecken, tragen, initialisieren...
+ break;
+ }
+ else {
+ if ((member(inv2, file))==-1) {
+ ob=0;
+ }
+ }
+ }
+ case REFRESH_NONE: // wird entfernt nach dem ersten clonen!!!
+ case REFRESH_ALWAYS:
+ // schauen ob der Gegenstand im environment liegt.
+ if ((j=member(inv2, file))!=-1) {
+ ob=inv1[j]; // inv1 kann leer sein, aber dann ist inv2 auch leer
+ inv1[j]=0; // wichtig falls mehrere gleiche AddItems
+ inv2[j]=0; // = 0 setzen um Array kopieren zu vermeiden
+ if (environment()) {
+ if (environment() == environment(ob))
+ tell_room(environment(),
+ capitalize(name(WER))+" hebt "+ob->name(WEN)+" auf.\n");
+ else
+ tell_room(environment(),break_string(
+ capitalize(name(WER))+" nimmt "+ob->name(WEN)+" aus "
+ +environment(ob)->name(WEM) + ".",78));
+ }
+ }
+ if (ob) {
+ ob->remove();
+ if (ob) destruct(ob);
+ }
+ if (catch(ob=clone_object(file);publish)) raise_error(sprintf(
+ "_clone_items(): %O does not exist or is not loadable\n", file));
+ ob->move(this_object(), M_NOCHECK);
+ switch( refresh ) {
+ case REFRESH_NONE:
+ items[i] = 0; // Speicher freimachen
+ break;
+ case REFRESH_ALWAYS:
+ items[i][RITEM_OBJECT] = 0; // Objekt "vergessen"
+ break;
+ default:
+ items[i][RITEM_OBJECT] = ob;
+ }
+ break;
+ default: ob=0; // nicht zuecken, tragen, initialisieren...
+ }
+ } // if (props ...) else
+ if (ob) {
+ if (mappingp(props)) walk_mapping(props, symbol_function("SetProp", ob));
+ // Eigentlich will man hier sowas wie command("zuecke schwert")
+ // machen, aber das handling der id's kann nicht sicherstellen,
+ // dass der NPC die richtige Waffe erwischt, deshalb machen wir
+ // das tragen hier von Hand.
+ if (mode & CLONE_WEAR) {
+ if( mode & CLONE_NO_CHECK ) {
+ object *armours;
+ ob->DoUnwear(); // evtl. dem Vorgaenger abnehmen.
+ UseHands(ob, ob->QueryProp(P_NR_HANDS));
+ armours=QueryProp(P_ARMOURS)+({ ob });
+ SetProp(P_ARMOURS, armours);
+ SetProp(P_TOTAL_AC, QueryProp(P_TOTAL_AC)+ob->QueryProp(P_AC));
+ ob->SetProp(P_WORN, this_object());
+ } else {
+ command( "trage \n"+object_name(ob) );
+ }
+ }
+ if (mode & CLONE_WIELD) {
+ if( mode & CLONE_NO_CHECK ) {
+ ob->DoUnwield(); // evtl. dem Vorgaenger abnehmen.
+ UseHands(ob, ob->QueryProp(P_NR_HANDS));
+ SetProp(P_WEAPON, ob);
+ SetProp(P_TOTAL_WC, ob->QueryProp(P_WC));
+ ob->SetProp(P_WIELDED, this_object());
+ } else {
+ command( "zuecke \n"+object_name(ob) );
+ }
+ }
+ } // if (ob)
+ } // for i
+ items-=({ 0 }); // REFRESH_NONEs von nicht unique Objekten
+ if (!sizeof(items)) items=0; // Speicher sparen...
+ SetProp(P_ITEMS, items);
+ Set(NPC_NEEDS_ITEM_INIT, 0);
+}
+
+public varargs object AddItem(mixed filename, int refresh, mixed props)
+{
+ // Aus Array ein Element auswaehlen
+ if (pointerp(filename))
+ filename = filename[random(sizeof(filename))];
+
+ // Kein String? -> Fehler
+ if (!stringp(filename)){
+ raise_error("AddItem: filename or array of filenames expected.\n");
+ } else {
+ // Pfad normieren und eventuell vorhandenes ".c" entfernen
+ filename = MASTER->_get_path(
+ filename[<2..]==".c"?filename=filename[0..<3]
+ : filename,"?");
+ // Property setzen
+ SetProp(P_ITEMS, (QueryProp(P_ITEMS)||({}))+
+ ({ ({ 0, filename, refresh, props }) }));
+
+ if (environment()) _clone_items();
+ }
+ return 0;
+}
+
+void reset()
+// fuer REFRESH_ Objekte...
+{
+ _clone_items();
+}
+
diff --git a/std/npc/moving.c b/std/npc/moving.c
new file mode 100644
index 0000000..f06e0da
--- /dev/null
+++ b/std/npc/moving.c
@@ -0,0 +1,39 @@
+// MorgenGrauen MUDlib
+//
+// living/moving.c -- moving of living objects
+//
+// $Id$
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/moving";
+
+#include <hook.h>
+#define NEED_PROTOTYPES
+#include <moving.h>
+
+//<int|<string|closure>* >* GuardExit(object room, int hookid,
+// <string|closure>* hdata)
+//{
+//}
+
+protected int _reg_exit_hook(object dest, object oldenv)
+{
+ closure cl = symbol_function("GuardExit", this_object());
+ if (cl)
+ {
+ if (oldenv)
+ oldenv->HUnregisterFromHook(H_HOOK_EXIT_USE, cl);
+ return dest->HRegisterModifier(H_HOOK_EXIT_USE, cl);
+ }
+ return 0;
+}
+// Krams nach dem Move machen und nebenbei zum Ueberschreiben.
+protected void NotifyMove(object dest, object oldenv, int method)
+{
+ _reg_exit_hook(dest, oldenv);
+ return ::NotifyMove(dest, oldenv, method);
+}
diff --git a/std/npc/put_and_get.c b/std/npc/put_and_get.c
new file mode 100644
index 0000000..8b72cf4
--- /dev/null
+++ b/std/npc/put_and_get.c
@@ -0,0 +1,36 @@
+// MorgenGrauen MUDlib
+//
+// npc/put_and_get.c.c -- Geben und nehmen fuer NPCs
+//
+// $Id: put_and_get.c 6371 2007-07-17 22:46:50Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "std/living/put_and_get";
+#include <moving.h>
+#include <properties.h>
+
+void give_notify( object obj )
+{
+ mixed* msg;
+ if (pointerp(msg=QueryProp(P_REJECT))) {
+ switch(msg[0]) {
+ case REJECT_GIVE:
+ say(msg[1]);
+ give_obj( obj, this_player() );
+ break;
+ case REJECT_LIGHT_MODIFIER:
+ if (obj->QueryProp(P_LIGHT_MODIFIER) ||
+ obj->QueryProp(P_LIGHT)) break;
+ case REJECT_DROP:
+ say(msg[1]);
+ drop_obj( obj );
+ break;
+ case REJECT_KEEP:
+ default: say(msg[1]); /* keep it */
+ }
+ }
+}
diff --git a/std/npc/sequencer.c b/std/npc/sequencer.c
new file mode 100644
index 0000000..50c85e3
--- /dev/null
+++ b/std/npc/sequencer.c
@@ -0,0 +1,125 @@
+// MorgenGrauen MUDlib
+//
+// npc/sequencer.c -- Scripte und Trigger fuer NPCs :)
+//
+// $Id: sequencer.c 7526 2010-04-02 00:01:26Z Arathorn $
+
+// seq2.c
+// Version 2.0 des Sequencers von Don Rumata 28.06.93
+//
+// Sinn:
+// Bereitstellung von Funktionen, ddamit ein npc mehrere Befehle
+// bei Eintritt eines bestimmten Ereignisses nacheinander
+// automatisch durchfuehrt.
+//
+// Ereignisse:
+// TellEvent: Es wird etwas in dem Raum, in dem der npc sich
+// befindet, gesagt.
+// GiveEvent: Es wird dem npc etwas gegeben.
+//
+// Programm:
+// Ein Programm ist eine Liste von Befehlen.
+// Jeder Befehl ist eine Liste, bestehend aus einem Kommando
+// und einer Zahl.
+// Das Kommendo wird aehnlich der Befehle, die ein Spieler ein-
+// gibt ausgefuehrt.
+// Vorsicht: NPCs koennen nur einen Teil der Befehle, die ein
+// Spieler kann, dafuer aber immer 'echo' und 'emote'.
+// Die Zahl gibt die Anzahl der Sekunden an, in der der naechste
+// Befehl ausgefuehrt wird.
+//
+// Funktionen:
+// RegisterTell( funktion, programm )
+// Wenn dem npc etwas gesagt wird, so wird die gesagte Meldung
+// an die Funktion uebergeben. Gibt die Funktionen nicht 0
+// zurueck, wird das Programm gestartet.
+// RegisterGive( funktion, programm )
+// Wird dem npc etwas gegeben, so wird das Objekt an die
+// Funktion uebergeben. Gibt die Funktion nicht 0 zurueck, so
+// wird das Programm gestartet.
+// Load( programm )
+// Starte das angegebene Programm.
+//
+// give_notify() gibt eine 1 zurueck, wenn das Objekt akzeptiert
+// wurde. (Es muss - falls gewuenscht - dann von Hand zuruech-
+// gegeben werden. (give_obj(ob,this_player())) in der Funk.)
+// mittels add_action() kann man im create() ds NPCs eigene
+// Verben fuer den NPC einfuehren.
+//
+// Es kann immer nur eine Funktion (egal ob Tell oder Give) angemeldet
+// sein. Es kann immer nur ein Programm gleichzeitig laufen.
+//
+// Ideen und Bugreports an Rumata
+
+string givefun, tellfun;
+mixed *program;
+int isRunning;
+
+// zur sicherheit nicht von aussen aufrufbar !!!!
+static string RegisterGive( string fun, mixed prog )
+{
+ if( isRunning ) return 0;
+ program = prog;
+ tellfun = 0;
+ return givefun = fun;
+}
+
+// zur sicherheit nicht von aussen aufrufbar !!!!
+static string RegisterTell( string fun, mixed prog )
+{
+ if( isRunning ) return 0;
+ program = prog;
+ givefun = 0;
+ return tellfun = fun;
+}
+
+// zur sicherheit nicht von aussen aufrufbar !!!!
+static void Start()
+{
+ if( isRunning || !objectp(this_object())) return;
+ isRunning = 1;
+ call_out( "nextStep", 0, 0 );
+}
+
+static void Load( mixed prog )
+{
+ if( isRunning ) return;
+ program = prog;
+ Start();
+}
+
+public void catch_tell( string str )
+{
+ if( isRunning || previous_object()==this_object()) return;
+ if( stringp(tellfun) && call_other( this_object(), tellfun, str ) )
+ Start();
+}
+
+int give_notify( object ob )
+{
+ if( isRunning ) return 0;
+ if( stringp(givefun) && call_other( this_object(), givefun, ob ) )
+ {
+ Start();
+ return 1;
+ }
+ return 0;
+}
+
+static void nextStep( int line )
+{
+ if( !isRunning ) return;
+ if( line >= sizeof( program ) )
+ {
+ isRunning = 0;
+ return;
+ }
+ command( process_string( program[line][0] ) );
+ if( !isRunning || !objectp(this_object()) ) return;
+ call_out( "nextStep", program[line][1], line+1 );
+}
+
+mixed QueryProg()
+{
+ return program[0..];
+}