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