MG Mud User | 88f1247 | 2016-06-24 23:31:02 +0200 | [diff] [blame^] | 1 | // MorgenGrauen MUDlib |
| 2 | // |
| 3 | // player/quests.c -- quest handler |
| 4 | // |
| 5 | // $Id: quests.c 9142 2015-02-04 22:17:29Z Zesstra $ |
| 6 | |
| 7 | // Dieses Modul enhaelt die Quest-spezifischen Teile der Playerobjekte. |
| 8 | #pragma strong_types |
| 9 | #pragma save_types |
| 10 | #pragma range_check |
| 11 | #pragma no_clone |
| 12 | #pragma pedantic |
| 13 | |
| 14 | #define NEED_PROTOTYPES |
| 15 | #include <player/life.h> |
| 16 | #include <player/quest.h> |
| 17 | #include <thing/properties.h> |
| 18 | #include <player/base.h> |
| 19 | #include <living/life.h> |
| 20 | #undef NEED_PROTOTYPES |
| 21 | |
| 22 | #include "/secure/questmaster.h" |
| 23 | #include <wizlevels.h> |
| 24 | #include <daemon.h> |
| 25 | #include <language.h> |
| 26 | #include <mail.h> |
| 27 | #include <defines.h> |
| 28 | #include <new_skills.h> |
| 29 | #include <properties.h> |
| 30 | #include <events.h> |
| 31 | |
| 32 | mixed quests; |
| 33 | |
| 34 | int QueryQuest(string questname); |
| 35 | // local properties prototype |
| 36 | static mixed _query_quests(); |
| 37 | static int _query_questpoints(); |
| 38 | |
| 39 | protected void create() { |
| 40 | Set(P_QUESTS, NOSETMETHOD, F_SET_METHOD); |
| 41 | Set(P_QUESTS, quests = ([]), F_VALUE); |
| 42 | Set(P_QUESTS, SECURED, F_MODE); |
| 43 | Set(P_QP, SAVE, F_MODE); |
| 44 | Set(P_QP, SECURED, F_MODE); |
| 45 | } |
| 46 | |
| 47 | varargs int GiveQuest(string questname, string message) { |
| 48 | mixed *quest = QM->QueryQuest(questname); |
| 49 | |
| 50 | // Questname ungueltig |
| 51 | if (!quest||!pointerp(quest)||quest==({})) |
| 52 | return GQ_KEY_INVALID; |
| 53 | // Unbefugter Zugriff auf deaktivierte Quest |
| 54 | if (!quest[6]&&!IS_ARCH(this_interactive())) |
| 55 | return GQ_IS_INACTIVE; |
| 56 | // Unbefugter Zugriff |
| 57 | if (member(quest[2], load_name(previous_object()))==-1 && |
| 58 | !IS_ARCH(this_interactive())) |
| 59 | return GQ_ILLEGAL_OBJ; |
| 60 | |
| 61 | // Gilde wird in jedem Fall informiert. |
| 62 | string guild=GUILD_DIR+QueryProp(P_GUILD); |
| 63 | if (find_object(guild) || file_size(guild+".c")>-1) |
| 64 | catch( call_other(guild, "NotifyGiveQuest", ME, questname);publish ); |
| 65 | |
| 66 | // Quest bereits gesetzt |
| 67 | if (QueryQuest(questname)) |
| 68 | return GQ_ALREADY_SET; |
| 69 | AddExp(quest[1]); |
| 70 | quests += ([ questname : quest[0]; time() ]); |
| 71 | force_save(); |
| 72 | // Event ausloesen |
| 73 | EVENTD->TriggerEvent(EVT_LIB_QUEST_SOLVED,([ |
| 74 | E_OBJECT: ME, |
| 75 | E_PLNAME: getuid(ME), |
| 76 | E_ENVIRONMENT: environment(), |
| 77 | E_QUESTNAME: questname, |
| 78 | E_QP_GRANTED: quest[0] ]) ); |
| 79 | |
| 80 | if (message && message!="") { |
| 81 | if (message!="__silent__") { |
| 82 | message=implode(explode(message,"@@name@@"), |
| 83 | capitalize(query_real_name())); |
| 84 | } |
| 85 | else { |
| 86 | message=""; |
| 87 | } |
| 88 | } |
| 89 | else |
| 90 | message=capitalize(query_real_name()) |
| 91 | +" hat gerade ein Abenteuer bestanden: "+ questname+"\n"; |
| 92 | if(message!="") |
| 93 | catch(QM->Channel(message);publish); |
| 94 | catch(QM->SendMail(questname, quest, ME);publish); |
| 95 | return OK; |
| 96 | } |
| 97 | |
| 98 | int DeleteQuest(string questname) { |
| 99 | // Quest ist nicht gesetzt |
| 100 | if(!QueryQuest(questname)) |
| 101 | return DQ_NOT_SET; |
| 102 | |
| 103 | mixed *quest = QM->QueryQuest(questname); |
| 104 | // Questname ungueltig |
| 105 | if (!quest||!pointerp(quest)||quest==({})) |
| 106 | return DQ_KEY_INVALID; |
| 107 | // Unbefugter Zugriff |
| 108 | if (!IS_ARCH(this_interactive())) |
| 109 | return DQ_ILLEGAL_OBJ; |
| 110 | AddExp(-quest[1]); |
| 111 | m_delete(quests, questname); |
| 112 | force_save(); |
| 113 | return OK; |
| 114 | } |
| 115 | |
| 116 | int QueryQuest(string questname) { |
| 117 | int dummy; |
| 118 | |
| 119 | // Gaeste haben keine Quests. |
| 120 | if( sscanf( getuid(), "gast%d", dummy ) == 1 ) |
| 121 | return QQ_GUEST; |
| 122 | // Uebergebener Parameter "questname" ungueltig oder leer? |
| 123 | if(!questname || !stringp(questname) || questname == "") |
| 124 | return QQ_KEY_INVALID; |
| 125 | // Questname ist tatsaechlich in der Questliste enthalten? Alles klar! |
| 126 | if ( member(quests, questname) ) |
| 127 | return OK; |
| 128 | // Ansonsten war der Name wohl ungueltig. |
| 129 | return QQ_KEY_INVALID; |
| 130 | } |
| 131 | |
| 132 | int ModifyQuestTime(string qname, int when) { |
| 133 | if ( process_call() ) |
| 134 | return -1; |
| 135 | |
| 136 | // Nur EM+ oder der Tagebuchmaster duerfen die Werte aendern. |
| 137 | if ( !IS_ARCH(this_interactive()) && |
| 138 | load_name(previous_object())!="/d/wald/leusel/quest/objs/tagebuch-master") |
| 139 | return -1; |
| 140 | |
| 141 | // Questliste ist unerwartet kein Mapping. |
| 142 | if ( !mappingp(quests) ) |
| 143 | return -2; |
| 144 | |
| 145 | // Kein Questname angegeben, oder Spieler hat diese Quest ueberhaupt nicht |
| 146 | // geloest. |
| 147 | if ( !stringp(qname) || !member(quests, qname) ) |
| 148 | return -3; |
| 149 | |
| 150 | // Der Tagebuchmaster setzt Eintraege ggf. auf 0, wenn er keine Daten |
| 151 | // findet, und EM+ wollen Eintraege auf -1 setzen koennen, um das Einlesen |
| 152 | // der Daten noch einmal zu ermoeglichen, d.h. diese Werte sind zusaetzlich |
| 153 | // zu gueltigen Zeitwerten erlaubt. |
| 154 | if ( !intp(when) || when < -1 || when > time() ) |
| 155 | return -4; |
| 156 | |
| 157 | // Neuen Wert eintragen. |
| 158 | quests[qname,1] = when; |
| 159 | return 1; |
| 160 | } |
| 161 | |
| 162 | int QueryQuestTime(string qname) { |
| 163 | return quests[qname,1]; |
| 164 | } |
| 165 | |
| 166 | // Konvertiert Datenstruktur von quests in ein Mapping mit folgendem Aufbau: |
| 167 | // quests = ([ "questname" : Abenteuerpunkte; Zeit_des_Questabschlusses, ]) |
| 168 | protected void updates_after_restore(mixed newflag) { |
| 169 | // Ganz frischer Spieler? Dann keine Behandlung erforderlich. |
| 170 | if ( newflag ) |
| 171 | return; |
| 172 | // Wenn die Questliste noch kein Mapping ist, Konvertierung anstossen. |
| 173 | if ( !mappingp(quests) ) { |
| 174 | // Wenn noch keine Quests eingetragen sind, Leermapping eintragen. |
| 175 | if ( !sizeof(quests) ) { |
| 176 | quests = ([:2]); |
| 177 | return; |
| 178 | } |
| 179 | // Vorsichtshalber Leereintraege rausziehen. |
| 180 | quests -= ({({})}); |
| 181 | // Liste der Questzeiten aus dem Spieler auslesen. Wenn nicht vorhanden, |
| 182 | // Array mit -1-Elementen passender Laenge erzeugen. |
| 183 | // -1 an allen Stellen eintragen, wo bisher keine Daten vorliegen. |
| 184 | // Diese werden dann vom Questtagebuch durch die Daten ersetzt. |
| 185 | int *qtimes = QueryProp("questtime")||allocate(sizeof(quests), -1); |
| 186 | // Falls die Questliste laenger ist als die Zeitenliste, wird letztere |
| 187 | // um die fehlende Laenge in Form von -1-eintraegen ergaenzt, unter der |
| 188 | // Annahme, dass die fehlenden Eintraege bisher lediglich nicht |
| 189 | // eingelesen wurden. Im umgekehrten Fall werden alle Zeiten verworfen, |
| 190 | // da dieser Fall eintritt, wenn einem Spieler eine Quest ausgetragen |
| 191 | // wurde und die Listen in der Vergangenheit nicht synchron gehalten |
| 192 | // wurden. |
| 193 | if ( sizeof(qtimes) < sizeof(quests) ) { |
| 194 | qtimes += allocate(sizeof(quests)-sizeof(qtimes), -1); |
| 195 | } |
| 196 | if ( sizeof(qtimes) > sizeof(quests) ) { |
| 197 | qtimes = allocate(sizeof(quests), -1); |
| 198 | } |
| 199 | // Questdaten und Questzeiten zusammenpferchen. Ergibt folg. Mapping: |
| 200 | // temp = ([ ({Questname1, QP1}) : Questzeit, ... ]) |
| 201 | mapping temp = mkmapping(quests, qtimes); |
| 202 | quests = m_allocate(sizeof(quests),2); |
| 203 | foreach(mixed qdata, int qtime : temp) { |
| 204 | quests += ([ qdata[0] : qdata[1]; qtime ]); |
| 205 | } |
| 206 | if (QueryProp("questtime")) { |
| 207 | SetProp("questtime",0); |
| 208 | Set("questtime", SAVE, F_MODE_AD); |
| 209 | } |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | // **** local property methods |
| 214 | static int _query_questpoints() { |
| 215 | int qp; |
| 216 | |
| 217 | if ( !mappingp(quests) || !sizeof(quests) ) { |
| 218 | return 0; |
| 219 | } |
| 220 | |
| 221 | closure qqp = symbol_function("QueryQuestPoints", QM); |
| 222 | // Die aktuell gueltigen Abenteuerpunkte aus dem Questmaster holen und |
| 223 | // die Questliste damit aktualisieren. qp wird als Referenz mit uebergeben |
| 224 | // damit das Additionsergebnis auch nach dem Durchlauf ausserhalb der |
| 225 | // Closure verfuegbar ist. |
| 226 | // Falls Abenteuerpunkte < 0 existieren, wird die entsprechende Quest |
| 227 | // aus der Liste ausgetragen. |
| 228 | walk_mapping(quests, |
| 229 | function void (string qname, int qpoints, int qtime, int sum) |
| 230 | { |
| 231 | qpoints = funcall(qqp, qname); |
| 232 | if (qpoints<0) |
| 233 | m_delete(quests,qname); |
| 234 | else |
| 235 | sum += qpoints; |
| 236 | }, &qp); |
| 237 | |
| 238 | Set(P_QP, qp); |
| 239 | return qp; |
| 240 | } |
| 241 | |
| 242 | static mixed _query_quests() { |
| 243 | return copy(quests); |
| 244 | } |