/* * dictionary.h * * Copyright (C) 2001 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation (version 2 of the License) * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #ifndef PS_DICTIONARY_ #define PS_DICTIONARY_ #include "util/prb.h" #include #include #include #include #include "rpgrules/psmoney.h" #include "psquest.h" #include "../client.h" #include "../server/iserver/idal.h" // Database Abstraction Layer Interface #include "../tools/wordnet/wn.h" #define MAX_RESP 5 class NpcTerm; class NpcTriggerGroupEntry; class NpcTrigger; class NpcResponse; class psSkillInfo; class gemNPC; class NPCDialogDict : public csRefCount { protected: BinaryRBTree phrases; BinaryRBTree trigger_groups; csHash trigger_groups_by_id; BinaryRBTree triggers; BinaryRBTree responses; BinaryRBTree disallowed_words; int dynamic_id; bool LoadSynonyms(iDataConnection *db); bool LoadTriggerGroups(iDataConnection *db); bool LoadTriggers(iDataConnection *db); bool LoadResponses(iDataConnection *db); bool LoadDisallowedWords(iDataConnection *db); /** All unknown words from 'trigger' that are not disallowed are added to 'phrases'. All disallowed words from 'trigger' are removed from 'trigger' */ void AddWords(csString& trigger); /** * Adds a new record to 'terms' * * @return A new term or and existing term if allready added. */ NpcTerm* AddTerm(const char *term); public: NPCDialogDict(); virtual ~NPCDialogDict(); bool Initialize(iDataConnection *db); /** Returns record of 'term' (or NULL if unknown) */ NpcTerm * FindTerm(const char *term); /** Returns synonym of 'term' (or NULL if unknown). If 'term' is known but has no synonym, then 'term' itself is returned */ NpcTerm * FindTermOrSynonym(const csString & term); NpcResponse *FindResponse(gemNPC * npc, const char *area, const char *trigger, int faction, int priorresponse, Client *client); /** Lookup the response with the given ID from responses */ NpcResponse * FindResponse(int responseID); /** Loads a trigger from database (its id=databaseID) */ void AddTrigger( iDataConnection* db, int databaseID ); /** substitute master trigger if this is child trigger in group. return true if substituted */ bool CheckForTriggerGroup(csString& trigger); /** Loads a response from database (its id=databaseID) */ void AddResponse( iDataConnection* db, int databaseID ); /** Adds a response dynamically not from the database. Supplies new_id if 0. */ NpcResponse *AddResponse(const char *response_text, const char *pronoun_him, const char *pronoun_her, const char *pronoun_it, const char *pronoun_them, int &new_id, psQuest * quest); NpcTrigger *AddTrigger(const char *k_area, const char *mytrigger, int prior_response, int trigger_response); void DeleteTriggerResponse(NpcTrigger * trigger, int responseId); /** * Dump the entire dictionary */ void Print(const char *area); }; /** * A phrase recognized by the dialog system. Each term can hold a pointer to a * synonym e.g. hello = greetings or a more general term e.g. "apple" --> "fruit". */ class NpcTerm { SynsetPtr hypernymSynNet; csArray hypernyms; void BuildHypernymList(); public: /** * The recognized phrase/term */ csString term; /** * Pointer to a synonym term. * * If nonzero, phrase is translated to 'synonym' * * Phrases that have 'synonym' should have no 'moreGeneral' term, * because 'moreGeneral' of their 'synonym' is used instead of it */ NpcTerm* synonym; /** * Constructor for the term */ NpcTerm(const char* term) { synonym = NULL; hypernymSynNet = NULL; this->term = term; } ~NpcTerm() { if(hypernymSynNet) free_syns(hypernymSynNet); } const char* GetInterleavedHypernym(size_t which); bool IsNoun(); /** * Compare if two terms are equal */ bool operator==(NpcTerm& other) const { return term==other.term; }; /** * Compare if one term is less than the other. */ bool operator<(NpcTerm& other) const { return (strcmp(term,other.term)<0); }; }; class NpcTriggerGroupEntry { public: int id; csString text; NpcTriggerGroupEntry *parent; NpcTriggerGroupEntry(int i,const char *txt,NpcTriggerGroupEntry *myParent=0) { id = i; text = txt; parent = myParent; } bool operator<(NpcTriggerGroupEntry& other) const { return text < other.text; } bool operator==(NpcTriggerGroupEntry& other) const { return text == other.text; } }; class NpcTrigger { public: int id; csString area; csString trigger; int priorresponseID; int min_attitude; int max_attitude; csArray responseIDlist; // int questID; /// Load the trigger from a database bool Load(iResultRow& row); /// Return true if there are one available response for this trigger bool HaveAvailableResponses(Client * client, gemNPC * npc, NPCDialogDict * dict, csArray *availableResponseList = NULL ); /// Return one of the members of responseIDlist array randomly int GetRandomResponse( const csArray &availableResponseList); /// Compare two triggers. Used when searching for triggers. bool operator==(NpcTrigger& other) const; /// Compare two triggers. Used when searching for triggers. bool operator<(NpcTrigger& other) const; }; class NpcResponse; class gemNPC; class gemActor; class Client; struct iDocumentNode; /** * Possible actions scriptable in the quest engine all * inherit from this class. */ class ResponseOperation { protected: csString name; public: virtual ~ResponseOperation() {}; virtual bool Load(iDocumentNode *node) = 0; virtual csString GetResponseScript() = 0; virtual bool Run(gemNPC *who, Client *target,NpcResponse *owner) = 0; const char *GetName() { return name; } }; /** * This class holds several possible responses and an * action script for the npc to run whenever an * appropriate trigger is triggered. */ class NpcResponse { public: int id; /// xref from trigger response csString response[MAX_RESP]; /// possible alternative answers for this response csString him,her,it,them; /// record antecedents for next question pronouns int type; /// record the type of response csPDelArray script; /// list of ops in script to execute when triggered psQuest * quest; /// Quest that this respons is part of psQuestPrereqOp * prerequisite; /// prerequisite for this Response to be available int active_quest; /// which one should be run. this is actually set by check quest avail op enum { VALID_RESPONSE, ERROR_RESPONSE }; NpcResponse(); virtual ~NpcResponse(); bool Load(iResultRow& row); void SetActiveQuest(int max); int GetActiveQuest() { return active_quest; } const char *GetResponse(); /// Check for SayResponseOp with public flag set, which tells chat whether it is public or private. bool HasPublicResponse(); bool ParseResponseScript(const char *xmlstr,bool insertBeginning=false); bool ExecuteScript(Client *client, gemNPC* target); csString GetResponseScript(); /** * Pars and append the xml based prerequisite script to the * prerequisite for this Response. * */ bool ParsePrerequisiteScript(const char *xmlstr,bool insertBeginning=false); /** * Add a prerequisite to the prerequisite for this response. * * @param op The prerequisite op to add * @insertBeginning Insert at beginning or at end (Default at end). * @return True if successfully added. */ bool AddPrerequisite(psQuestPrereqOp * op, bool insertBeginning = false); /** * Check if the prerequisite for this response is valid * * @param character The Character trying to get the response * @return True if prerequisite are all ok */ bool CheckPrerequisite(psCharacter * character); bool operator==(NpcResponse& other) const { return id==other.id; }; bool operator<(NpcResponse& other) const { return id offer; public: OfferRewardResponseOp() { name = "offer"; } virtual ~OfferRewardResponseOp() {}; virtual bool Load(iDocumentNode *node); virtual csString GetResponseScript(); virtual bool Run(gemNPC *who, Client *target,NpcResponse *owner); }; /** * This script operation makes an npc give money * to the player. */ class MoneyResponseOp : public ResponseOperation { protected: psMoney money; public: MoneyResponseOp() { name = "money"; } virtual ~MoneyResponseOp() {}; virtual bool Load(iDocumentNode *node); virtual csString GetResponseScript(); virtual bool Run(gemNPC *who, Client *target,NpcResponse *owner); }; #endif