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