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