blob: af3bd940f3dd777f13167da19fe70b8abcb12efb [file] [log] [blame]
// MorgenGrauen MUDlib
//
// living/description.c -- description for living objects
//
// $Id: description.c 9395 2015-12-08 23:04:38Z Zesstra $
#pragma strong_types
#pragma save_types
#pragma range_check
#pragma no_clone
#pragma pedantic
inherit "/std/container/description";
#define NEED_PROTOTYPES
#include <living/skills.h>
#include <living/clothing.h>
#include <thing/properties.h>
#include <wizlevels.h>
#include <new_skills.h>
#include <properties.h>
#include <language.h>
#include <defines.h>
#include <class.h>
#include <sys_debug.h>
#define XL_DURATION "xlduration"
#define XL_LOOKSTR "xllook"
#define XL_FUN "xlfun"
#define XL_ENDMSG "xlende"
#define XL_ENDFUN "xlendefun"
#define XL_OBJNAME "xlobjectname"
public string _query_internal_extralook() {
mixed xl;
int zeit;
string res, look="";
xl=Query(P_INTERNAL_EXTRA_LOOK,F_VALUE);
if (!mappingp(xl))
return(0);
foreach(string key, mapping xld: xl) {
if (intp(zeit=xld[XL_DURATION])) {
//hat offenbar nen Ablaufdatum
if ( (zeit > 0 && zeit < time()) ||
(zeit < 0 && abs(zeit) < object_time(ME)) ) {
// Zeit abgelaufen oder
// negative "Ablaufzeit" und das Objekt ist neuer als die
// Eintragzeit, also loeschen und weiter (ja, das geht. ;-) und xld
// hat das Eintragsmapping ja noch, weitere Benutzung also moeglich.)
m_delete(xl,key);
// ggf. Meldung ausgeben
if (interactive(ME)) {
if (sizeof(xld[XL_ENDMSG])) {
tell_object(ME,xld[XL_ENDMSG]);
}
//kein einfacher String, aber Objekt+Funktion gegeben?
else if (sizeof(xld[XL_ENDFUN]) && sizeof(xld[XL_OBJNAME]) &&
(!catch(res=call_other(xld[XL_OBJNAME],xld[XL_ENDFUN],ME)
;publish))) {
if (stringp(res) && sizeof(res))
tell_object(ME,res);
}
}
continue;
}
}
// Der Eintrag ist offenbar noch gueltig, Meldung anhaengen, bzw. via
// Funktionsaufruf beschaffen.
if (sizeof(xld[XL_LOOKSTR]))
look+=xld[XL_LOOKSTR];
else if (sizeof(xld[XL_FUN]) && sizeof(xld[XL_OBJNAME])) {
closure cl;
if (catch(cl=symbol_function(xld[XL_FUN],xld[XL_OBJNAME]);publish)
|| !cl) {
// wenn Fehler beim Laden/Closure erstellen, dann Eintrag loeschen
// -> Annahme, dass dieser Fehler permanent sein wird, z.B. Eintrag
// von Clonen
m_delete(xl,key);
continue;
}
else if (!catch(res=funcall(cl, ME); publish)) {
if (!stringp(res) || !sizeof(res)) {
// keinen String oder leeren String gekriegt -> ueberspringen.
continue;
}
else
look+=res;
}
}
}
// fertig. Wenn look nicht leer ist, zurueckgeben, sonst 0.
if (sizeof(look))
return(look);
else
return(0);
}
public varargs int AddExtraLook(string look, int duration, string key,
string lookende, object ob) {
mapping xl;
string oname;
if (!stringp(key) || !sizeof(key)) {
// Automatisch erzeugen, wenn moeglich
if (!objectp(previous_object()) ||
!stringp(key=object_name(previous_object())) || !sizeof(key))
return XL_NOKEY;
}
if (!stringp(look) || !sizeof(look))
return XL_INVALIDEXTRALOOK;
if (!intp(duration))
return XL_INVALIDDURATION;;
xl=Query(P_INTERNAL_EXTRA_LOOK,F_VALUE); // dran denken: liefert referenz zurueck
if (!mappingp(xl)) {
Set(P_INTERNAL_EXTRA_LOOK, xl=([]));
}
// kein Automatisches Ueberschreiben.
if (member(xl,key))
return XL_KEYEXISTS;
// neg. Werte: "bis Ende/reboot", abs(duration) == Eintragzeit
// 0: "fuer ewig", >0: Zeitdauer in Sekunden
if (duration > 0)
duration+=time(); // hoffentlich gibt es reichtzeitig 64bit-Ints
else if (duration < 0)
duration=negate(time());
// 0 bleibt, wie es ist.
if (objectp(ob)) {
// Funktionsname und Objektname (als Name, damit es auch noch geht, wenn
// das Objekt entladen wurde, Crash/reboot war etc.) speichern
// Clone werden auch gespeichert, aber es wird direkt ein harter Fehler
// ausgeloest, wenn ein permanenter Xlook fuer einen Clone registriert
// werden soll: das kann nicht gehen.
if (!duration && clonep(ob))
raise_error(sprintf(
"AddExtraLook(): Fehlerhaftes Argument <duration>: %d, "
"permanente Extralooks durch Clone (%s) nicht registrierbar.\n",
duration, object_name(ob)));
xl[key]=([XL_OBJNAME:object_name(ob),
XL_FUN: look,
]);
// ggf. Name der Funktion speichern, die bei Ablauf aufgerufen wird.
if (stringp(lookende) && sizeof(lookende))
xl[key][XL_ENDFUN]=lookende;
}
else {
// Einfacher Eintrag, nur den bearbeiteten String merken. ;-)
xl[key]=([XL_LOOKSTR: break_string(replace_personal(look,({ME}),1),78,
"",BS_LEAVE_MY_LFS),
]);
// ggf. Meldung speichern, die bei Ablauf ausgegeben werden soll.
if (stringp(lookende) && sizeof(lookende)) {
xl[key][XL_ENDMSG]=break_string(replace_personal(lookende,({ME}),1),78,
"",BS_LEAVE_MY_LFS);
}
}
// Endezeit vermerken.
if (duration != 0)
xl[key][XL_DURATION]=duration;
// Kein Set noetig, weil Query das Mapping ja als Referenz lieferte.
return XL_OK;
}
public int RemoveExtraLook(string key) {
mapping xl;
if (!stringp(key) || !sizeof(key)) {
// Automatisch erzeugen, wenn moeglich
if (!objectp(previous_object()) ||
!stringp(key=object_name(previous_object())) || !sizeof(key))
return XL_NOKEY;
}
xl=Query(P_INTERNAL_EXTRA_LOOK,F_VALUE); // dran denken: liefert referenz zurueck
if (!mappingp(xl))
return XL_KEYDOESNOTEXIST;
if (!member(xl,key))
return XL_KEYDOESNOTEXIST;
m_delete(xl,key);
// Kein Set noetig, weil Query das Mapping ja als Referenz lieferte.
return XL_OK;
}
void create()
{
::create();
Set(P_GENDER, SAVE, F_MODE);
// Extralook-Property speichern und vor manueller Aenderung schuetzen
// EMs duerfen, die wissen hoffentlich, was sie tun.
Set(P_INTERNAL_EXTRA_LOOK, SAVE|PROTECTED, F_MODE_AS);
SetProp(P_EXTRA_LOOK_OBS,({}));
Set(P_EXTRA_LOOK, PROTECTED, F_MODE_AS);
SetProp(P_CLOTHING,({}));
AddId("Living");
}
string condition()
{
int hpnt, max_hpnt, perc;
hpnt = QueryProp( P_HP );
max_hpnt = QueryProp( P_MAX_HP );
if(max_hpnt>0 && hpnt>0)
perc=100*hpnt/max_hpnt;
switch(perc) {
case 0..9:
return capitalize(QueryPronoun(WER))+" steht auf der Schwelle des Todes.\n";
case 10..19:
return capitalize(QueryPronoun(WER))+" braucht dringend einen Arzt.\n";
case 20..29:
return capitalize(QueryPronoun(WER))+" ist in keiner guten Verfassung.\n";
case 30..39:
return capitalize(QueryPronoun(WER))+" wankt bereits bedenklich.\n";
case 40..49:
return capitalize(QueryPronoun(WER))+" macht einen mitgenommenen Eindruck.\n";
case 50..59:
return capitalize(QueryPronoun(WER))+" sieht nicht mehr taufrisch aus.\n";
case 60..69:
return capitalize(QueryPronoun(WER))+" ist leicht angeschlagen.\n";
case 70..79:
return capitalize(QueryPronoun(WER))+" fuehlte sich heute schon besser.\n";
case 80..89:
return capitalize(QueryPronoun(WER))+" ist schon etwas geschwaecht.\n";
}
//fall-through
return capitalize(QueryPronoun(WER))+" ist absolut fit.\n";
}
varargs string long() {
string str, cap_pronoun;
string descr, invl,tmp,exl;
int hpnt, max_hpnt;
mixed filter_ldfied;
object ob;
str = process_string( QueryProp(P_LONG) );
if(!stringp(str)) str = "";
str += condition();
// Extralook
if(stringp(tmp = QueryProp(P_EXTRA_LOOK)))
str += tmp;
if (stringp(tmp = QueryProp(P_INTERNAL_EXTRA_LOOK)))
str += tmp;
foreach(ob : QueryProp(P_EXTRA_LOOK_OBS))
{
if(objectp(ob) && environment(ob)==ME)
{
exl=ob->QueryProp(P_EXTRA_LOOK);
if(stringp(exl) && sizeof(exl))
{
exl=replace_personal(exl, ({ME}), 1);
// Ist das letzte Zeichen kein \n, brechen wir um.
if(exl[<1]!='\n')
{
exl=break_string(exl,78);
}
str += exl;
}
else if(stringp(exl = ob->extra_look()))
{
str += exl; // TO BE REMOVED
}
}
else
{
// Bereinigen
SetProp(P_EXTRA_LOOK_OBS,QueryProp(P_EXTRA_LOOK_OBS)-({ob}));
}
}
if(filter_ldfied = QueryProp(P_TRANSPARENT))
{
invl = make_invlist(PL, all_inventory(ME));
if(invl != "")
str += capitalize(QueryPronoun(WER))+" traegt bei sich:\n" + invl;
}
return str;
}
varargs string name(int casus, int demonst)
{
if( QueryProp( P_INVIS ) )
{
if( casus == RAW ) return "Jemand";
return ({"Jemand","Jemands","Jemandem","Jemanden"})[casus];
}
if (QueryProp(P_FROG) && casus != RAW )
{
string s=QueryArticle(casus, 0, 1)+"Frosch";
if (casus==WESSEN) s += "es";
return s;
}
return ::name( casus, demonst );
}
static int _query_gender()
{
if (QueryProp(P_FROG)) return 1;
return Query(P_GENDER);
}
// NPC sollen aus Kompatibilitaetsgruenden auch eine "echte" Rasse haben.
// Default ist hier die Rasse, man kann aber hiermit auch einen NPC faken,
// der sich tarnt, indem man P_REAL_RACE per Hand setzt.
static string _query_real_race()
{
return Query(P_REAL_RACE,F_VALUE)||QueryProp(P_RACE);
}
static mixed _set_name(mixed nm )
{
string lvnam;
lvnam = nm;
if(pointerp(nm)) lvnam = nm[0];
set_living_name(lower_case(lvnam));
return Set(P_NAME, nm);
}
int _query_container()
{
return 0;
}
int is_class_member(mixed str) {
// Keine Klasse, keine Mitgliedschaft ...
if (!str || (!stringp(str) && !pointerp(str)) || str=="")
return 0;
if (::is_class_member(str))
return 1;
if (stringp(str))
str = ({str});
// Rassen werden als implizite Klassen aufgefasst.
// TODO: Pruefen, ob das unbedingt hart-kodiert sein muss.
string race = QueryProp(P_RACE);
if ( stringp(race) && member( str, lower_case(race) ) > -1 )
return 1;
else
return 0;
}
mapping _query_material() {
mixed res;
if (mappingp(res=Query(P_MATERIAL)))
return res;
return ([MAT_MISC_LIVING:100]);
}
public void NotifyInsert(object ob, object oldenv)
{
// Unterstuetzung dyn. Querymethoden (z.B. bei Kleidung, welche nur in
// getragenem Zustand nen Extralook hat).
if(stringp(ob->QueryProp(P_EXTRA_LOOK))
|| ob->Query(P_EXTRA_LOOK, F_QUERY_METHOD))
{
SetProp(P_EXTRA_LOOK_OBS,QueryProp(P_EXTRA_LOOK_OBS)+({ob}));
}
// Muss leider auch beachtet werden, sollte aber mal raus fliegen ...
if(function_exists("extra_look",ob))
{
SetProp(P_EXTRA_LOOK_OBS,QueryProp(P_EXTRA_LOOK_OBS)+({ob}));
catch(raise_error(
"Obsolete lfun extra_look() in "+load_name(ob));
publish);
}
}
public void NotifyLeave(object ob, object dest)
{
object *els=QueryProp(P_EXTRA_LOOK_OBS);
if(member(els,ob)!=-1)
{
SetProp(P_EXTRA_LOOK_OBS, els-({ob}) );
}
}