blob: aae566d8588d5a3992618b2362a3ba4d2b9d0378 [file] [log] [blame]
// MorgenGrauen MUDlib
//
// player/quests.c -- quest handler
//
// $Id: quests.c 9142 2015-02-04 22:17:29Z Zesstra $
// Dieses Modul enhaelt die Quest-spezifischen Teile der Playerobjekte.
#pragma strong_types
#pragma save_types
#pragma range_check
#pragma no_clone
#pragma pedantic
#define NEED_PROTOTYPES
#include <player/life.h>
#include <player/quest.h>
#include <thing/properties.h>
#include <player/base.h>
#include <living/life.h>
#undef NEED_PROTOTYPES
#include "/secure/questmaster.h"
#include <wizlevels.h>
#include <daemon.h>
#include <language.h>
#include <mail.h>
#include <defines.h>
#include <new_skills.h>
#include <properties.h>
#include <events.h>
mixed quests;
int QueryQuest(string questname);
// local properties prototype
static mixed _query_quests();
static int _query_questpoints();
protected void create() {
Set(P_QUESTS, NOSETMETHOD, F_SET_METHOD);
Set(P_QUESTS, quests = ([]), F_VALUE);
Set(P_QUESTS, SECURED, F_MODE);
Set(P_QP, SAVE, F_MODE);
Set(P_QP, SECURED, F_MODE);
}
varargs int GiveQuest(string questname, string message) {
mixed *quest = QM->QueryQuest(questname);
// Questname ungueltig
if (!quest||!pointerp(quest)||quest==({}))
return GQ_KEY_INVALID;
// Unbefugter Zugriff auf deaktivierte Quest
if (!quest[6]&&!IS_ARCH(this_interactive()))
return GQ_IS_INACTIVE;
// Unbefugter Zugriff
if (member(quest[2], load_name(previous_object()))==-1 &&
!IS_ARCH(this_interactive()))
return GQ_ILLEGAL_OBJ;
// Gilde wird in jedem Fall informiert.
string guild=GUILD_DIR+QueryProp(P_GUILD);
if (find_object(guild) || file_size(guild+".c")>-1)
catch( call_other(guild, "NotifyGiveQuest", ME, questname);publish );
// Quest bereits gesetzt
if (QueryQuest(questname))
return GQ_ALREADY_SET;
AddExp(quest[1]);
quests += ([ questname : quest[0]; time() ]);
force_save();
// Event ausloesen
EVENTD->TriggerEvent(EVT_LIB_QUEST_SOLVED,([
E_OBJECT: ME,
E_PLNAME: getuid(ME),
E_ENVIRONMENT: environment(),
E_QUESTNAME: questname,
E_QP_GRANTED: quest[0] ]) );
if (message && message!="") {
if (message!="__silent__") {
message=implode(explode(message,"@@name@@"),
capitalize(query_real_name()));
}
else {
message="";
}
}
else
message=capitalize(query_real_name())
+" hat gerade ein Abenteuer bestanden: "+ questname+"\n";
if(message!="")
catch(QM->Channel(message);publish);
catch(QM->SendMail(questname, quest, ME);publish);
return OK;
}
int DeleteQuest(string questname) {
// Quest ist nicht gesetzt
if(!QueryQuest(questname))
return DQ_NOT_SET;
mixed *quest = QM->QueryQuest(questname);
// Questname ungueltig
if (!quest||!pointerp(quest)||quest==({}))
return DQ_KEY_INVALID;
// Unbefugter Zugriff
if (!IS_ARCH(this_interactive()))
return DQ_ILLEGAL_OBJ;
AddExp(-quest[1]);
m_delete(quests, questname);
force_save();
return OK;
}
int QueryQuest(string questname) {
int dummy;
// Gaeste haben keine Quests.
if( sscanf( getuid(), "gast%d", dummy ) == 1 )
return QQ_GUEST;
// Uebergebener Parameter "questname" ungueltig oder leer?
if(!questname || !stringp(questname) || questname == "")
return QQ_KEY_INVALID;
// Questname ist tatsaechlich in der Questliste enthalten? Alles klar!
if ( member(quests, questname) )
return OK;
// Ansonsten war der Name wohl ungueltig.
return QQ_KEY_INVALID;
}
int ModifyQuestTime(string qname, int when) {
if ( process_call() )
return -1;
// Nur EM+ oder der Tagebuchmaster duerfen die Werte aendern.
if ( !IS_ARCH(this_interactive()) &&
load_name(previous_object())!="/d/wald/leusel/quest/objs/tagebuch-master")
return -1;
// Questliste ist unerwartet kein Mapping.
if ( !mappingp(quests) )
return -2;
// Kein Questname angegeben, oder Spieler hat diese Quest ueberhaupt nicht
// geloest.
if ( !stringp(qname) || !member(quests, qname) )
return -3;
// Der Tagebuchmaster setzt Eintraege ggf. auf 0, wenn er keine Daten
// findet, und EM+ wollen Eintraege auf -1 setzen koennen, um das Einlesen
// der Daten noch einmal zu ermoeglichen, d.h. diese Werte sind zusaetzlich
// zu gueltigen Zeitwerten erlaubt.
if ( !intp(when) || when < -1 || when > time() )
return -4;
// Neuen Wert eintragen.
quests[qname,1] = when;
return 1;
}
int QueryQuestTime(string qname) {
return quests[qname,1];
}
// Konvertiert Datenstruktur von quests in ein Mapping mit folgendem Aufbau:
// quests = ([ "questname" : Abenteuerpunkte; Zeit_des_Questabschlusses, ])
protected void updates_after_restore(mixed newflag) {
// Ganz frischer Spieler? Dann keine Behandlung erforderlich.
if ( newflag )
return;
// Wenn die Questliste noch kein Mapping ist, Konvertierung anstossen.
if ( !mappingp(quests) ) {
// Wenn noch keine Quests eingetragen sind, Leermapping eintragen.
if ( !sizeof(quests) ) {
quests = ([:2]);
return;
}
// Vorsichtshalber Leereintraege rausziehen.
quests -= ({({})});
// Liste der Questzeiten aus dem Spieler auslesen. Wenn nicht vorhanden,
// Array mit -1-Elementen passender Laenge erzeugen.
// -1 an allen Stellen eintragen, wo bisher keine Daten vorliegen.
// Diese werden dann vom Questtagebuch durch die Daten ersetzt.
int *qtimes = QueryProp("questtime")||allocate(sizeof(quests), -1);
// Falls die Questliste laenger ist als die Zeitenliste, wird letztere
// um die fehlende Laenge in Form von -1-eintraegen ergaenzt, unter der
// Annahme, dass die fehlenden Eintraege bisher lediglich nicht
// eingelesen wurden. Im umgekehrten Fall werden alle Zeiten verworfen,
// da dieser Fall eintritt, wenn einem Spieler eine Quest ausgetragen
// wurde und die Listen in der Vergangenheit nicht synchron gehalten
// wurden.
if ( sizeof(qtimes) < sizeof(quests) ) {
qtimes += allocate(sizeof(quests)-sizeof(qtimes), -1);
}
if ( sizeof(qtimes) > sizeof(quests) ) {
qtimes = allocate(sizeof(quests), -1);
}
// Questdaten und Questzeiten zusammenpferchen. Ergibt folg. Mapping:
// temp = ([ ({Questname1, QP1}) : Questzeit, ... ])
mapping temp = mkmapping(quests, qtimes);
quests = m_allocate(sizeof(quests),2);
foreach(mixed qdata, int qtime : temp) {
quests += ([ qdata[0] : qdata[1]; qtime ]);
}
if (QueryProp("questtime")) {
SetProp("questtime",0);
Set("questtime", SAVE, F_MODE_AD);
}
}
}
// **** local property methods
static int _query_questpoints() {
int qp;
if ( !mappingp(quests) || !sizeof(quests) ) {
return 0;
}
closure qqp = symbol_function("QueryQuestPoints", QM);
// Die aktuell gueltigen Abenteuerpunkte aus dem Questmaster holen und
// die Questliste damit aktualisieren. qp wird als Referenz mit uebergeben
// damit das Additionsergebnis auch nach dem Durchlauf ausserhalb der
// Closure verfuegbar ist.
// Falls Abenteuerpunkte < 0 existieren, wird die entsprechende Quest
// aus der Liste ausgetragen.
walk_mapping(quests,
function void (string qname, int qpoints, int qtime, int sum)
{
qpoints = funcall(qqp, qname);
if (qpoints<0)
m_delete(quests,qname);
else
sum += qpoints;
}, &qp);
Set(P_QP, qp);
return qp;
}
static mixed _query_quests() {
return copy(quests);
}