blob: b5db0d852543ccc8a7ff5edb6f91a9c6dcb12516 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// thing/description.c -- description handling for standard objects
4//
5// $Id: description.c 9561 2016-05-25 19:33:22Z Zesstra $
6
7#pragma strict_types
8#pragma save_types
9#pragma range_check
10#pragma no_clone
11#pragma pedantic
12
13#include <tls.h>
14#include <thing/description.h>
15#include <thing/material.h>
16#include <thing/lighttypes.h>
17#include <exploration.h> // wegen EPMASTER
18#include <class.h>
19
20#define NEED_PROTOTYPES
21#include <thing/properties.h>
22#include <thing/language.h>
23
24#undef NEED_PROTOTYPES
25#include <properties.h>
26
27// Variable um den FP abzuspeichern
28private nosave mixed *explore;
29
30// Prototypen
31public string short();
32public varargs string long(int mode);
33
34// #####################
35//######################## System-Funktionen ############################
36// #####################
37
38// Objekt erzeugen
39protected void create()
40{
41 string poid, tpid;
42 object tp;
43
44 SetProp( P_NAME, "Ding" );
45 SetProp( P_SHORT, "Nichts besonderes" );
46 SetProp( P_LONG, 0 );
47
48 Set( P_ADJECTIVES, ({}) );
49 Set( P_NAME_ADJ, ({}) );
50 Set( P_IDS, ({}) );
51 Set( P_CLASS, ({}) );
52
53 Set( P_READ_DETAILS, ([]), F_VALUE);
54 Set( P_READ_DETAILS, SECURED|NOSETMETHOD, F_MODE_AS);
55
56 Set( P_DETAILS, ([]), F_VALUE);
57 Set( P_DETAILS, SECURED|NOSETMETHOD, F_MODE_AS );
58
59 Set( P_SMELLS, ([]), F_VALUE);
60 Set( P_SMELLS, SECURED|NOSETMETHOD, F_MODE_AS );
61
62 Set( P_SOUNDS, ([]), F_VALUE);
63 Set( P_SOUNDS, SECURED|NOSETMETHOD, F_MODE_AS );
64
65 Set( P_TOUCH_DETAILS, ([]), F_VALUE);
66 Set( P_TOUCH_DETAILS, SECURED|NOSETMETHOD, F_MODE_AS );
67
68 // Aenderungen an dieser Prop sind tabu.
69 Set( P_CLONE_TIME, NOSETMETHOD|SECURED, F_MODE_AS );
70
Zesstra789b1d82018-02-12 16:22:22 +010071 // Wenn das Objekt bereits ein P_CLONER hat, wurde create() zweimal gerufen.
72 // Dies ist ein Fehler und vermutlich ist das Objekt jetzt kaputt. In jedem
73 // Fall wird das alte P_CLONER nicht ueberschrieben.
74 if (Query(P_CLONER, F_MODE))
MG Mud User88f12472016-06-24 23:31:02 +020075 {
Zesstra789b1d82018-02-12 16:22:22 +010076 catch(raise_error("create() wurde ein zweites Mal gerufen. Dieses "
77 "Objekt ist nun vermutlich kaputt.\n"); publish);
MG Mud User88f12472016-06-24 23:31:02 +020078 }
79 else
MG Mud User88f12472016-06-24 23:31:02 +020080 {
Zesstra789b1d82018-02-12 16:22:22 +010081 // Id des Cloners und des Besitzers kommen nach P_CLONER
82 if (objectp( tp=this_interactive()||this_player() ))
83 {
84 tpid=geteuid(tp);
85 if (!(tpid=geteuid(tp))) tpid=getuid(tp);
86 }
87 else
88 tpid="UNKNOWN";
MG Mud User88f12472016-06-24 23:31:02 +020089
Zesstra789b1d82018-02-12 16:22:22 +010090 if (previous_object())
91 {
92 if (!(poid = geteuid(previous_object())))
93 poid = getuid(previous_object());
94 }
95 else
96 poid="UNKNOWN";
97
98 Set( P_CLONER, (poid != tpid ? poid+":"+tpid: tpid) );
99 Set( P_CLONER, NOSETMETHOD|SECURED, F_MODE_AS );
100 }
MG Mud User88f12472016-06-24 23:31:02 +0200101
102 // Gibt es FPs ?
103 explore = (mixed *)EPMASTER->QueryExplore();
104
105 return;
106}
107
108protected void create_super() {
109 set_next_reset(-1);
110}
111
112// ##################
113//######################### Forscherpunkte ##############################
114// ##################
115
116// FP vergeben
117static void GiveEP( int type, string key )
118{
119 //Abbruch, wenn vergebendes Objekt schon zerstoert ist. ACHTUNG: Auch
120 //diese Abfrage wuerde kein FP vergeben werden, wenn sich das Objekt im
121 //vergebenden Kommando zerstoert, da der Driver call_other() von zerstoerten
122 //Objekten ignoriert!
123 if (!objectp(this_object())) return;
124 if (this_player()) this_player()->countCmds( type, key );
125
126 if (explore&&!extern_call()&&
127 (explore[0] == type) && (member(explore[1], key) >= 0) )
128 EPMASTER->GiveExplorationPoint(key);
129 return;
130}
131
132// Manche Objekte koennen mit rename_object einen neuen Filenamen bekommen.
133// Danach sollte der EPMASTER neu nach den Details befragt werden.
134void __reload_explore()
135{
136 explore = (mixed *)EPMASTER->QueryExplore();
137 return;
138}
139
140// #################
141//########################## ID-Management ##############################
142// #################
143
144// Gibt eine ID zurueck, die den Ladenamen, die aktuelle Kurzbeschreibung und
145// die aktuelle Langbeschreibung beruecksichtigt. Diese ID ist ein Hashwert.
146public string description_id() {
147 return hash(TLS_HASH_MD5, load_name() + short() + long());
148}
149
150/* Ids muessen uebergeben werden, da unit machmal mit plural-Ids, */
151/* also anderen als den normalen arbeiten muss. */
152int match_item( string str, string *ids )
153{
154 string *obj,*ads;
155 int len, i;
156
157 // Parameter falsch? Passt nicht ...
158 if(!str) return 0;
159 if(!pointerp(ids)) return 0;
160 if(!sizeof(ids)) return 0;
161
162 // Ist schon so dabei? Na Klasse :-)
163 if(member(ids,str)>-1) return 1;
164
165 // Keine Adjektive vorhanden ... passt nicht.
166 if (!(ads=QueryProp(P_ADJECTIVES))) return 0;
167 if (!sizeof(ads)) return 0;
168
169 // Nur ein Wort? Dann passt es nicht
170 obj=explode(str," ");
171 if (!(len=sizeof(obj)-1)) return 0;
172
173 // Adjektive stehen am Anfang. Sobald es nicht mehr passt,
174 // muss es das Objektiv sein.
175 while(i<len&&member(ads,obj[i])>-1) i++;
176
177 return (member(ids,implode(obj[i..len]," "))>-1);
178}
179
180// Wird vom Gamedriver aufgerufen (present)
181// Hat dieser Gegenstand die ID str?
182// lvl wird ignoriert
183varargs int id( string str, int lvl )
184{
185 string str2, tmp;
186 int count;
187 string|string* ids;
188
189 // Kein Argument? Dann passt es nicht ...
190 if (!stringp(str)) return 0;
191
192 // Keine IDs? Auch nicht gut ...
193 if (!pointerp(ids=QueryProp(P_IDS))) return 0;
194 if (!sizeof(ids)) return 0;
195
196 ids += ({ ("\n" + object_name()),
197 ("\n" + explode(object_name(),"#")[0]) });
198
199 // Id passt? Alles klar :-)
200 if (match_item( str, ids )) return 1;
201
202 // Die id hat eine Zahl drin. Wenn Zahl die Rohid passt,
203 // dann gucken, ob man selber das nte Element ist.
204 if (sscanf( str, "%s %d%s", str2, count, tmp)<2) return 0;
205 if (count<1) return 0;
206 if (sizeof(tmp)) return 0;
207 if (!match_item( str2, ids )) return 0;
208 if (!environment()) return 0;
209 return present(str2,count,environment())==this_object();
210}
211
212// Gleich eine ganze Liste von ids testen
213int match_ids(string *list)
214{
215 string *ids;
216
217 // Ungueltige Parameter? Weg hier ...
218 if (!pointerp(list)) return 0;
219 if (!pointerp(ids=QueryProp(P_IDS))) return 0;
220
221 ids += ({ ("\n" + object_name()),
222 ("\n" + explode(object_name(),"#")[0]) });
223
224 return sizeof( list & ids );
225}
226
227// ID hinzufuegen
228void AddId( string|string* str )
229{
230 if (stringp(str)) str = ({ str });
231 if (pointerp(str))
232 // Doppelte eliminieren
233 Set( P_IDS, Query(P_IDS, F_VALUE)-str+str, F_VALUE);
234 return;
235}
236
237// ID entfernen
238void RemoveId(string|string* str)
239{
240 if (stringp(str)) str = ({ str });
241 if (pointerp(str))
242 Set(P_IDS,Query(P_IDS, F_VALUE)-str, F_VALUE);
243 return;
244}
245
246// Alle Ids auf einmal setzen
247static string* _set_ids( string* ids )
248{
249 Set( P_IDS,({}));
250 AddId(ids);
251 return Query(P_IDS, F_VALUE);
252}
253
254// Adjektiv hinzufuegen
255void AddAdjective(string|string* str)
256{
257 if (stringp(str)) str = ({ str });
258 if (pointerp(str))
259 // Doppelte eliminieren
260 Set( P_ADJECTIVES, Query(P_ADJECTIVES, F_VALUE)-str+str, F_VALUE );
261 return;
262}
263
264// Adjektiv entfernen
265void RemoveAdjective(string|string* str)
266{
267 if (stringp(str)) str = ({ str });
268 if (pointerp(str))
269 Set( P_ADJECTIVES, Query(P_ADJECTIVES, F_VALUE) - str, F_VALUE );
270 return;
271}
272
273// Alle Adjektive auf einmal setzen
274static string* _set_adjectives(string* adjectives)
275{
276 Set( P_ADJECTIVES,({}), F_VALUE);
277 AddAdjective(adjectives);
278 return Query(P_ADJECTIVES, F_VALUE);
279}
280
281
282// ################
283//########################## Namensgebung ###############################
284// ################
285
286// Im Fall von mehreren Adjektiven muessen diese mit komma
287// zusamengebaut werden, dazu muss ich das leerzeichen aber erstmal
288// abschneiden und so weiter ...
Bugfixfd83c042017-02-18 11:45:35 +0100289private string depointer_adj( <string|string*>* adj, int casus, int demon ) {
MG Mud User88f12472016-06-24 23:31:02 +0200290 string msg;
291 int start;
292 string res,a;
293 adj = map( adj, #'DeclAdj, casus, demon );
294 start = 1;
295 res = "";
296 foreach( a: adj ) {
297 res += (start ? "" : ", ") + a[0..<2];
298 start = 0;
299 }
300 return res + " ";
301}
302
303// Wie lautet der Name des Objekts?
304varargs string name(int casus,int demon)
305{
306 mixed sh, adj;
307 int art, plural;
308
309 art = QueryProp(P_ARTICLE);
310
311 // RAW: direkt zurueckgeben ohne Verarbeitung
312 if (casus == RAW )
313 {
314 if(pointerp(QueryProp(P_NAME)))
315 return QueryProp(P_NAME)[WER];
316 return QueryProp(P_NAME);
317 }
318
319 // Unsichtbar: Etwas
320 if (QueryProp(P_INVIS))
321 return ({ "etwas", "von etwas", "etwas", "etwas" })[casus];
322
323 // Kein Name? Schade ...
324 if (!(sh=QueryProp(P_NAME)) ||
325 (stringp(sh) && !sizeof(sh))) return 0;
326
327 // P_NAME pruefen.
328 if (pointerp(sh) && sizeof(sh) != 4)
329 raise_error(sprintf("Ungueltige Arraygroesse in P_NAME: %d\n",
330 sizeof(sh)));
331
332 // Plural .. Namen verwursten
333 if (plural = QueryProp(P_PLURAL))
334 {
335 // Selber Artikel suchen ist nicht ...
336 if (demon==2||!art) demon = 0;
337
338 // falls P_NAME ein Array mit Faellen enthaelt, den richtigen
339 // extrahieren...
340 if (pointerp(sh)) {
341 sh = sh[casus];
342 }
343 else {
344 // sonst versuchen, zu deklinieren.
345 int last = sh[<1];
346 if (casus == WEM&&last!='s'&&last!='n') sh = sh + "n";
347 }
348
349 // Sind Adjektive vorhanden?
350 if ( pointerp(adj = QueryProp(P_NAME_ADJ)) && sizeof(adj))
351 adj = depointer_adj(adj,casus,demon);
352 if (!stringp(adj)) adj = "";
353
354 return sprintf("%s%s%s%s",QueryArticle(casus,demon,0),adj,
355 (plural < 2 ? "":(plural < 8 ?
356 ({ "zwei ", "drei ", "vier ", "fuenf ", "sechs ",
357 "sieben " })[plural-2] : to_string(plural)+" ")),sh);
358 }
359
360 // Name ist Pointer: Einfach den richtigen auswaehlen
361 if (pointerp(sh))
362 sh = sh[casus];
363
364 // Ansonsten doch wieder verwursten ...
365 else if (stringp(sh))
366 {
367 int last = sh[<1];
368
369 switch(casus)
370 {
371 case WEM:
372 case WEN:
373 if ( art && last=='e'&&QueryProp(P_GENDER) == MALE)
374 sh = (string)sh + "n";
375 break;
376
377 case WESSEN:
378 if( !art )
379 {
380 switch(last)
381 {
382 case 'x':
383 case 's':
384 case 'z':
385 sh = (string)sh + "'";
386 break;
387
388 default:
389 sh = (string)sh + "s";
390 }
391 }
392 else
393 {
394 switch(last)
395 {
396 default:
397 if (QueryProp(P_GENDER)!=FEMALE)
398 sh=(string)sh+"s";
399 break;
400 case 'e':
401 if (QueryProp(P_GENDER)==MALE)
402 sh=(string)sh+"n";
403 case 'x':
404 case 's':
405 case 'z':
406 break;
407 } /* switch (last) */
408 } /* if( !art ) else */
409 } /* switch( casus ) */
410 } /* pointerp(sh) */
411
412 // RAW? Dann mal zurueck
413 if (demon == RAW) return (string)sh;
414
415 // Selber Artikel suchen ...
416 if (demon==2)
417 {
418 if (art)
419 demon = SuggestArticle();
420 else
421 demon=0; // Kein Artikel: egal (SuggestArticle ist zeitaufwendig)
422 }
423
424 if (pointerp(adj = QueryProp(P_NAME_ADJ)) && sizeof(adj))
425 adj = depointer_adj(adj,casus,demon);
426
427 if (!stringp(adj)) adj = "";
428
429 return QueryArticle( casus, demon )+adj+sh;
430}
431
432// Grossgeschriebenen Namen zurueckgeben
433varargs string Name( int casus, int demon )
434{
435 return capitalize(name( casus, demon )||"");
436}
437
438// Langbeschreibung anzeigen
439public varargs string long(int mode)
440{
441 return process_string( QueryProp(P_LONG) );
442}
443
444// Kurzbeschreibung anzeigen, falls nicht unsichtbar
445public string short()
446{
447 string sh;
448
449 // Unsichtbar? Dann gibts nichts zu sehen ...
450 if (QueryProp(P_INVIS)||!(sh=QueryProp(P_SHORT)))
451 return 0;
Bugfixd6cd2e52017-02-27 18:23:42 +0100452
453 sh=process_string(sh);
454
455 // Ist das letzte Zeichen kein Satzzeichen, einen Punkt anhaengen.
456 // Note: matchen mit regexp [[:punct:]]$ waere sauberer bzgl. non-ASCII.
457 int i=sh[<1];
458 if(i!='.' && i!='!' && i!='?')
459 return sh+".\n";
460
461 return sh+"\n";
MG Mud User88f12472016-06-24 23:31:02 +0200462}
463
464// Namens-Adjektive setzen
Bugfixdc6d7352016-11-14 13:14:31 +0100465static <string|string*>* _set_name_adj(string|<string|string*>* adjectives)
MG Mud User88f12472016-06-24 23:31:02 +0200466{
467 if (!adjectives)
468 adjectives=({});
469 // In Array umwandeln
470 else if ( !pointerp(adjectives))
471 adjectives = ({ to_string(adjectives) });
472 return Set( P_NAME_ADJ, adjectives );
473}
474
475// ############################
476//#################### Details, Gerueche, Laerm #########################
477// ############################
478
479// Low-level Funktion zum Ergaenzen von Details, wird von den div. Add...()
480// gerufen, die das richtige Mapping uebergeben.
481// Aendert das Mapping <details> direkt.
482private void _add_details(string|string* keys,
483 string|string*|mapping|closure descr,
484 mapping details )
485{
486 if (stringp(keys))
Arathorn444e9ff2016-09-03 15:11:41 +0200487 details[lower_case(keys)]=descr;
MG Mud User88f12472016-06-24 23:31:02 +0200488 else if (pointerp(keys))
489 {
490 foreach(string key : keys)
491 details[lower_case(key)]=descr;
492 }
493 else
494 raise_error("Wrong type to argument 1, expected string|string*.\n");
495}
496
497// Low-level Funktion zum Entfernen von Details, wird von den div. Remove...()
498// gerufen, die das richtige Mapping uebergeben.
499// Aendert das Mapping <details> direkt.
500private void _remove_details(string|string* keys, mapping details )
501{
502 if (stringp(keys))
503 details -= ([keys]);
504 else if (pointerp(keys))
505 details -= mkmapping(keys);
506 else
507 raise_error("Wrong type to argument 1, expected string|string*.\n");
508}
509
510// Detail(s) hinzufuegen
511void AddDetail(string|string* keys, string|string*|mapping|closure descr)
512{
513 int i;
514 mapping details;
515
516 details = Query(P_DETAILS, F_VALUE);
517
518 // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
519 return _add_details(keys, descr, details);
520}
521
522// Detail(s) entfernen
523varargs void RemoveDetail(string|string* keys )
524{
525 // Alle loeschen geht direkt ...
526 if (!keys )
527 Set(P_DETAILS, ([]), F_VALUE);
528 else
529 // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
530 _remove_details(keys, Query(P_DETAILS, F_VALUE));
531}
532
533// SpecialDetail hinzufuegen
534void AddSpecialDetail(string|string* keys, string functionname )
535{
536 closure cl;
537
538 // Absichern! Sonst koennte jeder interne Funktionen aufrufen
539 if (extern_call() &&
540 (geteuid(previous_object()) != geteuid() || process_call()) &&
541 !(object_name(previous_object()) == "/obj/doormaster" &&
542 functionname == "special_detail_doors") )
543 raise_error( "Illegal use of AddSpecialDetail!\n" );
544
545 // Closure generieren
546 if ( !stringp(functionname)||
547 !(cl = symbol_function( functionname, this_object())) )
548 return;
549
550 // Detail hinzufuegen
551 AddDetail( keys, cl );
552 return;
553}
554
555// SpecialDetail(s) entfernen
556void RemoveSpecialDetail(string|string* keys )
557{
558 // RemoveSpecialDetail(0) wuerde sonst ALLE Details (auch die
559 // 'normalen') loeschen
560 if (pointerp(keys)||stringp(keys))
561 RemoveDetail(keys);
562 return;
563}
564
565// Lesbares Detail einfuegen
566void AddReadDetail(string|string* keys,
567 string|string*|mapping|closure descr )
568{
569 // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
570 return _add_details(keys, descr, Query(P_READ_DETAILS, F_VALUE));
571}
572
573// Lesbare(s) Detail(s) entfernen
574varargs void RemoveReadDetail(string|string* keys )
575{
576 // Alle loeschen geht direkt ...
577 if (!keys )
578 Set(P_READ_DETAILS, ([]), F_VALUE);
579 else
580 // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
581 _remove_details(keys, Query(P_READ_DETAILS, F_VALUE));
582}
583
584// Geraeusch(e) dazufuegen
585void AddSounds(string|string* keys,
586 string|string*|mapping|closure descr )
587{
588 // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
589 return _add_details(keys, descr, Query(P_SOUNDS, F_VALUE));
590}
591
592// Geraeusch(e) entfernen
593varargs void RemoveSounds(string|string* keys )
594{
595 // Alle loeschen geht direkt ...
596 if (!keys )
597 Set(P_SOUNDS, ([]), F_VALUE);
598 else
599 // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
600 _remove_details(keys, Query(P_SOUNDS, F_VALUE));
601}
602
603// Geru(e)ch(e) hinzufuegen
604void AddSmells(string|string* keys,
605 string|string*|mapping|closure descr )
606{
607 // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
608 return _add_details(keys, descr, Query(P_SMELLS, F_VALUE));
609}
610
611// Geru(e)ch(e) entfernen
612varargs void RemoveSmells(string|string* keys )
613{
614 // Alle loeschen geht direkt ...
615 if (!keys )
616 Set(P_SMELLS, ([]), F_VALUE);
617 else
618 // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
619 _remove_details(keys, Query(P_SMELLS, F_VALUE));
620}
621
622// Tastbare(s) Detail(s) hinzufuegen
623void AddTouchDetail(string|string* keys,
624 string|string*|mapping|closure descr )
625{
626 // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
627 return _add_details(keys, descr, Query(P_TOUCH_DETAILS, F_VALUE));
628}
629
630// Tastbare(s) Detail(s) entfernen
631varargs void RemoveTouchDetails(string|string* keys )
632{
633 // Alle loeschen geht direkt ...
634 if (!keys )
635 Set(P_TOUCH_DETAILS, ([]), F_VALUE);
636 else
637 // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
638 _remove_details(keys, Query(P_TOUCH_DETAILS, F_VALUE));
639}
640
641// Detailinfos fuer Detail key, Spieler hat die Rasse race
642// und benutzt seinen Sinn sense
643varargs string GetDetail(string key, string race, int sense)
644{
645 string|string*|mapping|closure detail;
646
647 if (stringp(race)) race = lower_case(race);
648
649 switch(sense)
650 {
651 case SENSE_SMELL: detail=Query(P_SMELLS, F_VALUE)[key];
652 sense=EP_SMELL; break;
653 case SENSE_SOUND: detail=Query(P_SOUNDS, F_VALUE)[key];
654 sense=EP_SOUND; break;
655 case SENSE_TOUCH: detail=Query(P_TOUCH_DETAILS, F_VALUE)[key];
656 sense=EP_TOUCH; break;
657 case SENSE_READ: detail=Query(P_READ_DETAILS, F_VALUE)[key];
658 sense=EP_RDET;
659 break;
660
661 default: detail=Query(P_DETAILS, F_VALUE)[key];
662 sense=EP_DETAIL; break;
663 }
664
665 if (!stringp(detail))
666 {
667 if (closurep(detail))
668 detail = (string)funcall(detail,key);
669 else if (mappingp(detail))
670 detail = (string)(detail[race]||detail[0]);
671 else if (pointerp(detail))
672 detail = (string)(detail[random(sizeof(detail))]);
673 }
674
675 // FP vergeben (so vorhanden ;-) )
676 if (detail) GiveEP(sense,key);
677
678 return detail;
679}
680
681// TODO: OBSOLET (Libgrep notwendig)
682void read( string str ) {
683 raise_error("Diese Funktion existiert nicht mehr.\n");
684}
685
686
687// ######################
688//####################### Zugriffsfunktionen ############################
689// ######################
690
691// Dienen dazu, die direkte Manipulation der Props von aussen zu erschweren.
692
693// Filter, um Specialdetails zu eliminieren
694// erstellt ausserdem ne Kopie vom Mapping. (Wichtig!)
695// Wird vor allem benoetigt, um P_DETAILS in P_DETAILS und
696// P_SPECIAL_DETAILS zu treffen.
697private int _closures(string x, mapping details, int yes )
698{
699 return yes ? closurep(details[x]) : !closurep(details[x]);
700}
701
702static mapping _query_details()
703{
704 return filter_indices(Query(P_DETAILS, F_VALUE), #'_closures,
705 Query(P_DETAILS, F_VALUE),0);
706}
707
708static mapping _query_special_details()
709{
710 return filter_indices(Query(P_DETAILS, F_VALUE),#'_closures,
711 Query(P_DETAILS, F_VALUE),1);
712}
713
714static mapping _query_read_details() {
715 return deep_copy(Query(P_READ_DETAILS, F_VALUE));
716}
717
718static mapping _query_sound_details() {
719 return deep_copy(Query(P_SOUNDS, F_VALUE));
720}
721
722static mapping _query_smell_details() {
723 return deep_copy(Query(P_SMELLS, F_VALUE));
724}
725
726
727// ##########################
728//##################### Klassen-Mitgliedschaft ##########################
729// ##########################
730
731// Klasse hinzufuegen
732public void AddClass(string|string* str)
733{
734 if (stringp(str))
735 str = ({ str });
736 // Aliase aufloesen und implizite Klassen addieren.
737 str = (string*)CLASSDB->AddImplicitClasses(str);
738 // Summe mit alten Klassen bilden und Doppelte eliminieren
739 str = str + Query(P_CLASS, F_VALUE);
740 Set( P_CLASS, m_indices(mkmapping(str)), F_VALUE);
741
742 return;
743}
744
745// Klasse entfernen
746void RemoveClass(string|string* str)
747{
748 if (stringp(str))
749 str = ({ str });
750
751 // Aliase aufloesen und implizite Klassen addieren.
752 str = (string*)CLASSDB->AddImplicitClasses(str);
753
754 // Und alle - inklusive impliziter Klassen - entfernen
755 // TODO: Pruefen, ob dies die richtige Entscheidung ist.
756 Set( P_CLASS, Query(P_CLASS, F_VALUE)-str, F_VALUE);
757
758 return;
759}
760
761// Ist das Objekt Mitglied der Klasse str?
762int is_class_member(string|string* str)
763{
764 // Keine Klasse, keine Mitgliedschaft ...
765 if (!str || str=="")
766 return 0;
767
768 // Es sollte schon ein Array sein
769 if (stringp(str))
770 str = ({ str });
771
772 // Klassen und Ids ins Array
773 string *classes=QueryProp(P_CLASS);
774 if (!pointerp(classes))
775 return 0;
776
777 // .. und testen
778 foreach(string class : str)
779 if (member(classes,class) > -1 ) return 1;
780
781 return 0;
782}
783
784// Klasse direkt setzen abfangen
785static string* _set_class(string* classes )
786{
787 Set( P_CLASS, ({}), F_VALUE );
788 AddClass(classes);
789 return QueryProp(P_CLASS);
790}
791
792// #####################
793//######################## Material-Handling ############################
794// #####################
795
796// Material setzen
797static mapping _set_material(mapping|string|string* mat )
798{
799 mapping mats = ([]);
800
801 if (mappingp(mat))
802 {
803 if( !sizeof(mat) || !widthof(mat) )
804 raise_error(sprintf("P_MATERIAL: expected mapping with at least one "
805 "key and one value, got %.50O\n",mat));
806 else
807 mats = mat;
808 }
809 else if (stringp(mat))
810 mats[mat]=100;
811 else
812 {
813 int sz = sizeof(mat);
814 // Kommt dann vor, wenn <mat> 0 oder ({}) ist.
815 if ( !sz )
816 raise_error(sprintf("P_MATERIAL: expected string or non-empty "
817 "mapping|string*, got %.50O.\n", mat));
818 mats = mkmapping(mat, allocate(sz, 100/sz));
819 }
820 return Set( P_MATERIAL, mats, F_VALUE );
821}
822
823// Woraus besteht das Objekt?
824static mapping _query_material()
825{
826 mixed res;
827
828 if ( !mappingp(res = Query(P_MATERIAL, F_VALUE)) )
829 return ([MAT_MISC:100]);
830
831 return res;
832}
833
834// Anteil von mat am Objekt?
835int QueryMaterial( string mat )
836{
837 mapping mats;
838
839 if ( !mappingp(mats = QueryProp(P_MATERIAL)) )
840 return 0;
841
842 return mats[mat];
843}
844
845// Anteil der Gruppe am Objekt
846int QueryMaterialGroup( string matgroup )
847{
848 return (int)call_other( MATERIALDB, "MaterialGroup",
849 QueryProp(P_MATERIAL), matgroup );
850}
851
852
853string MaterialList( int casus, mixed idinf )
854{
855 return (string)call_other( MATERIALDB, "ConvMaterialList",
856 QueryProp(P_MATERIAL), casus, idinf );
857}
858
859static int _set_size(int sz) {
860//Groesse muss > 0 sein, alles andere ist unsinnig! (0 und neg. Groessen
861//haben keine phys. Relevanz und machen u.U. Probleme mit Objekten, die
862//Schaden in Abhaengigkeit der Groesse machen)
863 if (sz>0)
864 Set(P_SIZE,sz,F_VALUE);
865 return(Query(P_SIZE,F_VALUE));
866}
867
868// P_CLONE_TIME
869static int _query_clone_time() { return object_time(); }
870