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