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