A.I.M.(http://forum.ja2.su/cgi-bin/yabb/YaBB.pl)
Обсуждаем игры >> Гробокопатели >> [Уголок багодела] Расширение структур OBJECTTYPE и
(Message started by: bugmonster на 20.09.2006 в 11:22:57)

Заголовок: [Уголок багодела] Расширение структур OBJECTTYPE и
Прислано пользователем bugmonster на 20.09.2006 в 11:22:57
Здравствуйте, почтенные гробокопатели и прочие интересующиеся.

Представляю на суд моё начинание под кодовым названием "Уголок багодела". Уголок багодела планируется как цикл статей, посвящённых доработке исходников JA2. Этот цикл статей будет интересен тем, кто занимается правкой исходников и созданием собственных модификаций JA2. Первую статью старался писать так, что бы было понятно начинающему, однако указанное усовершенствование должно быть полезно даже для опытных модостроителей. Для чтения очень желательно знание языка С.
Каждая статья построена как мануал, поэтому выполнив все описанные шаги вы получите требуемый результат, но для понимания сути изменений нужно читать и пояснения ;)


Расширение структур OBJECTTYPE и SOLDIERTYPE.


1. Что такое OBJECTTYPE и SOLDIERTYPE и зачем их "расширять"?
2. Ковыряем.
3. Что это было.


1. Что такое OBJECTTYPE и SOLDIERTYPE и зачем их "расширять"?

Это две из множества структур, используемых в Джа2 для хранения информации об объектах игры. В структурах типа OBJECTTYPE хранится информация о каждом предмете, существующем в игре. Важно понимать, что если в игре есть конкретная аптечка, которую можно взять, использовать, продать, то информация о ней будет храниться в OBJECTTYPE. Там хранится, например, статус аптечки, т.е. сколько процентов "бинтов" в ней осталось.
А каждый солдат, наёмник (и даже корова с вороной :) ) хранится в своём SOLDIERTYPE. В каждом SOLDIERTYPE есть место для информации об инвентаре солдата. Инвентарь - это массив из предметов OBJECTTYPE. Если увеличить размер структуры OBJECTTYPE, то изменится и размер структуры SOLDIERTYPE - именно поэтому в статье две этих структуры рассматриваются вместе.
А зачем вообще увеличивать их размеры? Дело в том, что множество нововведений ("фич"), требуют место для хранения новых данных в этих структурах, а свободного места - нет.
Например увеличение количества слотов под аттачи к оружию потребует в структуре OBJECTTYPE увеличить длину массивов
UINT16 usAttachItem[MAX_ATTACHMENTS];
INT8 bAttachStatus[MAX_ATTACHMENTS];

, но разработчики не оставили в структуре OBJECTTYPE свободного места
Увеличение количества "карманов" в инвентаре солдата потребует в структуре SOLDIERTYPE увеличить длину массива
OBJECTTYPE inv[ NUM_INV_SLOTS ];.
Здесь разработчики предусмотрели в конце структуры свободное место в виде
UINT8 bFiller[ 39 ];,
и его можно использовать для добавления небольших новых полей, но расширить или перенести на его место инвентарь не получится.


2.  Ковыряем.

А что нам мешает просто изменить OBJECTTYPE и SOLDIERTYPE, добавив в них новые поля по своему вкусу? Ничего. Все сохраняемые файлы игры, в т.ч. карты будут иметь новую структуру. Однако, загрузить существующие карты игра не сможет. Дело в том, что в файлах карт хранятся структуры OBJECTTYPE и SOLDIERTYPE (на самом деле информация о солдатах хранится в файле карты в виде структуры SOLDIERCREATE_STRUCT, но это дела не меняет). Хранятся они в "старом" виде, а игра теперь думает, что в "новом". Но мы - модостроители, зачем нам старые карты? Но ведь не все моды используют только новые карты, да и для сохранения переделанной карты в редакторе нужно сначала загрузить исходную в старом формате.
Есть простое решение - при загрузке карты читать OBJECTTYPE и SOLDIERTYPE старого типа и тут же в памяти преобразовывать их в новый вид.

Для начала откроем файл Tactical\Item types.h и скопипастим объявление структуры OBJECTTYPE. Старое объявление не трогая переименуем в OBJECTTYPE_OLD, а скопированное изменим по вкусу - это и будет наш новый OBJECTTYPE. В результате получится код вроде такого (избыточное цитирование не изменённого кода опущено под знаками " ...", изменённый код выделен жирным):

typedef struct
{
     UINT16      usItem;
     UINT8            ubNumberOfObjects;
...
 // attached objects
     UINT16      usAttachItem[MAX_ATTACHMENTS];
     INT8            bAttachStatus[MAX_ATTACHMENTS];
...
} OBJECTTYPE_OLD;

typedef struct
{
     UINT16      usItem;
     UINT8            ubNumberOfObjects;
...
 // attached objects
       // MAX_ATTACHMENTS*4 - это неправильный способ увеличить количество аттачей, зато наглядный :)
     UINT16      usAttachItem[MAX_ATTACHMENTS*4];
     INT8            bAttachStatus[MAX_ATTACHMENTS*4];

       // а тут я зарезервировал 256 байт прозапас
     union
     {
           UINT8 bChunk[256];
     };


     INT8            fFlags;
     UINT8            ubMission;
     INT8            bTrap;        // 1-10 exp_lvl to detect
     UINT8            ubImprintID;      // ID of merc that item is imprinted on
     UINT8            ubWeight;
     UINT8            fUsed;                        // flags for whether the item is used or not
} OBJECTTYPE;


OBJECTTYPE на картах используется как часть двух структур -  WORLDITEM и SOLDIERCREATE_STRUCT, значит нужно создать для этих структур прототипы старого размера и функции превращения (или переноса данных) из старого формата структуры в новый.
Открываем файл Tactical\World Items.h и копипастим объявление структуры WORLDITEM. В неё мы не планируем ничего добавлять, но так как членом этой структуры является OBJECTTYPE, нужно создать "старую версию" WORLDITEM для загрузки карт. Ещё нужно объявить функции переноса данных для OBJECTTYPE и WORLDITEM:

typedef struct
{
...
       OBJECTTYPE      o;
...
} WORLDITEM;

typedef struct
{
...
     OBJECTTYPE_OLD  o;
...
} WORLDITEM_OLD;

void RenovateObjectype(OBJECTTYPE * pNew, OBJECTTYPE_OLD * pOld);
void RenovateWorldItem(WORLDITEM * pNew, WORLDITEM_OLD * pOld);


Открываем файл Tactical\World Items.c и добавляем в него реализацию функций:

void RenovateObjectype(OBJECTTYPE * pNew, OBJECTTYPE_OLD * pOld)
{
     pNew->usItem = pOld->usItem;
     pNew->ubNumberOfObjects = pOld->ubNumberOfObjects;
     memcpy( &(pNew->bStatus), &(pOld->bStatus), MAX_OBJECTS_PER_SLOT);
       // заполняем нулями массив
     memset( &(pNew->usAttachItem), 0, sizeof(pNew->usAttachItem));
       // копируем старый массив в начало большого нового
     memcpy( &(pNew->usAttachItem), &(pOld->usAttachItem), MAX_ATTACHMENTS*2 );
     memset( &(pNew->bAttachStatus), 0, sizeof(pNew->bAttachStatus));
     memcpy( &(pNew->bAttachStatus), &(pOld->bAttachStatus), MAX_ATTACHMENTS );
     pNew->fFlags = pOld->fFlags;
     pNew->ubMission = pOld->ubMission;
     pNew->bTrap = pOld->bTrap;
     pNew->ubImprintID = pOld->ubImprintID;
     pNew->ubWeight = pOld->ubWeight;
     pNew->fUsed = pOld->fUsed;
}

void RenovateWorldItem(WORLDITEM * pNew, WORLDITEM_OLD * pOld)
{
     pNew->fExists = pOld->fExists;
     pNew->sGridNo = pOld->sGridNo;
     pNew->ubLevel = pOld->ubLevel;
     RenovateObjectype( &(pNew->o), &(pOld->o) );
     pNew->usFlags = pOld->usFlags;
     pNew->bRenderZHeightAboveLevel = pOld->bRenderZHeightAboveLevel;
     pNew->bVisible = pOld->bVisible;
     pNew->ubNonExistChance = pOld->ubNonExistChance;
}

Открываем файл Tactical\Soldier Create.h и делаем с SOLDIERCREATE_STRUCT то же самое.

typedef struct
{
...
     //Inventory
     OBJECTTYPE_OLD  Inv[ NUM_INV_SLOTS ];      
...      
} SOLDIERCREATE_STRUCT_OLD;

typedef struct
{
...
     //Inventory
     OBJECTTYPE  Inv[ NUM_INV_SLOTS ];      
...      
} SOLDIERCREATE_STRUCT;

void RenovateSoldierCreateStruct(SOLDIERCREATE_STRUCT * pNew, SOLDIERCREATE_STRUCT_OLD * pOld);


Открываем файл Tactical\Soldier Create.c и добавляем в него реализацию RenovateSoldierCreateStruct

void RenovateSoldierCreateStruct(SOLDIERCREATE_STRUCT * pNew, SOLDIERCREATE_STRUCT_OLD * pOld)
{
     int i;
       // просто копирую часть структуры до инвентаря
     memcpy( pNew, pOld, (char*)&(pNew->Inv)-(char*)pNew );
       // конвертирую инвентарь по предметам один за одним
     for( i=0; i<NUM_INV_SLOTS; i++ )
           RenovateObjectype( pNew->Inv+i, pOld->Inv+i );
       // копирую оставшуюся после инвентаря часть структуры
     memcpy( &(pNew->HeadPal),&(pOld->HeadPal), sizeof(SOLDIERCREATE_STRUCT)-((char*)&(pNew->HeadPal)-(char*)pNew) );
}

Теперь сама загрузка. Загрузка карты происходит в TileEngine\worlddef.c в функции
BOOLEAN LoadWorld( UINT8 *puiFilename )

добавим к переменным (строка ~2630) флажок, означающий загрузку старой карты.
BOOLEAN      fLegacyMap;

после загрузки записанного в файл размера структуры SOLDIERTYPE (строка ~2714) сравним его с используемым, и в случае несовпадения установим флажок загрузки старой карты.

// Load soldier size
LOADDATA( &uiSoldierSize, pBuffer, sizeof( INT32 ) );

fLegacyMap = uiSoldierSize!=sizeof(SOLDIERTYPE);

добавляем данный флажок как параметр к функциям загрузки предметов (строка ~3009)

LoadWorldItemsFromMap( &pBuffer, fLegacyMap );

и солдат (строка ~3062)

LoadSoldiersFromMap( &pBuffer, fLegacyMap );


Теперь модифицируем сами функции.
Tactical\Soldier Init List.h (строка ~25):
BOOLEAN LoadSoldiersFromMap( INT8 **hBuffer, BOOLEAN fLegacyMap );
Tactical\Soldier Init List.c (строка ~279):

BOOLEAN LoadSoldiersFromMap( INT8 **hBuffer, BOOLEAN fLegacyMap )
{
...
               if( tempBasicPlacement.fDetailedPlacement )
           { //Add the static detailed placement information in the same newly created node as the basic placement.
                 //read static detailed placement from file
                       
                 if( fLegacyMap )
                 {
                               // загружаем старую структуру
                       LOADDATA( &tempDetailedPlacementOld, *hBuffer, sizeof( SOLDIERCREATE_STRUCT_OLD ) );
                               // переконвертируем её в новую
                       RenovateSoldierCreateStruct(&tempDetailedPlacement, &tempDetailedPlacementOld);
                 }
                 else
                 { // хотя на первый взгляд скобки здесь не нужны, LOADDATA - это макрос, развертывающийся в несколько комманд

                       LOADDATA( &tempDetailedPlacement, *hBuffer, sizeof( SOLDIERCREATE_STRUCT ) );
                 }

                 //allocate memory for new static detailed placement
                 pNode->pDetailedPlacement = (SOLDIERCREATE_STRUCT*)MemAlloc( sizeof( SOLDIERCREATE_STRUCT ) );
                 if( !pNode->pDetailedPlacement )
                 {
                       AssertMsg( 0, "Failed to allocate memory for new detailed placement in LoadSoldiersFromMap." );
                       return FALSE;
                 }
...
     return TRUE;
}

Tactical\World Items.h (строка ~43):
void LoadWorldItemsFromMap( INT8 **hBuffer, BOOLEAN fLegacyMap );
Tactical\World Items.c (строка ~446):

void LoadWorldItemsFromMap( INT8 **hBuffer, BOOLEAN fLegacyMap )
{
...
     if( gTacticalStatus.uiFlags & LOADING_SAVED_GAME && !gfEditMode )
     { //The sector has already been visited.  The items are saved in a different format that will be
           //loaded later on.  So, all we need to do is skip the data entirely.
           
           if( fLegacyMap )
                 *hBuffer += sizeof( WORLDITEM_OLD ) * uiNumWorldItems;
           else
           

                 *hBuffer += sizeof( WORLDITEM ) * uiNumWorldItems;
           return;
     }
     else for ( i = 0; i < uiNumWorldItems; i++ )
     {      //Add all of the items to the world indirectly through AddItemToPool, but only if the chance
           //associated with them succeed.
           
           if( fLegacyMap )
           {
                 LOADDATA( &dummyItemOld, *hBuffer, sizeof( WORLDITEM_OLD ) );
                 RenovateWorldItem(&dummyItem, &dummyItemOld );
           }
           else
           {

                 LOADDATA( &dummyItem, *hBuffer, sizeof( WORLDITEM ) );
           }
...
}

Вот и всё.


3. Что это было.

Исходников готовых не прилагаю, так как эта инструкция больше руководство к действию, чем готовое решение, т.к. сама по себе такая модификация не добавляет в игру новых возможностей. Для тех, кто не понимает как распорядиться полученным добром - в следующих статьях я (а может и кто другой) расскажу как расширить инвентарь наёмника или как сделать оружие с двумя калибрами и двумя магазинами. Хотя в структуру SOLDIERTYPE не добавлялись новые поля, это можно сделать на основе полученных данных.

Заголовок: Re: [Уголок багодела] Расширение структур OBJECTTY
Прислано пользователем Farah_Aydid на 14.11.2009 в 08:39:02
(2009 год)
Наткнулся на эту тему в поисках информации об особых функциях предметов. Конкретно, меня интересуют эффекты наркотиков, алкоголя и прочих медикаментов.
В каких файлах хранится инфа для этих особых предметов? И насколько они функциональны для редактирования и создания новых предметов такого класса? Возможно ли, например, ввести зависимость от наркотиков? Возможно ли добавить абсолютно новые медикаменты с другими эффектами? Как и где вообще можно управлять настройкой этих эффектов?

Ковыряюсь в обычных ХМЛ-файлах для мода 113, но простыми средствами редактора новых предметов с необходимыми эффектами создать не получается. Видимо, инфа о наркоте хранится в каком-то особом файле, потому что даже обычным дупликатом старого предмета эффекты не переносятся на новый. Наверное, должен совпадать индекс предмета, с индексом, указанным в том самом загадочном файле, который я ищу.))

ОООо! Эта инфа наверное в JSD хранится. И как же ее ковырять. О, ужас!  :-/

Заголовок: Re: [Уголок багодела] Расширение структур OBJECTTY
Прислано пользователем <<GEM>> на 14.11.2009 в 18:06:11

on 1258180742, Farah_Aydid wrote:
Эта инфа наверное в JSD хранится.
- не думаю. Инфа эта строго в исходниках записана. В оригинальном Джа так.

Заголовок: Re: [Уголок багодела] Расширение структур OBJECTTY
Прислано пользователем bugmonster на 15.11.2009 в 06:47:26
2Farah_Aydid: JSDшки это совсем из другой оперы, в Джа2 (в том числе и 1.13) свойства алкоголя и наркотиков жестко закодированы на номер предмета.
А вообще это оффтоп, ты ошибся темой.

Заголовок: Re: [Уголок багодела] Расширение структур OBJECTTY
Прислано пользователем Lion на 25.03.2013 в 04:02:47
2bugmonster: В статье подробно описана возня с переделыванием карт из старого формата в новый с клонированием структур и прочими радостями жизни.

Меня больше интересует вопрос (можно, конечно, и в исходниках глянуть, но думаю, Вы знаете ответ), всегда ли в файле с картой хранятся эти структуры? Можно ли вольно править OBJECTTYPE и SOLDIERTYPE, а карты просто переносить, если на них нет ни одного персонажа и ни одного предмета?

Опять же можно ли приводить карты к новым структурам методом:
1) Удалить с карты в старом формате всех людей и все предметы.
2) Открыть карту как карту в новом формате, вернуть людей и предметы?

Заголовок: Re: [Уголок багодела] Расширение структур OBJECTTY
Прислано пользователем bugmonster на 26.03.2013 в 01:22:27
2Lion:  
Quote:
Можно ли вольно править OBJECTTYPE и SOLDIERTYPE, а карты просто переносить, если на них нет ни одного персонажа и ни одного предмета?  

Да, ЕМНИП.


Quote:
Опять же можно ли приводить карты к новым структурам методом:
1) Удалить с карты в старом формате всех людей и все предметы.
2) Открыть карту как карту в новом формате, вернуть людей и предметы?


Исходя из вышесказанного - можно. Но лучше дописать конвертацию в исходниках.

Заголовок: Re: [Уголок багодела] Расширение структур OBJECTTY
Прислано пользователем Lion на 26.03.2013 в 01:46:39

on 1364250147, bugmonster wrote:
Но лучше дописать конвертацию в исходниках.  
В каком отношении лучше?
При учёте того, что на картах, о которых идёт речь, предметов и людей просто нет. Т.е. удалять никого не надо.

Хотя можно и конвертацию написать.  ::)

Заголовок: Re: [Уголок багодела] Расширение структур OBJECTTY
Прислано пользователем bugmonster на 26.03.2013 в 02:11:14

on 1364251599, Lion wrote:
В каком отношении лучше?
При учёте того, что на картах, о которых идёт речь, предметов и людей просто нет. Т.е. удалять никого не надо.  

Лучше в общем случае.
Карты без предметов и людей это очень специфический случай.

Заголовок: Re: [Уголок багодела] Расширение структур OBJECTTY
Прислано пользователем Lion на 26.03.2013 в 02:46:36

on 1364253074, bugmonster wrote:
Карты без предметов и людей это очень специфический случай.  
Не согласен. Для определённого этапа создания проекта - вполне нормально. Какие карты - известно, а что конкретно на них будет - известно плохо, рисуется ландшафт, а положить предметы и людей - дело быстрое и нехитрое, к тому же лучше это делать на всех картах сразу.

Спасибо.

Заголовок: Re: [Уголок багодела] Расширение структур OBJECTTY
Прислано пользователем MAn на 26.03.2013 в 02:54:37
[offtop]
on 1364255196, Lion wrote:
а положить предметы и людей - дело быстрое и нехитрое,  

Положить - может и нехитрое. Если положить с пробором.

[/offtop]



A.I.M. » Powered by YaBB Modification 4 (v.4.0.0-pre)!
YaBB © 2000-2003. All Rights Reserved.