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

Управление предметами с помощью системы контроля имущества

Ваши предметы готовы быть разбросанными по миру, и только вопрос времени, как скоро игроки начнут пытаться утащить все, что не прибито гвоздями. Когда это произойдет, игрокам потребуется способ управления предметами, что включает применение следящей за предметами системы контроля имущества (Inventory Control System, ICS).

Не заблуждайтесь; ICS применяется не только к персонажам игроков, она следит за всем миром. Предметы могут лежать на карте, принадлежать персонажам или даже находиться внутри других предметов (например, предметы могут быть сложены в рюкзак). Это означает, что предмету должен быть назначено право собственности (ownership). Кроме того, у владельца может быть несколько экземпляров объекта — например, несколько монет.

Собрание предметов владельца называется списком имущества (inventory list), и любой объект может находиться в этом списке (а также несколько экземпляров одного объекта). Взаимоотношения между владельцами, списками имущества и количеством предметов показаны на рис. 11.6. (Примечание: не думайте о предметах, как об относящихся к владельцу; вместо этого думайте о владельце, как об имеющем набор предметов.)


Рис. 11.6. Система управления имуществом следит за каждым владельцем предметов, в том числе и за количеством предметов

Рис. 11.6. Система управления имуществом следит за каждым владельцем предметов, в том числе и за количеством предметов


Система управления имуществом работает рука об руку с MIL. В то время как MIL содержит только единственное, уникальное описание каждого объекта в мире, ICS может работать с несколькими экземплярами одного и того же объекта. Каждый раз, когда системе управления имуществом требуется информация о предмете, она обращается к главному списку предметов за его спецификациями. Это позволяет экономить память, сохраняя в ICS только ссылки с номерами объектов в MIL (как показано на рис. 11.7).


Рис. 11.7. Система управления имуществом хранит только номера для ссылок на предметы

Рис. 11.7. Если система управления имуществом хранит только номера для ссылок на предметы, можно сэкономить значительный объем памяти, разместив всю информацию о предмете в главном списке предметов


Для игровых карт и уровней замечательно подойдет простая система управления имуществом (называемая ICS карты, map ICS), хранящая только список предметов и их местоположение на карте, поскольку она позволит разместить повсюду предметы, чтобы игроки могли собирать их. Настоящая проблема возникнет, когда игроки начнут собирать разбросанные предметы, добавляя их к своему имуществу. Накапливаются дублирующие экземпляры, добавляются новые предметы, а другие предметы используются или выбрасываются. Предметы быстро приходят в беспорядок. Обработка наборов находящихся у персонажей объектов является работой системы управления имуществом персонажей, которая несколько сложнее, чем вариант, относящийся к карте.

Разработка ICS карты

ICS карты следит за предметами, находящимися внутри уровней, включая те предметы, которые находятся внутри других, — например, меч лежащий в сундуке. Способ размещения предметов на карте зависит от используемого типа карты. На трехмерных картах для размещения предметов вы используете три координаты — X, Y и Z. Если вы разделили ваш мир на несколько карт, придется следить за предметами каждой карты, используя собственный файл предметов для каждой карты.

ICS карты представлена структурой и классом:

typedef struct sMapItem
{
    long ItemNum;           // Номер предмета в MIL
    long Quantity;          // Количество предметов
                            // (например, монет)
    float XPos, YPos, ZPos; // Координаты на карте

    sMapItem *Prev, *Next;  // Указатели связанного списка

    long Index;             // Индекс данного предмета
    long Owner;             // Индекс владельца
    sMapItem *Parent;       // Родитель предмета

    sMapItem()
    {
        Prev = Next = Parent = NULL;
        Index = 0; Owner = -1;
    }

    ~sMapItem() { delete Next; Next = NULL; }
} sMapItem;

class cMapICS
{
  private:
    long m_NumItems;        // Количество предметов на карте
    sMapItem *m_ItemParent; // Родитель связанного списка
                            // предметов карты

    // Чтение из файла длинного целого числа
    // или числа с плавающей точкой
    long GetNextLong(FILE *fp);
    float GetNextFloat(FILE *fp);

  public:
    cMapICS();  // Конструктор
    ~cMapICS(); // Деструктор

    // Загрузка, сохранение и освобождение
    // списка предметов карты
    BOOL Load(char *Filename);
    BOOL Save(char *Filename);
    BOOL Free();

    // Добавление и удаление предмета на карте 
    BOOL Add(long ItemNum, long Quantity,
             float XPos, float YPos, float ZPos,
             sMapItem *OwnerItem = NULL);
    BOOL Remove(sMapItem *Item);

    // Возвращает количество предметов или
    // родителя связанного списка объектов
    long GetNumItems();
    sMapItem *GetParentItem();
    sMapItem *GetItem(long Num);
};

Сначала вы видите структуру sMapItem, которая хранит информацию для каждого предмета на карте. ItemNum — это номер для ссылки на элемент MIL (который должен находиться в диапазоне от 0 до 1023, если вы используете программу MIL Editor), а Quantity — это количество предметов ItemNum (это позволяет, например, представить горсть монет как единый объект). Затем расположены координаты предмета на карте  XPos, YPos и ZPos.

Далее следуют указатели Prev и Next. Они добавлены для формирования связанного списка структур sMapItem. Следующая пара переменных, Index и Owner, используются при загрузке и сохранении предметов на карте. Index хранит текущий индекс предмета в связанном списке. Если предмет принадлежит другому предмету, переменная Owner хранит индекс родительского объекта (в противном случае значение Owner равно –1).

При загрузке (или добавлении) объекта вы устанавливаете заключительную переменную в sMapItem (Parent), чтобы она указывала на структуру данных действительного владельца предмета. Концепция связанного списка структур sMapItem показана на рис. 11.8.


Рис. 11.8. Вы храните список предметов карты в виде связанного списка

Рис. 11.8. Вы храните список предметов карты в виде связанного списка. В процессе чтения с диска предметы нумеруются, что помогает устанавливать отношения родитель-потомок между объектами


Структура sMapItem использует и конструктор и деструктор, вызываемые когда для структуры выделяется память и при освобождении занятых ресурсов соответственно. Обе функции обеспечивают правильность указателей связанного списка и, когда структура удаляется, все последующие структуры sMapItem в связанном списке удаляются тоже.

ВНИМАНИЕ!
Если вы хотите удалить из связанного списка только один экземпляр структуры sMapItem, сперва присвойте переменной Next удаляемого экземпляра значение NULL. Это гарантирует, что все последующие экземпляры в связанном списке не будут удалены.

В классе cMapICS есть две закрытые функции (GetNextLong и GetNextFloat) используемые для чтения текста и преобразования его в значение типа long или float. Также в классе cMapICS есть восемь полезных открытых функций. Давайте познакомимся с ними поближе.

cMapICS::Load, cMapICS::Save и cMapICS::Free

Согласно своим именам, это трио функций загружает, сохраняет и освобождает список относящихся к карте предметов. Первая из трех, Load, создает и загружает список предметов. Для простоты, все предметы хранятся в текстовом файле, использующем следующий формат:

MIL_ItemNum
Quantity
XPos
YPos
ZPos
ParentID

Каждый предмет использует шесть строк текста, и каждый элемент (группа из шести строк) последовательно нумеруется (первый предмет в файле — это предмет 0, второй предмет — предмет 1 и т.д.). Вот пример файла, содержащего два предмета:

// Описание предмета 0:
10     // Номер предмета в MIL (значение long)
1      // Количество (значение long)
10.0   // XPos (значение float)
0.0    // YPos (значение float)
600.0  // ZPos (значение float)
-1     // Владелец (-1 = нет, иначе его индекс)
// Описание предмета 1:
1      // Номер предмета в MIL
1      // ...
10.0
0.0
600.0
0      // Относится к предмету 0 (первому предмету в файле)

Комментарии добавлены только для ясности; в реальном файле их не будет. Читая список предметов, такой как показан выше, функция Load преобразует текст в числа. Из этих чисел создается структура sMapItem для каждого загруженного предмета на карте и формируется связанный список загруженных предметов. После считывания данных каждого предмета выполняется сопоставление связанных между собою предметов (с использованием указателя Parent в структуре sMapItem).

Здесь нет ничего по-настоящему сложного, так что давайте перейдем прямо к коду cMapICS::Load:

BOOL cMapICS::Load(char *Filename)
{
    FILE *fp;
    long LongNum;

    sMapItem *Item, *ItemPtr = NULL;

    Free(); // Освобождаем предыдущий набор

    // Открываем файл
    if((fp=fopen(Filename, "rb"))==NULL)
        return FALSE;

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

    // Бесконечный цикл чтения данных предметов
    while(1) {
        // Получаем номер следующего предмета
        // (прерываемся, если больше нет предметов,
        // о чем свидетельствует возвращаемое значение -1)
        if((LongNum = GetNextLong(fp)) == -1)
            break;

        // Создаем новый указатель карты и привязываем его
        Item = new sMapItem();
        if(ItemPtr == NULL)
            m_ItemParent = Item;
        else {
            Item->Prev = ItemPtr;
            ItemPtr->Next = Item;
        }
        ItemPtr = Item;

        // Сохраняем номер предмета в MIL
        Item->ItemNum = LongNum;

        // Получаем количество
        Item->Quantity = GetNextLong(fp);

        // Получаем координаты
        Item->XPos = GetNextFloat(fp);
        Item->YPos = GetNextFloat(fp);
        Item->ZPos = GetNextFloat(fp);

        // Получаем номер владельца
        Item->Owner = GetNextLong(fp);

        // Сохраняем индекс и увеличиваем счетчик
        Item->Index = m_NumItems++;
    }

    // Закрываем файл
    fclose(fp);

    // Сопоставляем взаимосвязанные объекты
    ItemPtr = m_ItemParent;
    while(ItemPtr != NULL) {
        // Проверяем, относится ли предмет к другому
        if(ItemPtr->Owner != -1) {
            // Находим соответствующий родительский предмет
            Item = m_ItemParent;
            while(Item != NULL) {
                if(ItemPtr->Owner == Item->Index) {
                    // Связываем, устанавливая указатель на родителя
                    ItemPtr->Parent = Item;
                    break; // Останавливаем сканирование родителей
                }
                Item = Item->Next;
            }
        }
        // Переходим к следующему предмету
        ItemPtr = ItemPtr->Next;
    }
    return TRUE;
}

 

ПРИМЕЧАНИЕ
Подобно большей части кода из этой книги, функции класса cMapICS возвращают TRUE, если функция завершена успешно, и FALSE, если произошла ошибка.

Функция Save берет внутренний список предметов и, используя указанное вами имя файла, записывает этот список в файл на диске. Функция Save обычно используется для обновления игровых данных, поскольку игроки могут подбирать одни предметы и выбрасывать другие.

Сперва функция Save назначает каждой структуре sMapItem в связанном списке значение индекса (основываясь на их порядке следования). Первому элементу связанного списка присваивается индекс 0, второму — индекс 1 и т.д. Затем обновляются переменные Owner во всех дочерних объектах и данные записываются в файл:

BOOL cMapICS::Save(char *Filename)
{
    FILE *fp;
    sMapItem *Item;
    long Index = 0;

    // Открываем файл
    if((fp=fopen(Filename, "wb"))==NULL)
        return FALSE;

    // Назначаем индексы предметам
    if((Item = m_ItemParent) == NULL) {
        fclose(fp);
        return TRUE; // Нет предметов для сохранения
    }

    while(Item != NULL) {
        Item->Index = Index++;
        Item = Item->Next;
    }

    // Сопоставляем дочерние предметы и родителей
    Item = m_ItemParent;
    while(Item != NULL) {
        if(Item->Parent != NULL)
            Item->Owner = Item->Parent->Index;
        else
            Item->Owner = -1;
        Item = Item->Next;
    }

    // Сохраняем все
    Item = m_ItemParent;

    while(Item != NULL) {

        // Номер предмета
        fprintf(fp, "%lu\r\n", Item->ItemNum);

        // Количество
        fprintf(fp, "%lu\r\n", Item->Quantity);

        // Координаты
        fprintf(fp, "%lf\r\n%lf\r\n%lf\r\n",
                Item->XPos, Item->YPos, Item->ZPos);

        // Номер владельца
        fprintf(fp, "%ld\r\n", Item->Owner);

        // Следующий предмет
        Item = Item->Next;
    }
    fclose(fp);  // Закрываем файл
    return TRUE; // Возвращаем флаг успеха!
}

И, наконец, функцию Free вы используете, когда уничтожаете класс (удаляя таким образом связанный список предметов). Вот код для Free:

BOOL cMapICS::Free()
{
    m_NumItems = 0;
    delete m_ParentItem;
    m_ParentItem = NULL;
    return TRUE;
}

Вы просто удаляете связанный список предметов и подготавливаете класс для нового использования.

cMapICS::Add и cMapICS::Remove

Когда на карте появляется новый предмет (например, брошенный игроком), вам необходимо вызвать функцию Add, чтобы этот брошенный предмет был включен в связанный список объектов карты. Функция Add начинается с создания новой структуры sMapItem, затем она заполняет эту структуру предоставленной вами информацией о предмете и включает структуру в список объектов карты:

BOOL cMapICS::Add(long ItemNum, long Quantity,
                  float XPos, float YPos, float ZPos,
                  sMapItem *OwnerItem)
{
    sMapItem *Item;

    // Создание новой структуры данных предмета
    Item = new sMapItem();

    // Вставляем ее в вершину списка
    Item->Next = m_ItemParent;
    if(m_ItemParent != NULL)
        m_ItemParent->Prev = Item;
    m_ItemParent = Item;

    // Заполняем структуру данных
    Item->ItemNum = ItemNum;
    Item->Quantity = Quantity;
    Item->XPos = XPos;
    Item->YPos = YPos;
    Item->ZPos = ZPos;
    Item->Parent = OwnerItem;

    return TRUE;
}

Функцию Add вы вызываете чтобы добавить предмет к списку объектов карты, и точно так же вы используете функцию Remove для удаления предметов с карты. Вызывая Remove вы указываете идентификатор предмета, который хотите удалить из списка карты. Помимо этого Remove освобождает память, занятую структурой данных предмета, и заботится о тех предметах, которые были связаны с удаляемым:

BOOL cMapICS::Remove(sMapItem *Item)
{
    sMapItem *ItemPtr, *NextItem;

    // Сперва удаляем дочерние предметы
    if((ItemPtr = m_ItemParent) != NULL) {
        while(ItemPtr != NULL) {
            NextItem = ItemPtr->Next;
            if(ItemPtr->Parent == Item)
                Remove(ItemPtr);
            ItemPtr = NextItem;
        }
    }

    // Удаляем из связанного списка и сбрасываем
    // корень, если это текущая голова списка
    if(Item->Prev != NULL)
        Item->Prev->Next = Item->Next;
    else
        m_ItemParent = Item->Next;

    if(Item->Next != NULL)
        Item->Next->Prev = Item->Prev;

    // Очищаем связанный список
    Item->Prev = Item->Next = NULL;

    // Освобождаем память
    delete Item;

    return TRUE;
}

cMapICS::GetNumItems, cMapICS::GetParentItem и cMapICS::GetItem

Вы используете эти три функции для получения количества относящихся к карте предметов, получения родительской структуры sMapItem и получения указанной структуры данных предмета из связанного списка. Первые две функции возвращают единственную переменную, а третья функция выполняет сложную работу по сканированию связанного списка объектов и возвращает указанный элемент списка:

long cMapICS::GetNumItems() { return m_NumItems; }

sMapItem cMapICS::GetParentItem() { return m_ParentItem; }

sMapItem *cMapICS::GetItem(long Num)
{
    sMapItem *Item;

    // Начинаем с родительского предмета
    Item = m_ItemParent;

    // Цикл, пока не достигнут предмет с указанным номером
    while(Num-) { 
        if(Item == NULL)
            return NULL;   // Нет больше предметов для сканирования,
                           // возвращаем ошибку
        Item = Item->Next; // Переходим к следующему предмету
                           //в связанном списке
    }
    return Item; // Возвращаем найденный предмет
}

 

ПРИМЕЧАНИЕ
С указателем на родительскую структуру, возвращаемым GetParentItem, вы можете просканировать весь связанный список предметов, используя указатель Next каждой структуры. Если вам надо получить заданную структуру данных предмета, основываясь на ее индексе в списке, используйте функцию GetItem.

Использование класса cMapICS

У каждой карты в вашей игре есть связанный с ней список принадлежащих этой карте предметов. ICS карты загружает эти предметы и предоставляет их движку, когда надо визуализировать карту или добавить какой-нибудь предмет к имуществу игрока (если он подбирает находящийся на карте предмет).

Взгляните на фрагмент кода, загружающий пример списка предметов, добавляющий один предмет, удаляющий другой предмет и сохраняющий список:

cMapICS MapICS;

MapICS.Load("sample.mi"); // Загрузка файла

// Добавляем один предмет с номером 10
MapICS.Add(10, 1, 0.0f, 0.0f, 100.0f, NULL);

// Удаляем второй предмет в списке
MapICS.Remove(MapICS.GetItem(1));

// Сохраняем список
MapICS.Save("sample.mi");

Это очень простой пример модификации списка предметов карты, но что мешает нам двинуться дальше и посмотреть, насколько сложным он может быть?

Демонстрационная программа MapICS (рис. 11.9) содержит полный код класса cMapICS, показанный в этой главе и структуру sItem из программы MIL Editor. Вы можете загрузить программу MapICS с прилагаемого к книге CD-ROM (она находится в папке \BookCode\Chap11\MapICS). В ней используется ICS карты и MIL для визуализации набора объектов, разбросанных по небольшому уровню.


Рис. 11.9. Программа MapICS позволяет вам перемещаться по небольшому уровню, подбирать и бросать предметы

Рис. 11.9. Программа MapICS позволяет вам перемещаться по небольшому уровню, подбирать и бросать предметы


Программа MapICS загружает объекты карты и использует данные об их координатах для рисования сеток предметов в сцене. Всякий раз, когда вы приближаетесь к предмету, появляется значок цели и отображается название предмета.

В программе MapICS нет ничего нового для вас, поэтому я не буду приводть здесь ее код. Фактически, MapICS очень похожа на программу NodeTree, которую мы рассматривали в главе 8, «Создание трехмерного графического движка».

ПРИМЕЧАНИЕ
Программа MapICS позволяет вам перемещаться по простому уровню, используя клавиши управления курсором и мышь. Вы можете подобрать предмет, встав перед ним и нажав левую кнопку мыши. Если вы нажмете правую кнопку мыши, будет выброшен случайно выбранный предмет.

После визуализации уровня сканируются все предметы карты и рисуются те из них, которые находятся в поле зрения. Затем определяется ближайший предмет и на экран выводится его название. Код хорошо прокомментирован, так что у вас не должно возникнуть проблем при его исследовании.

Разработка ICS персонажа

Хотя разработка системы управления имуществом персонажа сначала может вызвать испуг, позвольте мне заверить вас, что она не слишком отличается от разработки системы управления имуществом карты. Вам нужна возможность добавлять и удалять предметы, но вам не приходится иметь дело с координатами предметов на карте. Вместо этого ICS игрока следит за порядком, то есть позволяет игроку переупорядочивать предметы, как ему удобно.

Конечно, упорядочивание это просто вариант сортировки связанного списка, но как быть с теми предметами, которые могут содержать внутри себя другие, например, котомки? Если вы в программе MIL Editor указали, что эти предметы являются контейнерами, то беспокоиться не о чем.

Что касается категорий, настоящее волшебство начинается, когда вы хотите использовать предмет или применить его для экипировки. Поскольку у каждого предмета есть категория, ICS персонажа может быстро определить, что делать с рассматриваемым предметом. Если предмет — оружие, ICS персонажа может спросить, надо ли добавить его к экипировке. Если это лекарство, игрок может сразу принять его. Начали улавливать идею?

И, наконец, ICS персонажа позволяет игроку исследовать объекты, для чего предназначены параметры файла сетки и файла изображения в MIL Editor. Когда игрок исследует объект, загружается и отображается соответствующая ему сетка или изображение.

Теперь переключим внимание на объединение всех описанных частей.

Определение класса cCharICS

Определение разработанных для этой книги класса ICS персонажа и вспомогательных структур выглядит так:

typedef struct sCharItem
{
    long ItemNum;           // Номер предмета в MIL
    long Quantity;          // Количество предметов
                            // (например, для монет)
    sCharItem *Prev, *Next; // Указатели связанного списка

    long Index;             // Индекс данного предмета
    long Owner;             // Индекс владельца

    sCharItem *Parent;      // Родитель предмета

    sCharItem()
    {
        Prev = Next = Parent = NULL;
        Index = 0; Owner = -1;
    }

    ~sCharItem() { delete Next; Next = NULL; }
} sCharItem;

class cCharICS
{
  private:
    long m_NumItems;         // Количество предметов в имуществе
    sCharItem *m_ItemParent; // Родитель связанного спсика предметов

    // Функции для чтения чисел из файла
    long GetNextLong(FILE *fp);
    float GetNextFloat(FILE *fp);

  public:
    cCharICS();  // Конструктор
    ~cCharICS(); // Деструктор

    // Загрузка, сохранение и освобождение списка предметов
    BOOL Load(char *Filename);
    BOOL Save(char *Filename);
    BOOL Free();

    // Добавление и удаление предмета
    BOOL Add(long ItemNum, long Quantity, \
    sCharItem *OwnerItem = NULL);
    BOOL Remove(sCharItem *Item);

    // Получение количества предметов или
    // родителя связанного списка объектов 
    long GetNumItems();
    sCharItem *GetParentItem();
    sCharItem *GetItem(long Num);

    // Функции переупорядочивания
    BOOL Arrange();
    BOOL MoveUp(sCharItem *Item);
    BOOL MoveDown(sCharItem *Item);
};

Подобно классу cMapICS, класс cCharICS использует специальную структуру (cCharItem), которая хранит номер предмета в MIL, количество предметов и управляет связанным списком. В отличие от структуры sMapItem, sCharItem не заботится о координатах предмета.

Точно так же и класс cCharICS во многом похож на cMapICS, за исключением трех дополнительных открытых функций — Arrange, MoveUp и MoveDown. Вы используете эти функции для сортировки списка предметов персонажа. Вот их код:

BOOL cCharICS::Arrange()
{
    sCharItem *Item, *PrevItem;

    // Начинаем с вершины связанного списка и каждый
    // предмет всплывает вверх, пока не наткнется на
    // предмет с меньшим ItemNum. Прерываем работу,
    // достигнув низа списка.
    Item = m_ItemParent;

    while(Item != NULL) {
        // Проверяем наличие предыдущего предмета
        if(Item->Prev != NULL) {
            // Всплываем, пока у предыдущего предмета
            // меньше значение ItemNum или пока не достигнем
            // вершины списка
            while(Item->Prev != NULL) {
                // Получаем указатель на предыдущий предмет
                PrevItem = Item->Prev;

                // Прерываемся, если некуда всплывать
                if(Item->ItemNum >= PrevItem->ItemNum)
                    break;

                // Обмен
                if((PrevItem = Item->Prev) != NULL) {
                    if(PrevItem->Prev != NULL)
                        PrevItem->Prev->Next = Item;
                    if((PrevItem->Next = Item->Next) != NULL)
                        Item->Next->Prev = PrevItem;
                    if((Item->Prev = PrevItem->Prev) == NULL)
                        m_ItemParent = Item;
                    PrevItem->Prev = Item;
                    Item->Next = PrevItem;
                }
            }
        }
        // Переход к следующему объекту
        Item = Item->Next;
    }
    return TRUE;
}

BOOL cCharICS::MoveUp(sCharItem *Item)
{
    sCharItem *PrevItem;

    // Обмениваем предмет с предыдущим
    if((PrevItem = Item->Prev) != NULL) {
        if(PrevItem->Prev != NULL)
            PrevItem->Prev->Next = Item;
        if((PrevItem->Next = Item->Next) != NULL)
            Item->Next->Prev = PrevItem;
        if((Item->Prev = PrevItem->Prev) == NULL)
            m_ItemParent = Item;
        PrevItem->Prev = Item;
        Item->Next = PrevItem;
    }
    return TRUE; // Возвращаем флаг успеха
}

BOOL cCharICS::MoveDown(sCharItem *Item)
{
    sCharItem *NextItem;

    // Обмениваем элемент с последующим
    if((NextItem = Item->Next) != NULL) {
        if((Item->Next = NextItem->Next) != NULL)
            NextItem->Next->Prev = Item;
        if((NextItem->Prev = Item->Prev) != NULL)
            Item->Prev->Next = NextItem;
        else
            m_ItemParent = NextItem;
        NextItem->Next = Item;
        Item->Prev = NextItem;
    }
    return TRUE; // Возвращаем флаг успеха
}

Функция Arrange сортирует связанный список предметов по номеру каждого предмета в MIL, от меньших к большим. Если же вам необходимо собственное особое упорядочивание списка, воспользуйтесь функциями MoveUp и MoveDown, которые получают указатель на уже содержащуюся в списке структуру sCharItem.

Функция MoveUp перемещает указанную структуру sItem в связанном списке вверх, а MoveDown перемещает указанную структуру в связанном списке вниз. На рис. 11.10 показано использование функций Arrange, MoveUp и MoveDown в простом связанном списке предметов.


Рис. 11.10. Слева направо показаны: исходный неотсортированный список...

Рис. 11.10. Слева направо показаны: исходный неотсортированный список, упорядоченный (отсортированный) список, список после перемещения предмета вверх и список после перемещения предмета вниз


Оставшиеся функции cCharICS идентичны функциональности класса cMapICS, за очевидным исключением координат предметов, используемых при добавлении предметов в список. Даже формат хранения данных предметов персонажа идентичен формату хранения данных предметов карты, опять же за исключением координат.

Использование класса cCharICS

Чтобы продемонстрировать систему ICS персонажа я создал демонстрационное приложение с именем CharICS, которое управляет списком предметов, содержащимся в файле главного списка предметов default.mil. Программа находится на прилагаемом к книге CD-ROM (посмотрите в папке \BookCode\Chap11\CharICS). При запуске программы отображается диалоговое окно Inventory (рис. 11.11). В этом окне показан список предметов воображаемого персонажа, которым вы можете управлять с помощью кнопок.


Рис. 11.11. Программа CharICS

Рис. 11.11. Программа CharICS позволяет вам просматривать имущество воображаемого персонажа, добавляя, удаляя или сортируя предметы по своему желанию


Использование CharICS выглядит так:

  1. Щелкните по кнопке Add Item. Программа CharICS добавит случайный предмет из MIL в список имущества персонажа.

  2. Выберите предмет в списке. Предметы классифицированы, так что кнопки Use и Equip будут активными или неактивными в зависимости от того, какой предмет вы выбрали. Щелчок по кнопке Equip не оказывает никакого эффекта; она вступит в игру позже, когда вы будете иметь дело с реальным персонажем. Щелчок по кнопке Use удаляет предмет из списка, если для него установлен флаг USEONCE.

  3. Щелкните по кнопке Drop чтобы выбросить предмет (удалить предмет из списка имущества), если для него установлен флаг CANDROP.

  4. Чтобы отсортировать предметы в списке имущества, вы можете щелкнуть по предмету, а затем щелкнуть по кнопке Sort Up или Sort Down. Выбранный предмет переместится в списке вверх или вниз, соответственно. Чтобы отсортировать список предметов по их номерам, щелкните по кнопке Arrange.

И, наконец, дополнительное удовольствие, — предметам соответствуют трехмерные сетки, которые отображаются в поле, находящемся в правой части окна приложения. Показываемые трехмерные объекты медленно вращаются, так что вы можете рассмотреть их со всех сторон. Как здорово!


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

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