netlib.narod.ru< Назад | Оглавление | Далее >

Выполнение скриптов Mad Lib

Уф! Могу честно сказать, что самая сложная часть позади, поскольку реализовать сейчас выполнение скриптов — это просто детские игрушки. Вы можете выбросить за дверь шаблоны действий, потому что теперь будете работать только со структурами sScript и sScriptEntry.

Первый этап работы со скриптом — загрузка его в память, выполняемая с помощью функции LoadScript (если вы хотите больше узнать об этой функции, обратитесь к разделу «Создание элементов скрипта»).

long NumActions;
sScript *LoadedScript = LoadScript("Script.mls", &NumActions);

После этого ваш игровой движок просто перебирает элементы связанного списка скрипта для выполнения каждого действия. Это требует некоторого объема кодирования, поскольку в этом случае действия известны только по номерам (так что вы должны знать, что каждое из действий делает). Вот пример, перебирающий элементы загруженного скрипта и ищущий действие Print (действие 0), содержащее единственный элемент (текст для печати):

sScript *ScriptPtr = LoadedScript; // Начинаем с корня

// Перебираем в цикле все действия скрипта
while(ScriptPtr != NULL) {
    // Это действие 0?
    if(ScriptPtr->Type == 0) {
        // У данного действия один элемент - текст.
        // Отображаем текст в окне сообщений
        MessageBox(NULL, ScriptPtr->Entries[0].Text, "TEXT", MB_OK);
    }
    // Переходим к следующему действию в скрипте
    ScriptPtr = ScriptPtr->Next;
}

Хотя здесь всего несколько строк кода, они демонстрируют удивительный потенциал обработки скриптов. Немного изобретательности и вы сможете приспособить MLS для обработки большинства скриптовых действий.

Как насчет использования условных инструкций if...then...else? Как вы знаете, эта инструкция проверяет, равно значение условия true или false, и, в зависимости от результата, выполняет различные последовательности действий. Возьмем, к примеру, следующий код на С:

BOOL GameFlags[256]; // Флаги, используемые в игре

if(GameFlags[0] == TRUE) {
    // Печатаем сообщение и устанавливаем флаг в FALSE
    MessageBox(NULL, "It's TRUE!", "Message", MB_OK);
    GameFlags[0] = FALSE;
} else {
    // Печатаем сообщение
    MessageBox(NULL, "It's FALSE.", "Message", MB_OK);
}

В зависимости от значений, хранящихся в массиве GameFlags, выполняются различные блоки кода. Создав несколько действий и слегка переработав код обработки скрипта, вы можете наслаждаться преимуществами использования конструкции if...then...else в MLS. Сперва взгляните на шаблон действий:

"If GameFlag ~ equals ~ then"
INT 0 255
BOOL
"Else"
"EndIf"
"Set GameFlag ~ to ~"
INT 0 255
BOOL
"Print ~"
TEXT

Здесь нет ничего особенного, поскольку настоящая работа происходит в коде выполнения скрипта:

// pScript = загруженный скрипт, содержащий следующий код:
// "If GameFlag (0) equals (TRUE) then"
// "Print (It's TRUE!)"
// "Set GameFlag (0) to (FALSE)"
// "Else"
// "Print (It's FALSE.)"
// "EndIf"

// Функции обработки действий
sScript *Script_IfThen(sScript *Script);
sScript *Script_Else(sScript *Script);
sScript *Script_EndIf(sScript *Script);
sScript *Script_SetFlag(sScript *Script);
sScript *Script_Print(sScript *Script);

// Структура исполнения действия скрипта
typedef struct sScriptProcesses {
    sScript *(*Func)(sScript *ScriptPtr);
} sScriptProcesses;

// Список структур исполнения действий скрипта
sScriptProcesses ScriptProcesses[] = {
    { Script_IfThen },
    { Script_Else },
    { Script_EndIf },
    { Script_SetFlag },
    { Script_Print }
}

BOOL GameFlags[256]; // Массив игровых флагов

void RunScript(sScript *pScript)
{
    // Очищаем массив GameFlags,
    // устанавливая везде значение FALSE
    for(short i = 0; i < 256; i++)
        GameFlags[i] = FALSE;

    // Сканируем скрипт и выполняем функции
    while(pScript != NULL) {
        // Вызываем функцию скрипта и прерываем обработку,
        // если она вернула NULL. Любое другое
        // возвращенное значение - это указатель на следующую
        // функцию, обычно pScript->Next.
        pScript = ScriptProcesses[pScript->Type].Func(pScript);
    }
}

sScript *Script_IfThen(sScript *Script)
{
    BOOL Skipping; // Флаг пропуска действий скрипта

    // Смотрим, совпадает ли флаг со вторым элементом
    if(g_Flags[Script->Entries[0].lValue % 256] ==
                                      Script->Entries[1].bValue)
        Skipping = FALSE;
    else
        Skipping = TRUE;

    // Здесь флаг Skipping установлен, если действия скрипта должны быть
    // пропущены согласно условной инструкции if ... then. Действия выполняются,
    // если Skipping = FALSE, также ищется else для переключения режима пропуска
    // или endif для завершения условного блока

    // Переходим к обработке следующего действия
    Script = Script->Next;

    while(Script != NULL) {
        // Если Else, переключаем режим пропуска
        if(Script->Type == 1)
            Skipping = (Skipping == TRUE) ? FALSE : TRUE;

        // Прерывание на EndIf
        if(Script->Type == 2)
            return Script->Next;

        // Обрабатываем функции скрипта в условном блоке,
        // при этом действия пропускаются, если условие не выполнено
        if(Skipping == TRUE)
            Script = Script->Next;
        else {
            if((Script =
                ScriptProcesses[Script->Type].Func(Script)) == NULL)
                return NULL;
        }
    }
    return NULL; // Достигнут конец скрипта
}

sScript *Script_SetFlag(sScript *Script)
{
    // Установка логического флага
    GameFlags[Script->Entries[0].lValue % 256] =
                             Script->Entries[1].bValue;
}

sScript *Script_Else(sScript *Script)  { return Script->Next; }

sScript *Script_EndIf(sScript *Script) { return Script->Next; }

sScript *Script_Print(sScript *Script)
{
    MessageBox(NULL, Script->Entries[0].Text, "Text", MB_OK);
    return Script->Next;
}

Как видите, все волшебство сосредоточено в рекурсивной функции Script_IfThen, обрабатывающей действия скрипта, находящиеся между парой if...then и действием EndIf. Действие Else просто переключает режим обработки (с пропуска действий на их выполнение или наоборот) в зависимости от исходного значения переменной Skipped.

Теперь вы почувствовали мощь, а если вам нужны еще доказательства, обратитесь к последующим главам, где используется система MLS, например, к главе 12, «Управление игроками и персонажами», и главе 16, «Объединяем все в законченную игру». Обе главы демонстрируют использование скриптов при взаимодействии с игровыми персонажами.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz