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