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