blob: 3bdcfe1ebf28161931811f7a9d578ff828620ea1 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// questmaster.c -- Questmaster, verwaltet die normalen Quests und
4// die MiniQuests
5//
6// $Id: questmaster.c 9136 2015-02-03 21:39:10Z Zesstra $
7//
8#pragma strict_types
9#pragma no_clone
10#pragma no_shadow
11#pragma no_inherit
12#pragma verbose_errors
13#pragma combine_strings
14//#pragma pedantic
15//#pragma range_check
16#pragma warn_deprecated
17
18#include <config.h>
19#include "/secure/wizlevels.h"
20#include "/secure/questmaster.h"
21#include "/secure/lepmaster.h"
22#include "/secure/telnetneg.h" // P_TTY
23#include <living/description.h> // P_LEVEL
24#include <player/base.h> // P_TESTPLAYER
25#include <daemon.h>
26#include <ansi.h>
27#include <events.h>
28
29#define DEBUG(x) if (funcall(symbol_function('find_player),"arathorn"))\
30 tell_object(funcall(symbol_function('find_player),"arathorn"),\
31 "QM: "+x+"\n")
32
33#define ME this_object()
34#define PL this_player()
35
36#define MQ_DATA_POINTS 0
37#define MQ_DATA_QUESTNO 1
38#define MQ_DATA_TASKDESC 2
39#define MQ_DATA_VISIBLE 3
40#define MQ_DATA_ACTIVE 4
41#define MQ_DATA_TITLE 5
42#define MQ_DATA_DIARYTEXT 6
43#define MQ_DATA_RESTRICTIONS 7
44#define MQ_DATA_ASSIGNED_DOMAIN 8
45#define MQ_DATA_QUERY_PERMITTED 9
46
47private int max_QP = 0;
48private int opt_QP = 0;
49// Die Questliste mit allen Daten
50private mapping quests = ([]);
51
52// Das Mapping mit der MQ-Liste an sich und alle zugehoerigen Daten
53private mapping miniquests = ([]);
54// Nach MQ-Nummern indizierter Cache:
55// Struktur ([int num : ({ int stupse, string mq-object }) ])
56private nosave mapping by_num = ([]);
57// Cache der Objekte, die die MQ-Listen der Regionen abfragen duerfen
58// Struktur ([string path : ({ string mq_object }) ])
59private nosave mapping mq_query_permitted = ([]);
60// Cache fuer die MQ-Punkte von Spielern, wird fuer den jeweiligen Spieler
61// beim Abfragen von dessen MQ-Punkten gefuellt. Spielername wird bei
62// Aenderungen an seinen MQ-Punkten (Bestehen einer MQ, manuelles Setzen
63// oder Loeschen einer seiner MQs) aus dem Cache ausgetragen
64private nosave mapping users_mq = ([]);
65// letzte vergebene MQ-Indexnummer. Es darf niemals eine MQ eine Indexnummer
66// kriegen, die eine andere MQ schonmal hatte, auch wenn die geloescht wurde.
67// (Zumindest nicht, ohne die entsprechenden Bits im Spieler zu loeschen, was
68// zZ nicht passiert.
69private int last_num = 0;
70
71
72void save_info() {
73 save_object(QUESTS);
74}
75
76// Caches aufbauen.
77static void make_num(string mqob_name, int stupse, int index,
78 string taskdesc, int vis, int active, string title,
79 string donedesc, mapping restr, string domain,
80 string *permitted_objs) {
Arathornd1d78102019-09-28 17:35:35 +020081 m_add(by_num, index, ({stupse, mqob_name}));
MG Mud User88f12472016-06-24 23:31:02 +020082 foreach ( string obj: permitted_objs ) {
83 if ( member(mq_query_permitted, obj) )
84 mq_query_permitted[obj] += ({mqob_name});
85 else
86 mq_query_permitted[obj] = ({mqob_name});
87 }
88}
89
Arathornd1d78102019-09-28 17:35:35 +020090private void RebuildMQCache() {
91 by_num = ([]);
92 mq_query_permitted = ([]);
93 walk_mapping(miniquests, #'make_num /*'*/ );
94}
95
MG Mud User88f12472016-06-24 23:31:02 +020096void create() {
97 seteuid(getuid(ME));
98 if (!restore_object(QUESTS)) {
99 save_info();
100 }
101
Arathornd1d78102019-09-28 17:35:35 +0200102 RebuildMQCache();
MG Mud User88f12472016-06-24 23:31:02 +0200103 set_next_reset(43200); // Reset alle 12 Stunden.
bugfixd94d0932020-04-08 11:27:13 +0200104 ({int})EVENTD->RegisterEvent(EVT_LIB_QUEST_SOLVED,"HandleQuestSolved",
MG Mud User88f12472016-06-24 23:31:02 +0200105 ME);
106}
107
108public int remove(int silent) {
109 save_info();
bugfixd94d0932020-04-08 11:27:13 +0200110 ({int})EVENTD->UnregisterEvent(EVT_LIB_QUEST_SOLVED, ME);
MG Mud User88f12472016-06-24 23:31:02 +0200111 destruct(ME);
112 return 1;
113}
114
115// Schreibzugriff nur fuer interaktive EMs und ARCH_SECURITY.
116private int allowed_write_access() {
117 if (process_call())
118 return 0;
119 if (ARCH_SECURITY) // prueft auch this_interactive() mit.
120 return 1;
121 return 0;
122}
123
124void reset() {
125 by_num = ([]);
126 mq_query_permitted = ([]);
Arathornd1d78102019-09-28 17:35:35 +0200127 RebuildMQCache();
MG Mud User88f12472016-06-24 23:31:02 +0200128 set_next_reset(43200);
129}
130
131/*
132 * (1) ABSCHNITT "NORMALE QUESTS"
133 */
134
135/* Die Quests werden in einem Mapping gespeichert. Der Schluessel ist dabei der
136 Quest-Name, die Eintraege sind Arrays der folgenden Form:
137
138 1. Element ist die Zahl der durch diese Quest zu erwerbenden Questpunkte.
139 2. Element ist die Zahl der Erfahrungspunkte, die der Spieler bekommt,
140 wenn er diese Quest loest.
141 3. Element ist ein Array mit den Filenamen der Objekte, denen es gestattet
142 ist, diese Quest beim Player als geloest zu markieren (Erzmagier duerfen
143 das aber sowieso immer).
144 4. Element ist ein String, der die Quest kurz beschreibt. Dieser String wird
145 dem Spieler vom Orakel als Hinweis gegeben.
146 5. Element ist eine Zahl zwischen -1 und 100, die den Schwierigkeitsgrad der
147 Quest angibt, nach Einschaetzung des EM fuer Quests. Das Orakel kann dann
148 evtl. sinnige Saetze wie "Diese Quest erscheint mir aber noch recht
149 schwer fuer Dich.", oder "Hm, die haettest Du ja schon viel eher loesen
150 koennen." absondern. :)
151
152 Ein Wert von -1 bedeutet eine Seherquest. Diese zaehlt nicht zu den
153 Maximalen Questpunkten, sondern zaehlt als optionale Quest
154 6. Element ist ein Integer von 0 bis 5 und gibt die "Klasse" an;
155 ausgegeben werden dort Sternchen
156 7. Element ist ein Integer, 0 oder 1.
157 0: Quest voruebergehend deaktiviert (suspendiert)
158 1: Quest aktiviert
159 8. Element ist ein String und enthaelt den Namen des Magiers, der die
160 Quest "verbrochen" hat.
161 9. Element ist ein String, der den Namen eines Magiers enthaelt, der
162 evtl. fuer die Wartung der Quest zustaendig ist.
163 10. Element ist eine Zahl von 0 bis 4, die der Quest ein Attribut
164 gibt (0 fuer keines)
165*/
166
167// geaendert:
168// 5 == diff geht nun von -1 bis 100
169// 6 == klasse geht nun von 0 bis 5
170// 10 == attribut geht nun von 0 bis 4
171
172private int RecalculateQP() {
173 int i;
174 mixed q,n;
175
176 if (!allowed_write_access())
177 return -1;
178
179 max_QP=0;
180 opt_QP=0;
181
182 n=m_indices(quests);
183 q=m_values(quests);
184 for (i=sizeof(q)-1;i>=0;i--)
185 if (q[i][Q_ACTIVE]) {
186 if (q[i][Q_DIFF]>=0)
187 max_QP+=q[i][Q_QP];
188 if (q[i][Q_DIFF]==-1)
189 opt_QP+=q[i][Q_QP];
190 }
191
192 return max_QP+opt_QP;
193}
194
195int AddQuest(string name, int questpoints, int experience,
196 string *allowedobj, string hint, int difficulty, int questclass,
197 int active, string wiz, string scndwiz, int questattribute)
198{
199 mixed *quest;
200 int i;
201
202 if (!allowed_write_access()) return 0;
203 if (!stringp(name) || sizeof(name)<5) return -1;
204 if (questpoints<1) return -2;
205 if (!intp(experience)) return -3;
206 if (!pointerp(allowedobj)) return -4;
207 for (i=sizeof(allowedobj)-1;i>=0;i--)
208 {
209 if (!stringp(allowedobj[i]) || allowedobj[i]=="") return -4;
Vanion50652322020-03-10 21:13:25 +0100210 allowedobj[i]=({string})master()->make_path_absolute(allowedobj[i]);
MG Mud User88f12472016-06-24 23:31:02 +0200211 }
212 if (!stringp(hint) || hint=="") return -5;
213 if (difficulty<-1 || difficulty>100) return -6;
214 if (questclass<0 || questclass>5) return -11;
215 if (active<0 || active>1) return -7;
216 if (!stringp(wiz) || wiz=="" ||
217 file_size("/players/"+(wiz=lower_case(wiz))) != -2) return -8;
218 if (!stringp(scndwiz))
219 scndwiz="";
220 else if (file_size("/players/"+(scndwiz=lower_case(scndwiz))) != -2)
221 return -9;
222 if (questattribute<0 || questattribute>4)
223 return -10;
224
225 if(quests[name]&&(quests[name][5]==0||quests[name][5]==1)&&quests[name][6])
226 max_QP-=quests[name][0];
227
228 quests+=([name: ({questpoints,experience,allowedobj,hint,difficulty,
229 questclass,active,wiz, scndwiz,questattribute,
230 ({0.0,0}) }) ]);
231 RecalculateQP();
232 save_info();
233 QMLOG(sprintf("add: %s %O (%s)",name,quests[name],
234 getuid(this_interactive())));
235 return 1;
236}
237
238int RemoveQuest(string name) {
239 mixed *quest;
240
241 if (!allowed_write_access()) return 0;
242 if (!quests[name]) return -1;
243 QMLOG(sprintf("remove: %s %O (%s)",name,quests[name],
244 getuid(this_interactive())));
245 m_delete(quests,name);
246 RecalculateQP();
247 save_info();
248 return 1;
249}
250
251int QueryNeededQP() {
252 return REQ_QP;
253}
254
255int QueryMaxQP() {
256 return max_QP;
257}
258
259int QueryOptQP() {
260 return opt_QP;
261}
262
263int QueryTotalQP() {
264 return max_QP+opt_QP;
265}
266
267mixed *QueryGroupedKeys() {
268 string *qliste;
269 mixed *qgliste;
270 int i, j;
271
272 qgliste = allocate(sizeof(QGROUPS)+1); // letzte Gruppe sind die Seherquests
273 qliste = m_indices(quests);
274
275 for (i=sizeof(qgliste)-1;i>=0;i--)
276 qgliste[i]=({});
277
278 for (i=sizeof(qliste)-1;i>=0;i--)
279 {
280 // inaktive quest?
281 if (!quests[qliste[i]][Q_ACTIVE])
282 continue;
283 // optionale quest? also Seherquest
284 if (quests[qliste[i]][Q_DIFF]==-1)
285 qgliste[sizeof(QGROUPS)] += ({qliste[i]});
286 else {
287 // dann haben wir also eine normale Quest und daher Einordnung
288 // nach dem Schwierigkeitswert
289 for (j=sizeof(QGROUPS)-1;
290 j>=0 && QGROUPS[j]>=quests[qliste[i]][Q_DIFF];j--)
291 ;
292 qgliste[j] += ({qliste[i]});
293 }
294 }
295 return qgliste;
296}
297
298
299// folgende funk brauch ich glaube ich nicht mehr:
300int QueryDontneed(object pl) {
301 raise_error("Ich glaub, die Func QueryDontneed() braucht kein Mensch mehr. "
302 "(Zook)");
303}
304
305// Die folgende Func braucht man nicht mehr
306int QueryReadyForWiz(object player) {
307 raise_error("Die Func QueryReadyForWiz() braucht keiner mehr. (Zook)");
308}
309
310mixed *QueryQuest(string name) {
311 if(!quests[name])
312 return ({});
313 if( extern_call() )
314 return deep_copy( quests[name] );
315 return quests[name];
316}
317
318int QueryQuestPoints(string name) {
319 if( !quests[name] )
320 return -1;
321
322 return quests[name][Q_QP];
323}
324
325mixed *QueryQuests() {
326 if( extern_call() )
327 return ({m_indices(quests),map(m_values(quests),#'deep_copy /*'*/)});
328 return ({ m_indices(quests), m_values(quests) });
329}
330
331string *QueryAllKeys() {
332 return m_indices(quests);
333}
334
335int SetActive(string name, int flag) {
336 mixed *quest;
337
338 if (!allowed_write_access()) return 0;
339 if (!(quest=quests[name])) return -1;
340 switch(flag)
341 {
342 case 0:
343 if (quest[Q_ACTIVE] == flag)
344 return -2;
345 quest[Q_ACTIVE] = flag;
346 break;
347 case 1:
348 if (quest[Q_ACTIVE] == flag)
349 return -2;
350 quest[Q_ACTIVE] = flag;
351 break;
352 default:
353 return -3;
354 }
355 quests[name]=quest;
356 RecalculateQP();
357 save_info();
358 QMLOG(sprintf("%s: %s (%s)",(flag?"activate":"deactivate"),name,
359 getuid(this_interactive())));
360 return 1;
361}
362
363string name() {
364 return "<Quest>";
365}
366string Name() {
367 return "<Quest>";
368}
369
370void Channel(string msg) {
371 if(!interactive(previous_object()))
372 return;
bugfixd94d0932020-04-08 11:27:13 +0200373 catch(({int})CHMASTER->send("Abenteuer", ME, msg);publish);
MG Mud User88f12472016-06-24 23:31:02 +0200374}
375
376 /* quoted from /sys/mail.h: */
377#define MSG_FROM 0
378#define MSG_SENDER 1
379#define MSG_RECIPIENT 2
380#define MSG_CC 3
381#define MSG_BCC 4
382#define MSG_SUBJECT 5
383#define MSG_DATE 6
384#define MSG_ID 7
385#define MSG_BODY 8
386
387void SendMail(string questname, mixed *quest, object player) {
388 mixed* mail;
389 string text;
390
391 mail = allocate(9);
392
393 text =
394 "Hallo "+capitalize(getuid(player))+",\n\n"+
395 break_string("Nachdem Du gerade eben das Abenteuer '"+
396 questname +"' ("+quest[Q_QP]+" Punkte), das "+
397 capitalize(quest[Q_WIZ])+" fuer das "MUDNAME" entworfen hat, "
398 "mit Erfolg bestanden hast, sind "
399 "wir nun an Deiner Meinung dazu interessiert:", 78)+
400 "\n Hat Dir das Abenteuer gefallen und wieso bzw. wieso nicht?\n"
401 " Ist die Einstufung Deiner Meinung nach richtig? (AP und Stufe)\n"
402 " Gab es Probleme oder gar Fehler?\n"
403 " Hast Du Verbesserungsvorschlaege?\n\n";
404
405 text += break_string("Diese Nachricht wurde automatisch verschickt, "
406 "wenn Du mit dem 'r' Kommando darauf antwortest, geht die Antwort "
407 "direkt an Ark als zustaendigem Erzmagier fuer Abenteuer.\n",78);
408
409 if (quest[Q_SCNDWIZ]!="") {
410 text += break_string(
411 "Falls Du mit dem Magier sprechen willst, der zur Zeit das "
412 "Abenteuer technisch betreut, kannst Du Dich an "
413 +capitalize(quest[Q_SCNDWIZ])+ " wenden.",78);
414 }
415
416 mail[MSG_FROM] = "Ark";
417 mail[MSG_SENDER] = "Ark";
418 mail[MSG_RECIPIENT] = getuid(player);
419 mail[MSG_CC]=0;
420 mail[MSG_BCC]=0;
421 mail[MSG_SUBJECT]="Das Abenteuer: "+questname;
422 mail[MSG_DATE]=dtime(time());
423 mail[MSG_ID]=MUDNAME":"+time();
424 mail[MSG_BODY]=text;
425
bugfixd94d0932020-04-08 11:27:13 +0200426 ({string*})"/secure/mailer"->DeliverMail(mail,0);
MG Mud User88f12472016-06-24 23:31:02 +0200427 return;
428}
429
430static int compare (mixed *i, mixed *j) {
431 if (i[4] == j[4])
432 return i[1] > j[1];
433 else
434 return i[4] > j[4];
435}
436
Zesstra0a62ddd2020-01-06 22:16:32 +0100437#define FILTER_GELOEST 1
438#define FILTER_UNGELOEST 2
439varargs string liste(mixed pl, int geloest_filter)
440{
MG Mud User88f12472016-06-24 23:31:02 +0200441 int qgroups, i, j, qrfw;
442 mixed *qlists, *qgrouped, *qtmp;
443 string str;
444 string ja, nein, format, ueberschrift;
445
446 if(!objectp(pl))
447 if(stringp(pl))
448 pl=find_player(pl) || find_netdead(pl);
449 if(!objectp(pl))
450 pl=PL;
451 if(!objectp(pl))
452 return "Ohne Spielernamen/Spielerobjekt gibt es auch keine Liste.\n";
453
bugfixd94d0932020-04-08 11:27:13 +0200454 if ( (({string})pl->QueryProp(P_TTY)) == "ansi")
MG Mud User88f12472016-06-24 23:31:02 +0200455 {
456 ja = ANSI_GREEN + "ja" + ANSI_NORMAL;
457 nein = ANSI_RED + "nein" + ANSI_NORMAL;
458 }
459 else
460 {
461 ja = "ja";
462 nein = "nein";
463 }
464
465 str = "";
466 // Festlegen des Ausgabeformates
467 format = "%=-:30s %:3d %-:6s %-:9s %:2s/%-:3s %-:12s %-s\n";
468 ueberschrift = sprintf("%-:30s %:3s %-:6s %-:9s %-:6s %-:12s %-:4s\n",
469 "Abenteuer", "AP", "Klasse", "Attribut",
470 "Stufe", "Autor", "Gel?");
471
472 qgroups = sizeof(QGROUPS);
473 qlists = allocate( qgroups+1 );
474 for( i=qgroups; i>=0; i-- )
475 qlists[i] = ({});
476
477 qgrouped = QueryGroupedKeys();
478
479 for (i=sizeof(qgrouped)-1;i>=0; i--)
Zesstra0a62ddd2020-01-06 22:16:32 +0100480 for (j=sizeof(qgrouped[i])-1;j>=0; j--)
481 {
MG Mud User88f12472016-06-24 23:31:02 +0200482 qtmp = QueryQuest(qgrouped[i][j]);
bugfixd94d0932020-04-08 11:27:13 +0200483 int geloest_status = ({int})pl->QueryQuest(qgrouped[i][j]);
Zesstra0a62ddd2020-01-06 22:16:32 +0100484 // Quest ausgeben, wenn "kein Filter" gegeben oder Quest geloest und
485 // Filter "geloest" oder Quest ungeloest und Filter "ungeloest".
486 if ( !(geloest_filter & (FILTER_GELOEST|FILTER_UNGELOEST))
487 || ((geloest_filter & FILTER_GELOEST) && geloest_status == OK)
488 || ((geloest_filter & FILTER_UNGELOEST) && geloest_status != OK)
489 )
490 {
491 qlists[i] += ({ ({
492 qgrouped[i][j],
493 qtmp[Q_QP],
494 QCLASS_STARS(qtmp[Q_CLASS]),
495 capitalize(QATTR_STRINGS[qtmp[Q_ATTR]]),
496 qtmp[Q_DIFF],
497 (qtmp[Q_AVERAGE][1]>10
498 ? to_string(to_int(qtmp[Q_AVERAGE][0]))
499 : "-"),
500 capitalize(qtmp[Q_WIZ]),
501 geloest_status == OK ? ja : nein
502 }) });
503 }
MG Mud User88f12472016-06-24 23:31:02 +0200504 }
505
506 for( i=0; i<qgroups; i++ )
507 {
508 if (sizeof(qlists[i])) {
509 str += "\n" + ueberschrift;
510 str += sprintf("Stufen %d%s:\n",
511 QGROUPS[i]+1,
512 i==qgroups-1?"+":sprintf("-%d", QGROUPS[i+1]));
513 qlists[i] = sort_array( qlists[i], "compare", ME );
514 for( j=0; j<sizeof(qlists[i]); j++ ) {
515 if(qlists[i][j][Q_DIFF]>=0)
516 str += sprintf( format,
517 qlists[i][j][0], qlists[i][j][1], qlists[i][j][2],
518 qlists[i][j][3], sprintf("%d",qlists[i][j][4]),
519 qlists[i][j][5],
520 qlists[i][j][6], qlists[i][j][7]);
521 }
522 str += "\n\n";
523 }
524 }
525
526 qlists[qgroups] = sort_array(qlists[qgroups], "compare", ME);
527 i = qgroups;
528 if (sizeof(qlists[i])) {
529 str += "\n" + ueberschrift;
530 str += "Nur fuer Seher:\n";
531 for( j=0; j<sizeof(qlists[qgroups]); j++ ) {
532 if(qlists[i][j][Q_DIFF]==-1)
533 str += sprintf( format,
534 qlists[i][j][0], qlists[i][j][1], qlists[i][j][2],
535 qlists[i][j][3], "S", qlists[i][j][5],
536 qlists[i][j][6],
537 qlists[i][j][7]);
538 }
539 }
540
541 str +=
542 "\nEine Erklaerung der einzelnen Spalten findest Du unter "
543 "\"hilfe abenteuerliste\".\n";
544
545 return str;
546}
547
548
549// mitloggen, mit welchen durchschnittlichen Leveln Quests so geloest
550// werden...
551void HandleQuestSolved(string eid, object trigob, mixed data) {
552 string qname = data[E_QUESTNAME];
553
554 if (!quests[qname] || !objectp(trigob)
bugfixd94d0932020-04-08 11:27:13 +0200555 || ({int|string})trigob->QueryProp(P_TESTPLAYER) || IS_LEARNER(trigob))
MG Mud User88f12472016-06-24 23:31:02 +0200556 return;
557
Vanion50652322020-03-10 21:13:25 +0100558 int lvl = ({int})trigob->QueryProp(P_LEVEL);
MG Mud User88f12472016-06-24 23:31:02 +0200559
560 if (lvl <= 0)
561 return;
562
563 // neuen Durchschnitt berechen.
564 mixed tmp = quests[qname][Q_AVERAGE];
565 float avg = tmp[0];
566 int count = tmp[1];
567 avg *= count;
568 avg += to_float(lvl);
569 tmp[1] = ++count;
570 tmp[0] = avg / count;
571
572 DEBUG(sprintf("%s: %f (%d)\n",qname,
573 quests[qname][Q_AVERAGE][0],
574 quests[qname][Q_AVERAGE][1]));
Zesstra247a2ba2020-08-23 23:08:15 +0200575
576 save_info();
MG Mud User88f12472016-06-24 23:31:02 +0200577}
578
579/*
580 * (2) ABSCHNITT "MINI" QUESTS
581 */
582
583int ClearUsersMQCache() {
584 if (!allowed_write_access())
585 return 0;
586
587 users_mq = ([]);
Arathornd1d78102019-09-28 17:35:35 +0200588 RebuildMQCache();
MG Mud User88f12472016-06-24 23:31:02 +0200589
590 return 1;
591}
592
593mixed QueryUsersMQCache() {
594 if (!allowed_write_access())
595 return 0;
596
597 return users_mq;
598}
599
600/* Beschreibung
601 *
602 * Die MiniQuests werden in einem Mapping gespeichert.
603 *
604 * Der Key ist dabei der Name des Objektes, das die Quest im Spieler
605 * markieren darf. Die Daten zu den Miniquests stehen in den Value-Spalten
606 * des Mappings.
607 *
608 * 1. Spalte ist die Zahl der durch diese Quest zu erwerbenden Stufenpunkte
609 * 2. Spalte ist die Nummer unter der die MiniQuest gefuehrt wird.
610 * 3. Spalte ist ein String, der die Quest(aufgabe) kurz beschreibt.
611 * 4. Spalte ist ein Integer, 0 oder 1:
612 * 0 : Quest ist fuer Spieler nicht sichtbar
613 * 1 : Quest ist fuer Spieler z.B. bei einer Anschlagtafel sichtbar
614 * Fuer Spieler unsichtbare MQs sollte es aber nicht mehr geben!
615 * 5. Spalte ist ein Integer, 0 oder 1:
616 * 0 : Quest voruebergehend deaktiviert
617 * 1 : Quest aktiviert
618 * 6. Spalte ist ein String, der den Kurztitel der Miniquest enthaelt
619 * 7. Spalte ist ein String, der eine kurze Beschreibung dessen enthaelt,
620 * was der Spieler im Verlauf der Miniquest erlebt hat.
621 * 8. Spalte ist ein Mapping, dessen Eintraege analog zu P_RESTRICTIONS
622 * gesetzt werden koennen, um anzugeben, welche Voraussetzungen erfuellt
623 * sein muessen, bevor ein Spieler diese Quest beginnen kann.
Zook0489eb62019-07-30 09:27:01 +0200624 * 9. Spalte ist die Zuordnung der MQ zu den Regionen; dies wird von der
625 * Bibliothek in der Fraternitas (/d/ebene/miril/fraternitas/room/bibliothek)
626 * ausgewertet. Diese kennt (Stand 2019-07-30) folgende Regionen:
627 * #define REGIONEN ({"wald","ebene","polar","wueste","inseln",\
628 * "unterwelt","fernwest","dschungel","gebirge"})
629 * D.h. MiniQuests im Verlorenen Land sollten als "inseln" geschluesselt
630 * werden.
MG Mud User88f12472016-06-24 23:31:02 +0200631 *10. Spalte ist ein Array aus Strings, das die Objekte enthaelt, die
632 * die Daten dieser Quest abfragen duerfen, um sie an Spieler auszugeben.
633 */
634
635int DumpMiniQuests(object who) {
636 int sum_points;
637
638 if (extern_call() && !allowed_write_access())
639 return 0;
640
641 if ( !objectp(who) || !query_once_interactive(who))
642 who = this_interactive();
643
644 MQMLOG(sprintf("DumpMiniQuests: PO: %O, TI: %O", previous_object(), who));
645 rm(MQ_DUMP_FILE);
646
647 write_file(MQ_DUMP_FILE, "MINIQUESTS: ("+dtime(time())+")\n\n"+
648 " Nr Pkt vis akt vergebendes Objekt\n");
649 string *msg = ({});
650
651 foreach(string obname, int stupse, int nummer, mixed descr, int vis,
652 int active /*, string title, string donedesc, mapping restrictions,
653 string domain, string *permitted_objs*/: miniquests)
654 {
655 msg += ({ sprintf("%4d %4d %4d %4d %s",
656 nummer, stupse, vis, active, obname)});
657 sum_points += stupse;
658 }
659
660 write_file(MQ_DUMP_FILE, implode(sort_array(msg, #'> /*'*/), "\n"));
661 write_file(MQ_DUMP_FILE, sprintf("\n\n"
662 "============================================================\n"
663 +"MiniQuests: %d Miniquests mit %d Punkten.\n\n",
664 sizeof(miniquests), sum_points));
665 return 1;
666}
667
668public int AddMiniQuest(int mquestpoints, string allowedobj, string descr,
669 int active, string title, string donedesc, mapping restrictions,
670 string domain, string *permitted_objs) {
671
672 if (!allowed_write_access())
673 return 0;
674
675 // Parameterpruefung: Questgeber, Restrictions, Region, Titel und
676 // zugelassene Abfrageobjekte muessen gueltig angegeben werden, alles
677 // weitere wird unten ggf. ausgenullt/korrigiert.
678 if (!stringp(allowedobj) || !sizeof(allowedobj) || !mappingp(restrictions)
679 || !stringp(domain) || !stringp(title) || !pointerp(permitted_objs))
680 return -1;
681
682 // Miniquest mit weniger als 1 Stups einzutragen ist wohl unsinnig.
683 if (mquestpoints<1)
684 return -2;
685
686 // Mindestens ein Objekt muss eingetragen werden, das Spielern Informationen
687 // ueber die Aufgabenstellung der MQ geben darf.
688 if ( !sizeof(permitted_objs) )
689 return -3;
690
691 // Pruefen, ob die als Questgeber angegebene Datei existiert.
692 if (allowedobj[<2..] == ".c")
693 allowedobj = allowedobj[0..<3];
694 allowedobj = explode(allowedobj, "#")[0];
Vanion50652322020-03-10 21:13:25 +0100695 allowedobj = ({string})master()->make_path_absolute(allowedobj);
MG Mud User88f12472016-06-24 23:31:02 +0200696 if (file_size(allowedobj+".c") <=0)
697 return -3;
698
699 // Vergibt das angegebene Objekt schon eine MQ? Dann abbrechen.
700 if (member(miniquests,allowedobj))
701 return -4;
702
703 if (!stringp(descr) || !sizeof(descr))
704 descr = 0;
705 if (!stringp(donedesc) || !sizeof(donedesc))
706 donedesc = 0;
707
708 // Eintrag hinzufuegen, visible ist per Default immer 1.
709 // MQ-Nummer hochzaehlen
710 int nummer = last_num + 1;
711 m_add(miniquests, allowedobj, mquestpoints, nummer, descr, 1, active,
712 title, donedesc, restrictions, domain, permitted_objs);
713 // und nummer als last_num merken.
714 last_num = nummer;
715 save_info();
716 m_add(by_num, nummer, ({mquestpoints, allowedobj}));
717 MQMLOG(sprintf("AddMiniQuest: %s %O (%s)", allowedobj, miniquests[allowedobj],
718 getuid(this_interactive())));
719
720 ClearUsersMQCache();
721 if (find_call_out(#'DumpMiniQuests) == -1)
722 call_out(#'DumpMiniQuests, 60, this_interactive());
723 return 1;
724}
725
726int RemoveMiniQuest(string name) {
727 if (!allowed_write_access())
728 return 0;
729 // Gibt es einen solchen Eintrag ueberhaupt?
730 if (!member(miniquests,name))
731 return -1;
732
733 MQMLOG(sprintf("RemoveMiniQuest: %s %O (%s)",
734 name, m_entry(miniquests, name), getuid(this_interactive())));
735
736 // MQ aus dem MQ-Indexnummern-Cache loeschen.
737 m_delete(by_num, miniquests[name,MQ_DATA_QUESTNO]);
738 // MQ aus der Miniquestliste austragen.
739 m_delete(miniquests, name);
740 save_info();
741
742 // MQ-Punkte-Cache loeschen, da nicht feststellbar ist, welcher der
743 // dort eingetragenen Spieler die gerade ausgetragene MQ geloest hatte.
744 ClearUsersMQCache();
745 if (find_call_out(#'DumpMiniQuests) == -1)
746 call_out(#'DumpMiniQuests, 60, this_interactive());
747 return 1;
748}
749
750int ChangeMiniQuest(mixed mq_obj, int param, mixed newvalue) {
751 if (!allowed_write_access())
752 return 0;
753
754 // MQ weder als Pfad, noch als Indexnummer angegeben?
755 if ( !stringp(mq_obj) && !intp(mq_obj) && !intp(param))
756 return MQ_KEY_INVALID;
757
758 // gewaehlter Parameter ungueltig?
759 if ( param < MQ_DATA_POINTS || param > MQ_DATA_QUERY_PERMITTED )
760 return MQ_KEY_INVALID;
761
762 // Indexnummer der MQ in den Pfad umwandeln
763 if ( intp(mq_obj) )
764 mq_obj = by_num[mq_obj][1];
765
766 // Vergebendes Objekt nicht gefunden? Bloed, das brauchen wir naemlich.
767 if (!stringp(mq_obj))
768 return MQ_KEY_INVALID;
769
770 if ( !member(miniquests, mq_obj) )
771 return MQ_ILLEGAL_OBJ;
772
773 switch(param) {
774 // MQ_DATA_QUESTNO ist nicht aenderbar, daher hier nicht behandelt, so
775 // dass Fallback auf default erfolgt.
776 // Stufenpunkte muessen Integers sein.
777 case MQ_DATA_POINTS:
778 if ( !intp(newvalue) || newvalue < 1 )
779 return MQ_KEY_INVALID;
780 break;
781 // Aufgabenbeschreibung, Titel, "geschafft"-Text und zugeordnete Region
782 // muessen Strings sein
783 case MQ_DATA_TASKDESC:
784 case MQ_DATA_TITLE:
785 case MQ_DATA_DIARYTEXT:
786 case MQ_DATA_ASSIGNED_DOMAIN:
787 if ( !stringp(newvalue) || !sizeof(newvalue) )
788 return MQ_KEY_INVALID;
789 break;
790 // das Sichtbarkeits- und das aktiv/inaktiv-Flag muessen 0/1 sein.
791 case MQ_DATA_VISIBLE:
792 case MQ_DATA_ACTIVE:
793 if ( !intp(newvalue) || newvalue < 0 || newvalue > 1 )
794 return MQ_KEY_INVALID;
795 break;
796 // Die Voraussetzungen muessen als Mapping eingetragen werden, das aber
797 // leer oder Null sein kann, wenn es keine Restriktionen gibt.
798 case MQ_DATA_RESTRICTIONS:
799 if ( !mappingp(newvalue) && newvalue != 0 )
800 return MQ_KEY_INVALID;
801 break;
802 // Regionszuordnung muss ein nicht-leeres Array sein, das nur aus Strings
803 // bestehen darf, die nicht leer sein duerfen.
804 case MQ_DATA_QUERY_PERMITTED:
805 if ( pointerp(newvalue) ) {
806 newvalue = filter(filter(newvalue, #'stringp), #'sizeof);
807 if (!sizeof(newvalue))
808 return MQ_KEY_INVALID;
809 }
810 else
811 return MQ_KEY_INVALID;
812 break;
813 default:
814 return MQ_KEY_INVALID;
815 }
816
817 mixed *altemq = m_entry(miniquests, mq_obj);
818 int nummer = miniquests[mq_obj,MQ_DATA_QUESTNO];
819 miniquests[mq_obj, param] = newvalue;
820 by_num[nummer] = ({miniquests[mq_obj,MQ_DATA_POINTS], mq_obj});
821 save_info();
822
823 MQMLOG(sprintf("ChangeMiniQuest: %s from %O to %O (%s)", mq_obj,
824 altemq, m_entry(miniquests, mq_obj), getuid(this_interactive())));
825
826 ClearUsersMQCache();
827 if (find_call_out(#'DumpMiniQuests) == -1)
828 call_out(#'DumpMiniQuests, 60, this_interactive());
829 return 1;
830}
831
832mixed QueryMiniQuestByName(string name) {
833 if (!allowed_write_access())
834 return 0;
835 return deep_copy(miniquests & ({name}));
836}
837
838mixed QueryMiniQuestByNumber(int nummer) {
839 // Zugriffsabsicherung erfolgt dort auch, daher hier unnoetig
840 return (by_num[nummer]?QueryMiniQuestByName(by_num[nummer][1]):0);
841}
842
843// Das vollstaendige MQ-Mapping nur als Kopie ausliefern.
844mixed QueryMiniQuests() {
845 return allowed_write_access() ? deep_copy(miniquests) : 0;
846}
847
848// De-/Aktivieren einer Miniquest, wirkt als Umschalter, d.h. eine aktive
849// MQ wird durch Aufruf dieser Funktion als inaktiv markiert und umgekehrt.
850int SwitchMiniQuestActive(string name) {
851 if (!allowed_write_access())
852 return -1;
853 // Haben wir eine solche MQ ueberhaupt?
854 if (!member(miniquests, name))
855 return -2;
856
857 // active-Flag invertieren
858 miniquests[name, MQ_DATA_ACTIVE] = !miniquests[name, MQ_DATA_ACTIVE];
859 save_info();
860
861 MQMLOG(sprintf("%s: %s (%s)",
862 (miniquests[name,MQ_DATA_ACTIVE]?"Activate":"Deactivate"), name,
863 getuid(this_interactive()))
864 );
865 return miniquests[name,MQ_DATA_ACTIVE];
866}
867
868int GiveMiniQuest(object winner) {
869 // Spieler muss existieren und interactive sein.
870 if (!winner ||
871 (this_interactive() && (this_interactive() != winner)) ||
872 ((this_player() == winner) && !query_once_interactive(winner)))
873 return MQ_ILLEGAL_OBJ;
874 // Gaeste koennen keine Miniquests bestehen.
bugfixd94d0932020-04-08 11:27:13 +0200875 if (({int})winner->QueryGuest())
MG Mud User88f12472016-06-24 23:31:02 +0200876 return MQ_GUEST;
877 // Aufrufendes Objekt existiert gar nicht?
878 if (!previous_object())
879 return MQ_ILLEGAL_OBJ;
880
881 string objname = load_name(previous_object());
882 // Miniquest muss existieren
883 if (!member(miniquests,objname))
884 return MQ_KEY_INVALID;
885 // Inaktive Miniquests koennen nicht vergeben werden.
886 if (!miniquests[objname, MQ_DATA_ACTIVE])
887 return MQ_IS_INACTIVE;
888
bugfixd94d0932020-04-08 11:27:13 +0200889 string mq = (({string})MASTER->query_mq(getuid(winner)) || "");
MG Mud User88f12472016-06-24 23:31:02 +0200890
891 // Spieler hat die MQ schonmal bestanden? Dann keine weiteren Aktivitaet
892 // noetig
893 if (test_bit(mq, miniquests[objname, MQ_DATA_QUESTNO]))
894 return MQ_ALREADY_SET;
895
896 catch(mq = set_bit(mq, miniquests[objname,MQ_DATA_QUESTNO]);publish);
bugfixd94d0932020-04-08 11:27:13 +0200897 ({int})MASTER->update_mq(getuid(winner), mq);
MG Mud User88f12472016-06-24 23:31:02 +0200898
899 MQSOLVEDLOG(sprintf("%s: %s, (#%d), (Stupse %d)",
900 objname, geteuid(winner), miniquests[objname, MQ_DATA_QUESTNO],
901 miniquests[objname, MQ_DATA_POINTS]));
902
903 // Miniquest-Event ausloesen
bugfixd94d0932020-04-08 11:27:13 +0200904 ({int})EVENTD->TriggerEvent( EVT_LIB_MINIQUEST_SOLVED, ([
MG Mud User88f12472016-06-24 23:31:02 +0200905 E_OBJECT: previous_object(),
906 E_OBNAME: objname,
907 E_PLNAME: getuid(winner),
908 E_MINIQUESTNAME: miniquests[objname, MQ_DATA_TITLE] ]) );
909
910 // Spielereintrag aus dem MQ-Punkte-Cache loeschen
911 m_delete(users_mq, getuid(winner));
912
913 return 1;
914}
915
916int QueryMiniQuestPoints(mixed pl) {
917 string spieler;
918
919 //if (!allowed_write_access())
920 // return 0;
921
922 if (!pl)
923 return -1;
924
925 if (!objectp(pl) && !stringp(pl))
926 return -2;
927
928 if (objectp(pl) && !query_once_interactive(pl))
929 return -3;
930
931 if (objectp(pl))
932 spieler = getuid(pl);
933 else
934 spieler = pl;
935
936 if (!member(users_mq, spieler)) {
937 int mqpoints;
938 int p=-1;
bugfixd94d0932020-04-08 11:27:13 +0200939 string s = (({string})MASTER->query_mq(spieler) || "");
MG Mud User88f12472016-06-24 23:31:02 +0200940 while( (p=next_bit(s, p)) != -1) {
941 mqpoints+=by_num[p][0];
942 }
943 users_mq[spieler] = mqpoints;
944 }
945 return users_mq[spieler];
946}
947
948int HasMiniQuest(mixed pl, mixed name) {
949 string mq, spieler;
950
951 if (!pl || !name)
952 return MQ_ILLEGAL_OBJ;
953
954 if (!objectp(pl) && !stringp(pl))
955 return MQ_ILLEGAL_OBJ;
956
957 if (objectp(pl) && !query_once_interactive(pl))
958 return MQ_ILLEGAL_OBJ;
959
960 if (!objectp(name) && !stringp(name) && !intp(name))
961 return MQ_ILLEGAL_OBJ;
962
963 if (objectp(name))
964 name = explode(object_name(name), "#")[0];
965
966 if ( intp(name) )
967 name = by_num[name][1];
968
969 if (objectp(pl))
970 spieler = getuid(pl);
971 else
972 spieler = pl;
973
974 if (!member(miniquests,name))
975 return MQ_KEY_INVALID;
976
bugfixd94d0932020-04-08 11:27:13 +0200977 mq = (({string})MASTER->query_mq(spieler) || "");
MG Mud User88f12472016-06-24 23:31:02 +0200978
979 return test_bit(mq, miniquests[name, MQ_DATA_QUESTNO]);
980}
981
982// Zum Von-Hand-Setzen der MiniQuests
983int SetPlayerMiniQuest(string pl, string name) {
984 if(!allowed_write_access())
985 return 0;
986 if(!pl)
987 return MQ_ILLEGAL_OBJ;
988 if(!previous_object())
989 return MQ_ILLEGAL_OBJ;
990
991 if (!member(miniquests,name))
992 return MQ_KEY_INVALID;
993
bugfixd94d0932020-04-08 11:27:13 +0200994 string mq = (({string})MASTER->query_mq(pl) || "");
MG Mud User88f12472016-06-24 23:31:02 +0200995
996 if (test_bit(mq, miniquests[name,MQ_DATA_QUESTNO]))
997 return MQ_ALREADY_SET;
998
999 catch (mq = set_bit(mq, miniquests[name, MQ_DATA_QUESTNO]);publish);
bugfixd94d0932020-04-08 11:27:13 +02001000 ({int})MASTER->update_mq(pl, mq);
MG Mud User88f12472016-06-24 23:31:02 +02001001
1002 MQMLOG(sprintf("SetPlayerMiniQuest: %s %s (%s)",
1003 pl, name, getuid(this_interactive())));
1004 // Spielereintrag aus dem MQ-Punkte-Cache loeschen
1005 m_delete(users_mq, pl);
1006 return 1;
1007}
1008
1009int ClearPlayerMiniQuest(string pl, string name) {
1010 if (!allowed_write_access())
1011 return 0;
1012 if (!pl)
1013 return MQ_ILLEGAL_OBJ;
1014 if (!previous_object())
1015 return MQ_ILLEGAL_OBJ;
1016
1017 if (!member(miniquests,name))
1018 return MQ_KEY_INVALID;
1019
bugfixd94d0932020-04-08 11:27:13 +02001020 string mq = (({string})MASTER->query_mq(pl) || "");
MG Mud User88f12472016-06-24 23:31:02 +02001021
1022 if (!test_bit(mq, miniquests[name, MQ_DATA_QUESTNO]))
1023 return MQ_ALREADY_SET;
1024
1025 catch (mq = clear_bit(mq, miniquests[name, MQ_DATA_QUESTNO]);publish);
bugfixd94d0932020-04-08 11:27:13 +02001026 ({int})MASTER->update_mq(pl, mq);
MG Mud User88f12472016-06-24 23:31:02 +02001027
1028 MQMLOG(sprintf("ClearPlayerMiniQuest: %s %s (%s)",
1029 pl, name, getuid(this_interactive())));
1030 // Spielereintrag aus dem MQ-Punkte-Cache loeschen
1031 m_delete(users_mq, pl);
1032 return 1;
1033}
1034
1035// Umtragen von Miniquests von Objekt <old> auf Objekt <new>
1036int MoveMiniQuest(string old_mqob, string new_mqob) {
1037 if ( !allowed_write_access() )
1038 return -1;
1039
1040 // Haben wir ueberhaupt einen solchen Eintrag?
1041 if ( !member(miniquests, old_mqob) )
1042 return -2;
1043
1044 // Pruefen, ob die als <new_mqob> angegebene Datei existiert.
1045 if (new_mqob[<2..] == ".c")
1046 new_mqob = new_mqob[0..<3];
1047 new_mqob = explode(new_mqob, "#")[0];
Vanion50652322020-03-10 21:13:25 +01001048 new_mqob = ({string})master()->make_path_absolute(new_mqob);
MG Mud User88f12472016-06-24 23:31:02 +02001049 if (file_size(new_mqob+".c") <= 0)
1050 return -3;
1051 // Wenn das neue Objekt schon eine MQ vergibt, kann es keine weitere
1052 // annehmen.
1053 if ( member(miniquests, new_mqob) )
1054 return -4;
1055
1056 // Der Miniquestliste einen neuen Key "new" mit den Daten des alten Keys
1057 // hinzufuegen. m_entry() liefert alle Values dazu als Array, und der
1058 // flatten-Operator "..." uebergibt dessen Elemente als einzelne Parameter.
1059 m_add(miniquests, new_mqob, m_entry(miniquests, old_mqob)...);
1060 m_delete(miniquests, old_mqob);
1061 // Nummern-Index auch umtragen, sonst koennen Funktionen wie zB
1062 // QueryMiniQuestByNumber() die neue nicht finden.
1063 by_num[miniquests[new_mqob,MQ_DATA_QUESTNO]][1] = new_mqob;
1064 return 1;
1065}
1066
1067#define FRA_BIB "/d/ebene/miril/fraternitas/room/bibliothek"
1068
1069// Erlaubt die Abfrage aller MQs einer bestimmten Region fuer die Bibliothek
1070// der kleinen und grossen Heldentaten in der Fraternitas.
1071// Gibt ein Mapping der Form ([ indexnummer : titel; erledigt_Beschreibung ])
1072// zurueck.
1073mapping QuerySolvedMQsByDomain(mixed pl, string region) {
1074 if ( !objectp(pl) && !stringp(pl) &&
1075 load_name(previous_object())!=FRA_BIB) /*|| !allowed_write_access())*/
1076 return ([:2]);
1077
1078 mapping res = m_allocate(30,2); // reicht vermutlich
1079 // Die angegebene Region muss in der Spalte MQ_DATA_ASSIGNED_DOMAIN
1080 // enthalten sein, und das abfragende Objekt muss die Fraternitas-Bib sein
1081 foreach(string mqobj, int mqp, int index, string task, int vis, int act,
1082 string title, string donedesc, mapping restr, string domain:
1083 miniquests) {
1084 // aktive MQs der angeforderten Region zusammentragen, die der Spieler
1085 // bestanden hat.
1086 if ( domain == region && act && HasMiniQuest(pl, mqobj) )
1087 m_add(res, index, title, donedesc);
1088 }
1089 //DEBUG(sprintf("%O\n",res));
1090 return res;
1091}
1092#undef FRA_BIB
1093
1094// Abfrage der noch offenen MQs des angegebenen Spielers.
1095// Zurueckgegeben wird ein Mapping mit den Miniquest-Nummern als Keys und
1096// den Aufgabenbeschreibungen als Values, oder ein leeres Mapping, falls
1097// das abfragende Objekt keine Zugriffsberechtigung hat, oder das
1098// uebergebene Spielerobjekt keine offenen Miniquests mehr hat.
1099mapping QueryOpenMiniQuestsForPlayer(object spieler) {
1100 // map() etwas beschleunigen
1101 closure chk_restr = symbol_function("check_restrictions",
1102 "/std/restriction_checker");
1103 // Cache-Eintrag fuer das abfragende Objekt holen
1104 string *list = mq_query_permitted[load_name(previous_object())];
1105 mapping res = ([:2]);
1106
1107 if (!pointerp(list) || !sizeof(list))
1108 return res;
1109 // Liste der MQ-Objekte umwandeln in deren MQ-Nummer plus
1110 // Aufgabenbeschreibung, sofern der Spieler die MQ noch nicht bestanden
1111 // hat, aber die Voraussetzungen erfuellt.
1112 foreach ( string mq_obj : list )
1113 {
Bugfixbf92f882017-05-24 20:23:48 +02001114 // Ist die MQ nicht aktiv, ist der Rest hinfaellig.
1115 if(!miniquests[mq_obj,MQ_DATA_ACTIVE]) continue;
1116
MG Mud User88f12472016-06-24 23:31:02 +02001117 // Nur wenn der Spieler die MQ noch nicht hat, kann er ueberhaupt einen
1118 // Tip dazu bekommen.
1119 if ( !HasMiniQuest(spieler, mq_obj) ) {
1120 // Restriction Checker fragen, ob der Spieler statt des Hinweises
1121 // eine Info bekommen muss, dass er eine Vorbedingung nicht erfuellt.
1122 string restr_result = funcall(chk_restr, spieler, miniquests[mq_obj,
1123 MQ_DATA_RESTRICTIONS]);
1124 // Wenn so eine Info dabei rauskommt, wird diese in die Ergebnisliste
1125 // uebernommen. In diesem Fall wird KEIN MQ-Hinweistext ausgeliefert.
1126 if ( stringp(restr_result) )
1127 {
1128 m_add(res, miniquests[mq_obj,MQ_DATA_QUESTNO], 0, restr_result);
1129 }
1130 // Anderenfalls wird der Hinweistext uebernommen; einen Eintrag
1131 // bzgl. eines eventuellen Hinderungsgrundes gibt's dann nicht.
1132 else
1133 {
1134 m_add(res, miniquests[mq_obj,MQ_DATA_QUESTNO],
1135 miniquests[mq_obj,MQ_DATA_TASKDESC], 0);
1136 }
1137 }
1138 }
1139 // Ergebnisliste zurueckgeben.
1140 return res;
1141}
1142
1143// Datenkonverter fuer das bisherige MQ-Mapping von
1144// ([ obj : ({daten1..daten5}) ]) nach
1145// ([ obj : daten1; daten2; ...; daten9 ]), wobei daten6..9 als Leerspalten
1146// erzeugt werden.
1147/*void ConvertMQData() {
1148 if ( !allowed_write_access() )
1149 return;
1150
1151 by_num=([]);
1152 // spaltenweise aus dem miniquests-Mapping ein Array aus Arrays erzeugen
1153 // Zunaechst die Keys des Mappings aufnehmen.
1154 mixed *mqdata = ({m_indices(miniquests)});
1155
1156 // Dann das Datenarray spaltenweise dazu (jedes Array ist eine Spalte).
1157 mqdata += transpose_array(m_values(miniquests));
1158 // 1. Array: Keys, alle weiteren jeweils eine Wertespalte
1159
1160 // Array erzeugen als Array aus 5 Array-Elementen, die alle soviele
1161 // Nullen enthalten, wie es Miniquests gibt,
1162 // ({ ({0,0,...}), ({0,0,...}), ({0,0,...}), ({0,0,...}), ({0,0,...}) })
1163 // dieses hinzufuegen. Sind die hinzukommenden 5 Spalten.
1164 mqdata += allocate(5, allocate(sizeof(miniquests),0));
1165
1166 // Mapping erzeugen, indem mit dem flatten-Operator "..." die Einzel-
1167 // Arrays des Datenpakets uebergeben werden. Erzeugt auf diese Weise
1168 // ([ keys : daten1; daten2; daten3; daten4; daten5; 0; 0; 0; 0; 0,])
1169 miniquests=mkmapping(mqdata...);
1170}
1171
1172// Neue Daten einlesen, Visible-Flag bei allen MQs per Default auf 1 setzen.
1173// Es gibt keine wirklich unsichtbaren MQs mehr.
1174void ReadNewData() {
1175 if ( !allowed_write_access() )
1176 return;
1177
1178 string *import = explode(read_file("/players/arathorn/mqdata"),"\n")-({""});
1179 string *fields;
1180 foreach(string mqdata : import) {
1181 fields = explode(mqdata, "#")-({""});
1182 DEBUG(sprintf("%O\n", fields[0]));
1183 if ( miniquests[fields[4], MQ_DATA_QUESTNO] != to_int(fields[0]) ) {
1184 raise_error("MQ-Nummern stimmen nicht ueberein!\n");
1185 return;
1186 }
1187 // fields[4] ist der MQ-Objektpfad
1188 miniquests[fields[4], MQ_DATA_TITLE] = fields[7];
1189 miniquests[fields[4], MQ_DATA_DIARYTEXT] = fields[6];
1190 miniquests[fields[4], MQ_DATA_TASKDESC] = fields[5];
1191 miniquests[fields[4], MQ_DATA_VISIBLE] = 1; // Default: visible
1192 miniquests[fields[4], MQ_DATA_ASSIGNED_DOMAIN] = fields[8];
1193 miniquests[fields[4], MQ_DATA_QUERY_PERMITTED] = fields[9];
1194 if ( fields[3] != "0" ) {
1195 miniquests[fields[4], MQ_DATA_RESTRICTIONS] =
1196 restore_value(fields[3]+"\n");
1197 }
1198 else miniquests[fields[4], MQ_DATA_RESTRICTIONS] = 0;
1199 if ( fields[9] != "0" )
1200 miniquests[fields[4], MQ_DATA_QUERY_PERMITTED] =
1201 restore_value(fields[9]+"\n");
1202 else miniquests[fields[4], MQ_DATA_QUERY_PERMITTED] = 0;
1203 }
1204}
1205*/