int CharCreationManager::IsReserved(const char* name, AccountID acctID)
{
    // Check to see if this name is reserved.  Does this check by comparing
    // the email address of the account with that stored in the migration table.
    csString query;
    csString escape;
    db->Escape( escape, name );
    query.Format( "SELECT m.email FROM migration m WHERE m.username='******'", escape.GetData() );
    Result result (db->Select( query ) );

    if ( result.IsValid() && result.Count() == 1 )
    {
        csString savedEmail( result[0][0] );

        query.Format("SELECT username FROM accounts WHERE id=%d\n", acctID.Unbox());

        Result result2(db->Select( query ) );
        if ( result2.IsValid() && result2.Count() == 1 )
        {
            csString email( result2[0][0] );
            if ( savedEmail.CompareNoCase(email) )
                return NAME_RESERVED_FOR_YOU;
            else
                return NAME_RESERVED;
        }
    }

    return NAME_AVAILABLE;
}
Ejemplo n.º 2
0
bool psNPCLoader::WriteSpecificKnowledge(int questid)
{
    csRef<iDocumentNode> specificsNode = npcRoot->CreateNodeBefore(CS_NODE_ELEMENT);

    specificsNode->SetValue("specificknowledge");

    Result result;

    if(questid == -1)
        result = db->Select("SELECT DISTINCT trigger "
                            "FROM npc_triggers "
                            "WHERE prior_response_required=0 and area=\"%s\" and quest_id<1 "
                            ,area.GetData());
    else
        result = db->Select("SELECT DISTINCT trigger "
                            "FROM npc_triggers "
                            "WHERE prior_response_required=0 and quest_id=%i "
                            ,questid);

    if(result.IsValid())
    {
        for(int i=0; i < (int)result.Count(); i++)
        {
            csString res(result[i][0]);
            if(!WriteTrigger(specificsNode, res, 0, questid))
                return false;
        }
    }
    return true;
}
bool CharCreationManager::IsUnique( const char* name , bool dbUniqueness)
{
    csString escape;
    db->Escape( escape, name );
    // Check to see if name already exists in character database.
    // Querying the DB is slow, but I don't see another way to do it
    csString query;
    query.Format( "Select id from characters where name='%s'", escape.GetData() );
    Result result (db->Select( query ) );
    //if dbUniqueness is true we will check result.Count() for > 1 else > 0 (like >= 1)
    return ! ( result.IsValid() && (result.Count() > (unsigned long)(dbUniqueness ? 1:0)) );
}
void CharCreationManager::HandleUploadMessage( MsgEntry* me, Client *client )
{
    Debug1( LOG_NEWCHAR, me->clientnum,"New Character is being created" );

    psCharUploadMessage upload(me);

    if (!upload.valid)
    {
        Debug2(LOG_NET,me->clientnum,"Received unparsable psUploadMessage from client %u.",me->clientnum);
        return;
    }

    AccountID acctID = client->GetAccountID();
    if (!acctID.IsValid())
    {
        Error2("Player tried to upload a character to unknown account %s.", ShowID(acctID));

        psCharRejectedMessage reject(me->clientnum);

        psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET);
        psserver->RemovePlayer (me->clientnum,"Could not find your account.");
        return;
    }

    // Check to see if the player already has 4 accounts;
    csString query;
    query.Format("SELECT id FROM characters WHERE account_id=%d", acctID.Unbox());
    Result result (db->Select( query ) );
    if ( result.IsValid() && result.Count() >= CHARACTERS_ALLOWED )
    {        
        psserver->RemovePlayer (me->clientnum,"At your character limit.");
        return;
    }
        
    csString playerName =  upload.name;
    csString lastName =  upload.lastname;

    playerName = NormalizeCharacterName(playerName);
    lastName = NormalizeCharacterName(lastName);

    // Check banned names
    if(psserver->GetCharManager()->IsBanned(playerName))
    {
        csString error;
        error.Format("The name %s is banned", playerName.GetData() );
        psCharRejectedMessage reject(me->clientnum,
                            psCharRejectedMessage::RESERVED_NAME,
                            (char*)error.GetData() );
        reject.SendMessage();
        return;
    }

    if(psserver->GetCharManager()->IsBanned(lastName))
    {
        csString error;
        error.Format("The lastname %s is banned", lastName.GetData() );
        psCharRejectedMessage reject(me->clientnum,
                            psCharRejectedMessage::RESERVED_NAME,
                            (char*)error.GetData() );
        reject.SendMessage();
        return;
    }

    Debug3( LOG_NEWCHAR, me->clientnum,"Got player firstname (%s) and lastname (%s)\n",playerName.GetData(), lastName.GetData() );

    ///////////////////////////////////////////////////////////////
    //  Check to see if the player name is valid
    ///////////////////////////////////////////////////////////////
    if ( playerName.Length() == 0 || !FilterName(playerName) )
    {
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::NON_LEGAL_NAME,
                                     "The name you specifed is not a legal player name." );

        psserver->GetEventManager()->SendMessage(reject.msg);
        return;
    }

    if ( lastName.Length() != 0 && !FilterName(lastName) )
    {
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::NON_LEGAL_NAME,
                                     "The name you specifed is not a legal lastname." );

        psserver->GetEventManager()->SendMessage(reject.msg);
        return;
    }

    Debug2( LOG_NEWCHAR, me->clientnum,"Checking player firstname '%s'..\n",playerName.GetData());
    ///////////////////////////////////////////////////////////////
    //  Check to see if the character name is unique in 'characters'.
    ///////////////////////////////////////////////////////////////
    if  ( !IsUnique( playerName ) )
    {
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::NON_UNIQUE_NAME,
                                     "The firstname you specifed is not unique." );

        psserver->GetEventManager()->SendMessage(reject.msg);
        return;
    }

    if (lastName.Length())
    {
        Debug2( LOG_NEWCHAR, me->clientnum,"Checking player lastname '%s'..\n",lastName.GetData());
        if (!IsLastNameAvailable(lastName, acctID))
        {
            psCharRejectedMessage reject(me->clientnum,
                                        psCharRejectedMessage::NON_UNIQUE_NAME,
                                        "The lastname you specifed is not unique." );

            psserver->GetEventManager()->SendMessage(reject.msg);
            return;
        }
    }
    ///////////////////////////////////////////////////////////////
    //  Check to see if the character name is on the reserve list.
    ///////////////////////////////////////////////////////////////
    int reservedName = IsReserved( playerName, acctID );
    if ( reservedName == NAME_RESERVED )
    {
        csString error;
        error.Format("The name %s is reserved", playerName.GetData() );
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::RESERVED_NAME,
                                     (char*)error.GetData() );

        psserver->GetEventManager()->SendMessage(reject.msg);
        return;
    }


    csString error;
    if (!psserver->charCreationManager->Validate(upload, error))
    {
        error.Append(", your creation choices are invalid." );
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::INVALID_CREATION,
                                     (char*)error.GetData() );

        reject.SendMessage();
        return;
    }        
    
    ///////////////////////////////////////////////////////////////
    //  Create the psCharacter structure for the player.
    ///////////////////////////////////////////////////////////////
    psCharacter *chardata=new psCharacter();
    chardata->SetCharType(PSCHARACTER_TYPE_PLAYER);
    chardata->SetFullName(playerName,lastName);
    chardata->SetCreationInfo(upload.bio);

    psRaceInfo *raceinfo=psserver->GetCacheManager()->GetRaceInfoByNameGender( upload.race, (PSCHARACTER_GENDER)upload.gender);
    if (raceinfo==NULL)
    {
        Error3("Invalid race/gender combination on character creation:  Race='%d' Gender='%d'", upload.race, upload.gender );
        psCharRejectedMessage reject(me->clientnum);
        psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET);
        psserver->RemovePlayer (me->clientnum,"Player tried to create an invalid race/gender.");
        delete chardata;
        return;
    }
    chardata->SetRaceInfo(raceinfo);
    chardata->SetHitPoints(50.0);
    chardata->GetMaxHP().SetBase(0.0);
    chardata->GetMaxMana().SetBase(0.0);

    //range is unused here
    float x,y,z,yrot,range;
    const char *sectorname;
    InstanceID newinstance = DEFAULT_INSTANCE;
    
    //get the option entries for tutorial from the server options. Note it's tutorial:variousdata
    optionEntry* tutorialEntry = psserver->GetCacheManager()->getOptionSafe("tutorial","");   
    sectorname = tutorialEntry->getOptionSafe("sectorname", "tutorial")->getValue();

    psSectorInfo *sectorinfo = psserver->GetCacheManager()->GetSectorInfoByName(sectorname);

    if (!sectorinfo || PlayerHasFinishedTutorial(acctID, sectorinfo->uid))
    {
        raceinfo->GetStartingLocation(x,y,z,yrot,range,sectorname);
        sectorinfo = psserver->GetCacheManager()->GetSectorInfoByName(sectorname);
        
        //As we aren't going in the tutorial disable the tutorial help messages disable them
        for(int i = 0; i < TutorialManager::TUTOREVENTTYPE_COUNT; i++)
            chardata->CompleteHelpEvent(i);
    }
    else
    {
        // Try tutorial level first.
        x = tutorialEntry->getOptionSafe("sectorx", "-225.37")->getValueAsDouble();
        y = tutorialEntry->getOptionSafe("sectory", "-21.32")->getValueAsDouble();
        z = tutorialEntry->getOptionSafe("sectorz", "26.79")->getValueAsDouble();
        yrot = tutorialEntry->getOptionSafe("sectoryrot", "-2.04")->getValueAsDouble();
    }
    
    bool sectorFound = true;
    
    if ( sectorinfo && EntityManager::GetSingleton().FindSector(sectorinfo->name) == NULL )
    {
        Error2("Sector='%s' found but no map file was detected for it. Using NPCroom1", sectorname );    
        sectorinfo = psserver->GetCacheManager()->GetSectorInfoByName("NPCroom1");
        if ( sectorinfo && EntityManager::GetSingleton().FindSector(sectorinfo->name) == NULL )
        {
            Error1("NPCroom1 failed - Critical");                
            sectorFound = false;    
        }    
        else if ( sectorinfo && EntityManager::GetSingleton().FindSector(sectorinfo->name) )
        {
            sectorFound = true;
        }                        
        else
        {
            sectorFound = false;
        }
    }
    else if ( sectorinfo && EntityManager::GetSingleton().FindSector(sectorinfo->name) )
    {
        sectorFound = true;
    }
    else 
    {
        sectorFound = false;
    }
    
    
    if (!sectorFound)
    {
        Error2("Unresolvable starting sector='%s'", sectorname );
        psCharRejectedMessage reject(me->clientnum);
        psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET);
        psserver->RemovePlayer (me->clientnum,"No starting Sector.");
        delete chardata;
        return;    
    }
    
    chardata->SetLocationInWorld(newinstance, sectorinfo, x, y, z, yrot);

    psTrait * trait;
//    CPrintf(CON_DEBUG, "Trait: %d\n", upload.selectedFace );    
    trait = psserver->GetCacheManager()->GetTraitByID(upload.selectedFace);    
    if ( trait )
        chardata->SetTraitForLocation( trait->location, trait );
            
    trait = psserver->GetCacheManager()->GetTraitByID(upload.selectedHairStyle);    
    if ( trait )
        chardata->SetTraitForLocation( trait->location, trait );
    
    trait = psserver->GetCacheManager()->GetTraitByID(upload.selectedBeardStyle);    
    if ( trait )
        chardata->SetTraitForLocation( trait->location, trait );
    
    trait = psserver->GetCacheManager()->GetTraitByID(upload.selectedHairColour);    
    if ( trait )
        chardata->SetTraitForLocation( trait->location, trait );
    
    trait = psserver->GetCacheManager()->GetTraitByID(upload.selectedSkinColour);    
    if ( trait )
        chardata->SetTraitForLocation( trait->location, trait );
    
    gemActor *actor = new gemActor( gemSupervisor, cacheManager, entityManager, chardata,
                                    raceinfo->mesh_name,
                                    newinstance,
                                    EntityManager::GetSingleton().FindSector(sectorinfo->name),
                                    csVector3(x,y,z),yrot,
                                    client->GetClientNum());

    actor->SetupCharData();

    if( !upload.verify )
    {
        if (!psServer::CharacterLoader.NewCharacterData(acctID,chardata))
        {
            Error1("Character could not be created.");
            psCharRejectedMessage reject(me->clientnum);
            psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET);
            psserver->RemovePlayer (me->clientnum,"Your character could not be created in the database.");
            delete chardata;
            return;
        }
    }

    // Check to see if a path name was set. If so we will use that to generate 
    // the character starting stats and skills.    
    if ( upload.path != "None" )
    {
        // Progression Event name is PATH_PathName
        csString name("PATH_");
        name.Append(upload.path);
        ProgressionScript *script = psserver->GetProgressionManager()->FindScript(name.GetData());
        if (script)
        {
            // The script uses the race base character points to calculate starting stats.
            MathEnvironment env;
            env.Define("CharPoints", raceinfo->initialCP);
            env.Define("Actor", actor);
            script->Run(&env);
        }
    }
    else
    {
    //int cpUsage = psserver->charCreationManager->CalculateCPChoice( upload.choices ) +
    //              psserver->charCreationManager->CalculateCPLife(upload.lifeEvents );    
    for ( size_t ci = 0; ci < upload.choices.GetSize(); ci++ )
    {
        CharCreationManager::CreationChoice* choice = psserver->charCreationManager->FindChoice( upload.choices[ci] );
        if ( choice )
        {
            csString name( psserver->charCreationManager->FindChoice( upload.choices[ci] )->name.GetData() );
            Debug3(LOG_NEWCHAR, me->clientnum,"Choice: %s Creation Script: %s", name.GetData(), choice->eventScript.GetData());

            MathEnvironment env;
            env.Define("Actor", actor);
            if ( choice->choiceArea == FATHER_JOB || choice->choiceArea == MOTHER_JOB )
            {
                int modifier = (choice->choiceArea == FATHER_JOB) ? upload.fatherMod : upload.motherMod;
                if ( modifier > 3 || modifier < 1 )
                    modifier = 1;
                
                env.Define("ParentStatus", modifier);
            }
            ProgressionScript *script = psserver->GetProgressionManager()->FindScript(choice->eventScript);
            if (script)
                script->Run(&env);
        }            
        else
        {
            Debug2( LOG_NEWCHAR, me->clientnum,"Character Choice %d not found\n", upload.choices[ci] );
        }
    }
    for ( size_t li = 0; li < upload.lifeEvents.GetSize(); li++ )
    {
        MathEnvironment env;
        env.Define("Actor", actor);
        csString scriptName(psserver->charCreationManager->FindLifeEvent(upload.lifeEvents[li])->eventScript.GetData());
        Debug2(LOG_NEWCHAR, me->clientnum, "LifeEvent Script: %s", scriptName.GetData());

        ProgressionScript *script = psserver->GetProgressionManager()->FindScript(scriptName);
        if (script)
            script->Run(&env);
    }
    }
    
    if ( !upload.verify )
    {

        if ( reservedName == NAME_RESERVED_FOR_YOU )
        {
            AssignScript( chardata );
        }
        // This function recalculates the Max HP, Mana and Stamina of the new character
        chardata->RecalculateStats();

        // Make sure the new player have HP, Mana and Samina that was calculated
        chardata->SetHitPoints(chardata->GetMaxHP().Base());
        chardata->SetMana(chardata->GetMaxMana().Base());
        chardata->SetStamina(chardata->GetMaxPStamina().Base(),true);
        chardata->SetStamina(chardata->GetMaxMStamina().Base(),false);
        
                   
        psServer::CharacterLoader.SaveCharacterData( chardata, actor );
        Debug1(LOG_NEWCHAR,me->clientnum,"Player Creation Complete");

        // Remove cached objects to make sure that the client gets a fresh character
        // list from the database if it logs out and in within 2 minutes.
        iCachedObject *obj = psserver->GetCacheManager()->RemoveFromCache(psserver->GetCacheManager()->MakeCacheName("list",client->GetAccountID().Unbox()));
        if (obj)
        {
            obj->ProcessCacheTimeout();
            obj->DeleteSelf();
        }
        obj = psserver->GetCacheManager()->RemoveFromCache(psserver->GetCacheManager()->MakeCacheName("auth",client->GetAccountID().Unbox()));
        if (obj)
        {
            obj->ProcessCacheTimeout();
            obj->DeleteSelf();
        }

        // Here everything is ok
        client->SetPID(chardata->GetPID());
        client->SetName(playerName);

        psCharApprovedMessage app(me->clientnum);
        if (app.valid)
            psserver->GetEventManager()->SendMessage(app.msg);
        else
            Bug2("Could not create valid psCharApprovedMessage for client %u.\n",me->clientnum);                               
    }    
    else
    {
        psCharVerificationMesg mesg( me->clientnum );
        size_t z;
        //unfortunately count goes out of valid area so we need to check on charisma
        for ( z = 0; z <= PSITEMSTATS_STAT_CHARISMA; z++ )
        {
            int value = chardata->GetSkillRank(statToSkill((PSITEMSTATS_STAT) z)).Current();
            if ( value > 0 )
            {
                mesg.AddStat( value, psserver->GetCacheManager()->Attribute2String((PSITEMSTATS_STAT)z));                
            }                                    
        }
        for ( z = 0; z < psserver->GetCacheManager()->GetSkillAmount(); z++ )
        {
            unsigned int rank = chardata->Skills().GetSkillRank((PSSKILL) z).Base();
            
            psSkillInfo* info = psserver->GetCacheManager()->GetSkillByID(z);
            csString name("Not found");
            if ( info )
                name.Replace( info->name );
            
            if ( rank > 0 )
            {
                mesg.AddSkill(rank, name);                
            }                                            
        }                
        mesg.Construct();
        mesg.SendMessage();
    }
    
    delete actor;

    if (!upload.verify)
    {
        // Remove char data from the cache
        iCachedObject *obj = psserver->GetCacheManager()->RemoveFromCache(psserver->GetCacheManager()->MakeCacheName("char", chardata->GetPID().Unbox()));
        if (obj)
        {
            obj->ProcessCacheTimeout();
            obj->DeleteSelf();
        }
    }
}
void CharCreationManager::HandleName( MsgEntry* me, Client *client )
{
    psNameCheckMessage name;
    name.FromClient( me );
    /// Check in migration/reserve table
    csString query;
    csString escape;
    if ( name.firstName.Length() == 0 )
    {
        psNameCheckMessage response( me->clientnum, false, "Cannot have an empty first name!");      
        response.SendMessage();
        return;
    }

    if (name.firstName.Length() > MAX_PLAYER_NAME_LENGTH)
    {
        psNameCheckMessage response(me->clientnum, false, "First name is too long!");
        response.SendMessage();
        return;
    }

    if (name.lastName.Length() > MAX_PLAYER_NAME_LENGTH)
    {
        psNameCheckMessage response(me->clientnum, false, "Last name is too long!");
        response.SendMessage();
        return;
    }

    db->Escape( escape, name.firstName );
    query.Format( "SELECT * FROM migration m WHERE m.username='******'", escape.GetData() );    
    Result result (db->Select( query ) );    
    if ( result.IsValid() && result.Count() == 1 )
    {
        int reservedName = IsReserved(name.firstName, client->GetAccountID());
        if ( reservedName == NAME_RESERVED )
        {
            psNameCheckMessage response( me->clientnum, false, "Name is in reserved database"); 
            response.SendMessage();
            return;
        }            
    }
    
    // Check uniqueness of the names
    if(!IsUnique(name.firstName))
    {
        psNameCheckMessage response( me->clientnum, false, "First name is already in use"); 
        response.SendMessage();        
        return;
    }

    if(!IsLastNameAvailable(name.lastName, client->GetAccountID()))
    {
        psNameCheckMessage response( me->clientnum, false, "Last name is already in use"); 
        response.SendMessage();        
        return;
    }

    csString playerName = name.firstName;
    csString lastName   = name.lastName;

    // Check if the name is banned
    if(psserver->GetCharManager()->IsBanned(playerName))
    {
        csString error;
        error.Format("The name %s is banned", playerName.GetData() );
        psNameCheckMessage reject(me->clientnum,
                                    false,
                                    error );
        reject.SendMessage();
        return;
    }
    
    if(psserver->GetCharManager()->IsBanned(lastName))
    {
        csString error;
        error.Format("The lastname %s is banned", lastName.GetData() );
        psNameCheckMessage reject(me->clientnum,
                                    false,
                                    error );
        reject.SendMessage();
        return;
    }

    psNameCheckMessage response( me->clientnum, true, "Ok"); 
    response.SendMessage();                                            
}
Ejemplo n.º 6
0
bool psNPCLoader::WriteTrigger(csRef<iDocumentNode> specificsNode, csString &trigger,int priorID, int questID)
{
    // <trigger>
    //      <phrase value="..">
    //      <phrase value="..">
    //      <attitude min="0" max="100">
    //          <response say=".. "/>
    //          <trigger>
    //              ..
    //          </trigger>
    //      </attitude>
    // </trigger>

    Result result;
    Result phrases;

    csString escArea;
    csString escTrigger;
    db->Escape(escArea, area);
    db->Escape(escTrigger, trigger);

    if(questID == -1)
    {
        result = db->Select("SELECT response_id, "
                            "       min_attitude_required, "
                            "       max_attitude_required "
                            "FROM npc_triggers "
                            "WHERE prior_response_required=\"%i\" and trigger=\"%s\" and area=\"%s\" and quest_id<1;",
                            priorID,
                            escTrigger.GetData(),
                            escArea.GetData());

        // all triggers that have the same response id will be grouped together
        // and written to the xml file as <phrase value="..">
        phrases = db->Select("SELECT id, trigger "
                             "FROM npc_triggers "
                             "WHERE response_id=\"%i\" and area=\"%s\" and quest_id<1;",
                             result[0].GetInt(0),
                             escArea.GetData());
    }
    else
    {
        result = db->Select("SELECT response_id, "
                            "       min_attitude_required, "
                            "       max_attitude_required "
                            "FROM npc_triggers "
                            "WHERE prior_response_required=\"%i\" and trigger=\"%s\" and quest_id=\"%i\";",
                            priorID,
                            escTrigger.GetData(),
                            questID);


        // all triggers that have the same response id will be grouped together
        // and written to the xml file as <phrase value="..">
        phrases = db->Select("SELECT id, trigger, area "
                             "FROM npc_triggers "
                             "WHERE response_id=\"%i\" and quest_id=\"%i\";",
                             result[0].GetInt(0),
                             questID);
    }

    csString temp;
    csRef<iDocumentNode> triggerNode;

    for(int i=0; i< (int)phrases.Count(); i++)
    {
        int triggerID = phrases[i].GetInt(0);
        bool duplicate=false;

        for(size_t j=0; j<triggers.GetSize(); j++)
        {
            if(triggerID==triggers[j])
                duplicate=true;
        }

        // if the trigger hasn't already been added
        if(!duplicate)
        {
            triggers.Push(triggerID);
            if(!triggerNode)
            {
                triggerNode = specificsNode->CreateNodeBefore(CS_NODE_ELEMENT);
                triggerNode->SetValue("trigger");
                csString area(phrases[0][2]);
                if(this->area.IsEmpty() && !area.IsEmpty())
                    triggerNode->SetAttribute("area", area);
            }
            csRef<iDocumentNode> phraseNode = triggerNode->CreateNodeBefore(CS_NODE_ELEMENT);

            phraseNode->SetValue("phrase");
            phraseNode->SetAttribute("value", phrases[i][1]);
        }
    }

    if(triggerNode)
    {
        for(int i=0; i<(int)result.Count(); i++)
        {
            int responseID  = result[i].GetInt(0);
            int minAttitude = result[i].GetInt(1);
            int maxAttitude = result[i].GetInt(2);

            csRef<iDocumentNode> attitudeNode = triggerNode->CreateNodeBefore(CS_NODE_ELEMENT);

            attitudeNode->SetValue("attitude");
            attitudeNode->SetAttributeAsInt("min", minAttitude);
            attitudeNode->SetAttributeAsInt("max", maxAttitude);

            if(!WriteResponse(attitudeNode, responseID, questID))
                return false;

            // check if this trigger contains other triggers
            Result childResult(db->Select("SELECT DISTINCT trigger "
                                          "FROM npc_triggers "
                                          "WHERE prior_response_required=%i;" ,responseID));

            if(childResult.IsValid())
            {
                for(int j=0; j<(int)childResult.Count(); j++)
                {
                    csString res(childResult[j][0]);
                    if(!WriteTrigger(attitudeNode, res, responseID, questID))
                        return false;
                }
            }
        }
    }
    return true;
}