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