blob: 0d0ab06fa6ab8d74f2164f41dc546a6c098ff644 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001#pragma strong_types, save_types, rtt_checks
2#pragma no_inherit, no_clone, no_shadow
3
4#include <defines.h>
5#include <properties.h>
6#include <wizlevels.h>
7#include <items/kraeuter/kraeuter.h>
8#include <items/kraeuter/trankattribute.h>
9#include <living/comm.h>
10
11
12#ifndef BS
13#define BS(x) break_string(x, 78)
14#endif
15
16// Weiter unten beim Parsen des Datenfiles werden SetProp() und Name()
17// verwendet, daher erben wir thing.
18inherit "/std/secure_thing";
19
20// Liste aller moeglichen Zutaten, gemappt ueber einen key
21// AN: irritierender Name, denn es sind nicht direkt die Zutaten enthalten
22// sondern die ueber einen Key identifizierten Kraeutergruppen, die die
23// Wirkungen/Funktionen festlegen.
24// Format:
25// ([ key : ({ attr_pos, attr_neg1, attr_neg2, Haltbark., Wirkdauer,
26// Unterstuetzung, Stoerung, Haeufigkeit }) ])
27// Beispiel: [( "a7":({({2,400}),({11,150}),0,172800,0,({"n*"}),0,1}) ])
28// HU: War ein Mapping, bleibt ein Mapping, und neu: Die Eintraege werden
29// auch Mappings!
30private mapping ingredients;
31
32// Mapping von id zum Key der Zutaten im ingredients-mapping: id2key[id]=key
33// Die beiden Listen werden benoetigt, um zu den Kraut-IDs die jeweiligen
34// Keys zu finden.
35// HU: KEIN MAPPING. Ein Array von Strings. Aber das geht natuerlich.
36private string *id2key;
37
38// Mapping von key zu Kraeuter-ID der Zutaten.
39private mapping key2id;
40
41// Eigenschaften der Kraeuter:
42// krautprops[id]=({ID, Demon, Gender, Name, Adjektiv(e), P_LONG, Raumdetail})
43// krautprops[0] = 0; ID 0 darf nicht existieren
44private mixed *krautprops;
45
46// hier wird gespeichert welche Version einer Zutat fuer einen Spieler ist.
47// AN/TODO: Wenn das fuer Nichtseher nutzbar sein soll, sollte hier besser
48// mit getuuid() gearbeitet werden, statt getuid() zu verwenden.
49// player enthaelt fuer jede Spieler-UID einen Bitstring. Gesetztes Bit
50// bedeutet, dass der Spieler ueber das Kraut mit dieser ID etwas ueber
51// die Verwendung und Wirkung weiss. Es gibt jedoch noch keine Lernmoeglich-
52// keit und INSBESONDERE keine Funktionalitaet im Master, ueber die man
53// die Bits setzen koennte.
54private mapping player;
55
56// verstuemmeltes mapping fuer den VC in service (Save-Dummy)
57private mapping map_ldfied;
58
59// rooms enthaelt die Teileintraege des map_ldfied mapping vom VC
60private mapping rooms;
61
62// Enthaelt Daten zu den Raeumen, in denen das Trocknen von Kraeutern
63// moeglich ist.
64private mapping drying_data = ([:4]);
65
66string build_plantname(mixed *props);
67
68// struct-Templat fuer Trankattribute
69// Fuer das SQL-Log MUSS die Reihenfolge der Trankattribute hier genau die
70// sein, wie die Spalten in der Tabelle.
71/* Currently not used.
72struct trank_attrib_s {
73 int car;
74 int da;
75 int dm;
76 int du;
77 int dn;
78 int flt;
79 int fro;
80 int hI;
81 int hP;
82 int hK;
83 int hL;
84 int pa;
85 int pm;
86 int pu;
87 int ss;
88 int sp;
89 int sd;
90};
91*/
92
93#define allowed() (!process_call() && \
94 IS_ARCH(RPL) && IS_ARCH(this_interactive()) )
95
96#define POTIONFILES ({ TRANKITEM })
97
98// eigenschaften im krautprops-array
99#define PROP_ID INGREDIENT_ID
100#define PROP_DEMON INGREDIENT_DEMON
101#define PROP_GENDER INGREDIENT_GENDER
102#define PROP_NAME INGREDIENT_NAME
103#define PROP_ADJ INGREDIENT_ADJ
104#define PROP_LONG INGREDIENT_LONG
105#define PROP_ROOMDETAIL INGREDIENT_ROOMDETAIL
106
107// ATTR_ is immer ein array ({ attribut, ability })
108#define ATTR_ARR_ATTRIB 0
109#define ATTR_ARR_ABILITY 1
110
111// maximal erlaubter Wert fuer eine Eigenschaft
112#define MAX_ATTR_ABILITY 999
113
114void debug(string str)
115{
116 //write("debug: "+str+"\n");
117 if (this_player() && IS_ARCH(this_player()))
118 this_player()->ReceiveMsg(str,536870912);
119}
120
121protected void create()
122{
123 seteuid(getuid());
124 // AN/TODO: Variablen oben direkt initialisieren. Pruefen, ob davon
125 // irgendwas nosave sein kann.
126 if (!restore_object(__DIR__"/ARCH/krautmaster"))
127 {
128 ingredients=([]);
129 id2key=({});
130 key2id=([]);
131 krautprops=({});
132 player=([]);
133 map_ldfied=([]);
134 rooms=([]);
135 }
136 if (sl_open("/log/ARCH/krauttrank.sqlite") != 1)
137 {
138 raise_error("Datenbank konnte nicht geoeffnet werden.\n");
139 }
140 sl_exec("CREATE TABLE IF NOT EXISTS traenke(id INTEGER PRIMARY KEY, "
141 "uid TEXT NOT NULL, rnd INTEGER, "
142 "time INTEGER DEFAULT CURRENT_TIMESTAMP, "
143 "receipe TEXT NOT NULL, "
144 "quality TEXT NOT NULL, "
145 "car INTEGER, "
146 "da INTEGER, "
147 "dm INTEGER, "
148 "du INTEGER, "
149 "dn INTEGER, "
150 "flt INTEGER, "
151 "fro INTEGER, "
152 "hI INTEGER, "
153 "hP INTEGER, "
154 "hK INTEGER, "
155 "hL INTEGER, "
156 "pa INTEGER, "
157 "pm INTEGER, "
158 "pu INTEGER, "
159 "ss INTEGER, "
160 "sp INTEGER, "
161 "sd INTEGER);"
162 );
163 sl_exec("CREATE INDEX IF NOT EXISTS idx_uid ON traenke(uid);");
164 sl_exec("CREATE INDEX IF NOT EXISTS idx_receipe ON traenke(receipe);");
165 sl_exec("CREATE TABLE IF NOT EXISTS rohdaten(id INTEGER PRIMARY KEY, "
166 "uid TEXT NOT NULL, rnd INTEGER, "
167 "time INTEGER DEFAULT CURRENT_TIMESTAMP, "
168 "receipe TEXT NOT NULL, "
169 "quality TEXT NOT NULL, "
170 "car INTEGER, "
171 "da INTEGER, "
172 "dm INTEGER, "
173 "du INTEGER, "
174 "dn INTEGER, "
175 "flt INTEGER, "
176 "fro INTEGER, "
177 "hI INTEGER, "
178 "hP INTEGER, "
179 "hK INTEGER, "
180 "hL INTEGER, "
181 "pa INTEGER, "
182 "pm INTEGER, "
183 "pu INTEGER, "
184 "ss INTEGER, "
185 "sp INTEGER, "
186 "sd INTEGER);"
187 );
188 sl_exec("CREATE INDEX IF NOT EXISTS idx_uid_r ON rohdaten(uid);");
189}
190
191public string QueryPlantFile(int id)
192{
193 if (member(krautprops, id))
194 return build_plantname(krautprops[id]);
195 return 0;
196}
197
198// AN: Funktion ermittelt, ob ein Spieler pl das Kraut mit der ID id
199// verwenden kann. Laut Kommentar unten muss man dafuer wohl was ueber das
200// Kraut gelernt haben, von wem ist mir gerade noch nicht klar.
201// AN/TODO: Es ist bisher keine Funktionalitaet vorhanden, um die IDs fuer
202// den Spieler freizuschalten. Die Funktionsweise muss aus dem Beispielcode
203// unten fuer das Learn-On-Demand abgelesen werden.
204nomask int CanUseIngredient(object pl, int id)
205{
206 // Ich mach mal den harten Weg. -- Humni
207 return 1;
208 // Bitstring des Spielers aus der Liste auslesen.
209 string str=player[getuid(pl)];
210 if (!stringp(str)) str="";
211 if (test_bit(str, id))
212 return 1; // make the common case fast
213 else {
214 // letztenendes habe ich mich entschlossen einzubauen, das man nur die
215 // Kraeuter verwenden kann, ueber die man schonmal etwas gelesen/gelernt
216 // hat, aus diesem Grund, ist folgender Block auskommentiert.
217 // Dieser Block bedeutet quasi ein "auto learning on demand" d.h.
218 // wird ein Kraut verwendet wird geprueft ob fuer diese Gruppe bereits
219 // eine ID freigeschaltet wurde - ansonsten wird eine freigeschaltet.
220 /*
221 // pruefen ob fuer den Key bereits ein Bit gesetzt ist, ggf. setzen
222 if (id>=sizeof(id2key)) return 0; // illegale Id
223 int idlist=key2id[id2key[id]];
224 int i;
225 for (i=sizeof(idlist)-1; i>=0; i--) {
226 if (test_bit(str, idlist[i])) return 0; // Key bereits vorhanden
227 }
228 // Zufaellig ein Bit fuer den Key setzen
229 i=random(sizeof(idlist));
230 player[getuid(pl)]=set_bit(str, idlist[i]);
231 save_object(object_name());
232 return (i==id);
233 */
234 return 0;
235 }
236}
237
238// Diese Funktion wird vom Metzelorakel aufgerufen, um die Belohnungen zu
239// erzeugen, die man dort fuer erfolgreich absolvierte Metzelauftraege erhaelt
240#define ALLOWED_CALLER ({ "/d/ebene/arathorn/orakel/secure/schamane" })
241object get_plant_by_category(int npc_class)
242{
243 if ( member(ALLOWED_CALLER, load_name(previous_object()))<0 )
244 raise_error("unauthorised call to get_plant_by_category()\n");
245
246 // Uebergebene NPC-Klasse in Kraut-Kategorie umsetzen.
247 // Kategorie 7 wird als 7-9 interpretiert (siehe unten).
248 int category;
249 switch(npc_class) {
250 case 1: case 2: category=4; break;
251 case 3: case 4: category=5; break;
252 case 5: case 6: case 7: category=6; break;
253 default: category=7; break;
254 }
255
256 // Alle Kraeuter der ermittelten Kategorie raussuchen. Bei Kategorie 7
257 // werden auch alle aus 8 und 9 dazugenommen.
258 int *eligible_plant_ids=({});
259 foreach( string grp, mapping grpdata : ingredients ) {
260 if ( category == 7 && grpdata[T_ABUNDANCE]>=7 ||
261 category == grpdata[T_ABUNDANCE] )
262 eligible_plant_ids += key2id[grp];
263 }
264
265 // Krautnamen zu den Kraut-IDs ermitteln.
266 string *plantfiles=map(eligible_plant_ids, function string (int plantid) {
267 return build_plantname(krautprops[plantid]);});
268
269 // Ein Kraut zufaellig auswaehlen, clonen und das Objekt zurueckgeben.
270 object plant=clone_object(PLANTDIR+plantfiles[random(sizeof(plantfiles))]);
271 plant->DryPlant(80+random(11));
272 // Aufschreiben, wer welches Kraut mit welcher Qualitaet rausbekommt.
273 log_file("ARCH/plant_by_category",
274 sprintf("%s %-12s %-s Qual %d Kat %d Class %d\n",
275 strftime("%x %X",time()), getuid(PL),
276 object_name(plant)[sizeof(PLANTDIR)..],
277 plant->QueryProp(P_QUALITY), category, npc_class));
278// sprintf("%24s: call from %O, player: %s (PL: %O), kategory: %d\n", ctime(),
279// previous_object(), getuid(player), PL, kategory))
280 return plant;
281}
282
283private nosave object simul_efun, plantvc;
284
285// fuer SIMUL_EFUN_FILE
286#include <config.h>
287
288// AN/TODO: Klaeren, warum hier eine eigene Funktion get_cloner() existiert,
289// wo doch der Kraeuter-VC schon eine hat.
290nomask private string get_cloner()
291{
292 int i;
293 object po;
294 if (!simul_efun) {
295 if (!(simul_efun=find_object(SIMUL_EFUN_FILE)))
296 simul_efun=find_object(SPARE_SIMUL_EFUN_FILE);
297 }
298 // wenn sie jetzt nicht existiert - auch gut, dann gibt es halt keine
299 // sefuns.
300
301 if (!plantvc) plantvc=find_object(KRAEUTERVC);
302
303 for (i=0; po=previous_object(i); i++) {
304 if (po==master() || po==simul_efun || po==ME ||
305 po==previous_object() || po==plantvc)
306 continue;
307 else return object_name(po);
308 }
309 return 0;
310}
311
312// AN:
313nomask string CheckPlant(int id)
314{
315 if (id<=0 || id>=sizeof(id2key)) return 0;
316 if (!stringp(id2key[id])) return 0;
317 return get_cloner();
318}
319
320// ueber diese Funktion kann die Liste der Id's updatet werden
321// es wird <filename> eingelesen und durchgeparst.
322// Diese Datei muss in jeder Zeile folgendes Format einhalten und darf keine
323// leerzeilen enthalten! > Id,key,Gender,Name,Adjektiv
324// AN: Das ist Quatsch. Das Format muss so aussehen:
325// String-ID;Kraut-ID;demon;gender;P_NAME;adjektiv;P_LONG;roomdetail;
326// HU: Diese Funktion lass ich so. Harhar.
327// Update: Na gut. Fast.
328nomask private int LoadIdList(string filename)
329{
330 int i, id, si, demon;
331 string *lines, file;
332 mixed *data;
333 file=read_file(filename);
334 if (!stringp(file)) raise_error("Missing File: "+filename+"\n");
335 lines=explode(read_file(filename), "\n");
336 si=sizeof(lines)-1;
337 // AN/TODO: Warum verwendet man hier nicht auch einfach explode()?
338 // Wenn "##ende##" als Trennzeichen enthalten ist, liefert explode()
339 // als letzten Eintrag einen Leerstring, darauf kann man pruefen.
340 // Allerdings muesste das vor dem explode() zur Zeilentrennung passieren.
341 for ( ;si>=0; si--)
342 {
343 string lili=lines[si];
344 if (strstr(lines[si],"##ende##")>=0) break;
345 }
346 si--;
347 if (si<0) raise_error("##ende## not found in id-list.\n");
348 id2key=allocate(si+2);
349 krautprops=allocate(si+2);
350 // AN: Fuer das Auslesen der Werte aus dem Array "data" muessen die
351 // Indizierungs-Defines jeweils +1 verwendet werden, weil im Datenfile
352 // schlauerweise ein Datenfeld *vorne* angefuegt wurde.
353 // AN/TODO: vielleicht sollte man besser Element 0 zuerst auslesen und
354 // dann das Array arr um ein Element kuerzen: arr = arr[1..];
355 for (i=si; i>=0; i--) {
356 data=explode(lines[i], ";");
357 if (sizeof(data)!=8) raise_error("Wrong id-list format in line "+(i+1)+"\n");
358 id=to_int(data[PROP_ID+1]);
359 data[PROP_ID+1]=id;
360 if (id>si+1) raise_error(sprintf("Line %d: id %d greater than array size %d.\n", i, id, si));
361 id2key[id]=data[0];
362 // AN: Ich sehe noch nicht so ganz, warum man von dem Datenfeld
363 // PROP_DEMON nur das letzte Zeichen als Literal pruefen sollte.
364 // Komplett geht doch genausogut?
365 switch(data[PROP_DEMON+1][<1]) {
366 case 'R': data[PROP_DEMON+1]=RAW; break;
367 case '0': data[PROP_DEMON+1]=0; break;
368 case '1': data[PROP_DEMON+1]=1; break;
369 case '2': data[PROP_DEMON+1]=2; break;
370 default: raise_error("Unknown demonformat '"+data[PROP_DEMON+1]+
371 "' in idlist line "+(i+1)+"\n");
372 }
373 switch(data[PROP_GENDER+1][<1]) {
374 case 'N': data[PROP_GENDER+1]=NEUTER; break;
375 case 'F': data[PROP_GENDER+1]=FEMALE; break;
376 case 'M': data[PROP_GENDER+1]=MALE; break;
377 default: raise_error("Unknown genderformat '"+data[PROP_GENDER+1]+
378 "' in idlist line "+(i+1)+"\n");
379 }
380 SetProp(P_GENDER, data[PROP_GENDER+1]);
381 SetProp(P_NAME, data[PROP_NAME+1]);
382 // AN/TODO: data[PROP_ADJ] muss man nicht unbedingt vorher auf 0 setzen
383 if (!sizeof(data[PROP_ADJ+1])) data[PROP_ADJ+1]=0;
384 if (data[PROP_ADJ+1])
385 SetProp(P_NAME_ADJ, ({"ganz normal", data[PROP_ADJ+1]}));
386 else SetProp(P_NAME_ADJ, "ganz normal");
387 SetProp(P_ARTICLE, data[PROP_DEMON+1]!=RAW);
388 demon=(data[PROP_DEMON+1]==RAW ? 0 : data[PROP_DEMON+1]);
389 // AN: Wenn keine Langbeschreibung hinterlegt wurde, wird der Name
390 // des Krautes als solche verwendet. Ebenso fuer das Raumdetail, das
391 // beim Betrachten des Krautes im Raum ausgegeben wird.
392 if (!sizeof(data[PROP_LONG+1])) {
393 data[PROP_LONG+1] = Name(WER, demon)+".\n";
394 }
395 else data[PROP_LONG+1] = BS(data[PROP_LONG+1]);
396// Humni: Offenbar kommen am Zeilenende manchmal Zeichen dazu. Ich gehe davon
397// aus, dass keine Beschreibung kuerzer als 2 Zeichen ist.
398 if (sizeof(data[PROP_ROOMDETAIL+1])<2) {
399 data[PROP_ROOMDETAIL+1] = Name(WER, demon)+".\n";
400 }
401 else data[PROP_ROOMDETAIL+1] = BS(data[PROP_ROOMDETAIL+1]);
402 krautprops[id]=data[1..];
403 }
404 SetProp(P_NAME, 0);
405 SetProp(P_NAME_ADJ, "");
406
407 // key2id-Cache neu aufbauen.
408 key2id=([]);
409 for (i=sizeof(id2key)-1; i>=0; i--) {
410 if (member(key2id, id2key[i]))
411 key2id[id2key[i]]+=({ i });
412 else key2id[id2key[i]]=({ i });
413 }
414 return 1;
415}
416
417// Hilfsfunktion wird zum einparsen benötigt
418// wandelt z.B. den string von "h 500" in ({ 3, 500 }) um
419private nomask <string|int>* buildAttrArr(string attr)
420{
421 if (!attr) return 0;
422 attr=trim(attr);
423 <string|int>* res=explode(attr, " ")-({""});
424 //debug(sprintf(" build Attr %O",res));
425 if (sizeof(res)!=2) raise_error("Wrong attrib format!\n");
426 //debug(sprintf("%O",T_KRAUT_MAP));
427 return ({T_KRAUT_MAP[res[0]],(int)res[1]});
428}
429
430// AN: Hilfsfunktion zum Einlesen der Traenkeliste.
431nomask private void LoadIndex(string filename)
432{
433 int i, j, si;
434 string *lines, file;
435 mixed *data;
436
437 file=read_file(filename);
438 if (!stringp(file)) raise_error("Missing File: "+filename+"\n");
439
440 // AN/TODO: Auch hier waere wieder die Verwendung von explode() zu
441 // erwaegen.
442 lines=explode(file, "\n")-({""});
443 si=sizeof(lines);
444 for (i=0; i<si; i++) if (lines[i]=="##start##") break;
445 i++;
446 if (i>=si) raise_error("missing ##start## in Index.\n");
447 ingredients=([]);
448 for (; i<si; i++) { // alle zeilen durchlaufen...
449 // AN/TODO: Tabulatoren als Trennzeichen finde ich irgendwie bloed.
450 // HU: Darum nun Semikolons
451 // debug("Zeile "+lines[i]);
452 data=old_explode(lines[i], ";"); // an Semikolons trennen...
453 // debug(sprintf("%O",data));
454 if (sizeof(data)!=9)
455 {
456 //debug(sprintf("%O",data));
457 raise_error("Wrong indexlist format in line "+(i+1)+"\n");
458 }
459 for (j=8; j>=0; j--) {
460 // AN/TODO: if(data[j]=="" || data[j]=="-") data[j]=0;
461 // es sind ohnehin ueberall Strings enthalten.
462 // Wir machen aus "-" oder "0" eine echte int-Null.
463 if (sizeof(data[j])==0 || data[j][0]=='-') data[j]=0;
464 }
465 // HU: Ab hier bau ich mal neu. Den Rest pack ich auskommentiert darunter, wenn jemand den alten Code
466 // nachschauen will.
467 // Ich will ein Mapping von dieser Kraeutergruppe.
468 mapping mk=([]);
469 // Dieses Mapping soll die Eintraege nun enthalten.
470 mk[T_EFFECT_DURATION]=to_int(data[5]);
471 mk[T_ABUNDANCE]=to_int(data[8]);
472 // positive Effekte aufteilen
473 //debug(sprintf("Vorposis %O - %O",mk,data));
474 if (stringp(data[1]))
475 {
476 string* posis=explode(data[1],",");
477 //debug(sprintf("%O - %O",mk,posis));
478 foreach (string q:posis) {
479 //debug(q);
480 <string|int>* arr=buildAttrArr(q);
481 //debug(sprintf("%O",arr));
482 mk[arr[0]]=mk[arr[0]]+arr[1];
483 }
484 }
485 //debug(sprintf("%O",mk));
486 // Erster Negativer Effekt
487 if (data[2]!=0)
488 {
489 <string|int>* arr=buildAttrArr(data[2]);
490 mk[arr[0]]=mk[arr[0]]-arr[1];
491 }
492 //debug(sprintf("vorneg %O",mk));
493 // Zeiter negativer Effekt
494 if (data[3]!=0)
495 {
496 <string|int>* arr=buildAttrArr(data[3]);
497 mk[arr[0]]=mk[arr[0]]-arr[1];
498 }
499 // Haltbarkeit wird umgerechnet
500 string* sti=explode(data[4]," ")-({""});
501 //debug("haltbar "+sti[0]);
502 string stt=trim(sti[0]);
503 int dur;
504 if (stt[<1..]=="d") // Tage
505 {
506 //debug("Tage");
507 // Der erste Teil ist die Dauer in Tagen.
508 dur=to_int(sti[0][..<2]);
509 dur=dur*24*60*60; // Sekunden
510 mk[T_EXPIRE]=dur;
511 }
512 if (stt[<1..]=="h") // Stunden
513 {
514 //debug("Stunden");
515 // Sonst ist es halt die Dauer in Stunden.
516 dur=to_int(sti[0][..<2]);
517 dur=dur*60*60;
518 mk[T_EXPIRE]=dur;
519 }
520 //debug("ergibt "+dur);
521 // Nun die lustigen Unterstuetzungen. Dazu benutzen wir unseren lustigen Glueckshasen.
522 // Und ein Reh.
523 string* glueckshase;
524 string reh;
525 // Alle Leerzeichen raus!
526 //debug("Rehe");
527 //debug(sprintf("Data: %O",data));
528 if (stringp(data[6]))
529 {
530 reh=(explode(data[6]," ")-({""}))[0];
531
532 glueckshase=explode(reh,",")-({});
533 mk[T_SUPPORTER]=glueckshase;
534 }
535 else
536 {
537 mk[T_SUPPORTER]=0;
538 }
539 // Nun machen wir genauso die Blockaden.
540 // Das tolle ist: Reh und Glueckshase koennen wir wiederverwenden! Das freut.
541 if (stringp(data[7]))
542 {
543 reh=(explode(data[7]," ")-({""}))[0];
544 glueckshase=explode(reh,",")-({});
545 mk[T_BLOCKING]=glueckshase;
546 }
547 else
548 {
549 mk[T_BLOCKING]=0;
550 }
551 ingredients[trim(data[0])]=mk;
552 }
553 //debug("Wuff");
554 //debug(sprintf("%O",ingredients));
555}
556
557nomask private void save()
558{
559 save_object(__DIR__"/ARCH/krautmaster");
560}
561
562// AN: erzeugt aus Namen + Adjektiv der Pflanzendaten den Objektnamen,
563// z.B. waldrebe_gemein oder ackerklee_gelb, wobei Bindestriche auch
564// durch Unterstriche ersetzt werden (acker_rettich).
565string build_plantname(mixed *props)
566{
567 string key;
568 // AN/TODO: erst PROP_NAME in key schreiben, dann ggf. PROP_ADJ dazu
569 if (sizeof(props[PROP_ADJ])>0)
570 key=lowerstring(props[PROP_NAME]+"_"+props[PROP_ADJ]);
571 else key=lowerstring(props[PROP_NAME]);
572 // AN/TODO: ersetzen durch regreplace();
573 key=implode(old_explode(key, " "), "_");
574 key=implode(old_explode(key, "-"), "_");
575 return key;
576}
577
578public void UpdateVC()
579{
580 KRAEUTERVC->update(map_ldfied);
581}
582
583// AN: Daten neu parsen
584// Nach dem Schreiben des Savefiles mittels save() wird auch das
585// Kraeuter-Headerfile vom Kraeuter-VC neu geschrieben.
586int _refresh()
587{
588 int i;
589 string key;
590 if (extern_call() && !allowed())
591 return 0;
592
593 LoadIdList(__DIR__"ARCH/kraeuterliste.dump");
594 LoadIndex(__DIR__"ARCH/kraeuterindex.dump");
595 map_ldfied=([]);
596 for (i=sizeof(krautprops)-1; i>=0; i--)
597 {
598 if (sizeof(krautprops[i])<=PROP_ROOMDETAIL) continue;
599 key = build_plantname(krautprops[i]);
600 map_ldfied[key]=({ krautprops[i], rooms[key]||([]) });
601 }
602 save();
603 UpdateVC();
604
605 // Update Headerfile mit Kraeuterliste
606 string *keys = sort_array(m_indices(map_ldfied), #'<);
607 string headerfile =
608 "// Automatisch generiertes File, nicht von Hand editieren!\n"
609 "// Erzeugendes File: "+object_name()+"\n\n"
610 "#define PLANTCOUNT "+to_string(sizeof(keys))+"\n\n"
611 +"#define PLANT(x) \"/items/kraeuter/\"+x\n\n";
612 foreach(key: keys)
613 {
614 headerfile += sprintf("#define %-30s PLANT(\"%s\")\n",
615 upperstring(key), key);
616 }
617 write_file(KRAEUTERLISTE, headerfile, 1);
618
619 write("Inputfiles parsed. Save & Headerfiles updated!\n");
620 return 1;
621}
622
623int _cloneplant(string str)
624{
625 if (allowed())
626 {
627 if (to_string(to_int(str)) == str)
628 str = QueryPlantFile(to_int(str));
629 clone_object(PLANTDIR+str)->move(this_player(), M_NOCHECK);
630 write("Kraut " + str + " geclont.\n");
631 return 1;
632 }
633 return 0;
634}
635
636#define MAX_ROOMS 10 /* kein Kraut ist in mehr als 10 Raeumen */
637// AN: Ausgabe der Kategorienliste ueber das Planttool.
638int _showplant(string str)
639{
640 int i, si, kat, secure;
641 string *list, key;
642 mixed *res, *arr;
643 mapping props;
644
645 secure=allowed();
646 notify_fail("Es gibt folgende Kraeuterkategorien:\n"
647 +" 0 - haeufig und an vielen Stellen im Mud anzufinden\n"
648 +" 1 - etwas seltener zu finden, aber immer noch leicht\n"
649 +" 2 - an wenigen gut versteckten Stellen in abgelegenen Gebieten\n"
650 +" 3 - dito, jedoch muss das Kraut durch einen NPC (XP >= 500000) bewacht sein.\n"
651 +" 4 - aeusserst selten und XP >= 1 mio\n"
652 +" 5 - aeusserst selten und XP >= 2 mio\n"
653 +" 6 - aeusserst selten und NPC bringt >= 5 Stupse\n"
654 +" 7 - aeusserst selten und NPC bringt >= 10 Stupse\n"
655 +" 8 - aeusserst selten und NPC bringt >= 20 Stupse\n"
656 +" 9 - aeusserst selten und NPC bringt >= 50 Stupse\n"
657 +"\nSyntax: showplant <kategorie>.\n");
658 kat=to_int(str);
659 if (kat<0 || kat>9) return 0;
660 if (to_string(kat)!=str) return 0;
661 list=m_indices(map_ldfied);
662 // AN: *grummel*
663 // res = allocate(MAX_ROOMS, ({}));
664 res=map(allocate(MAX_ROOMS), #'allocate); // ({ ({}) ... ({}) })
665 for (i=sizeof(list)-1; i>=0; i--) {
666 arr=map_ldfied[list[i]];
667 if (sizeof(arr)!=2) raise_error("Wrong map_ldfied-Format by "+list[i]+"\n");
668 key=id2key[arr[0][PROP_ID]];
669 if (!key) raise_error("Missing Key for id "+arr[0][PROP_ID]+"\n");
670 props=ingredients[key];
671 //if (!pointerp(props)) continue; // noch nicht eingetragen
672 //if (sizeof(props)!=8)
673 // printf("Warning: Wrong ingredient-content by "+key+"\n");
674 //debug(sprintf("%O",props));
675 if (props==0)
676 {
677 debug("Falscher Key: "+key);
678 }
679 else
680 {
681 if (props[T_ABUNDANCE]==kat)
682 {
683 si=sizeof(arr[1]);
684 if (si<MAX_ROOMS) {
685 if (stringp(arr[0][PROP_ADJ])) {
686 SetProp(P_ARTICLE, 0);
687 SetProp(P_NAME, arr[0][PROP_NAME]);
688 SetProp(P_NAME_ADJ, arr[0][PROP_ADJ]);
689 SetProp(P_GENDER, arr[0][PROP_GENDER]);
690 key=Name(WER);
691 }
692 else key=arr[0][PROP_NAME];
693 if (secure)
694 res[si]+=({ sprintf("%3d %-40s: %d\n", arr[0][PROP_ID], key, si) });
695 else res[si]+=({ sprintf("%-40s: %d\n", key, si) });
696 }
697 }
698 }
699 }
700 for (i=0; i<MAX_ROOMS; i++) {
701 sort_array(res[i], #'>);
702 filter(res[i], #'write);
703 }
704 return 1;
705}
706
707// AN: Ausgabe der Raeume, in denen die Kraeuter zu finden sind.
708// angesteuert ueber das Planttool.
709int _showrooms(string str)
710{
711 int i, j, id;
712 string *list, dummy;
713 mixed *arr;
714 if (!allowed()) return 0;
715 notify_fail("Syntax: showrooms <id> oder showrooms all\n");
716 if (str!="all") {
717 id=to_int(str);
718 if (to_string(id)!=str) return 0;
719 }
720 else id=-1;
721 list=m_indices(map_ldfied);
722 for (i=sizeof(list)-1; i>=0; i--) {
723 arr=map_ldfied[list[i]];
724 if (sizeof(arr)!=2) raise_error("Wrong map_ldfied-Format by "+list[i]+"\n");
725 if (arr[0][PROP_ID]==id || id<0) {
726 if (!sizeof(m_indices(arr[1]))) {
727 if (id>=0) write("Fuer diese Id sind keine Raeume eingetragen.\n");
728 }
729 else if (id>=0) {
730 write("Folgende Raeume sind fuer "+arr[0][PROP_ID]+" eingetragen.\n");
731 filter(map(m_indices(arr[1]), #'+, "\n"), #'write);
732 return 1;
733 }
734 else filter(map(m_indices(arr[1]), #'+, ": "+arr[0][PROP_ID]+", "+arr[0][PROP_NAME]+"\n"), #'write);
735 if (id>=0) return 1;
736 }
737 }
738 write("Fuer diese Id sind bisher keine Kraeuter eingetragen.\n");
739 return 1;
740}
741
742// Nutzung der Kraeuter in Gebieten liefert nur dann gueltige Kraeuter,
743// wenn der Raum eingetragen ist.
744int _addroom(string str)
745{
746 int id, i;
747 string *list, vc;
748
749 if (!allowed()) {
750 write("Fuer das Eintragen der Raeume wende Dich doch bitte "
751 "an einen EM.\n");
752 return 1;
753 }
754 notify_fail("Syntax: addroom <krautnummer> <filename>\n");
755 str=PL->_unparsed_args();
756 if (!str || sscanf(str, "%d %s", id, str)!=2) return 0;
757 if (str=="hier" || str=="here")
758 {
759 if (!this_player())
760 {
761 notify_fail("Kein Spielerobjekt, kann "
762 "Raum nicht identifizieren.\n");
763 return 0;
764 }
765 str=to_string(environment(this_player()));
766 }
767 if (str[<2..]==".c") str=str[0..<3]; // .c abschneiden
768 if (file_size(str+".c")<=0) {
769 list=explode(str, "/");
770 vc=implode(list[0..<2], "/")+"/virtual_compiler.c";
771 if (file_size(vc)<=0 || !call_other(vc, "Validate", list[<1])) {
772 write("No such file \""+str+"\".\n");
773 return 1;
774 }
775 }
776 if (id<=0 || id>=sizeof(id2key)) {
777 write("Illegal plantid "+id+".\n");
778 return 1;
779 }
780 list=m_indices(map_ldfied);
781 for (i=sizeof(list)-1; i>=0; i--) {
782 if (map_ldfied[list[i]][0][PROP_ID]==id) {
783 if (!member(map_ldfied[list[i]][1], str)) {
784 map_ldfied[list[i]][1]+=([ str ]);
785 rooms[list[i]]=(rooms[list[i]]||([]))+([ str ]);
786 write("Raum Erfolgreich eingetragen!\n");
787 }
788 else write("Raum bereits eingetragen.\n");
789 save();
790 _refresh();
791 return 1;
792 }
793 }
794 write("Kraut mit id "+id+" nicht gefunden.\n");
795 return 1;
796}
797
798int _delroom(string str)
799{
800 int i, done;
801 string *list;
802
803 if (!allowed()) {
804 write("Fuer das Loeschen von Raeumen wende Dich doch bitte "
805 "an einen EM.\n");
806 return 1;
807 }
808 notify_fail("Syntax: delroom <filename>.\n");
809 str=PL->_unparsed_args();
810 if (!str) return 0;
811 if (str[<2..]==".c") str=str[0..<3];
812 list=m_indices(map_ldfied); done=0;
813 for (i=sizeof(list)-1; i>=0; i--)
814 {
815 if (member(map_ldfied[list[i]][1], str)) {
816 m_delete(map_ldfied[list[i]][1], str);
817 m_delete(rooms[list[i]], str);
818 write("Raum bei id "+map_ldfied[list[i]][0][PROP_ID]
819 +" ausgetragen.\n");
820 done=1;
821 }
822 }
823 if (!done) {
824 if (file_size(str+".c")<0)
825 write("No such file \""+str+"\".\n");
826 else write("Fuer "+str+" sind keine Kraeuter eingetragen!\n");
827 }
828 else {
829 save();
830 _refresh();
831 }
832 return 1;
833}
834
835// Veranlasst den Kraeuter-VC, eine phys. Datei aus den Kraeuterdaten eines
836// Krautes zu erzeugen, falls man diese ausbeschreiben will.
837int _createfile(string str)
838{
839 int id;
840 if (!allowed()) {
841 write("Diese Funktion wurde fuer Dich nicht freigegeben!\n");
842 return 1;
843 }
844 id=to_int(str);
845 if (to_string(id)!=str || id<=0 || id>=sizeof(id2key)) {
846 write("Illegal plantid '"+str+"'.\n");
847 return 1;
848 }
849 notify_fail("Unknown Function im kraeuterVC: _createfile()\n");
850 return call_other(KRAEUTERVC, "_createfile", build_plantname(krautprops[id]));
851}
852
853// AN: Hilfsfunktionen, derzeit ebenfalls deaktiviert.
854// i = 0..7, Position des Krautes, fuer das der Aufruf erfolgt, in der
855// Liste der in den Kessel einfuellten Kraeuter.
856// keyLst ist die Liste der Kraeutergruppen, der die Kraeuter zugeordnet
857// sind.
858// An dieser Stelle kann also die Wirkung von Kraeutergruppen abgehaengt
859// werden. Unklar ist mir aktuell nur, warum diese Funktion den Parameter
860// "i" benoetigen wuerde.
861// Idee: Es soll die Entscheidung davon abhaengig gemacht werden koennen,
862// wie die Gesamtkombination aussieht, und zusaetzlich davon, aus welcher
863// Gruppe das einzelne Kraut stammt.
864nomask private int IsBlocked(int i, string *keyLst)
865{
866 return 0;
867}
868
869// AN: Diese Funktion muesste nach dem Code in make_potion() zu urteilen
870// 0 oder 1 zurueckgeben, dann wird der Eigenschaftswert der Kraeutergruppe
871// um den Faktor 1.5 verstaerkt.
872nomask private int IsBoosted(int i, string *keyLst)
873{
874 return 0;
875}
876
877#define PRNG "/std/util/rand-glfsr"
878// Individuelle Boni/Mali fuer Spieler. ploffset soll via Referenz uebergeben
879// werden und wird von der Funktion gesetzt.
880int calculate_mod(int krautindex, string plname, int ploffset)
881{
882 // Startoffset zufaellig ermittelt, aber immer gleich
883 // fuer jeden Spielernamen
884 PRNG->InitWithUUID(plname);
885 ploffset = PRNG->random(16);
886 // Jedes Kraut hat auch einen iOffset (der konstant bleibt und sich nicht
887 // aendert). Der wird auch addiert. Bei Ueberschreiten von 30 wird nach 0
888 // gewrappt.
889 // Der Offset ist dann (spieleroffset + krautindex) % 16, d.h. alle Modifikatoren werden
890 // der Reihe nach durchlaufen. So kriegt jeder Spieler - fast egal, bei welchem
891 // Offset er startet - auch der Reihe nach alle Boni+Mali.
892 int offset = ((ploffset + krautindex) % 16) * 2;
893 // Am Ende wird das ganze noch nach 85 bis 115 verlegt.
894 return offset + 85;
895}
896
897#define ZWEITIES "/secure/zweities"
898
899mapping calculate_potion(int* plantids, int* qualities, string familie)
900{
901 // Man sollte ohne die Kraeuter nicht so einfach Wirkungen beliebig
902 // berechnen koennen.
903 if (extern_call() && !ARCH_SECURITY)
904 return 0;
905
906 // Hier speichern wir unser Ergebnis bzw. unser Zwischenergebnis.
907 mapping attrib;
908 // Hier speichern wir die Wirkgruppen, die aus den Plants gezogen werden.
909 mapping* wirkungen=({});
910 // Hier speichern wir gleich schon beim Erzeugen die wichtigsten Blockaden.
911 string* unterstuetzungen=({});
912 string* blockaden=({});
913 int zufall;
914 // Die Sortierung nach PlantID ist nur fuer das Tranklog wichtig.
915 plantids = sort_array(plantids, #'<=);
916
917 // PASS 1: Pflanzen durch Wirkungen ersetzen, dabei Unterstuetzer
918 // und Blocker merken.
919 foreach (int id, int qual : mkmapping(plantids,qualities))
920 {
921 //debug(sprintf("Gehe durch Plant: %d",id));
922 string key=id2key[id];
923 // Wirkungen dieses Krauts kopieren
924 mapping ing=copy(ingredients[key]);
925 //debug(sprintf("%O",ing));
926 // Zu den Wirkungen noch den Key hinzufuegen.
927 ing["key"]=key;
928 // Die Qualitaet des Krautes wird noch mit dem spielerindividuellen
929 // Modifikator skaliert.
930 ing["quality"]=(qual * calculate_mod(id, familie, &zufall)) / 100;
931 wirkungen+=({ing});
932 if (pointerp(ing[T_SUPPORTER]))
933 {
934 foreach (string pi:ing[T_SUPPORTER])
935 {
936 unterstuetzungen+=({pi});
937 }
938 }
939 if (pointerp(ing[T_BLOCKING]))
940 {
941 foreach (string pi:ing[T_BLOCKING])
942 {
943 blockaden+=({pi});
944 }
945 }
946 debug(sprintf("Kraut %s ergibt Werte %O.",key,wirkungen));
947 }
948 // PASS 2: Jetzt die Unterstuetzungen auswerten
949 foreach (mapping mar:wirkungen)
950 {
951 foreach (string pi:unterstuetzungen)
952 {
953 // Haben wir eine Unterstuetzung?
954 if (mar["key"]==pi || ((pi[1]=='*') && (pi[0]==mar["key"][0])))
955 {
956 debug (sprintf("mar=%O, pi=%O",mar,pi));
957 // ALLE ZAHLEN mal 1.5 nehmen. Mir ist klar, dass das nun auch
958 // mit irgendwelchen Haeufigkeiten passiert, aber mal ehrlich,
959 // das ist zur Berechnung egal.
960 foreach (string kk, mixed val : &mar)
961 {
962 if (intp(val))
963 {
964 val=15*val/10;
965 }
966 }
967 }
968 }
969 }
970 // PASS 3: Jetzt die Blockaden auswerten
971 foreach (mapping mar:wirkungen)
972 {
973 foreach (string pi:blockaden)
974 {
975 // Haben wir eine Blockade?
976 if (mar["key"]==pi || ((pi[1]=='*') && (pi[0]==mar["key"][0])))
977 {
978 debug (sprintf("mar=%O, pi=%O",mar,pi));
979 // Hier werden alle Zahlen auf Null gesetzt.
980 foreach (string kk, mixed val : &mar)
981 {
982 if (intp(val))
983 {
984 val=0;
985 }
986 }
987 }
988 }
989 }
990
991 // PASS 3.5: Qualitaet der Kraeuter skalieren.
992 foreach (mapping mar:wirkungen)
993 {
994 foreach (string kk, mixed val : &mar)
995 {
996 if (intp(val) && kk!="quality")
997 {
998 val=val*mar["quality"]/100;
999 }
1000 }
1001 }
1002
1003 // PASS 4: Nun addieren wir alles auf in das Mapping attrib.
1004 attrib=([]);
1005 foreach (mapping mar:wirkungen)
1006 {
1007 foreach (string kk:mar)
1008 {
1009 if (intp(mar[kk]))
1010 {
1011 attrib[kk]=attrib[kk]+mar[kk];
1012 }
1013 }
1014 }
1015
1016 // Die Wirkungsdauer ist der Durchschnitt der Wirkungsdauern
1017 attrib[T_EFFECT_DURATION] /= sizeof(plantids);
1018 debug(sprintf("Duration: %d\n",attrib[T_EFFECT_DURATION]));
1019
1020 // Die Haltbarkeit des Tranks ist die Haltbarkeit des kleinsten Krautes.
1021 int dur=10000000;
1022 foreach (mapping mar:wirkungen)
1023 {
1024 if (mar[T_EXPIRE]>0 && dur>mar[T_EXPIRE])
1025 {
1026 dur=mar[T_EXPIRE];
1027 }
1028 }
1029 if (dur==10000000)
1030 {
1031 dur=0;
1032 }
1033 attrib[T_EXPIRE]=dur;
1034 debug(sprintf("Expire: %d\n",dur));
1035
1036 int maximum=0;
1037 // Effekte rausrechnen, die nicht maximal sind
1038 foreach (string kk, mixed val:attrib)
1039 {
1040 if (member(T_KRAUT_EFFECTS,kk)>=0)
1041 {
1042 if (val>0 && maximum<val)
1043 {
1044 maximum=val;
1045 }
1046 }
1047 }
1048 // Logeintrag erstellen.
1049 sl_exec("INSERT INTO rohdaten(uid, rnd, receipe, quality, car, da, dm, du, "
1050 "dn, flt, fro, hI, hP, hK, hL, pa, pm, pu, ss, sp, sd) "
1051 "VALUES(?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11, ?12, ?13, ?14, "
1052 "?15, ?16, ?17, ?18, ?19, ?20, ?21);",
1053 this_player() ? getuid(this_player()) : "<unknown>",
1054 zufall,
1055 implode(map(plantids, #'to_string), ", "),
1056 implode(map(qualities, #'to_string), ", "),
1057 attrib[T_CARRY], attrib[T_DAMAGE_ANIMALS],
1058 attrib[T_DAMAGE_MAGIC], attrib[T_DAMAGE_UNDEAD],
1059 attrib[T_EFFECT_DURATION], attrib[T_FLEE_TPORT],
1060 attrib[T_FROG], attrib[T_HEAL_DISEASE],
1061 attrib[T_HEAL_POISON], attrib[T_HEAL_SP],
1062 attrib[T_HEAL_LP], attrib[T_PROTECTION_ANIMALS],
1063 attrib[T_PROTECTION_MAGIC], attrib[T_PROTECTION_UNDEAD],
1064 attrib[T_SA_SPEED], attrib[T_SA_SPELL_PENETRATION],
1065 attrib[T_SA_DURATION]);
1066
1067 // Maximal zwei positive Effekte.
1068 int cteff=0;
1069 foreach (string kk, mixed val : &attrib)
1070 {
1071 if (member(T_KRAUT_EFFECTS,kk)>=0)
1072 {
1073 // Nur die 2 staerksten positiven Wirkungen bleiben ueber (dazu
1074 // muessen sie wirklich den gleichen Zahlenwert haben, kann bei
1075 // Heilungen vorkommen, sonst eher unwahrscheinlich).
1076 if (val>0 && maximum>val)
1077 {
1078 val=0;
1079 }
1080 // Thresholds. Zu zu grosse Wirkungen haben die Grenze als
1081 // Auswirkung. Negative Wirkungen, die -T_MINIMUM_THRESHOLD nicht
1082 // ueberschreiben, fallen weg. Bei den positiven bleibt ja ohnehin nur
1083 // die staerkste Wirkung ueber, da gibt es erstmal keine
1084 // Mindestgroesse mehr.
1085 if (val>T_MAXIMUM_THRESHOLD)
1086 {
1087 val=T_MAXIMUM_THRESHOLD;
1088 }
1089 if (val < 0 && val > -T_MINIMUM_THRESHOLD)
1090 {
1091 val=0;
1092 }
1093 if (maximum==val && val>0)
1094 {
1095 cteff++;
1096 // Voellig willkuerlich, was hier getroffen wird. Ob reproduzierbar,
1097 // vermutlich ja, aber haengt mit der Mappingstruktur zusammen.
1098 // Harhar.
1099 if (cteff>2)
1100 {
1101 val=0;
1102 }
1103 }
1104 }
1105 }
1106 debug(sprintf(" TRANKERGEBNIS: %O",attrib));
1107 // Logeintrag erstellen.
1108 sl_exec("INSERT INTO traenke(uid, rnd, receipe, quality, car, da, dm, du, "
1109 "dn, flt, fro, hI, hP, hK, hL, pa, pm, pu, ss, sp, sd) "
1110 "VALUES(?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11, ?12, ?13, ?14, "
1111 "?15, ?16, ?17, ?18, ?19, ?20, ?21);",
1112 this_player() ? getuid(this_player()) : "<unknown>",
1113 zufall,
1114 implode(map(plantids, #'to_string), ", "),
1115 implode(map(qualities, #'to_string), ", "),
1116 attrib[T_CARRY], attrib[T_DAMAGE_ANIMALS],
1117 attrib[T_DAMAGE_MAGIC], attrib[T_DAMAGE_UNDEAD],
1118 attrib[T_EFFECT_DURATION], attrib[T_FLEE_TPORT],
1119 attrib[T_FROG], attrib[T_HEAL_DISEASE],
1120 attrib[T_HEAL_POISON], attrib[T_HEAL_SP],
1121 attrib[T_HEAL_LP], attrib[T_PROTECTION_ANIMALS],
1122 attrib[T_PROTECTION_MAGIC], attrib[T_PROTECTION_UNDEAD],
1123 attrib[T_SA_SPEED], attrib[T_SA_SPELL_PENETRATION],
1124 attrib[T_SA_DURATION]);
1125
1126 return attrib;
1127}
1128
1129mapping make_potion(object* plants)
1130{
1131 // -> mappt nicht-Objekt zu 0, aber 0 ist auch ne gueltige PlantID. Daher
1132 // müssen zerstoerten Objekte vorher raus.
1133 // TODO: drauf verlassen, dass nur intakte Objekt enthalten sind?
1134 if (member(plants, 0) >= 0)
1135 raise_error(sprintf("make_potion() got invalid object in plant array "
1136 "%.50O\n",plants));
1137
1138 int* plantids = (int*)plants->QueryPlantId();
1139 int* qualities = (int*)plants->QueryProp(P_QUALITY);
1140
1141 return calculate_potion(plantids, qualities,
1142 ZWEITIES->QueryFamilie(this_player()));
1143}
1144
1145// AN: Sucht alle Pflanzen raus, in deren Namen der Suchbegriff "str"
1146// vorkommt und listet diese auf. Laeuft allerdings momentan noch in einen
1147// Fehler "index out of bounds", aber man muesste hier (TODO) sowieso mal
1148// von explode() auf strstr() umbauen, denke ich.
1149string _findplant(string str) {
1150 int i, k;
1151 string *ind, *tmp;
1152
1153 if(!str) return "";
1154 write("Suche nach '"+str+"':\n\n");
1155 ind = m_indices(map_ldfied);
1156 for(i=0;i<sizeof(ind);i++) {
1157 tmp = map_ldfied[ind[i]][0];
1158 if( stringp(tmp[3]) &&
1159 old_explode(lower_case(tmp[3]),str)[0] != lower_case(tmp[3])
1160 ||
1161 stringp(tmp[4]) &&
1162 old_explode(lower_case(tmp[4]),str)[0] != lower_case(tmp[4]))
1163 write(" - "+tmp[3]+
1164 (stringp(tmp[4])?" ("+tmp[4]+")":"")+" - "+tmp[0]+"\n");
1165 }
1166
1167 return "";
1168}
1169
1170// AN: Funktion liefert das Ergebnisarray aus make_potion() fuer eine Liste
1171// von Kraeutern, die als ", "-getrennte Kraut-IDs uebergeben werden muessen.
1172mixed _checkTrank(string str)
1173{
1174 if (extern_call() && !allowed())
1175 return 0;
1176
1177 string *ind, *args, name;
1178 object *objs;
1179 int k, l;
1180
1181 objs = ({});
1182 if(!str) return "Keine Kraeuter uebergeben!";
1183 ind = old_explode(str,",");
1184// ind = ({"180","11","53"});
1185 for(int i=0;i<sizeof(ind);i++)
1186 {
1187 name = build_plantname(krautprops[to_int(ind[i])]);
1188 write("Input: '"+name+"' ("+ind[i]+")\n");
1189 objs += ({clone_object(PLANTDIR+name)});
1190 }
1191 mapping ragtest = make_potion(objs);
1192 objs->remove();
1193/* name="";
1194 for(int i=0;i<sizeof(ragtest);i++)
1195 name = name + ragtest[i]+",";
1196 write("Result: ({ "+name+" })\n");*/
1197 return sprintf("%O\n",ragtest);
1198}
1199
1200#define QUAL_BASE 0
1201#define QUAL_RND 1
1202#define DELAY_BASE 2
1203#define DELAY_RND 3
1204
1205#define ALLOWED_DRIER "/items/kraeuter/trockner"
1206
1207// Liefert die Trocknungsdaten eines Raumes aus, mit denen der Kraeuter-
1208// trockner dann das Kraut bearbeiten kann.
1209int *QueryDryingData() {
1210 // Es muss allerdings das aufrufende Objekt ein Trockner-Clone sein,
1211 // der in einem der zugelassenen Raeume stehen muss.
1212 // Wenn das nicht der Fall ist, wird der Trockner das leere Array, das
1213 // zurueckgegeben wird, als Indiz werten, dass er im falschen Raum steht.
1214 if ( objectp(previous_object()) &&
1215 load_name(previous_object()) == ALLOWED_DRIER &&
1216 member(drying_data, load_name(environment(previous_object()))) &&
1217 clonep(previous_object()) )
1218 {
1219 // Raum ermitteln, Delay/Quali errechnen, Ergebnisarray zurueckgeben.
1220 string where = load_name(environment(previous_object()));
1221 int delay = drying_data[where,DELAY_BASE]+
1222 random(drying_data[where,DELAY_RND]);
1223 int qual = drying_data[where,QUAL_BASE]+
1224 random(drying_data[where,QUAL_RND]);
1225 return ({ delay, qual });
1226 }
1227 return ({});
1228}
1229
1230// Modifizieren der Trocknungsdaten.
1231// <room> muss der volle Dateiname des Raumes sein, ohne .c am Ende.
1232// <values> enthaelt die vier Parameter zu dem Raum in folgender Reihenfolge:
1233// ({ Quali-Basis, Quali-Zufallsanteil, Delay-Basis, Delay-Zufallsanteil })
1234// Wenn <values> nicht angeben wird oder 0 ist, werden die Daten zu <room>
1235// geloescht.
1236int|mapping SetDryingData(string room, int* values)
1237{
1238 // keine Zugriffsrechte
1239 if ( !allowed() )
1240 return -1;
1241
1242 // <values> wurde nicht uebergeben? Dann Daten loeschen.
1243 if ( !values )
1244 {
1245 m_delete(drying_data, room);
1246 return 1;
1247 }
1248
1249 // Ansonsten muessen 4 Integer-Werte als <values> uebergeben werden.
1250 if ( sizeof(values) != 4 )
1251 return -2;
1252
1253 if ( room[<2..<1] == ".c" )
1254 room = room[..<3];
1255
1256 // Uebergebene Daten aendern direkt das Mapping der Trocknungsdaten.
1257 m_add(drying_data, room, values...);
1258 save();
1259 return ([ room : drying_data[room,0]; drying_data[room,1];
1260 drying_data[room,2]; drying_data[room,3]]);
1261}
1262
1263varargs mapping QueryDrying()
1264{
1265 return (allowed() ? drying_data : ([]) );
1266}
1267
1268varargs int remove(int silent)
1269{
1270 save();
1271 return ::remove(silent);
1272}
1273
1274/*
1275#define DRYINGDATA "/secure/ARCH/kraeutertrocknungsdaten"
1276
1277private void ReadDryingData()
1278{
1279 mixed data = explode(read_file(DRYINGDATA), "\n")-({""});
1280 foreach(string line : data)
1281 {
1282 if ( line[0] == '#' )
1283 continue;
1284 string *fields = explode(line,";");
1285 fields[1..] = map(fields[1..], #'to_int);
1286 m_add(tmp_drying_data, fields...);
1287 }
1288}*/
1289