blob: aae566d8588d5a3992618b2362a3ba4d2b9d0378 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// 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
32mixed quests;
33
34int QueryQuest(string questname);
35// local properties prototype
36static mixed _query_quests();
37static int _query_questpoints();
38
39protected 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
47varargs 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
98int 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
116int 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
132int 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
162int 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, ])
168protected 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
214static 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
242static mixed _query_quests() {
243 return copy(quests);
244}