blob: 7f71bc4c3b6acde3130fc165ee378d1071e5e17b [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// - Prototypen und Properties in thing/material.h
4// - Liste in materials.h
5//
6// TODO: properties.h um materials.h erweitern
7//
8// - implizite Gruppenzuordnung entfernen, da jetzt explizit in
9// Definitionsdateien vorhanden
10// - Materialdoku ueberarbeiten, dabei Hinweis auf nicht mehr implizite
11// Gruppenzuordnung
12// /p/daemon/materialdb.c -- Materialdatenbank
13//
14// $Id: materialdb.c 8755 2014-04-26 13:13:40Z Zesstra $
15
16#pragma strong_types
17#pragma no_clone
18#pragma no_inherit
19#pragma no_shadow
20#pragma pedantic
21
22// Die Propertydefinition reinholen
23#include <language.h>
24#include <thing/description.h>
25// Materialliste nicht mit reinziehen
26#define _SKIP_MATERIALS_
27#include <thing/material.h>
28#include <player/description.h>
29#include <rtlimits.h>
30
31// Basisverzeichnis der Materialien
32#define MAT_DIR "/doc/materials"
33
34// Ausgabeverzeichnis fuer Doku
35#define DOC_DIR(x) ("/doc/materials/"+x)
36
37// Dateiname des Headers mit Materialdefinitionen
38#define HEADERFILE "/sys/materials.h"
39
40// Savefile.
41#define SAVEFILE DOC_DIR("materialdb")
42
43// Rein intern verwendete Namen
44#define P_RECOC "recognizability"
45#define P_ID "id"
46#define P_DEFSTR "defstr"
47#define P_MEMBERS "members"
48#define P_MG_FRACTIONS "mg_fractions"
49
50#define LOG_ERROR(x) if(find_player("raschaua")&&find_player("raschaua")->QueryProp("mdb-debug"))tell_object(find_player("raschaua"),"MDB-Error:"+x)
51#define LOG_WARN(x) if(find_player("raschaua")&&find_player("raschaua")->QueryProp("mdb-debug"))tell_object(find_player("raschaua"),"MDB-Warn:"+x)
52
53// Prototypes:
54// (Liefert den Anteil der Materialgruppe grp an mats)
55int MaterialGroup(mapping mats, string grp);
56// Konvertiert eine Liste von Materialien zu ihren Namen, dabei wird die
57// Erkennungsfaehigkeit beruecksichtigt und evtl. falsch erkannt
58varargs string ConvMaterialList(mixed mats, int casus, mixed idinf);
59varargs string MaterialName(string mat, int casus, mixed idinf);
60// Gibt den Namen einer Materialgruppe zurueck
61string GroupName(string grp);
62// Gibt alle Materialien zurueck
63string *AllMaterials();
64// Gibt alle Gruppen zurueck
65string *AllGroups();
66// Gibt alle Gruppen zurueck, in denen mat enthalten ist
67string *GetMatMembership(string mat);
68// Gibt alle Materialien zurueck, die in grp enthalten sind
69string *GetGroupMembers(string grp);
70// Erneuert die Materialien durch Scannen des Materialverzeichnisses
71void Update();
72// generiert Headerfile aus den Daten
73varargs void GenHeaderFile(string fn);
74
75mapping materials;
76mapping material_groups;
77private status initialized;
78mapping old_mat_keys; // Alter Materialkey -> neuer Key (Kompatibilitaet)
79nosave mapping new_materials; // Materialien waehrend des Scannens
80nosave mapping new_material_groups; // Materialgruppen waehrend des Scannens
81private nosave status isScanning;
82private nosave int updateTicks;
83
84void create() {
85 seteuid(getuid());
86 // Savefile einlesen, falls moeglich, damit die DB direkt initialisert ist,
87 // wenn auch ggf. mit alten Daten.
88 restore_object(SAVEFILE);
89 if (initialized) {
90 // falls erfolgreich, direkt Header fuer die Mudlib schreiben
91 GenHeaderFile();
92 }
93 // jetzt Update der Daten durchfuehren.
94 Update();
95}
96
97//==================== Umwandeln der Strings aus der thing/material.h
98private string getMatId(string key) {
99 // Alte Bezeichner umwandeln
100 if (!member(materials, key))
101 key = old_mat_keys[key];
102 return key;
103}
104private string getMatGroupId(string key) {
105 // Alte Bezeichner umwandeln
106 if (!member(material_groups, key))
107 key = "MATGROUP_"+upperstring(key[3..]);
108 return key;
109}
110private string matKey2Defstr(string key, mapping mats) {
111 string id;
112 if (member(mats[key], P_DEFSTR))
113 id = mats[key][P_DEFSTR];
114 else
115 id = key;
116 return id;
117}
118private string groupKey2Defstr(string key) {
119 if (sizeof(key) > 9)
120 key = "mg_"+lowerstring(key[9..]);
121 else
122 key = "";
123 return key;
124}
125
126//==================== Schnittstellenfunktionen zur Verwendung der DB
127varargs string MaterialName(string mat, int casus, mixed idinf) {
128 if (initialized) {
129 string *names;
130 mapping props;
131 mixed *dif;
132 // Anpassen der Materialid
133 mat = getMatId(mat);
134
135 if (!mappingp(props=materials[mat]))
136 props=([]);
137
138 // Je nach Koennen des Spielers kann man das exakte Material
139 // mehr oder weniger gut erkennen:
140 if (pointerp(dif=props[P_RECOC])
141&& (!intp(idinf)||idinf<100) ) { // 100=exakte Erkennung
142 int i, n, recval;
143 mixed *grps, tmp, x;
144
145 recval=0;
146 grps=props[P_MG_FRACTIONS];
147 if (!pointerp(idinf))
148 idinf=({idinf});
149
150 // Zunaechst die Faehigkeit des Spielers (da koennen noch
151 // Gildenfaehigkeiten hinzu kommen) ermitteln, dieses
152 // Material zu erkennen:
153 i=sizeof(idinf);
154 while(i--) {
155 tmp=idinf[i];
156 if (objectp(tmp)) // Diese Property ist hauptsaechlich fuer Rassen:
157 tmp=tmp->QueryProp(P_MATERIAL_KNOWLEDGE);
158 if (intp(tmp)) {
159 recval+=tmp; // Allgemeine Erkennungsfaehigkeit
160 break;
161 }
162 if (closurep(tmp) && intp(x=funcall(tmp,mat,grps))) {
163 recval+=x;
164 break; // Closures koennen immer nuetzlich sein :)
165 }
166 if (mappingp(tmp)) {
167 int j;
168 if ((x=tmp[mat]) && intp(x)){
169 // Erkennung von speziell diesem Material
170 recval+=x;
171 break;
172 }
173 // Erkennung von Gruppen
174 j=sizeof(grps);
175 while(j--)
176 if((x=tmp[grps[j]]) && intp(x))
177 recval+=x;
178 if (pointerp(tmp=tmp[MATERIAL_SYMMETRIC_RECOGNIZABILITY])) {
179 for (j=sizeof(tmp)-2;j>=0;j-=2) {
180 if (!intp(x=tmp[j+1]))
181 raise_error("materialdb: illegal sym.recoc. format\n");
182 if (props[tmp[j]])
183 recval+=x;
184 else // bei passenden Gruppen +, bei anderen -
185 recval-=x;
186 }
187 }
188 }
189 }
190
191 // Jetzt wird ermittelt, ob vielleicht eine ungenauere
192 // Beschreibung gegeben werden soll:
193 x=dif[0];
194 n = sizeof(dif)-1;
195 for (i=2;i<=n;i+=2) {
196 if (recval>=dif[i-1])
197 x=dif[i];
198 }
199 // Wenn die Faehigkeiten des Spielers nicht fuer den echten Klarnamen
200 // ausreichen, gib die Alternative zurueck:
201 if (x!=mat)
202 return MaterialName(x, casus, 100);
203 }
204
205 if (!pointerp(names=props[P_NAME]) || sizeof(names)<4)
206 names=({"unbekanntes Material", "unbekannten Materials",
207 "unbekanntem Material", "unbekannten Material"});
208 if (casus<0 || casus>3)
209 casus=0;
210 return names[casus];
211 }
212}
213
214varargs string ConvMaterialList(mixed mats, int casus, mixed idinf) {
215 if (initialized) {
216 string *ms,ml;
217 int i;
218
219 ml="";
220 if (mappingp(mats))
221 ms=m_indices(mats);
222 else if (stringp(mats))
223 ms=({mats});
224 else if (pointerp(mats))
225 ms=mats;
226 else
227 ms=({});
228 i=sizeof(ms);
229 while(i) {
230 ml+=MaterialName(ms[--i],casus,idinf);
231 if (i)
232 ml+=((i>1)?", ":" und ");
233 }
234 return ml;
235 }
236}
237
238int MaterialGroup(mapping mats, string grp) {
239 if (initialized) {
240 string *ms;
241 int i,res;
242
243 res=0;
244 if (!mappingp(mats) || !stringp(grp))
245 return res;
246 ms=m_indices(mats);
247 i=sizeof(ms);
248 while(i--) {
249 string mat;
250 mapping props;
251 mat=ms[i];
252 if (mappingp(props=materials[getMatId(mat)]))
253 res+=(mats[mat]*props[P_MG_FRACTIONS][getMatGroupId(grp)])/100;
254 }
255 if (res<-100) // Vielleicht noch Antimaterie zulassen
256 res=-100; // (noch nicht sicher ob das so bleiben wird oder 0 sein wird)
257 if (res>100)
258 res=100;
259 return res;
260 }
261}
262
263string *AllMaterials() {
264 if (initialized) {
265 // Aus Kompatibilitaetsgruenden die alten Schluessel (#define-String)
266 // zurueckgeben
267 return m_indices(old_mat_keys);
268 }
269 return 0;
270}
271
272string *AllGroups() {
273 if (initialized) {
274 // Aus Kompatibilitaetsgruenden die alten Schluessel (#define-String)
275 // zurueckgeben
276 return map(m_indices(material_groups), #'groupKey2Defstr);
277 }
278 return 0;
279}
280
281string *GetMatMembership(string mat) {
282 if (initialized) {
283 mapping props;
284 // Anpassen der Materialid
285 mat = getMatId(mat);
286
287 if (!mappingp(props=materials[mat]))
288 return ({});
289 return map(m_indices(props[P_MG_FRACTIONS]), #'groupKey2Defstr);
290 }
291 return 0;
292}
293
294string *GetGroupMembers(string grp) {
295 if (initialized) {
296 string *mats;
297 // Anpassen der Materialid
298 grp = getMatGroupId(grp);
299 if (!member(material_groups, grp) ||
300 !pointerp(mats=material_groups[grp][P_MEMBERS]))
301 return ({});
302 return map(mats, #'matKey2Defstr, materials);
303 }
304 return 0;
305}
306
307string GroupName(string grp) {
308 if (initialized) {
309 if (member(material_groups, getMatGroupId(grp)))
310 return material_groups[getMatGroupId(grp)][P_NAME];
311 else
312 return "Unbekanntes";
313 }
314}
315
316string GroupDescription(string grp) {
317 if (initialized) {
318 if (member(material_groups, getMatGroupId(grp)))
319 return material_groups[getMatGroupId(grp)][P_DESCRIPTION];
320 else
321 return "Gruppe unbekannt";
322 }
323}
324
325//==================== Generieren von Headerfile und Manpages
326private string *get_ordered_groups()
327{
328 return ({"MATGROUP_WOOD", "MATGROUP_JEWEL", "MATGROUP_STONE", "MATGROUP_MAGNETIC",
329 "MATGROUP_METAL", "MATGROUP_DRUG", "MATGROUP_HERBAL", "MATGROUP_FLEXIBLE",
330 "MATGROUP_BIO", "MATGROUP_ACIDIC", "MATGROUP_BASIC", "MATGROUP_POISONOUS",
331 "MATGROUP_EXPLOSIVE", "MATGROUP_INFLAMMABLE",
332 "MATGROUP_ELEMENTAL", "MATGROUP_ELECTRICAL", "MATGROUP_MAGIC",
333 "MATGROUP_HOLY", "MATGROUP_UNHOLY", "MATGROUP_INVIS",
334 "MATGROUP_SOLID", "MATGROUP_FLUID", "MATGROUP_GAS"});
335}
336private string gen_material_h_head()
337{
338 return
339 "// MorgenGrauen MUDlib\n//\n"
340 "// materials.h -- material definitions\n//\n"
341 "// This file is generated by /secure/materialdb.c\n//\n"
342 "// DO NOT EDIT!\n//\n"
343 "// $Id: materialdb.c 8755 2014-04-26 13:13:40Z Zesstra $\n\n"
344 "#ifndef __MATERIALS_H__\n"
345 "#define __MATERIALS_H__\n\n";
346}
347private string gen_material_h_material(string mat, string last_grp)
348{
349 mat = old_mat_keys[mat];
350 return sprintf("#define %-24s\"%-20s // %s\n", mat,
351 (member(materials[mat], P_DEFSTR)?materials[mat][P_DEFSTR]:mat)+"\"",
352 materials[mat][P_DESCRIPTION]||materials[mat][P_NAME][WER]);
353}
354private string gen_material_h_materials_grp(string grp, string *left)
355{
356 string txt, *mats;
357 txt = sprintf("\n// Gruppe: %s\n", GroupName(grp));
358 mats = GetGroupMembers(grp) - (GetGroupMembers(grp) - left);
359 txt += sprintf("%@s", map(sort_array(mats, #'>), #'gen_material_h_material));
360 left -= GetGroupMembers(grp);
361 return txt;
362}
363private string gen_material_h_materials()
364{
365 string txt, last_grp;
366 string *grps, *mats;
367 txt = "// ****************************** Materialien ******************************\n";
368 // Gruppenweise ordnen
369 grps = get_ordered_groups();
370 mats = AllMaterials();
371 txt += sprintf("%@s", map(grps, #'gen_material_h_materials_grp,
372 &mats));
373 // Übriggebliene Materialien ausgeben
374 txt += "// sonstige Materialien:\n";
375 txt += sprintf("%@s", map(mats, #'gen_material_h_material));
376 return txt;
377}
378private string gen_material_h_group(string grp)
379{
380 return sprintf("#define %-27s\"%-18s // %s\n",
381 grp, groupKey2Defstr(grp)+"\"", GroupName(grp));
382}
383private string gen_material_h_groups()
384{
385 string txt;
386 txt = "\n// **************************** Materialgruppen ****************************\n\n"
387 "#ifndef _IS_MATERIALDB_\n";
388 txt += sprintf("%@s\n", map(sort_array(m_indices(material_groups), #'>),
389 #'gen_material_h_group));
390 txt += "\n#endif // _IS_MATERIALDB_\n";
391 return txt;
392}
393private string gen_material_h_foot()
394{
395 return
396 "#endif // __THING_MATERIAL_H__\n";
397}
398private int dump_material_h(string fn)
399{
400 return (write_file(fn, gen_material_h_head()) &&
401 write_file(fn, gen_material_h_materials()) &&
402 write_file(fn, gen_material_h_groups()) &&
403 write_file(fn, gen_material_h_foot()));
404}
405private string gen_material_list_material(string mat)
406{
407 mat = old_mat_keys[mat];
408 return sprintf(" %-28s%=-45s\n", mat,
409 materials[mat][P_DESCRIPTION]||materials[mat][P_NAME][WER]);
410}
411private string gen_material_list_materials_grp(string grp, string *left)
412{
413 string txt, *mats;
414 txt = sprintf("%s:\n", capitalize(GroupName(grp)));
415 mats = sort_array(GetGroupMembers(grp) - (GetGroupMembers(grp) - left), #'>);
416 txt += sprintf("%@s\n", map(mats, #'gen_material_list_material));
417 left -= GetGroupMembers(grp);
418 return txt;
419}
420private void dump_material(string fn)
421{
422 string txt;
423 string *grps, *mats;
424 // Gruppenweise ordnen
425 grps = get_ordered_groups();
426 mats = AllMaterials();
427 txt = sprintf("%@s", map(grps, #'gen_material_list_materials_grp,
428 &mats));
429 // Übriggebliene Materialien ausgeben
430 txt += "sonstige Materialien:\n";
431 txt += sprintf("%@s", map(mats, #'gen_material_list_material));
432 write_file(fn, txt) ||
433 raise_error(sprintf("Konnte Liste nicht weiter in Datei %s schreiben,"
434 " Abbruch\n", fn));
435}
436private void dump_group(string grp, string fn)
437{
438 // upperstring langsame simul_efun, warum?
439 write_file(fn, sprintf(" %-28s%=-48s\n", (grp),
440 GroupName(grp))) ||
441 raise_error(sprintf("Konnte Liste nicht weiter in Datei %s schreiben,"
442 " Abbruch\n", fn));
443}
444private string gen_doc_foot(string other)
445{
446 return sprintf("\nSIEHE AUCH:\n"
447 " Konzepte: material, materialerkennung\n"
448 " Grundlegend: P_MATERIAL, /sys/materials.h, /sys/thing/material.h\n"
449 " Methoden: QueryMaterial(), QueryMaterialGroup(), MaterialList(),\n"
450 " Listen: AllMaterials(), AllGroups()\n"
451 " %s\n"
452 " Master: ConvMaterialList(), MaterialGroup(),\n"
453 " GroupName(), MaterialName(),\n"
454 " GetGroupMembers(), GetMatMembership()\n"
455 " Sonstiges: P_MATERIAL_KNOWLEDGE\n\n"
456 "%s generiert aus /secure/materialdb\n", other, dtime(time()));
457}
458
459/* GenMatList
460 *
461 * Generiert Datei mit registrierten Materialien fuer die Dokumentation,
462 */
463varargs void GenMatList(string fn)
464{
465 if (initialized) {
466 string txt;
467 if (!stringp(fn) || !sizeof(fn))
468 fn = DOC_DIR("materialliste");
469 if (file_size(fn) >= 0) {
470 printf("Datei %s existiert bereits, loesche sie\n", fn);
471 rm(fn);
472 }
473 if (write_file(fn, "Material Liste\n==============\n\n")) {
474 dump_material(fn);
475 write_file(fn, gen_doc_foot("materialgruppen"));
476 printf("Materialliste erfolgreich in Datei %s geschrieben\n", fn);
477 } else
478 printf("Konnte Liste nicht in Datei %s schreiben, Abbruch\n", fn);
479 }
480}
481
482/* GenMatGroupList
483 *
484 * Generiert Datei mit registrierten Materialgruppen fuer die Dokumentation,
485 */
486varargs void GenMatGroupList(string fn)
487{
488 if (initialized) {
489 string txt;
490 if (!stringp(fn) || !sizeof(fn))
491 fn = DOC_DIR("materialgruppen");
492 if (file_size(fn) >= 0) {
493 printf("Datei %s existiert bereits, loesche sie\n", fn);
494 rm(fn);
495 }
496 if (write_file(fn, "Materialgruppen\n===============\n")) {
497 map(sort_array(m_indices(material_groups), #'>), #'dump_group, fn);
498 write_file(fn, gen_doc_foot("materialliste"));
499 printf("Materialliste erfolgreich in Datei %s geschrieben\n", fn);
500 } else
501 printf("Konnte Liste nicht in Datei %s schreiben, Abbruch\n", fn);
502 }
503}
504
505/* GenHeaderFile
506 *
507 * Generiert Headerfile mit Definitionen der moeglichen Materialien und
508 * Gruppen
509 */
510varargs void GenHeaderFile(string fn)
511{
512 if (initialized) {
513 string txt;
514 if (!stringp(fn) || !sizeof(fn))
515 fn = HEADERFILE;
516 if (file_size(fn) >= 0) {
517 printf("Datei %s existiert bereits, loesche sie\n", fn);
518 rm(fn);
519 }
520 if (dump_material_h(fn))
521 printf("Headerdatei erfolgreich in %s geschrieben\n", fn);
522 else
523 printf("Konnte Headerdatei nicht in Datei %s schreiben, Abbruch\n", fn);
524 }
525}
526
527//==================== Pruef- und Hilfsfunktionen fuer Materialien
528private void updateGroupMembers(mapping groups, string mat_id, mapping mat) {
529 mixed *addgrps; // Array zum Ableiten von Gruppenzugehoerigkeiten
530 string *h;
531 int i, val;
532 mapping fractions; // Mapping mit Anteilen an Gruppen
533 fractions = mat[P_MG_FRACTIONS];
534 if (!mappingp(fractions))
535 fractions = ([]);
536 addgrps=({ // Reihenfolge wird rueckwaerts abgearbeitet
537 // Ableitungen sind z.T. abenteuerlich gewesen, mal ordentlich
538 // ausmisten. Die Zugehoerigkeit gehoert explizit in die
539 // Materialdefinition
540 // Gase sieht man normalerweise nicht:
541 ({"MATGROUP_INVIS", "MATGROUP_GAS"}),
542 // Mineralien sind auch Steine
543 ({"MATGROUP_STONE","MATGROUP_MINERAL"}),
544 // Edelmetalle sind Metalle:
545 ({"MATGROUP_METAL","MATGROUP_PRECIOUS_METAL"}),
546 // Lebewesen und deren Ueberreste, Paiere und Stoffe sind biologisch
547 ({"MATGROUP_BIO","MATGROUP_LIVING","MATGROUP_DEAD",
548 "MATGROUP_PAPER"}),
549 // Holz ist pflanzlich:
550 ({"MATGROUP_HERBAL", "MATGROUP_WOOD"}),
551 // Holz ist meistens tot:
552 ({"MATGROUP_DEAD","MATGROUP_WOOD"}),
553 // Holz, Papier und Stoffe brennen:
554 ({"MATGROUP_INFLAMMABLE","MATGROUP_WOOD","MATGROUP_PAPER"}),
555 // Laubhoelzer, Nadelhoelzer und Tropenhoelzer sind Holz
556 ({"MATGROUP_WOOD","MATGROUP_TROPICAL_WOOD","MATGROUP_DECIDUOUS_WOOD",
557 "MATGROUP_CONIFER_WOOD"}),
558 // Explosive Dinge sind immer entzuendlich:
559 ({"MATGROUP_INFLAMMABLE","MATGROUP_EXPLOSIVE"})
560 });
561 i=sizeof(addgrps);
562 while(i--) {
563 int j;
564 h=addgrps[i];
565 if (member(fractions,h[0])) // Existiert schon eigener Eintrag?
566 continue; // Automatische Eintragung unnoetig
567 val=0;
568 for (j=sizeof(h)-1;j>=1;j--)
569 val+=fractions[h[j]];
570 if (!val)
571 continue;
572 if (val>100)
573 val=100;
574 else if (val<-100)
575 val=-100;
576 fractions[h[0]]=val;
577 }
578 if (fractions["MATGROUP_LIVING"]) // Im Falle von lebendem Holz, tot loeschen
579 m_delete(fractions,"MATGROUP_DEAD");
580 // Alles, was nicht als gasfoerming, fluessig oder fest eingeordnet ist, ist
581 // sonstwas:
582 if (!member(fractions, "MATGROUP_FLUID")
583 && !member(fractions, "MATGROUP_GAS")
584 && !member(fractions, "MATGROUP_SOLID"))
585 fractions["MATGROUP_MISC"]=100;
586 // Materialien als Mitglieder in die Gruppen eintragen
587 addgrps=m_indices(fractions);
588 i=sizeof(addgrps);
589 while(i--) {
590 mixed ind;
591 ind=addgrps[i];
592 if (!fractions[ind] || !member(groups, ind)) {
593 // Unbekannte Gruppe und Gruppe ohne Anteil aus Mapping loeschen
594 m_delete(fractions,ind);
595 continue;
596 }
597 if (!pointerp(h=groups[ind][P_MEMBERS]))
598 h=({});
599 h+=({mat_id});
600 groups[ind][P_MEMBERS]=h;
601 }
602 mat[P_MG_FRACTIONS] = fractions;
603}
604
605//==================== Einlesen der Mappings aus Dateien
606
607#define MDESC_ERROR(x, y) LOG_ERROR(sprintf("Materialbeschreibung '%s': %s\n", x, y))
608#define MDESC_WARN(x, y) //LOG_WARN(sprintf("Materialbeschreibung '%s': %s\n", x, y))
609
610private mapping getDescParts(string s) {
611 string* lines;
612 string key, val;
613 int i, n;
614 mapping m;
615 m = ([]);
616 val = "";
617 lines = explode(s, "\n");
618 n = sizeof(lines);
619 if (n > 0) {
620 while (i < n) {
621 if (sscanf(lines[i], "%s:", key)) {
622 status multiline;
623 multiline = 0;
624 // Schluessel gefunden, Wert auslesen
625 while ( (++i < n) && sizeof(lines[i])) {
626 // Mehrzeilige Werte mit newline verketten
627 if (multiline) {
628 val += "\n";
629 }
630 val += lines[i];
631 multiline = 1;
632 }
633 m += ([key:val]);
634 val = "";
635 }
636 i++;
637 }
638 }
639 return m;
640}
641private varargs int isFile(string fn, string path) {
642 if (stringp(path) && sizeof(path))
643 fn = path+"/"+fn;
644 return (file_size(fn) >= 0);
645}
646
647private varargs mixed readGroupDesc(string id) {
648 mixed m;
649 string fn;
650 fn = MAT_DIR+"/groups/"+id;
651 if (file_size(fn) > 0) {
652 mapping parts;
653 string desc;
654 parts = getDescParts(read_file(fn));
655 m = ([P_NAME:parts["Name"],
656 P_MEMBERS:({})]);
657 if (member(parts,"Beschreibung"))
658 m += ([P_DESCRIPTION:parts["Beschreibung"]]);
659 if (parts["Gruppenid"] != id)
660 LOG_WARN(sprintf("Unstimmigkeit Gruppenid bei '%s'\n", id));
661 } else {
662 LOG_ERROR(sprintf("Kann Gruppenbeschreibung %s nicht laden\n", fn));
663 }
664 return m;
665}
666
667private mapping convMatId(string s) {
668 mapping m;
669 string* parts;
670 parts = explode(s, "\"");
671 if (sizeof(parts)) {
672 int ende;
673 ende = strstr(parts[0]," ")-1;
674 if (ende < 0)
675 ende = sizeof(parts[0]);
676 m = ([P_ID:parts[0][0..ende]]);
677 if (sizeof(parts) > 1)
678 m += ([P_DEFSTR:parts[1]]);
679 }
680 return m;
681}
682private string* convMatNames(string s) {
683 string* names;
684 names = filter(explode(s, "\""),
685 lambda( ({'x}), ({#'>, ({#'sizeof, 'x}), 1}) ));
686 if (sizeof(names)<1)
687 names=0;
688 else {
689 if (sizeof(names)<2)
690 names+=({names[0]+"s"});
691 if (sizeof(names)<3)
692 names+=({names[0]});
693 if (sizeof(names)<4)
694 names+=({names[0]});
695 }
696 return names;
697}
698private int convMatGender(string s) {
699 int gender;
700 s = lowerstring(s);
701 // Ein Buchstabe reicht zur Bestimmung. Wenn nur weiblich|female
702 // bzw. maennlich|male verwendet wird. Dabei ist dann allerdings die
703 // Reihenfolge der Auswertung wichtig, damit das m bei MALE nicht mehr bei
704 // female passt.
705 if (sizeof(regexp( ({s}), "f|w"))) {
706 gender = FEMALE;
707 } else if (sizeof(regexp( ({s}), "m"))) {
708 gender = MALE;
709 } else {
710 gender = NEUTER;
711 }
712 return gender;
713}
714private string convMatDesc(string s) {
715 if (sizeof(regexp( ({s}), "- nicht vorhanden -"))) {
716 s = 0;
717 } else {
718 // Mehrzeilige Beschreibungen zu einer Zeile zusammenfassen
719 s = implode(explode(s, "\n"), " ");
720 }
721 return s;
722}
723private void addRecocLine(string s, mixed* r) {
724 // Die weitere Bewertung der Schwierigkeit kann erst vorgenommen werden,
725 // wenn alle Materialien bekannt sind und passiert spaeter. Zuerst werden
726 // nur die Elemente des Arrays konvertiert und eingetragen
727 string mat;
728 int val;
729 if (sscanf(s, "%s:%d", mat, val)) {
730 r += ({mat,val});
731 } else if (sscanf(s, "%d", val)) {
732 r += ({val});
733 } else {
734 r += ({s});
735 }
736}
737private mixed convMatRec(string s) {
738 mixed difficulties;
739 if (sizeof(regexp( ({s}), "- keine Einschraenkung -"))) {
740 difficulties = 0;
741 } else {
742 difficulties = ({});
743 // Jede Zeile enthaelt eine Bedingung
744 map(explode(s, "\n"), #'addRecocLine, &difficulties);
745 }
746 return difficulties;
747}
748private void addGroupLine(string s, mapping g) {
749 // Die weitere Bewertung der Zugehoerigkeit passiert spaeter.
750 string grp;
751 int val;
752 if (sscanf(s, "%s:%d", grp, val)) {
753 g += ([grp:val]);
754 } else {
755 g += ([grp:100]);
756 }
757}
758private mapping convMatGroups(string s) {
759 mapping groups;
760 if (!sizeof(regexp( ({s}), "- keine -"))) {
761 groups = ([]);
762 // Jede Zeile enthaelt eine Bedingung
763 map(explode(s, "\n"), #'addGroupLine, groups);
764 }
765 return groups;
766}
767private mapping convMaterialDesc(string id, mapping desc) {
768 /* Struktur Materialmapping:
769 P_GENDER,
770 P_NAME:({name_nom, name_gen, name_dativ, name_akkusativ}),
771 (P_RECOC:({mat1,faehigkeit1,mat2,faehigkeit2,...}),)
772 (P_DEFSTR: bei bedarf),
773 P_DESCRIPTION,
774 (grupp1:anteil1,
775 gruppe2:anteil2,
776 ...)
777 */
778 mapping m;
779 mixed val, val2;
780 m = ([]);
781 // Der string fuer das #define zuerst:
782 val = convMatId(desc["Materialid"]);
783 if (mappingp(val)) {
784 if (val[P_ID] != id)
785 LOG_WARN(sprintf("Unstimmigkeit Materialid bei '%s':%O\n", id, val[P_ID]));
786 if (member(val, P_DEFSTR)) {
787 m += ([P_DEFSTR:val[P_DEFSTR]]);
788 } else {
789 // Wenn kein String fuers #define angegeben wurde, dann direkt ID verwenden
790 //m += ([P_DEFSTR:lowerstring(id)[4..]]);
791 }
792 }
793 // Die Namen
794 if (val = convMatNames(desc["Name"])) {
795 m += ([P_NAME:val]);
796 } else {
797 MDESC_WARN(id, "keine Namen");
798 m += ([P_NAME:({"", "", "", ""})]);
799 }
800 // Das Geschlecht, standard ist NEUTER
801 m += ([P_GENDER:convMatGender(desc["Geschlecht"]) ]);
802 // Die Beschreibung
803 val = convMatDesc(desc["Beschreibung"]);
804 if (sizeof(val)) {
805 m += ([P_DESCRIPTION:val]);
806 } else {
807 MDESC_WARN(id, "keine Beschreibung");
808 }
809 // Die Erkennbarkeit
810 val = convMatRec(desc["Erkennbarkeit"]);
811 if (sizeof(val)) {
812 m += ([P_RECOC:val]);
813 }
814 // und zum Schluss die Gruppenzugehoerigkeit
815 val = convMatGroups(desc["Gruppenzugehoerigkeit"]);
816 if (mappingp(val) && sizeof(val)) {
817 m += ([P_MG_FRACTIONS:val]);
818 }
819 return m;
820}
821private varargs mixed readMaterialDesc(string id) {
822 mixed m;
823 string fn;
824 fn = MAT_DIR+"/materials/"+id;
825 if (file_size(fn) > 0) {
826 mapping parts;
827 string desc;
828 parts = getDescParts(read_file(fn));
829 m = convMaterialDesc(id, parts);
830 } else {
831 LOG_ERROR(sprintf("MDB:Kann Materialbeschreibung %s nicht laden\n", fn));
832 }
833 return m;
834}
835
836public int GetUpdateTicks() {
837 return updateTicks;
838}
839
840private void scanFinished() {
841 isScanning = 0;
842 initialized = 1;
843 // Mappings umkopieren
844 materials = new_materials;
845 material_groups = new_material_groups;
846 // Letzter Schritt: Mapping mit alten Schluesseln anlegen
847 old_mat_keys = mkmapping(map(m_indices(materials), #'matKey2Defstr, materials),
848 m_indices(materials));
849 // Generieren der Doku und des Materialheaders
850 GenHeaderFile();
851 GenMatList();
852 GenMatGroupList();
853 // Savefile schreiben
854 save_object(SAVEFILE);
855}
856
857public int IsScanning() {
858 return isScanning;
859}
860
861private varargs void doScanMaterials(string* mats, int i, int step) {
862 int ticks, start;
863 string matid;
864 start = get_eval_cost();
865 if (step < 2) {
866 while ( (i < sizeof(mats)) &&
867 ((start - get_eval_cost()) < query_limits()[LIMIT_EVAL]/3 ) ) {
868 matid = mats[i];
869 switch (step) {
870 case 0:
871 // Erster Schritt: Einlesen der Dateien
872 new_materials[matid] = readMaterialDesc(matid);
873 break;
874 case 1:
875 // Zweiter Schritt: Bearbeiten der Erkennung und Gruppenzugehoerigkeit
876 updateGroupMembers(new_material_groups, matid, new_materials[matid]);
877 break;
878 default:
879 break;
880 }
881 i++;
882 }
883 }
884 if (i < sizeof(mats)) {
885 catch(raise_error(sprintf("MaterialDB: Initialisierung noch nicht beendet,"
886 " fehlende Materialbeschreibungen moeglich"
887 " (Phase %d:%d/%d)\n",
888 step, i, sizeof(mats)));publish);
889 call_out(#'doScanMaterials, 2, mats, i, step);
890 } else {
891 // Zweite Stufe ausloesen oder beenden
892 if (step < 1) {
893 if ((start - get_eval_cost()) < query_limits()[LIMIT_EVAL]/2 )
894 doScanMaterials(mats, 0, step+1);
895 else
896 call_out(#'doScanMaterials, 2, mats, 0, step+1);
897 }
898 else
899 scanFinished();
900 }
901 updateTicks += start - get_eval_cost();
902}
903
904private mapping ScanGroups() {
905 mapping groups;
906 string* grpfiles;
907 groups = ([]);
908 grpfiles = filter(get_dir(MAT_DIR+"/groups/MATGROUP_*"),
909 #'isFile, MAT_DIR+"/groups");
910 groups = mkmapping(grpfiles, map(grpfiles, #'readGroupDesc, 1));
911 return groups;
912}
913
914private void ScanMaterials() {
915 string *matfiles;
916 matfiles = filter(get_dir(MAT_DIR+"/materials/MAT_*"),
917 #'isFile, MAT_DIR+"/materials");
918 doScanMaterials(matfiles);
919}
920
921void Update() {
922 int start;
923 updateTicks = 0;
924 start = get_eval_cost();
925 if (!isScanning) {
926 if (sizeof(get_dir(MAT_DIR))) {
927 isScanning = 1;
928 new_material_groups = ScanGroups();
929 new_materials = ([]);
930 updateTicks = start - get_eval_cost();
931 ScanMaterials();
932 } else {
933 LOG_ERROR("Kann Materialverzeichnis nicht finden, keine Materialien angelegt!\n");
934 }
935 }
936}