Logo Search packages:      
Sourcecode: adonthell version File versions  Download package

dialog.cc

Go to the documentation of this file.
/*
   $Id: dialog.cc,v 1.35 2003/04/20 21:35:42 ksterker Exp $

   (C) Copyright 2000/2001/2002 Kai Sterker <kaisterker@linuxgames.com>
   Part of the Adonthell Project http://adonthell.linuxgames.com

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License.
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY.

   See the COPYING file for more details
*/

/**
 * @file   dialog.cc
 * @author Kai Sterker <kaisterker@linuxgames.com>
 * 
 * @brief  Declares the dialog class.
 * 
 * 
 */
 
#include <algorithm>
#include <iostream>
#include <string.h>

#include "character.h"
#include "dialog.h"
#include "nls.h"
#include "yarg.h"

// Constructor
00034 dialog::dialog (character_base *npc)
{
    strings = NULL;
    npc_portrait_= npc->get_portrait ();
    npc_name_ = npc->get_name ();
}

// Destructor
00042 dialog::~dialog ()
{
    clear ();
}

// Prepare the dialogue for execution
00048 bool dialog::init (string fpath, string name, PyObject *args)
{
    // Load and instanciate the dialogue object
    if (!dialogue.create_instance (fpath, name, args))
        return false;

    // Remaining setup tasks
    if (!setup ())
        return false;

    return true;
}

// Misc. initialisation
bool dialog::setup ()
{
    PyObject *list, *s;
    u_int32 i, size;
    
    // Get the text that may loop
    list = dialogue.get_attribute ("loop");
    if (list && PyList_Check (list))
    {
        size = PyList_Size (list);
    
        for (i = 0; i < size; i++)
        {
            s = PyList_GetItem (list, i);
            if (s && PyInt_Check (s)) loop.push_back (PyInt_AsLong (s));
        }
        
        Py_DECREF (list);
    }
    
    // Extract the dialogue's strings
    list = dialogue.get_attribute ("text");
    if (list && PyList_Check (list))
    {
        size = PyList_Size (list);
        strings = new char*[size];

        for (i = 1; i < size; i++)
        {
            s = PyList_GetItem (list, i);
            if (s && PyString_Check (s)) strings[i] = PyString_AsString (s);
            else strings[i] = "*** Error";
        }

        Py_DECREF (list);
    }
    else return false;

    // Init the first answer
    answers.push_back (0);
        
    return true;
}

// Reload a dialogue script that has changed on disk
00107 bool dialog::reload (string fpath, string name, PyObject *args)
{
    // Load and instanciate the dialogue object
    if (!dialogue.reload_instance (fpath, name, args))
        return false;

    // Remaining setup tasks
    if (!setup ())
        return false;

    return true;
}

// Clean up
void dialog::clear ()
{
    if (strings) delete[] strings;
}

// iterate over the dialogue text
00127 string dialog::text ()
{
    if (i_text == text_.end ())
    {
        i_text = text_.begin ();
        return "";
    }
    
    return *i_text++;
}

// Gets the index of either the player or npc array
00139 void dialog::run (u_int32 index)
{
    PyObject *arg, *result, *speaker, *speech;
    s_int32 s, answer = answers[index];
    u_int32 stop, size;
    
    // empty previous dialogue text
    text_.clear ();
    answers.clear ();
    
    // end of dialogue
    if (answer == -1)
        return;
    
    // Mark the Player text as used unless loops allowed
    if (find (loop.begin (), loop.end (), answer) == loop.end ())
        used.push_back (answer);
        
    do
    {
        // Execute the next part of the dialogue
        arg = Py_BuildValue ("(i)", answer);
        
         // run next part of dialogue
        dialogue.run (arg);
#ifdef PY_DEBUG
        python::show_traceback ();
#endif
        Py_XDECREF (arg);
    
        // Now fill in the NPC's and Player's responses:
        // 1. Get the neccesary attributes of the dialogue class
        speaker = dialogue.get_attribute ("speaker");
        speech = dialogue.get_attribute ("speech");

        // 2. Search the NPC part for used text
        for (int i = 0; i < PyList_Size (speech); i++)
        {
            s = PyInt_AsLong (PyList_GetItem (speech, i));

            // Remove text that was already used and isn't allowed to loop
            if (find (used.begin (), used.end (), s) != used.end ())
            {
                PySequence_DelItem (speaker, i);
                PySequence_DelItem (speech, i--);
            }
        }

        // check if some text is left at all
        size = PyList_Size (speech);
        if (size == 0) 
        {
            i_text = text_.begin ();
            return;
        }
                
        // prepare the random number generator        
        yarg::range (0, size - 1);

        // check type of speaker
        if (PyList_GetItem (speaker, 0) != Py_None)
        {
            // got NPC text, so let the engine decide
            int rnd = yarg::get ();
            
            // get the text
            answer = PyInt_AsLong (PyList_GetItem (speech, rnd));
            text_.push_back (scan_string (nls::translate (strings[answer])));

            // get the NPC color, portrait and name
            char *npc = PyString_AsString (PyList_GetItem (speaker, rnd));
            if (npc != NULL)
            {
                if (strcmp ("Narrator", npc) == 0) npc_color_ = 0;
                else
                {
                    // set color and portrait of the NPC
                    character_base *mychar = data::characters[npc];

                    npc_color_ = mychar->get_color ();
                    npc_portrait_ = mychar->get_portrait ();
                    npc_name_ = npc;
                }
            }
            
            // check whether we shall continue or not
            arg = Py_BuildValue ("(i)", answer);
            result = dialogue.call_method_ret ("stop", arg);
            stop = PyInt_AsLong (result);
            Py_XDECREF (result);
            Py_XDECREF (arg);
            
            // Mark the NPC text as used unless loops allowed
            if (find (loop.begin (), loop.end (), answer) == loop.end ())
                used.push_back (answer);
            
            answers.push_back (answer);
        }
        else
        {
            // got Player text, so let the player decide
            for (u_int32 i = 0; i < size; i++)
            {
                // simply add all text to let the player select an answer
                answer = PyInt_AsLong (PyList_GetItem (speech, i));
                text_.push_back (scan_string (nls::translate (strings[answer])));
                answers.push_back (answer);
            }
            
            // let the player make his decision
            stop = true;
        }
    
        // cleanup
        Py_XDECREF (speaker);
        Py_XDECREF (speech);
    }
    while (!stop);            

    // init the iterator for dialogue text retrieval
    i_text = text_.begin ();
}

// execute embedded functions and replace shortcuts
// yeah, the c string library hurts, but at least it's fast ;)
string dialog::scan_string (const char *s)
{
    u_int32 begin, end, len;
    PyObject *result;
    char *tmp, *start, *mid, *str = NULL;
    character *the_player = data::the_player;
    string newstr (s); 

    // replace $... shortcuts
    while (1)
    {
        // check wether the string contains shortcut code at all
        start = strchr (newstr.c_str (), '$');
        if (start == NULL) break;

        // replace "$name"
        if (strncmp (start, "$name", 5) == 0)
        {
            begin = newstr.length () - strlen (start);
            string t (newstr, 0, begin);
            t += the_player->get_name ();
            t += (start+5);
            
            newstr = t;
            continue;
        }

        // replace "$fm"
        if (strncmp (start, "$fm", 3) == 0)
        {
            // extract the "$fm{.../...} part
            end = strcspn (start, "}");
            str = new char[end];
            str[end-1] = 0;        
            strncpy (str, start+3, end);

            if (the_player->storage::get_val ("gender") == FEMALE)
                mid = get_substr (str, "{", "/");
            else
                mid = get_substr (str, "/", "}");

            begin = newstr.length () - strlen(start);
            tmp = new char[newstr.length () - end + strlen (mid)];
            strncpy (tmp, newstr.c_str (), begin);
            tmp[begin] = 0;
            strcat (tmp, mid);
            strcat (tmp, start+end+1);
            
            delete[] str;
            delete[] mid;
            newstr = tmp;

            continue;
        }

        // Error!
        cout << "\n*** Error, unknown macro " << start << flush;
        start[0] = ' ';
    }
    
    // execute python functions
    while (1)
    {
        // check wether the string contains python code at all
        start = strchr (newstr.c_str (), '{');
        if (start == NULL) break;

        end = strcspn (start, "}");
        mid = NULL;

        str = new char[end];
        str[end-1] = 0;        

        // extract the code without the brackets
        strncpy (str, start+1, end-1);

        // run the string
        result = PyObject_CallMethod (dialogue.get_instance (false), str, NULL);

        if (result)
            if (PyString_Check (result))
                mid = (char*) nls::translate (PyString_AS_STRING (result));    
        
        // Replace existing with new, changed string
        // 1. Calculate string's length
        len = newstr.length ();
        begin = len - strlen (start);
        tmp = new char[(mid ? strlen(mid) : 0) + len - strlen(str)];

        // 2. Merge prefix, resulting string and postfix into new string
        strncpy (tmp, newstr.c_str (), begin);
        tmp[begin] = 0;
        if (mid) strcat (tmp, mid);
        strcat (tmp, start+end+1);

        // 3. Exchange strings
        newstr = tmp;

        // Cleanup
        Py_XDECREF (result);
        delete[] str;
        delete[] tmp;
    }

    return newstr;
}

char *dialog::get_substr (const char* string, char* begin, char* end)
{
    u_int32 b, e;
    b = strcspn (string, begin) + 1;
    e = strcspn (string, end) - b;

    char *result = new char[e+1];
    strncpy (result, string+b, e);
    result[e] = 0;

    return result;
}

Generated by  Doxygen 1.6.0   Back to index