blob: 034b7cb157d5783ca0561c1f395af4a499ad60ca [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// living/description.c -- description for living objects
4//
5// $Id: description.c 9395 2015-12-08 23:04:38Z Zesstra $
6#pragma strong_types
7#pragma save_types
8#pragma range_check
9#pragma no_clone
MG Mud User88f12472016-06-24 23:31:02 +020010
11inherit "/std/container/description";
12
13#define NEED_PROTOTYPES
14
15#include <living/skills.h>
16#include <living/clothing.h>
17#include <thing/properties.h>
18#include <wizlevels.h>
19#include <new_skills.h>
20#include <properties.h>
21#include <language.h>
22#include <defines.h>
23#include <class.h>
24#include <sys_debug.h>
25
Christian Georg Beckerea699e22017-06-16 23:48:17 +020026#define XL_DURATION "xlduration"
27#define XL_LOOKSTR "xllook"
28#define XL_FUN "xlfun"
29#define XL_ENDMSG "xlende"
30#define XL_ENDFUN "xlendefun"
31#define XL_OBJNAME "xlobjectname"
32
MG Mud User88f12472016-06-24 23:31:02 +020033public string _query_internal_extralook() {
34 mixed xl;
35 int zeit;
36 string res, look="";
37
38 xl=Query(P_INTERNAL_EXTRA_LOOK,F_VALUE);
39 if (!mappingp(xl))
40 return(0);
41
42 foreach(string key, mapping xld: xl) {
Christian Georg Beckerea699e22017-06-16 23:48:17 +020043 if (intp(zeit=xld[XL_DURATION])) {
MG Mud User88f12472016-06-24 23:31:02 +020044 //hat offenbar nen Ablaufdatum
45 if ( (zeit > 0 && zeit < time()) ||
46 (zeit < 0 && abs(zeit) < object_time(ME)) ) {
47 // Zeit abgelaufen oder
48 // negative "Ablaufzeit" und das Objekt ist neuer als die
49 // Eintragzeit, also loeschen und weiter (ja, das geht. ;-) und xld
50 // hat das Eintragsmapping ja noch, weitere Benutzung also moeglich.)
51 m_delete(xl,key);
52 // ggf. Meldung ausgeben
53 if (interactive(ME)) {
Christian Georg Beckerea699e22017-06-16 23:48:17 +020054 if (sizeof(xld[XL_ENDMSG])) {
55 tell_object(ME,xld[XL_ENDMSG]);
MG Mud User88f12472016-06-24 23:31:02 +020056 }
57 //kein einfacher String, aber Objekt+Funktion gegeben?
Christian Georg Beckerea699e22017-06-16 23:48:17 +020058 else if (sizeof(xld[XL_ENDFUN]) && sizeof(xld[XL_OBJNAME]) &&
59 (!catch(res=call_other(xld[XL_OBJNAME],xld[XL_ENDFUN],ME)
MG Mud User88f12472016-06-24 23:31:02 +020060 ;publish))) {
61 if (stringp(res) && sizeof(res))
62 tell_object(ME,res);
63 }
64 }
65 continue;
66 }
67 }
68 // Der Eintrag ist offenbar noch gueltig, Meldung anhaengen, bzw. via
69 // Funktionsaufruf beschaffen.
Christian Georg Beckerea699e22017-06-16 23:48:17 +020070 if (sizeof(xld[XL_LOOKSTR]))
71 look+=xld[XL_LOOKSTR];
72 else if (sizeof(xld[XL_FUN]) && sizeof(xld[XL_OBJNAME])) {
MG Mud User88f12472016-06-24 23:31:02 +020073 closure cl;
Christian Georg Beckerea699e22017-06-16 23:48:17 +020074 if (catch(cl=symbol_function(xld[XL_FUN],xld[XL_OBJNAME]);publish)
MG Mud User88f12472016-06-24 23:31:02 +020075 || !cl) {
76 // wenn Fehler beim Laden/Closure erstellen, dann Eintrag loeschen
77 // -> Annahme, dass dieser Fehler permanent sein wird, z.B. Eintrag
78 // von Clonen
79 m_delete(xl,key);
80 continue;
81 }
82 else if (!catch(res=funcall(cl, ME); publish)) {
83 if (!stringp(res) || !sizeof(res)) {
84 // keinen String oder leeren String gekriegt -> ueberspringen.
85 continue;
86 }
87 else
88 look+=res;
89 }
90 }
91 }
92 // fertig. Wenn look nicht leer ist, zurueckgeben, sonst 0.
93 if (sizeof(look))
94 return(look);
95 else
96 return(0);
97}
98
Christian Georg Beckerea699e22017-06-16 23:48:17 +020099
MG Mud User88f12472016-06-24 23:31:02 +0200100public varargs int AddExtraLook(string look, int duration, string key,
101 string lookende, object ob) {
102 mapping xl;
Arathornb3051452021-05-13 21:13:03 +0200103
MG Mud User88f12472016-06-24 23:31:02 +0200104 if (!stringp(key) || !sizeof(key)) {
105 // Automatisch erzeugen, wenn moeglich
106 if (!objectp(previous_object()) ||
107 !stringp(key=object_name(previous_object())) || !sizeof(key))
Christian Georg Beckerea699e22017-06-16 23:48:17 +0200108 return XL_NOKEY;
MG Mud User88f12472016-06-24 23:31:02 +0200109 }
110
111 if (!stringp(look) || !sizeof(look))
Christian Georg Beckerea699e22017-06-16 23:48:17 +0200112 return XL_INVALIDEXTRALOOK;
MG Mud User88f12472016-06-24 23:31:02 +0200113 if (!intp(duration))
Christian Georg Beckerea699e22017-06-16 23:48:17 +0200114 return XL_INVALIDDURATION;;
MG Mud User88f12472016-06-24 23:31:02 +0200115
116 xl=Query(P_INTERNAL_EXTRA_LOOK,F_VALUE); // dran denken: liefert referenz zurueck
117 if (!mappingp(xl)) {
Christian Georg Beckerea699e22017-06-16 23:48:17 +0200118 Set(P_INTERNAL_EXTRA_LOOK, xl=([]));
MG Mud User88f12472016-06-24 23:31:02 +0200119 }
120
121 // kein Automatisches Ueberschreiben.
122 if (member(xl,key))
Christian Georg Beckerea699e22017-06-16 23:48:17 +0200123 return XL_KEYEXISTS;
MG Mud User88f12472016-06-24 23:31:02 +0200124
125 // neg. Werte: "bis Ende/reboot", abs(duration) == Eintragzeit
126 // 0: "fuer ewig", >0: Zeitdauer in Sekunden
127 if (duration > 0)
128 duration+=time(); // hoffentlich gibt es reichtzeitig 64bit-Ints
129 else if (duration < 0)
130 duration=negate(time());
131 // 0 bleibt, wie es ist.
132
133 if (objectp(ob)) {
134 // Funktionsname und Objektname (als Name, damit es auch noch geht, wenn
135 // das Objekt entladen wurde, Crash/reboot war etc.) speichern
136 // Clone werden auch gespeichert, aber es wird direkt ein harter Fehler
137 // ausgeloest, wenn ein permanenter Xlook fuer einen Clone registriert
138 // werden soll: das kann nicht gehen.
139 if (!duration && clonep(ob))
140 raise_error(sprintf(
141 "AddExtraLook(): Fehlerhaftes Argument <duration>: %d, "
142 "permanente Extralooks durch Clone (%s) nicht registrierbar.\n",
143 duration, object_name(ob)));
144
Christian Georg Beckerea699e22017-06-16 23:48:17 +0200145 xl[key]=([XL_OBJNAME:object_name(ob),
146 XL_FUN: look,
MG Mud User88f12472016-06-24 23:31:02 +0200147 ]);
148 // ggf. Name der Funktion speichern, die bei Ablauf aufgerufen wird.
149 if (stringp(lookende) && sizeof(lookende))
Christian Georg Beckerea699e22017-06-16 23:48:17 +0200150 xl[key][XL_ENDFUN]=lookende;
MG Mud User88f12472016-06-24 23:31:02 +0200151 }
152 else {
153 // Einfacher Eintrag, nur den bearbeiteten String merken. ;-)
Christian Georg Beckerea699e22017-06-16 23:48:17 +0200154 xl[key]=([XL_LOOKSTR: break_string(replace_personal(look,({ME}),1),78,
MG Mud User88f12472016-06-24 23:31:02 +0200155 "",BS_LEAVE_MY_LFS),
156 ]);
157 // ggf. Meldung speichern, die bei Ablauf ausgegeben werden soll.
158 if (stringp(lookende) && sizeof(lookende)) {
Christian Georg Beckerea699e22017-06-16 23:48:17 +0200159 xl[key][XL_ENDMSG]=break_string(replace_personal(lookende,({ME}),1),78,
MG Mud User88f12472016-06-24 23:31:02 +0200160 "",BS_LEAVE_MY_LFS);
161 }
162 }
163 // Endezeit vermerken.
164 if (duration != 0)
Christian Georg Beckerea699e22017-06-16 23:48:17 +0200165 xl[key][XL_DURATION]=duration;
MG Mud User88f12472016-06-24 23:31:02 +0200166
167 // Kein Set noetig, weil Query das Mapping ja als Referenz lieferte.
Christian Georg Beckerea699e22017-06-16 23:48:17 +0200168 return XL_OK;
MG Mud User88f12472016-06-24 23:31:02 +0200169}
170
171public int RemoveExtraLook(string key) {
172 mapping xl;
173 if (!stringp(key) || !sizeof(key)) {
174 // Automatisch erzeugen, wenn moeglich
175 if (!objectp(previous_object()) ||
176 !stringp(key=object_name(previous_object())) || !sizeof(key))
Christian Georg Beckerea699e22017-06-16 23:48:17 +0200177 return XL_NOKEY;
MG Mud User88f12472016-06-24 23:31:02 +0200178 }
179 xl=Query(P_INTERNAL_EXTRA_LOOK,F_VALUE); // dran denken: liefert referenz zurueck
180 if (!mappingp(xl))
Christian Georg Beckerea699e22017-06-16 23:48:17 +0200181 return XL_KEYDOESNOTEXIST;
MG Mud User88f12472016-06-24 23:31:02 +0200182 if (!member(xl,key))
Christian Georg Beckerea699e22017-06-16 23:48:17 +0200183 return XL_KEYDOESNOTEXIST;
MG Mud User88f12472016-06-24 23:31:02 +0200184
185 m_delete(xl,key);
186 // Kein Set noetig, weil Query das Mapping ja als Referenz lieferte.
Christian Georg Beckerea699e22017-06-16 23:48:17 +0200187 return XL_OK;
MG Mud User88f12472016-06-24 23:31:02 +0200188}
189
Arathorn65a38202019-08-28 19:21:26 +0200190// Ist ein bestimmter, nicht bereits abgelaufener Eintrag in den
191// Extralooks vorhanden?
192public int HasExtraLook(string key) {
193 // abgelaufene Extralooks austragen
194 QueryProp(P_INTERNAL_EXTRA_LOOK);
195 return member(Query(P_INTERNAL_EXTRA_LOOK) || ([]), key);
196}
197
MG Mud User88f12472016-06-24 23:31:02 +0200198void create()
199{
200 ::create();
201 Set(P_GENDER, SAVE, F_MODE);
202 // Extralook-Property speichern und vor manueller Aenderung schuetzen
203 // EMs duerfen, die wissen hoffentlich, was sie tun.
204 Set(P_INTERNAL_EXTRA_LOOK, SAVE|PROTECTED, F_MODE_AS);
Bugfix5a9775f2017-02-17 13:48:56 +0100205 SetProp(P_EXTRA_LOOK_OBS,({}));
206 Set(P_EXTRA_LOOK, PROTECTED, F_MODE_AS);
MG Mud User88f12472016-06-24 23:31:02 +0200207 SetProp(P_CLOTHING,({}));
208 AddId("Living");
209}
210
211string condition()
212{
213 int hpnt, max_hpnt, perc;
214
215 hpnt = QueryProp( P_HP );
216 max_hpnt = QueryProp( P_MAX_HP );
217
218 if(max_hpnt>0 && hpnt>0)
219 perc=100*hpnt/max_hpnt;
220
221 switch(perc) {
222 case 0..9:
223 return capitalize(QueryPronoun(WER))+" steht auf der Schwelle des Todes.\n";
224 case 10..19:
225 return capitalize(QueryPronoun(WER))+" braucht dringend einen Arzt.\n";
226 case 20..29:
227 return capitalize(QueryPronoun(WER))+" ist in keiner guten Verfassung.\n";
228 case 30..39:
229 return capitalize(QueryPronoun(WER))+" wankt bereits bedenklich.\n";
230 case 40..49:
231 return capitalize(QueryPronoun(WER))+" macht einen mitgenommenen Eindruck.\n";
232 case 50..59:
233 return capitalize(QueryPronoun(WER))+" sieht nicht mehr taufrisch aus.\n";
234 case 60..69:
235 return capitalize(QueryPronoun(WER))+" ist leicht angeschlagen.\n";
236 case 70..79:
237 return capitalize(QueryPronoun(WER))+" fuehlte sich heute schon besser.\n";
238 case 80..89:
239 return capitalize(QueryPronoun(WER))+" ist schon etwas geschwaecht.\n";
240 }
241 //fall-through
242 return capitalize(QueryPronoun(WER))+" ist absolut fit.\n";
243}
244
245varargs string long() {
Arathornb3051452021-05-13 21:13:03 +0200246 string str;
247 string invl,tmp,exl;
MG Mud User88f12472016-06-24 23:31:02 +0200248 object ob;
249
250 str = process_string( QueryProp(P_LONG) );
251 if(!stringp(str)) str = "";
252
253 str += condition();
254
255 // Extralook
256 if(stringp(tmp = QueryProp(P_EXTRA_LOOK)))
257 str += tmp;
258 if (stringp(tmp = QueryProp(P_INTERNAL_EXTRA_LOOK)))
259 str += tmp;
Bugfix5a9775f2017-02-17 13:48:56 +0100260 foreach(ob : QueryProp(P_EXTRA_LOOK_OBS))
261 {
262 if(objectp(ob) && environment(ob)==ME)
263 {
Bugfix7a9961f2017-02-28 10:37:55 +0100264 exl=ob->QueryProp(P_EXTRA_LOOK);
265 if(stringp(exl) && sizeof(exl))
Bugfix5a9775f2017-02-17 13:48:56 +0100266 {
Zesstrab6a6eab2017-02-27 21:39:29 +0100267 exl=replace_personal(exl, ({ME}), 1);
Bugfix967926a2017-02-27 16:51:10 +0100268 // Ist das letzte Zeichen kein \n, brechen wir um.
269 if(exl[<1]!='\n')
270 {
271 exl=break_string(exl,78);
272 }
Bugfix5a9775f2017-02-17 13:48:56 +0100273 str += exl;
274 }
275 else if(stringp(exl = ob->extra_look()))
276 {
277 str += exl; // TO BE REMOVED
278 }
279 }
280 else
281 {
282 // Bereinigen
283 SetProp(P_EXTRA_LOOK_OBS,QueryProp(P_EXTRA_LOOK_OBS)-({ob}));
284 }
285 }
MG Mud User88f12472016-06-24 23:31:02 +0200286
Arathornb3051452021-05-13 21:13:03 +0200287 if(QueryProp(P_TRANSPARENT))
MG Mud User88f12472016-06-24 23:31:02 +0200288 {
289 invl = make_invlist(PL, all_inventory(ME));
290 if(invl != "")
291 str += capitalize(QueryPronoun(WER))+" traegt bei sich:\n" + invl;
292 }
293 return str;
294}
295
296varargs string name(int casus, int demonst)
297{
298 if( QueryProp( P_INVIS ) )
299 {
300 if( casus == RAW ) return "Jemand";
301 return ({"Jemand","Jemands","Jemandem","Jemanden"})[casus];
302 }
303 if (QueryProp(P_FROG) && casus != RAW )
304 {
305 string s=QueryArticle(casus, 0, 1)+"Frosch";
306 if (casus==WESSEN) s += "es";
307 return s;
308 }
309 return ::name( casus, demonst );
310}
311
312static int _query_gender()
313{
314 if (QueryProp(P_FROG)) return 1;
315 return Query(P_GENDER);
316}
317
318// NPC sollen aus Kompatibilitaetsgruenden auch eine "echte" Rasse haben.
319// Default ist hier die Rasse, man kann aber hiermit auch einen NPC faken,
320// der sich tarnt, indem man P_REAL_RACE per Hand setzt.
321static string _query_real_race()
322{
323 return Query(P_REAL_RACE,F_VALUE)||QueryProp(P_RACE);
324}
325
Zesstra0d1bd1d2019-11-23 10:19:15 +0100326static <string|string*> _set_name(<string|string*> nm )
MG Mud User88f12472016-06-24 23:31:02 +0200327{
328 string lvnam;
Zesstra0d1bd1d2019-11-23 10:19:15 +0100329 if (pointerp(nm))
330 lvnam = nm[WER];
331 else
332 lvnam = nm;
MG Mud User88f12472016-06-24 23:31:02 +0200333 set_living_name(lower_case(lvnam));
Zesstra0d1bd1d2019-11-23 10:19:15 +0100334 return Set(P_NAME, nm, F_VALUE);
MG Mud User88f12472016-06-24 23:31:02 +0200335}
336
337int _query_container()
338{
339 return 0;
340}
341
Zesstra0d1bd1d2019-11-23 10:19:15 +0100342int is_class_member(<string|string*> str) {
MG Mud User88f12472016-06-24 23:31:02 +0200343 // Keine Klasse, keine Mitgliedschaft ...
344 if (!str || (!stringp(str) && !pointerp(str)) || str=="")
345 return 0;
346
347 if (::is_class_member(str))
348 return 1;
349
350 if (stringp(str))
351 str = ({str});
352
353 // Rassen werden als implizite Klassen aufgefasst.
354 // TODO: Pruefen, ob das unbedingt hart-kodiert sein muss.
355 string race = QueryProp(P_RACE);
356 if ( stringp(race) && member( str, lower_case(race) ) > -1 )
357 return 1;
358 else
359 return 0;
360}
361
362mapping _query_material() {
363 mixed res;
364
365 if (mappingp(res=Query(P_MATERIAL)))
366 return res;
367 return ([MAT_MISC_LIVING:100]);
368}
369
Bugfix5a9775f2017-02-17 13:48:56 +0100370public void NotifyInsert(object ob, object oldenv)
371{
Dominik Schäfer5bfa1282017-02-27 11:40:40 +0100372 // Unterstuetzung dyn. Querymethoden (z.B. bei Kleidung, welche nur in
373 // getragenem Zustand nen Extralook hat).
374 if(stringp(ob->QueryProp(P_EXTRA_LOOK))
375 || ob->Query(P_EXTRA_LOOK, F_QUERY_METHOD))
Bugfix5a9775f2017-02-17 13:48:56 +0100376 {
377 SetProp(P_EXTRA_LOOK_OBS,QueryProp(P_EXTRA_LOOK_OBS)+({ob}));
378 }
Dominik Schäfer5bfa1282017-02-27 11:40:40 +0100379
Bugfix5a9775f2017-02-17 13:48:56 +0100380 // Muss leider auch beachtet werden, sollte aber mal raus fliegen ...
381 if(function_exists("extra_look",ob))
382 {
383 SetProp(P_EXTRA_LOOK_OBS,QueryProp(P_EXTRA_LOOK_OBS)+({ob}));
384 catch(raise_error(
385 "Obsolete lfun extra_look() in "+load_name(ob));
386 publish);
387 }
388}
389
390public void NotifyLeave(object ob, object dest)
391{
392 object *els=QueryProp(P_EXTRA_LOOK_OBS);
393 if(member(els,ob)!=-1)
394 {
Zesstrab716ff82017-02-27 10:31:49 +0100395 SetProp(P_EXTRA_LOOK_OBS, els-({ob}) );
Bugfix5a9775f2017-02-17 13:48:56 +0100396 }
397}