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