blob: 900746089860dd4235cc6f2725d480fa93e1fbee [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.
104 EVENTD->RegisterEvent(EVT_LIB_QUEST_SOLVED,"HandleQuestSolved",
105 ME);
106}
107
108public int remove(int silent) {
109 save_info();
110 EVENTD->UnregisterEvent(EVT_LIB_QUEST_SOLVED, ME);
111 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;
Zesstra5cd26092018-11-08 22:21:21 +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;
373 catch(CHMASTER->send("Abenteuer", ME, msg);publish);
374}
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
426 "/secure/mailer"->DeliverMail(mail,0);
427 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
437varargs string liste(mixed pl) {
438 int qgroups, i, j, qrfw;
439 mixed *qlists, *qgrouped, *qtmp;
440 string str;
441 string ja, nein, format, ueberschrift;
442
443 if(!objectp(pl))
444 if(stringp(pl))
445 pl=find_player(pl) || find_netdead(pl);
446 if(!objectp(pl))
447 pl=PL;
448 if(!objectp(pl))
449 return "Ohne Spielernamen/Spielerobjekt gibt es auch keine Liste.\n";
450
451 if ( ((string)pl->QueryProp(P_TTY)) == "ansi")
452 {
453 ja = ANSI_GREEN + "ja" + ANSI_NORMAL;
454 nein = ANSI_RED + "nein" + ANSI_NORMAL;
455 }
456 else
457 {
458 ja = "ja";
459 nein = "nein";
460 }
461
462 str = "";
463 // Festlegen des Ausgabeformates
464 format = "%=-:30s %:3d %-:6s %-:9s %:2s/%-:3s %-:12s %-s\n";
465 ueberschrift = sprintf("%-:30s %:3s %-:6s %-:9s %-:6s %-:12s %-:4s\n",
466 "Abenteuer", "AP", "Klasse", "Attribut",
467 "Stufe", "Autor", "Gel?");
468
469 qgroups = sizeof(QGROUPS);
470 qlists = allocate( qgroups+1 );
471 for( i=qgroups; i>=0; i-- )
472 qlists[i] = ({});
473
474 qgrouped = QueryGroupedKeys();
475
476 for (i=sizeof(qgrouped)-1;i>=0; i--)
477 for (j=sizeof(qgrouped[i])-1;j>=0; j--) {
478 qtmp = QueryQuest(qgrouped[i][j]);
479 qlists[i] += ({ ({
480 qgrouped[i][j],
481 qtmp[Q_QP],
482 QCLASS_STARS(qtmp[Q_CLASS]),
483 capitalize(QATTR_STRINGS[qtmp[Q_ATTR]]),
484 qtmp[Q_DIFF],
485 (qtmp[Q_AVERAGE][1]>10 /*&& IS_ARCH(this_player())*/
486 ? to_string(to_int(qtmp[Q_AVERAGE][0]))
487 : "-"),
488 capitalize(qtmp[Q_WIZ]),
489 (int)pl->QueryQuest(qgrouped[i][j]) == OK ? ja : nein
490 }) });
491 }
492
493 for( i=0; i<qgroups; i++ )
494 {
495 if (sizeof(qlists[i])) {
496 str += "\n" + ueberschrift;
497 str += sprintf("Stufen %d%s:\n",
498 QGROUPS[i]+1,
499 i==qgroups-1?"+":sprintf("-%d", QGROUPS[i+1]));
500 qlists[i] = sort_array( qlists[i], "compare", ME );
501 for( j=0; j<sizeof(qlists[i]); j++ ) {
502 if(qlists[i][j][Q_DIFF]>=0)
503 str += sprintf( format,
504 qlists[i][j][0], qlists[i][j][1], qlists[i][j][2],
505 qlists[i][j][3], sprintf("%d",qlists[i][j][4]),
506 qlists[i][j][5],
507 qlists[i][j][6], qlists[i][j][7]);
508 }
509 str += "\n\n";
510 }
511 }
512
513 qlists[qgroups] = sort_array(qlists[qgroups], "compare", ME);
514 i = qgroups;
515 if (sizeof(qlists[i])) {
516 str += "\n" + ueberschrift;
517 str += "Nur fuer Seher:\n";
518 for( j=0; j<sizeof(qlists[qgroups]); j++ ) {
519 if(qlists[i][j][Q_DIFF]==-1)
520 str += sprintf( format,
521 qlists[i][j][0], qlists[i][j][1], qlists[i][j][2],
522 qlists[i][j][3], "S", qlists[i][j][5],
523 qlists[i][j][6],
524 qlists[i][j][7]);
525 }
526 }
527
528 str +=
529 "\nEine Erklaerung der einzelnen Spalten findest Du unter "
530 "\"hilfe abenteuerliste\".\n";
531
532 return str;
533}
534
535
536// mitloggen, mit welchen durchschnittlichen Leveln Quests so geloest
537// werden...
538void HandleQuestSolved(string eid, object trigob, mixed data) {
539 string qname = data[E_QUESTNAME];
540
541 if (!quests[qname] || !objectp(trigob)
542 || trigob->QueryProp(P_TESTPLAYER) || IS_LEARNER(trigob))
543 return;
544
545 int lvl = (int)trigob->QueryProp(P_LEVEL);
546
547 if (lvl <= 0)
548 return;
549
550 // neuen Durchschnitt berechen.
551 mixed tmp = quests[qname][Q_AVERAGE];
552 float avg = tmp[0];
553 int count = tmp[1];
554 avg *= count;
555 avg += to_float(lvl);
556 tmp[1] = ++count;
557 tmp[0] = avg / count;
558
559 DEBUG(sprintf("%s: %f (%d)\n",qname,
560 quests[qname][Q_AVERAGE][0],
561 quests[qname][Q_AVERAGE][1]));
562}
563
564/*
565 * (2) ABSCHNITT "MINI" QUESTS
566 */
567
568int ClearUsersMQCache() {
569 if (!allowed_write_access())
570 return 0;
571
572 users_mq = ([]);
Arathornd1d78102019-09-28 17:35:35 +0200573 RebuildMQCache();
MG Mud User88f12472016-06-24 23:31:02 +0200574
575 return 1;
576}
577
578mixed QueryUsersMQCache() {
579 if (!allowed_write_access())
580 return 0;
581
582 return users_mq;
583}
584
585/* Beschreibung
586 *
587 * Die MiniQuests werden in einem Mapping gespeichert.
588 *
589 * Der Key ist dabei der Name des Objektes, das die Quest im Spieler
590 * markieren darf. Die Daten zu den Miniquests stehen in den Value-Spalten
591 * des Mappings.
592 *
593 * 1. Spalte ist die Zahl der durch diese Quest zu erwerbenden Stufenpunkte
594 * 2. Spalte ist die Nummer unter der die MiniQuest gefuehrt wird.
595 * 3. Spalte ist ein String, der die Quest(aufgabe) kurz beschreibt.
596 * 4. Spalte ist ein Integer, 0 oder 1:
597 * 0 : Quest ist fuer Spieler nicht sichtbar
598 * 1 : Quest ist fuer Spieler z.B. bei einer Anschlagtafel sichtbar
599 * Fuer Spieler unsichtbare MQs sollte es aber nicht mehr geben!
600 * 5. Spalte ist ein Integer, 0 oder 1:
601 * 0 : Quest voruebergehend deaktiviert
602 * 1 : Quest aktiviert
603 * 6. Spalte ist ein String, der den Kurztitel der Miniquest enthaelt
604 * 7. Spalte ist ein String, der eine kurze Beschreibung dessen enthaelt,
605 * was der Spieler im Verlauf der Miniquest erlebt hat.
606 * 8. Spalte ist ein Mapping, dessen Eintraege analog zu P_RESTRICTIONS
607 * gesetzt werden koennen, um anzugeben, welche Voraussetzungen erfuellt
608 * sein muessen, bevor ein Spieler diese Quest beginnen kann.
Zook0489eb62019-07-30 09:27:01 +0200609 * 9. Spalte ist die Zuordnung der MQ zu den Regionen; dies wird von der
610 * Bibliothek in der Fraternitas (/d/ebene/miril/fraternitas/room/bibliothek)
611 * ausgewertet. Diese kennt (Stand 2019-07-30) folgende Regionen:
612 * #define REGIONEN ({"wald","ebene","polar","wueste","inseln",\
613 * "unterwelt","fernwest","dschungel","gebirge"})
614 * D.h. MiniQuests im Verlorenen Land sollten als "inseln" geschluesselt
615 * werden.
MG Mud User88f12472016-06-24 23:31:02 +0200616 *10. Spalte ist ein Array aus Strings, das die Objekte enthaelt, die
617 * die Daten dieser Quest abfragen duerfen, um sie an Spieler auszugeben.
618 */
619
620int DumpMiniQuests(object who) {
621 int sum_points;
622
623 if (extern_call() && !allowed_write_access())
624 return 0;
625
626 if ( !objectp(who) || !query_once_interactive(who))
627 who = this_interactive();
628
629 MQMLOG(sprintf("DumpMiniQuests: PO: %O, TI: %O", previous_object(), who));
630 rm(MQ_DUMP_FILE);
631
632 write_file(MQ_DUMP_FILE, "MINIQUESTS: ("+dtime(time())+")\n\n"+
633 " Nr Pkt vis akt vergebendes Objekt\n");
634 string *msg = ({});
635
636 foreach(string obname, int stupse, int nummer, mixed descr, int vis,
637 int active /*, string title, string donedesc, mapping restrictions,
638 string domain, string *permitted_objs*/: miniquests)
639 {
640 msg += ({ sprintf("%4d %4d %4d %4d %s",
641 nummer, stupse, vis, active, obname)});
642 sum_points += stupse;
643 }
644
645 write_file(MQ_DUMP_FILE, implode(sort_array(msg, #'> /*'*/), "\n"));
646 write_file(MQ_DUMP_FILE, sprintf("\n\n"
647 "============================================================\n"
648 +"MiniQuests: %d Miniquests mit %d Punkten.\n\n",
649 sizeof(miniquests), sum_points));
650 return 1;
651}
652
653public int AddMiniQuest(int mquestpoints, string allowedobj, string descr,
654 int active, string title, string donedesc, mapping restrictions,
655 string domain, string *permitted_objs) {
656
657 if (!allowed_write_access())
658 return 0;
659
660 // Parameterpruefung: Questgeber, Restrictions, Region, Titel und
661 // zugelassene Abfrageobjekte muessen gueltig angegeben werden, alles
662 // weitere wird unten ggf. ausgenullt/korrigiert.
663 if (!stringp(allowedobj) || !sizeof(allowedobj) || !mappingp(restrictions)
664 || !stringp(domain) || !stringp(title) || !pointerp(permitted_objs))
665 return -1;
666
667 // Miniquest mit weniger als 1 Stups einzutragen ist wohl unsinnig.
668 if (mquestpoints<1)
669 return -2;
670
671 // Mindestens ein Objekt muss eingetragen werden, das Spielern Informationen
672 // ueber die Aufgabenstellung der MQ geben darf.
673 if ( !sizeof(permitted_objs) )
674 return -3;
675
676 // Pruefen, ob die als Questgeber angegebene Datei existiert.
677 if (allowedobj[<2..] == ".c")
678 allowedobj = allowedobj[0..<3];
679 allowedobj = explode(allowedobj, "#")[0];
Zesstra5cd26092018-11-08 22:21:21 +0100680 allowedobj = (string)master()->make_path_absolute(allowedobj);
MG Mud User88f12472016-06-24 23:31:02 +0200681 if (file_size(allowedobj+".c") <=0)
682 return -3;
683
684 // Vergibt das angegebene Objekt schon eine MQ? Dann abbrechen.
685 if (member(miniquests,allowedobj))
686 return -4;
687
688 if (!stringp(descr) || !sizeof(descr))
689 descr = 0;
690 if (!stringp(donedesc) || !sizeof(donedesc))
691 donedesc = 0;
692
693 // Eintrag hinzufuegen, visible ist per Default immer 1.
694 // MQ-Nummer hochzaehlen
695 int nummer = last_num + 1;
696 m_add(miniquests, allowedobj, mquestpoints, nummer, descr, 1, active,
697 title, donedesc, restrictions, domain, permitted_objs);
698 // und nummer als last_num merken.
699 last_num = nummer;
700 save_info();
701 m_add(by_num, nummer, ({mquestpoints, allowedobj}));
702 MQMLOG(sprintf("AddMiniQuest: %s %O (%s)", allowedobj, miniquests[allowedobj],
703 getuid(this_interactive())));
704
705 ClearUsersMQCache();
706 if (find_call_out(#'DumpMiniQuests) == -1)
707 call_out(#'DumpMiniQuests, 60, this_interactive());
708 return 1;
709}
710
711int RemoveMiniQuest(string name) {
712 if (!allowed_write_access())
713 return 0;
714 // Gibt es einen solchen Eintrag ueberhaupt?
715 if (!member(miniquests,name))
716 return -1;
717
718 MQMLOG(sprintf("RemoveMiniQuest: %s %O (%s)",
719 name, m_entry(miniquests, name), getuid(this_interactive())));
720
721 // MQ aus dem MQ-Indexnummern-Cache loeschen.
722 m_delete(by_num, miniquests[name,MQ_DATA_QUESTNO]);
723 // MQ aus der Miniquestliste austragen.
724 m_delete(miniquests, name);
725 save_info();
726
727 // MQ-Punkte-Cache loeschen, da nicht feststellbar ist, welcher der
728 // dort eingetragenen Spieler die gerade ausgetragene MQ geloest hatte.
729 ClearUsersMQCache();
730 if (find_call_out(#'DumpMiniQuests) == -1)
731 call_out(#'DumpMiniQuests, 60, this_interactive());
732 return 1;
733}
734
735int ChangeMiniQuest(mixed mq_obj, int param, mixed newvalue) {
736 if (!allowed_write_access())
737 return 0;
738
739 // MQ weder als Pfad, noch als Indexnummer angegeben?
740 if ( !stringp(mq_obj) && !intp(mq_obj) && !intp(param))
741 return MQ_KEY_INVALID;
742
743 // gewaehlter Parameter ungueltig?
744 if ( param < MQ_DATA_POINTS || param > MQ_DATA_QUERY_PERMITTED )
745 return MQ_KEY_INVALID;
746
747 // Indexnummer der MQ in den Pfad umwandeln
748 if ( intp(mq_obj) )
749 mq_obj = by_num[mq_obj][1];
750
751 // Vergebendes Objekt nicht gefunden? Bloed, das brauchen wir naemlich.
752 if (!stringp(mq_obj))
753 return MQ_KEY_INVALID;
754
755 if ( !member(miniquests, mq_obj) )
756 return MQ_ILLEGAL_OBJ;
757
758 switch(param) {
759 // MQ_DATA_QUESTNO ist nicht aenderbar, daher hier nicht behandelt, so
760 // dass Fallback auf default erfolgt.
761 // Stufenpunkte muessen Integers sein.
762 case MQ_DATA_POINTS:
763 if ( !intp(newvalue) || newvalue < 1 )
764 return MQ_KEY_INVALID;
765 break;
766 // Aufgabenbeschreibung, Titel, "geschafft"-Text und zugeordnete Region
767 // muessen Strings sein
768 case MQ_DATA_TASKDESC:
769 case MQ_DATA_TITLE:
770 case MQ_DATA_DIARYTEXT:
771 case MQ_DATA_ASSIGNED_DOMAIN:
772 if ( !stringp(newvalue) || !sizeof(newvalue) )
773 return MQ_KEY_INVALID;
774 break;
775 // das Sichtbarkeits- und das aktiv/inaktiv-Flag muessen 0/1 sein.
776 case MQ_DATA_VISIBLE:
777 case MQ_DATA_ACTIVE:
778 if ( !intp(newvalue) || newvalue < 0 || newvalue > 1 )
779 return MQ_KEY_INVALID;
780 break;
781 // Die Voraussetzungen muessen als Mapping eingetragen werden, das aber
782 // leer oder Null sein kann, wenn es keine Restriktionen gibt.
783 case MQ_DATA_RESTRICTIONS:
784 if ( !mappingp(newvalue) && newvalue != 0 )
785 return MQ_KEY_INVALID;
786 break;
787 // Regionszuordnung muss ein nicht-leeres Array sein, das nur aus Strings
788 // bestehen darf, die nicht leer sein duerfen.
789 case MQ_DATA_QUERY_PERMITTED:
790 if ( pointerp(newvalue) ) {
791 newvalue = filter(filter(newvalue, #'stringp), #'sizeof);
792 if (!sizeof(newvalue))
793 return MQ_KEY_INVALID;
794 }
795 else
796 return MQ_KEY_INVALID;
797 break;
798 default:
799 return MQ_KEY_INVALID;
800 }
801
802 mixed *altemq = m_entry(miniquests, mq_obj);
803 int nummer = miniquests[mq_obj,MQ_DATA_QUESTNO];
804 miniquests[mq_obj, param] = newvalue;
805 by_num[nummer] = ({miniquests[mq_obj,MQ_DATA_POINTS], mq_obj});
806 save_info();
807
808 MQMLOG(sprintf("ChangeMiniQuest: %s from %O to %O (%s)", mq_obj,
809 altemq, m_entry(miniquests, mq_obj), getuid(this_interactive())));
810
811 ClearUsersMQCache();
812 if (find_call_out(#'DumpMiniQuests) == -1)
813 call_out(#'DumpMiniQuests, 60, this_interactive());
814 return 1;
815}
816
817mixed QueryMiniQuestByName(string name) {
818 if (!allowed_write_access())
819 return 0;
820 return deep_copy(miniquests & ({name}));
821}
822
823mixed QueryMiniQuestByNumber(int nummer) {
824 // Zugriffsabsicherung erfolgt dort auch, daher hier unnoetig
825 return (by_num[nummer]?QueryMiniQuestByName(by_num[nummer][1]):0);
826}
827
828// Das vollstaendige MQ-Mapping nur als Kopie ausliefern.
829mixed QueryMiniQuests() {
830 return allowed_write_access() ? deep_copy(miniquests) : 0;
831}
832
833// De-/Aktivieren einer Miniquest, wirkt als Umschalter, d.h. eine aktive
834// MQ wird durch Aufruf dieser Funktion als inaktiv markiert und umgekehrt.
835int SwitchMiniQuestActive(string name) {
836 if (!allowed_write_access())
837 return -1;
838 // Haben wir eine solche MQ ueberhaupt?
839 if (!member(miniquests, name))
840 return -2;
841
842 // active-Flag invertieren
843 miniquests[name, MQ_DATA_ACTIVE] = !miniquests[name, MQ_DATA_ACTIVE];
844 save_info();
845
846 MQMLOG(sprintf("%s: %s (%s)",
847 (miniquests[name,MQ_DATA_ACTIVE]?"Activate":"Deactivate"), name,
848 getuid(this_interactive()))
849 );
850 return miniquests[name,MQ_DATA_ACTIVE];
851}
852
853int GiveMiniQuest(object winner) {
854 // Spieler muss existieren und interactive sein.
855 if (!winner ||
856 (this_interactive() && (this_interactive() != winner)) ||
857 ((this_player() == winner) && !query_once_interactive(winner)))
858 return MQ_ILLEGAL_OBJ;
859 // Gaeste koennen keine Miniquests bestehen.
860 if (winner->QueryGuest())
861 return MQ_GUEST;
862 // Aufrufendes Objekt existiert gar nicht?
863 if (!previous_object())
864 return MQ_ILLEGAL_OBJ;
865
866 string objname = load_name(previous_object());
867 // Miniquest muss existieren
868 if (!member(miniquests,objname))
869 return MQ_KEY_INVALID;
870 // Inaktive Miniquests koennen nicht vergeben werden.
871 if (!miniquests[objname, MQ_DATA_ACTIVE])
872 return MQ_IS_INACTIVE;
873
874 string mq = (MASTER->query_mq(getuid(winner)) || "");
875
876 // Spieler hat die MQ schonmal bestanden? Dann keine weiteren Aktivitaet
877 // noetig
878 if (test_bit(mq, miniquests[objname, MQ_DATA_QUESTNO]))
879 return MQ_ALREADY_SET;
880
881 catch(mq = set_bit(mq, miniquests[objname,MQ_DATA_QUESTNO]);publish);
882 MASTER->update_mq(getuid(winner), mq);
883
884 MQSOLVEDLOG(sprintf("%s: %s, (#%d), (Stupse %d)",
885 objname, geteuid(winner), miniquests[objname, MQ_DATA_QUESTNO],
886 miniquests[objname, MQ_DATA_POINTS]));
887
888 // Miniquest-Event ausloesen
889 EVENTD->TriggerEvent( EVT_LIB_MINIQUEST_SOLVED, ([
890 E_OBJECT: previous_object(),
891 E_OBNAME: objname,
892 E_PLNAME: getuid(winner),
893 E_MINIQUESTNAME: miniquests[objname, MQ_DATA_TITLE] ]) );
894
895 // Spielereintrag aus dem MQ-Punkte-Cache loeschen
896 m_delete(users_mq, getuid(winner));
897
898 return 1;
899}
900
901int QueryMiniQuestPoints(mixed pl) {
902 string spieler;
903
904 //if (!allowed_write_access())
905 // return 0;
906
907 if (!pl)
908 return -1;
909
910 if (!objectp(pl) && !stringp(pl))
911 return -2;
912
913 if (objectp(pl) && !query_once_interactive(pl))
914 return -3;
915
916 if (objectp(pl))
917 spieler = getuid(pl);
918 else
919 spieler = pl;
920
921 if (!member(users_mq, spieler)) {
922 int mqpoints;
923 int p=-1;
924 string s = (MASTER->query_mq(spieler) || "");
925 while( (p=next_bit(s, p)) != -1) {
926 mqpoints+=by_num[p][0];
927 }
928 users_mq[spieler] = mqpoints;
929 }
930 return users_mq[spieler];
931}
932
933int HasMiniQuest(mixed pl, mixed name) {
934 string mq, spieler;
935
936 if (!pl || !name)
937 return MQ_ILLEGAL_OBJ;
938
939 if (!objectp(pl) && !stringp(pl))
940 return MQ_ILLEGAL_OBJ;
941
942 if (objectp(pl) && !query_once_interactive(pl))
943 return MQ_ILLEGAL_OBJ;
944
945 if (!objectp(name) && !stringp(name) && !intp(name))
946 return MQ_ILLEGAL_OBJ;
947
948 if (objectp(name))
949 name = explode(object_name(name), "#")[0];
950
951 if ( intp(name) )
952 name = by_num[name][1];
953
954 if (objectp(pl))
955 spieler = getuid(pl);
956 else
957 spieler = pl;
958
959 if (!member(miniquests,name))
960 return MQ_KEY_INVALID;
961
962 mq = (MASTER->query_mq(spieler) || "");
963
964 return test_bit(mq, miniquests[name, MQ_DATA_QUESTNO]);
965}
966
967// Zum Von-Hand-Setzen der MiniQuests
968int SetPlayerMiniQuest(string pl, string name) {
969 if(!allowed_write_access())
970 return 0;
971 if(!pl)
972 return MQ_ILLEGAL_OBJ;
973 if(!previous_object())
974 return MQ_ILLEGAL_OBJ;
975
976 if (!member(miniquests,name))
977 return MQ_KEY_INVALID;
978
979 string mq = (MASTER->query_mq(pl) || "");
980
981 if (test_bit(mq, miniquests[name,MQ_DATA_QUESTNO]))
982 return MQ_ALREADY_SET;
983
984 catch (mq = set_bit(mq, miniquests[name, MQ_DATA_QUESTNO]);publish);
985 MASTER->update_mq(pl, mq);
986
987 MQMLOG(sprintf("SetPlayerMiniQuest: %s %s (%s)",
988 pl, name, getuid(this_interactive())));
989 // Spielereintrag aus dem MQ-Punkte-Cache loeschen
990 m_delete(users_mq, pl);
991 return 1;
992}
993
994int ClearPlayerMiniQuest(string pl, string name) {
995 if (!allowed_write_access())
996 return 0;
997 if (!pl)
998 return MQ_ILLEGAL_OBJ;
999 if (!previous_object())
1000 return MQ_ILLEGAL_OBJ;
1001
1002 if (!member(miniquests,name))
1003 return MQ_KEY_INVALID;
1004
1005 string mq = (MASTER->query_mq(pl) || "");
1006
1007 if (!test_bit(mq, miniquests[name, MQ_DATA_QUESTNO]))
1008 return MQ_ALREADY_SET;
1009
1010 catch (mq = clear_bit(mq, miniquests[name, MQ_DATA_QUESTNO]);publish);
1011 MASTER->update_mq(pl, mq);
1012
1013 MQMLOG(sprintf("ClearPlayerMiniQuest: %s %s (%s)",
1014 pl, name, getuid(this_interactive())));
1015 // Spielereintrag aus dem MQ-Punkte-Cache loeschen
1016 m_delete(users_mq, pl);
1017 return 1;
1018}
1019
1020// Umtragen von Miniquests von Objekt <old> auf Objekt <new>
1021int MoveMiniQuest(string old_mqob, string new_mqob) {
1022 if ( !allowed_write_access() )
1023 return -1;
1024
1025 // Haben wir ueberhaupt einen solchen Eintrag?
1026 if ( !member(miniquests, old_mqob) )
1027 return -2;
1028
1029 // Pruefen, ob die als <new_mqob> angegebene Datei existiert.
1030 if (new_mqob[<2..] == ".c")
1031 new_mqob = new_mqob[0..<3];
1032 new_mqob = explode(new_mqob, "#")[0];
Zesstra5cd26092018-11-08 22:21:21 +01001033 new_mqob = (string)master()->make_path_absolute(new_mqob);
MG Mud User88f12472016-06-24 23:31:02 +02001034 if (file_size(new_mqob+".c") <= 0)
1035 return -3;
1036 // Wenn das neue Objekt schon eine MQ vergibt, kann es keine weitere
1037 // annehmen.
1038 if ( member(miniquests, new_mqob) )
1039 return -4;
1040
1041 // Der Miniquestliste einen neuen Key "new" mit den Daten des alten Keys
1042 // hinzufuegen. m_entry() liefert alle Values dazu als Array, und der
1043 // flatten-Operator "..." uebergibt dessen Elemente als einzelne Parameter.
1044 m_add(miniquests, new_mqob, m_entry(miniquests, old_mqob)...);
1045 m_delete(miniquests, old_mqob);
1046 // Nummern-Index auch umtragen, sonst koennen Funktionen wie zB
1047 // QueryMiniQuestByNumber() die neue nicht finden.
1048 by_num[miniquests[new_mqob,MQ_DATA_QUESTNO]][1] = new_mqob;
1049 return 1;
1050}
1051
1052#define FRA_BIB "/d/ebene/miril/fraternitas/room/bibliothek"
1053
1054// Erlaubt die Abfrage aller MQs einer bestimmten Region fuer die Bibliothek
1055// der kleinen und grossen Heldentaten in der Fraternitas.
1056// Gibt ein Mapping der Form ([ indexnummer : titel; erledigt_Beschreibung ])
1057// zurueck.
1058mapping QuerySolvedMQsByDomain(mixed pl, string region) {
1059 if ( !objectp(pl) && !stringp(pl) &&
1060 load_name(previous_object())!=FRA_BIB) /*|| !allowed_write_access())*/
1061 return ([:2]);
1062
1063 mapping res = m_allocate(30,2); // reicht vermutlich
1064 // Die angegebene Region muss in der Spalte MQ_DATA_ASSIGNED_DOMAIN
1065 // enthalten sein, und das abfragende Objekt muss die Fraternitas-Bib sein
1066 foreach(string mqobj, int mqp, int index, string task, int vis, int act,
1067 string title, string donedesc, mapping restr, string domain:
1068 miniquests) {
1069 // aktive MQs der angeforderten Region zusammentragen, die der Spieler
1070 // bestanden hat.
1071 if ( domain == region && act && HasMiniQuest(pl, mqobj) )
1072 m_add(res, index, title, donedesc);
1073 }
1074 //DEBUG(sprintf("%O\n",res));
1075 return res;
1076}
1077#undef FRA_BIB
1078
1079// Abfrage der noch offenen MQs des angegebenen Spielers.
1080// Zurueckgegeben wird ein Mapping mit den Miniquest-Nummern als Keys und
1081// den Aufgabenbeschreibungen als Values, oder ein leeres Mapping, falls
1082// das abfragende Objekt keine Zugriffsberechtigung hat, oder das
1083// uebergebene Spielerobjekt keine offenen Miniquests mehr hat.
1084mapping QueryOpenMiniQuestsForPlayer(object spieler) {
1085 // map() etwas beschleunigen
1086 closure chk_restr = symbol_function("check_restrictions",
1087 "/std/restriction_checker");
1088 // Cache-Eintrag fuer das abfragende Objekt holen
1089 string *list = mq_query_permitted[load_name(previous_object())];
1090 mapping res = ([:2]);
1091
1092 if (!pointerp(list) || !sizeof(list))
1093 return res;
1094 // Liste der MQ-Objekte umwandeln in deren MQ-Nummer plus
1095 // Aufgabenbeschreibung, sofern der Spieler die MQ noch nicht bestanden
1096 // hat, aber die Voraussetzungen erfuellt.
1097 foreach ( string mq_obj : list )
1098 {
Bugfixbf92f882017-05-24 20:23:48 +02001099 // Ist die MQ nicht aktiv, ist der Rest hinfaellig.
1100 if(!miniquests[mq_obj,MQ_DATA_ACTIVE]) continue;
1101
MG Mud User88f12472016-06-24 23:31:02 +02001102 // Nur wenn der Spieler die MQ noch nicht hat, kann er ueberhaupt einen
1103 // Tip dazu bekommen.
1104 if ( !HasMiniQuest(spieler, mq_obj) ) {
1105 // Restriction Checker fragen, ob der Spieler statt des Hinweises
1106 // eine Info bekommen muss, dass er eine Vorbedingung nicht erfuellt.
1107 string restr_result = funcall(chk_restr, spieler, miniquests[mq_obj,
1108 MQ_DATA_RESTRICTIONS]);
1109 // Wenn so eine Info dabei rauskommt, wird diese in die Ergebnisliste
1110 // uebernommen. In diesem Fall wird KEIN MQ-Hinweistext ausgeliefert.
1111 if ( stringp(restr_result) )
1112 {
1113 m_add(res, miniquests[mq_obj,MQ_DATA_QUESTNO], 0, restr_result);
1114 }
1115 // Anderenfalls wird der Hinweistext uebernommen; einen Eintrag
1116 // bzgl. eines eventuellen Hinderungsgrundes gibt's dann nicht.
1117 else
1118 {
1119 m_add(res, miniquests[mq_obj,MQ_DATA_QUESTNO],
1120 miniquests[mq_obj,MQ_DATA_TASKDESC], 0);
1121 }
1122 }
1123 }
1124 // Ergebnisliste zurueckgeben.
1125 return res;
1126}
1127
1128// Datenkonverter fuer das bisherige MQ-Mapping von
1129// ([ obj : ({daten1..daten5}) ]) nach
1130// ([ obj : daten1; daten2; ...; daten9 ]), wobei daten6..9 als Leerspalten
1131// erzeugt werden.
1132/*void ConvertMQData() {
1133 if ( !allowed_write_access() )
1134 return;
1135
1136 by_num=([]);
1137 // spaltenweise aus dem miniquests-Mapping ein Array aus Arrays erzeugen
1138 // Zunaechst die Keys des Mappings aufnehmen.
1139 mixed *mqdata = ({m_indices(miniquests)});
1140
1141 // Dann das Datenarray spaltenweise dazu (jedes Array ist eine Spalte).
1142 mqdata += transpose_array(m_values(miniquests));
1143 // 1. Array: Keys, alle weiteren jeweils eine Wertespalte
1144
1145 // Array erzeugen als Array aus 5 Array-Elementen, die alle soviele
1146 // Nullen enthalten, wie es Miniquests gibt,
1147 // ({ ({0,0,...}), ({0,0,...}), ({0,0,...}), ({0,0,...}), ({0,0,...}) })
1148 // dieses hinzufuegen. Sind die hinzukommenden 5 Spalten.
1149 mqdata += allocate(5, allocate(sizeof(miniquests),0));
1150
1151 // Mapping erzeugen, indem mit dem flatten-Operator "..." die Einzel-
1152 // Arrays des Datenpakets uebergeben werden. Erzeugt auf diese Weise
1153 // ([ keys : daten1; daten2; daten3; daten4; daten5; 0; 0; 0; 0; 0,])
1154 miniquests=mkmapping(mqdata...);
1155}
1156
1157// Neue Daten einlesen, Visible-Flag bei allen MQs per Default auf 1 setzen.
1158// Es gibt keine wirklich unsichtbaren MQs mehr.
1159void ReadNewData() {
1160 if ( !allowed_write_access() )
1161 return;
1162
1163 string *import = explode(read_file("/players/arathorn/mqdata"),"\n")-({""});
1164 string *fields;
1165 foreach(string mqdata : import) {
1166 fields = explode(mqdata, "#")-({""});
1167 DEBUG(sprintf("%O\n", fields[0]));
1168 if ( miniquests[fields[4], MQ_DATA_QUESTNO] != to_int(fields[0]) ) {
1169 raise_error("MQ-Nummern stimmen nicht ueberein!\n");
1170 return;
1171 }
1172 // fields[4] ist der MQ-Objektpfad
1173 miniquests[fields[4], MQ_DATA_TITLE] = fields[7];
1174 miniquests[fields[4], MQ_DATA_DIARYTEXT] = fields[6];
1175 miniquests[fields[4], MQ_DATA_TASKDESC] = fields[5];
1176 miniquests[fields[4], MQ_DATA_VISIBLE] = 1; // Default: visible
1177 miniquests[fields[4], MQ_DATA_ASSIGNED_DOMAIN] = fields[8];
1178 miniquests[fields[4], MQ_DATA_QUERY_PERMITTED] = fields[9];
1179 if ( fields[3] != "0" ) {
1180 miniquests[fields[4], MQ_DATA_RESTRICTIONS] =
1181 restore_value(fields[3]+"\n");
1182 }
1183 else miniquests[fields[4], MQ_DATA_RESTRICTIONS] = 0;
1184 if ( fields[9] != "0" )
1185 miniquests[fields[4], MQ_DATA_QUERY_PERMITTED] =
1186 restore_value(fields[9]+"\n");
1187 else miniquests[fields[4], MQ_DATA_QUERY_PERMITTED] = 0;
1188 }
1189}
1190*/