Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/secure/krautmaster.c b/secure/krautmaster.c
new file mode 100644
index 0000000..0d0ab06
--- /dev/null
+++ b/secure/krautmaster.c
@@ -0,0 +1,1289 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_inherit, no_clone, no_shadow
+
+#include <defines.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <items/kraeuter/kraeuter.h>
+#include <items/kraeuter/trankattribute.h>
+#include <living/comm.h>
+
+
+#ifndef BS
+#define BS(x) break_string(x, 78)
+#endif
+
+// Weiter unten beim Parsen des Datenfiles werden SetProp() und Name()
+// verwendet, daher erben wir thing.
+inherit "/std/secure_thing";
+
+// Liste aller moeglichen Zutaten, gemappt ueber einen key
+// AN: irritierender Name, denn es sind nicht direkt die Zutaten enthalten
+// sondern die ueber einen Key identifizierten Kraeutergruppen, die die
+// Wirkungen/Funktionen festlegen.
+// Format:
+// ([ key : ({ attr_pos, attr_neg1, attr_neg2, Haltbark., Wirkdauer,
+// Unterstuetzung, Stoerung, Haeufigkeit }) ])
+// Beispiel: [( "a7":({({2,400}),({11,150}),0,172800,0,({"n*"}),0,1}) ])
+// HU: War ein Mapping, bleibt ein Mapping, und neu: Die Eintraege werden
+// auch Mappings!
+private mapping ingredients;
+
+// Mapping von id zum Key der Zutaten im ingredients-mapping: id2key[id]=key
+// Die beiden Listen werden benoetigt, um zu den Kraut-IDs die jeweiligen
+// Keys zu finden.
+// HU: KEIN MAPPING. Ein Array von Strings. Aber das geht natuerlich.
+private string *id2key;
+
+// Mapping von key zu Kraeuter-ID der Zutaten.
+private mapping key2id;
+
+// Eigenschaften der Kraeuter:
+// krautprops[id]=({ID, Demon, Gender, Name, Adjektiv(e), P_LONG, Raumdetail})
+// krautprops[0] = 0; ID 0 darf nicht existieren
+private mixed *krautprops;
+
+// hier wird gespeichert welche Version einer Zutat fuer einen Spieler ist.
+// AN/TODO: Wenn das fuer Nichtseher nutzbar sein soll, sollte hier besser
+// mit getuuid() gearbeitet werden, statt getuid() zu verwenden.
+// player enthaelt fuer jede Spieler-UID einen Bitstring. Gesetztes Bit
+// bedeutet, dass der Spieler ueber das Kraut mit dieser ID etwas ueber
+// die Verwendung und Wirkung weiss. Es gibt jedoch noch keine Lernmoeglich-
+// keit und INSBESONDERE keine Funktionalitaet im Master, ueber die man
+// die Bits setzen koennte.
+private mapping player;
+
+// verstuemmeltes mapping fuer den VC in service (Save-Dummy)
+private mapping map_ldfied;
+
+// rooms enthaelt die Teileintraege des map_ldfied mapping vom VC
+private mapping rooms;
+
+// Enthaelt Daten zu den Raeumen, in denen das Trocknen von Kraeutern
+// moeglich ist.
+private mapping drying_data = ([:4]);
+
+string build_plantname(mixed *props);
+
+// struct-Templat fuer Trankattribute
+// Fuer das SQL-Log MUSS die Reihenfolge der Trankattribute hier genau die
+// sein, wie die Spalten in der Tabelle.
+/* Currently not used.
+struct trank_attrib_s {
+ int car;
+ int da;
+ int dm;
+ int du;
+ int dn;
+ int flt;
+ int fro;
+ int hI;
+ int hP;
+ int hK;
+ int hL;
+ int pa;
+ int pm;
+ int pu;
+ int ss;
+ int sp;
+ int sd;
+};
+*/
+
+#define allowed() (!process_call() && \
+ IS_ARCH(RPL) && IS_ARCH(this_interactive()) )
+
+#define POTIONFILES ({ TRANKITEM })
+
+// eigenschaften im krautprops-array
+#define PROP_ID INGREDIENT_ID
+#define PROP_DEMON INGREDIENT_DEMON
+#define PROP_GENDER INGREDIENT_GENDER
+#define PROP_NAME INGREDIENT_NAME
+#define PROP_ADJ INGREDIENT_ADJ
+#define PROP_LONG INGREDIENT_LONG
+#define PROP_ROOMDETAIL INGREDIENT_ROOMDETAIL
+
+// ATTR_ is immer ein array ({ attribut, ability })
+#define ATTR_ARR_ATTRIB 0
+#define ATTR_ARR_ABILITY 1
+
+// maximal erlaubter Wert fuer eine Eigenschaft
+#define MAX_ATTR_ABILITY 999
+
+void debug(string str)
+{
+ //write("debug: "+str+"\n");
+ if (this_player() && IS_ARCH(this_player()))
+ this_player()->ReceiveMsg(str,536870912);
+}
+
+protected void create()
+{
+ seteuid(getuid());
+ // AN/TODO: Variablen oben direkt initialisieren. Pruefen, ob davon
+ // irgendwas nosave sein kann.
+ if (!restore_object(__DIR__"/ARCH/krautmaster"))
+ {
+ ingredients=([]);
+ id2key=({});
+ key2id=([]);
+ krautprops=({});
+ player=([]);
+ map_ldfied=([]);
+ rooms=([]);
+ }
+ if (sl_open("/log/ARCH/krauttrank.sqlite") != 1)
+ {
+ raise_error("Datenbank konnte nicht geoeffnet werden.\n");
+ }
+ sl_exec("CREATE TABLE IF NOT EXISTS traenke(id INTEGER PRIMARY KEY, "
+ "uid TEXT NOT NULL, rnd INTEGER, "
+ "time INTEGER DEFAULT CURRENT_TIMESTAMP, "
+ "receipe TEXT NOT NULL, "
+ "quality TEXT NOT NULL, "
+ "car INTEGER, "
+ "da INTEGER, "
+ "dm INTEGER, "
+ "du INTEGER, "
+ "dn INTEGER, "
+ "flt INTEGER, "
+ "fro INTEGER, "
+ "hI INTEGER, "
+ "hP INTEGER, "
+ "hK INTEGER, "
+ "hL INTEGER, "
+ "pa INTEGER, "
+ "pm INTEGER, "
+ "pu INTEGER, "
+ "ss INTEGER, "
+ "sp INTEGER, "
+ "sd INTEGER);"
+ );
+ sl_exec("CREATE INDEX IF NOT EXISTS idx_uid ON traenke(uid);");
+ sl_exec("CREATE INDEX IF NOT EXISTS idx_receipe ON traenke(receipe);");
+ sl_exec("CREATE TABLE IF NOT EXISTS rohdaten(id INTEGER PRIMARY KEY, "
+ "uid TEXT NOT NULL, rnd INTEGER, "
+ "time INTEGER DEFAULT CURRENT_TIMESTAMP, "
+ "receipe TEXT NOT NULL, "
+ "quality TEXT NOT NULL, "
+ "car INTEGER, "
+ "da INTEGER, "
+ "dm INTEGER, "
+ "du INTEGER, "
+ "dn INTEGER, "
+ "flt INTEGER, "
+ "fro INTEGER, "
+ "hI INTEGER, "
+ "hP INTEGER, "
+ "hK INTEGER, "
+ "hL INTEGER, "
+ "pa INTEGER, "
+ "pm INTEGER, "
+ "pu INTEGER, "
+ "ss INTEGER, "
+ "sp INTEGER, "
+ "sd INTEGER);"
+ );
+ sl_exec("CREATE INDEX IF NOT EXISTS idx_uid_r ON rohdaten(uid);");
+}
+
+public string QueryPlantFile(int id)
+{
+ if (member(krautprops, id))
+ return build_plantname(krautprops[id]);
+ return 0;
+}
+
+// AN: Funktion ermittelt, ob ein Spieler pl das Kraut mit der ID id
+// verwenden kann. Laut Kommentar unten muss man dafuer wohl was ueber das
+// Kraut gelernt haben, von wem ist mir gerade noch nicht klar.
+// AN/TODO: Es ist bisher keine Funktionalitaet vorhanden, um die IDs fuer
+// den Spieler freizuschalten. Die Funktionsweise muss aus dem Beispielcode
+// unten fuer das Learn-On-Demand abgelesen werden.
+nomask int CanUseIngredient(object pl, int id)
+{
+ // Ich mach mal den harten Weg. -- Humni
+ return 1;
+ // Bitstring des Spielers aus der Liste auslesen.
+ string str=player[getuid(pl)];
+ if (!stringp(str)) str="";
+ if (test_bit(str, id))
+ return 1; // make the common case fast
+ else {
+ // letztenendes habe ich mich entschlossen einzubauen, das man nur die
+ // Kraeuter verwenden kann, ueber die man schonmal etwas gelesen/gelernt
+ // hat, aus diesem Grund, ist folgender Block auskommentiert.
+ // Dieser Block bedeutet quasi ein "auto learning on demand" d.h.
+ // wird ein Kraut verwendet wird geprueft ob fuer diese Gruppe bereits
+ // eine ID freigeschaltet wurde - ansonsten wird eine freigeschaltet.
+ /*
+ // pruefen ob fuer den Key bereits ein Bit gesetzt ist, ggf. setzen
+ if (id>=sizeof(id2key)) return 0; // illegale Id
+ int idlist=key2id[id2key[id]];
+ int i;
+ for (i=sizeof(idlist)-1; i>=0; i--) {
+ if (test_bit(str, idlist[i])) return 0; // Key bereits vorhanden
+ }
+ // Zufaellig ein Bit fuer den Key setzen
+ i=random(sizeof(idlist));
+ player[getuid(pl)]=set_bit(str, idlist[i]);
+ save_object(object_name());
+ return (i==id);
+ */
+ return 0;
+ }
+}
+
+// Diese Funktion wird vom Metzelorakel aufgerufen, um die Belohnungen zu
+// erzeugen, die man dort fuer erfolgreich absolvierte Metzelauftraege erhaelt
+#define ALLOWED_CALLER ({ "/d/ebene/arathorn/orakel/secure/schamane" })
+object get_plant_by_category(int npc_class)
+{
+ if ( member(ALLOWED_CALLER, load_name(previous_object()))<0 )
+ raise_error("unauthorised call to get_plant_by_category()\n");
+
+ // Uebergebene NPC-Klasse in Kraut-Kategorie umsetzen.
+ // Kategorie 7 wird als 7-9 interpretiert (siehe unten).
+ int category;
+ switch(npc_class) {
+ case 1: case 2: category=4; break;
+ case 3: case 4: category=5; break;
+ case 5: case 6: case 7: category=6; break;
+ default: category=7; break;
+ }
+
+ // Alle Kraeuter der ermittelten Kategorie raussuchen. Bei Kategorie 7
+ // werden auch alle aus 8 und 9 dazugenommen.
+ int *eligible_plant_ids=({});
+ foreach( string grp, mapping grpdata : ingredients ) {
+ if ( category == 7 && grpdata[T_ABUNDANCE]>=7 ||
+ category == grpdata[T_ABUNDANCE] )
+ eligible_plant_ids += key2id[grp];
+ }
+
+ // Krautnamen zu den Kraut-IDs ermitteln.
+ string *plantfiles=map(eligible_plant_ids, function string (int plantid) {
+ return build_plantname(krautprops[plantid]);});
+
+ // Ein Kraut zufaellig auswaehlen, clonen und das Objekt zurueckgeben.
+ object plant=clone_object(PLANTDIR+plantfiles[random(sizeof(plantfiles))]);
+ plant->DryPlant(80+random(11));
+ // Aufschreiben, wer welches Kraut mit welcher Qualitaet rausbekommt.
+ log_file("ARCH/plant_by_category",
+ sprintf("%s %-12s %-s Qual %d Kat %d Class %d\n",
+ strftime("%x %X",time()), getuid(PL),
+ object_name(plant)[sizeof(PLANTDIR)..],
+ plant->QueryProp(P_QUALITY), category, npc_class));
+// sprintf("%24s: call from %O, player: %s (PL: %O), kategory: %d\n", ctime(),
+// previous_object(), getuid(player), PL, kategory))
+ return plant;
+}
+
+private nosave object simul_efun, plantvc;
+
+// fuer SIMUL_EFUN_FILE
+#include <config.h>
+
+// AN/TODO: Klaeren, warum hier eine eigene Funktion get_cloner() existiert,
+// wo doch der Kraeuter-VC schon eine hat.
+nomask private string get_cloner()
+{
+ int i;
+ object po;
+ if (!simul_efun) {
+ if (!(simul_efun=find_object(SIMUL_EFUN_FILE)))
+ simul_efun=find_object(SPARE_SIMUL_EFUN_FILE);
+ }
+ // wenn sie jetzt nicht existiert - auch gut, dann gibt es halt keine
+ // sefuns.
+
+ if (!plantvc) plantvc=find_object(KRAEUTERVC);
+
+ for (i=0; po=previous_object(i); i++) {
+ if (po==master() || po==simul_efun || po==ME ||
+ po==previous_object() || po==plantvc)
+ continue;
+ else return object_name(po);
+ }
+ return 0;
+}
+
+// AN:
+nomask string CheckPlant(int id)
+{
+ if (id<=0 || id>=sizeof(id2key)) return 0;
+ if (!stringp(id2key[id])) return 0;
+ return get_cloner();
+}
+
+// ueber diese Funktion kann die Liste der Id's updatet werden
+// es wird <filename> eingelesen und durchgeparst.
+// Diese Datei muss in jeder Zeile folgendes Format einhalten und darf keine
+// leerzeilen enthalten! > Id,key,Gender,Name,Adjektiv
+// AN: Das ist Quatsch. Das Format muss so aussehen:
+// String-ID;Kraut-ID;demon;gender;P_NAME;adjektiv;P_LONG;roomdetail;
+// HU: Diese Funktion lass ich so. Harhar.
+// Update: Na gut. Fast.
+nomask private int LoadIdList(string filename)
+{
+ int i, id, si, demon;
+ string *lines, file;
+ mixed *data;
+ file=read_file(filename);
+ if (!stringp(file)) raise_error("Missing File: "+filename+"\n");
+ lines=explode(read_file(filename), "\n");
+ si=sizeof(lines)-1;
+ // AN/TODO: Warum verwendet man hier nicht auch einfach explode()?
+ // Wenn "##ende##" als Trennzeichen enthalten ist, liefert explode()
+ // als letzten Eintrag einen Leerstring, darauf kann man pruefen.
+ // Allerdings muesste das vor dem explode() zur Zeilentrennung passieren.
+ for ( ;si>=0; si--)
+ {
+ string lili=lines[si];
+ if (strstr(lines[si],"##ende##")>=0) break;
+ }
+ si--;
+ if (si<0) raise_error("##ende## not found in id-list.\n");
+ id2key=allocate(si+2);
+ krautprops=allocate(si+2);
+ // AN: Fuer das Auslesen der Werte aus dem Array "data" muessen die
+ // Indizierungs-Defines jeweils +1 verwendet werden, weil im Datenfile
+ // schlauerweise ein Datenfeld *vorne* angefuegt wurde.
+ // AN/TODO: vielleicht sollte man besser Element 0 zuerst auslesen und
+ // dann das Array arr um ein Element kuerzen: arr = arr[1..];
+ for (i=si; i>=0; i--) {
+ data=explode(lines[i], ";");
+ if (sizeof(data)!=8) raise_error("Wrong id-list format in line "+(i+1)+"\n");
+ id=to_int(data[PROP_ID+1]);
+ data[PROP_ID+1]=id;
+ if (id>si+1) raise_error(sprintf("Line %d: id %d greater than array size %d.\n", i, id, si));
+ id2key[id]=data[0];
+ // AN: Ich sehe noch nicht so ganz, warum man von dem Datenfeld
+ // PROP_DEMON nur das letzte Zeichen als Literal pruefen sollte.
+ // Komplett geht doch genausogut?
+ switch(data[PROP_DEMON+1][<1]) {
+ case 'R': data[PROP_DEMON+1]=RAW; break;
+ case '0': data[PROP_DEMON+1]=0; break;
+ case '1': data[PROP_DEMON+1]=1; break;
+ case '2': data[PROP_DEMON+1]=2; break;
+ default: raise_error("Unknown demonformat '"+data[PROP_DEMON+1]+
+ "' in idlist line "+(i+1)+"\n");
+ }
+ switch(data[PROP_GENDER+1][<1]) {
+ case 'N': data[PROP_GENDER+1]=NEUTER; break;
+ case 'F': data[PROP_GENDER+1]=FEMALE; break;
+ case 'M': data[PROP_GENDER+1]=MALE; break;
+ default: raise_error("Unknown genderformat '"+data[PROP_GENDER+1]+
+ "' in idlist line "+(i+1)+"\n");
+ }
+ SetProp(P_GENDER, data[PROP_GENDER+1]);
+ SetProp(P_NAME, data[PROP_NAME+1]);
+ // AN/TODO: data[PROP_ADJ] muss man nicht unbedingt vorher auf 0 setzen
+ if (!sizeof(data[PROP_ADJ+1])) data[PROP_ADJ+1]=0;
+ if (data[PROP_ADJ+1])
+ SetProp(P_NAME_ADJ, ({"ganz normal", data[PROP_ADJ+1]}));
+ else SetProp(P_NAME_ADJ, "ganz normal");
+ SetProp(P_ARTICLE, data[PROP_DEMON+1]!=RAW);
+ demon=(data[PROP_DEMON+1]==RAW ? 0 : data[PROP_DEMON+1]);
+ // AN: Wenn keine Langbeschreibung hinterlegt wurde, wird der Name
+ // des Krautes als solche verwendet. Ebenso fuer das Raumdetail, das
+ // beim Betrachten des Krautes im Raum ausgegeben wird.
+ if (!sizeof(data[PROP_LONG+1])) {
+ data[PROP_LONG+1] = Name(WER, demon)+".\n";
+ }
+ else data[PROP_LONG+1] = BS(data[PROP_LONG+1]);
+// Humni: Offenbar kommen am Zeilenende manchmal Zeichen dazu. Ich gehe davon
+// aus, dass keine Beschreibung kuerzer als 2 Zeichen ist.
+ if (sizeof(data[PROP_ROOMDETAIL+1])<2) {
+ data[PROP_ROOMDETAIL+1] = Name(WER, demon)+".\n";
+ }
+ else data[PROP_ROOMDETAIL+1] = BS(data[PROP_ROOMDETAIL+1]);
+ krautprops[id]=data[1..];
+ }
+ SetProp(P_NAME, 0);
+ SetProp(P_NAME_ADJ, "");
+
+ // key2id-Cache neu aufbauen.
+ key2id=([]);
+ for (i=sizeof(id2key)-1; i>=0; i--) {
+ if (member(key2id, id2key[i]))
+ key2id[id2key[i]]+=({ i });
+ else key2id[id2key[i]]=({ i });
+ }
+ return 1;
+}
+
+// Hilfsfunktion wird zum einparsen benötigt
+// wandelt z.B. den string von "h 500" in ({ 3, 500 }) um
+private nomask <string|int>* buildAttrArr(string attr)
+{
+ if (!attr) return 0;
+ attr=trim(attr);
+ <string|int>* res=explode(attr, " ")-({""});
+ //debug(sprintf(" build Attr %O",res));
+ if (sizeof(res)!=2) raise_error("Wrong attrib format!\n");
+ //debug(sprintf("%O",T_KRAUT_MAP));
+ return ({T_KRAUT_MAP[res[0]],(int)res[1]});
+}
+
+// AN: Hilfsfunktion zum Einlesen der Traenkeliste.
+nomask private void LoadIndex(string filename)
+{
+ int i, j, si;
+ string *lines, file;
+ mixed *data;
+
+ file=read_file(filename);
+ if (!stringp(file)) raise_error("Missing File: "+filename+"\n");
+
+ // AN/TODO: Auch hier waere wieder die Verwendung von explode() zu
+ // erwaegen.
+ lines=explode(file, "\n")-({""});
+ si=sizeof(lines);
+ for (i=0; i<si; i++) if (lines[i]=="##start##") break;
+ i++;
+ if (i>=si) raise_error("missing ##start## in Index.\n");
+ ingredients=([]);
+ for (; i<si; i++) { // alle zeilen durchlaufen...
+ // AN/TODO: Tabulatoren als Trennzeichen finde ich irgendwie bloed.
+ // HU: Darum nun Semikolons
+ // debug("Zeile "+lines[i]);
+ data=old_explode(lines[i], ";"); // an Semikolons trennen...
+ // debug(sprintf("%O",data));
+ if (sizeof(data)!=9)
+ {
+ //debug(sprintf("%O",data));
+ raise_error("Wrong indexlist format in line "+(i+1)+"\n");
+ }
+ for (j=8; j>=0; j--) {
+ // AN/TODO: if(data[j]=="" || data[j]=="-") data[j]=0;
+ // es sind ohnehin ueberall Strings enthalten.
+ // Wir machen aus "-" oder "0" eine echte int-Null.
+ if (sizeof(data[j])==0 || data[j][0]=='-') data[j]=0;
+ }
+ // HU: Ab hier bau ich mal neu. Den Rest pack ich auskommentiert darunter, wenn jemand den alten Code
+ // nachschauen will.
+ // Ich will ein Mapping von dieser Kraeutergruppe.
+ mapping mk=([]);
+ // Dieses Mapping soll die Eintraege nun enthalten.
+ mk[T_EFFECT_DURATION]=to_int(data[5]);
+ mk[T_ABUNDANCE]=to_int(data[8]);
+ // positive Effekte aufteilen
+ //debug(sprintf("Vorposis %O - %O",mk,data));
+ if (stringp(data[1]))
+ {
+ string* posis=explode(data[1],",");
+ //debug(sprintf("%O - %O",mk,posis));
+ foreach (string q:posis) {
+ //debug(q);
+ <string|int>* arr=buildAttrArr(q);
+ //debug(sprintf("%O",arr));
+ mk[arr[0]]=mk[arr[0]]+arr[1];
+ }
+ }
+ //debug(sprintf("%O",mk));
+ // Erster Negativer Effekt
+ if (data[2]!=0)
+ {
+ <string|int>* arr=buildAttrArr(data[2]);
+ mk[arr[0]]=mk[arr[0]]-arr[1];
+ }
+ //debug(sprintf("vorneg %O",mk));
+ // Zeiter negativer Effekt
+ if (data[3]!=0)
+ {
+ <string|int>* arr=buildAttrArr(data[3]);
+ mk[arr[0]]=mk[arr[0]]-arr[1];
+ }
+ // Haltbarkeit wird umgerechnet
+ string* sti=explode(data[4]," ")-({""});
+ //debug("haltbar "+sti[0]);
+ string stt=trim(sti[0]);
+ int dur;
+ if (stt[<1..]=="d") // Tage
+ {
+ //debug("Tage");
+ // Der erste Teil ist die Dauer in Tagen.
+ dur=to_int(sti[0][..<2]);
+ dur=dur*24*60*60; // Sekunden
+ mk[T_EXPIRE]=dur;
+ }
+ if (stt[<1..]=="h") // Stunden
+ {
+ //debug("Stunden");
+ // Sonst ist es halt die Dauer in Stunden.
+ dur=to_int(sti[0][..<2]);
+ dur=dur*60*60;
+ mk[T_EXPIRE]=dur;
+ }
+ //debug("ergibt "+dur);
+ // Nun die lustigen Unterstuetzungen. Dazu benutzen wir unseren lustigen Glueckshasen.
+ // Und ein Reh.
+ string* glueckshase;
+ string reh;
+ // Alle Leerzeichen raus!
+ //debug("Rehe");
+ //debug(sprintf("Data: %O",data));
+ if (stringp(data[6]))
+ {
+ reh=(explode(data[6]," ")-({""}))[0];
+
+ glueckshase=explode(reh,",")-({});
+ mk[T_SUPPORTER]=glueckshase;
+ }
+ else
+ {
+ mk[T_SUPPORTER]=0;
+ }
+ // Nun machen wir genauso die Blockaden.
+ // Das tolle ist: Reh und Glueckshase koennen wir wiederverwenden! Das freut.
+ if (stringp(data[7]))
+ {
+ reh=(explode(data[7]," ")-({""}))[0];
+ glueckshase=explode(reh,",")-({});
+ mk[T_BLOCKING]=glueckshase;
+ }
+ else
+ {
+ mk[T_BLOCKING]=0;
+ }
+ ingredients[trim(data[0])]=mk;
+ }
+ //debug("Wuff");
+ //debug(sprintf("%O",ingredients));
+}
+
+nomask private void save()
+{
+ save_object(__DIR__"/ARCH/krautmaster");
+}
+
+// AN: erzeugt aus Namen + Adjektiv der Pflanzendaten den Objektnamen,
+// z.B. waldrebe_gemein oder ackerklee_gelb, wobei Bindestriche auch
+// durch Unterstriche ersetzt werden (acker_rettich).
+string build_plantname(mixed *props)
+{
+ string key;
+ // AN/TODO: erst PROP_NAME in key schreiben, dann ggf. PROP_ADJ dazu
+ if (sizeof(props[PROP_ADJ])>0)
+ key=lowerstring(props[PROP_NAME]+"_"+props[PROP_ADJ]);
+ else key=lowerstring(props[PROP_NAME]);
+ // AN/TODO: ersetzen durch regreplace();
+ key=implode(old_explode(key, " "), "_");
+ key=implode(old_explode(key, "-"), "_");
+ return key;
+}
+
+public void UpdateVC()
+{
+ KRAEUTERVC->update(map_ldfied);
+}
+
+// AN: Daten neu parsen
+// Nach dem Schreiben des Savefiles mittels save() wird auch das
+// Kraeuter-Headerfile vom Kraeuter-VC neu geschrieben.
+int _refresh()
+{
+ int i;
+ string key;
+ if (extern_call() && !allowed())
+ return 0;
+
+ LoadIdList(__DIR__"ARCH/kraeuterliste.dump");
+ LoadIndex(__DIR__"ARCH/kraeuterindex.dump");
+ map_ldfied=([]);
+ for (i=sizeof(krautprops)-1; i>=0; i--)
+ {
+ if (sizeof(krautprops[i])<=PROP_ROOMDETAIL) continue;
+ key = build_plantname(krautprops[i]);
+ map_ldfied[key]=({ krautprops[i], rooms[key]||([]) });
+ }
+ save();
+ UpdateVC();
+
+ // Update Headerfile mit Kraeuterliste
+ string *keys = sort_array(m_indices(map_ldfied), #'<);
+ string headerfile =
+ "// Automatisch generiertes File, nicht von Hand editieren!\n"
+ "// Erzeugendes File: "+object_name()+"\n\n"
+ "#define PLANTCOUNT "+to_string(sizeof(keys))+"\n\n"
+ +"#define PLANT(x) \"/items/kraeuter/\"+x\n\n";
+ foreach(key: keys)
+ {
+ headerfile += sprintf("#define %-30s PLANT(\"%s\")\n",
+ upperstring(key), key);
+ }
+ write_file(KRAEUTERLISTE, headerfile, 1);
+
+ write("Inputfiles parsed. Save & Headerfiles updated!\n");
+ return 1;
+}
+
+int _cloneplant(string str)
+{
+ if (allowed())
+ {
+ if (to_string(to_int(str)) == str)
+ str = QueryPlantFile(to_int(str));
+ clone_object(PLANTDIR+str)->move(this_player(), M_NOCHECK);
+ write("Kraut " + str + " geclont.\n");
+ return 1;
+ }
+ return 0;
+}
+
+#define MAX_ROOMS 10 /* kein Kraut ist in mehr als 10 Raeumen */
+// AN: Ausgabe der Kategorienliste ueber das Planttool.
+int _showplant(string str)
+{
+ int i, si, kat, secure;
+ string *list, key;
+ mixed *res, *arr;
+ mapping props;
+
+ secure=allowed();
+ notify_fail("Es gibt folgende Kraeuterkategorien:\n"
+ +" 0 - haeufig und an vielen Stellen im Mud anzufinden\n"
+ +" 1 - etwas seltener zu finden, aber immer noch leicht\n"
+ +" 2 - an wenigen gut versteckten Stellen in abgelegenen Gebieten\n"
+ +" 3 - dito, jedoch muss das Kraut durch einen NPC (XP >= 500000) bewacht sein.\n"
+ +" 4 - aeusserst selten und XP >= 1 mio\n"
+ +" 5 - aeusserst selten und XP >= 2 mio\n"
+ +" 6 - aeusserst selten und NPC bringt >= 5 Stupse\n"
+ +" 7 - aeusserst selten und NPC bringt >= 10 Stupse\n"
+ +" 8 - aeusserst selten und NPC bringt >= 20 Stupse\n"
+ +" 9 - aeusserst selten und NPC bringt >= 50 Stupse\n"
+ +"\nSyntax: showplant <kategorie>.\n");
+ kat=to_int(str);
+ if (kat<0 || kat>9) return 0;
+ if (to_string(kat)!=str) return 0;
+ list=m_indices(map_ldfied);
+ // AN: *grummel*
+ // res = allocate(MAX_ROOMS, ({}));
+ res=map(allocate(MAX_ROOMS), #'allocate); // ({ ({}) ... ({}) })
+ for (i=sizeof(list)-1; i>=0; i--) {
+ arr=map_ldfied[list[i]];
+ if (sizeof(arr)!=2) raise_error("Wrong map_ldfied-Format by "+list[i]+"\n");
+ key=id2key[arr[0][PROP_ID]];
+ if (!key) raise_error("Missing Key for id "+arr[0][PROP_ID]+"\n");
+ props=ingredients[key];
+ //if (!pointerp(props)) continue; // noch nicht eingetragen
+ //if (sizeof(props)!=8)
+ // printf("Warning: Wrong ingredient-content by "+key+"\n");
+ //debug(sprintf("%O",props));
+ if (props==0)
+ {
+ debug("Falscher Key: "+key);
+ }
+ else
+ {
+ if (props[T_ABUNDANCE]==kat)
+ {
+ si=sizeof(arr[1]);
+ if (si<MAX_ROOMS) {
+ if (stringp(arr[0][PROP_ADJ])) {
+ SetProp(P_ARTICLE, 0);
+ SetProp(P_NAME, arr[0][PROP_NAME]);
+ SetProp(P_NAME_ADJ, arr[0][PROP_ADJ]);
+ SetProp(P_GENDER, arr[0][PROP_GENDER]);
+ key=Name(WER);
+ }
+ else key=arr[0][PROP_NAME];
+ if (secure)
+ res[si]+=({ sprintf("%3d %-40s: %d\n", arr[0][PROP_ID], key, si) });
+ else res[si]+=({ sprintf("%-40s: %d\n", key, si) });
+ }
+ }
+ }
+ }
+ for (i=0; i<MAX_ROOMS; i++) {
+ sort_array(res[i], #'>);
+ filter(res[i], #'write);
+ }
+ return 1;
+}
+
+// AN: Ausgabe der Raeume, in denen die Kraeuter zu finden sind.
+// angesteuert ueber das Planttool.
+int _showrooms(string str)
+{
+ int i, j, id;
+ string *list, dummy;
+ mixed *arr;
+ if (!allowed()) return 0;
+ notify_fail("Syntax: showrooms <id> oder showrooms all\n");
+ if (str!="all") {
+ id=to_int(str);
+ if (to_string(id)!=str) return 0;
+ }
+ else id=-1;
+ list=m_indices(map_ldfied);
+ for (i=sizeof(list)-1; i>=0; i--) {
+ arr=map_ldfied[list[i]];
+ if (sizeof(arr)!=2) raise_error("Wrong map_ldfied-Format by "+list[i]+"\n");
+ if (arr[0][PROP_ID]==id || id<0) {
+ if (!sizeof(m_indices(arr[1]))) {
+ if (id>=0) write("Fuer diese Id sind keine Raeume eingetragen.\n");
+ }
+ else if (id>=0) {
+ write("Folgende Raeume sind fuer "+arr[0][PROP_ID]+" eingetragen.\n");
+ filter(map(m_indices(arr[1]), #'+, "\n"), #'write);
+ return 1;
+ }
+ else filter(map(m_indices(arr[1]), #'+, ": "+arr[0][PROP_ID]+", "+arr[0][PROP_NAME]+"\n"), #'write);
+ if (id>=0) return 1;
+ }
+ }
+ write("Fuer diese Id sind bisher keine Kraeuter eingetragen.\n");
+ return 1;
+}
+
+// Nutzung der Kraeuter in Gebieten liefert nur dann gueltige Kraeuter,
+// wenn der Raum eingetragen ist.
+int _addroom(string str)
+{
+ int id, i;
+ string *list, vc;
+
+ if (!allowed()) {
+ write("Fuer das Eintragen der Raeume wende Dich doch bitte "
+ "an einen EM.\n");
+ return 1;
+ }
+ notify_fail("Syntax: addroom <krautnummer> <filename>\n");
+ str=PL->_unparsed_args();
+ if (!str || sscanf(str, "%d %s", id, str)!=2) return 0;
+ if (str=="hier" || str=="here")
+ {
+ if (!this_player())
+ {
+ notify_fail("Kein Spielerobjekt, kann "
+ "Raum nicht identifizieren.\n");
+ return 0;
+ }
+ str=to_string(environment(this_player()));
+ }
+ if (str[<2..]==".c") str=str[0..<3]; // .c abschneiden
+ if (file_size(str+".c")<=0) {
+ list=explode(str, "/");
+ vc=implode(list[0..<2], "/")+"/virtual_compiler.c";
+ if (file_size(vc)<=0 || !call_other(vc, "Validate", list[<1])) {
+ write("No such file \""+str+"\".\n");
+ return 1;
+ }
+ }
+ if (id<=0 || id>=sizeof(id2key)) {
+ write("Illegal plantid "+id+".\n");
+ return 1;
+ }
+ list=m_indices(map_ldfied);
+ for (i=sizeof(list)-1; i>=0; i--) {
+ if (map_ldfied[list[i]][0][PROP_ID]==id) {
+ if (!member(map_ldfied[list[i]][1], str)) {
+ map_ldfied[list[i]][1]+=([ str ]);
+ rooms[list[i]]=(rooms[list[i]]||([]))+([ str ]);
+ write("Raum Erfolgreich eingetragen!\n");
+ }
+ else write("Raum bereits eingetragen.\n");
+ save();
+ _refresh();
+ return 1;
+ }
+ }
+ write("Kraut mit id "+id+" nicht gefunden.\n");
+ return 1;
+}
+
+int _delroom(string str)
+{
+ int i, done;
+ string *list;
+
+ if (!allowed()) {
+ write("Fuer das Loeschen von Raeumen wende Dich doch bitte "
+ "an einen EM.\n");
+ return 1;
+ }
+ notify_fail("Syntax: delroom <filename>.\n");
+ str=PL->_unparsed_args();
+ if (!str) return 0;
+ if (str[<2..]==".c") str=str[0..<3];
+ list=m_indices(map_ldfied); done=0;
+ for (i=sizeof(list)-1; i>=0; i--)
+ {
+ if (member(map_ldfied[list[i]][1], str)) {
+ m_delete(map_ldfied[list[i]][1], str);
+ m_delete(rooms[list[i]], str);
+ write("Raum bei id "+map_ldfied[list[i]][0][PROP_ID]
+ +" ausgetragen.\n");
+ done=1;
+ }
+ }
+ if (!done) {
+ if (file_size(str+".c")<0)
+ write("No such file \""+str+"\".\n");
+ else write("Fuer "+str+" sind keine Kraeuter eingetragen!\n");
+ }
+ else {
+ save();
+ _refresh();
+ }
+ return 1;
+}
+
+// Veranlasst den Kraeuter-VC, eine phys. Datei aus den Kraeuterdaten eines
+// Krautes zu erzeugen, falls man diese ausbeschreiben will.
+int _createfile(string str)
+{
+ int id;
+ if (!allowed()) {
+ write("Diese Funktion wurde fuer Dich nicht freigegeben!\n");
+ return 1;
+ }
+ id=to_int(str);
+ if (to_string(id)!=str || id<=0 || id>=sizeof(id2key)) {
+ write("Illegal plantid '"+str+"'.\n");
+ return 1;
+ }
+ notify_fail("Unknown Function im kraeuterVC: _createfile()\n");
+ return call_other(KRAEUTERVC, "_createfile", build_plantname(krautprops[id]));
+}
+
+// AN: Hilfsfunktionen, derzeit ebenfalls deaktiviert.
+// i = 0..7, Position des Krautes, fuer das der Aufruf erfolgt, in der
+// Liste der in den Kessel einfuellten Kraeuter.
+// keyLst ist die Liste der Kraeutergruppen, der die Kraeuter zugeordnet
+// sind.
+// An dieser Stelle kann also die Wirkung von Kraeutergruppen abgehaengt
+// werden. Unklar ist mir aktuell nur, warum diese Funktion den Parameter
+// "i" benoetigen wuerde.
+// Idee: Es soll die Entscheidung davon abhaengig gemacht werden koennen,
+// wie die Gesamtkombination aussieht, und zusaetzlich davon, aus welcher
+// Gruppe das einzelne Kraut stammt.
+nomask private int IsBlocked(int i, string *keyLst)
+{
+ return 0;
+}
+
+// AN: Diese Funktion muesste nach dem Code in make_potion() zu urteilen
+// 0 oder 1 zurueckgeben, dann wird der Eigenschaftswert der Kraeutergruppe
+// um den Faktor 1.5 verstaerkt.
+nomask private int IsBoosted(int i, string *keyLst)
+{
+ return 0;
+}
+
+#define PRNG "/std/util/rand-glfsr"
+// Individuelle Boni/Mali fuer Spieler. ploffset soll via Referenz uebergeben
+// werden und wird von der Funktion gesetzt.
+int calculate_mod(int krautindex, string plname, int ploffset)
+{
+ // Startoffset zufaellig ermittelt, aber immer gleich
+ // fuer jeden Spielernamen
+ PRNG->InitWithUUID(plname);
+ ploffset = PRNG->random(16);
+ // Jedes Kraut hat auch einen iOffset (der konstant bleibt und sich nicht
+ // aendert). Der wird auch addiert. Bei Ueberschreiten von 30 wird nach 0
+ // gewrappt.
+ // Der Offset ist dann (spieleroffset + krautindex) % 16, d.h. alle Modifikatoren werden
+ // der Reihe nach durchlaufen. So kriegt jeder Spieler - fast egal, bei welchem
+ // Offset er startet - auch der Reihe nach alle Boni+Mali.
+ int offset = ((ploffset + krautindex) % 16) * 2;
+ // Am Ende wird das ganze noch nach 85 bis 115 verlegt.
+ return offset + 85;
+}
+
+#define ZWEITIES "/secure/zweities"
+
+mapping calculate_potion(int* plantids, int* qualities, string familie)
+{
+ // Man sollte ohne die Kraeuter nicht so einfach Wirkungen beliebig
+ // berechnen koennen.
+ if (extern_call() && !ARCH_SECURITY)
+ return 0;
+
+ // Hier speichern wir unser Ergebnis bzw. unser Zwischenergebnis.
+ mapping attrib;
+ // Hier speichern wir die Wirkgruppen, die aus den Plants gezogen werden.
+ mapping* wirkungen=({});
+ // Hier speichern wir gleich schon beim Erzeugen die wichtigsten Blockaden.
+ string* unterstuetzungen=({});
+ string* blockaden=({});
+ int zufall;
+ // Die Sortierung nach PlantID ist nur fuer das Tranklog wichtig.
+ plantids = sort_array(plantids, #'<=);
+
+ // PASS 1: Pflanzen durch Wirkungen ersetzen, dabei Unterstuetzer
+ // und Blocker merken.
+ foreach (int id, int qual : mkmapping(plantids,qualities))
+ {
+ //debug(sprintf("Gehe durch Plant: %d",id));
+ string key=id2key[id];
+ // Wirkungen dieses Krauts kopieren
+ mapping ing=copy(ingredients[key]);
+ //debug(sprintf("%O",ing));
+ // Zu den Wirkungen noch den Key hinzufuegen.
+ ing["key"]=key;
+ // Die Qualitaet des Krautes wird noch mit dem spielerindividuellen
+ // Modifikator skaliert.
+ ing["quality"]=(qual * calculate_mod(id, familie, &zufall)) / 100;
+ wirkungen+=({ing});
+ if (pointerp(ing[T_SUPPORTER]))
+ {
+ foreach (string pi:ing[T_SUPPORTER])
+ {
+ unterstuetzungen+=({pi});
+ }
+ }
+ if (pointerp(ing[T_BLOCKING]))
+ {
+ foreach (string pi:ing[T_BLOCKING])
+ {
+ blockaden+=({pi});
+ }
+ }
+ debug(sprintf("Kraut %s ergibt Werte %O.",key,wirkungen));
+ }
+ // PASS 2: Jetzt die Unterstuetzungen auswerten
+ foreach (mapping mar:wirkungen)
+ {
+ foreach (string pi:unterstuetzungen)
+ {
+ // Haben wir eine Unterstuetzung?
+ if (mar["key"]==pi || ((pi[1]=='*') && (pi[0]==mar["key"][0])))
+ {
+ debug (sprintf("mar=%O, pi=%O",mar,pi));
+ // ALLE ZAHLEN mal 1.5 nehmen. Mir ist klar, dass das nun auch
+ // mit irgendwelchen Haeufigkeiten passiert, aber mal ehrlich,
+ // das ist zur Berechnung egal.
+ foreach (string kk, mixed val : &mar)
+ {
+ if (intp(val))
+ {
+ val=15*val/10;
+ }
+ }
+ }
+ }
+ }
+ // PASS 3: Jetzt die Blockaden auswerten
+ foreach (mapping mar:wirkungen)
+ {
+ foreach (string pi:blockaden)
+ {
+ // Haben wir eine Blockade?
+ if (mar["key"]==pi || ((pi[1]=='*') && (pi[0]==mar["key"][0])))
+ {
+ debug (sprintf("mar=%O, pi=%O",mar,pi));
+ // Hier werden alle Zahlen auf Null gesetzt.
+ foreach (string kk, mixed val : &mar)
+ {
+ if (intp(val))
+ {
+ val=0;
+ }
+ }
+ }
+ }
+ }
+
+ // PASS 3.5: Qualitaet der Kraeuter skalieren.
+ foreach (mapping mar:wirkungen)
+ {
+ foreach (string kk, mixed val : &mar)
+ {
+ if (intp(val) && kk!="quality")
+ {
+ val=val*mar["quality"]/100;
+ }
+ }
+ }
+
+ // PASS 4: Nun addieren wir alles auf in das Mapping attrib.
+ attrib=([]);
+ foreach (mapping mar:wirkungen)
+ {
+ foreach (string kk:mar)
+ {
+ if (intp(mar[kk]))
+ {
+ attrib[kk]=attrib[kk]+mar[kk];
+ }
+ }
+ }
+
+ // Die Wirkungsdauer ist der Durchschnitt der Wirkungsdauern
+ attrib[T_EFFECT_DURATION] /= sizeof(plantids);
+ debug(sprintf("Duration: %d\n",attrib[T_EFFECT_DURATION]));
+
+ // Die Haltbarkeit des Tranks ist die Haltbarkeit des kleinsten Krautes.
+ int dur=10000000;
+ foreach (mapping mar:wirkungen)
+ {
+ if (mar[T_EXPIRE]>0 && dur>mar[T_EXPIRE])
+ {
+ dur=mar[T_EXPIRE];
+ }
+ }
+ if (dur==10000000)
+ {
+ dur=0;
+ }
+ attrib[T_EXPIRE]=dur;
+ debug(sprintf("Expire: %d\n",dur));
+
+ int maximum=0;
+ // Effekte rausrechnen, die nicht maximal sind
+ foreach (string kk, mixed val:attrib)
+ {
+ if (member(T_KRAUT_EFFECTS,kk)>=0)
+ {
+ if (val>0 && maximum<val)
+ {
+ maximum=val;
+ }
+ }
+ }
+ // Logeintrag erstellen.
+ sl_exec("INSERT INTO rohdaten(uid, rnd, receipe, quality, car, da, dm, du, "
+ "dn, flt, fro, hI, hP, hK, hL, pa, pm, pu, ss, sp, sd) "
+ "VALUES(?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11, ?12, ?13, ?14, "
+ "?15, ?16, ?17, ?18, ?19, ?20, ?21);",
+ this_player() ? getuid(this_player()) : "<unknown>",
+ zufall,
+ implode(map(plantids, #'to_string), ", "),
+ implode(map(qualities, #'to_string), ", "),
+ attrib[T_CARRY], attrib[T_DAMAGE_ANIMALS],
+ attrib[T_DAMAGE_MAGIC], attrib[T_DAMAGE_UNDEAD],
+ attrib[T_EFFECT_DURATION], attrib[T_FLEE_TPORT],
+ attrib[T_FROG], attrib[T_HEAL_DISEASE],
+ attrib[T_HEAL_POISON], attrib[T_HEAL_SP],
+ attrib[T_HEAL_LP], attrib[T_PROTECTION_ANIMALS],
+ attrib[T_PROTECTION_MAGIC], attrib[T_PROTECTION_UNDEAD],
+ attrib[T_SA_SPEED], attrib[T_SA_SPELL_PENETRATION],
+ attrib[T_SA_DURATION]);
+
+ // Maximal zwei positive Effekte.
+ int cteff=0;
+ foreach (string kk, mixed val : &attrib)
+ {
+ if (member(T_KRAUT_EFFECTS,kk)>=0)
+ {
+ // Nur die 2 staerksten positiven Wirkungen bleiben ueber (dazu
+ // muessen sie wirklich den gleichen Zahlenwert haben, kann bei
+ // Heilungen vorkommen, sonst eher unwahrscheinlich).
+ if (val>0 && maximum>val)
+ {
+ val=0;
+ }
+ // Thresholds. Zu zu grosse Wirkungen haben die Grenze als
+ // Auswirkung. Negative Wirkungen, die -T_MINIMUM_THRESHOLD nicht
+ // ueberschreiben, fallen weg. Bei den positiven bleibt ja ohnehin nur
+ // die staerkste Wirkung ueber, da gibt es erstmal keine
+ // Mindestgroesse mehr.
+ if (val>T_MAXIMUM_THRESHOLD)
+ {
+ val=T_MAXIMUM_THRESHOLD;
+ }
+ if (val < 0 && val > -T_MINIMUM_THRESHOLD)
+ {
+ val=0;
+ }
+ if (maximum==val && val>0)
+ {
+ cteff++;
+ // Voellig willkuerlich, was hier getroffen wird. Ob reproduzierbar,
+ // vermutlich ja, aber haengt mit der Mappingstruktur zusammen.
+ // Harhar.
+ if (cteff>2)
+ {
+ val=0;
+ }
+ }
+ }
+ }
+ debug(sprintf(" TRANKERGEBNIS: %O",attrib));
+ // Logeintrag erstellen.
+ sl_exec("INSERT INTO traenke(uid, rnd, receipe, quality, car, da, dm, du, "
+ "dn, flt, fro, hI, hP, hK, hL, pa, pm, pu, ss, sp, sd) "
+ "VALUES(?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11, ?12, ?13, ?14, "
+ "?15, ?16, ?17, ?18, ?19, ?20, ?21);",
+ this_player() ? getuid(this_player()) : "<unknown>",
+ zufall,
+ implode(map(plantids, #'to_string), ", "),
+ implode(map(qualities, #'to_string), ", "),
+ attrib[T_CARRY], attrib[T_DAMAGE_ANIMALS],
+ attrib[T_DAMAGE_MAGIC], attrib[T_DAMAGE_UNDEAD],
+ attrib[T_EFFECT_DURATION], attrib[T_FLEE_TPORT],
+ attrib[T_FROG], attrib[T_HEAL_DISEASE],
+ attrib[T_HEAL_POISON], attrib[T_HEAL_SP],
+ attrib[T_HEAL_LP], attrib[T_PROTECTION_ANIMALS],
+ attrib[T_PROTECTION_MAGIC], attrib[T_PROTECTION_UNDEAD],
+ attrib[T_SA_SPEED], attrib[T_SA_SPELL_PENETRATION],
+ attrib[T_SA_DURATION]);
+
+ return attrib;
+}
+
+mapping make_potion(object* plants)
+{
+ // -> mappt nicht-Objekt zu 0, aber 0 ist auch ne gueltige PlantID. Daher
+ // müssen zerstoerten Objekte vorher raus.
+ // TODO: drauf verlassen, dass nur intakte Objekt enthalten sind?
+ if (member(plants, 0) >= 0)
+ raise_error(sprintf("make_potion() got invalid object in plant array "
+ "%.50O\n",plants));
+
+ int* plantids = (int*)plants->QueryPlantId();
+ int* qualities = (int*)plants->QueryProp(P_QUALITY);
+
+ return calculate_potion(plantids, qualities,
+ ZWEITIES->QueryFamilie(this_player()));
+}
+
+// AN: Sucht alle Pflanzen raus, in deren Namen der Suchbegriff "str"
+// vorkommt und listet diese auf. Laeuft allerdings momentan noch in einen
+// Fehler "index out of bounds", aber man muesste hier (TODO) sowieso mal
+// von explode() auf strstr() umbauen, denke ich.
+string _findplant(string str) {
+ int i, k;
+ string *ind, *tmp;
+
+ if(!str) return "";
+ write("Suche nach '"+str+"':\n\n");
+ ind = m_indices(map_ldfied);
+ for(i=0;i<sizeof(ind);i++) {
+ tmp = map_ldfied[ind[i]][0];
+ if( stringp(tmp[3]) &&
+ old_explode(lower_case(tmp[3]),str)[0] != lower_case(tmp[3])
+ ||
+ stringp(tmp[4]) &&
+ old_explode(lower_case(tmp[4]),str)[0] != lower_case(tmp[4]))
+ write(" - "+tmp[3]+
+ (stringp(tmp[4])?" ("+tmp[4]+")":"")+" - "+tmp[0]+"\n");
+ }
+
+ return "";
+}
+
+// AN: Funktion liefert das Ergebnisarray aus make_potion() fuer eine Liste
+// von Kraeutern, die als ", "-getrennte Kraut-IDs uebergeben werden muessen.
+mixed _checkTrank(string str)
+{
+ if (extern_call() && !allowed())
+ return 0;
+
+ string *ind, *args, name;
+ object *objs;
+ int k, l;
+
+ objs = ({});
+ if(!str) return "Keine Kraeuter uebergeben!";
+ ind = old_explode(str,",");
+// ind = ({"180","11","53"});
+ for(int i=0;i<sizeof(ind);i++)
+ {
+ name = build_plantname(krautprops[to_int(ind[i])]);
+ write("Input: '"+name+"' ("+ind[i]+")\n");
+ objs += ({clone_object(PLANTDIR+name)});
+ }
+ mapping ragtest = make_potion(objs);
+ objs->remove();
+/* name="";
+ for(int i=0;i<sizeof(ragtest);i++)
+ name = name + ragtest[i]+",";
+ write("Result: ({ "+name+" })\n");*/
+ return sprintf("%O\n",ragtest);
+}
+
+#define QUAL_BASE 0
+#define QUAL_RND 1
+#define DELAY_BASE 2
+#define DELAY_RND 3
+
+#define ALLOWED_DRIER "/items/kraeuter/trockner"
+
+// Liefert die Trocknungsdaten eines Raumes aus, mit denen der Kraeuter-
+// trockner dann das Kraut bearbeiten kann.
+int *QueryDryingData() {
+ // Es muss allerdings das aufrufende Objekt ein Trockner-Clone sein,
+ // der in einem der zugelassenen Raeume stehen muss.
+ // Wenn das nicht der Fall ist, wird der Trockner das leere Array, das
+ // zurueckgegeben wird, als Indiz werten, dass er im falschen Raum steht.
+ if ( objectp(previous_object()) &&
+ load_name(previous_object()) == ALLOWED_DRIER &&
+ member(drying_data, load_name(environment(previous_object()))) &&
+ clonep(previous_object()) )
+ {
+ // Raum ermitteln, Delay/Quali errechnen, Ergebnisarray zurueckgeben.
+ string where = load_name(environment(previous_object()));
+ int delay = drying_data[where,DELAY_BASE]+
+ random(drying_data[where,DELAY_RND]);
+ int qual = drying_data[where,QUAL_BASE]+
+ random(drying_data[where,QUAL_RND]);
+ return ({ delay, qual });
+ }
+ return ({});
+}
+
+// Modifizieren der Trocknungsdaten.
+// <room> muss der volle Dateiname des Raumes sein, ohne .c am Ende.
+// <values> enthaelt die vier Parameter zu dem Raum in folgender Reihenfolge:
+// ({ Quali-Basis, Quali-Zufallsanteil, Delay-Basis, Delay-Zufallsanteil })
+// Wenn <values> nicht angeben wird oder 0 ist, werden die Daten zu <room>
+// geloescht.
+int|mapping SetDryingData(string room, int* values)
+{
+ // keine Zugriffsrechte
+ if ( !allowed() )
+ return -1;
+
+ // <values> wurde nicht uebergeben? Dann Daten loeschen.
+ if ( !values )
+ {
+ m_delete(drying_data, room);
+ return 1;
+ }
+
+ // Ansonsten muessen 4 Integer-Werte als <values> uebergeben werden.
+ if ( sizeof(values) != 4 )
+ return -2;
+
+ if ( room[<2..<1] == ".c" )
+ room = room[..<3];
+
+ // Uebergebene Daten aendern direkt das Mapping der Trocknungsdaten.
+ m_add(drying_data, room, values...);
+ save();
+ return ([ room : drying_data[room,0]; drying_data[room,1];
+ drying_data[room,2]; drying_data[room,3]]);
+}
+
+varargs mapping QueryDrying()
+{
+ return (allowed() ? drying_data : ([]) );
+}
+
+varargs int remove(int silent)
+{
+ save();
+ return ::remove(silent);
+}
+
+/*
+#define DRYINGDATA "/secure/ARCH/kraeutertrocknungsdaten"
+
+private void ReadDryingData()
+{
+ mixed data = explode(read_file(DRYINGDATA), "\n")-({""});
+ foreach(string line : data)
+ {
+ if ( line[0] == '#' )
+ continue;
+ string *fields = explode(line,";");
+ fields[1..] = map(fields[1..], #'to_int);
+ m_add(tmp_drying_data, fields...);
+ }
+}*/
+