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